diff --git a/.github/workflows/references.yaml b/.github/workflows/references.yaml new file mode 100644 index 0000000..ee66152 --- /dev/null +++ b/.github/workflows/references.yaml @@ -0,0 +1,51 @@ +name: references +on: + repository_dispatch: + types: [generate-references] + workflow_dispatch: + inputs: + ref: + description: 'webview-bundle ref (tag/branch/SHA) to generate from' + required: false + default: 'main' +permissions: + contents: write + pull-requests: write +concurrency: + group: references + cancel-in-progress: true +jobs: + generate: + runs-on: ubuntu-latest + env: + WVB_REF: ${{ github.event.client_payload.ref || inputs.ref || 'main' }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1 + with: + repository: webview-bundle/webview-bundle + ref: ${{ env.WVB_REF }} + path: .webview-bundle + - uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # 4.0.1 + with: + install_args: 'node' + - run: corepack enable + - run: yarn install + - run: yarn install --mode=skip-build + working-directory: .webview-bundle + - run: yarn gen:references --repo .webview-bundle + - run: yarn gen:fumadocs + - run: yarn format content/docs/references/api + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # 8.1.1 + with: + branch: docs/regenerate-references + delete-branch: true + add-paths: content/docs/references/api + commit-message: 'docs(references): regenerate API references from webview-bundle@${{ env.WVB_REF }}' + title: 'docs(references): regenerate API references' + body: | + Auto-generated JavaScript API references from + [`webview-bundle@${{ env.WVB_REF }}`](https://github.com/webview-bundle/webview-bundle/tree/${{ env.WVB_REF }}). + + Generated by `yarn gen:references` (TypeScript + JSDoc via TypeDoc) — + see `scripts/generate-references.ts` and `.github/workflows/references.yaml`. diff --git a/.gitignore b/.gitignore index af13e13..2ddc373 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ dist/ # AI .claude/ .env +.webview-bundle/ diff --git a/WORKS.md b/WORKS.md new file mode 100644 index 0000000..df04874 --- /dev/null +++ b/WORKS.md @@ -0,0 +1,111 @@ +# 26.07.04 문서 작업 : 1단계 + +docs/guide/getting-started +docs/guide/core-concepts + +위 두 경로에 있는 문서들이 아직 미완성 상태입니다. +`(설명)`으로 감싼 부분은 들어가야 하는 내용에 대한 간단한 설명입니다. + +당신의 역할은 이미 작성된 문서를 리뷰하고 내용을 채워넣는 것입니다. + +- 이미 잡아둔 헤드라인 내에서만 문서를 채워주세요. +- 명확하고 정확하고, 간결한 내용만 작성해주세요. 불필요한 첨언은 생략해주세요. +- https://technical-writing.dev/tutorial/review-prompt.html 테크니컬 라이팅 기술을 참고하여 작성해주세요. +- `.mdx`에는 영문을, `.ko.mdx`에는 한글로 작성합니다. +- 이미 한글로 작성이 완료된 문서가 있다면, 영문 문서에 번역해서 기입해주세요. +- OTA는 "원격"으로 번역해주세요. + +# 26.07.05 문서 작업 : 2단계 + +content/docs/guide/frontend/** +content/docs/guide/native/** + +이제 웹뷰 번들을 개발하는 단계에서 실질적인 가이드를 적을 예정입니다. +위 두 경로의 문서들은 아래 내용을 포함해야 합니다. +(파일명 - 대주제 - 섹션별 세부항목) + +- 네이티브 + - index - 웹뷰 번들을 설치하기 위한 설정 : 패키지 설치, 요구사항, 시작하는 법(wvb 인스턴스 생성), Bundle Protocol / Source / Remote / Updater 설정법, 브릿지 노출방법, 이후 웹뷰를 띄우는 동작까지 + - local-development - 로컬 개발환경 설정 : Local Protocol 설정으로 로컬 개발 서버를 웹뷰에 띄워서 개발하는 방법 + - builtin - 빌트인 번들 설치 : 앱을 패키징할 때 빌트인 번들을 설치하는 방법 설명 / 리모트에서 다운받아 설치 / 로컬 파일을 설치 +- 프론트엔드 + - packing - 번들 패키징 : 번들 패키징을 위한 설정 방법 + - development - 개발 서버 띄워서 네이티브에서 개발 : Local Protocol 설정으로 로컬 개발 서버를 웹뷰에 띄우는 방법 + - upload - 웹뷰 번들 업로드 하기 : 배포할 버전의 웹뷰 번들을 리모트에 업로드 하기 + - deploy - 웹뷰 번들 배포하기 : 업르도된 웹뷰 번들을 현재 버전을 배포하기 + - bridges + - index - 브릿지 설명 : 브릿지 패키지 설명 / 브릿지의 역할 및 제공하는 기능 + - checkout-sources - Source 브릿지 사용법 : 번들 소스 유즈케이스 설명 + - update-remote-bundles - Updater 브릿지 사용법 : 브릿지를 사용하여 리모트에서 원격으로 새 웹뷰 번들을 다운받고 설치하는 방법 설명 + - testing - 테스트 방법 : 브릿지를 테스트하는 방법 설명 / mockInvoke 사용법 + +문장의 어투는 기존 content/docs/getting-started, content/docs/core-concepts 에 있는 문서를 참고해주시고, +간결하고 정확하게, 그리고 불필요한 내용이 거의 없게 (no verbose) 작성해주세요. 살은 제가 붙이겠습니다. + +API에 대한 자세한 스펙은 소스코드를 참고해주세요. + +- webview-bundle : /Users/seokju.me/workspaces/webview-bundle +- android : /Users/seokju.me/workspaces/webview-bundle-android +- ios : /Users/seokju.me/workspaces/webview-bundle-ios + +다른 참고할 레퍼런스 문서 + +- Tauri : https://tauri.app/develop/ +- Capacitor : https://capacitorjs.com/docs +- Expo : https://docs.expo.dev/ + +# 26.07.05 문서 작업 : 3단계 + +위치 : content/docs/guide/remote/\*\* + +이제 리모트 구성 및 리모트로부터 신규 웹뷰 번들을 안전하게 다운받아 설치하는 Over-The-Air를 사용하는 방법에 대한 가이드 문서를 작성하겠습니다. + +- remote + - index.mdx : 리모트가 왜 필요한지랑 목적에 대해서 간단하게 설명합니다. CLI로 리모트를 사용하는 방법, Providers를 사용하여 리모트 구성하는 방법에 대해 설명합니다. + - using-cli.mdx : CLI를 사용하여 리모트를 구성하고 신규 웹뷰 번들을 다운받아 설치하는 방법에 대한 가이드 문서입니다. + - providers + - aws.mdx : AWS에 리모트 서버를 구축하는 방법에 대한 가이드 (`@wvb/remote-aws-provider`) / 클라이언트에서 리모트 사용을 위한 가이드 (`@wvb/remote-aws`) + - cloudflare.mdx : Cloudflare에 리모트 서버를 구축하는 방법에 대한 가이드 (`@wvb/remote-cloudflare-provider`) / 클라이언트에서 리모트 사용을 위한 가이드 (`@wvb/remote-cloudflare`) + - local.mdx : 로컬에 리모트 서버를 구축하는 방법에 대한 가이드 (`@wvb/remote-local-provider`) / 클라이언트에서 리모트 사용을 위한 가이드 (`@wvb/remote-local`) + - http-spec + - index.mdx : 직접 리모트 서버를 구축할 때 HTTP 스펙에 대한 가이드입니다. + - endpoints + - (각 엔트리 포인트 별로 문서가 생성되고, OpenAPI 스펙과 유사한 디자인으로 문서를 만들어주세요.) + - errors.mdx : 리모트 서버에서 발생하는 에러에 대한 가이드입니다. + +문장의 어투는 기존 content/docs/getting-started, content/docs/core-concepts 에 있는 문서를 참고해주시고, +간결하고 정확하게, 그리고 불필요한 내용이 거의 없게 (no verbose) 작성해주세요. 살은 제가 붙이겠습니다. + +--- + +API에 대한 자세한 스펙은 소스코드를 참고해주세요. + +- webview-bundle : /Users/seokju.me/workspaces/webview-bundle +- android : /Users/seokju.me/workspaces/webview-bundle-android +- ios : /Users/seokju.me/workspaces/webview-bundle-ios + +다른 참고할 레퍼런스 문서 + +- Tauri : https://tauri.app/develop/ +- Capacitor : https://capacitorjs.com/docs +- Expo : https://docs.expo.dev/ + +# 26.07.05 문서 작업 : 4단계 + +References 문서 자동화를 구축합니다. 각 언어별 패키지들의 레퍼런스 문서화 전략은 다음과 같습니다. + +- JavaScript + - 리포지토리 https://github.com/webview-bundle/webview-bundle 에서 각 패키지 TypeScript + JSDoc 기반으로 레퍼런스 자동 생성 +- Rust : docs.rs 링크 external 처리 +- Android : javadoc.io 링크 external 처리 + - 리포지토리 : https://github.com/webview-bundle/webview-bundle-android + - 소스 코드 : /Users/seokju.me/workspaces/webview-bundle-android +- iOS : swift package index 링크 external 처리 + - 리포지토리 : https://github.com/webview-bundle/webview-bundle-ios + - 소스 코드 : /Users/seokju.me/workspaces/webview-bundle-ios + +JavaScript 패키지들의 레퍼런스 문서 자동화는 다음과 같이 동작을 설계합니다. + +1. https://github.com/webview-bundle/webview-bundle 리포지토리에서 release 워크플로가 실행되고 이후 결과가 이 리포지토리(webview-bundle/website)로 dispatch event +2. 이벤트를 받은 이후, latest 패키지들 기준으로 TypeScript + JSDoc 기반으로 레퍼런스 자동 생성 +3. 이후, 변경된 레퍼런스 정보를 신규 브랜치에서 커밋하여 PR을 자동 생성합니다. diff --git a/content/docs/changelog/index.ko.mdx b/content/docs/changelog/index.ko.mdx deleted file mode 100644 index 208453c..0000000 --- a/content/docs/changelog/index.ko.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: 변경 이력 -description: Webview Bundle 릴리스와 지금까지 배포된 버전을 추적합니다. ---- - -릴리스는 패키지별로 발행됩니다. 따라서 공식 릴리스 노트는 각 레지스트리와 GitHub 릴리스 페이지에 있습니다. - - - - - - -## 배포된 버전 - -패키지별 현재 배포 버전은 다음과 같습니다. - -| Package | Registry | Version | -| ------------------------ | ----------------------------------------------------------- | ------- | -| `wvb` (Rust 코어) | [crates.io](https://crates.io/crates/wvb) | 0.2.0 | -| `wvb-tauri` | [crates.io](https://crates.io/crates/wvb-tauri) | 0.1.0 | -| `@wvb/cli` | [npm](https://www.npmjs.com/package/@wvb/cli) | 0.1.0 | -| `@wvb/config` | [npm](https://www.npmjs.com/package/@wvb/config) | 0.1.0 | -| `@wvb/node` | [npm](https://www.npmjs.com/package/@wvb/node) | 0.1.0 | -| `@wvb/bridge` | [npm](https://www.npmjs.com/package/@wvb/bridge) | 0.1.0 | -| `@wvb/electron` | [npm](https://www.npmjs.com/package/@wvb/electron) | 0.1.0 | -| `@wvb/remote-aws` | [npm](https://www.npmjs.com/package/@wvb/remote-aws) | 0.1.0 | -| `@wvb/remote-cloudflare` | [npm](https://www.npmjs.com/package/@wvb/remote-cloudflare) | 0.1.0 | - -## 버전 관리 - -패키지는 [유의적 버전](https://semver.org)을 따릅니다. 재현 가능한 빌드를 위해 정확한 버전을 고정하세요. `.wvb` -번들 포맷은 패키지 버전과 별개로 자체 포맷 버전(`v1`)을 가집니다 — -[번들 포맷](/docs/guide/bundle-format)을 참고하세요. diff --git a/content/docs/changelog/index.mdx b/content/docs/changelog/index.mdx deleted file mode 100644 index 0ac4339..0000000 --- a/content/docs/changelog/index.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Changelog -description: Track Webview Bundle releases and the versions published so far. ---- - -Releases are cut per package, so the authoritative release notes live with each registry and the -GitHub release pages. - - - - - - -## Published versions - -The current published versions, by package: - -| Package | Registry | Version | -| ------------------------ | ----------------------------------------------------------- | ------- | -| `wvb` (Rust core) | [crates.io](https://crates.io/crates/wvb) | 0.2.0 | -| `wvb-tauri` | [crates.io](https://crates.io/crates/wvb-tauri) | 0.1.0 | -| `@wvb/cli` | [npm](https://www.npmjs.com/package/@wvb/cli) | 0.1.0 | -| `@wvb/config` | [npm](https://www.npmjs.com/package/@wvb/config) | 0.1.0 | -| `@wvb/node` | [npm](https://www.npmjs.com/package/@wvb/node) | 0.1.0 | -| `@wvb/bridge` | [npm](https://www.npmjs.com/package/@wvb/bridge) | 0.1.0 | -| `@wvb/electron` | [npm](https://www.npmjs.com/package/@wvb/electron) | 0.1.0 | -| `@wvb/remote-aws` | [npm](https://www.npmjs.com/package/@wvb/remote-aws) | 0.1.0 | -| `@wvb/remote-cloudflare` | [npm](https://www.npmjs.com/package/@wvb/remote-cloudflare) | 0.1.0 | - -## Versioning - -Packages follow [semantic versioning](https://semver.org). Pin exact versions for reproducible -builds. The `.wvb` -bundle format carries its own format version (`v1`) independent of package versions — see the -[bundle format](/docs/guide/bundle-format). diff --git a/content/docs/changelog/meta.json b/content/docs/changelog/meta.json deleted file mode 100644 index 48c9822..0000000 --- a/content/docs/changelog/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - "title": "Changelog", - "pages": ["index"] -} diff --git a/content/docs/changelog/meta.ko.json b/content/docs/changelog/meta.ko.json deleted file mode 100644 index 0cca8eb..0000000 --- a/content/docs/changelog/meta.ko.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - "title": "변경 이력", - "pages": ["index"] -} diff --git a/content/docs/config/index.ko.mdx b/content/docs/config/index.ko.mdx deleted file mode 100644 index de045d9..0000000 --- a/content/docs/config/index.ko.mdx +++ /dev/null @@ -1,307 +0,0 @@ ---- -title: 설정 -description: CLI와 프로그래밍 API가 pack, remote, serve, builtin 옵션을 읽어 오는 wvb.config 파일입니다. ---- - -단 하나의 `wvb.config` 파일이 모든 Webview Bundle 워크플로를 이끕니다. `wvb` CLI와 -프로그래밍 API(`@wvb/cli/api`)가 모두 이 파일을 불러옵니다. 따라서 설정 하나로 -패킹, 제공, 원격 배포, 빌트인 설치를 일관되게 유지합니다. `@wvb/config`의 -`defineConfig`로 작성하면 완전한 타입 체크와 에디터 자동 완성을 얻습니다. - -이 페이지에서는 설정 파일 자체와 최상위 `pack`, `serve`, `builtin` 섹션을 다룹니다. -원격 배포, 무결성, 서명 옵션은 별도 페이지에서 설명합니다. - - - - - - - -## 설정 파일 - -설정 파일을 프로젝트 루트에 두세요. CLI는 작업 디렉터리에서 다음 순서로 처음 일치하는 -파일을 자동으로 찾습니다. - -```text -wvb.config.js -wvb.config.cjs -wvb.config.mjs -wvb.config.ts -wvb.config.cts -wvb.config.mts -webview-bundle.config.js -webview-bundle.config.cjs -webview-bundle.config.mjs -webview-bundle.config.ts -webview-bundle.config.cts -webview-bundle.config.mts -wvb.config.json -wvb.config.jsonc -``` - -`wvb.config.*`와 `webview-bundle.config.*` 두 기본 이름 모두 `.js`, `.cjs`, `.mjs`, -`.ts`, `.cts`, `.mts` 확장자를 지원합니다. `.json`과 `.jsonc` 형식은 `wvb.config` -기본 이름에서만 인식됩니다. 자동 탐색에 의존하는 대신 특정 파일을 지정하려면 어떤 -명령어에든 `--config`(별칭 `-C`)를 전달하세요. - -```sh -wvb pack --config ./configs/wvb.prod.ts -``` - -### defineConfig - -설정을 `@wvb/config`의 `defineConfig`로 감싸세요. 이 함수는 런타임에서는 항등 함수이며, -타입을 붙이는 것이 유일한 역할입니다. 객체, 설정으로 이행되는 프로미스, 또는 설정을 -반환하는 동기 혹은 비동기 함수를 받습니다. 따라서 불러오는 시점에 값을 계산할 수 있습니다. - - - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - root: process.cwd(), - pack: { - srcDir: './dist', - }, - serve: { - port: 4312, - }, -}); -``` - - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig(async () => { - const endpoint = process.env.WVB_ENDPOINT; - return { - remote: { - endpoint, - }, - }; -}); -``` - - - -이 패키지를 개발 의존성으로 설치하세요: `npm install -D @wvb/config`. - -## 최상위 필드 - -설정 객체는 다섯 개의 선택 필드를 가집니다. 최상위에서는 그 외에 아무것도 읽지 않습니다. - -| Field | Type | Default | -| --------- | --------------- | --------------- | -| `root` | `string` | `process.cwd()` | -| `pack` | `PackConfig` | — | -| `remote` | `RemoteConfig` | — | -| `serve` | `ServeConfig` | — | -| `builtin` | `BuiltinConfig` | — | - -`root`는 상대 경로를 해석하는 데 사용하는 프로젝트 루트를 설정합니다. 절대 경로일 수도 -있고 설정 파일 기준 상대 경로일 수도 있습니다. `remote` 필드는 -[원격, 무결성 및 서명](/docs/config/remote) 페이지에서 설명합니다. - -## pack - -`pack`(`PackConfig`)은 `wvb pack`의 기본값을 설정합니다. CLI 플래그는 호출할 때마다 -이 값을 재정의합니다. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - srcDir: './dist', - outFile: '.wvb/app', - overwrite: true, - ignore: ['*.map', /\.DS_Store$/], - headers: { - '*.html': { 'cache-control': 'max-age=0' }, - '*.js': { 'cache-control': 'max-age=31536000' }, - }, - }, -}); -``` - -| Field | Type | Default | -| ----------- | ------------------------------------------------------------------------------------ | ------------- | -| `srcDir` | `string` | `./dist` | -| `outFile` | `string` | `.wvb/` | -| `overwrite` | `boolean` | `true` | -| `ignore` | `Array \| ((file: string) => boolean \| Promise)` | — | -| `headers` | `Record \| Array<[glob, HeadersInit]> \| ((file) => HeadersInit)` | — | - -`outFile`은 단일 출력 경로입니다. 확장자를 생략하면 `.wvb` 확장자가 자동으로 붙습니다. -경로가 절대 경로가 아니면 `root` 기준 상대 경로로 해석됩니다. 기본값 `.wvb/`은 -`package.json`의 name에서 스코프 접두사를 제거하여 ``을 도출합니다. - - - 이 필드는 완전한 경로인 `outFile`입니다. `pack`에는 `outFileName`이나 `outDir`가 없습니다. 하위 - 디렉터리를 포함한 전체 출력 경로를 `outFile`에 작성하세요. - - -`ignore`는 글로브와 정규식의 배열, 또는 불리언을 반환하는 프레디킷(선택적으로 비동기)을 -받습니다. `headers`는 일치하는 파일에 HTTP 헤더를 붙이며 세 가지 형태를 받습니다. - -```ts -// 1. 글로브를 키로 갖는 Record -headers: { - '*.html': { 'cache-control': 'max-age=3600' }, -} - -// 2. [glob, HeadersInit] 튜플의 배열 -headers: [ - ['*.html', { 'cache-control': 'max-age=3600' }], - ['*.png', [['cache-control', 'max-age=0']]], // HeadersInit는 [name, value] 쌍도 받습니다 -] - -// 3. 파일별로 헤더를 반환하는 함수 -headers: (file) => (file.endsWith('.html') ? { 'cache-control': 'max-age=0' } : undefined) -``` - -## serve - -`serve`(`ServeConfig`)는 `wvb serve`의 기본값을 설정합니다. `wvb serve`는 개발용으로 -패킹된 번들을 제공하는 로컬 서버입니다. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - serve: { - file: './.wvb/app.wvb', - port: 4312, - silent: false, - }, -}); -``` - -| Field | Type | Default | -| -------- | --------- | ----------------------- | -| `file` | `string` | the `pack.outFile` path | -| `port` | `number` | `4312` | -| `silent` | `boolean` | — | - -`file`을 생략하면 해석된 `pack.outFile` 경로로 대체됩니다. 요청 로그 출력을 끄려면 -`silent`를 설정하세요. - - - `serve`에는 `hostname` 필드가 없습니다. 다른 호스트에 바인딩하려면 명령줄에서 `wvb serve`에 - `--hostname`(별칭 `-H`)을 전달하세요. - - -## builtin - -`builtin`(`BuiltinConfig`)은 `wvb builtin`의 기본값을 설정합니다. `wvb builtin`은 앱 -안에 함께 배포되는 번들을 설치합니다. 번들은 원격에서 다운로드하거나 로컬 워크스페이스에서 -수집합니다. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - builtin: { - outDir: '.wvb/builtin/bundles', - target: { type: 'remote' }, - include: ['app*'], - exclude: [/^internal-/], - clean: true, - }, -}); -``` - -| Field | Type | Default | -| --------- | -------------------------------------------------------------------- | ---------------------- | -| `outDir` | `string` | `.wvb/builtin/bundles` | -| `target` | `BuiltinTarget` | `{ type: 'remote' }` | -| `include` | `string \| RegExp \| Array \| ((info) => boolean)` | — | -| `exclude` | `string \| RegExp \| Array \| ((info) => boolean)` | — | -| `clean` | `boolean` | `true` | - -`include`와 `exclude`는 후보 번들을 필터링합니다. 각 필드는 글로브 문자열, 정규식, 둘 중 -하나의 배열, 또는 프레디킷 `(info: { name: string; version: string }) => boolean`(선택적으로 -비동기)을 받습니다. `clean`을 활성화하면 설치 전에 출력 디렉터리를 비웁니다. - -### target - -`target`은 `type`을 기준으로 하는 판별 유니온입니다. 서버에서 번들을 다운로드하려면 -`remote`를, 저장소의 워크스페이스에서 번들을 수집하려면 `local`을 선택하세요. - -```ts -// remote 타겟 — 서버에서 다운로드 -target: { - type: 'remote', - endpoint: 'https://updates.example.com', - download: { - concurrency: 4, - // http: { /* @wvb/node의 HttpOptions */ }, - }, -} - -// local 타겟 — 워크스페이스에서 수집 -target: { - type: 'local', - workspaces: ['packages/*'], - bundleName: { from: 'package.json' }, - version: { from: 'package.json' }, - packBeforeInstall: true, -} -``` - -`remote` 타겟에서 `endpoint`와 `download`는 선택 사항입니다. 유일한 동시성 조절 값은 -`download.concurrency`입니다. `download.http`는 [`@wvb/node`](/docs/references/node)의 -`HttpOptions` 타입을 받습니다. - -| `remote` field | Type | Default | -| ---------------------- | ------------- | ------- | -| `endpoint` | `string` | — | -| `download.concurrency` | `number` | — | -| `download.http` | `HttpOptions` | — | - -`local` 타겟에서 `workspaces`는 필수입니다. 이는 설정 전체에서 유일하게 필수인 필드입니다. -`integrity`와 `signature` 옵션은 remote 섹션과 동일한 형태를 공유합니다. -[원격, 무결성 및 서명](/docs/config/remote)을 참고하세요. - -| `local` field | Type | Default | -| ------------------- | --------------------------------------------------- | -------- | -| `workspaces` | `string[] \| (() => string[] \| Promise)` | required | -| `bundleName` | `BundleNameResolver` | — | -| `version` | `VersionResolver` | — | -| `integrity` | `boolean \| IntegrityMakeConfig` | — | -| `signature` | `SignatureSignConfig` | — | -| `packBeforeInstall` | `boolean` | `true` | - -## 다음 단계 - - - - - - diff --git a/content/docs/config/index.mdx b/content/docs/config/index.mdx deleted file mode 100644 index 2985ebc..0000000 --- a/content/docs/config/index.mdx +++ /dev/null @@ -1,314 +0,0 @@ ---- -title: Configuration -description: The wvb.config file that the CLI and the programmatic API read for pack, remote, serve, and builtin options. ---- - -A single `wvb.config` file drives every Webview Bundle workflow. Both the `wvb` CLI -and the programmatic API (`@wvb/cli/api`) load it, so one config keeps packing, -serving, remote publishing, and builtin installs consistent. Author it with -`defineConfig` from `@wvb/config` to get full type-checking and editor autocompletion. - -This page covers the config file itself and the top-level `pack`, `serve`, and -`builtin` sections. Remote publishing, integrity, and signature options live on a -dedicated page. - - - - - - - -## The config file - -Place the config in your project root. The CLI auto-discovers the first matching -file in the working directory, in this order: - -```text -wvb.config.js -wvb.config.cjs -wvb.config.mjs -wvb.config.ts -wvb.config.cts -wvb.config.mts -webview-bundle.config.js -webview-bundle.config.cjs -webview-bundle.config.mjs -webview-bundle.config.ts -webview-bundle.config.cts -webview-bundle.config.mts -wvb.config.json -wvb.config.jsonc -``` - -Both base names `wvb.config.*` and `webview-bundle.config.*` are supported with the -`.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, and `.mts` extensions. The `.json` and `.jsonc` -forms are only recognized for the `wvb.config` base name. Pass `--config` (alias -`-C`) to any command to point at a specific file instead of relying on discovery. - -```sh -wvb pack --config ./configs/wvb.prod.ts -``` - -### defineConfig - -Wrap your config with `defineConfig` from `@wvb/config`. It is an identity function -at runtime — its only job is to attach types. It accepts an object, a promise that -resolves to a config, or a synchronous or asynchronous function that returns one, so -you can compute values at load time. - - - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - root: process.cwd(), - pack: { - srcDir: './dist', - }, - serve: { - port: 4312, - }, -}); -``` - - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig(async () => { - const endpoint = process.env.WVB_ENDPOINT; - return { - remote: { - endpoint, - }, - }; -}); -``` - - - - - Install the package as a dev dependency: `npm install -D @wvb/config`. - - -## Top-level fields - -The config object has five optional fields. Nothing else is read at the top level. - -| Field | Type | Default | -| --------- | --------------- | --------------- | -| `root` | `string` | `process.cwd()` | -| `pack` | `PackConfig` | — | -| `remote` | `RemoteConfig` | — | -| `serve` | `ServeConfig` | — | -| `builtin` | `BuiltinConfig` | — | - -`root` sets the project root used to resolve relative paths. It may be absolute or -relative to the config file. The `remote` field is documented on the -[Remote, integrity & signature](/docs/config/remote) page. - -## pack - -`pack` (`PackConfig`) sets the defaults for `wvb pack`. CLI flags override these per -invocation. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - srcDir: './dist', - outFile: '.wvb/app', - overwrite: true, - ignore: ['*.map', /\.DS_Store$/], - headers: { - '*.html': { 'cache-control': 'max-age=0' }, - '*.js': { 'cache-control': 'max-age=31536000' }, - }, - }, -}); -``` - -| Field | Type | Default | -| ----------- | ------------------------------------------------------------------------------------ | ------------- | -| `srcDir` | `string` | `./dist` | -| `outFile` | `string` | `.wvb/` | -| `overwrite` | `boolean` | `true` | -| `ignore` | `Array \| ((file: string) => boolean \| Promise)` | — | -| `headers` | `Record \| Array<[glob, HeadersInit]> \| ((file) => HeadersInit)` | — | - -`outFile` is a single output path. The `.wvb` extension is appended automatically -when you omit it, and the path resolves relative to `root` unless it is absolute. The -default `.wvb/` derives `` from your `package.json` name with any scope -prefix stripped. - - - The field is `outFile`, a complete path. There is no `outFileName` or `outDir` on `pack` — write - the full output path, including any subdirectory, in `outFile`. - - -`ignore` accepts an array of globs and regular expressions, or a predicate that -returns a boolean (optionally async). `headers` attaches HTTP headers to matching -files and accepts three shapes: - -```ts -// 1. Record keyed by glob -headers: { - '*.html': { 'cache-control': 'max-age=3600' }, -} - -// 2. Array of [glob, HeadersInit] tuples -headers: [ - ['*.html', { 'cache-control': 'max-age=3600' }], - ['*.png', [['cache-control', 'max-age=0']]], // HeadersInit also accepts [name, value] pairs -] - -// 3. Function returning headers per file -headers: (file) => (file.endsWith('.html') ? { 'cache-control': 'max-age=0' } : undefined) -``` - -## serve - -`serve` (`ServeConfig`) sets the defaults for `wvb serve`, the local server that -serves a packed bundle for development. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - serve: { - file: './.wvb/app.wvb', - port: 4312, - silent: false, - }, -}); -``` - -| Field | Type | Default | -| -------- | --------- | ----------------------- | -| `file` | `string` | the `pack.outFile` path | -| `port` | `number` | `4312` | -| `silent` | `boolean` | — | - -`file` falls back to the resolved `pack.outFile` path when omitted. Set `silent` to -disable request-log output. - - - `serve` has no `hostname` field. To bind a different host, pass `--hostname` (alias `-H`) to `wvb - serve` on the command line. - - -## builtin - -`builtin` (`BuiltinConfig`) sets the defaults for `wvb builtin`, which installs -bundles that ship inside your app — either downloaded from a remote or collected from -local workspaces. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - builtin: { - outDir: '.wvb/builtin/bundles', - target: { type: 'remote' }, - include: ['app*'], - exclude: [/^internal-/], - clean: true, - }, -}); -``` - -| Field | Type | Default | -| --------- | -------------------------------------------------------------------- | ---------------------- | -| `outDir` | `string` | `.wvb/builtin/bundles` | -| `target` | `BuiltinTarget` | `{ type: 'remote' }` | -| `include` | `string \| RegExp \| Array \| ((info) => boolean)` | — | -| `exclude` | `string \| RegExp \| Array \| ((info) => boolean)` | — | -| `clean` | `boolean` | `true` | - -`include` and `exclude` filter the candidate bundles. Each accepts a glob string, a -regular expression, an array of either, or a predicate -`(info: { name: string; version: string }) => boolean` (optionally async). With -`clean` enabled, the output directory is cleared before install. - -### target - -`target` is a discriminated union on `type`. Choose `remote` to download bundles from -a server, or `local` to collect them from workspaces in your repository. - -```ts -// Remote target — download from a server -target: { - type: 'remote', - endpoint: 'https://updates.example.com', - download: { - concurrency: 4, - // http: { /* HttpOptions from @wvb/node */ }, - }, -} - -// Local target — collect from workspaces -target: { - type: 'local', - workspaces: ['packages/*'], - bundleName: { from: 'package.json' }, - version: { from: 'package.json' }, - packBeforeInstall: true, -} -``` - -For the `remote` target, `endpoint` and `download` are optional. The only concurrency -knob is `download.concurrency`; `download.http` accepts the `HttpOptions` type from -[`@wvb/node`](/docs/references/node). - -| `remote` field | Type | Default | -| ---------------------- | ------------- | ------- | -| `endpoint` | `string` | — | -| `download.concurrency` | `number` | — | -| `download.http` | `HttpOptions` | — | - -For the `local` target, `workspaces` is required — it is the only required field -anywhere in the config. The `integrity` and `signature` options share the same shapes -as the remote section; see [Remote, integrity & signature](/docs/config/remote). - -| `local` field | Type | Default | -| ------------------- | --------------------------------------------------- | -------- | -| `workspaces` | `string[] \| (() => string[] \| Promise)` | required | -| `bundleName` | `BundleNameResolver` | — | -| `version` | `VersionResolver` | — | -| `integrity` | `boolean \| IntegrityMakeConfig` | — | -| `signature` | `SignatureSignConfig` | — | -| `packBeforeInstall` | `boolean` | `true` | - -## Next steps - - - - - - diff --git a/content/docs/config/meta.json b/content/docs/config/meta.json deleted file mode 100644 index 578a090..0000000 --- a/content/docs/config/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - "title": "Config", - "pages": ["index", "remote"] -} diff --git a/content/docs/config/meta.ko.json b/content/docs/config/meta.ko.json deleted file mode 100644 index 71280a9..0000000 --- a/content/docs/config/meta.ko.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "root": true, - "title": "설정", - "pages": ["index", "remote"] -} diff --git a/content/docs/guide/bundle-format.ko.mdx b/content/docs/guide/bundle-format.ko.mdx deleted file mode 100644 index 5bbe29f..0000000 --- a/content/docs/guide/bundle-format.ko.mdx +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: 번들 포맷 -description: .wvb 아카이브가 디스크에 섹션별로 어떻게 배치되는지, 그리고 체크섬이 손상으로부터 아카이브를 어떻게 보호하는지 설명합니다. ---- - -Webview Bundle은 빌드된 웹 자산을 압축하고 무결성을 검증한 하나의 아카이브로 묶은 단일 `.wvb` 파일입니다. 세 개의 섹션이 연속으로 기록됩니다. 고정 길이 17바이트 헤더 뒤에 가변 길이의 인덱스와 데이터 영역이 이어집니다. - -| Offset | Section | Size | 담는 내용 | -| ----------- | ------- | ---------- | ----------------------------------------- | -| `0` | Header | `17 bytes` | 매직 넘버, 포맷 버전, 인덱스 크기, 체크섬 | -| `17` | Index | variable | 경로 → 오프셋/길이/헤더 엔트리 맵 | -| after index | Data | variable | 경로순으로 정렬된 LZ4 압축 파일 내용 | - -각 섹션은 자체 체크섬을 가지므로, 잘리거나 손상된 아카이브는 내용을 제공하기 전에 걸러집니다. 번들을 만들려면 [CLI](/docs/guide/cli) 또는 [Node API](/docs/references/node)를 참고하세요. - -```ts title="build.ts" -import { readFile } from 'node:fs/promises'; -import { BundleBuilder, writeBundle } from '@wvb/node'; - -const builder = new BundleBuilder(); -builder.insertEntry('/index.html', await readFile('dist/index.html'), 'text/html'); -builder.insertEntry('/app.js', await readFile('dist/app.js'), 'application/javascript'); - -const bundle = builder.build(); -await writeBundle(bundle, '.wvb/app_1.0.0.wvb'); -``` - -관례상 패킹된 파일은 `_.wvb` 형식으로 이름을 짓습니다. 예를 들어 `app_1.0.0.wvb`입니다. 번들 이름과 버전은 소스 수준의 개념으로, 파일명과 매니페스트에 존재할 뿐 아카이브 내부에는 들어가지 않습니다. [번들 소스](/docs/guide/bundle-sources)를 참고하세요. - -## 헤더 - -헤더는 정확히 17바이트이며 고정 레이아웃을 가집니다. 직렬화기를 거치지 않고 직접 기록하므로, 모든 필드가 알려진 오프셋에 자리합니다. - -| Field | Offset | Length | Encoding | -| ------------ | ------ | ------ | ------------------------------ | -| Magic number | 0 | 8 | raw bytes `0xF09F8C90F09F8E81` | -| Version | 8 | 1 | single byte (포맷 버전) | -| Index size | 9 | 4 | `u32`, big-endian | -| Checksum | 13 | 4 | `u32`, big-endian xxHash-32 | - -인덱스 크기가 `1234`인 `V1` 번들의 경우, 17바이트는 다음과 같습니다. - -```text -F0 9F 8C 90 F0 9F 8E 81 magic (🌐🎁) -01 version (V1) -00 00 04 D2 index size = 1234, big-endian -31 38 03 10 xxHash-32 of the first 13 bytes -``` - -- 매직 넘버 `0xF09F8C90F09F8E81`은 🌐🎁(지구본 + 포장된 선물)의 UTF-8 인코딩입니다. 이 바이트를 찾지 못한 리더는 해당 파일을 번들이 아닌 것으로 판단해 거부합니다. -- 버전 바이트는 번들의 **포맷 버전**을 담습니다. 이 enum은 현재 `V1`이라는 단일 값만 가집니다. 이는 `1.0.0` 같은 번들의 릴리스 버전과는 별개이며, 릴리스 버전은 아카이브 내부에 절대 나타나지 않습니다. -- 인덱스 크기는 뒤따르는 인덱스의 바이트 길이입니다. 인덱스 자체의 후행 체크섬은 여기에 **포함되지 않습니다**. -- 체크섬은 처음 13바이트(체크섬 앞의 모든 바이트)에 대한 xxHash-32이며, big-endian으로 저장됩니다. - -디스크립터를 통해 헤더 필드를 다시 읽어올 수 있습니다. - -```ts -import { readBundle } from '@wvb/node'; - -const header = (await readBundle('app_1.0.0.wvb')).descriptor().header(); -header.version(); // 'v1' -header.indexSize(); // 인덱스의 바이트 길이 -header.indexEndOffset(); // 17 + indexSize + 4 -> 데이터 섹션이 시작되는 위치 -``` - -## 인덱스 - -인덱스는 헤더 바로 뒤에 위치하며, 각 파일 경로(예: `/index.html`)를 해당 파일의 바이트 위치와 제공 방식을 설명하는 엔트리로 매핑합니다. 각 엔트리는 다음을 담습니다. - -| Field | Type | 의미 | -| ---------------- | ------ | ------------------------------------------------- | -| `offset` | `u64` | 데이터 섹션 내에서 이 파일 데이터의 바이트 오프셋 | -| `len` | `u64` | **압축된** 바이트의 길이 | -| `content_type` | string | MIME 타입. 제공 시 `Content-Type` 헤더에 사용됨 | -| `content_length` | `u64` | 원본 **비압축** 크기. `Content-Length`에 사용됨 | -| `headers` | map | 파일 제공 시 재생할 HTTP 헤더 이름/값 쌍 | - -```ts -const index = (await readBundle('app_1.0.0.wvb')).descriptor().index(); - -index.containsPath('/index.html'); // true -index.getEntry('/index.html'); -// { -// offset: 0, -// len: 184, // 압축됨 -// contentType: 'text/html', -// contentLength: 412, // 비압축 -// headers: { 'cache-control': 'no-cache' }, -// isEmpty: false, -// } -``` - -직렬화 세부 사항: - -- big-endian 가변 길이 정수 구성으로 [bincode](https://github.com/bincode-org/bincode)를 사용해 인코딩합니다. 숫자 필드는 고정 폭이 아니라 varint로 인코딩됩니다. -- 맵은 인코딩 전에 경로순으로 정렬됩니다. 따라서 출력 바이트는 결정적이며 삽입 순서와 무관합니다. -- 인덱스 바이트에 대한 4바이트 big-endian xxHash-32 체크섬이 인덱스 바로 뒤에 기록됩니다. - -프로토콜 핸들러가 파일을 제공할 때는 저장된 `headers`를 재생한 다음, `content_type`으로 `Content-Type`을, `content_length`(비압축 크기)로 `Content-Length`를 설정합니다. URL이 엔트리로 어떻게 매핑되는지는 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. - -## 데이터 - -데이터 섹션은 인덱스와 동일한 순서로, 경로순으로 정렬된 파일 내용을 담습니다. 각 파일은 LZ4로 압축된 바이트와 그 압축 바이트에 대한 4바이트 big-endian xxHash-32 체크섬으로 구성됩니다. - -```text -[ LZ4 bytes for file A ][ xxHash-32 ][ LZ4 bytes for file B ][ xxHash-32 ] ... -``` - -파일은 크기를 앞에 붙이는 블록 포맷으로 [LZ4](https://github.com/lz4/lz4)를 사용해 압축됩니다. 따라서 비압축 길이가 압축 데이터와 함께 전달됩니다. 엔트리의 `offset`은 압축 바이트의 시작을 가리키고, `len`은 그 바이트의 길이이며, 파일별 체크섬은 `offset + len` 위치에 자리합니다. - -`getData`는 압축 해제된 바이트를 반환하고, `getDataChecksum`은 저장된 xxHash-32를 반환합니다. 호출자는 이를 사용해 정지 상태의 파일을 검증할 수 있습니다. - -```ts -const bundle = await readBundle('app_1.0.0.wvb'); - -bundle.getData('/index.html'); // Buffer (압축 해제됨) | null -bundle.getDataChecksum('/index.html'); // number (xxHash-32) | null -``` - -## 체크섬과 무결성 - -이 포맷은 서로 다른 두 가지 작업을 위해 두 가지 해싱 방식을 사용합니다. - -헤더, 인덱스, 파일별 데이터에 들어가는 **내부 체크섬**은 모두 **xxHash-32**입니다. 이들은 저장 무결성을 지킵니다. 파일이 잘리거나, 일부만 기록되거나, 디스크에서 손상되는 경우를 막습니다. xxHash는 빠르고 비암호학적이며, 이것이 바로 손상 검사에 필요한 특성입니다. - -번들의 **무결성** 값은 이와 다릅니다. 다운로드된 번들의 직렬화된 바이트에 대한 **SHA-2** 해시로, 클라이언트가 서버가 게시한 것을 정확히 받았는지 확인하는 데 쓰입니다. 알고리즘은 `sha256`(기본값), `sha384`, `sha512`이며, `:` 형식으로 직렬화됩니다. - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -sha384:Ws2q... -``` - -```ts -import type { IntegrityAlgorithm, IntegrityPolicy } from '@wvb/node'; -// IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512' -// IntegrityPolicy = 'strict' | 'optional' | 'none' (기본값: 'optional') -``` - - - 이 포맷은 무결성에 SHA-3가 아니라 **SHA-2**를 사용합니다. 두 작업은 서로 구별됩니다. xxHash-32는 - 정지 상태의 바이트를 보호하고, SHA-2 무결성은 다운로드한 내용을 검증합니다. 업데이트 중에 무결성과 - 서명이 어떻게 검사되는지는 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. - - -## 다음으로 볼 내용 - -정확한 상수와 필드 레이아웃을 포함한 바이트 수준 명세는 [docs.rs/wvb](https://docs.rs/wvb)의 Rust 크레이트 문서에 있습니다. - - - - - - diff --git a/content/docs/guide/bundle-format.mdx b/content/docs/guide/bundle-format.mdx deleted file mode 100644 index 4d6ebf0..0000000 --- a/content/docs/guide/bundle-format.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: Bundle format -description: How a .wvb archive is laid out on disk, section by section, and how its checksums protect against corruption. ---- - -A Webview Bundle is a single `.wvb` file that packs your built web assets into one compressed, -integrity-checked archive. It is three sections written back to back — a fixed 17-byte header followed -by a variable-length index and data region: - -| Offset | Section | Size | Holds | -| ----------- | ------- | ---------- | -------------------------------------------------- | -| `0` | Header | `17 bytes` | magic number, format version, index size, checksum | -| `17` | Index | variable | path → offset/length/headers entry map | -| after index | Data | variable | LZ4-compressed file contents, sorted by path | - -Every section carries its own checksum, so a truncated or corrupted archive is caught before any content -is served. To produce one, see the [CLI](/docs/guide/cli) or the [Node API](/docs/references/node): - -```ts title="build.ts" -import { readFile } from 'node:fs/promises'; -import { BundleBuilder, writeBundle } from '@wvb/node'; - -const builder = new BundleBuilder(); -builder.insertEntry('/index.html', await readFile('dist/index.html'), 'text/html'); -builder.insertEntry('/app.js', await readFile('dist/app.js'), 'application/javascript'); - -const bundle = builder.build(); -await writeBundle(bundle, '.wvb/app_1.0.0.wvb'); -``` - -By convention a packed file is named `_.wvb`, e.g. `app_1.0.0.wvb`. The bundle name and -version are source-level concepts that live in the filename and the manifest, never inside the archive. -See [Bundle sources](/docs/guide/bundle-sources). - -## Header - -The header is exactly 17 bytes with a fixed layout, written by hand rather than through a serializer, so -every field sits at a known offset. - -| Field | Offset | Length | Encoding | -| ------------ | ------ | ------ | ------------------------------ | -| Magic number | 0 | 8 | raw bytes `0xF09F8C90F09F8E81` | -| Version | 8 | 1 | single byte (format version) | -| Index size | 9 | 4 | `u32`, big-endian | -| Checksum | 13 | 4 | `u32`, big-endian xxHash-32 | - -For a `V1` bundle with an index size of `1234`, the 17 bytes are: - -```text -F0 9F 8C 90 F0 9F 8E 81 magic (🌐🎁) -01 version (V1) -00 00 04 D2 index size = 1234, big-endian -31 38 03 10 xxHash-32 of the first 13 bytes -``` - -- The magic number `0xF09F8C90F09F8E81` is the UTF-8 encoding of 🌐🎁 (globe + wrapped gift). A reader - that does not find these bytes rejects the file as not a bundle. -- The version byte holds the bundle **format version**. The enum currently has a single value, `V1`. - This is distinct from a bundle's release version such as `1.0.0`, which never appears inside the archive. -- The index size is the byte length of the index that follows, **not** counting the index's own trailing - checksum. -- The checksum is an xxHash-32 over the first 13 bytes (everything before the checksum), stored big-endian. - -Read header fields back through the descriptor: - -```ts -import { readBundle } from '@wvb/node'; - -const header = (await readBundle('app_1.0.0.wvb')).descriptor().header(); -header.version(); // 'v1' -header.indexSize(); // byte length of the index -header.indexEndOffset(); // 17 + indexSize + 4 -> where the data section starts -``` - -## Index - -The index sits immediately after the header and maps each file path, e.g. `/index.html`, to an entry -describing where its bytes are and how to serve them. Each entry holds: - -| Field | Type | Meaning | -| ---------------- | ------ | -------------------------------------------------------------- | -| `offset` | `u64` | byte offset of this file's data within the data section | -| `len` | `u64` | length of the **compressed** bytes | -| `content_type` | string | MIME type, used for the `Content-Type` header when served | -| `content_length` | `u64` | original **uncompressed** size, used for `Content-Length` | -| `headers` | map | HTTP header name/value pairs to replay when the file is served | - -```ts -const index = (await readBundle('app_1.0.0.wvb')).descriptor().index(); - -index.containsPath('/index.html'); // true -index.getEntry('/index.html'); -// { -// offset: 0, -// len: 184, // compressed -// contentType: 'text/html', -// contentLength: 412, // uncompressed -// headers: { 'cache-control': 'no-cache' }, -// isEmpty: false, -// } -``` - -Serialization details: - -- Encoded with [bincode](https://github.com/bincode-org/bincode) in a big-endian, variable-length integer - configuration. Numeric fields are varint-encoded, not fixed width. -- The map is sorted by path before encoding, so the output bytes are deterministic and independent of - insertion order. -- A 4-byte big-endian xxHash-32 checksum of the index bytes is written right after the index. - -When the protocol handler serves a file, it replays the stored `headers`, then sets `Content-Type` from -`content_type` and `Content-Length` from `content_length` (the uncompressed size). See -[Protocol handling](/docs/guide/protocol-handling) for how a URL maps to an entry. - -## Data - -The data section holds file contents sorted by path, in the same order as the index. Each file is its -LZ4-compressed bytes followed by a 4-byte big-endian xxHash-32 checksum of those compressed bytes: - -```text -[ LZ4 bytes for file A ][ xxHash-32 ][ LZ4 bytes for file B ][ xxHash-32 ] ... -``` - -Files are compressed with [LZ4](https://github.com/lz4/lz4) in its size-prepended block format, so the -uncompressed length travels with the compressed data. An entry's `offset` points at the start of its -compressed bytes, `len` is the length of those bytes, and the per-file checksum sits at `offset + len`. - -`getData` returns the decompressed bytes; `getDataChecksum` returns the stored xxHash-32 so callers can -verify a file at rest: - -```ts -const bundle = await readBundle('app_1.0.0.wvb'); - -bundle.getData('/index.html'); // Buffer (decompressed) | null -bundle.getDataChecksum('/index.html'); // number (xxHash-32) | null -``` - -## Checksums versus integrity - -The format uses two different hashing schemes for two different jobs. - -**Internal checksums** in the header, index, and per-file data are all **xxHash-32**. They guard storage -integrity: a file truncated, partially written, or corrupted on disk. xxHash is fast and -non-cryptographic, which is exactly what a corruption check needs. - -A bundle's **integrity** value is something else: a **SHA-2** hash over the serialized bytes of a -downloaded bundle, so a client can confirm it received exactly what the server published. Algorithms are -`sha256` (default), `sha384`, and `sha512`, serialized as `:`: - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -sha384:Ws2q... -``` - -```ts -import type { IntegrityAlgorithm, IntegrityPolicy } from '@wvb/node'; -// IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512' -// IntegrityPolicy = 'strict' | 'optional' | 'none' (default: 'optional') -``` - - - The format uses **SHA-2** for integrity, not SHA-3. The two jobs are distinct: xxHash-32 protects - bytes at rest, while SHA-2 integrity verifies what was downloaded. See [Remote - bundles](/docs/guide/remote-bundles) for how integrity and signatures are checked during an - update. - - -## Where to go next - -The byte-level specification, including exact constants and field layouts, lives in the Rust crate -documentation at [docs.rs/wvb](https://docs.rs/wvb). - - - - - - diff --git a/content/docs/guide/bundle-sources.ko.mdx b/content/docs/guide/bundle-sources.ko.mdx deleted file mode 100644 index 4e62ef8..0000000 --- a/content/docs/guide/bundle-sources.ko.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: 번들 소스 -description: Webview Bundle이 builtin 및 remote 소스에 걸쳐 번들을 디스크에 저장하고, 매니페스트로 추적하며, 채널별로 버전을 선택하는 방식을 설명합니다. ---- - -**소스**는 번들이 저장되는 기기 내의 위치입니다. 모든 앱은 두 개의 소스를 가집니다. 하나는 앱 패키지 안에 포함되어 배포되는 읽기 전용 `builtin` 소스이고, 다른 하나는 다운로드된 업데이트를 위한 쓰기 가능한 `remote` 소스입니다. 이 페이지에서는 소스가 어떻게 구성되는지, `manifest.json`이 버전을 어떻게 추적하는지, 그리고 채널이 클라이언트에게 전달할 버전을 어떻게 선택하는지 설명합니다. - -## 번들, 이름, 버전 - -**번들**은 하나의 논리적 웹 앱이며, **번들 이름**(예: `app`)으로 식별됩니다. 번들은 여러 **버전**(`1.0.0`, `1.1.0`, …)을 가지며, 각 버전은 별도의 `.wvb` 파일입니다. 어느 시점이든 하나의 버전이 **현재(current)** 버전으로, 웹뷰에 제공됩니다. - -번들 이름과 버전은 _소스 수준_ 식별자입니다. 이 값들은 디스크에 저장된 파일명과 매니페스트에 나타나지만, `.wvb` 아카이브 자체에는 저장되지 **않습니다**. 아카이브는 바이너리 파일 포맷 버전(`v1`)만 담습니다. 바이트 레이아웃은 [번들 포맷](/docs/guide/bundle-format)을 참고하세요. - -## builtin 소스와 remote 소스 - -앱은 동일한 번들 이름을 서로 다른 저장소에서 제공하는 두 개의 소스로 실행됩니다. - -- **`builtin`** — 앱 패키지 _안에_ 포함되어 배포되는 번들입니다. 읽기 전용이며, 앱을 처음 실행할 때 아직 아무것도 다운로드되지 않은 상태에서 폴백으로 사용됩니다. -- **`remote`** — 업데이트 서버에서 다운로드한 번들입니다. 쓰기 가능하며, 앱스토어 릴리스 없이 무선(OTA)으로 업데이트됩니다. - -번들이 두 소스에 모두 존재하면 **remote가 우선합니다**. 소스는 remote 매니페스트의 현재 버전을 먼저 확인하여 버전을 결정하며, remote에 아무것도 없으면 builtin으로 폴백합니다. - - - 두 소스는 서로 독립적인 디렉터리입니다. 소스 객체는 별도의 `builtinDir`과 `remoteDir`을 가지며, 각 - 디렉터리는 자체 `manifest.json`을 가집니다. 버전을 결정할 때는 remote 매니페스트를 먼저 읽고 - builtin 매니페스트를 나중에 읽습니다. - - -이러한 분리 덕분에 업데이트가 안전해집니다. 폴백할 수 있는 배포된 builtin 버전이 항상 존재하며, 다운로드된 remote 버전은 검증과 설치를 마치면 자연스럽게 builtin 버전을 가립니다. 다운로드 및 검증 흐름은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. - -## 디스크 레이아웃 - -소스 디렉터리 안에서 각 번들은 자체 폴더를 가지며, 매니페스트는 루트에 위치합니다. - -```text -{source_dir}/ -├── {name}/ -│ └── {name}_{version}.wvb -└── manifest.json -``` - -따라서 `app` 번들의 두 버전을 담고 있는 remote 소스는 다음과 같습니다. - -```text -{remote_dir}/ -├── app/ -│ ├── app_1.0.0.wvb -│ └── app_1.1.0.wvb -└── manifest.json -``` - -파일 경로는 항상 `{source_dir}/{name}/{name}_{version}.wvb`입니다. builtin 소스와 remote 소스는 서로 다른 디렉터리이므로, 동일한 `app_1.1.0.wvb` 파일명이 양쪽에 모두 존재할 수 있으며 각각 별도의 매니페스트로 추적됩니다. - - - 번들 이름과 버전은 경로 구성 요소가 되므로 ASCII `[A-Za-z0-9._-]`로 제한됩니다. 빈 값일 수 없고, - `.`이나 `..`일 수 없으며, `.`으로 끝날 수 없고, Windows 예약 이름(`CON`, `PRN`, `AUX`, `NUL`, - `COM1`–`COM9`, `LPT1`–`LPT9`)일 수 없습니다. 그 외의 값은 파일 시스템에 닿기 전에 거부됩니다. - - -## 매니페스트 - -`manifest.json`은 소스 디렉터리의 루트에 위치합니다. 각 번들에 대해 존재하는 버전, 해당 메타데이터, 그리고 어느 버전이 현재 버전인지를 기록합니다. - -```json title="manifest.json" -{ - "manifestVersion": 1, - "entries": { - "app": { - "versions": { - "1.0.0": {}, - "1.1.0": {} - }, - "currentVersion": "1.0.0" - } - } -} -``` - -각 필드는 다음과 같습니다. - -| Field | Type | Description | -| -------------------------------- | ----------------- | ------------------------------------------- | -| `manifestVersion` | integer | 매니페스트 스키마 버전. 항상 `1`입니다. | -| `entries` | object | 번들 이름 → 엔트리의 맵입니다. | -| `entries.{name}.versions` | object | 버전 문자열 → 버전별 메타데이터의 맵입니다. | -| `entries.{name}.currentVersion` | string (optional) | 이 번들에 대해 제공되는 활성 버전입니다. | -| `entries.{name}.previousVersion` | string (optional) | 현재 버전 직전에 활성이었던 버전입니다. | - -각 버전의 메타데이터는 모든 필드가 선택 사항인 객체입니다. 빈 `{}`도 유효합니다. 메타데이터는 번들을 다운로드할 때 remote 서버의 응답 헤더에서만 채워지기 때문입니다. - -| Field | Type | Description | -| -------------- | ------ | ------------------------------------------------------------- | -| `etag` | string | 다운로드한 번들에 대한 서버의 `ETag` 값입니다. | -| `integrity` | string | 무결성 해시. `:` 형식입니다(예: `sha256:n4bQ…`). | -| `signature` | string | 무결성 문자열에 대한 Base64 서명입니다. | -| `lastModified` | string | 서버의 `Last-Modified` 값입니다. | - -### 현재 버전과 이전 버전 - -`currentVersion`은 소스가 지금 제공하는 버전입니다. 여러 버전이 스테이징되었지만 아직 활성화된 버전이 없을 때는 이 값이 없을 수 있습니다. 버전을 스테이징하면 `currentVersion`을 바꾸지 않은 채 `versions` 아래에 해당 버전이 기록됩니다. - -`previousVersion`은 가장 최근 활성화 직전에 현재 버전이었던 버전입니다. 소스는 두 가지 이유로 이 버전을 보관합니다. 첫째, 정상 동작이 확인된 빌드로 롤백할 수 있게 해줍니다. 둘째, 이미 해당 파일을 연 진행 중인 요청이 새 버전이 현재 버전이 되는 동안에도 읽기를 깔끔하게 마칠 수 있도록 이전 파일을 디스크에 유지합니다. 현재 버전과 이전 버전만 보관되며, 더 오래된 버전은 정리됩니다. - -## 채널 - -**채널**을 사용하면 동일한 서버에서 서로 다른 대상에게 서로 다른 버전을 전달할 수 있습니다. `stable`, `beta`, `canary` 등이 그 예입니다. 채널은 단계적 롤아웃과 사전 배포 테스트를 가능하게 합니다. - -채널은 순수하게 **remote**와 **updater**의 개념입니다. `.wvb` 포맷의 일부가 _아니며_ `manifest.json`에도 저장되지 _않습니다_. 클라이언트는 remote의 HTTP 호출에 `channel` 쿼리 파라미터를 실어 요청마다 채널을 선택합니다. - -```text -GET /bundles?channel=beta -HEAD /bundles/app?channel=beta -GET /bundles/app?channel=beta -``` - -채널은 선택 사항입니다. **하드코딩된 기본 채널 이름은 없습니다.** 채널이 설정되지 않으면 쿼리 파라미터가 그냥 생략되고, 서버가 무엇을 반환할지 결정합니다. 전체 HTTP 규약과 업데이터 흐름은 [원격 번들](/docs/guide/remote-bundles)을, 업데이터의 채널 설정은 [원격 설정](/docs/config/remote)을 참고하세요. - -## 소스가 제공되는 방식 - -소스는 웹뷰 요청에 응답하는 프로토콜 계층에 데이터를 공급합니다. 프로토콜은 들어오는 URI를 번들 이름과 파일 경로로 매핑하고, 소스에 현재 버전을 결정하도록 요청한 뒤, 일치하는 `.wvb` 안에서 파일을 제공합니다. URI가 번들 조회로 변환되는 방식은 [프로토콜 처리](/docs/guide/protocol-handling)를, 런타임이 builtin 및 remote 디렉터리를 가리키도록 설정하는 방법은 [wvb.config 파일](/docs/config)을 참고하세요. - -## 관련 페이지 - - - - - - - diff --git a/content/docs/guide/bundle-sources.mdx b/content/docs/guide/bundle-sources.mdx deleted file mode 100644 index 4b5fb36..0000000 --- a/content/docs/guide/bundle-sources.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Bundle sources -description: How Webview Bundle stores bundles on disk across builtin and remote sources, tracks them in a manifest, and selects versions per channel. ---- - -A **source** is a place on the device where bundles live. Each app has two of them: a read-only -`builtin` source shipped inside the app package, and a writable `remote` source for downloaded -updates. This page explains how a source is laid out, how the `manifest.json` tracks versions, and -how channels select which version a client receives. - -## Bundles, names, and versions - -A **bundle** is one logical web app, identified by a **bundle name** (for example `app`). A bundle -has many **versions** (`1.0.0`, `1.1.0`, …), and each version is a separate `.wvb` file. At any -moment one version is the **current** version — the one served to the webview. - -The bundle name and version are _source-level_ identifiers. They appear in the on-disk filename and -in the manifest, but they are **not** stored inside the `.wvb` archive itself. The archive only -carries the binary file-format version (`v1`). See [Bundle format](/docs/guide/bundle-format) for -the byte layout. - -## Builtin vs remote sources - -Apps run with two sources that serve the same bundle name from different storage: - -- **`builtin`** — bundles shipped _inside_ the app package. Read-only, and used as the fallback the - first time the app runs, before anything has been downloaded. -- **`remote`** — bundles downloaded from your update server. Writable, and updated over the air - (OTA) without an app-store release. - -When a bundle exists in both sources, **remote wins**. The source resolves a version by checking the -remote manifest's current version first, then falling back to builtin if the remote has nothing. - - - The two sources are independent directories. A source object holds a separate `builtinDir` and - `remoteDir`, and each directory has its own `manifest.json`. Resolving a version reads the remote - manifest first and the builtin manifest second. - - -This split is what makes updates safe. You always have the shipped builtin version to fall back to, -and downloaded remote versions transparently shadow it once they are verified and installed. See -[Remote bundles](/docs/guide/remote-bundles) for the download and verification flow. - -## On-disk layout - -Within a source directory, each bundle gets its own folder and the manifest sits at the root: - -```text -{source_dir}/ -├── {name}/ -│ └── {name}_{version}.wvb -└── manifest.json -``` - -So a remote source holding two versions of the `app` bundle looks like this: - -```text -{remote_dir}/ -├── app/ -│ ├── app_1.0.0.wvb -│ └── app_1.1.0.wvb -└── manifest.json -``` - -The file path is always `{source_dir}/{name}/{name}_{version}.wvb`. Because the builtin and remote -sources are separate directories, the same `app_1.1.0.wvb` filename can exist in both, tracked by -two separate manifests. - - - Bundle names and versions become path components, so they are restricted to ASCII - `[A-Za-z0-9._-]`. They cannot be empty, cannot be `.` or `..`, cannot end with `.`, and cannot be - a Windows reserved name (`CON`, `PRN`, `AUX`, `NUL`, `COM1`–`COM9`, `LPT1`–`LPT9`). Anything else - is rejected before it touches the filesystem. - - -## The manifest - -`manifest.json` lives at the root of a source directory. For each bundle it records the versions -present, their metadata, and which version is current: - -```json title="manifest.json" -{ - "manifestVersion": 1, - "entries": { - "app": { - "versions": { - "1.0.0": {}, - "1.1.0": {} - }, - "currentVersion": "1.0.0" - } - } -} -``` - -The fields are: - -| Field | Type | Description | -| -------------------------------- | ----------------- | ---------------------------------------------- | -| `manifestVersion` | integer | Manifest schema version. Always `1`. | -| `entries` | object | Map of bundle name → entry. | -| `entries.{name}.versions` | object | Map of version string → per-version metadata. | -| `entries.{name}.currentVersion` | string (optional) | The active version served for this bundle. | -| `entries.{name}.previousVersion` | string (optional) | The version active immediately before current. | - -Each version's metadata is an object whose fields are all optional. Empty `{}` is valid, since the -metadata is only filled in from the remote server's response headers when a bundle is downloaded: - -| Field | Type | Description | -| -------------- | ------ | ------------------------------------------------------------------------ | -| `etag` | string | The server's `ETag` for the downloaded bundle. | -| `integrity` | string | Integrity hash, formatted `:` (for example `sha256:n4bQ…`). | -| `signature` | string | Base64 signature over the integrity string. | -| `lastModified` | string | The server's `Last-Modified` value. | - -### Current and previous versions - -`currentVersion` is the version a source serves right now. It may be absent when versions have been -staged but none has been activated yet — staging a version records it under `versions` without -changing `currentVersion`. - -`previousVersion` is the version that was current immediately before the latest activation. The -source retains it for two reasons: it lets you roll back to a known-good build, and it keeps the old -file on disk so in-flight requests that already opened it finish reading cleanly while a new version -becomes current. Only the current and previous versions are retained; older versions are pruned. - -## Channels - -A **channel** lets you deliver different versions to different audiences from the same server — -`stable`, `beta`, `canary`, and so on. Channels power staged rollouts and pre-release testing. - -A channel is purely a **remote** and **updater** concept. It is _not_ part of the `.wvb` format and -_not_ stored in `manifest.json`. The client selects a channel per request by sending it as a -`channel` query parameter on the remote's HTTP calls: - -```text -GET /bundles?channel=beta -HEAD /bundles/app?channel=beta -GET /bundles/app?channel=beta -``` - -The channel is optional. There is **no hardcoded default channel name** — when no channel is set, -the query parameter is simply omitted and the server decides what to return. See -[Remote bundles](/docs/guide/remote-bundles) for the full HTTP contract and the updater flow, and -[Remote config](/docs/config/remote) for configuring the updater's channel. - -## How sources are served - -A source feeds the protocol layer that answers webview requests. The protocol maps an incoming URI -to a bundle name and file path, asks the source to resolve the current version, and serves the file -from inside the matching `.wvb`. See [Protocol handling](/docs/guide/protocol-handling) for how URIs -become bundle lookups, and [The wvb.config file](/docs/config) for pointing the runtime at your -builtin and remote directories. - -## Related pages - - - - - - - diff --git a/content/docs/guide/commands/meta.json b/content/docs/guide/commands/meta.json deleted file mode 100644 index 75ac8fb..0000000 --- a/content/docs/guide/commands/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Commands", - "pages": ["pack", "extract", "serve", "upload", "deploy", "download", "builtin", "remote"] -} diff --git a/content/docs/guide/commands/meta.ko.json b/content/docs/guide/commands/meta.ko.json deleted file mode 100644 index d40b31d..0000000 --- a/content/docs/guide/commands/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "명령어", - "pages": ["pack", "extract", "serve", "upload", "deploy", "download", "builtin", "remote"] -} diff --git a/content/docs/guide/core-concepts/bundle-source.ko.mdx b/content/docs/guide/core-concepts/bundle-source.ko.mdx new file mode 100644 index 0000000..275ba20 --- /dev/null +++ b/content/docs/guide/core-concepts/bundle-source.ko.mdx @@ -0,0 +1,89 @@ +--- +title: 번들 소스 +--- + +**소스**는 번들이 저장되는 기기 내의 위치입니다. 두 가지 유형의 소스가 있습니다. 하나는 앱 패키지 안에 포함되어 배포되는 읽기 전용 빌트인(`builtin`) 소스이고, 다른 하나는 리모트로부터 원격으로 업데이트하는 리모트(`remote`) 소스입니다. + +## 빌트인 vs 리모트 + +웹뷰 번들은 빌트인(`builtin`)과 리모트(`remote`) 번들 소스를 나눕니다. + +- **`builtin`** — 앱 패키지 _안에_ 포함되어 배포되는 번들입니다. 읽기 전용이며, 앱을 처음 실행할 때 아직 아무것도 다운로드되지 않은 상태에서 폴백으로 사용됩니다. +- **`remote`** — 리모트 서버에서 다운로드한 번들입니다. 쓰기 가능하며, 앱스토어 릴리스 없이 원격으로 업데이트됩니다. + +번들이 두 소스에 모두 존재하면 **리모트가 우선합니다**. 소스는 리모트 매니페스트의 현재 버전을 먼저 확인하여 버전을 결정하며, 리모트에 없으면 빌트인으로 폴백합니다. + +--- + +이러한 분리 덕분에 업데이트가 안전해집니다. 폴백할 수 있는 배포된 빌트인 버전이 항상 존재하며, 다운로드된 리모트 버전은 검증과 설치를 마치면 자연스럽게 빌트인 버전을 대체합니다. 다운로드 및 검증 흐름은 [Over-The-Air](/docs/guide/core-concepts/over-the-air)를 참고하세요. + +## 디스크 레이아웃 + +소스 디렉터리 안에서 각 번들은 자체 폴더를 가지며, 매니페스트는 루트에 위치합니다. + +```text +{source_dir}/ +├── {name}/ +│ └── {name}_{version}.wvb +└── manifest.json +``` + +예를들어, `app` 번들이 `1.0.0`, `1.1.0` 두 버전이 설치되어 있을 때 디스크 레이아웃은 다음과 같습니다. + +```text +{source_dir}/ +├── app/ +│ ├── app_1.0.0.wvb +│ └── app_1.1.0.wvb +└── manifest.json +``` + +파일 경로는 항상 `{source_dir}/{name}/{name}_{version}.wvb`입니다. 빌트인 번들과 리모트 번들은 서로 다른 소스이므로, 각각 별도의 매니페스트로 추적됩니다. + + + 번들 이름과 버전은 경로 구성 요소가 되므로 ASCII `[A-Za-z0-9._-]`로 제한됩니다. + + +## 매니페스트 + +`manifest.json`은 소스 디렉터리의 루트에 위치합니다. 각 번들에 대해 존재하는 버전, 해당 메타데이터, 그리고 어느 버전이 현재 버전인지를 기록합니다. + +```json title="manifest.json" +{ + "manifestVersion": 1, + "entries": { + "app": { + "versions": { + "1.0.0": {}, + "1.1.0": {} + }, + "currentVersion": "1.0.0" + } + } +} +``` + +각 필드는 다음과 같습니다. + +| Field | Type | Description | +| -------------------------------- | ----------------- | ---------------------------------------- | +| `manifestVersion` | integer | 매니페스트 스키마 버전. 항상 `1`입니다. | +| `entries` | object | 번들 엔트리의 맵 | +| `entries.{name}.versions` | object | 버전별 메타데이터의 맵 | +| `entries.{name}.currentVersion` | string (optional) | 이 번들에 대해 제공되는 활성 버전입니다. | +| `entries.{name}.previousVersion` | string (optional) | 현재 버전 직전에 활성이었던 버전입니다. | + +각 버전의 메타데이터는 모든 필드가 선택 사항인 객체입니다. 일반적으로 메타데이터는 번들을 다운로드할 때 리모트 서버의 응답 헤더에서 채워집니다. + +| Field | Type | Description | +| -------------- | ------ | ------------------------------------------------------------- | +| `etag` | string | 다운로드한 번들에 대한 서버의 `ETag` 값입니다. | +| `integrity` | string | 무결성 해시. `:` 형식입니다(예: `sha256:n4bQ…`). | +| `signature` | string | 무결성 문자열에 대한 Base64 서명입니다. | +| `lastModified` | string | 서버의 `Last-Modified` 값입니다. | + +### 현재 버전과 이전 버전 + +`currentVersion`은 소스가 지금 제공하는 버전입니다. 여러 버전이 스테이징되었지만 아직 활성화된 버전이 없을 때는 이 값이 없을 수 있습니다. 버전을 스테이징하면 `currentVersion`을 바꾸지 않은 채 `versions` 아래에 해당 버전이 기록됩니다. + +`previousVersion`은 가장 최근 활성화 직전에 현재 버전이었던 버전입니다. 소스는 두 가지 이유로 이 버전을 보관합니다. 첫째, 정상 동작이 확인된 빌드로 롤백할 수 있게 해줍니다. 둘째, 이미 해당 파일을 연 진행 중인 요청이 새 버전이 현재 버전이 되는 동안에도 읽기를 깔끔하게 마칠 수 있도록 이전 파일을 디스크에 유지합니다. 현재 버전과 이전 버전만 보관되며, 더 오래된 버전은 정리됩니다. diff --git a/content/docs/guide/core-concepts/bundle-source.mdx b/content/docs/guide/core-concepts/bundle-source.mdx new file mode 100644 index 0000000..8960c35 --- /dev/null +++ b/content/docs/guide/core-concepts/bundle-source.mdx @@ -0,0 +1,90 @@ +--- +title: Bundle source +--- + +A **source** is the location on the device where bundles are stored. There are two types of source. One is the read-only builtin (`builtin`) source shipped inside the app package, and the other is the remote (`remote`) source that updates over the air from a remote server. + +## Builtin vs remote + +Webview Bundle splits bundle sources into builtin (`builtin`) and remote (`remote`). + +- **`builtin`** — a bundle shipped _inside_ the app package. It is read-only and serves as the fallback on first launch, before anything has been downloaded. +- **`remote`** — a bundle downloaded from a remote server. It is writable and updates over the air without an app store release. + +When a bundle exists in both sources, **remote wins**. A source determines the version by checking the current version in the remote manifest first, and falls back to builtin when the bundle is not in remote. + +--- + +This separation makes updates safe. A shipped builtin version is always available to fall back to, and a downloaded remote version naturally replaces the builtin version once it passes verification and installs. See [Over-The-Air](/docs/guide/core-concepts/over-the-air) for the download and verification flow. + +## Disk layout + +Inside the source directory, each bundle has its own folder, and the manifest lives at the root. + +```text +{source_dir}/ +├── {name}/ +│ └── {name}_{version}.wvb +└── manifest.json +``` + +For example, when the `app` bundle has two versions installed, `1.0.0` and `1.1.0`, the disk layout looks like this. + +```text +{source_dir}/ +├── app/ +│ ├── app_1.0.0.wvb +│ └── app_1.1.0.wvb +└── manifest.json +``` + +The file path is always `{source_dir}/{name}/{name}_{version}.wvb`. The builtin bundle and the remote bundle are separate sources, so each is tracked by its own manifest. + + + Bundle names and versions become path components, so they are restricted to ASCII + `[A-Za-z0-9._-]`. + + +## Manifest + +`manifest.json` lives at the root of the source directory. For each bundle, it records the versions that exist, their metadata, and which version is current. + +```json title="manifest.json" +{ + "manifestVersion": 1, + "entries": { + "app": { + "versions": { + "1.0.0": {}, + "1.1.0": {} + }, + "currentVersion": "1.0.0" + } + } +} +``` + +The fields are as follows. + +| Field | Type | Description | +| -------------------------------- | ----------------- | -------------------------------------------------------- | +| `manifestVersion` | integer | Manifest schema version. Always `1`. | +| `entries` | object | Map of bundle entries | +| `entries.{name}.versions` | object | Map of per-version metadata | +| `entries.{name}.currentVersion` | string (optional) | The active version served for this bundle. | +| `entries.{name}.previousVersion` | string (optional) | The version that was active just before the current one. | + +Each version's metadata is an object whose fields are all optional. The metadata is usually populated from the remote server's response headers when the bundle is downloaded. + +| Field | Type | Description | +| -------------- | ------ | --------------------------------------------------------------- | +| `etag` | string | The server's `ETag` value for the downloaded bundle. | +| `integrity` | string | Integrity hash, in `:` form (e.g. `sha256:n4bQ…`). | +| `signature` | string | Base64 signature over the integrity string. | +| `lastModified` | string | The server's `Last-Modified` value. | + +### Current version and previous version + +`currentVersion` is the version the source serves right now. It may be absent when several versions are staged but none is active yet. Staging a version records it under `versions` without changing `currentVersion`. + +`previousVersion` is the version that was current just before the most recent activation. A source keeps it for two reasons. First, it lets you roll back to a build confirmed to work. Second, it keeps the previous file on disk so an in-flight request that already opened that file can finish reading cleanly while the new version becomes current. Only the current and previous versions are kept; older versions are cleaned up. diff --git a/content/docs/guide/core-concepts/communicate-between-native-and-webview.ko.mdx b/content/docs/guide/core-concepts/communicate-between-native-and-webview.ko.mdx new file mode 100644 index 0000000..d994696 --- /dev/null +++ b/content/docs/guide/core-concepts/communicate-between-native-and-webview.ko.mdx @@ -0,0 +1,18 @@ +--- +title: 네이티브와 웹뷰 간 통신 +--- + +웹뷰의 웹 코드에서 네이티브 기능을 사용하려면 네이티브와 통신해야 합니다. 웹뷰 번들은 이를 위해 브릿지를 제공합니다. + +## 브릿지 호출하기 + +웹뷰에서는 통합 브릿지 패키지 `@wvb/bridge`의 `invoke(name, params)`로 네이티브 기능을 호출합니다. + +호출은 네이티브 단에 등록된 핸들러가 처리하고 결과를 돌려줍니다. 플랫폼/프레임워크별 전달 방식은 다음과 같습니다. + +- **Electron** — Electron IPC +- **Tauri** — Tauri 커맨드 +- **Android** — WebView 메시지 리스너(`WebViewCompat.addWebMessageListener`) +- **iOS** — `WKScriptMessageHandler` + +브릿지 API를 사용하는 자세한 방법은 [브릿지](/docs/guide/frontend/bridges) 가이드를 참고하세요. diff --git a/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx b/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx new file mode 100644 index 0000000..641e69b --- /dev/null +++ b/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx @@ -0,0 +1,18 @@ +--- +title: Communicate between native and webview +--- + +To use native features from web code in the webview, you communicate with the native side. Webview Bundle provides a bridge for this. + +## Call the bridge + +In the webview, call native features with `invoke(name, params)` from the unified bridge package `@wvb/bridge`. + +A handler registered on the native side processes the call and returns a result. The delivery mechanism per platform and framework is as follows. + +- **Electron** — Electron IPC +- **Tauri** — Tauri command +- **Android** — WebView message listener (`WebViewCompat.addWebMessageListener`) +- **iOS** — `WKScriptMessageHandler` + +For details on using the bridge API, see the [Bridges](/docs/guide/frontend/bridges) guide. diff --git a/content/docs/guide/core-concepts/glue-to-native.ko.mdx b/content/docs/guide/core-concepts/glue-to-native.ko.mdx new file mode 100644 index 0000000..56f3888 --- /dev/null +++ b/content/docs/guide/core-concepts/glue-to-native.ko.mdx @@ -0,0 +1,51 @@ +--- +title: 네이티브 연동 +--- + +웹뷰 번들은 특정 네이티브 플랫폼이나 프레임워크에 종속되지 않도록 설계되었습니다. 익숙한 웹 애플리케이션 코드를 한 번만 작성하면, 하나의 코어를 통해 여러 플랫폼과 프레임워크에 동일하게 연동할 수 있습니다. + +## Rust로 작성된 코어 + +코어 크레이트(`wvb`)는 Rust로 작성되어 멀티 플랫폼 환경에서 동작하게 설계하였습니다. + +코어 크레이트에서 제공되는 기능은 다음과 같습니다. + +- `.wvb` 번들 포맷 읽기·쓰기 +- 번들 소스 관리(빌트인·리모트) +- 리모트에서 번들 다운로드 +- 원격 업데이터 +- 프로토콜 핸들링(번들을 커스텀 스킴으로 제공하며 `GET`·`HEAD`와 범위 요청 지원) +- 무결성·서명 검증 + +## 멀티 플랫폼/환경 지원 + +여러 플랫폼 및 환경을 지원하기 위해 `wvb` 크레이트는 바인딩되어 제공됩니다. 바인딩은 코어 크레이트인 `wvb`의 API를 그대로 노출하여 서로 다른 플랫폼/환경에서도 동일한 API를 사용할 수 있습니다. + +- **Node.js** +- [napi.rs](https://napi.rs/)를 이용해 Rust 코드를 [N-API](https://nodejs.org/api/n-api.html)를 바인딩한 네이티브 에드온을 사용합니다. +- 사용하는 N-API 버전은 8으로 호환가능한 Node.js 버전은 [Node.js 문서](https://nodejs.org/api/n-api.html#node-api-version-matrix)를 참고해주세요. +- **Android/iOS** +- [uniffi](https://github.com/mozilla/uniffi-rs)를 이용해 Rust 코드를 Kotlin/Swift 바인딩을 만듭니다. +- 만들어진 바인딩은 각각 [Android](https://github.com/webview-bundle/webview-bundle-android), [iOS](https://github.com/webview-bundle/webview-bundle-ios) 라이브러리에 포함돼 퍼블리싱 됩니다. +- **Deno** +- [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API를 이용해 동적으로 라이브러리를 불러와 사용합니다. + +자세한 API 레퍼런스는 [여기서](/docs/references) 확인해주세요. + +## 네이티브에 붙이기 + +코어는 웹뷰의 네트워크 요청을 가로채(intercept) 번들에서 리소스를 읽어 응답합니다. 예를 들어 Android에서는 `WebViewClient.shouldInterceptRequest`를 재정의해 요청을 코어의 프로토콜 핸들러로 넘깁니다. + +```kotlin +class WebViewBundleClient( + private val owner: WebViewBundle, +) : WebViewClient() { + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest, + ): WebResourceResponse? = + owner.handleRequest(request) +} +``` + +각 플랫폼/프레임워크의 통합 패키지가 이 연동을 대신 처리하므로, 대부분 직접 인터셉트 코드를 작성할 필요는 없습니다. 자세한 방법은 **네이티브** 섹션을 참고하세요. diff --git a/content/docs/guide/core-concepts/glue-to-native.mdx b/content/docs/guide/core-concepts/glue-to-native.mdx new file mode 100644 index 0000000..14261cc --- /dev/null +++ b/content/docs/guide/core-concepts/glue-to-native.mdx @@ -0,0 +1,51 @@ +--- +title: Glue to native +--- + +Webview Bundle is designed to avoid being tied to any specific native platform or framework. Write your familiar web application code once, and a single core lets you integrate it the same way across multiple platforms and frameworks. + +## A core written in Rust + +The core crate (`wvb`) is written in Rust and designed to run across multiple platforms. + +The core crate provides the following: + +- Read and write the `.wvb` bundle format +- Manage bundle sources (builtin and remote) +- Download bundles from a remote +- Remote updater +- Protocol handling (serve bundles over a custom scheme, with `GET`, `HEAD`, and range request support) +- Integrity and signature verification + +## Multi-platform and multi-environment support + +To support multiple platforms and environments, the `wvb` crate ships with bindings. Each binding exposes the API of the `wvb` core crate as-is, so you use the same API across different platforms and environments. + +- **Node.js** + - Uses a native addon built with [napi.rs](https://napi.rs/) that binds the Rust code to [N-API](https://nodejs.org/api/n-api.html). + - The N-API version is 8; for compatible Node.js versions, see the [Node.js docs](https://nodejs.org/api/n-api.html#node-api-version-matrix). +- **Android/iOS** + - Uses [uniffi](https://github.com/mozilla/uniffi-rs) to generate Kotlin/Swift bindings from the Rust code. + - The generated bindings are published as part of the [Android](https://github.com/webview-bundle/webview-bundle-android) and [iOS](https://github.com/webview-bundle/webview-bundle-ios) libraries, respectively. +- **Deno** + - Uses the [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API to load the library dynamically. + +See the full API reference [here](/docs/references). + +## Glue to native + +The core intercepts the webview's network requests, reads resources from the bundle, and responds. On Android, for example, you override `WebViewClient.shouldInterceptRequest` to hand the request to the core's protocol handler. + +```kotlin +class WebViewBundleClient( + private val owner: WebViewBundle, +) : WebViewClient() { + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest, + ): WebResourceResponse? = + owner.handleRequest(request) +} +``` + +The integration package for each platform and framework handles this glue for you, so in most cases you never write intercept code yourself. See the **Native** section for details. diff --git a/content/docs/guide/core-concepts/over-the-air.ko.mdx b/content/docs/guide/core-concepts/over-the-air.ko.mdx new file mode 100644 index 0000000..5ace0eb --- /dev/null +++ b/content/docs/guide/core-concepts/over-the-air.ko.mdx @@ -0,0 +1,109 @@ +--- +title: Over-The-Air +--- + +신규 웹뷰 번들을 원격(Over-The-Air)에서 다운로드할 수 있습니다. 네이티브 앱 배포없이도 업데이트된 앱 코드를 웹뷰 번들 리모트 서버를 통해 배포할 수 있습니다. + +## 업데이트 라이프사이클 + +앱 코드를 빌드하여 웹뷰 번들 포맷으로 패키징한 이후 기기에 다운로드되기까지 다섯 단계를 거칩니다. + +1. 빌드 결과물을 `.wvb` 포맷으로 패킹합니다. +2. 패킹된 웹뷰 번들 파일을 리모트 서버로 업로드합니다. 무결성 해시와 서명을 추가할 수 있습니다. +3. 업로드한 버전을 배포하면 클라이언트가 업데이트 정보를 불러올 수 있습니다. +4. 클라이언트에서 새로운 버전을 확인한 뒤, 다운로드 합니다. +5. 다운로드된 데이터를 검증하고, 이를 리모트 소스에 설치합니다. + +1~3 단계는 CLI 명령어로 실행할 수 있습니다. + +```sh +wvb pack # `.wvb` 파일 패키징 +wvb upload --version 1.2.0 # 리모트 서버로 1.2.0 버전 업로드 +wvb deploy --version 1.2.0 # 1.2.0을 현재 버전으로 배포 +``` + +또는, 이용중인 플랫폼/프레임워크에서 더 편한 방식으로 연동할 수 있습니다. 이는 **네이티브** 섹션에서 가이드를 확인해주세요. + +## 클라이언트에서 업데이트 + +클라이언트에서 리모트 번들을 다운받아 리모트 [번들 소스](/docs/guide/core-concepts/bundle-source)에 설치하는 과정을 제어할 수 있습니다. 사용할 수 있는 API는 다음과 같습니다. + +- **updater.getUpdate** : 리모트의 현재 버전 정보를 조회(HEAD 요청)해 설치된 버전과 비교합니다. 다운로드는 하지 않으며, 업데이트 가능 여부와 버전 정보를 반환합니다. +- **updater.download** : 새 버전의 번들을 내려받아 검증한 뒤 디스크에 준비합니다. 이 단계에서는 아직 활성화되지 않습니다. +- **updater.install** : 내려받아 둔 버전을 현재 버전으로 활성화합니다. 설치한 뒤부터 해당 버전이 제공됩니다. + +다운로드와 설치 과정이 분리되어 있어서, 앱의 비즈니스 요구사항에 맞춰 원하는 타이밍에 신규 번들을 반영할 수 있습니다. + +--- + +Rust 코어 크레이트에서 API를 활용하는 방법입니다. + +```rust +use std::sync::Arc; +use wvb::updater::Updater; + +let updater = Updater::new(source, remote, None); + +let update = updater.get_update("app").await?; +if update.is_available { + let info = updater.download("app", None).await?; + updater.install("app", info.version).await?; + // 이후 새 버전으로 번들 사용 가능 +} +``` + +각 플랫폼/프레임워크에서 업데이트 API를 사용하는 방법은 가이드 문서에서 확인해주세요. + +### 무결성 + +무결성 해시를 사용하면 클라이언트가 다운로드한 번들이 리모트에서 배포된 번들과 데이터가 일치하는지 확인할 수 있습니다. 지원하는 알고리즘(SHA-2)은 `sha256`(기본값), `sha384`, `sha512`이며, `":"` 형식으로 직렬화됩니다. + +```text +sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= +``` + +클라이언트는 다운로드한 번들 데이터로 해시를 다시 계산해 리모트가 알려 준 무결성 해시와 일치하는지 확인합니다. 검증은 설치 전에 수행하며, 일치하지 않으면 설치가 실패합니다. 검증 정책은 세 가지입니다. + +- `strict` : 무결성 해시가 반드시 있어야 하며, 없거나 일치하지 않으면 실패합니다. +- `optional`(기본값) : 해시가 있으면 검증하고, 없으면 건너뜁니다. +- `none` : 무결성을 검증하지 않습니다. + +### 서명 + +서명은 번들이 개인 키 소유자가 배포한 것임을 증명합니다. 무결성 해시는 데이터가 특정 해시와 일치하는지만 보장하지만, 서명은 배포자의 진위까지 확인합니다. 서명은 무결성 문자열에 대해 생성되므로, 서명을 사용하려면 무결성을 함께 활성화해야 합니다. + +지원하는 알고리즘은 다음과 같습니다. + +- ECDSA (P-256, P-384) +- Ed25519 +- RSA (PKCS#1 v1.5, PSS) + +서명 검증은 선택 사항입니다. 클라이언트에 공개 키로 서명 검증기를 설정하면, 다운로드한 번들의 서명을 무결성 문자열과 대조해 확인하고, 검증에 실패하면 설치를 중단합니다. + +## 리모트 + +리모트는 클라이언트가 번들과 메타데이터를 내려받는 HTTP 서버입니다. 엔드포인트 URL로 구성하며, 번들 목록 조회, 현재 버전 메타데이터 조회(HEAD), 현재 버전 및 특정 버전 다운로드를 제공합니다. 버전·무결성·서명 값은 응답 헤더로 전달됩니다. + +### 제공자 + +번들 서버를 직접 운영하지 않아도 되도록 기본 제공자를 제공합니다. + +- **AWS** — `@wvb/remote-aws` +- **Cloudflare** — `@wvb/remote-cloudflare` + +리모트 HTTP 규약을 구현하면 직접 제공자를 만들 수도 있습니다. 자세한 내용은 [리모트](/docs/guide/remote) 문서를 참고하세요. + +### 채널 + +채널은 배포를 구분하는 이름표입니다(예: `stable`, `beta`). 클라이언트는 지정한 채널의 현재 버전을 받으며, 채널을 지정하지 않으면 기본 배포를 받습니다. + +Rust에서는 `UpdaterConfig`에 채널을 지정합니다. + +```rust +use wvb::updater::{Updater, UpdaterConfig}; + +let config = UpdaterConfig::new().channel("beta"); +let updater = Updater::new(source, remote, Some(config)); +``` + +채널은 리모트 요청에 `?channel=beta` 쿼리 파라미터로 전달됩니다. diff --git a/content/docs/guide/core-concepts/over-the-air.mdx b/content/docs/guide/core-concepts/over-the-air.mdx new file mode 100644 index 0000000..7a1b4e0 --- /dev/null +++ b/content/docs/guide/core-concepts/over-the-air.mdx @@ -0,0 +1,109 @@ +--- +title: Over-the-air +--- + +Download new webview bundles remotely. Ship updated app code through the Webview Bundle remote server without redeploying the native app. + +## Update lifecycle + +After you build your app code and package it into the Webview Bundle format, it goes through five steps before it reaches the device. + +1. Pack the build output into the `.wvb` format. +2. Upload the packed webview bundle file to the remote server. You can attach an integrity hash and a signature. +3. Deploy the uploaded version so clients can fetch its update info. +4. The client checks for a new version, then downloads it. +5. Verify the downloaded data, then install it into the remote source. + +Steps 1 through 3 run as CLI commands. + +```sh +wvb pack # Package the `.wvb` file +wvb upload --version 1.2.0 # Upload version 1.2.0 to the remote server +wvb deploy --version 1.2.0 # Deploy 1.2.0 as the current version +``` + +Or integrate it in a more convenient way for the platform/framework you use. See the guide in the **Native** section. + +## Updating on the client + +Control how the client downloads the remote bundle and installs it into the remote [bundle source](/docs/guide/core-concepts/bundle-source). The available APIs are: + +- **updater.getUpdate** : Queries the remote's current version info (a HEAD request) and compares it against the installed version. It does not download anything; it returns whether an update is available along with the version info. +- **updater.download** : Downloads the new version's bundle, verifies it, then stages it on disk. It is not activated at this step yet. +- **updater.install** : Activates the downloaded version as the current version. That version is served from installation onward. + +Because download and install are separate, you can apply a new bundle at whatever timing fits your app's business requirements. + +--- + +Here is how to use the API from the Rust core crate. + +```rust +use std::sync::Arc; +use wvb::updater::Updater; + +let updater = Updater::new(source, remote, None); + +let update = updater.get_update("app").await?; +if update.is_available { + let info = updater.download("app", None).await?; + updater.install("app", info.version).await?; + // The new version bundle is available from here on +} +``` + +See the guide docs for how to use the update API on each platform/framework. + +### Integrity + +An integrity hash lets the client confirm that the bundle it downloaded matches the data of the bundle deployed on the remote. The supported algorithms (SHA-2) are `sha256` (default), `sha384`, and `sha512`, serialized in the `":"` format. + +```text +sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= +``` + +The client recomputes the hash from the downloaded bundle data and checks that it matches the integrity hash the remote reported. Verification runs before installation, and installation fails if they do not match. There are three verification policies. + +- `strict` : An integrity hash must be present; it fails if the hash is missing or does not match. +- `optional` (default) : Verifies the hash if present, and skips it if absent. +- `none` : Does not verify integrity. + +### Signature + +A signature proves that the bundle was deployed by the holder of the private key. An integrity hash only guarantees that the data matches a specific hash, but a signature also confirms the authenticity of the deployer. The signature is generated over the integrity string, so you must enable integrity together to use signatures. + +The supported algorithms are: + +- ECDSA (P-256, P-384) +- Ed25519 +- RSA (PKCS#1 v1.5, PSS) + +Signature verification is optional. Configure a signature verifier with a public key on the client, and it checks the downloaded bundle's signature against the integrity string, aborting installation if verification fails. + +## Remote + +The remote is an HTTP server from which clients download bundles and metadata. Configure it with an endpoint URL. It provides listing bundles, querying current version metadata (HEAD), and downloading the current version and specific versions. Version, integrity, and signature values are passed in the response headers. + +### Providers + +Builtin providers let you skip running a bundle server yourself. + +- **AWS** — `@wvb/remote-aws` +- **Cloudflare** — `@wvb/remote-cloudflare` + +You can also build your own provider by implementing the remote HTTP protocol. See the [Remote](/docs/guide/remote) docs for details. + +### Channels + +A channel is a label that distinguishes deployments (for example, `stable`, `beta`). The client receives the current version of the channel it specifies, and receives the default deployment when it specifies no channel. + +In Rust, you specify the channel on `UpdaterConfig`. + +```rust +use wvb::updater::{Updater, UpdaterConfig}; + +let config = UpdaterConfig::new().channel("beta"); +let updater = Updater::new(source, remote, Some(config)); +``` + +The channel is passed to the remote request as the `?channel=beta` query parameter. diff --git a/content/docs/guide/core-concepts/protocol-handling.ko.mdx b/content/docs/guide/core-concepts/protocol-handling.ko.mdx new file mode 100644 index 0000000..c9c7b98 --- /dev/null +++ b/content/docs/guide/core-concepts/protocol-handling.ko.mdx @@ -0,0 +1,93 @@ +--- +title: 프로토콜 핸들링 +--- + +웹뷰 번들은 HTTP 요청에 대해 번들 내 적절한 리소스를 응답하는 프로토콜 핸들러 기능을 제공합니다. 각 플랫폼/프레임워크에서 웹뷰 요청에 대한 응답을, 이 프로토콜 핸들러를 이용하면 웹뷰 번들 내 리소스를 표준 HTTP 응답으로 반환할 수 있습니다. + +## 번들 프로토콜 + +번들 프로토콜은 요청 URI에 매핑되는 웹뷰 번들로부터 요청 경로에 해당하는 파일을 불러와 HTTP 응답합니다. + +예를들어, `app://app.wvb/index.html` 요청 URI에 대해서 찾을 번들명은 `app`, 번들 내에서 불러올 파일경로는 `/index.html`로 해석하여 HTTP 응답을 프로토콜 핸들러에서 반환하게 됩니다. + +```text +app://app.wvb/index.html -> bundle "app", file "/index.html" +app://myapp.wvb/assets/logo.png -> bundle "myapp", file "/assets/logo.png" +``` + +경로 해석은 정적 파일 서버처럼 `index.html`을 채워 넣습니다. 끝에 슬래시가 있거나, 경로의 마지막 세그먼트에 `.`이 없는 경우 `index.html`을 채워 넣어 번들에서 파일을 찾습니다. + +```text +/ -> /index.html # 끝에 슬래시가 있는 경우 +/about -> /about/index.html # 마지막 세그먼트에 "."이 없는 경우 +/a.js -> /a.js # 마지막 세그먼트에 "."이 있으면 그대로 제공 +``` + +번들을 못찾은 경우에는 `BundleNotFound` 오류로 핸들러가 실패하고, 500(Internal Server Error)을 응답하게 됩니다. + +만약, 번들은 존재하나 번들 내에서 파일을 못찾은 경우 404(Not Found)를 응답합니다. + +### 메서드, 헤더, 응답 + +`GET`과 `HEAD`만 제공하며, 그 외의 메서드는 405(Method Not Allowed)를 반환합니다. `HEAD`는 본문이 비어 있는 헤더를 반환합니다. + +파일을 제공할 때 핸들러는 인덱스 엔트리에 기록된 `Content-Type`과 `Content-Length` 헤더값을 기본으로 추가합니다. 그 외, 사용자가 임의로 추가한 헤더값도 응답에 포함됩니다. + +```text +GET app://app.wvb/assets/app.js +-> 200 OK + cache-control: public, max-age=31536000 # 인덱스 엔트리에 사용자 임의로 지정한 값 + content-type: application/javascript # 인덱스 엔트리에 기록된 값 + content-length: 84213 # 압축 해제된 크기 +``` + +### 범위 요청 + +웹뷰는 미디어 탐색과 대용량 다운로드 재개를 위해 범위 요청에 의존합니다. `Range` 헤더가 있으면 `Accept-Ranges: bytes`와 `Content-Range`를 담아 206(Partial Content)를 반환합니다. + +```text +GET app://app.wvb/media/clip.mp4 +Range: bytes=0-1023 +-> 206 Partial Content + accept-ranges: bytes + content-range: bytes 0-1023/5242880 + content-length: 1024 +``` + +- 반환되는 각 범위는 `1000 * 1024` 바이트(약 1 MB)로 제한되며, 이보다 큰 요청 범위는 잘립니다. +- 여러 범위는 `multipart/byteranges` 응답으로 반환됩니다. +- 충족할 수 없는 범위는 `Content-Range: bytes */`와 함께 416(Range Not Satisfiable)를 반환합니다. + +```text +Range: bytes=99999999- +-> 416 Range Not Satisfiable + content-range: bytes */5242880 +``` + +범위는 압축 해제된 콘텐츠를 기준으로 계산됩니다. 따라서 오프셋은 빌드가 생성한 파일과 일치합니다. + +## 로컬 프로토콜 + +웹뷰 번들 파일(`.wvb`)에서 리소스를 가져오는 대신, 로컬 프로토콜을 사용하면 localhost로부터 프록시로 리소스를 불러올 수 있습니다. + +이는 개발 단계에서 유용합니다. 예를들어, [Vite](https://vite.dev/) 개발 서버 (예: `http://localhost:5173`)로부터 핫 리로드를 동작시키고 싶다면 Electron에서 아래와 같이 설정할 수 있습니다. + +```ts title="main.ts" +import { localProtocol, wvb } from '@wvb/electron'; + +const instance = wvb({ + protocols: [ + localProtocol('myscheme', { + hosts: { + 'myapp.wvb': 'http://localhost:5173', + }, + }), + ], +}); +``` + +이후, `myscheme://myapp.wvb` URL로 웹뷰를 로드하여 사용할 수 있습니다. + +--- + +각 플랫폼/프레임워크에서 프로토콜을 사용하는 자세한 방법은 "네이티브" 섹션에서 확인해주세요. diff --git a/content/docs/guide/core-concepts/protocol-handling.mdx b/content/docs/guide/core-concepts/protocol-handling.mdx new file mode 100644 index 0000000..f7022ab --- /dev/null +++ b/content/docs/guide/core-concepts/protocol-handling.mdx @@ -0,0 +1,93 @@ +--- +title: Handling protocols +--- + +Webview Bundle provides a protocol handler that responds to HTTP requests with the matching resource inside a bundle. On each platform and framework, this protocol handler serves webview requests by returning resources from a Webview Bundle as standard HTTP responses. + +## Bundle protocol + +The bundle protocol loads the file at the requested path from the Webview Bundle mapped to the request URI, then returns it as an HTTP response. + +For example, given the request URI `app://app.wvb/index.html`, the handler resolves the bundle name to `app` and the file path within the bundle to `/index.html`, then returns the HTTP response. + +```text +app://app.wvb/index.html -> bundle "app", file "/index.html" +app://myapp.wvb/assets/logo.png -> bundle "myapp", file "/assets/logo.png" +``` + +Path resolution fills in `index.html` like a static file server. When the path ends with a slash, or the last segment has no `.`, the handler fills in `index.html` and looks up that file in the bundle. + +```text +/ -> /index.html # path ends with a slash +/about -> /about/index.html # last segment has no "." +/a.js -> /a.js # last segment has a ".", so it is served as-is +``` + +When the bundle is not found, the handler fails with a `BundleNotFound` error and responds with 500 (Internal Server Error). + +When the bundle exists but the file is not found inside it, the handler responds with 404 (Not Found). + +### Methods, headers, responses + +Only `GET` and `HEAD` are served; any other method returns 405 (Method Not Allowed). `HEAD` returns the headers with an empty body. + +When serving a file, the handler adds the `Content-Type` and `Content-Length` header values recorded in the index entry by default. Any other headers you added are also included in the response. + +```text +GET app://app.wvb/assets/app.js +-> 200 OK + cache-control: public, max-age=31536000 # value you set on the index entry + content-type: application/javascript # value recorded in the index entry + content-length: 84213 # decompressed size +``` + +### Range requests + +Webviews rely on range requests to seek media and resume large downloads. When the `Range` header is present, the handler returns 206 (Partial Content) with `Accept-Ranges: bytes` and `Content-Range`. + +```text +GET app://app.wvb/media/clip.mp4 +Range: bytes=0-1023 +-> 206 Partial Content + accept-ranges: bytes + content-range: bytes 0-1023/5242880 + content-length: 1024 +``` + +- Each returned range is capped at `1000 * 1024` bytes (about 1 MB); a requested range larger than this is truncated. +- Multiple ranges are returned as a `multipart/byteranges` response. +- An unsatisfiable range returns 416 (Range Not Satisfiable) with `Content-Range: bytes */`. + +```text +Range: bytes=99999999- +-> 416 Range Not Satisfiable + content-range: bytes */5242880 +``` + +Ranges are computed against the decompressed content. Offsets therefore match the files the build produced. + +## Local protocol + +Instead of fetching resources from a Webview Bundle file (`.wvb`), the local protocol lets you proxy resources from localhost. + +This is useful during development. For example, to run hot reload from a [Vite](https://vite.dev/) dev server (such as `http://localhost:5173`), configure Electron as follows. + +```ts title="main.ts" +import { localProtocol, wvb } from '@wvb/electron'; + +const instance = wvb({ + protocols: [ + localProtocol('myscheme', { + hosts: { + 'myapp.wvb': 'http://localhost:5173', + }, + }), + ], +}); +``` + +You can then load the webview from the `myscheme://myapp.wvb` URL. + +--- + +For details on using protocols on each platform and framework, see the "Native" section. diff --git a/content/docs/guide/core-concepts/webview-bundle.ko.mdx b/content/docs/guide/core-concepts/webview-bundle.ko.mdx new file mode 100644 index 0000000..1da3cb1 --- /dev/null +++ b/content/docs/guide/core-concepts/webview-bundle.ko.mdx @@ -0,0 +1,66 @@ +--- +title: 웹뷰 번들 (.wvb) +--- + +웹뷰 번들은 여러 웹 리소스를 `.wvb` 파일 하나로 묶는 아카이브 포맷입니다. 웹 애플리케이션을 패킹하면 HTML·JS·CSS·이미지 같은 자산이 이 파일 하나에 담깁니다. 네이티브 호스트는 웹뷰의 요청을 가로채 이 파일에서 자산을 읽어 응답합니다. 각 자산은 체크섬과 함께 저장되므로, 네트워크 없이도 무결성이 검증된 자산을 제공할 수 있습니다. + +## 데이터 구조 + +![.wvb 파일은 헤더, 인덱스, 데이터 세 부분으로 이루어지며, 각 부분은 xxHash-32 체크섬으로 끝납니다.](/diagrams/wvb-structure.svg) + +`.wvb` 파일은 헤더, 인덱스, 데이터 세 부분으로 구성됩니다. 세 부분은 각각 xxHash-32 체크섬으로 끝나며, 이 값으로 무결성을 검증합니다. + +### 헤더 + +헤더는 파일의 첫 17바이트로, 포맷 메타데이터를 담습니다. + +- **매직 넘버 (8바이트)** — 웹뷰 번들 포맷을 식별하는 고정값입니다. `0xF09F8C90F09F8E81`을 사용합니다. 이는 UTF-8로 "🌐🎁"이며 빅엔디언으로 기록됩니다. +- **버전 (1바이트)** — 포맷 버전입니다. 현재 값은 v1(`0x01`)입니다. +- **인덱스 크기 (4바이트)** — 인덱스 부분의 바이트 크기입니다. u32 값을 빅엔디언으로 저장합니다. +- **체크섬 (4바이트)** — 앞의 13바이트가 온전한지 검증하는 xxHash-32 해시입니다. + +### 인덱스 + +인덱스는 요청 경로를 데이터 위치에 1:1로 대응시키는 맵입니다. 호스트는 요청 경로로 인덱스를 조회해 해당 자산의 데이터 범위를 찾고, 그 범위를 읽어 응답합니다. + +![요청 경로를 인덱스에서 조회해 오프셋과 길이를 얻고, 그 범위의 바이트를 데이터에서 읽습니다.](/diagrams/wvb-index-lookup.svg) + +- 인덱스는 헤더에 명시된 크기만큼 차지하며, 빅엔디언으로 인코딩됩니다. +- 경로(key)를 항목(value)에 대응시키는 맵이며, 결과가 항상 같도록 key를 정렬한 순서로 직렬화됩니다. +- 각 항목은 다음 값을 담습니다. + - **오프셋, 길이** — 데이터에서 파일 바이트를 읽을 범위를 지정합니다. + - **콘텐츠 타입, 콘텐츠 길이** — 자산의 MIME 타입과 압축 전 원본 크기입니다. + - **HTTP 헤더** — 프로토콜로 응답할 때 함께 내보낼 헤더입니다. (선택) +- 마지막 4바이트는 인덱스가 온전한지 검증하는 xxHash-32 체크섬입니다. + +### 데이터 + +데이터는 각 항목의 파일 바이트를 순서대로 저장합니다. 호스트는 인덱스에서 얻은 오프셋과 길이로 읽을 범위를 지정해 해당 자산만 읽습니다. + +![데이터는 각 항목의 압축된 바이트를 이어 붙여 저장하고, 인덱스가 각 항목의 오프셋과 길이를 기록합니다.](/diagrams/wvb-data-layout.svg) + +예를 들어 `/index.js` 항목이 오프셋 `0`, 길이 `768`로 기록되어 있으면, 호스트는 데이터의 `0`부터 `768`바이트까지를 읽습니다. + +- 각 항목은 [LZ4 블록 포맷](https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md)으로 압축됩니다. +- 마지막 4바이트는 데이터가 온전한지 검증하는 xxHash-32 체크섬입니다. + +## 설계 결정 + +### `.zip` 대신 새 포맷을 만든 이유 + +`.zip`은 파일 목록(중앙 디렉터리)을 파일 끝에 두므로, 자산을 찾으려면 먼저 끝을 읽어야 합니다. 또한 자산을 HTTP로 응답하는 데 필요한 콘텐츠 타입이나 헤더 같은 정보를 담지 않습니다. + +웹뷰 번들은 이 포맷을 웹뷰에 자산을 제공하는 용도에 맞춰 설계했습니다. + +- **인덱스를 앞에 둡니다.** 파일을 스캔하지 않고, 한 번의 조회로 경로에서 데이터 위치를 얻습니다. +- **항목마다 HTTP 메타데이터를 저장합니다.** 호스트는 콘텐츠 타입과 헤더를 그대로 HTTP 응답으로 내보냅니다. +- **바이트 배치를 고정합니다.** 오프셋과 길이로 필요한 범위만 읽으므로, 부분 요청(HTTP range)과 스트리밍에 유리합니다. +- **가벼운 체크섬으로 검증합니다.** 헤더·인덱스·데이터를 각각 xxHash-32로 검증합니다. + +### LZ4를 선택한 이유 + +자산 압축 해제는 웹뷰에 응답하는 경로에서 매번 일어나므로, 압축률보다 해제 속도가 중요합니다. + +- **압축 해제가 매우 빠릅니다.** 첫 화면을 그리는 순간을 지연시키지 않습니다. +- **CPU를 적게 씁니다.** UI 스레드를 막지 않습니다. +- **항목마다 독립적으로 압축합니다.** [LZ4 블록 포맷](https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md)으로 각 자산을 따로 해제하므로, 필요한 자산만 임의 접근으로 읽을 수 있습니다. diff --git a/content/docs/guide/core-concepts/webview-bundle.mdx b/content/docs/guide/core-concepts/webview-bundle.mdx new file mode 100644 index 0000000..59a18ed --- /dev/null +++ b/content/docs/guide/core-concepts/webview-bundle.mdx @@ -0,0 +1,66 @@ +--- +title: Webview Bundle (.wvb) +--- + +A webview bundle packs many web resources into a single `.wvb` file. Packing a web app puts its assets — HTML, JS, CSS, images — into this one file. The native host intercepts the webview's requests and answers them by reading assets from the file. Each asset is stored with a checksum, so the host serves integrity-checked assets with no network. + +## Data structure + +![A .wvb file has three sections — Header, Index, and Data — and each ends with an xxHash-32 checksum.](/diagrams/wvb-structure.svg) + +A `.wvb` file has three sections: a header, an index, and data. Each section ends with an xxHash-32 checksum that verifies its integrity. + +### Header + +The header is the first 17 bytes of the file and holds the format metadata. + +- **Magic number (8 bytes)** — a fixed value that identifies the webview bundle format. It is `0xF09F8C90F09F8E81`, which is "🌐🎁" in UTF-8, written big-endian. +- **Version (1 byte)** — the format version. The current value is v1 (`0x01`). +- **Index size (4 bytes)** — the size of the index section in bytes, stored as a big-endian u32. +- **Checksum (4 bytes)** — an xxHash-32 hash that verifies the preceding 13 bytes. + +### Index + +The index maps each request path one-to-one to the location of its data. The host looks a request path up in the index to find that asset's byte range, then reads the range to answer the request. + +![A request path is looked up in the index to get an offset and length, then those bytes are read from the data section.](/diagrams/wvb-index-lookup.svg) + +- The index occupies the size declared in the header and is encoded big-endian. +- It maps a path (key) to an entry (value), serialized in sorted key order so the output is deterministic. +- Each entry holds: + - **Offset and length** — the byte range to read the file's data from the data section. + - **Content type and content length** — the asset's MIME type and its original size before compression. + - **HTTP headers** — headers to send when serving the asset over the protocol. (Optional) +- The last 4 bytes are an xxHash-32 checksum that verifies the index. + +### Data + +The data section stores each entry's file bytes in order. The host reads a single asset by seeking to the offset and length it got from the index. + +![The data section stores each entry's compressed bytes back to back, and the index records the offset and length of each.](/diagrams/wvb-data-layout.svg) + +For example, if the `/index.js` entry is recorded with offset `0` and length `768`, the host reads bytes `0` through `768` from the data section. + +- Each entry is compressed with the [LZ4 block format](https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md). +- The last 4 bytes are an xxHash-32 checksum that verifies the data. + +## Design decisions + +### Why a new format instead of `.zip` + +`.zip` keeps its file listing (the central directory) at the end of the file, so finding an asset means reading the end first. It also carries none of the information — like content type or headers — needed to answer an HTTP request. + +The webview bundle format is built for one job: serving assets to a webview. + +- **The index comes first.** One lookup turns a path into a data location, with no scan of the file. +- **Every entry stores HTTP metadata.** The host sends the content type and headers straight back as the HTTP response. +- **The byte layout is fixed.** Reading only the needed range by offset and length makes HTTP range requests and streaming straightforward. +- **A cheap checksum verifies it.** The header, index, and data are each verified with xxHash-32. + +### Why LZ4 + +Decompression runs every time the host answers the webview, so decompression speed matters more than compression ratio. + +- **Decompression is very fast.** It does not delay first paint. +- **It uses little CPU.** It does not block the UI thread. +- **Each entry compresses independently.** The [LZ4 block format](https://github.com/lz4/lz4/blob/dev/doc/lz4_Block_format.md) decompresses each asset on its own, so the host reads only the assets it needs by random access. diff --git a/content/docs/guide/frontend/bridges/checking-sources.ko.mdx b/content/docs/guide/frontend/bridges/checking-sources.ko.mdx new file mode 100644 index 0000000..a99088c --- /dev/null +++ b/content/docs/guide/frontend/bridges/checking-sources.ko.mdx @@ -0,0 +1,37 @@ +--- +title: '소스 확인하기' +description: 소스 브리지로 웹 앱에서 설치된 번들을 읽습니다. 현재 버전, 전체 버전, 빌트인과 리모트를 구분합니다. +--- + +`source` 브리지는 웹 앱에서 네이티브 번들 소스를 읽습니다. 활성 버전을 표시하거나, 설치된 목록을 나열하거나, 빌트인 번들과 리모트(원격) 번들을 구분하는 데 사용하세요. + +## 현재 버전 읽기 + +`loadVersion`은 활성 버전과 그 유형을 반환하며, 번들이 로드되지 않았으면 `null`을 반환합니다. + +```ts title="src/version.ts" +import { source } from '@wvb/bridge'; + +const current = await source.loadVersion('app'); +// { type: 'builtin' | 'remote', version: '1.2.0' } | null +``` + +## 설치된 번들 나열하기 + +`listBundles`는 호스트가 알고 있는 모든 번들을 반환합니다. 각 항목은 `type`과 매니페스트 항목을 담고 있으며, `current` 여부도 포함됩니다. + +```ts +import { source } from '@wvb/bridge'; + +const bundles = await source.listBundles(); + +for (const { type, item } of bundles) { + console.log(item.name, item.version, type, item.current); +} +``` + +## 활용 사례 + +- 실행 중인 버전을 표시하는 "정보" 화면이나 진단 화면을 렌더링합니다. +- 사용자가 빌트인 폴백을 사용 중인지, 다운로드한 리모트 번들을 사용 중인지 감지합니다. +- `loadBuiltinMetadata` / `loadRemoteMetadata`를 읽어 무결성이나 마지막 수정 정보를 노출합니다. diff --git a/content/docs/guide/frontend/bridges/checking-sources.mdx b/content/docs/guide/frontend/bridges/checking-sources.mdx new file mode 100644 index 0000000..a2ce384 --- /dev/null +++ b/content/docs/guide/frontend/bridges/checking-sources.mdx @@ -0,0 +1,37 @@ +--- +title: 'Checking the sources' +description: Read installed bundles from the web app with the source bridge — current version, all versions, builtin vs remote. +--- + +The `source` bridge reads the native bundle source from your web app. Use it to show the active version, list what is installed, and tell builtin bundles apart from remote (over-the-air) ones. + +## Read the current version + +`loadVersion` returns the active version and its type, or `null` if the bundle is not loaded. + +```ts title="src/version.ts" +import { source } from '@wvb/bridge'; + +const current = await source.loadVersion('app'); +// { type: 'builtin' | 'remote', version: '1.2.0' } | null +``` + +## List installed bundles + +`listBundles` returns every bundle the host knows about. Each item carries its `type` and the manifest item, including whether it is `current`. + +```ts +import { source } from '@wvb/bridge'; + +const bundles = await source.listBundles(); + +for (const { type, item } of bundles) { + console.log(item.name, item.version, type, item.current); +} +``` + +## Use cases + +- Render an "about" or diagnostics screen with the running version. +- Detect whether the user is on a builtin fallback or a downloaded remote bundle. +- Read `loadBuiltinMetadata` / `loadRemoteMetadata` to surface integrity or last-modified details. diff --git a/content/docs/guide/frontend/bridges/index.ko.mdx b/content/docs/guide/frontend/bridges/index.ko.mdx new file mode 100644 index 0000000..149ceb1 --- /dev/null +++ b/content/docs/guide/frontend/bridges/index.ko.mdx @@ -0,0 +1,22 @@ +--- +title: '개요' +description: 자동 감지된 트랜스포트로 네이티브 호스트를 호출하는 웹 측 @wvb/bridge 패키지입니다. +--- + +`@wvb/bridge`은 웹 측 패키지입니다. 웹뷰 안의 JavaScript는 이 패키지를 임포트해서 번들 소스와 리모트, 업데이터를 관리하는 네이티브 호스트를 호출합니다. 플랫폼마다 트랜스포트를 자동으로 감지하므로, 동일한 웹 코드가 Electron, Tauri, Android, iOS에서 그대로 실행됩니다. + +```ts +import { source, remote, updater } from '@wvb/bridge'; +``` + +## 세 가지 표면 + +- `source` — 네이티브 번들 소스를 확인하고 관리합니다: 번들 목록 조회, 버전 로드 및 전환, 파일 해석. +- `remote` — 호스트를 통해 설정된 리모트와 통신합니다: 번들 목록 조회, 확인, 다운로드. +- `updater` — 원격 업데이트 흐름을 구동합니다: 업데이트 확인, 다운로드, 설치. + +각 표면은 타입이 지정되어 있으며 저수준 `invoke()` 위에 구축됩니다. 전체 API는 [브릿지 레퍼런스](/docs/references/api/bridge)를 참고하세요. + +## 언제 사용하나요 + +웹 코드가 어떤 번들이 제공되는지 알아야 하거나 바꿔야 할 때 `@wvb/bridge`을 사용하세요. 활성 소스를 확인하거나, 리모트에서 더 새로운 번들을 가져오거나, 앱 안에서 원격 업데이트를 실행하는 경우입니다. diff --git a/content/docs/guide/frontend/bridges/index.mdx b/content/docs/guide/frontend/bridges/index.mdx new file mode 100644 index 0000000..0380e90 --- /dev/null +++ b/content/docs/guide/frontend/bridges/index.mdx @@ -0,0 +1,22 @@ +--- +title: 'Overview' +description: The web-side @wvb/bridge package that calls the native host over an auto-detected transport. +--- + +`@wvb/bridge` is the web-side package. JavaScript inside the webview imports it to call the native host that manages your bundle source, remote, and updater. It auto-detects the transport per platform, so the same web code runs on Electron, Tauri, Android, and iOS. + +```ts +import { source, remote, updater } from '@wvb/bridge'; +``` + +## Three surfaces + +- `source` — inspect and manage the native bundle source: list bundles, load and switch versions, resolve files. +- `remote` — talk to the configured remote through the host: list, inspect, and download bundles. +- `updater` — drive the over-the-air update flow: check for an update, download it, install it. + +Each surface is typed and builds on the low-level `invoke()`. See the [bridge reference](/docs/references/api/bridge) for the full API. + +## When to reach for it + +Reach for `@wvb/bridge` when your web code needs to know or change which bundle is served — checking the active source, pulling a newer bundle from the remote, or running an OTA update from inside the app. diff --git a/content/docs/guide/frontend/bridges/meta.json b/content/docs/guide/frontend/bridges/meta.json new file mode 100644 index 0000000..03a1eb5 --- /dev/null +++ b/content/docs/guide/frontend/bridges/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Bridges", + "pages": ["index", "update-remote-bundles", "checking-sources", "testing"] +} diff --git a/content/docs/guide/frontend/bridges/meta.ko.json b/content/docs/guide/frontend/bridges/meta.ko.json new file mode 100644 index 0000000..dedb0a4 --- /dev/null +++ b/content/docs/guide/frontend/bridges/meta.ko.json @@ -0,0 +1 @@ +{ "title": "브릿지", "pages": ["index", "update-remote-bundles", "checking-sources", "testing"] } diff --git a/content/docs/guide/frontend/bridges/testing.ko.mdx b/content/docs/guide/frontend/bridges/testing.ko.mdx new file mode 100644 index 0000000..2dd6bcf --- /dev/null +++ b/content/docs/guide/frontend/bridges/testing.ko.mdx @@ -0,0 +1,65 @@ +--- +title: '테스트' +description: mockBridge로 invoke 호출과 플랫폼을 목 처리하여, 네이티브 호스트 없이 브릿지를 호출하는 웹 코드를 테스트합니다. +--- + +네이티브 호스트 없이 브릿지를 호출하는 웹 코드를 테스트합니다. `@wvb/bridge/testing` 서브패스는 브릿지 호출을 목 처리하여, 실제 플랫폼 대신 픽스처를 기준으로 코드가 결과를 반환하도록 합니다. + +## 브릿지 목 만들기 + +`mockBridge(options?)`은 핸들러를 등록하는 해제 가능한 브릿지를 생성합니다. 점으로 구분된 커맨드 키마다 `.mockInvoke(command, handler)`을 체이닝하세요. 핸들러는 메서드의 params를 위치 인자로 받고, 호출이 반환할 값(또는 그 값의 프로미스)을 돌려줍니다. + +```ts title="load-version.test.ts" +import { expect, test } from 'vitest'; +import { source } from '@wvb/bridge'; +import { mockBridge } from '@wvb/bridge/testing'; + +test('loads the current version', async () => { + using bridge = mockBridge({ platform: 'android' }); + bridge.mockInvoke('source.loadVersion', bundleName => ({ + type: 'remote', + version: '1.0.0', + })); + + await expect(source.loadVersion('app')).resolves.toEqual({ + type: 'remote', + version: '1.0.0', + }); +}); +``` + +플랫폼을 함께 목 처리하려면 `{ platform }`을 전달하세요. + +```ts +using bridge = mockBridge({ platform: 'ios' }).mockInvoke('source.loadVersion', () => ({ + type: 'remote', + version: '1.0.0', +})); +``` + + + `using`은 블록이 끝날 때 브릿지를 해제하며, 등록한 모든 목을 정리합니다. `using` 없이 사용한다면 + 스텁이 다음 테스트에 영향가지 않도록 `bridge.clear()`을 직접 호출하세요. + + +## 플랫폼 목 처리하기 + +`mockPlatform(type)`은 `platform.type`(`platform.isIos`, `platform.isElectron` 등)으로 분기하는 코드를 위해 플랫폼 감지를 단독으로 재정의합니다. 스코프가 끝나면 이전 플랫폼을 복원하는 해제 가능한 핸들을 반환합니다. `type`은 `'electron'`, `'tauri'`, `'android'`, `'ios'`, `'deno'` 중 하나입니다. + +```ts +import { platform } from '@wvb/bridge'; +import { mockPlatform } from '@wvb/bridge/testing'; + +test('detects the iOS platform', () => { + using _platform = mockPlatform('ios'); + expect(platform.isIos).toBe(true); +}); +``` + +## 단일 커맨드 스텁 처리하기 + +`mockInvoke(command, handler)`은 브릿지 인스턴스 없이 커맨드 하나를 등록하며, `mockBridge`이 기반으로 삼는 기본 API입니다. 해제 가능한 핸들을 반환하며, 등록된 목을 모두 초기화하려면 `afterEach` 등에서 `clearInvokeMocks()`을 호출하세요. + +```ts +using _mock = mockInvoke('source.loadVersion', () => ({ type: 'remote', version: '1.0.0' })); +``` diff --git a/content/docs/guide/frontend/bridges/testing.mdx b/content/docs/guide/frontend/bridges/testing.mdx new file mode 100644 index 0000000..42ea4fe --- /dev/null +++ b/content/docs/guide/frontend/bridges/testing.mdx @@ -0,0 +1,65 @@ +--- +title: Testing +description: Test web code that calls the bridge without a native host — mock invoke calls and the platform with mockBridge. +--- + +Test web code that calls the bridge without a native host. The `@wvb/bridge/testing` subpath mocks bridge calls so your code resolves against fixtures instead of a real platform. + +## Mock a bridge + +`mockBridge(options?)` creates a disposable bridge you register handlers on. Chain `.mockInvoke(command, handler)` once per dotted command key; the handler receives the method's params positionally and returns the value the call resolves to (or a promise of it). + +```ts title="load-version.test.ts" +import { expect, test } from 'vitest'; +import { source } from '@wvb/bridge'; +import { mockBridge } from '@wvb/bridge/testing'; + +test('loads the current version', async () => { + using bridge = mockBridge({ platform: 'android' }); + bridge.mockInvoke('source.loadVersion', bundleName => ({ + type: 'remote', + version: '1.0.0', + })); + + await expect(source.loadVersion('app')).resolves.toEqual({ + type: 'remote', + version: '1.0.0', + }); +}); +``` + +Pass `{ platform }` to mock the platform at the same time: + +```ts +using bridge = mockBridge({ platform: 'ios' }).mockInvoke('source.loadVersion', () => ({ + type: 'remote', + version: '1.0.0', +})); +``` + + + `using` disposes the bridge at the end of the block, clearing every mock it registered. Without + it, call `bridge.clear()` yourself so the stubs do not leak into the next test. + + +## Mock the platform + +`mockPlatform(type)` overrides platform detection on its own, for code that branches on `platform.type` (`platform.isIos`, `platform.isElectron`, …). It returns a disposable that restores the previous platform when the scope exits. `type` is one of `'electron'`, `'tauri'`, `'android'`, `'ios'`, or `'deno'`. + +```ts +import { platform } from '@wvb/bridge'; +import { mockPlatform } from '@wvb/bridge/testing'; + +test('detects the iOS platform', () => { + using _platform = mockPlatform('ios'); + expect(platform.isIos).toBe(true); +}); +``` + +## Stub a single command + +`mockInvoke(command, handler)` registers one command without a bridge instance — the primitive `mockBridge` builds on. It returns a disposable handle; reset every ambient mock with `clearInvokeMocks()`, for example in `afterEach`. + +```ts +using _mock = mockInvoke('source.loadVersion', () => ({ type: 'remote', version: '1.0.0' })); +``` diff --git a/content/docs/guide/frontend/bridges/update-remote-bundles.ko.mdx b/content/docs/guide/frontend/bridges/update-remote-bundles.ko.mdx new file mode 100644 index 0000000..7cf8861 --- /dev/null +++ b/content/docs/guide/frontend/bridges/update-remote-bundles.ko.mdx @@ -0,0 +1,38 @@ +--- +title: '리모트 번들 업데이트' +description: updater 브리지로 웹 앱에서 원격 업데이트를 진행합니다 — 확인, 다운로드, 설치, 리로드까지. +--- + +`updater` 브리지는 웹뷰 안의 웹 코드에서 원격 업데이트를 진행합니다. 새 번들이 있는지 확인하고, 다운로드하고, 설치한 뒤 리로드합니다. + +## 업데이트 흐름 + +`getUpdate`은 새 버전이 있는지 알려줍니다. `download`은 새 버전을 준비하고(검증한 뒤 네이티브 소스 저장소에 기록), `install`은 활성화하며, 리로드하면 해당 번들이 제공됩니다. + +```ts title="src/update.ts" +import { updater } from '@wvb/bridge'; + +async function checkForUpdate(bundleName: string) { + const update = await updater.getUpdate(bundleName); + if (!update.isAvailable) { + return; + } + + await updater.download(bundleName); + await updater.install(bundleName, update.version); + + // 리로드하면 웹뷰가 새로 설치된 번들을 제공합니다. + window.location.reload(); +} +``` + +`getUpdate`은 `BundleUpdateInfo`로 resolve됩니다. `version`은 리모트가 제공하는 버전이고, `localVersion`은 설치되어 있는 버전입니다. 준비 전에 `isAvailable`을 확인하세요. + +## 먼저 updater를 구성하세요 + +브리지는 이 호출들을 네이티브 호스트로 전달할 뿐입니다. updater와 그 리모트는 네이티브 쪽에 있습니다. 연동 방법은 플랫폼별 가이드를 참고하세요. + + + 호스트에 updater가 구성되어 있지 않으면 `updater` 호출은 `code`가 + `BridgeErrorCode.UpdaterNotInitialized`인 `BridgeError`로 reject됩니다. + diff --git a/content/docs/guide/frontend/bridges/update-remote-bundles.mdx b/content/docs/guide/frontend/bridges/update-remote-bundles.mdx new file mode 100644 index 0000000..319201a --- /dev/null +++ b/content/docs/guide/frontend/bridges/update-remote-bundles.mdx @@ -0,0 +1,38 @@ +--- +title: 'Update remote bundles' +description: Drive an over-the-air update from the web app with the updater bridge — check, download, install, and reload. +--- + +The `updater` bridge drives an over-the-air update from web code inside the webview: check for a newer bundle, download it, install it, then reload. + +## Update flow + +`getUpdate` reports whether a newer version is available. `download` stages it (verify and write to the native source store), `install` activates it, and a reload serves it. + +```ts title="src/update.ts" +import { updater } from '@wvb/bridge'; + +async function checkForUpdate(bundleName: string) { + const update = await updater.getUpdate(bundleName); + if (!update.isAvailable) { + return; + } + + await updater.download(bundleName); + await updater.install(bundleName, update.version); + + // Reload so the webview serves the newly installed bundle. + window.location.reload(); +} +``` + +`getUpdate` resolves to a `BundleUpdateInfo`: `version` is what the remote offers, `localVersion` is what is installed. Check `isAvailable` before staging. + +## Configure the updater first + +The bridge only forwards these calls to the native host — the updater and its remote live on the native side. See your platform's guide to wire them up. + + + If no updater is configured on the host, `updater` calls reject with a `BridgeError` whose `code` + is `BridgeErrorCode.UpdaterNotInitialized`. + diff --git a/content/docs/guide/frontend/deploy.ko.mdx b/content/docs/guide/frontend/deploy.ko.mdx new file mode 100644 index 0000000..3eee359 --- /dev/null +++ b/content/docs/guide/frontend/deploy.ko.mdx @@ -0,0 +1,38 @@ +--- +title: 배포하기 +description: 이미 업로드된 버전을 현재 버전으로 승격하여 클라이언트가 원격으로 받아 가도록 합니다. +--- + +업로드는 버전을 리모트에 저장합니다. 배포는 그 버전을 **현재** 버전, 즉 클라이언트가 원격으로 다운로드하는 버전으로 만듭니다. 배포는 별도의 빠른 단계로, 다시 패킹하거나 다시 업로드하지 않습니다. + +[업로드](/docs/guide/frontend/upload) 뒤에 배포하거나, `wvb upload`에 `--deploy`를 전달해 둘을 한 번에 처리할 수 있습니다. + +## 버전 배포하기 + +이미 업로드된 버전으로 리모트를 지정합니다. + +```sh +wvb deploy app --version 1.2.0 +``` + +`app`은 번들 이름입니다(설정이나 `package.json` 값으로 대체됩니다). 버전은 `--version`(`-V`) 플래그로 지정하며, 위치 인자로 버전을 넣는 방식은 없습니다. + +## 채널 + +이름이 지정된 채널로 배포하면 릴리스 트랙(`stable`, `beta`, `internal`, …)을 따로 유지할 수 있습니다. 해당 채널의 클라이언트는 그 채널에 배포한 버전을 받습니다. + +```sh +wvb deploy app --version 1.2.0 --channel beta +``` + +`--channel`을 생략하면 기본 채널로 배포됩니다. + +## 롤백 + +전용 롤백 명령어는 없습니다. 롤백하려면 이전에 업로드해 둔 버전을 배포하세요. 그 버전이 다시 현재 버전이 됩니다. + +```sh +wvb deploy app --version 1.1.0 +``` + +리모트에 이미 올라와 있는 버전은 무엇이든 다시 배포할 수 있으므로, 문제없이 동작하는 버전들을 업로드된 상태로 유지하세요. diff --git a/content/docs/guide/frontend/deploy.mdx b/content/docs/guide/frontend/deploy.mdx new file mode 100644 index 0000000..177c053 --- /dev/null +++ b/content/docs/guide/frontend/deploy.mdx @@ -0,0 +1,38 @@ +--- +title: Deploy +description: Promote an already-uploaded version to current so clients pick it up over the air. +--- + +Uploading stores a version on the remote. Deploying makes it the **current** version — the one clients download over the air. Deploy is a separate, fast step: no repack, no re-upload. + +Deploy after [uploading](/docs/guide/frontend/upload), or pass `--deploy` to `wvb upload` to do both at once. + +## Deploy a version + +Point the remote at a version that is already uploaded: + +```sh +wvb deploy app --version 1.2.0 +``` + +`app` is the bundle name (falls back to your config or `package.json`). The version is the `--version` (`-V`) flag — there is no positional version. + +## Channels + +Deploy to a named channel to keep separate release tracks (`stable`, `beta`, `internal`, …). Clients on that channel receive the version you deploy there. + +```sh +wvb deploy app --version 1.2.0 --channel beta +``` + +Omit `--channel` to deploy to the default channel. + +## Rollback + +There is no dedicated rollback command. To roll back, deploy a previously uploaded version — it becomes current again: + +```sh +wvb deploy app --version 1.1.0 +``` + +Any version already on the remote can be re-deployed, so keep known-good versions uploaded. diff --git a/content/docs/guide/frontend/development.ko.mdx b/content/docs/guide/frontend/development.ko.mdx new file mode 100644 index 0000000..f9d979c --- /dev/null +++ b/content/docs/guide/frontend/development.ko.mdx @@ -0,0 +1,68 @@ +--- +title: '개발' +description: 프레임워크의 개발 서버를 실행하고 네이티브 로컬 프로토콜을 그 서버로 향하게 하여, 웹뷰가 핫 리로드되는 실시간 UI를 불러오도록 합니다. +--- + +개발 중에는 변경이 있을 때마다 `.wvb`을 패킹하고 싶지 않을 것입니다. 대신, 프레임워크의 개발 서버를 실행하고 네이티브 **로컬 프로토콜**을 그 서버로 향하게 하여, 웹뷰가 핫 리로드되는 실시간 UI를 불러오도록 합니다. + +## 개발 서버 실행하기 + +평소처럼 프레임워크의 개발 서버를 시작하세요. + +```sh +vite dev +``` + +출력되는 URL(예: `http://localhost:5173`)을 확인하세요. 네이티브 쪽에서 이 URL로 프록시합니다. + +## 로컬 프로토콜을 개발 서버로 향하게 하기 + +네이티브 앱은 두 개의 스킴을 등록합니다. + +- 패킹된 `.wvb` 번들에서 파일을 제공하는 **번들 프로토콜**. +- 요청을 개발 서버로 프록시하는 **로컬 프로토콜**. + +앱이 개발 환경에서 실행 중인지 프로덕션 환경에서 실행 중인지에 따라 웹뷰가 어느 쪽을 불러올지 선택합니다. + +- **개발** — 로컬 프로토콜 URL(예: `app-local://app.wvb`)을 불러옵니다. 요청이 개발 서버로 전달되므로 핫 리로드가 동작합니다. +- **프로덕션** — 번들 프로토콜 URL(예: `app://app.wvb`)을 불러옵니다. 파일이 번들에서 곧바로 제공됩니다. + +정확한 스킴 이름, 호스트와 개발 서버 간의 매핑, 그리고 개발과 프로덕션을 전환하는 방식은 네이티브 쪽 설정입니다. 사용하는 플랫폼의 로컬 개발 페이지에서 설정하세요. + + + + + + + + + +## 패킹된 번들 미리보기 + +배포 전에 프로덕션 빌드(개발 서버가 아니라 패킹된 `.wvb`)을 확인하려면, [`wvb serve`](/docs/references/cli/serve)로 HTTP를 통해 제공하세요. + +```sh +wvb serve ./build/app.wvb +``` + +이 명령은 번들 하나를 풀어서 `http://localhost:4312`에 제공하며, 이는 런타임에 웹뷰가 번들을 불러오는 방식과 동일합니다. diff --git a/content/docs/guide/frontend/development.mdx b/content/docs/guide/frontend/development.mdx new file mode 100644 index 0000000..e07f34c --- /dev/null +++ b/content/docs/guide/frontend/development.mdx @@ -0,0 +1,68 @@ +--- +title: 'Development' +description: Run your framework's dev server and point the native Local Protocol at it so the webview loads your live, hot-reloading UI. +--- + +During development you don't want to pack a `.wvb` on every change. Instead, run your framework's dev server and point the native **Local Protocol** at it, so the webview loads your live, hot-reloading UI. + +## Run your dev server + +Start your framework's dev server as you normally would: + +```sh +vite dev +``` + +Note the URL it prints (for example `http://localhost:5173`) — the native side proxies to it. + +## Point the Local Protocol at it + +The native app registers two schemes: + +- A **Bundle Protocol** that serves files from the packed `.wvb` bundle. +- A **Local Protocol** that proxies requests to your dev server. + +You pick which one the webview loads based on whether the app is running in development or production: + +- **Development** — load the Local Protocol URL (e.g. `app-local://app.wvb`). Requests forward to your dev server, so hot reload works. +- **Production** — load the Bundle Protocol URL (e.g. `app://app.wvb`). Files come straight from the bundle. + +The exact scheme names, the host-to-dev-server mapping, and the dev-vs-prod switch are native-side config. Set them up on your platform's Local development page: + + + + + + + + + +## Preview a packed bundle + +To check the production build — the packed `.wvb`, not the dev server — before shipping, serve it over HTTP with [`wvb serve`](/docs/references/cli/serve): + +```sh +wvb serve ./build/app.wvb +``` + +This unpacks one bundle and serves it at `http://localhost:4312`, matching how the webview loads it at runtime. diff --git a/content/docs/guide/frontend/packing.ko.mdx b/content/docs/guide/frontend/packing.ko.mdx new file mode 100644 index 0000000..e1b8141 --- /dev/null +++ b/content/docs/guide/frontend/packing.ko.mdx @@ -0,0 +1,47 @@ +--- +title: '패킹' +description: wvb pack으로 빌드된 웹 앱을 하나의 .wvb 번들로 패킹합니다. +--- + +패킹은 빌드된 웹 앱을 하나의 `.wvb` 파일로 만듭니다. 이 파일은 앱이 웹뷰에 배포하거나 원격 업데이트를 위해 업로드하는 단위입니다. + +## 번들 패킹하기 + +빌드 산출물 디렉터리를 대상으로 `wvb pack`을 실행하세요. 소스 디렉터리 아래의 모든 파일을 읽어 하나의 압축된 `.wvb` 아카이브로 씁니다. + +```sh +wvb pack # uses config defaults +wvb pack ./dist # explicit source directory +wvb pack ./dist --outfile ./build/app.wvb # explicit output path +``` + +소스 디렉터리의 기본값은 `pack.srcDir`(또는 `./dist`)이고, 출력 경로의 기본값은 `pack.outFile`입니다. `.wvb` 확장자는 생략하면 자동으로 붙습니다. 모든 플래그는 [`wvb pack`](/docs/references/cli/pack)을 참고하세요. + +## 이름과 버전 + +번들 이름은 `package.json`의 `name` 필드에서 스코프 프리픽스를 제거한 값입니다. 이 값이 기본 출력 경로 `.wvb/.wvb`을 결정합니다. + +버전은 `package.json`의 `version` 필드에서 가져옵니다. 패킹은 이 값을 파일명에 넣지 않습니다. 버전은 번들을 업로드하거나 빌트인으로 설치할 때 새겨집니다. + +## 기본값 설정하기 + +`wvb.config` 파일에 `pack` 기본값을 설정하면 인자 없이 명령을 실행할 수 있습니다. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + pack: { + srcDir: './dist', + outFile: '.wvb/app', + }, +}); +``` + +`outFile`은 단일 출력 경로이며, 별도의 `outDir`은 없습니다. 명령줄에서 전달한 플래그는 항상 config 값을 덮어씁니다. 전체 `pack` 스키마는 [Configuration](/docs/references/configuration)을 참고하세요. + +## 빌트인으로 배포하기 + +패킹된 `.wvb`은 네이티브 앱 안에 빌트인 번들로 배포할 수 있으며, 앱이 오프라인 상태이거나 새 번들을 원격으로 가져오기 전에 제공됩니다. + +`wvb builtin`은 번들을 출력 디렉터리(기본값 `.wvb/builtin/bundles`)로 모으며, `manifest.json`과 함께 `/_.wvb` 형태로 배치합니다. diff --git a/content/docs/guide/frontend/packing.mdx b/content/docs/guide/frontend/packing.mdx new file mode 100644 index 0000000..e172b01 --- /dev/null +++ b/content/docs/guide/frontend/packing.mdx @@ -0,0 +1,47 @@ +--- +title: 'Packing' +description: Pack a built web app into a single .wvb bundle with wvb pack. +--- + +Packing turns a built web app into one `.wvb` file — the unit your app ships to its webview or uploads for over-the-air updates. + +## Pack a bundle + +Run `wvb pack` against your build output directory. It reads every file under the source directory and writes a single compressed `.wvb` archive. + +```sh +wvb pack # uses config defaults +wvb pack ./dist # explicit source directory +wvb pack ./dist --outfile ./build/app.wvb # explicit output path +``` + +The source directory defaults to `pack.srcDir` (or `./dist`), and the output path defaults to `pack.outFile`. The `.wvb` extension is appended when you omit it. See [`wvb pack`](/docs/references/cli/pack) for every flag. + +## Name and version + +The bundle name comes from your `package.json` `name` field with any scope prefix stripped. It sets the default output path, `.wvb/.wvb`. + +The version comes from your `package.json` `version` field. Packing does not embed it in the filename — the version is stamped when you upload the bundle or install it as a builtin. + +## Configure defaults + +Set `pack` defaults in your `wvb.config` file so the command runs with no arguments. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + pack: { + srcDir: './dist', + outFile: '.wvb/app', + }, +}); +``` + +`outFile` is a single output path — there is no separate `outDir`. A flag passed on the command line always overrides the config value. See [Configuration](/docs/references/configuration) for the full `pack` schema. + +## Ship as builtin + +A packed `.wvb` can ship inside your native app as a builtin bundle, served when the app is offline or before it fetches a newer bundle over the air. + +`wvb builtin` collects bundles into an output directory (default `.wvb/builtin/bundles`), laid out as `/_.wvb` alongside a `manifest.json`. diff --git a/content/docs/guide/frontend/upload.ko.mdx b/content/docs/guide/frontend/upload.ko.mdx new file mode 100644 index 0000000..b7e485d --- /dev/null +++ b/content/docs/guide/frontend/upload.ko.mdx @@ -0,0 +1,38 @@ +--- +title: '리모트에 업로드' +description: 패킹한 .wvb 버전을 리모트에 스테이징해 두었다가 나중에 배포합니다. +--- + +`wvb upload`은 패킹한 `.wvb`을 설정된 리모트로 전송합니다. 업로드는 버전을 *스테이징*할 뿐, 해당 버전을 현재 버전으로 만들지는 않습니다. + +## 버전 업로드 + +```sh +wvb upload app --version 1.2.0 +``` + +기본적으로 `wvb upload`은 `pack.srcDir`을 패킹하고, 무결성 해시를 계산하고, (서명이 설정되어 있으면) 번들에 서명한 뒤, `remote.uploader`을 통해 업로드합니다. 성공하면 번들 엔드포인트를 출력합니다. + +이미 빌드해 둔 번들을 업로드하려면, 패킹을 건너뛰고 해당 파일을 지정하세요. + +```sh +wvb upload app --version 1.2.0 --no-pack --file ./build/app.wvb +``` + +리모트에 이미 존재하는 버전을 덮어쓰려면 `--force`을 전달하세요. + +## 스테이징과 배포 + +`--deploy`의 기본값은 `false`이므로, 업로드는 버전을 스테이징만 합니다. 이 버전을 활성화하기 전까지 클라이언트는 이전에 배포된 버전을 계속 받습니다. + +- **업로드**은 리모트에 버전을 저장합니다. +- **배포**은 클라이언트가 다운로드하는 버전을 전환합니다. + + + `--channel`은 `--deploy`과 함께 사용할 때만 적용됩니다. 한 번에 업로드하고 활성화하려면 + `--deploy`을 추가하세요(특정 채널을 대상으로 하려면 `--channel beta`도 함께 사용하세요). + + +## 요구 사항 + +업로드하려면 설정에 `remote.uploader`이 필요합니다. 선택 사항인 `--deploy` 단계에는 `remote.deployer`도 필요합니다. [리모트 설정](/docs/references/configuration/remote)을 참고하세요. diff --git a/content/docs/guide/frontend/upload.mdx b/content/docs/guide/frontend/upload.mdx new file mode 100644 index 0000000..25f2519 --- /dev/null +++ b/content/docs/guide/frontend/upload.mdx @@ -0,0 +1,38 @@ +--- +title: 'Upload to remote' +description: Stage a packed .wvb version on the remote so you can deploy it later. +--- + +`wvb upload` sends a packed `.wvb` to your configured remote. Uploading _stages_ a version — it does not make it current. + +## Upload a version + +```sh +wvb upload app --version 1.2.0 +``` + +By default `wvb upload` packs `pack.srcDir`, computes an integrity hash, signs the bundle (when signing is configured), then uploads through `remote.uploader`. On success it prints the bundle endpoint. + +To upload a bundle you already built, skip packing and point at the file: + +```sh +wvb upload app --version 1.2.0 --no-pack --file ./build/app.wvb +``` + +Pass `--force` to overwrite a version that already exists on the remote. + +## Stage vs deploy + +`--deploy` defaults to `false`, so an upload only stages the version. Clients keep receiving the previously deployed version until you activate this one. + +- **Upload** stores the version on the remote. +- **Deploy** flips which version clients download. + + + `--channel` applies only with `--deploy`. To upload and activate in one step, add `--deploy` (and + `--channel beta` to target a channel). + + +## Requirements + +Uploading requires `remote.uploader` in your config. The optional `--deploy` step also needs `remote.deployer`. See [Remote configuration](/docs/references/configuration/remote). diff --git a/content/docs/guide/getting-started/example.ko.mdx b/content/docs/guide/getting-started/example.ko.mdx new file mode 100644 index 0000000..45cd197 --- /dev/null +++ b/content/docs/guide/getting-started/example.ko.mdx @@ -0,0 +1,34 @@ +--- +title: 예제와 함께 시작하기 +--- + +`create-wvb` 를 사용하여 선호하는 도구로 예제 프로젝트를 시작해 보세요. + + + + +```sh +npx create-wvb@latest +``` + + + +```sh +pnpx create-wvb@latest +``` + + +```sh +yarn dlx create-wvb@latest +``` + + + +## 예제 목록 + +사용가능한 예제 목록은 다음 테이블에서 확인해주세요. + +| 예제 | 설명 | +| ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | +| [electron-builder-app](https://github.com/webview-bundle/webview-bundle/tree/main/examples/electron-builder-app) | electron-builder로 패키징하는 Electron 앱 | +| [electron-forge-app](https://github.com/webview-bundle/webview-bundle/tree/main/examples/electron-forge-app) | Electron Forge로 패키징하는 Electron 앱 | diff --git a/content/docs/guide/getting-started/example.mdx b/content/docs/guide/getting-started/example.mdx new file mode 100644 index 0000000..cdfec1f --- /dev/null +++ b/content/docs/guide/getting-started/example.mdx @@ -0,0 +1,34 @@ +--- +title: Start with an example +--- + +Use `create-wvb` to scaffold an example project with your preferred tool. + + + + +```sh +npx create-wvb@latest +``` + + + +```sh +pnpx create-wvb@latest +``` + + +```sh +yarn dlx create-wvb@latest +``` + + + +## Example list + +Find the available examples in the table below. + +| Example | Description | +| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | +| [electron-builder-app](https://github.com/webview-bundle/webview-bundle/tree/main/examples/electron-builder-app) | Electron app packaged with electron-builder | +| [electron-forge-app](https://github.com/webview-bundle/webview-bundle/tree/main/examples/electron-forge-app) | Electron app packaged with Electron Forge | diff --git a/content/docs/guide/getting-started/index.ko.mdx b/content/docs/guide/getting-started/index.ko.mdx new file mode 100644 index 0000000..466a321 --- /dev/null +++ b/content/docs/guide/getting-started/index.ko.mdx @@ -0,0 +1,35 @@ +--- +title: 소개 +--- + +--- + +## 개요 + +웹뷰 번들은 웹뷰 기반 프레임워크 및 플랫폼을 위한 "오프라인 우선(Offline First)" 방식의 웹 애플리케이션 배포 시스템입니다. + +웹 애플리케이션 코드를 네트워크를 통해 불러와 웹뷰에 렌더하는 대신, 웹뷰 번들 파일(`.wvb`)로 패킹하여 로컬에서 리소스를 불러와 웹뷰에 렌더합니다. 또한, 리모트를 구성하여 원격으로 웹뷰 번들을 다운받아 최신상태의 애플리케이션 코드를 네이티브 배포 없이 효과적으로 업데이트할 수 있습니다. + +웹뷰 번들은 웹뷰가 제공되는 [Electron](https://electronjs.org), [Tauri](https://tauri.app), Android, iOS, 그리고 [Deno Desktop](https://deno.com/blog/v2.9#deno-desktop)(실험적)을 지원하며, 웹뷰와 통신할 수 있는 [브릿지](/docs/guide/core-concepts/communicate-between-native-and-webview) 또한 제공됩니다. + +## 기능 + +### 네이티브에 웹을 탑재 + +웹뷰 번들을 이용하면 익숙한 프론트엔드 코드를 네이티브에 탑재하여 오프라인에서 동작하는 애플리케이션을 만들 수 있습니다. 이를 지원하기 위해 웹 애플리케이션을 웹뷰 번들(`.wvb`) 포맷으로 패킹하여, 네이티브 앱 번들에 포함시킵니다. + +네이티브에서는 웹뷰의 요청을 인터셉트하여 네트워크 요청을 웹뷰 번들 파일 내의 리소스를 읽어 응답합니다. 따라서, 네트워크 요청이 인터넷을 통할 필요없이 로컬데이터를 읽어 응답하므로 오프라인에서도 웹 애플리케이션이 동작할 수 있습니다. + +### 원격 + +네이티브 앱 번들에 포함된 번들을 최신 상태로 업데이트하기 위해 웹뷰 번들 시스템은 원격 기술을 제공합니다. [리모트](/docs/guide/core-concepts/over-the-air) 제공자를 만들어 배포할 번들을 업로드할 수 있고, 프론트엔드 혹은 네이티브에서 원하는 타이밍에 신규 번들을 다운받아 최신 상태를 유지할 수 있습니다. + +웹뷰 번들을 원격으로 업데이트하는 과정은 네이티브 앱 배포 혹은 앱 재시작없이 수행되기에 UX를 해치지 않고 최신 상태를 유지할 수 있습니다. 예를들어, Electron은 업데이트 하려면 수십MB를 다운받고 앱을 재시작해야 업데이트가 반영되는데, 웹뷰 번들을 이용하면 이 과정을 생략할 수 있습니다. + +### 멀티 플랫폼 지원 + +웹뷰 번들 코어는 Rust로 작성되었으며 여러 플랫폼에 호환가능하게 빌드됩니다. + +- Node.js : [napi.rs](https://napi.rs/)를 이용해 [네이티브 애드온](https://nodejs.org/api/n-api.html)을 만들어 사용합니다. +- Android, iOS : [uniffi](https://github.com/mozilla/uniffi-rs)로 kotlin/swift 바인딩을 생성합니다. +- Deno : [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API를 이용해 동적 라이브러리를 링킹하여 사용합니다. diff --git a/content/docs/guide/getting-started/index.mdx b/content/docs/guide/getting-started/index.mdx new file mode 100644 index 0000000..1f45b7b --- /dev/null +++ b/content/docs/guide/getting-started/index.mdx @@ -0,0 +1,35 @@ +--- +title: Introduction +--- + +--- + +## Overview + +Webview Bundle is an offline-first distributing system for web applications on webview-based frameworks and platforms. + +Instead of loading your web app over the network and rendering it in the webview, you pack it into a webview bundle file (`.wvb`) and render it from local resources. You can also set up a remote, download bundles from it, and update your app's code over the air — with no native release. + +Webview Bundle supports the platforms that host a webview — [Electron](https://electronjs.org), [Tauri](https://tauri.app), Android, iOS, and [Deno Desktop](https://deno.com/blog/v2.9#deno-desktop) (experimental) — and provides a [bridge](/docs/guide/core-concepts/communicate-between-native-and-webview) for communicating with the webview. + +## Features + +### Web inside native + +With Webview Bundle, you embed familiar frontend code in a native app and ship an application that runs offline. You pack the web app into the webview bundle (`.wvb`) format and include it in the native app bundle. + +On the native side, the host intercepts the webview's requests and answers them by reading resources from the bundle file. Because it serves local data instead of going over the network, the web app works offline. + +### Over-the-air (OTA) + +Webview Bundle provides over-the-air (OTA) updates to keep the bundle inside the native app current. You create a [remote](/docs/guide/core-concepts/over-the-air) provider, upload the bundles you want to ship, and download new bundles from the frontend or the native side whenever you choose. + +An over-the-air update needs no native release and no app restart, so the app stays current without disrupting the user. Electron, for example, downloads tens of megabytes and restarts the app to apply an update; Webview Bundle skips that. + +### Multi-platform support + +The Webview Bundle core is written in Rust and builds for many platforms. + +- Node.js — a [native add-on](https://nodejs.org/api/n-api.html) built with [napi.rs](https://napi.rs/). +- Android and iOS — Kotlin and Swift bindings generated with [UniFFI](https://github.com/mozilla/uniffi-rs). +- Deno — a dynamic library linked through the [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API. diff --git a/content/docs/guide/getting-started/platform-and-framework-support.ko.mdx b/content/docs/guide/getting-started/platform-and-framework-support.ko.mdx new file mode 100644 index 0000000..b5e71a9 --- /dev/null +++ b/content/docs/guide/getting-started/platform-and-framework-support.ko.mdx @@ -0,0 +1,18 @@ +--- +title: 플랫폼/프레임워크 지원 +description: Webview Bundle을 실행하는 플랫폼/프레임워크에 대해 정리합니다. +--- + +웹뷰 번들은 여러 네이티브 플랫폼/프레임워크를 지원합니다. + +## 지원 현황 + +| 플랫폼 | 호환 버전 | 상태 | +| ------------ | ----------------------------- | ------ | +| Electron | Electron >= 15 | 안정적 | +| Tauri | Tauri v2 / Windows >= 10 | 안정적 | +| Android | Android minSdk >= 24 | 안정적 | +| iOS | iOS 16 / macOS 12.0 / Swift 6 | 안정적 | +| Deno Desktop | Deno >= 2.9.0 | 실험적 | + +더 많은 플랫폼/프레임워크 지원이 필요하다면 [깃헙 논의](https://github.com/webview-bundle/webview-bundle/discussions)에 요청사항을 적어주시면 감사하겠습니다. diff --git a/content/docs/guide/getting-started/platform-and-framework-support.mdx b/content/docs/guide/getting-started/platform-and-framework-support.mdx new file mode 100644 index 0000000..acdcfbf --- /dev/null +++ b/content/docs/guide/getting-started/platform-and-framework-support.mdx @@ -0,0 +1,18 @@ +--- +title: Platform and framework support +description: The platforms and frameworks that run Webview Bundle. +--- + +Webview Bundle supports several native platforms and frameworks. + +## Support status + +| Platform | Compatible versions | Status | +| ------------ | ----------------------------- | ------------ | +| Electron | Electron >= 15 | Stable | +| Tauri | Tauri v2 / Windows >= 10 | Stable | +| Android | Android minSdk >= 24 | Stable | +| iOS | iOS 16 / macOS 12.0 / Swift 6 | Stable | +| Deno Desktop | Deno >= 2.9.0 | Experimental | + +If you need support for more platforms or frameworks, please file a request in the [GitHub discussions](https://github.com/webview-bundle/webview-bundle/discussions). diff --git a/content/docs/guide/getting-started/prerequisites.ko.mdx b/content/docs/guide/getting-started/prerequisites.ko.mdx new file mode 100644 index 0000000..cd93292 --- /dev/null +++ b/content/docs/guide/getting-started/prerequisites.ko.mdx @@ -0,0 +1,38 @@ +--- +title: 필요 사항 +--- + +프로젝트를 시작하려면 먼저 몇 가지 종속성을 설치해야 합니다. + +## Node.js + +웹뷰 번들 커맨드라인 사용을 위해 [Node.js](https://nodejs.org/ko)를 설치해주세요. + +- 최소 요구 버전 : 18 +- 권장 버전 : LTS + +## Android + +안드로이드 앱 개발을 위해서 Android SDK 설치가 필요합니다. (혹은 [Android Studio](https://developer.android.com/studio)를 설치) + +사용 도구의 최소 요구 버전은 다음과 같습니다. + +- JDK 17 +- Android 최소 SDK 버전 : 24 + +## iOS + +iOS 앱 개발을 위해서 [Xcode](https://developer.apple.com/xcode/) 설치가 필요합니다. + +사용 도구의 최소 요구 버전은 다음과 같습니다. + +- Swift v5 +- iOS 16 + +## Deno + +[Deno Desktop](/docs/guide/native/deno)을 사용한다면 Deno 설치가 필요합니다. + +--- + +플랫폼 별로 필요한 종속성은 각 프레임워크의 가이드를 참고해주세요. diff --git a/content/docs/guide/getting-started/prerequisites.mdx b/content/docs/guide/getting-started/prerequisites.mdx new file mode 100644 index 0000000..dcf4948 --- /dev/null +++ b/content/docs/guide/getting-started/prerequisites.mdx @@ -0,0 +1,38 @@ +--- +title: Prerequisites +--- + +Install a few dependencies before you start a project. + +## Node.js + +Install [Node.js](https://nodejs.org/en) to use the Webview Bundle command line. + +- Minimum required version: 18 +- Recommended version: LTS + +## Android + +Android app development requires the Android SDK. (Or install [Android Studio](https://developer.android.com/studio).) + +The minimum required tool versions are: + +- JDK 17 +- Minimum Android SDK version: 24 + +## iOS + +iOS app development requires [Xcode](https://developer.apple.com/xcode/). + +The minimum required tool versions are: + +- Swift v5 +- iOS 16 + +## Deno + +Install Deno if you use [Deno Desktop](/docs/guide/native/deno). + +--- + +For the dependencies each platform requires, see the guide for each framework. diff --git a/content/docs/guide/index.ko.mdx b/content/docs/guide/index.ko.mdx deleted file mode 100644 index 25b25a8..0000000 --- a/content/docs/guide/index.ko.mdx +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: 소개 -description: 웹뷰 기반 프레임워크와 플랫폼을 위한 오프라인 우선 웹 애플리케이션 배포 시스템입니다. ---- - -Webview Bundle(`wvb`)은 빌드한 웹 자산(HTML/JS/CSS/미디어)을 압축되고 무결성이 검증된 단일 -아카이브(`.wvb`)로 묶습니다. 앱은 이 아카이브를 함께 배포하고, 커스텀 URL 스킴을 통해 웹뷰에 -제공합니다. 따라서 첫 화면은 네트워크를 기다리지 않습니다. 원격 서버를 설정하면 앱이 더 새로운 -번들을 무선(OTA, over-the-air)으로 다운로드하여, 네이티브 앱스토어 배포 없이 갱신된 코드를 -전달합니다. 하나의 `.wvb` 포맷이 공유 Rust 코어를 통해 모든 웹뷰 플랫폼에서 동작합니다. - -![빌드 산출물을 .wvb 아카이브로 묶어 웹뷰에 제공하고, 원격 서버에서 무선으로 갱신합니다.](https://static.wvb.dev/diagrams/pack-serve-update.svg) - -## 빠른 시작 - -CLI를 설치하고, 빌드 산출물을 묶은 뒤, 로컬에서 미리 봅니다. - - - - -```sh -npm install --save-dev @wvb/cli -``` - - - - -```sh -pnpm add -D @wvb/cli -``` - - - - -```sh -yarn add -D @wvb/cli -``` - - - - -```sh -# ./dist를 .wvb/으로 묶습니다 (--outfile,-O로 변경). -npx wvb pack - -# http://localhost:4312에서 로컬 미리 보기용으로 번들을 제공합니다. -npx wvb serve -``` - -`wvb.config.ts`에서 `defineConfig`로 빌드를 설정하세요. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - // `.wvb`가 자동으로 붙습니다 -> .wvb/app.wvb - outFile: '.wvb/app', - }, - serve: { - port: 4312, - }, -}); -``` - -모든 명령어와 필드는 [CLI 레퍼런스](/docs/guide/cli)와 [`wvb.config` 레퍼런스](/docs/config)를 참고하세요. - -## Webview Bundle을 쓰는 이유 - -- **오프라인 우선.** 리소스가 로컬에 번들로 포함되므로 첫 화면은 네트워크를 기다리지 않습니다. -- **무선 업데이트.** 새 번들 버전을 배포하면 수정 사항이나 기능을 전달합니다. 네이티브 배포가 - 필요 없습니다. -- **무결성과 진위성.** 모든 번들은 내부 체크섬을 담고 있습니다. 다운로드는 SHA-2 무결성 - 해시(`sha256`, `sha384`, `sha512`)와 디지털 서명(ECDSA, Ed25519, RSA)으로 검증할 수 있습니다. -- **하나의 포맷, 모든 플랫폼.** 동일한 `.wvb` 아카이브가 공유 Rust 코어를 통해 Electron, Tauri, - Android, iOS, Deno Desktop에서 동작합니다. - -설정에서 원격의 무결성 및 서명 검증을 활성화하세요. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - remote: { - integrity: { algorithm: 'sha256' }, - signature: { - algorithm: 'ed25519', - keyFormat: 'pkcs8', - // privateKey: ... - }, - }, -}); -``` - -전체 스키마는 [원격, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. - -## 여기서 시작하세요 - - - - - - - - - - - -## 기능 - - - - - - - - - -## 패키지 - -코어는 Rust 크레이트입니다. 각 플랫폼은 얇은 통합 패키지를 통해 코어를 사용합니다. - -| Package | Version | What it is | -| ---------------------------- | ------- | ----------------------------------------------------------------------------------------- | -| [`wvb`](https://docs.rs/wvb) | 0.2.0 | Rust 코어: 번들 포맷, 소스, 원격, 업데이터, 프로토콜, 무결성, 서명 | -| `@wvb/cli` | 0.1.0 | 커맨드라인 도구(바이너리 `wvb`, `webview-bundle`): pack, serve, upload, deploy, 로컬 원격 | -| `@wvb/config` | 0.1.0 | `wvb.config.ts`를 위한 `defineConfig` | -| `@wvb/node` | 0.1.0 | Node.js용 코어 N-API 바인딩 | -| `@wvb/bridge` | 0.1.0 | 웹 앱이 네이티브 호스트와 통신하기 위한 브리지 | -| `@wvb/electron` | 0.1.0 | Electron 통합(프로토콜, IPC, 업데이터) | -| `@wvb/electron-builder` | Stable | electron-builder 통합 | -| `@wvb/electron-forge` | Stable | Electron Forge 플러그인 | -| `wvb-tauri` | 0.1.0 | Tauri 통합, crates.io에 Rust 크레이트로 배포됨 | -| `@wvb/remote-aws` | 0.1.0 | AWS용 원격 설정 및 프로바이더 | -| `@wvb/remote-cloudflare` | 0.1.0 | Cloudflare용 원격 설정 및 프로바이더 | -| `@wvb/remote-local` | 0.0.0 | 로컬 시뮬레이션을 위한 원격 설정 | - -CLI와 함께 플랫폼 통합을 설치하세요. - -```sh -# Electron -npm install @wvb/electron - -# Tauri (Rust 크레이트) -cargo add wvb-tauri -``` - - - Tauri 통합은 crates.io의 **`wvb-tauri` 크레이트**입니다. `@wvb/tauri` npm 패키지는 없습니다. 전체 - 상태는 [플랫폼 지원](/docs/guide/platform-support)을 참고하세요. - - -## 다음 단계 - -아카이브 안에 무엇이 담겨 배포되는지 이해하려면 [`.wvb` 번들 포맷](/docs/guide/bundle-format)을 -읽으세요. 전체 API는 [Rust 크레이트 문서](https://docs.rs/wvb)를 살펴보세요. diff --git a/content/docs/guide/index.mdx b/content/docs/guide/index.mdx deleted file mode 100644 index 765b30e..0000000 --- a/content/docs/guide/index.mdx +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: Introduction -description: An offline-first web application deployment system for webview-based frameworks and platforms. ---- - -Webview Bundle (`wvb`) packs your built web assets (HTML/JS/CSS/media) into a single compressed, -integrity-checked archive (`.wvb`). Your app ships with the archive and serves it to its webview -through a custom URL scheme — so first paint never waits on the network. Configure a remote and the -app downloads newer bundles over the air (OTA = over-the-air), shipping updated code without a -native app-store release. One `.wvb` format runs on every webview platform via a shared Rust core. - -![Build output is packed into a .wvb archive, served to the webview, and updated over the air from a remote server.](https://static.wvb.dev/diagrams/pack-serve-update.svg) - -## Quickstart - -Install the CLI, pack your build output, and preview it locally. - - - - -```sh -npm install --save-dev @wvb/cli -``` - - - - -```sh -pnpm add -D @wvb/cli -``` - - - - -```sh -yarn add -D @wvb/cli -``` - - - - -```sh -# Pack ./dist into .wvb/ (override with --outfile,-O) -npx wvb pack - -# Serve the bundle for local preview on http://localhost:4312 -npx wvb serve -``` - -Configure the build in `wvb.config.ts` with `defineConfig`: - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - // `.wvb` is appended automatically -> .wvb/app.wvb - outFile: '.wvb/app', - }, - serve: { - port: 4312, - }, -}); -``` - -See the [CLI reference](/docs/guide/cli) and the [`wvb.config` reference](/docs/config) for every command and field. - -## Why Webview Bundle? - -- **Offline-first.** Resources are bundled locally, so the first paint never waits on the network. -- **Over-the-air updates.** Ship a fix or feature by deploying a new bundle version — no native - release required. -- **Integrity and authenticity.** Every bundle carries internal checksums, and downloads can be - verified with SHA-2 integrity hashes (`sha256`, `sha384`, `sha512`) and digital signatures - (ECDSA, Ed25519, RSA). -- **One format, every platform.** The same `.wvb` archive runs on Electron, Tauri, Android, iOS, - and Deno Desktop through a shared Rust core. - -Enable integrity and signature verification on the remote in config: - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - remote: { - integrity: { algorithm: 'sha256' }, - signature: { - algorithm: 'ed25519', - keyFormat: 'pkcs8', - // privateKey: ... - }, - }, -}); -``` - -See [Remote, integrity & signature config](/docs/config/remote) for the full schema. - -## Start here - - - - - - - - - - - -## Features - - - - - - - - - -## Packages - -The core is a Rust crate. Each platform consumes it through a thin integration package. - -| Package | Version | What it is | -| ---------------------------- | ------- | ---------------------------------------------------------------------------------------------- | -| [`wvb`](https://docs.rs/wvb) | 0.2.0 | Rust core: bundle format, source, remote, updater, protocol, integrity, signature | -| `@wvb/cli` | 0.1.0 | Command-line tool (bins `wvb` and `webview-bundle`): pack, serve, upload, deploy, local remote | -| `@wvb/config` | 0.1.0 | `defineConfig` for `wvb.config.ts` | -| `@wvb/node` | 0.1.0 | N-API bindings to the core for Node.js | -| `@wvb/bridge` | 0.1.0 | Bridge for the web app to talk to the native host | -| `@wvb/electron` | 0.1.0 | Electron integration (protocols, IPC, updater) | -| `@wvb/electron-builder` | Stable | electron-builder integration | -| `@wvb/electron-forge` | Stable | Electron Forge plugin | -| `wvb-tauri` | 0.1.0 | Tauri integration, published as a Rust crate on crates.io | -| `@wvb/remote-aws` | 0.1.0 | Remote configuration and provider for AWS | -| `@wvb/remote-cloudflare` | 0.1.0 | Remote configuration and provider for Cloudflare | -| `@wvb/remote-local` | 0.0.0 | Remote configuration for local simulation | - -Install a platform integration alongside the CLI: - -```sh -# Electron -npm install @wvb/electron - -# Tauri (Rust crate) -cargo add wvb-tauri -``` - - - The Tauri integration is the **`wvb-tauri` crate** on crates.io — there is no `@wvb/tauri` npm - package. See [Platform Support](/docs/guide/platform-support) for the full status. - - -## Next steps - -Read [the `.wvb` bundle format](/docs/guide/bundle-format) to understand what ships inside an -archive, or browse the [Rust crate docs](https://docs.rs/wvb) for the full API. diff --git a/content/docs/guide/meta.json b/content/docs/guide/meta.json index 913b498..8c15bc1 100644 --- a/content/docs/guide/meta.json +++ b/content/docs/guide/meta.json @@ -2,29 +2,34 @@ "root": true, "title": "Guide", "pages": [ - "---Introduction---", - "index", - "platform-support", - "why-webview-bundle", - "---Features---", - "bundle-format", - "bundle-sources", - "protocol-handling", - "remote-bundles", - "platform-integration", - "---Platforms---", - "platforms/electron", - "platforms/tauri", - "platforms/android", - "platforms/ios", - "platforms/deno", - "---CLI---", - "cli", - "commands", - "cli-programmatic", + "---Getting Started---", + "getting-started/index", + "getting-started/prerequisites", + "getting-started/example", + "getting-started/platform-and-framework-support", + "---Core Concepts---", + "core-concepts/webview-bundle", + "core-concepts/glue-to-native", + "core-concepts/protocol-handling", + "core-concepts/bundle-source", + "core-concepts/over-the-air", + "core-concepts/communicate-between-native-and-webview", + "---Native---", + "native/electron", + "native/tauri", + "native/android", + "native/ios", + "native/deno", + "---Frontend---", + "frontend/development", + "frontend/packing", + "frontend/upload", + "frontend/deploy", + "frontend/bridges", "---Remote---", - "remote", - "providers", - "spec" + "remote/index", + "remote/using-cli", + "remote/providers", + "remote/http-spec" ] } diff --git a/content/docs/guide/meta.ko.json b/content/docs/guide/meta.ko.json index c5a6c51..0035fc2 100644 --- a/content/docs/guide/meta.ko.json +++ b/content/docs/guide/meta.ko.json @@ -2,29 +2,34 @@ "root": true, "title": "가이드", "pages": [ - "---소개---", - "index", - "platform-support", - "why-webview-bundle", - "---기능---", - "bundle-format", - "bundle-sources", - "protocol-handling", - "remote-bundles", - "platform-integration", - "---플랫폼---", - "platforms/electron", - "platforms/tauri", - "platforms/android", - "platforms/ios", - "platforms/deno", - "---CLI---", - "cli", - "commands", - "cli-programmatic", + "---시작하기---", + "getting-started/index", + "getting-started/prerequisites", + "getting-started/example", + "getting-started/platform-and-framework-support", + "---핵심 개념---", + "core-concepts/webview-bundle", + "core-concepts/glue-to-native", + "core-concepts/protocol-handling", + "core-concepts/bundle-source", + "core-concepts/over-the-air", + "core-concepts/communicate-between-native-and-webview", + "---네이티브---", + "native/electron", + "native/tauri", + "native/android", + "native/ios", + "native/deno", + "---프론트엔드---", + "frontend/development", + "frontend/packing", + "frontend/upload", + "frontend/deploy", + "frontend/bridges", "---원격---", - "remote", - "providers", - "spec" + "remote/index", + "remote/using-cli", + "remote/providers", + "remote/http-spec" ] } diff --git a/content/docs/guide/native/android/builtin.ko.mdx b/content/docs/guide/native/android/builtin.ko.mdx new file mode 100644 index 0000000..7f9ca59 --- /dev/null +++ b/content/docs/guide/native/android/builtin.ko.mdx @@ -0,0 +1,51 @@ +--- +title: '내장 번들' +description: wvb builtin으로 빌트인 Webview Bundle을 Android 앱 모듈에 설치하면, 소스가 오프라인에서 이를 제공합니다. +--- + +`wvb builtin`은 Android 앱이 함께 배포하는 번들을 모듈의 `assets/bundles/`에 설치합니다. [소스](/docs/guide/native/android)는 원격 업데이트가 이루어지기 전, 첫 실행 시 이곳에서 번들을 읽습니다. + +## 앱 모듈에 설치하기 + +`--android` 프리셋으로 CLI를 실행하세요. `--android`만 쓰면 `com.android.application` 모듈을 자동으로 감지합니다. 모듈을 명시적으로 지정하려면 `--android=`을 전달하세요. + +```sh +# 앱 모듈을 자동 감지하여 src/main/assets/bundles에 설치 +wvb builtin --android + +# 또는 특정 모듈을 지정 +wvb builtin --android=./app +``` + +번들은 config의 `builtin.target`에서 가져옵니다. 기본값은 원격 엔드포인트이며, 로컬 워크스페이스일 수도 있습니다. 원격 대상을 재정의하려면 `--endpoint`와 `--channel`을 사용하세요. 모든 플래그는 [`wvb builtin`](/docs/references/cli/builtin)을 참고하세요. + +프리셋은 `/src/main/assets/bundles/`에 파일을 씁니다. `manifest.json` 하나와, 번들마다 `/_.wvb` 파일 하나가 생성됩니다. + +```text +app/src/main/assets/bundles/ +├── manifest.json +└── app/ + └── app_0.1.0.wvb +``` + + + `.wvb` 에셋이 APK에서 다시 압축되지 않도록, 모듈의 `build.gradle.kts`에 있는 `androidResources` + 블록에 `noCompress += "wvb"`을 추가하세요. 이 설정이 없으면 CLI가 경고합니다. + + +## 소스가 번들을 로드하는 방식 + +네이티브 소스는 에셋 스트림이 아니라 파일을 읽습니다. 그래서 라이브러리는 설치하거나 업데이트할 때마다 `assets/bundles/`를 앱의 `filesDir`로 복사합니다. 이 동작은 `SourceOptions.builtinAssetsDir`로 제어합니다. + +```kotlin +import dev.wvb.SourceOptions + +WebViewBundleConfig( + protocols = listOf(WebViewBundleProtocol.bundle()), + source = SourceOptions( + builtinAssetsDir = "bundles", // APK의 assets/, null이면 추출 비활성화 + ), +) +``` + +`/wvb/builtin` 아래로 추출된 사본은 읽기 전용입니다. 원격 다운로드는 쓰기 가능한 `/wvb/remote`에 저장됩니다. diff --git a/content/docs/guide/native/android/builtin.mdx b/content/docs/guide/native/android/builtin.mdx new file mode 100644 index 0000000..9040380 --- /dev/null +++ b/content/docs/guide/native/android/builtin.mdx @@ -0,0 +1,52 @@ +--- +title: 'Builtin bundles' +description: Install builtin Webview Bundles into an Android app module with wvb builtin so the Source serves them offline. +--- + +`wvb builtin` installs the bundles your Android app ships into the module's `assets/bundles/`, where the [Source](/docs/guide/native/android) reads them on first launch — before any over-the-air update. + +## Install into the app module + +Run the CLI with the `--android` preset. Bare `--android` auto-detects the `com.android.application` module; pass `--android=` to point at it explicitly. + +```sh +# Auto-detect the app module and install into src/main/assets/bundles +wvb builtin --android + +# Or target an explicit module +wvb builtin --android=./app +``` + +The bundles come from `builtin.target` in your config — a remote endpoint by default, or local workspaces. Use `--endpoint` and `--channel` to override the remote target. See [`wvb builtin`](/docs/references/cli/builtin) for every flag. + +The preset writes into `/src/main/assets/bundles/`: a `manifest.json` plus one `/_.wvb` file per bundle. + +```text +app/src/main/assets/bundles/ +├── manifest.json +└── app/ + └── app_0.1.0.wvb +``` + + + Keep `.wvb` assets uncompressed so the APK does not re-compress them: add `noCompress += "wvb"` to + the `androidResources` block in your module's `build.gradle.kts`. The CLI warns when it is + missing. + + +## How the Source loads them + +The native Source reads files, not asset streams, so the library copies `assets/bundles/` into the app's `filesDir` on each install or update. This is controlled by `SourceOptions.builtinAssetsDir`: + +```kotlin +import dev.wvb.SourceOptions + +WebViewBundleConfig( + protocols = listOf(WebViewBundleProtocol.bundle()), + source = SourceOptions( + builtinAssetsDir = "bundles", // APK assets/; null disables extraction + ), +) +``` + +The extracted copy under `/wvb/builtin` is read-only; over-the-air downloads land in the writable `/wvb/remote`. diff --git a/content/docs/guide/platforms/android.ko.mdx b/content/docs/guide/native/android/index.ko.mdx similarity index 93% rename from content/docs/guide/platforms/android.ko.mdx rename to content/docs/guide/native/android/index.ko.mdx index c65a787..e199143 100644 --- a/content/docs/guide/platforms/android.ko.mdx +++ b/content/docs/guide/native/android/index.ko.mdx @@ -1,5 +1,5 @@ --- -title: Android +title: 설정 description: webview-bundle-android Kotlin 라이브러리로 Android WebView 안에서 Webview Bundle을 제공하고 업데이트합니다. --- @@ -28,7 +28,8 @@ dependencies { ``` - 모든 플랫폼의 지원 현황은 [플랫폼 지원](/docs/guide/platform-support)을 참고하세요. + 모든 플랫폼의 지원 현황은 [플랫폼 + 지원](/docs/guide/getting-started/platform-and-framework-support)을 참고하세요. ## 빠른 시작 @@ -110,7 +111,7 @@ wvb(context, config) // 짧은 별칭 커스텀 URL 스킴은 등록되지 않습니다. `install()`은 `WebViewClient`를 설정하며, 이 클라이언트의 `shouldInterceptRequest`가 일반적인 `http`/`https` 요청을 검사해 일치하는 요청을 번들 소스에서 제공합니다. 각 요청마다 핸들러는 호스트를 소문자로 바꾼 뒤 등록된 프로토콜을 순서대로 살펴봅니다. 호스트를 처음으로 받아들이는 매처가 선택됩니다. 번들 프로토콜은 첫 번째 호스트 레이블을 번들 이름으로 삼고, 요청 경로에 해당하는 파일을 반환합니다. 일치하는 것이 없으면 `null`을 반환하고 `WebView`는 네트워크에서 로드합니다. -핸들러가 예외를 던지면 라이브러리는 `500 text/plain` 응답을 만들고 선택적 `onError` 콜백을 호출합니다. 요청 경로가 `.wvb` 안의 파일로 어떻게 매핑되는지는 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. +핸들러가 예외를 던지면 라이브러리는 `500 text/plain` 응답을 만들고 선택적 `onError` 콜백을 호출합니다. 요청 경로가 `.wvb` 안의 파일로 어떻게 매핑되는지는 [프로토콜 처리](/docs/guide/core-concepts/protocol-handling)를 참고하세요. `install()`은 메인 프레임에 네이티브 브리지를 `window.wvbAndroid`로 함께 연결합니다. install 옵션을 사용하면 추가 네이티브 핸들러를 등록하거나, 직접 만든 `WebViewClient`를 감싸거나, 브리지를 생략할 수 있습니다. @@ -175,7 +176,7 @@ WebViewBundleConfig( } ``` -매니페스트 포맷은 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +매니페스트 포맷은 [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. ## 프로토콜 @@ -314,24 +315,4 @@ await invoke('updaterDownload', { name: 'app' }); await invoke('updaterInstall', { name: 'app', version: '0.2.0' }); ``` -플랫폼 전반의 원격 HTTP 규약, 무결성, 서명에 대해서는 [원격 번들](/docs/guide/remote-bundles)과 [원격 구축하기](/docs/guide/remote) 가이드를 참고하세요. - -## 다음 단계 - - - - - - +플랫폼 전반의 원격 HTTP 규약, 무결성, 서명에 대해서는 [원격 번들](/docs/guide/core-concepts/over-the-air)과 [원격 구축하기](/docs/guide/remote) 가이드를 참고하세요. diff --git a/content/docs/guide/platforms/android.mdx b/content/docs/guide/native/android/index.mdx similarity index 93% rename from content/docs/guide/platforms/android.mdx rename to content/docs/guide/native/android/index.mdx index b977b65..a5dcaf1 100644 --- a/content/docs/guide/platforms/android.mdx +++ b/content/docs/guide/native/android/index.mdx @@ -1,5 +1,5 @@ --- -title: Android +title: Setup description: Serve and update Webview Bundles inside an Android WebView with the webview-bundle-android Kotlin library. --- @@ -28,7 +28,8 @@ dependencies { ``` - See [Platform support](/docs/guide/platform-support) for the status of every platform. + See [Platform support](/docs/guide/getting-started/platform-and-framework-support) for the status + of every platform. ## Quick start @@ -110,7 +111,7 @@ The manifest needs the internet permission. For local development against a clea No custom URL scheme is registered. `install()` sets a `WebViewClient` whose `shouldInterceptRequest` inspects ordinary `http`/`https` requests and serves matching ones from the bundle source. For each request, the handler lowercases the host and walks your registered protocols in order; the first matcher to accept the host wins. The bundle protocol takes the bundle name from the first host label and returns the file at the request path. No match returns `null` and the `WebView` loads from the network. -If a handler throws, the library synthesizes a `500 text/plain` response and calls the optional `onError` callback. See [Protocol handling](/docs/guide/protocol-handling) for how a request path maps to a file inside a `.wvb`. +If a handler throws, the library synthesizes a `500 text/plain` response and calls the optional `onError` callback. See [Protocol handling](/docs/guide/core-concepts/protocol-handling) for how a request path maps to a file inside a `.wvb`. `install()` also attaches the native bridge as `window.wvbAndroid` on the main frame. Use the install options to register extra native handlers, wrap your own `WebViewClient`, or skip the bridge: @@ -175,7 +176,7 @@ Re-extraction happens on each APK install or update and is additive — removed } ``` -See [Bundle sources](/docs/guide/bundle-sources) for the manifest format. +See [Bundle sources](/docs/guide/core-concepts/bundle-source) for the manifest format. ## Protocols @@ -314,24 +315,4 @@ await invoke('updaterDownload', { name: 'app' }); await invoke('updaterInstall', { name: 'app', version: '0.2.0' }); ``` -For the remote HTTP contract, integrity, and signatures across platforms, see [Remote bundles](/docs/guide/remote-bundles) and the guide to [building a remote](/docs/guide/remote). - -## Next steps - - - - - - +For the remote HTTP contract, integrity, and signatures across platforms, see [Remote bundles](/docs/guide/core-concepts/over-the-air) and the guide to [building a remote](/docs/guide/remote). diff --git a/content/docs/guide/native/android/local-development.ko.mdx b/content/docs/guide/native/android/local-development.ko.mdx new file mode 100644 index 0000000..7abb32b --- /dev/null +++ b/content/docs/guide/native/android/local-development.ko.mdx @@ -0,0 +1,39 @@ +--- +title: '로컬 개발' +description: Local 프로토콜로 번들 호스트를 개발 서버에 연결해, Android WebView가 실시간 개발 빌드를 로드하도록 합니다. +--- + +Local 프로토콜을 등록해 번들 호스트를 번들러의 개발 서버로 매핑하세요. 그러면 `WebView`는 핫 리로드를 그대로 유지한 채 실시간 개발 빌드를 로드하며, 매핑되지 않은 호스트는 여전히 배포된 번들로 폴백됩니다. + +## 번들 호스트를 개발 서버로 매핑하기 + +`WebViewBundleProtocol.local(hosts)`은 전체 요청 호스트를 로컬 베이스 URL로 매핑합니다. 모든 호스트에 일치하는 `bundle()`보다 앞에 등록해, 폴백이 가장 마지막에 오도록 하세요. + +```kotlin title="MainActivity.kt" +import dev.wvb.WebViewBundleConfig +import dev.wvb.WebViewBundleProtocol + +WebViewBundleConfig( + protocols = listOf( + WebViewBundleProtocol.local(mapOf("app.wvb" to "http://10.0.2.2:3000")), + WebViewBundleProtocol.bundle(), + ), +) +``` + +이제 `app.wvb`로 향하는 요청은 `http://10.0.2.2:3000`으로 프록시되고, 그 밖의 모든 호스트는 번들 소스에서 제공됩니다. + +## 에뮬레이터에서 호스트 머신에 연결하기 + +Android 에뮬레이터에서 `10.0.2.2`는 호스트 머신의 루프백에 연결됩니다. 따라서 `localhost:3000`에서 실행 중인 개발 서버는 `http://10.0.2.2:3000`이 됩니다. 평문(cleartext) `http://` 서버를 사용하려면 매니페스트에서 `usesCleartextTraffic`을 활성화해야 합니다. + +```xml title="AndroidManifest.xml" + + + +``` + + + `usesCleartextTraffic="true"`는 로컬 개발 전용입니다. 릴리스 빌드에서는 비활성화한 채로 두세요. + diff --git a/content/docs/guide/native/android/local-development.mdx b/content/docs/guide/native/android/local-development.mdx new file mode 100644 index 0000000..915fade --- /dev/null +++ b/content/docs/guide/native/android/local-development.mdx @@ -0,0 +1,39 @@ +--- +title: Local development +description: Route a bundle host to your dev server with a Local protocol so the Android WebView loads your live dev build. +--- + +Register a Local protocol to map a bundle host to your bundler's dev server. The `WebView` then loads your live dev build with hot reload intact, while unmapped hosts still fall through to the shipped bundle. + +## Map the bundle host to your dev server + +`WebViewBundleProtocol.local(hosts)` maps a full request host to a local base URL. Register it before `bundle()`, which matches every host, so the fallback stays last. + +```kotlin title="MainActivity.kt" +import dev.wvb.WebViewBundleConfig +import dev.wvb.WebViewBundleProtocol + +WebViewBundleConfig( + protocols = listOf( + WebViewBundleProtocol.local(mapOf("app.wvb" to "http://10.0.2.2:3000")), + WebViewBundleProtocol.bundle(), + ), +) +``` + +Requests to `app.wvb` now proxy to `http://10.0.2.2:3000`; every other host serves from the bundle source. + +## Reach the host machine from the emulator + +On the Android emulator, `10.0.2.2` reaches the host machine's loopback, so a dev server on `localhost:3000` is `http://10.0.2.2:3000`. A cleartext `http://` server needs `usesCleartextTraffic` enabled in the manifest. + +```xml title="AndroidManifest.xml" + + + +``` + + + `usesCleartextTraffic="true"` is for local development only. Leave it disabled in release builds. + diff --git a/content/docs/guide/native/android/meta.json b/content/docs/guide/native/android/meta.json new file mode 100644 index 0000000..737d79d --- /dev/null +++ b/content/docs/guide/native/android/meta.json @@ -0,0 +1 @@ +{ "title": "Android", "pages": ["index", "local-development", "builtin"] } diff --git a/content/docs/guide/native/android/meta.ko.json b/content/docs/guide/native/android/meta.ko.json new file mode 100644 index 0000000..737d79d --- /dev/null +++ b/content/docs/guide/native/android/meta.ko.json @@ -0,0 +1 @@ +{ "title": "Android", "pages": ["index", "local-development", "builtin"] } diff --git a/content/docs/guide/native/deno/building.ko.mdx b/content/docs/guide/native/deno/building.ko.mdx new file mode 100644 index 0000000..352c5ff --- /dev/null +++ b/content/docs/guide/native/deno/building.ko.mdx @@ -0,0 +1,40 @@ +--- +title: '빌드' +description: Deno 데스크톱 앱을 네이티브 cdylib와 빌트인 번들을 임베드한 자체 완결형 바이너리로 컴파일합니다. +--- + +`deno desktop`으로 Deno 데스크톱 앱을 배포 가능한 바이너리로 컴파일하세요. `@wvb/deno` 네이티브 라이브러리와 빌트인 번들을 임베드하므로 별도 설치 없이 오프라인으로 실행됩니다. + + + 컴파일된 바이너리 하나는 하나의 플랫폼만 대상으로 합니다. 배포하는 target triple마다 한 번씩 + 벤더링하고 컴파일하세요. + + +## 네이티브 라이브러리 벤더링 + +`@wvb/deno`는 FFI를 통해 사전 빌드된 Rust `cdylib`를 로드합니다. 빌드하려는 대상에 맞는 라이브러리를 프로젝트로 다운로드하세요. + +```sh +deno run -A jsr:@wvb/deno/install --out vendor/wvb --target aarch64-apple-darwin +``` + +라이브러리는 `libwvb_deno.dylib`, `libwvb_deno.so`, `wvb_deno.dll`처럼 단순한 플랫폼 이름으로 저장되며, 파일을 쓰기 전에 SHA-256 체크섬을 검증합니다. 지원하는 target triple은 [Deno Desktop 설정](/docs/guide/native/deno)을 참고하세요. + +## 배포 가능한 바이너리 컴파일 + +`deno desktop --include`로 벤더링한 `cdylib`와 패킹된 `bundles` 디렉터리를 임베드한 다음, 런타임에 `import.meta.url`에서 라이브러리를 resolve하세요. + +```sh +deno desktop --allow-ffi --include vendor/wvb/libwvb_deno.dylib --include bundles main.ts +``` + +```ts title="main.ts" +import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + lib: new URL('./vendor/wvb/libwvb_deno.dylib', import.meta.url), + protocols: [bundleProtocol('app')], +}); +``` + +앱은 진입 모듈 옆에 포함된 `bundles` 디렉터리에서 빌트인 번들을 읽으므로, 컴파일된 바이너리는 네트워크 접근 없이 이를 제공합니다. diff --git a/content/docs/guide/native/deno/building.mdx b/content/docs/guide/native/deno/building.mdx new file mode 100644 index 0000000..6886450 --- /dev/null +++ b/content/docs/guide/native/deno/building.mdx @@ -0,0 +1,39 @@ +--- +title: Building +description: Compile a Deno Desktop app into a self-contained binary that embeds the native cdylib and your builtin bundles. +--- + +Compile a Deno Desktop app into a distributable binary with `deno desktop`, embedding the `@wvb/deno` native library and your builtin bundles so it runs offline with no separate install. + + + A single compiled binary targets one platform. Vendor and compile once per target triple you ship. + + +## Vendor the native library + +`@wvb/deno` loads a prebuilt Rust `cdylib` over FFI. Download it into your project for the target you are building: + +```sh +deno run -A jsr:@wvb/deno/install --out vendor/wvb --target aarch64-apple-darwin +``` + +The library is saved under the plain platform name — `libwvb_deno.dylib`, `libwvb_deno.so`, or `wvb_deno.dll` — and its SHA-256 checksum is verified before it is written. See the [Deno Desktop setup](/docs/guide/native/deno) for the supported target triples. + +## Compile a distributable binary + +Embed the vendored `cdylib` and your packed `bundles` directory with `deno desktop --include`, then resolve the library from `import.meta.url` at runtime: + +```sh +deno desktop --allow-ffi --include vendor/wvb/libwvb_deno.dylib --include bundles main.ts +``` + +```ts title="main.ts" +import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + lib: new URL('./vendor/wvb/libwvb_deno.dylib', import.meta.url), + protocols: [bundleProtocol('app')], +}); +``` + +The app reads builtin bundles from the included `bundles` directory next to its entry module, so the compiled binary serves them without network access. diff --git a/content/docs/guide/native/deno/builtin.ko.mdx b/content/docs/guide/native/deno/builtin.ko.mdx new file mode 100644 index 0000000..3dbbe16 --- /dev/null +++ b/content/docs/guide/native/deno/builtin.ko.mdx @@ -0,0 +1,29 @@ +--- +title: '내장 번들' +description: 번들을 BundleSource가 읽는 빌트인 디렉터리에 설치하여 Deno Desktop 앱 안에 함께 배포합니다. +--- + +빌트인 번들은 앱 안에 함께 배포되므로, 아직 원격으로 다운로드한 번들이 없는 첫 실행 시점에도 창이 오프라인으로 로드됩니다. `BundleSource`는 `builtinDir`에서 이 번들들을 읽고, `wvb builtin`은 설치되는 번들 세트를 그 디렉터리에 기록합니다. + +## 소스를 빌트인 디렉터리로 지정하기 + +`webviewBundle`은 `source`로부터 `BundleSource`를 만듭니다. `builtinDir`에는 번들을 설치할 디렉터리를 지정하세요. 기본값은 엔트리 모듈 옆의 `bundles`입니다. + +```ts title="main.ts" +import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + protocols: [bundleProtocol('app')], + source: { builtinDir: './bundles' }, +}); +``` + +## wvb builtin으로 설치하기 + +`wvb builtin`을 실행하고 `--out`을 같은 디렉터리로 지정하세요. 이 명령은 `manifest.json` 하나와, 번들마다 `/_.wvb` 파일 하나씩을 기록합니다. + +```sh +wvb builtin --out ./bundles +``` + +번들을 리모트 엔드포인트에서 가져올지 로컬 워크스페이스에서 가져올지는 CLI 플래그가 아니라 [설정](/docs/references/configuration)의 `builtin.target`으로 정합니다. 기본값은 리모트 타깃입니다. 모든 플래그와 두 가지 타깃 형태는 [`wvb builtin`](/docs/references/cli/builtin)을 참고하세요. diff --git a/content/docs/guide/native/deno/builtin.mdx b/content/docs/guide/native/deno/builtin.mdx new file mode 100644 index 0000000..e718693 --- /dev/null +++ b/content/docs/guide/native/deno/builtin.mdx @@ -0,0 +1,29 @@ +--- +title: 'Builtin bundles' +description: Ship bundles inside a Deno Desktop app by installing them into the builtin directory the BundleSource reads. +--- + +Builtin bundles ship inside the app so the window loads offline on first launch, before any bundle has been downloaded over the air. The `BundleSource` reads them from its `builtinDir`; `wvb builtin` writes the installed set into that directory. + +## Point the source at a builtin directory + +`webviewBundle` builds its `BundleSource` from `source`. Set `builtinDir` to the directory you install into. It defaults to `bundles` next to your entry module. + +```ts title="main.ts" +import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + protocols: [bundleProtocol('app')], + source: { builtinDir: './bundles' }, +}); +``` + +## Install with wvb builtin + +Run `wvb builtin` and point `--out` at that same directory. It writes a `manifest.json` plus one `/_.wvb` file per bundle. + +```sh +wvb builtin --out ./bundles +``` + +Whether bundles come from a remote endpoint or from local workspaces is set by `builtin.target` in your [config](/docs/references/configuration), not by a CLI flag. It defaults to a remote target. See [`wvb builtin`](/docs/references/cli/builtin) for every flag and both target shapes. diff --git a/content/docs/guide/platforms/deno.ko.mdx b/content/docs/guide/native/deno/index.ko.mdx similarity index 82% rename from content/docs/guide/platforms/deno.ko.mdx rename to content/docs/guide/native/deno/index.ko.mdx index de7ceb3..58d7f15 100644 --- a/content/docs/guide/platforms/deno.ko.mdx +++ b/content/docs/guide/native/deno/index.ko.mdx @@ -1,5 +1,5 @@ --- -title: Deno Desktop +title: 설정 description: Deno.serve 요청 핸들러를 통해 Deno 데스크톱 웹뷰에서 Webview Bundle 아카이브를 제공하고 업데이트합니다. --- @@ -11,7 +11,7 @@ Deno Desktop은 `Deno.BrowserWindow`로 네이티브 웹뷰를 렌더링하고 ` import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], }); const server = Deno.serve(wvb.fetch); @@ -19,11 +19,11 @@ const win = new Deno.BrowserWindow({ url: `http://localhost:${server.addr.port}` await win.closed; ``` -`@wvb/deno-desktop`은 통합 계층입니다. `@wvb/deno`는 [`@wvb/node`](/docs/references/node)의 FFI 짝으로, 공유 Rust 코어를 구동합니다. +`@wvb/deno-desktop`은 통합 계층입니다. `@wvb/deno`는 [`@wvb/node`](/docs/references/api/node)의 FFI 짝으로, 공유 Rust 코어를 구동합니다. ## 구성 방식 -창은 `Deno.serve`가 제공하는 단일 로컬 origin을 가리킵니다. 따라서 이 통합은 **단일 origin**이며, 정확히 하나의 프로토콜만 허용합니다. 코어가 각 플랫폼에 도달하는 방식은 [플랫폼 통합](/docs/guide/platform-integration)을, 프로토콜 모델은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. +창은 `Deno.serve`가 제공하는 단일 로컬 origin을 가리킵니다. 따라서 이 통합은 **단일 origin**이며, 정확히 하나의 프로토콜만 허용합니다. 코어가 각 플랫폼에 도달하는 방식은 [플랫폼 통합](/docs/guide/core-concepts/glue-to-native)을, 프로토콜 모델은 [프로토콜 처리](/docs/guide/core-concepts/protocol-handling)를 참고하세요. ## @wvb/deno-desktop @@ -33,7 +33,7 @@ await win.closed; import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com' }, }, @@ -52,7 +52,7 @@ await win.closed; import { webviewBundle, bundleProtocol, registerBindings } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], }); const server = Deno.serve(wvb.fetch); @@ -85,7 +85,7 @@ if (update.isAvailable) { ## @wvb/deno -`@wvb/deno`는 [`@wvb/node`](/docs/references/node)의 FFI 짝입니다. `Deno.dlopen`을 통해 사전 빌드된 Rust `cdylib`를 로드하고, 동일한 코어 클래스인 `BundleProtocol`, `LocalProtocol`, `Remote`, `BundleSource`, `Updater`를 노출합니다. +`@wvb/deno`는 [`@wvb/node`](/docs/references/api/node)의 FFI 짝입니다. `Deno.dlopen`을 통해 사전 빌드된 Rust `cdylib`를 로드하고, 동일한 코어 클래스인 `BundleProtocol`, `LocalProtocol`, `Remote`, `BundleSource`, `Updater`를 노출합니다. 각 클래스는 `Disposable`입니다. `free()`로 명시적으로 해제하거나, `using` 선언으로 스코프를 벗어날 때 `[Symbol.dispose]`를 호출하게 하세요. @@ -147,7 +147,7 @@ deno run -A jsr:@wvb/deno/install --out vendor/wvb deno run -A jsr:@wvb/deno/install --out vendor/wvb --target x86_64-unknown-linux-gnu ``` -전체 클래스 및 메서드 레퍼런스는 [Deno API 레퍼런스](/docs/references/deno)를 참고하세요. +전체 클래스 및 메서드 레퍼런스는 [Deno API 레퍼런스](/docs/references/api/deno)를 참고하세요. ## 제한 사항 @@ -157,7 +157,7 @@ deno run -A jsr:@wvb/deno/install --out vendor/wvb --target x86_64-unknown-linux ```ts title="updater.ts" const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com' }, integrityPolicy: 'strict', @@ -175,7 +175,7 @@ const wvb = await webviewBundle({ ```ts title="http.ts" const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com', @@ -185,24 +185,4 @@ const wvb = await webviewBundle({ }); ``` -무결성과 서명이 플랫폼 전반에서 동작하는 방식은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. - -## 다음 단계 - - - - - - +무결성과 서명이 플랫폼 전반에서 동작하는 방식은 [원격 번들](/docs/guide/core-concepts/over-the-air)을 참고하세요. diff --git a/content/docs/guide/platforms/deno.mdx b/content/docs/guide/native/deno/index.mdx similarity index 81% rename from content/docs/guide/platforms/deno.mdx rename to content/docs/guide/native/deno/index.mdx index 5bb4d53..1bea5eb 100644 --- a/content/docs/guide/platforms/deno.mdx +++ b/content/docs/guide/native/deno/index.mdx @@ -1,5 +1,5 @@ --- -title: Deno Desktop +title: Setup description: Serve and update Webview Bundle archives in a Deno desktop webview through a Deno.serve request handler. --- @@ -11,7 +11,7 @@ Deno Desktop renders a native webview with `Deno.BrowserWindow` and serves it ov import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], }); const server = Deno.serve(wvb.fetch); @@ -19,11 +19,11 @@ const win = new Deno.BrowserWindow({ url: `http://localhost:${server.addr.port}` await win.closed; ``` -`@wvb/deno-desktop` is the integration layer; `@wvb/deno` is the FFI peer of [`@wvb/node`](/docs/references/node) that drives the shared Rust core. +`@wvb/deno-desktop` is the integration layer; `@wvb/deno` is the FFI peer of [`@wvb/node`](/docs/references/api/node) that drives the shared Rust core. ## How it fits together -The window points at a single local origin served by `Deno.serve`, so the integration is **single-origin**: it allows exactly one protocol. See [Platform integration](/docs/guide/platform-integration) for how the core reaches each platform and [Protocol handling](/docs/guide/protocol-handling) for the protocol model. +The window points at a single local origin served by `Deno.serve`, so the integration is **single-origin**: it allows exactly one protocol. See [Platform integration](/docs/guide/core-concepts/glue-to-native) for how the core reaches each platform and [Protocol handling](/docs/guide/core-concepts/protocol-handling) for the protocol model. ## @wvb/deno-desktop @@ -33,7 +33,7 @@ Call `webviewBundle(config)` — aliased as `wvb`, backed by the `WebviewBundle` import { webviewBundle, bundleProtocol } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com' }, }, @@ -52,7 +52,7 @@ Call `registerBindings(win, wvb)` to expose native commands to your web app. It import { webviewBundle, bundleProtocol, registerBindings } from '@wvb/deno-desktop'; const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], }); const server = Deno.serve(wvb.fetch); @@ -85,7 +85,7 @@ if (update.isAvailable) { ## @wvb/deno -`@wvb/deno` is the FFI peer of [`@wvb/node`](/docs/references/node). It loads a prebuilt Rust `cdylib` through `Deno.dlopen` and exposes the same core classes: `BundleProtocol`, `LocalProtocol`, `Remote`, `BundleSource`, and `Updater`. +`@wvb/deno` is the FFI peer of [`@wvb/node`](/docs/references/api/node). It loads a prebuilt Rust `cdylib` through `Deno.dlopen` and exposes the same core classes: `BundleProtocol`, `LocalProtocol`, `Remote`, `BundleSource`, and `Updater`. Each class is `Disposable` — free it explicitly with `free()`, or let a `using` declaration call `[Symbol.dispose]` at scope exit: @@ -147,7 +147,7 @@ Vendor a specific target with `--target`: deno run -A jsr:@wvb/deno/install --out vendor/wvb --target x86_64-unknown-linux-gnu ``` -For the full class and method reference, see the [Deno API reference](/docs/references/deno). +For the full class and method reference, see the [Deno API reference](/docs/references/api/deno). ## Limitations @@ -157,7 +157,7 @@ Two gaps apply to the Deno bindings today. ```ts title="updater.ts" const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com' }, integrityPolicy: 'strict', @@ -175,7 +175,7 @@ The custom `integrityChecker` / `signatureVerifier` function callbacks available ```ts title="http.ts" const wvb = await webviewBundle({ - protocols: [bundleProtocol({ scheme: 'app' })], + protocols: [bundleProtocol('app')], updater: { remote: { endpoint: 'https://bundles.example.com', @@ -185,24 +185,4 @@ const wvb = await webviewBundle({ }); ``` -For how integrity and signatures work across platforms, see [Remote bundles](/docs/guide/remote-bundles). - -## Next steps - - - - - - +For how integrity and signatures work across platforms, see [Remote bundles](/docs/guide/core-concepts/over-the-air). diff --git a/content/docs/guide/native/deno/local-development.ko.mdx b/content/docs/guide/native/deno/local-development.ko.mdx new file mode 100644 index 0000000..3f6566a --- /dev/null +++ b/content/docs/guide/native/deno/local-development.ko.mdx @@ -0,0 +1,32 @@ +--- +title: '로컬 개발' +description: localProtocol로 Deno 데스크톱 웹뷰를 개발 서버에 프록시하여 라이브 리로드를 사용합니다. +--- + +실험적 + +개발 중에는 `bundleProtocol`을 `localProtocol`로 바꾸세요. 웹뷰를 개발 서버에 프록시하므로, 패킹된 `.wvb` 자산을 로드하는 대신 수정 사항이 라이브로 리로드됩니다. + +## 개발 서버로 프록시하기 + +`localProtocol(scheme, { hosts })`은 제공되는 host를 개발 서버의 base URL로 매핑합니다. 이를 Vite(또는 프런트엔드를 제공하는 무엇이든)로 향하게 하고, 프로덕션과 동일한 단일 origin `webviewBundle` 연결을 그대로 재사용하세요. + +```ts title="main.ts" +import { webviewBundle, localProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + protocols: [ + localProtocol('app', { + hosts: { app: 'http://localhost:5173' }, + }), + ], +}); + +const server = Deno.serve(wvb.fetch); +const win = new Deno.BrowserWindow({ url: `http://localhost:${server.addr.port}` }); +await win.closed; +``` + +## 프로덕션으로 되돌리기 + +패킹된 `.wvb` 자산을 오프라인으로 제공하려면 `localProtocol`을 다시 `bundleProtocol`로 바꾸세요. 프로덕션 연결은 [설정](/docs/guide/native/deno)을 참고하세요. diff --git a/content/docs/guide/native/deno/local-development.mdx b/content/docs/guide/native/deno/local-development.mdx new file mode 100644 index 0000000..f038852 --- /dev/null +++ b/content/docs/guide/native/deno/local-development.mdx @@ -0,0 +1,32 @@ +--- +title: 'Local development' +description: Proxy the Deno desktop webview to your dev server for live reload with localProtocol. +--- + +Experimental + +In development, swap `bundleProtocol` for `localProtocol`. It proxies the webview to your dev server, so edits reload live instead of loading packed `.wvb` assets. + +## Proxy to your dev server + +`localProtocol(scheme, { hosts })` maps the served host to your dev server's base URL. Point it at Vite (or whatever serves your frontend) and reuse the same single-origin `webviewBundle` wiring as production. + +```ts title="main.ts" +import { webviewBundle, localProtocol } from '@wvb/deno-desktop'; + +const wvb = await webviewBundle({ + protocols: [ + localProtocol('app', { + hosts: { app: 'http://localhost:5173' }, + }), + ], +}); + +const server = Deno.serve(wvb.fetch); +const win = new Deno.BrowserWindow({ url: `http://localhost:${server.addr.port}` }); +await win.closed; +``` + +## Switch back for production + +Swap `localProtocol` back for `bundleProtocol` to serve packed `.wvb` assets offline. See [Setup](/docs/guide/native/deno) for the production wiring. diff --git a/content/docs/guide/native/deno/meta.json b/content/docs/guide/native/deno/meta.json new file mode 100644 index 0000000..48ae628 --- /dev/null +++ b/content/docs/guide/native/deno/meta.json @@ -0,0 +1 @@ +{ "title": "Deno Desktop", "pages": ["index", "local-development", "building", "builtin"] } diff --git a/content/docs/guide/native/deno/meta.ko.json b/content/docs/guide/native/deno/meta.ko.json new file mode 100644 index 0000000..48ae628 --- /dev/null +++ b/content/docs/guide/native/deno/meta.ko.json @@ -0,0 +1 @@ +{ "title": "Deno Desktop", "pages": ["index", "local-development", "building", "builtin"] } diff --git a/content/docs/guide/platforms/electron/builder.ko.mdx b/content/docs/guide/native/electron/builder.ko.mdx similarity index 84% rename from content/docs/guide/platforms/electron/builder.ko.mdx rename to content/docs/guide/native/electron/builder.ko.mdx index ba2d6ad..3e87be5 100644 --- a/content/docs/guide/platforms/electron/builder.ko.mdx +++ b/content/docs/guide/native/electron/builder.ko.mdx @@ -3,7 +3,7 @@ title: Electron Builder description: 패키징 시점에 빌트인 .wvb 번들을 설치하고, electron-builder 앱에서 네이티브 @wvb/node 애드온을 유지합니다. --- -`@wvb/electron-builder`는 Webview Bundle을 [electron-builder](https://www.electron.build/) 패키징과 통합합니다. electron-builder의 `afterPack` 단계에 훅을 걸어, 빌트인 `.wvb` 번들을 패키징된 앱의 리소스에 설치합니다. 그 결과 `@wvb/electron`이 런타임에 제공하는 번들이 앱과 함께 배포됩니다. 단, 네이티브 `@wvb/node` 애드온을 ASAR 아카이브에서 제외하도록 electron-builder를 직접 설정해야 합니다. 런타임 설정은 [Electron 가이드](/docs/guide/platforms/electron)를 먼저 읽으세요. Forge용 동일 기능은 [Electron Forge](/docs/guide/platforms/electron/forge)를 참고하세요. +`@wvb/electron-builder`는 Webview Bundle을 [electron-builder](https://www.electron.build/) 패키징과 통합합니다. electron-builder의 `afterPack` 단계에 훅을 걸어, 빌트인 `.wvb` 번들을 패키징된 앱의 리소스에 설치합니다. 그 결과 `@wvb/electron`이 런타임에 제공하는 번들이 앱과 함께 배포됩니다. 단, 네이티브 `@wvb/node` 애드온을 ASAR 아카이브에서 제외하도록 electron-builder를 직접 설정해야 합니다. 런타임 설정은 [Electron 가이드](/docs/guide/native/electron)를 먼저 읽으세요. Forge용 동일 기능은 [Electron Forge](/docs/guide/native/electron/forge)를 참고하세요. ## 하는 일 @@ -57,7 +57,7 @@ export default withWebviewBundle( | `channel` | `string` | — | 번들을 설치할 릴리스 채널(예: `"beta"`) | | `throwWhenBuiltinIsEmpty` | `boolean` | `true` | 설치된 번들이 하나도 없으면 예외를 던짐 | -`builtin.target`을 설정하지 않으면, 플러그인은 해석된 원격 엔드포인트를 사용하는 `remote` 타깃을 기본값으로 씁니다. `bundlesDir`은 `..` 세그먼트가 없는 상대 경로여야 합니다. 내부의 `builtin` 및 `remote` 설정은 [설정](/docs/config)을 참고하고, 설치 소스는 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. +`builtin.target`을 설정하지 않으면, 플러그인은 해석된 원격 엔드포인트를 사용하는 `remote` 타깃을 기본값으로 씁니다. `bundlesDir`은 `..` 세그먼트가 없는 상대 경로여야 합니다. 내부의 `builtin` 및 `remote` 설정은 [설정](/docs/references/configuration)을 참고하고, 설치 소스는 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. ## 네이티브 애드온을 ASAR에서 제외하기 @@ -82,28 +82,3 @@ export default withWebviewBundle( 개발 환경에서는 앱이 동작하지만 패키징 후 `@wvb/node`에 대한 모듈 누락 오류가 발생한다면, 네이티브 애드온이 ASAR로 묶인 것입니다. 위의 `asarUnpack` 글로브를 추가하고 다시 패키징하세요. - -## 다음 단계 - - - - - - - diff --git a/content/docs/guide/platforms/electron/builder.mdx b/content/docs/guide/native/electron/builder.mdx similarity index 82% rename from content/docs/guide/platforms/electron/builder.mdx rename to content/docs/guide/native/electron/builder.mdx index 12e9515..1996a32 100644 --- a/content/docs/guide/platforms/electron/builder.mdx +++ b/content/docs/guide/native/electron/builder.mdx @@ -3,7 +3,7 @@ title: Electron Builder description: Install builtin .wvb bundles at package time and keep the native @wvb/node addon in your electron-builder app. --- -`@wvb/electron-builder` integrates Webview Bundle with [electron-builder](https://www.electron.build/) packaging. It hooks electron-builder's `afterPack` step to install your builtin `.wvb` bundles into the packaged app's resources, so the bundles `@wvb/electron` serves at runtime ship with the app. You still configure electron-builder to keep the native `@wvb/node` addon out of the ASAR archive. Read the [Electron guide](/docs/guide/platforms/electron) first for the runtime setup, and see [Electron Forge](/docs/guide/platforms/electron/forge) for the Forge equivalent. +`@wvb/electron-builder` integrates Webview Bundle with [electron-builder](https://www.electron.build/) packaging. It hooks electron-builder's `afterPack` step to install your builtin `.wvb` bundles into the packaged app's resources, so the bundles `@wvb/electron` serves at runtime ship with the app. You still configure electron-builder to keep the native `@wvb/node` addon out of the ASAR archive. Read the [Electron guide](/docs/guide/native/electron) first for the runtime setup, and see [Electron Forge](/docs/guide/native/electron/forge) for the Forge equivalent. ## What it does @@ -57,7 +57,7 @@ Both `@wvb/electron-builder` and `@wvb/electron-forge` share these options: | `channel` | `string` | — | release channel to install bundles from (e.g. `"beta"`) | | `throwWhenBuiltinIsEmpty` | `boolean` | `true` | throw if zero bundles end up installed | -When `builtin.target` is not set, the plugin defaults to a `remote` target using the resolved remote endpoint. `bundlesDir` must be a relative path with no `..` segments. See [configuration](/docs/config) for the underlying `builtin` and `remote` config, and [Building a remote](/docs/guide/remote) for the install source. +When `builtin.target` is not set, the plugin defaults to a `remote` target using the resolved remote endpoint. `bundlesDir` must be a relative path with no `..` segments. See [configuration](/docs/references/configuration) for the underlying `builtin` and `remote` config, and [Building a remote](/docs/guide/remote) for the install source. ## Keep the native addon out of the ASAR @@ -82,28 +82,3 @@ The plugin handles installing the builtin bundles, so you do not need `extraReso If the app works in development but throws a missing-module error for `@wvb/node` once packaged, the native addon was packed into the ASAR. Add the `asarUnpack` glob above and repackage. - -## Next steps - - - - - - - diff --git a/content/docs/guide/native/electron/builtin.ko.mdx b/content/docs/guide/native/electron/builtin.ko.mdx new file mode 100644 index 0000000..10a054e --- /dev/null +++ b/content/docs/guide/native/electron/builtin.ko.mdx @@ -0,0 +1,41 @@ +--- +title: '내장 번들' +description: 오프라인 폴백으로 사용할 읽기 전용 .wvb 번들을 Electron 패키지 안에 포함해 배포하세요. +--- + +내장 번들은 Electron 패키지 안에 포함해 배포하는 `.wvb` 파일입니다. 읽기 전용이며 항상 존재하므로, 앱이 오프라인 상태로 시작하거나 원격으로 더 새로운 번들을 아직 가져오기 전에도 제공할 무언가가 있습니다. 런타임에서는 다운로드된 업데이트가 내장 번들보다 우선합니다. [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. + +## 내장 번들이 위치하는 곳 + +`@wvb/electron`은 `source.builtinDir`에서 내장 번들을 읽으며, 패키징된 앱에서는 기본값이 `process.resourcesPath/bundles`, 즉 `resources/bundles` 디렉터리입니다. 각 번들은 `manifest.json` 옆에 `/_.wvb` 형태로 배치됩니다. + +## `wvb builtin`으로 설치하기 + +`wvb builtin`은 번들을 출력 디렉터리에 설치합니다. 소스는 [설정](/docs/references/configuration)의 `builtin.target`으로 지정합니다. `remote` 타깃은 업데이트 서버에서 번들을 다운로드하고, `local` 타깃은 로컬 워크스페이스에서 번들을 패킹합니다. + +```sh +# 설정된 타깃에서 bundles 디렉터리로 설치합니다 +wvb builtin --out bundles + +# 또는 리모트 엔드포인트를 명시적으로 지정해 가져옵니다 +wvb builtin --endpoint https://updates.example.com --out bundles +``` + +`--out`은 앱과 함께 배포할 디렉터리를 가리키게 하세요. 모든 플래그는 [`wvb builtin`](/docs/references/cli/builtin)을 참고하세요. + +## 패키징 시점에 설치하기 + +Forge 및 Builder 통합은 패키징 과정에서 내장 번들을 대신 설치하고 패키징된 앱의 `resources/bundles`로 복사해 주므로, `.wvb` 파일을 직접 준비할 필요가 없습니다. + + + + + diff --git a/content/docs/guide/native/electron/builtin.mdx b/content/docs/guide/native/electron/builtin.mdx new file mode 100644 index 0000000..bdc35ee --- /dev/null +++ b/content/docs/guide/native/electron/builtin.mdx @@ -0,0 +1,41 @@ +--- +title: 'Builtin bundles' +description: Ship read-only .wvb bundles inside your Electron package as an offline fallback. +--- + +Builtin bundles are the `.wvb` files you ship inside your Electron package. They are read-only and always present, so your app has something to serve when it starts offline or before it has fetched a newer bundle over the air. Downloaded updates take priority over builtin bundles at runtime — see [Bundle sources](/docs/guide/core-concepts/bundle-source). + +## Where builtin bundles land + +`@wvb/electron` reads builtin bundles from `source.builtinDir`, which defaults to `process.resourcesPath/bundles` in a packaged app — the `resources/bundles` directory. Each bundle is laid out as `/_.wvb` next to a `manifest.json`. + +## Install with `wvb builtin` + +`wvb builtin` installs bundles into an output directory. The source is set by `builtin.target` in your [config](/docs/references/configuration): a `remote` target downloads them from your update server, a `local` target packs them from local workspaces. + +```sh +# Install from the configured target into a bundles directory +wvb builtin --out bundles + +# Or pull from a remote endpoint explicitly +wvb builtin --endpoint https://updates.example.com --out bundles +``` + +Point `--out` at the directory you ship with your app. See [`wvb builtin`](/docs/references/cli/builtin) for every flag. + +## Install at package time + +The Forge and Builder integrations install builtin bundles for you during packaging and copy them into the packaged app's `resources/bundles`, so you never stage `.wvb` files by hand. + + + + + diff --git a/content/docs/guide/platforms/electron/forge.ko.mdx b/content/docs/guide/native/electron/forge.ko.mdx similarity index 81% rename from content/docs/guide/platforms/electron/forge.ko.mdx rename to content/docs/guide/native/electron/forge.ko.mdx index 6dc3f87..721d092 100644 --- a/content/docs/guide/platforms/electron/forge.ko.mdx +++ b/content/docs/guide/native/electron/forge.ko.mdx @@ -3,7 +3,7 @@ title: Electron Forge description: 패킹된 .wvb 빌트인을 추가 리소스로 함께 배포하고, @wvb/electron-forge 플러그인으로 네이티브 @wvb/node 애드온을 ASAR에서 언팩합니다. --- -`@wvb/electron-forge`는 Webview Bundle을 패키징 단계에 연결하는 Electron Forge 플러그인입니다. 패킹된 `.wvb` 빌트인을 패키징된 앱의 리소스에 설치하고, 네이티브 `@wvb/node` 애드온이 런타임에 항상 사용 가능하도록 도와줍니다. 덕분에 번들 파일을 직접 배치할 필요가 없습니다. 메인 [Electron 가이드](/docs/guide/platforms/electron)와 함께 사용하세요. 이 가이드는 커스텀 프로토콜로 번들을 제공하고 무선(OTA)으로 업데이트하는 방법을 다룹니다. +`@wvb/electron-forge`는 Webview Bundle을 패키징 단계에 연결하는 Electron Forge 플러그인입니다. 패킹된 `.wvb` 빌트인을 패키징된 앱의 리소스에 설치하고, 네이티브 `@wvb/node` 애드온이 런타임에 항상 사용 가능하도록 도와줍니다. 덕분에 번들 파일을 직접 배치할 필요가 없습니다. 메인 [Electron 가이드](/docs/guide/native/electron)와 함께 사용하세요. 이 가이드는 커스텀 프로토콜로 번들을 제공하고 무선(OTA)으로 업데이트하는 방법을 다룹니다. ## 플러그인이 하는 일 @@ -35,7 +35,7 @@ export default config; | `channel` | `string` | — | 번들을 설치할 릴리스 채널 | | `throwWhenBuiltinIsEmpty` | `boolean` | `true` | 설치된 번들이 하나도 없으면 예외를 던짐 | -또한 공유 [Webview Bundle 설정](/docs/config)에서 가져온 인라인 `root`와 `builtin` 설정을 받습니다. `builtin.target`을 지정하지 않으면, 플러그인은 확인된 원격 엔드포인트를 사용하는 `remote` 타깃을 기본값으로 씁니다. +또한 공유 [Webview Bundle 설정](/docs/references/configuration)에서 가져온 인라인 `root`와 `builtin` 설정을 받습니다. `builtin.target`을 지정하지 않으면, 플러그인은 확인된 원격 엔드포인트를 사용하는 `remote` 타깃을 기본값으로 씁니다. ## 네이티브 바이너리와 추가 리소스 @@ -58,7 +58,7 @@ const config: ForgeConfig = { }; ``` -`extraResource: ['bundles']`는 빌트인 번들을 패키징된 앱의 리소스로 배포합니다. `AutoUnpackNativesPlugin`은 `@wvb/node` 네이티브 `.node` 바이너리를 ASAR에서 언팩하여 런타임에 로드할 수 있게 합니다. [`wvb pack`](/docs/guide/commands/pack)을 실행하면 그 `bundles` 디렉터리에 들어갈 `.wvb` 파일을 생성합니다. +`extraResource: ['bundles']`는 빌트인 번들을 패키징된 앱의 리소스로 배포합니다. `AutoUnpackNativesPlugin`은 `@wvb/node` 네이티브 `.node` 바이너리를 ASAR에서 언팩하여 런타임에 로드할 수 있게 합니다. [`wvb pack`](/docs/references/cli/pack)을 실행하면 그 `bundles` 디렉터리에 들어갈 `.wvb` 파일을 생성합니다. ## node_modules를 유지해 네이티브 모듈을 번들에 포함하기 @@ -80,28 +80,3 @@ const config: ForgeConfig = { `@wvb/node` 바이너리가 빠졌을 가능성이 큽니다. 위의 `extraResource`, `AutoUnpackNativesPlugin`, `packagerConfig.ignore` 재정의를 확인하세요. - -## 다음 단계 - - - - - - - diff --git a/content/docs/guide/platforms/electron/forge.mdx b/content/docs/guide/native/electron/forge.mdx similarity index 82% rename from content/docs/guide/platforms/electron/forge.mdx rename to content/docs/guide/native/electron/forge.mdx index 3cecc07..cec0848 100644 --- a/content/docs/guide/platforms/electron/forge.mdx +++ b/content/docs/guide/native/electron/forge.mdx @@ -3,7 +3,7 @@ title: Electron Forge description: Ship packed .wvb builtins as an extra resource and unpack the native @wvb/node addon from the ASAR with the @wvb/electron-forge plugin. --- -`@wvb/electron-forge` is an Electron Forge plugin that wires Webview Bundle into your packaging step. It installs your packed `.wvb` builtins into the packaged app's resources and helps keep the native `@wvb/node` addon available at runtime, so you do not stage bundle files by hand. Use it alongside the main [Electron guide](/docs/guide/platforms/electron), which covers serving bundles through a custom protocol and updating them over the air. +`@wvb/electron-forge` is an Electron Forge plugin that wires Webview Bundle into your packaging step. It installs your packed `.wvb` builtins into the packaged app's resources and helps keep the native `@wvb/node` addon available at runtime, so you do not stage bundle files by hand. Use it alongside the main [Electron guide](/docs/guide/native/electron), which covers serving bundles through a custom protocol and updating them over the air. ## What the plugin does @@ -35,7 +35,7 @@ The plugin reads its options from `WebviewBundlePluginConfig`: | `channel` | `string` | — | release channel to install bundles from | | `throwWhenBuiltinIsEmpty` | `boolean` | `true` | throw if zero bundles are installed | -It also accepts inline `root` and `builtin` config picked from the shared [Webview Bundle config](/docs/config). When `builtin.target` is unset, the plugin defaults to a `remote` target using your resolved remote endpoint. +It also accepts inline `root` and `builtin` config picked from the shared [Webview Bundle config](/docs/references/configuration). When `builtin.target` is unset, the plugin defaults to a `remote` target using your resolved remote endpoint. ## Native binary and extra resource @@ -58,7 +58,7 @@ const config: ForgeConfig = { }; ``` -`extraResource: ['bundles']` ships the builtin bundles into the packaged app's resources. `AutoUnpackNativesPlugin` unpacks the `@wvb/node` native `.node` binary out of the ASAR so it can be loaded at runtime. Run [`wvb pack`](/docs/guide/commands/pack) to produce the `.wvb` files that land in that `bundles` directory. +`extraResource: ['bundles']` ships the builtin bundles into the packaged app's resources. `AutoUnpackNativesPlugin` unpacks the `@wvb/node` native `.node` binary out of the ASAR so it can be loaded at runtime. Run [`wvb pack`](/docs/references/cli/pack) to produce the `.wvb` files that land in that `bundles` directory. ## Keep node_modules so the native module is bundled @@ -80,28 +80,3 @@ const config: ForgeConfig = { the native `@wvb/node` binary was most likely left out. Verify `extraResource`, `AutoUnpackNativesPlugin`, and the `packagerConfig.ignore` override above. - -## Next steps - - - - - - - diff --git a/content/docs/guide/platforms/electron/index.ko.mdx b/content/docs/guide/native/electron/index.ko.mdx similarity index 85% rename from content/docs/guide/platforms/electron/index.ko.mdx rename to content/docs/guide/native/electron/index.ko.mdx index eeb8770..90269a6 100644 --- a/content/docs/guide/platforms/electron/index.ko.mdx +++ b/content/docs/guide/native/electron/index.ko.mdx @@ -1,11 +1,11 @@ --- -title: Electron +title: 설정 description: 커스텀 프로토콜을 통해 Electron UI를 .wvb 번들에서 제공하고, 개발 서버 프록시와 무선(OTA) 업데이트를 활용하세요. --- `@wvb/electron`은 세 단계로 Webview Bundle을 Electron에 연결합니다. 커스텀 프로토콜을 통해 UI를 `.wvb` 번들에서 제공하고, 개발 중에는 개발 서버로 프록시하며, 필요하면 번들을 무선(OTA)으로 업데이트합니다. -## Install +## 설치 `@wvb/electron`은 Electron 15 이상이 필요하며 `@wvb/node`(사전 빌드된 N-API 바이너리 — Rust 툴체인 불필요)를 함께 설치합니다. `@wvb/cli`는 빌드 시점에 번들을 패킹합니다. @@ -36,7 +36,7 @@ yarn add -D @wvb/cli -## Register the protocol in the main process +## 메인 프로세스에서 프로토콜 등록하기 윈도우를 로드하기 전에 `wvb(...)`(`webviewBundle(...)`의 별칭)를 호출하세요. 스킴을 privileged로 등록하고, 소스를 빌드하며, 프로토콜 핸들러와 IPC를 연결합니다. @@ -100,10 +100,11 @@ bundleProtocol('app', { 각 스킴은 다음 기본값으로 privileged로 등록됩니다. `standard`, `secure`, `bypassCSP`, `allowServiceWorkers`, `supportFetchAPI`, `corsEnabled`, `codeCache`는 모두 `true`이고, `stream`은 - `false`입니다. 전체 모델은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. + `false`입니다. 전체 모델은 [프로토콜 처리](/docs/guide/core-concepts/protocol-handling)를 + 참고하세요. -## Add the preload script +## 프리로드 스크립트 추가하기 프리로드는 브리지가 소스, 원격, 업데이터 호출에 사용하는 안전한 전송 계층(`window.wvbElectron.invoke`)을 노출합니다. @@ -115,7 +116,7 @@ preload(); `webPreferences.preload`가 컴파일된 프리로드를 가리키게 하고, `contextIsolation: true`와 `nodeIntegration: false`를 유지하세요. -## Call the API from the renderer +## 렌더러에서 API 호출하기 UI에서 업데이트를 제어하려면 렌더러 코드에서 브리지를 임포트하세요. 예를 들어 "업데이트 확인" 버튼을 만들 수 있습니다. 브리지는 IPC를 통해 메인 프로세스로 전달되므로 프리로드가 로드되었을 때만 동작합니다. @@ -137,7 +138,7 @@ if (update) { `source`, `remote`, `updater`는 `@wvb/bridge`에서 가져오며, `@wvb/bridge`는 `@wvb/electron/preload`가 설치한 Electron 전송 계층을 자동으로 감지합니다. - `getUpdate`/`download`의 결과 형태는 [Node API 레퍼런스](/docs/references/node)에 있습니다. + `getUpdate`/`download`의 결과 형태는 [Node API 레퍼런스](/docs/references/api/node)에 있습니다. `wvb(...)`가 반환하는 인스턴스에서 메인 프로세스의 동일한 인터페이스에 접근할 수 있습니다. @@ -149,7 +150,7 @@ instance.updater; // Updater | null (null unless `updater` is configured) await instance.whenProtocolRegistered(); ``` -## Configure over-the-air updates +## 원격 업데이트 구성하기 원격을 가리키는 `updater` 블록을 추가하세요. 이 블록이 있어야 `instance.remote`와 `instance.updater`가 활성화됩니다. 블록을 생략하면 렌더러의 `remote.*` / `updater.*` 호출이 "not initialized" 브리지 오류로 실패합니다. @@ -178,9 +179,9 @@ updater: { }, ``` -`SignatureAlgorithm`은 `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss` 중 하나입니다. 무결성과 서명에 대한 자세한 내용은 [원격 번들](/docs/guide/remote-bundles)을, 서버를 세우는 방법(테스트용 로컬 서버 포함)은 [원격 서버 구축](/docs/guide/remote)을 참고하세요. +`SignatureAlgorithm`은 `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss` 중 하나입니다. 무결성과 서명에 대한 자세한 내용은 [원격 번들](/docs/guide/core-concepts/over-the-air)을, 서버를 세우는 방법(테스트용 로컬 서버 포함)은 [원격 서버 구축](/docs/guide/remote)을 참고하세요. -## Where bundles live +## 번들이 저장되는 위치 `source`는 `builtinDir`(함께 배포되는 읽기 전용)와 `remoteDir`(다운로드된 업데이트)를 받으며, 둘 다 Electron에 맞는 기본값을 가집니다. @@ -189,9 +190,9 @@ updater: { | `builtinDir` | 패키징된 경우 `process.resourcesPath/bundles`, 그 외에는 `process.cwd()/bundles` | | `remoteDir` | `app.getPath('userData')/bundles` | -다운로드된 버전이 builtin 버전보다 우선하므로, `updater.download(...)`와 `updater.install(...)` 후에는 설치된 업데이트가 자동으로 제공됩니다. 해석 방식에 대한 자세한 내용은 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +다운로드된 버전이 builtin 버전보다 우선하므로, `updater.download(...)`와 `updater.install(...)` 후에는 설치된 업데이트가 자동으로 제공됩니다. 해석 방식에 대한 자세한 내용은 [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. -## Pack and ship bundles +## 번들을 패킹하고 배포하기 웹 앱을 빌드한 다음, 산출물을 `builtinDir`로 지정한 디렉터리에 패킹하세요. @@ -200,53 +201,28 @@ updater: { npx wvb pack ./dist --outfile bundles/app/app_1.0.0.wvb ``` -프로토콜이 런타임에 번들을 제공할 수 있도록 `bundles` 디렉터리를 앱과 함께 배포하세요. 이 파일들을 패키지에 스테이징하는 방법은 패키징 도구에 따라 다릅니다 — [패키징](#packaging)을 참고하세요. +프로토콜이 런타임에 번들을 제공할 수 있도록 `bundles` 디렉터리를 앱과 함께 배포하세요. 이 파일들을 패키지에 스테이징하는 방법은 패키징 도구에 따라 다릅니다 — [패키징](#패키징)을 참고하세요. -## Packaging +## 패키징 `@wvb/electron`을 사용하는 Electron 앱을 패키징하려면 최종 빌드에 두 가지가 포함되어야 합니다. builtin `bundles` 디렉터리가 앱과 함께 배포되어야 하고, 네이티브 `@wvb/node` 바이너리가 런타임에 로드될 수 있도록 ASAR 아카이브 밖에 있어야 합니다. 전용 통합이 두 가지를 모두 처리하고 패키징 시점에 builtin 번들을 설치하므로, `.wvb` 파일을 직접 스테이징할 필요가 없습니다. -## Troubleshooting +## 문제 해결 - **빈 윈도우 또는 `ERR_FAILED`** — `wvb(...)`가 실행되고 `whenProtocolRegistered()`가 `loadURL` 전에 resolve되는지, 그리고 URL의 번들 이름이 패킹된 파일과 일치하는지 확인하세요(`app://app.wvb` → 번들 `app`). - **렌더러가 브리지 API에 접근하지 못함** — 프리로드가 로드되지 않았습니다. `webPreferences.preload`와 프리로드가 `preload()`를 호출하는지 확인하세요. -- **`remote_not_initialized` / `updater_not_initialized`** — `remote.*` / `updater.*` 호출이 있었지만 `wvb(...)`에 `updater` 블록을 전달하지 않았습니다. [무선(OTA) 업데이트](#configure-over-the-air-updates) 설정을 추가하세요. -- **개발에서는 동작하지만 패키징하면 실패함** — `bundles` 리소스나 네이티브 `@wvb/node` 바이너리가 패키지에 포함되지 않았습니다. [패키징](#packaging)을 참고하세요. - -## Next steps - - - - - - - +- **`remote_not_initialized` / `updater_not_initialized`** — `remote.*` / `updater.*` 호출이 있었지만 `wvb(...)`에 `updater` 블록을 전달하지 않았습니다. [무선(OTA) 업데이트](#원격-업데이트-구성하기) 설정을 추가하세요. +- **개발에서는 동작하지만 패키징하면 실패함** — `bundles` 리소스나 네이티브 `@wvb/node` 바이너리가 패키지에 포함되지 않았습니다. [패키징](#패키징)을 참고하세요. diff --git a/content/docs/guide/platforms/electron/index.mdx b/content/docs/guide/native/electron/index.mdx similarity index 88% rename from content/docs/guide/platforms/electron/index.mdx rename to content/docs/guide/native/electron/index.mdx index 4c60aef..45be35c 100644 --- a/content/docs/guide/platforms/electron/index.mdx +++ b/content/docs/guide/native/electron/index.mdx @@ -1,5 +1,5 @@ --- -title: Electron +title: Setup description: Serve your Electron UI from a .wvb bundle through a custom protocol, with dev-server proxying and over-the-air updates. --- @@ -100,7 +100,7 @@ bundleProtocol('app', { Each scheme is registered as privileged with these defaults: `standard`, `secure`, `bypassCSP`, `allowServiceWorkers`, `supportFetchAPI`, `corsEnabled`, `codeCache` all `true`; `stream` `false`. - See [Protocol handling](/docs/guide/protocol-handling) for the full model. + See [Protocol handling](/docs/guide/core-concepts/protocol-handling) for the full model. ## Add the preload script @@ -137,7 +137,7 @@ if (update) { `source`, `remote`, and `updater` come from `@wvb/bridge`, which auto-detects the Electron transport installed by `@wvb/electron/preload`. Result shapes for `getUpdate`/`download` are in - the [Node API reference](/docs/references/node). + the [Node API reference](/docs/references/api/node). The same surfaces are reachable in the main process on the instance returned by `wvb(...)`: @@ -178,7 +178,7 @@ updater: { }, ``` -`SignatureAlgorithm` is one of `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss`. See [Remote bundles](/docs/guide/remote-bundles) for integrity and signature details, and [Building a remote](/docs/guide/remote) for standing a server up — including a local one for testing. +`SignatureAlgorithm` is one of `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss`. See [Remote bundles](/docs/guide/core-concepts/over-the-air) for integrity and signature details, and [Building a remote](/docs/guide/remote) for standing a server up — including a local one for testing. ## Where bundles live @@ -189,7 +189,7 @@ updater: { | `builtinDir` | `process.resourcesPath/bundles` when packaged, else `process.cwd()/bundles` | | `remoteDir` | `app.getPath('userData')/bundles` | -Downloaded versions take priority over builtin ones, so an installed update is served automatically after `updater.download(...)` and `updater.install(...)`. See [Bundle sources](/docs/guide/bundle-sources) for resolution details. +Downloaded versions take priority over builtin ones, so an installed update is served automatically after `updater.download(...)` and `updater.install(...)`. See [Bundle sources](/docs/guide/core-concepts/bundle-source) for resolution details. ## Pack and ship bundles @@ -209,12 +209,12 @@ Packaging an Electron app that uses `@wvb/electron` needs two things to land in @@ -225,28 +225,3 @@ Packaging an Electron app that uses `@wvb/electron` needs two things to land in - **Renderer cannot reach the bridge API** — the preload is not loaded. Check `webPreferences.preload` and that it calls `preload()`. - **`remote_not_initialized` / `updater_not_initialized`** — a `remote.*` / `updater.*` call was made but no `updater` block was passed to `wvb(...)`. Add the [over-the-air updates](#configure-over-the-air-updates) config. - **Works in dev, fails when packaged** — the `bundles` resource or the native `@wvb/node` binary was not included in the package. See [Packaging](#packaging). - -## Next steps - - - - - - - diff --git a/content/docs/guide/native/electron/local-development.ko.mdx b/content/docs/guide/native/electron/local-development.ko.mdx new file mode 100644 index 0000000..447377f --- /dev/null +++ b/content/docs/guide/native/electron/local-development.ko.mdx @@ -0,0 +1,65 @@ +--- +title: '로컬 개발' +description: 번들 호스트를 Vite 개발 서버로 프록시하여 핫 리로드를 지원하고, app.isPackaged에 따라 프로토콜을 전환하세요. +--- + +개발 중에는 패킹된 `.wvb`이 아니라 핫 리로드가 필요합니다. 번들 스킴에 `localProtocol`을 등록하면, 웹뷰는 동일한 URL을 로드하면서 요청은 프레임워크 개발 서버로 프록시됩니다. + +## 개발 서버로 프록시하기 + +`localProtocol(scheme, { hosts })`은 매핑된 각 호스트를 실행 중인 개발 서버로 전달하므로, 프로덕션에서 사용하는 것과 동일한 `app://app.wvb` URL을 통해 수정 사항이 실시간으로 리로드됩니다. `hosts`은 `.wvb`을 개발 서버 오리진으로 매핑합니다. + +## `app.isPackaged`로 전환하기 + +개발에서는 `localProtocol`을, 프로덕션에서는 `bundleProtocol`을 같은 스킴에 등록하세요. 렌더러 URL은 절대 바뀌지 않으며, 그 뒤의 핸들러만 바뀝니다. + +```ts title="src/main.ts" +import path from 'node:path'; +import { app, BrowserWindow } from 'electron'; +import { bundleProtocol, localProtocol, wvb } from '@wvb/electron'; + +const DEV_SERVER_URL = 'http://localhost:5173'; + +const instance = wvb({ + source: { + builtinDir: path.join(process.resourcesPath, 'bundles'), + }, + protocols: app.isPackaged + ? // Prod: serve `app://app.wvb/...` from the packed bundle. + [bundleProtocol('app')] + : // Dev: proxy `app://app.wvb/...` to the Vite dev server for hot reload. + [localProtocol('app', { hosts: { 'app.wvb': DEV_SERVER_URL } })], +}); + +async function createWindow() { + await instance.whenProtocolRegistered(); + const win = new BrowserWindow({ width: 800, height: 600 }); + await win.loadURL('app://app.wvb'); +} + +app.whenReady().then(createWindow); +``` + + + 하나의 스킴은 한 번에 하나의 핸들러만 소유할 수 있으므로, 둘 다 등록하지 말고 `protocols` 배열을 + 조건에 따라 구성하세요. `loadURL` 전에는 항상 `await instance.whenProtocolRegistered()`를 + 호출하세요. + + +## 여러 호스트 + +`hosts`은 항목 수에 제한이 없습니다 — 각 윈도우의 번들 호스트를 저마다의 개발 서버로 매핑한 다음, 호스트별로 로드하세요. + +```ts +localProtocol('app', { + hosts: { + 'app.wvb': 'http://localhost:5173', + 'admin.wvb': 'http://localhost:5174', + }, +}); + +// win.loadURL('app://app.wvb'); +// adminWin.loadURL('app://admin.wvb'); +``` + +프로덕션에서는 `bundleProtocol('app')`이 소스에 있는 `app`과 `admin` 번들에서 둘 다 제공합니다. diff --git a/content/docs/guide/native/electron/local-development.mdx b/content/docs/guide/native/electron/local-development.mdx new file mode 100644 index 0000000..36c4fff --- /dev/null +++ b/content/docs/guide/native/electron/local-development.mdx @@ -0,0 +1,64 @@ +--- +title: Local development +description: Proxy your bundle host to the Vite dev server for hot reload, and switch protocols on app.isPackaged. +--- + +In development you want hot reload, not a packed `.wvb`. Register a `localProtocol` on your bundle scheme so the webview loads the same URL while requests proxy to your framework dev server. + +## Proxy to the dev server + +`localProtocol(scheme, { hosts })` forwards each mapped host to a running dev server, so edits reload live over the same `app://app.wvb` URL you use in production. `hosts` maps `.wvb` to the dev server origin. + +## Switch on `app.isPackaged` + +Register `localProtocol` in development and `bundleProtocol` in production, both on the same scheme. The renderer URL never changes — only the handler behind it does. + +```ts title="src/main.ts" +import path from 'node:path'; +import { app, BrowserWindow } from 'electron'; +import { bundleProtocol, localProtocol, wvb } from '@wvb/electron'; + +const DEV_SERVER_URL = 'http://localhost:5173'; + +const instance = wvb({ + source: { + builtinDir: path.join(process.resourcesPath, 'bundles'), + }, + protocols: app.isPackaged + ? // Prod: serve `app://app.wvb/...` from the packed bundle. + [bundleProtocol('app')] + : // Dev: proxy `app://app.wvb/...` to the Vite dev server for hot reload. + [localProtocol('app', { hosts: { 'app.wvb': DEV_SERVER_URL } })], +}); + +async function createWindow() { + await instance.whenProtocolRegistered(); + const win = new BrowserWindow({ width: 800, height: 600 }); + await win.loadURL('app://app.wvb'); +} + +app.whenReady().then(createWindow); +``` + + + Only one handler can own a scheme at a time, so build the `protocols` array conditionally rather + than registering both. Always `await instance.whenProtocolRegistered()` before `loadURL`. + + +## Multiple hosts + +`hosts` takes any number of entries — map each window's bundle host to its own dev server, then load each by host. + +```ts +localProtocol('app', { + hosts: { + 'app.wvb': 'http://localhost:5173', + 'admin.wvb': 'http://localhost:5174', + }, +}); + +// win.loadURL('app://app.wvb'); +// adminWin.loadURL('app://admin.wvb'); +``` + +In production, `bundleProtocol('app')` serves both from the `app` and `admin` bundles in your source. diff --git a/content/docs/guide/native/electron/meta.json b/content/docs/guide/native/electron/meta.json new file mode 100644 index 0000000..7051cfc --- /dev/null +++ b/content/docs/guide/native/electron/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Electron", + "pages": ["index", "local-development", "builtin", "---Integration---", "forge", "builder"] +} diff --git a/content/docs/guide/native/electron/meta.ko.json b/content/docs/guide/native/electron/meta.ko.json new file mode 100644 index 0000000..859f3d5 --- /dev/null +++ b/content/docs/guide/native/electron/meta.ko.json @@ -0,0 +1,4 @@ +{ + "title": "Electron", + "pages": ["index", "local-development", "builtin", "---통합---", "forge", "builder"] +} diff --git a/content/docs/guide/native/ios/builtin.ko.mdx b/content/docs/guide/native/ios/builtin.ko.mdx new file mode 100644 index 0000000..2d69cff --- /dev/null +++ b/content/docs/guide/native/ios/builtin.ko.mdx @@ -0,0 +1,30 @@ +--- +title: '내장 번들' +description: wvb builtin으로 내장 .wvb 번들을 iOS 앱 리소스에 설치하면 Source가 오프라인으로 이를 제공합니다. +--- + +iOS Source는 내장 번들을 `/bundles`에서 읽습니다. 앱 번들의 읽기 전용 `bundles` 폴더입니다. `wvb builtin`으로 그 위치에 설치한 뒤, 폴더를 폴더 레퍼런스로 배포하세요. + +## 설치 + +`wvb builtin`은 설정에 지정된 타깃(`builtin.target`, 기본값은 리모트)에서 번들을 받아와 `manifest.json`과 함께 번들마다 `/_.wvb` 파일을 하나씩 씁니다. + +```sh +# Tuist 프로젝트: 프로젝트를 자동 감지하고 폴더 레퍼런스를 연결 +wvb builtin --ios + +# 그 외: 앱 타깃에 추가할 폴더에 설치 +wvb builtin --out path/to/App/bundles +``` + +폴더 레퍼런스는 앱 리소스의 `bundles/`에 놓여야 하므로, 산출물 폴더 이름을 `bundles`로 지정하세요. + +## 폴더 레퍼런스로 배포하기 + +Source는 디렉터리를 있는 그대로 로드하므로, 앱 타깃에 **폴더 레퍼런스**(Xcode의 파란색 폴더)로 추가하세요. 그룹으로 추가하면 매니페스트가 가리키는 `/_.wvb` 구조가 평탄화됩니다. `--ios` 프리셋은 `Project.swift`에 `folderReference`를 추가해 이를 대신 처리합니다(Tuist 프로젝트). + +다른 위치에서 읽으려면 `WebViewBundleConfig.source`에 `builtinDir`을 설정하세요. iOS 설정 가이드의 [Sources 섹션](/docs/guide/native/ios#sources)을 참고하세요. + +## 리모트 또는 로컬 + +설치 소스는 플래그가 아니라 설정으로 정해집니다. `builtin.target`은 `remote`(기본값)와 `local`의 구별 합집합입니다. `--endpoint`, `--channel`, `--concurrency`, `--progress`는 리모트 타깃에만 적용됩니다. 모든 옵션은 [wvb builtin](/docs/references/cli/builtin)을 참고하세요. diff --git a/content/docs/guide/native/ios/builtin.mdx b/content/docs/guide/native/ios/builtin.mdx new file mode 100644 index 0000000..fac2e2f --- /dev/null +++ b/content/docs/guide/native/ios/builtin.mdx @@ -0,0 +1,30 @@ +--- +title: Builtin bundles +description: Install builtin .wvb bundles into an iOS app's resources with wvb builtin so the Source serves them offline. +--- + +The iOS Source reads builtin bundles from `/bundles` — the app bundle's read-only `bundles` folder. Install them there with `wvb builtin`, then ship the folder as a folder reference. + +## Install + +`wvb builtin` pulls bundles from the target set in your config (`builtin.target`, remote by default) and writes a `manifest.json` plus one `/_.wvb` file per bundle. + +```sh +# Tuist project: auto-detect the project and wire the folder reference +wvb builtin --ios + +# Otherwise: install into a folder you add to the app target +wvb builtin --out path/to/App/bundles +``` + +The folder reference must land at `bundles/` in the app's resources, so name the output folder `bundles`. + +## Ship it as a folder reference + +The Source loads the directory as-is, so add it to the app target as a **folder reference** (blue folder in Xcode) — a group would flatten the `/_.wvb` layout the manifest points at. The `--ios` preset does this for you by adding a `folderReference` to `Project.swift` (Tuist projects). + +To read from a different location, set `builtinDir` on `WebViewBundleConfig.source`. See the [Sources section](/docs/guide/native/ios#sources) of the iOS setup guide. + +## Remote or local + +The install source is config-driven, not a flag: `builtin.target` is a discriminated union of `remote` (the default) and `local`. `--endpoint`, `--channel`, `--concurrency`, and `--progress` apply to the remote target only. See [wvb builtin](/docs/references/cli/builtin) for every option. diff --git a/content/docs/guide/platforms/ios.ko.mdx b/content/docs/guide/native/ios/index.ko.mdx similarity index 80% rename from content/docs/guide/platforms/ios.ko.mdx rename to content/docs/guide/native/ios/index.ko.mdx index 24c63f1..d7e8fff 100644 --- a/content/docs/guide/platforms/ios.ko.mdx +++ b/content/docs/guide/native/ios/index.ko.mdx @@ -1,5 +1,5 @@ --- -title: iOS +title: 설정 description: webview-bundle-ios Swift 패키지로 WKWebView에서 Webview Bundle 아카이브를 제공하고 업데이트합니다. --- @@ -8,7 +8,7 @@ description: webview-bundle-ios Swift 패키지로 WKWebView에서 Webview Bundl 가리키면, 이 패키지가 디스크에 있는 번들에서 모든 요청에 응답합니다. 오프라인 우선으로 동작하며, 업데이터가 백그라운드에서 더 새로운 번들을 받아오므로 앱스토어 출시가 필요 없습니다. -## Requirements +## 요구 사항 | Requirement | Value | | ---------------- | ---------------------------------------------------------- | @@ -18,13 +18,13 @@ description: webview-bundle-ios Swift 패키지로 WKWebView에서 Webview Bundl 이 패키지는 `WebViewBundleLibrary`라는 UniFFI 생성 모듈을 통해 Rust 코어에 바인딩되며, 둘 다 `WebViewBundle` 모듈로 노출됩니다. 공유 코어가 각 플랫폼에 어떻게 도달하는지는 -[플랫폼 통합](/docs/guide/platform-integration)을 참고하세요. +[플랫폼 통합](/docs/guide/core-concepts/glue-to-native)을 참고하세요. -패키지를 의존성으로 추가하세요(네이티브 FFI는 아래 설치 스크립트가 연결합니다). +패키지를 의존성으로 추가하세요. ```swift title="Package.swift" dependencies: [ - .package(path: "../webview-bundle-ios"), + .package(url: "https://github.com/webview-bundle/webview-bundle-ios", from: "0.1.0"), ], targets: [ .target(name: "App", dependencies: [ @@ -33,30 +33,9 @@ targets: [ ] ``` -## Install the native FFI +Xcode 앱 프로젝트라면 **File → Add Package Dependencies…** 메뉴에서 같은 URL을 추가하세요. -빌드하기 전에 릴리스에서 네이티브 바이너리를 해석하세요. - -```sh -node scripts/install.mjs latest # 가장 높은 ffi/* 릴리스 -node scripts/install.mjs 0.1.0 # -> ffi/0.1.0 태그 -node scripts/install.mjs ffi/0.1.0 # 명시적 릴리스 태그 -``` - -이 스크립트는 해석된 태그의 GitHub 릴리스를 읽어 다음을 수행합니다. - -- `apple.zip` 자산에서 생성된 Swift 바인딩을 추출해 `Sources/WebViewBundle/`에 넣습니다. -- `WebViewBundleFFI.xcframework.zip` 자산의 SHA-256 체크섬을 해석하고(GitHub가 보고하는 다이제스트를 - 우선 사용하며, 없으면 직접 다운로드해 해시함), 그 체크섬과 태그를 `Package.swift`에 기록합니다. - -`PATH`에 `unzip`이 필요합니다. 소스 저장소를 재정의하고, 비공개 저장소는 인증하세요. - -```sh -export GITHUB_TOKEN=ghp_xxx # 비공개 소스 저장소의 경우, 또는 GH_TOKEN -node scripts/install.mjs latest --repo webview-bundle/webview-bundle -``` - -## Quick start +## 빠른 시작 한 번 설정하고, `WKWebViewConfiguration`에 스킴을 등록한 뒤, 엔트리 URL을 로드하세요. **커스텀 스킴**을 사용하세요. `http`와 `https`는 예약되어 있어 초기화 시 거부됩니다. @@ -104,7 +83,7 @@ let wvb = WebViewBundle.shared // 아직 설정되지 않았으면 pre let maybe = WebViewBundle.safeShared // 아직 설정되지 않았으면 nil ``` -## How serving works +## 제공이 동작하는 방식 각 스킴에는 `setURLSchemeHandler(_:forURLScheme:)`으로 등록된 `WKURLSchemeHandler`가 부여됩니다. 이 핸들러는 각 요청을 Rust 코어에 매핑하고, Rust 코어는 디스크에 있는 번들에서 응답합니다. @@ -127,7 +106,7 @@ do { 프로토콜 종류는 두 가지입니다. `.bundle`은 번들 엔트리를 제공하고, `.local`은 로컬 HTTP 서버로 프록시합니다(전체 요청 호스트를 `hosts` 맵과 대조). 모델은 -[프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. +[프로토콜 처리](/docs/guide/core-concepts/protocol-handling)를 참고하세요. ```swift WebViewBundleConfig(protocols: [ @@ -136,7 +115,7 @@ WebViewBundleConfig(protocols: [ ]) ``` -## The bridge +## 브리지 `install(on:)`은 JavaScript-네이티브 브리지도 함께 연결합니다. 웹 앱은 `window.webkit.messageHandlers.wvbIos`로 메시지를 보내며, 브리지는 **메인 프레임 메시지만** 받습니다 @@ -149,9 +128,9 @@ window.webkit.messageHandlers.wvbIos.postMessage({ }); ``` -## Sources +## 소스 -각 앱은 두 개의 소스에서 읽습니다. 전체 모델은 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +각 앱은 두 개의 소스에서 읽습니다. 전체 모델은 [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. - **Builtin** — 앱 안에 함께 배포되는 읽기 전용 번들입니다. 기본 디렉터리는 앱 번들의 `/bundles` 폴더입니다. 파일이 그 위치에 놓이도록 폴더 레퍼런스로 배포하세요. @@ -177,7 +156,7 @@ WebViewBundleConfig( 클래스로 해석됩니다. 앱 번들을 의미할 때는 `Foundation.Bundle`을 명시적으로 쓰세요. -## OTA updates +## 원격 업데이트 `WebViewBundleConfig.updater`를 설정하면 패키지가 `Remote`와 `Updater`를 만들어 주며, 각각 `instance.remote`와 `instance.updater`로 접근할 수 있습니다. @@ -227,21 +206,5 @@ await webView.evaluateJavaScript("location.reload()") 서명 실패는 FFI 오류 `Error.Signature(message:)`로 나타납니다. -원격 HTTP 규약, 무결성, 서명 세부 사항은 [원격 번들](/docs/guide/remote-bundles)과 +원격 HTTP 규약, 무결성, 서명 세부 사항은 [원격 번들](/docs/guide/core-concepts/over-the-air)과 [원격 구축하기](/docs/guide/remote)를 참고하세요. - -## Next steps - - - - - - diff --git a/content/docs/guide/platforms/ios.mdx b/content/docs/guide/native/ios/index.mdx similarity index 81% rename from content/docs/guide/platforms/ios.mdx rename to content/docs/guide/native/ios/index.mdx index d376dcc..5157b3a 100644 --- a/content/docs/guide/platforms/ios.mdx +++ b/content/docs/guide/native/ios/index.mdx @@ -1,5 +1,5 @@ --- -title: iOS +title: Setup description: Serve and update Webview Bundle archives in a WKWebView using the webview-bundle-ios Swift package. --- @@ -17,14 +17,14 @@ with the updater pulling newer bundles in the background without an App Store re | SwiftPM product | `WebViewBundle` | The package binds the Rust core through a UniFFI-generated module named `WebViewBundleLibrary`, both -exposed under the `WebViewBundle` module. See [Platform integration](/docs/guide/platform-integration) +exposed under the `WebViewBundle` module. See [Platform integration](/docs/guide/core-concepts/glue-to-native) for how the shared core reaches each platform. -Add the package as a dependency (the install script below wires in the native FFI): +Add the package as a dependency: ```swift title="Package.swift" dependencies: [ - .package(path: "../webview-bundle-ios"), + .package(url: "https://github.com/webview-bundle/webview-bundle-ios", from: "0.1.0"), ], targets: [ .target(name: "App", dependencies: [ @@ -33,29 +33,7 @@ targets: [ ] ``` -## Install the native FFI - -Resolve the native binary from a release before you build: - -```sh -node scripts/install.mjs latest # highest ffi/* release -node scripts/install.mjs 0.1.0 # -> tag ffi/0.1.0 -node scripts/install.mjs ffi/0.1.0 # explicit release tag -``` - -The script reads the GitHub release for the resolved tag and: - -- Extracts the generated Swift bindings from the `apple.zip` asset into `Sources/WebViewBundle/`. -- Resolves the SHA-256 checksum of the `WebViewBundleFFI.xcframework.zip` asset (preferring the digest - GitHub reports, falling back to downloading and hashing) and writes the checksum and tag into - `Package.swift`. - -It needs `unzip` on `PATH`. Override the source repo and authenticate for private repos: - -```sh -export GITHUB_TOKEN=ghp_xxx # or GH_TOKEN, for a private source repo -node scripts/install.mjs latest --repo webview-bundle/webview-bundle -``` +In an Xcode app project, add the same URL through **File → Add Package Dependencies…** instead. ## Quick start @@ -129,7 +107,7 @@ do { Two protocol kinds exist — `.bundle` serves bundle entries, `.local` proxies to a local HTTP server (matching the full request host against the `hosts` map). See -[Protocol handling](/docs/guide/protocol-handling) for the model. +[Protocol handling](/docs/guide/core-concepts/protocol-handling) for the model. ```swift WebViewBundleConfig(protocols: [ @@ -153,7 +131,7 @@ window.webkit.messageHandlers.wvbIos.postMessage({ ## Sources -Each app reads from two sources. See [Bundle sources](/docs/guide/bundle-sources) for the full model. +Each app reads from two sources. See [Bundle sources](/docs/guide/core-concepts/bundle-source) for the full model. - **Builtin** — read-only bundles shipped inside the app. Default dir is the app bundle's `/bundles` folder; ship it as a folder reference so files land there. @@ -229,24 +207,4 @@ await webView.evaluateJavaScript("location.reload()") A failed signature surfaces as the FFI error `Error.Signature(message:)`. For the remote HTTP contract, integrity, and signing details, see -[Remote bundles](/docs/guide/remote-bundles) and [Building a remote](/docs/guide/remote). - -## Next steps - - - - - - +[Remote bundles](/docs/guide/core-concepts/over-the-air) and [Building a remote](/docs/guide/remote). diff --git a/content/docs/guide/native/ios/local-development.ko.mdx b/content/docs/guide/native/ios/local-development.ko.mdx new file mode 100644 index 0000000..0e054ca --- /dev/null +++ b/content/docs/guide/native/ios/local-development.ko.mdx @@ -0,0 +1,45 @@ +--- +title: '로컬 개발' +description: 로컬 프로토콜을 등록해 WKWebView가 번들 스킴으로 개발 서버를 로드하게 하고, #if DEBUG로 전환합니다. +--- + +개발 중에는 `WKWebView`가 패킹된 `.wvb`이 아니라 실행 중인 개발 서버를 로드하기를 원할 것입니다. 번들 스킴에 `.local` 프로토콜을 등록하면, 동일한 `app://app.wvb` URL이 개발 서버로 프록시되고 수정 사항이 실시간으로 다시 로드됩니다. + +## 개발 서버 프록시하기 + +`.local(scheme:hosts:)`은 매핑된 각 호스트를 실행 중인 개발 서버로 전달합니다. `hosts` 키는 요청 호스트 **전체**와 대조되므로, 로드하는 전체 호스트인 `app.wvb`을 개발 서버 오리진에 매핑하세요. + +`protocols` 배열은 동일한 스킴에 대해 조건부로 구성하세요. `#if DEBUG`에서는 `.local`을, 릴리스에서는 `.bundle`을 사용합니다. 로드되는 URL은 절대 바뀌지 않고, 그 뒤의 핸들러만 바뀝니다. + +```swift title="ContentView.swift" +import WebKit +import WebViewBundle + +#if DEBUG +let protocols: [WebViewBundleProtocol] = [ + // Dev: proxy `app://app.wvb/...` to the Vite dev server for hot reload. + .local(scheme: "app", hosts: ["app.wvb": "http://localhost:5173"]), +] +#else +let protocols: [WebViewBundleProtocol] = [ + // Prod: serve `app://app.wvb/...` from the packed bundle. + .bundle(scheme: "app"), +] +#endif + +let instance = try WebViewBundle.configure(WebViewBundleConfig(protocols: protocols)) + +let config = WKWebViewConfiguration() +instance.install(on: config) + +let webView = WKWebView(frame: .zero, configuration: config) +webView.load(URLRequest(url: URL(string: "app://app.wvb")!)) +``` + +## App Transport Security + + + 개발 서버가 평문 `http://localhost`라면, iOS App Transport Security가 평문 요청을 차단할 수 + 있습니다. 개발 빌드용으로 앱의 `Info.plist`에서 `NSAppTransportSecurity` 아래에 + `NSAllowsLocalNetworking` 예외를 추가하세요. + diff --git a/content/docs/guide/native/ios/local-development.mdx b/content/docs/guide/native/ios/local-development.mdx new file mode 100644 index 0000000..5489d05 --- /dev/null +++ b/content/docs/guide/native/ios/local-development.mdx @@ -0,0 +1,45 @@ +--- +title: Local development +description: Register a local protocol so WKWebView loads your dev server over the bundle scheme, and switch on #if DEBUG. +--- + +In development you want `WKWebView` to load your running dev server, not a packed `.wvb`. Register a `.local` protocol on your bundle scheme so the same `app://app.wvb` URL proxies to the dev server and edits reload live. + +## Proxy the dev server + +`.local(scheme:hosts:)` forwards each mapped host to a running dev server. The `hosts` key is matched against the **entire** request host, so map `app.wvb` — the full host you load — to the dev server origin. + +Build the `protocols` array conditionally on the same scheme: `.local` in `#if DEBUG`, `.bundle` in release. The loaded URL never changes — only the handler behind it does. + +```swift title="ContentView.swift" +import WebKit +import WebViewBundle + +#if DEBUG +let protocols: [WebViewBundleProtocol] = [ + // Dev: proxy `app://app.wvb/...` to the Vite dev server for hot reload. + .local(scheme: "app", hosts: ["app.wvb": "http://localhost:5173"]), +] +#else +let protocols: [WebViewBundleProtocol] = [ + // Prod: serve `app://app.wvb/...` from the packed bundle. + .bundle(scheme: "app"), +] +#endif + +let instance = try WebViewBundle.configure(WebViewBundleConfig(protocols: protocols)) + +let config = WKWebViewConfiguration() +instance.install(on: config) + +let webView = WKWebView(frame: .zero, configuration: config) +webView.load(URLRequest(url: URL(string: "app://app.wvb")!)) +``` + +## App Transport Security + + + If your dev server is plain `http://localhost`, iOS App Transport Security may block the cleartext + request. Add an `NSAllowsLocalNetworking` exception under `NSAppTransportSecurity` in your app's + `Info.plist` for development builds. + diff --git a/content/docs/guide/native/ios/meta.json b/content/docs/guide/native/ios/meta.json new file mode 100644 index 0000000..620022e --- /dev/null +++ b/content/docs/guide/native/ios/meta.json @@ -0,0 +1 @@ +{ "title": "iOS", "pages": ["index", "local-development", "builtin"] } diff --git a/content/docs/guide/native/ios/meta.ko.json b/content/docs/guide/native/ios/meta.ko.json new file mode 100644 index 0000000..620022e --- /dev/null +++ b/content/docs/guide/native/ios/meta.ko.json @@ -0,0 +1 @@ +{ "title": "iOS", "pages": ["index", "local-development", "builtin"] } diff --git a/content/docs/guide/native/tauri/builtin.ko.mdx b/content/docs/guide/native/tauri/builtin.ko.mdx new file mode 100644 index 0000000..a39e39d --- /dev/null +++ b/content/docs/guide/native/tauri/builtin.ko.mdx @@ -0,0 +1,48 @@ +--- +title: '내장 번들' +description: wvb builtin으로 빌드 시점에 빌트인 .wvb 번들을 Tauri 앱에 설치하고 Tauri 리소스로 함께 배포하세요. +--- + +하나 이상의 `.wvb` 번들을 Tauri 앱 안에 포함하여 배포하면, 원격 업데이트가 이루어지기 전에도 웹뷰가 오프라인으로 로드됩니다. `wvb builtin`으로 빌드 시점에 번들을 설치한 뒤, 해당 디렉터리를 Tauri 리소스로 배포하세요. + +## 빌드 시점에 번들 설치하기 + +`wvb builtin`은 `--out`에 `manifest.json`과 함께 번들마다 하나씩 `/_.wvb` 파일을 기록합니다. 리소스로 배포할 디렉터리를 가리키도록 설정하세요. + +```sh +wvb builtin --out src-tauri/bundles +``` + +소스는 [설정](/docs/references/configuration)의 `builtin.target`으로 선택됩니다. `remote` 타겟은 엔드포인트에서 다운로드하고, `local` 타겟은 워크스페이스의 번들을 패킹합니다. 모든 플래그는 [`wvb builtin` 레퍼런스](/docs/references/cli/builtin)를 참고하세요. + +## 디렉터리를 리소스로 배포하기 + +Tauri가 번들 디렉터리를 앱에 패킹하도록 `bundle.resources` 아래에 해당 디렉터리를 나열하세요. + +```json title="src-tauri/tauri.conf.json" +{ + "bundle": { + "resources": ["bundles/**/*"] + } +} +``` + +## 런타임에 번들이 해석되는 위치 + +기본적으로 플러그인은 앱의 리소스 디렉터리 내 `bundles` 디렉터리에서 빌트인 번들을 제공합니다. 위치는 `Source`에서 재정의하세요. + +```rust title="src-tauri/src/lib.rs" +use wvb_tauri::Source; + +// 정적 경로. Tauri의 path API를 통해 해석됩니다. +Source::new().builtin_dir("$RESOURCE/bundles"); + +// 또는 AppHandle에서 계산할 수도 있습니다. +Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles"))); +``` + + + Android에서 빌트인 번들은 APK 안에 파일시스템이 직접 읽을 수 없는 `asset://` 리소스로 포함되어 + 배포됩니다. 플러그인은 첫 요청 시 APK에서 번들을 추출하므로, 앱에서 `tauri_plugin_fs::init()`도 + 함께 등록해야 합니다. [Tauri 모바일](/docs/guide/native/tauri/mobile)을 참고하세요. + diff --git a/content/docs/guide/native/tauri/builtin.mdx b/content/docs/guide/native/tauri/builtin.mdx new file mode 100644 index 0000000..3628693 --- /dev/null +++ b/content/docs/guide/native/tauri/builtin.mdx @@ -0,0 +1,48 @@ +--- +title: Builtin bundles +description: Install builtin .wvb bundles into a Tauri app at build time with wvb builtin and ship them as Tauri resources. +--- + +Ship one or more `.wvb` bundles inside your Tauri app so the webview loads offline, before any over-the-air update. Install them at build time with `wvb builtin`, then ship the directory as a Tauri resource. + +## Install bundles at build time + +`wvb builtin` writes a `manifest.json` plus one `/_.wvb` file per bundle into `--out`. Point it at the directory you ship as a resource: + +```sh +wvb builtin --out src-tauri/bundles +``` + +The source is chosen by `builtin.target` in your [config](/docs/references/configuration) — a `remote` target downloads from an endpoint, a `local` target packs bundles from your workspaces. See the [`wvb builtin` reference](/docs/references/cli/builtin) for every flag. + +## Ship the directory as a resource + +List the bundles directory under `bundle.resources` so Tauri packs it into the app: + +```json title="src-tauri/tauri.conf.json" +{ + "bundle": { + "resources": ["bundles/**/*"] + } +} +``` + +## Where bundles resolve at runtime + +By default the plugin serves builtin bundles from a `bundles` directory in the app's resource directory. Override the location on the `Source`: + +```rust title="src-tauri/src/lib.rs" +use wvb_tauri::Source; + +// Static path, resolved through Tauri's path API. +Source::new().builtin_dir("$RESOURCE/bundles"); + +// Or compute it from the AppHandle. +Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles"))); +``` + + + On Android, builtin bundles ship inside the APK as `asset://` resources that the filesystem cannot + read directly. The plugin extracts them from the APK on first request, so the app must also + register `tauri_plugin_fs::init()`. See [Tauri mobile](/docs/guide/native/tauri/mobile). + diff --git a/content/docs/guide/platforms/tauri.ko.mdx b/content/docs/guide/native/tauri/index.ko.mdx similarity index 92% rename from content/docs/guide/platforms/tauri.ko.mdx rename to content/docs/guide/native/tauri/index.ko.mdx index 81b4b58..3cbbe12 100644 --- a/content/docs/guide/platforms/tauri.ko.mdx +++ b/content/docs/guide/native/tauri/index.ko.mdx @@ -1,5 +1,5 @@ --- -title: Tauri +title: 설정 description: Tauri v2 앱에 wvb-tauri 플러그인을 추가하면 웹뷰를 .wvb 번들에서 제공하며, 개발 서버 프록시와 무선(OTA) 업데이트를 사용할 수 있습니다. --- @@ -96,7 +96,7 @@ Source::new().builtin_dir("$RESOURCE/bundles").remote_dir("$APPLOCALDATA/bundles Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles"))); ``` -값을 설정하지 않으면 builtin 디렉터리는 앱 리소스 디렉터리의 `bundles`로, remote 디렉터리는 앱 로컬 데이터의 `bundles`로 기본 설정됩니다. 각 `.protocol(...)` 호출은 자체 비동기 URI 스킴을 등록합니다. 스킴이 요청을 해석하는 방식은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. +값을 설정하지 않으면 builtin 디렉터리는 앱 리소스 디렉터리의 `bundles`로, remote 디렉터리는 앱 로컬 데이터의 `bundles`로 기본 설정됩니다. 각 `.protocol(...)` 호출은 자체 비동기 URI 스킴을 등록합니다. 스킴이 요청을 해석하는 방식은 [프로토콜 처리](/docs/guide/core-concepts/protocol-handling)를 참고하세요. ## 창을 커스텀 스킴으로 지정 @@ -158,7 +158,7 @@ resources에 디렉터리를 나열하여 빌드와 함께 배포하세요. } ``` -전체 패킹 워크플로와 플래그는 [CLI 레퍼런스](/docs/guide/cli)와 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +전체 패킹 워크플로와 플래그는 [CLI 레퍼런스](/docs/references/cli)와 [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. ## 프런트엔드에서 업데이트 제어 @@ -254,7 +254,7 @@ await invoke('plugin:wvb-tauri|source_update_version', { }); ``` -전체 커맨드 집합(`source_*` 12개, `remote_*` 4개, `updater_*` 4개)은 [`packages/tauri/src/commands.rs`](https://github.com/webview-bundle/webview-bundle/blob/main/packages/tauri/src/commands.rs)에 정의되어 있습니다. 무결성 및 서명 검증이 다운로드 흐름에 어떻게 들어맞는지는 [원격 번들](/docs/guide/remote-bundles)을, 서버 쪽은 [원격 설정](/docs/config/remote)을 참고하세요. +전체 커맨드 집합(`source_*` 12개, `remote_*` 4개, `updater_*` 4개)은 [`packages/tauri/src/commands.rs`](https://github.com/webview-bundle/webview-bundle/blob/main/packages/tauri/src/commands.rs)에 정의되어 있습니다. 무결성 및 서명 검증이 다운로드 흐름에 어떻게 들어맞는지는 [원격 번들](/docs/guide/core-concepts/over-the-air)을, 서버 쪽은 [원격 설정](/docs/references/configuration/remote)을 참고하세요. `remote_*` 커맨드는 설정에 `Remote`가 필요하고, `updater_*` 커맨드는 추가로 `Updater`가 @@ -327,7 +327,7 @@ tauri::Builder::default() .plugin(wvb_tauri::init(/* ... */)); ``` -그러면 플러그인이 첫 요청 시 APK에서 각 번들을 추출하여 앱 로컬 데이터에 캐시합니다. 원격 전용 앱(builtin 번들 없음)은 추가 Android 설정이 필요하지 않습니다. 플랫폼별 세부 사항은 [Android](/docs/guide/platforms/android) 및 [iOS](/docs/guide/platforms/ios) 가이드를, 현재 상태는 [플랫폼 지원](/docs/guide/platform-support)을 참고하세요. +그러면 플러그인이 첫 요청 시 APK에서 각 번들을 추출하여 앱 로컬 데이터에 캐시합니다. 원격 전용 앱(builtin 번들 없음)은 추가 Android 설정이 필요하지 않습니다. 플랫폼별 세부 사항은 [Android](/docs/guide/native/android) 및 [iOS](/docs/guide/native/ios) 가이드를, 현재 상태는 [플랫폼 지원](/docs/guide/getting-started/platform-and-framework-support)을 참고하세요. ## 문제 해결 @@ -335,29 +335,3 @@ tauri::Builder::default() - **`remote_not_initialized` / `updater_not_initialized`** — `Config`에 `.remote(...)`(및 `.updater(...)`) 없이 `remote_*` 또는 `updater_*` 커맨드를 호출했습니다. `error.code`로 분기하세요. - **ACL이 커맨드를 거부함** — 호출하는 창을 대상으로 하는 capability에 `wvb-tauri:default`(또는 특정 `wvb-tauri:allow-*` 권한)를 추가하세요. - **런타임에 번들을 찾을 수 없음** — `bundles` 디렉터리가 `tauri.conf.json`의 resources에 있는지, 그리고 `Source`가 이를 올바르게 해석하는지 확인하세요. Android에서는 builtin 번들을 배포할 때 `tauri_plugin_fs::init()`이 등록되어 있는지 확인하세요. - -## 더 알아보기 - - - - - - - -``` diff --git a/content/docs/guide/platforms/tauri.mdx b/content/docs/guide/native/tauri/index.mdx similarity index 91% rename from content/docs/guide/platforms/tauri.mdx rename to content/docs/guide/native/tauri/index.mdx index 3a7d0d1..b27f5b3 100644 --- a/content/docs/guide/platforms/tauri.mdx +++ b/content/docs/guide/native/tauri/index.mdx @@ -1,5 +1,5 @@ --- -title: Tauri +title: Setup description: Add the wvb-tauri plugin to a Tauri v2 app so the webview is served from a .wvb bundle, with dev proxying and over-the-air updates. --- @@ -96,7 +96,7 @@ Source::new().builtin_dir("$RESOURCE/bundles").remote_dir("$APPLOCALDATA/bundles Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles"))); ``` -When unset, the builtin dir defaults to `bundles` in the app's resource directory and the remote dir to `bundles` in app local data. Each `.protocol(...)` call registers its own asynchronous URI scheme. See [Protocol handling](/docs/guide/protocol-handling) for how the schemes resolve requests. +When unset, the builtin dir defaults to `bundles` in the app's resource directory and the remote dir to `bundles` in app local data. Each `.protocol(...)` call registers its own asynchronous URI scheme. See [Protocol handling](/docs/guide/core-concepts/protocol-handling) for how the schemes resolve requests. ## Point a window at the custom scheme @@ -158,7 +158,7 @@ Ship the directory with the build by listing it in resources: } ``` -For the full packing workflow and flags, see the [CLI reference](/docs/guide/cli) and [Bundle sources](/docs/guide/bundle-sources). +For the full packing workflow and flags, see the [CLI reference](/docs/references/cli) and [Bundle sources](/docs/guide/core-concepts/bundle-source). ## Drive updates from the frontend @@ -254,7 +254,7 @@ await invoke('plugin:wvb-tauri|source_update_version', { }); ``` -The full command set (twelve `source_*`, four `remote_*`, and four `updater_*` commands) is defined in [`packages/tauri/src/commands.rs`](https://github.com/webview-bundle/webview-bundle/blob/main/packages/tauri/src/commands.rs). See [Remote bundles](/docs/guide/remote-bundles) for how integrity and signature verification fit into the download flow, and [Remote config](/docs/config/remote) for the server side. +The full command set (twelve `source_*`, four `remote_*`, and four `updater_*` commands) is defined in [`packages/tauri/src/commands.rs`](https://github.com/webview-bundle/webview-bundle/blob/main/packages/tauri/src/commands.rs). See [Remote bundles](/docs/guide/core-concepts/over-the-air) for how integrity and signature verification fit into the download flow, and [Remote config](/docs/references/configuration/remote) for the server side. The `remote_*` commands require a `Remote` on the config; the `updater_*` commands additionally @@ -327,7 +327,7 @@ tauri::Builder::default() .plugin(wvb_tauri::init(/* ... */)); ``` -The plugin then extracts each bundle from the APK on first request and caches it in app local data. Remote-only apps (no builtin bundles) need no extra Android setup. See the [Android](/docs/guide/platforms/android) and [iOS](/docs/guide/platforms/ios) guides for platform specifics, and [Platform support](/docs/guide/platform-support) for current status. +The plugin then extracts each bundle from the APK on first request and caches it in app local data. Remote-only apps (no builtin bundles) need no extra Android setup. See the [Android](/docs/guide/native/android) and [iOS](/docs/guide/native/ios) guides for platform specifics, and [Platform support](/docs/guide/getting-started/platform-and-framework-support) for current status. ## Troubleshooting @@ -335,28 +335,3 @@ The plugin then extracts each bundle from the APK on first request and caches it - **`remote_not_initialized` / `updater_not_initialized`** — you called a `remote_*` or `updater_*` command without `.remote(...)` (and `.updater(...)`) on the `Config`. Branch on `error.code`. - **Command rejected by the ACL** — add `wvb-tauri:default` (or the specific `wvb-tauri:allow-*` permission) to a capability targeting the calling window. - **Bundle not found at runtime** — confirm the `bundles` directory is in `tauri.conf.json` resources and `Source` resolves to it. On Android, confirm `tauri_plugin_fs::init()` is registered when shipping builtin bundles. - -## Learn more - - - - - - - diff --git a/content/docs/guide/native/tauri/local-development.ko.mdx b/content/docs/guide/native/tauri/local-development.ko.mdx new file mode 100644 index 0000000..17e734f --- /dev/null +++ b/content/docs/guide/native/tauri/local-development.ko.mdx @@ -0,0 +1,41 @@ +--- +title: '로컬 개발' +description: wvb 커스텀 스킴을 Tauri/Vite 개발 서버로 프록시하여 웹뷰를 실시간으로 로드하고, 개발 번들과 패킹된 번들을 전환합니다. +--- + +개발 중에는 웹뷰가 패킹된 `.wvb`가 아니라 실행 중인 개발 서버에서 로드되어야 합니다. `local` 프로토콜을 등록하면 커스텀 스킴이 Tauri의 Vite 개발 서버로 프록시되어 핫 리로드가 유지됩니다. + +## 개발 서버 프록시 + +`Protocol::bundle` 옆에 `Protocol::local`을 등록하여 스킴 호스트를 Tauri의 Vite 개발 서버(기본값 `http://localhost:1420`)에 매핑하세요. + +```rust title="src-tauri/src/lib.rs" +use wvb_tauri::{Config, Protocol, Source}; + +Config::new() + .source(Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles")))) + // 프로덕션: 패킹된 번들에서 파일을 제공합니다. + .protocol(Protocol::bundle("bundle")) + // 개발: 동일한 호스트를 실행 중인 개발 서버로 프록시합니다. + .protocol(Protocol::local("local").host("example.com", "http://localhost:1420")); +``` + +`Protocol::local(scheme)`는 `local://example.com/...`을 개발 서버로 전달하므로 편집 내용이 실시간으로 다시 로드됩니다. `.host(...)` 호출을 여러 번 사용하거나 하나의 `.hosts(...)`로 여러 호스트를 매핑하세요. + +## 개발과 프로덕션 전환 + +개발 중에는 창을 `local` 스킴으로, 배포할 때는 `bundle` 스킴으로 지정하세요. + +```json title="src-tauri/tauri.conf.json" +{ + "app": { + "windows": [ + { + "url": "local://example.com" + } + ] + } +} +``` + +프로덕션 빌드에서는 창의 `url`을 `bundle://app.wvb`로 바꾸거나, `cfg!(debug_assertions)`로 런타임에 선택하여 전환이 자동으로 이뤄지도록 하세요. diff --git a/content/docs/guide/native/tauri/local-development.mdx b/content/docs/guide/native/tauri/local-development.mdx new file mode 100644 index 0000000..986b6de --- /dev/null +++ b/content/docs/guide/native/tauri/local-development.mdx @@ -0,0 +1,41 @@ +--- +title: Local development +description: Proxy the wvb custom scheme to your Tauri/Vite dev server so the webview loads live, and switch between dev and packed bundles. +--- + +In development you want the webview to load from your running dev server, not from a packed `.wvb`. Register a `local` protocol so the custom scheme proxies to Tauri's Vite dev server and keeps hot reload. + +## Proxy the dev server + +Register `Protocol::local` next to `Protocol::bundle`, mapping a scheme host to Tauri's Vite dev server (default `http://localhost:1420`): + +```rust title="src-tauri/src/lib.rs" +use wvb_tauri::{Config, Protocol, Source}; + +Config::new() + .source(Source::new().builtin_dir_fn(|app| Ok(app.path().resource_dir()?.join("bundles")))) + // Production: serve files from the packed bundle. + .protocol(Protocol::bundle("bundle")) + // Development: proxy the same host to the running dev server. + .protocol(Protocol::local("local").host("example.com", "http://localhost:1420")); +``` + +`Protocol::local(scheme)` forwards `local://example.com/...` to the dev server, so edits reload live. Map several hosts with more `.host(...)` calls or a single `.hosts(...)`. + +## Switch dev and prod + +Point the window at the `local` scheme while developing and at the `bundle` scheme when you ship: + +```json title="src-tauri/tauri.conf.json" +{ + "app": { + "windows": [ + { + "url": "local://example.com" + } + ] + } +} +``` + +Swap the window `url` to `bundle://app.wvb` for production builds, or pick it at runtime with `cfg!(debug_assertions)` so the switch is automatic. diff --git a/content/docs/guide/native/tauri/meta.json b/content/docs/guide/native/tauri/meta.json new file mode 100644 index 0000000..98d3a12 --- /dev/null +++ b/content/docs/guide/native/tauri/meta.json @@ -0,0 +1 @@ +{ "title": "Tauri", "pages": ["index", "local-development", "builtin", "mobile"] } diff --git a/content/docs/guide/native/tauri/meta.ko.json b/content/docs/guide/native/tauri/meta.ko.json new file mode 100644 index 0000000..98d3a12 --- /dev/null +++ b/content/docs/guide/native/tauri/meta.ko.json @@ -0,0 +1 @@ +{ "title": "Tauri", "pages": ["index", "local-development", "builtin", "mobile"] } diff --git a/content/docs/guide/native/tauri/mobile.ko.mdx b/content/docs/guide/native/tauri/mobile.ko.mdx new file mode 100644 index 0000000..02fcbd7 --- /dev/null +++ b/content/docs/guide/native/tauri/mobile.ko.mdx @@ -0,0 +1,22 @@ +--- +title: '모바일' +description: wvb-tauri 플러그인을 Tauri 모바일(iOS 및 Android)에서 실행할 때 무엇이 달라지는지, 그리고 builtin 번들이 어디에 위치하는지 설명합니다. +--- + +동일한 `wvb-tauri` 플러그인이 데스크톱과 Tauri 모바일에서 모두 동작합니다. [설정](/docs/guide/native/tauri)에서와 똑같이 등록하세요 — 아래 차이점은 각 플랫폼에서 builtin 번들이 위치하는 곳에 관한 것뿐입니다. + +## iOS + +builtin 번들은 실제 파일 시스템 리소스 디렉터리에 위치합니다. 데스크톱 설정 외에 추가 설정은 필요하지 않습니다. + +## Android + +builtin 번들은 APK 안에 `asset://` 리소스로 배포되는데, 파일 시스템이 이를 직접 읽을 수 없습니다. builtin 번들을 배포하는 앱은 Tauri 파일 시스템 플러그인도 등록해야 합니다. + +```rust title="src-tauri/src/lib.rs" +tauri::Builder::default() + .plugin(tauri_plugin_fs::init()) + .plugin(wvb_tauri::init(/* ... */)); +``` + +플러그인은 첫 요청 시 APK에서 각 번들을 추출하여 앱 로컬 데이터에 캐시합니다. 원격 전용 앱(builtin 번들 없음)은 추가 Android 설정이 필요하지 않습니다. diff --git a/content/docs/guide/native/tauri/mobile.mdx b/content/docs/guide/native/tauri/mobile.mdx new file mode 100644 index 0000000..f797da6 --- /dev/null +++ b/content/docs/guide/native/tauri/mobile.mdx @@ -0,0 +1,22 @@ +--- +title: 'For mobile' +description: What changes when you run the wvb-tauri plugin on Tauri mobile (iOS and Android), and where builtin bundles live. +--- + +The same `wvb-tauri` plugin runs on desktop and on Tauri mobile. Register it exactly as in [Setup](/docs/guide/native/tauri) — the differences below are only about where builtin bundles live on each platform. + +## iOS + +Builtin bundles live in a real filesystem resource directory. No extra setup beyond the desktop configuration. + +## Android + +Builtin bundles ship inside the APK as `asset://` resources, which the filesystem cannot read directly. An app that ships builtin bundles must also register the Tauri filesystem plugin: + +```rust title="src-tauri/src/lib.rs" +tauri::Builder::default() + .plugin(tauri_plugin_fs::init()) + .plugin(wvb_tauri::init(/* ... */)); +``` + +The plugin extracts each bundle from the APK on first request and caches it in app local data. Remote-only apps (no builtin bundles) need no extra Android setup. diff --git a/content/docs/guide/platform-integration.ko.mdx b/content/docs/guide/platform-integration.ko.mdx deleted file mode 100644 index 498352b..0000000 --- a/content/docs/guide/platform-integration.ko.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: 플랫폼 통합 -description: 하나의 Rust 코어가 얇은 바인딩과 @wvb/bridge 웹 레이어를 통해 Electron, Tauri, Android, iOS, Deno에 도달하는 방법. ---- - -Webview Bundle은 다섯 개가 아니라 하나의 시스템입니다. 포맷, 번들 소스, 프로토콜 핸들러, 원격 클라이언트, 업데이터, 무결성 및 서명 검사가 모두 하나의 Rust 크레이트 안에 있습니다. 모든 플랫폼은 얇은 바인딩을 통해 바로 그 크레이트를 사용합니다. 그래서 Electron용으로 빌드한 `.wvb`는 Tauri, Android, iOS, Deno에서도 똑같이 동작합니다. 개별 플랫폼 가이드를 읽기 전에 이 문서를 먼저 읽으세요. 각 가이드는 어떤 패키지가 어디에 속하는지 이미 안다고 가정합니다. - -## 하나의 코어, 여러 호스트 - -Rust 크레이트 `wvb`([crates.io](https://crates.io/crates/wvb), 버전 0.2.0)가 단일 진실 공급원입니다. 이 크레이트는 다음을 구현합니다. - -- `.wvb` 번들 포맷(헤더, 인덱스, LZ4로 압축된 데이터), -- 번들 소스(builtin 및 remote 디렉터리), -- 번들 프로토콜 핸들러와 로컬 프로토콜 핸들러, -- 원격 HTTP 클라이언트, -- 업데이터(확인, 다운로드, 검증, 설치), -- 무결성(SHA-2) 및 서명 검증. - -각 바인딩은 의도적으로 얇습니다. 코어를 호스트 언어에 노출하고, 해당 호스트에 URL 스킴을 등록하며, 요청을 코어로 전달합니다. 코어를 수정하거나 기능을 추가하면 다음 바인딩 릴리스에서 모든 플랫폼에 반영됩니다. 전체 API는 [docs.rs/wvb](https://docs.rs/wvb)에서 확인하세요. - - - Rust 코어는 URL 스킴을 등록하거나 검증하지 않습니다. `app://`이나 `bundle://` 같은 스킴을 선택하고 - OS에 등록하는 일은 바인딩의 몫입니다. 요청이 파일로 매핑되는 방식은 [프로토콜 - 처리](/docs/guide/protocol-handling)를 참고하세요. - - -## 전달 방식 - -각 플랫폼은 서로 다른 배포 채널을 통해 코어에 도달합니다. - -| 플랫폼 | 바인딩 | 배포 방식 | -| ------------------------ | ------------------------- | --------------------------------------------------------------------- | -| Electron / Node.js | `@wvb/node` | N-API 네이티브 애드온(NAPI-RS), npm에 플랫폼별 프리빌드 바이너리 제공 | -| Tauri(데스크톱 + 모바일) | `wvb-tauri` Rust 크레이트 | crates.io의 Tauri v2 플러그인, 버전 0.1.0 | -| Android | `packages/ffi`(UniFFI) | Kotlin 바인딩, `webview-bundle-android` 저장소에서 사용 | -| iOS | `packages/ffi`(UniFFI) | Swift 바인딩, `webview-bundle-ios` 저장소에서 사용 | -| Deno Desktop | `@wvb/deno` | 프리빌드 dylib 위의 Deno FFI(실험적) | - -### Electron과 Node.js - -`@wvb/node`(0.1.0)는 코어를 프리빌드 바이너리가 포함된 N-API 네이티브 애드온으로 감쌉니다. 여느 npm 의존성처럼 설치하세요. - -```sh -npm install @wvb/node -``` - -```ts title="main.ts" -import { readBundle, BundleSource } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles', - remoteDir: './remote', -}); -const bundle = await readBundle('./app.wvb'); -``` - -Electron에서는 `@wvb/electron`(0.1.0)을 사용하세요. 이 패키지는 `@wvb/node` 위에 만들어졌으며, 스킴 권한, 프로토콜 등록, IPC 채널을 대신 연결해 줍니다. - -```sh -npm install @wvb/electron -``` - -```js title="main.cjs" -const { app, BrowserWindow } = require('electron'); -const { bundleProtocol, wvb } = require('@wvb/electron'); - -const instance = wvb({ - source: { builtinDir: path.join(__dirname, 'bundles') }, - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow({ - webPreferences: { contextIsolation: true, nodeIntegration: false }, - }); - await window.loadURL('app://hacker-news.wvb'); -}); -``` - -electron-builder와 Electron Forge용 패키징 헬퍼인 `@wvb/electron-builder`와 `@wvb/electron-forge`는 번들 패킹을 빌드에 연결합니다. [Electron 가이드](/docs/guide/platforms/electron)와 [Node API 레퍼런스](/docs/references/node)를 참고하세요. - -### Tauri - -Tauri 통합은 Rust 크레이트 `wvb-tauri`(crates.io의 0.1.0)입니다. `@wvb/tauri` npm 패키지는 없습니다. 크레이트를 추가하고 Tauri v2 플러그인으로 등록하세요. - -```toml title="src-tauri/Cargo.toml" -[dependencies] -wvb-tauri = "0.1.0" -``` - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new().builtin_dir("bundles")) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .unwrap(); -``` - -플러그인 명령어를 호출하는 프런트엔드는 capability 파일에 설정된 ACL 권한을 부여해야 합니다. - -```json title="src-tauri/capabilities/default.json" -{ - "permissions": ["core:default", "wvb-tauri:default"] -} -``` - -같은 크레이트가 데스크톱과 모바일을 모두 처리합니다. Android에서는 builtin 번들이 APK 안에 `asset://` 리소스로 포함됩니다. 그래서 builtin 번들을 함께 배포하는 앱은 `tauri_plugin_fs`도 등록해야 합니다. - -```rust -tauri::Builder::default() - .plugin(tauri_plugin_fs::init()) // Android에서 builtin 번들에 필요함 - .plugin(wvb_tauri::init(config)) -``` - -[Tauri 가이드](/docs/guide/platforms/tauri)를 참고하세요. - -### Android와 iOS - -Android와 iOS는 하나의 바인딩 생성기 `packages/ffi`를 공유합니다. 이는 [UniFFI](https://mozilla.github.io/uniffi-rs/)를 사용해 코어로부터 Kotlin과 Swift 바인딩을 생성하는 Rust 크레이트입니다. 빌드하면 세 개의 GitHub 릴리스 자산이 만들어집니다. - -- `android.zip` — Android용 Kotlin 바인딩과 `jniLibs`, -- `apple.zip` — Apple 플랫폼용 Swift 바인딩과 정적 라이브러리, -- `WebViewBundleFFI.xcframework.zip` — iOS용 xcframework. - -두 개의 전용 저장소가 이 자산들을 사용하며, 릴리스 태그 `ffi/`으로 해석합니다. - -```kotlin title="Android — dev.wvb 바인딩" -import dev.wvb.BundleSource -import dev.wvb.BundleSourceConfig - -val source = BundleSource( - BundleSourceConfig( - builtinDir = "$filesDir/bundles", - remoteDir = "$cacheDir/remote", - builtinManifestFilepath = null, - remoteManifestFilepath = null, - ), -) -val bundle = source.fetchBundle("app") -``` - -```swift title="iOS — WebViewBundleLibrary 바인딩" -import WebViewBundleLibrary - -let source = BundleSource( - config: BundleSourceConfig( - builtinDir: "\(docs)/bundles", - remoteDir: "\(caches)/remote", - builtinManifestFilepath: nil, - remoteManifestFilepath: nil - ) -) -let bundle = try await source.fetchBundle(bundleName: "app") -``` - -```swift title="Package.swift" -.binaryTarget( - name: "WebViewBundleFFI", - url: "https://github.com/webview-bundle/webview-bundle/releases/download/ffi/0.1.0/WebViewBundleFFI.xcframework.zip", - checksum: "" -) -``` - -### Deno - -`@wvb/deno`는 프리빌드 동적 라이브러리 위의 Deno FFI를 통해 코어에 도달합니다. GitHub Releases에서 dylib를 설치한 다음 로드하세요. - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb -``` - -이 통합은 실험적입니다. [Deno 가이드](/docs/guide/platforms/deno)와 [Deno API 레퍼런스](/docs/references/deno)를 참고하세요. - -## 브리지 - -위의 바인딩들은 네이티브 쪽에서 실행됩니다. 브리지 `@wvb/bridge`(0.1.0)는 웹 쪽입니다. 웹뷰 안의 JavaScript가 호스트와 상관없이 하나의 통일된 API로 네이티브 소스, 원격, 업데이터 작업을 호출하게 해 줍니다. - -```sh -npm install @wvb/bridge -``` - -단일 진입점은 `invoke()`입니다. - -```ts -import { invoke } from '@wvb/bridge'; - -const update = await invoke('updaterGetUpdate', { bundleName: 'app' }); -``` - -`invoke()`를 이름으로 직접 호출하는 경우는 드뭅니다. 브리지는 네이티브 명령어를 세 개의 타입이 지정된 객체 `source.*`, `remote.*`, `updater.*`로 묶으며, 각 메서드는 타입이 지정된 `invoke()` 호출입니다. - -```ts -import { source, remote, updater } from '@wvb/bridge'; - -const bundles = await source.listBundles(); -const info = await updater.getUpdate('app'); -if (info.isAvailable) { - await updater.download('app'); - await updater.install('app', info.version); -} -``` - -- `source.*` — 번들 목록 조회, 파일 경로 해석, 메타데이터 읽기, 다운로드한 버전 관리. -- `remote.*` — 원격 서버에서 번들 목록 조회, 검사, 다운로드. -- `updater.*` — 업데이트 확인, 다운로드, 설치. - -브리지는 런타임에 호스트를 감지하고 각 호출을 알맞은 전송 방식으로 라우팅합니다. - -| 플랫폼 | 전송 방식 | -| -------- | ------------------------------------------------------- | -| Electron | `window.wvbElectron.invoke(name, params)` | -| Tauri | Tauri `invoke('plugin:wvb-tauri\|', params)` | -| Android | `window.wvbAndroid.postMessage(...)` | -| iOS | `window.webkit.messageHandlers.wvbIos.postMessage(...)` | - -어디서나 동일한 `source`, `remote`, `updater` 메서드를 사용합니다. `@wvb/bridge` 클라이언트는 electron, tauri, android, ios를 지원합니다. Deno Desktop은 `@wvb/deno-desktop`을 통해 자체 바인딩을 제공합니다. - -`@wvb/bridge/testing`을 임포트하면 네이티브 호스트 없이 웹뷰 코드를 테스트할 수 있습니다. - -```ts title="app.test.ts" -import { mockBridge } from '@wvb/bridge/testing'; -import { updater } from '@wvb/bridge'; - -using bridge = mockBridge({ platform: 'electron' }); -bridge.mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, -})); - -const info = await updater.getUpdate('app'); -``` - - - `@wvb/bridge/testing`은 `mockInvoke`, `mockPlatform`, `mockBridge`를 제공하므로, 단위 테스트에서 - 명령어 응답을 스텁으로 대체할 수 있습니다. - - -## 아키텍처 한눈에 보기 - -웹 앱에서 온 요청은 브리지를 지나 네이티브 호스트로 내려가고, 코어로 들어간 다음, 번들 소스나 원격 서버로 나갑니다. - -![웹 앱이 @wvb/bridge를 통해 네이티브 호스트를 호출하고, 호스트는 wvb Rust 코어를 임베드하며, 코어는 소스와 원격 서버에서 읽어 온다.](https://static.wvb.dev/diagrams/architecture.svg) - -웹뷰도 같은 코어를 통해 자산을 로드합니다. 번들 프로토콜이 소스에서 바로 파일을 제공하므로, 페이지 바이트와 `invoke()` 명령어가 하나의 번들 소스를 공유합니다. - -## 다음으로 볼 곳 - - - - - - - - - - - diff --git a/content/docs/guide/platform-integration.mdx b/content/docs/guide/platform-integration.mdx deleted file mode 100644 index 9d69162..0000000 --- a/content/docs/guide/platform-integration.mdx +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: Platform integration -description: How one Rust core reaches Electron, Tauri, Android, iOS, and Deno through thin bindings and the @wvb/bridge web layer. ---- - -Webview Bundle is one system, not five. The format, bundle source, protocol handlers, remote client, updater, and integrity and signature checks all live in a single Rust crate. Every platform consumes that same crate through a thin binding, so a `.wvb` built for Electron behaves identically on Tauri, Android, iOS, and Deno. Read this before the individual platform guides — they assume you know which package belongs where. - -## One core, many hosts - -The Rust crate `wvb` ([crates.io](https://crates.io/crates/wvb), version 0.2.0) is the single source of truth. It implements: - -- the `.wvb` bundle format (header, index, LZ4-compressed data), -- the bundle source (builtin and remote directories), -- the bundle and local protocol handlers, -- the remote HTTP client, -- the updater (check, download, verify, install), -- integrity (SHA-2) and signature verification. - -Each binding is intentionally thin: it exposes the core to the host language, registers a URL scheme with that host, and forwards requests into the core. A core fix or feature reaches every platform on the next binding release. Browse the full API on [docs.rs/wvb](https://docs.rs/wvb). - - - The Rust core never registers or validates a URL scheme. Picking a scheme such as `app://` or - `bundle://` and registering it with the OS is the binding's job. See [Protocol - handling](/docs/guide/protocol-handling) for how a request maps to a file. - - -## Delivery mechanisms - -Each platform reaches the core through a different distribution channel. - -| Platform | Binding | How it is shipped | -| ------------------------ | ----------------------- | ------------------------------------------------------------------- | -| Electron / Node.js | `@wvb/node` | N-API native addon (NAPI-RS), prebuilt per-platform binaries on npm | -| Tauri (desktop + mobile) | `wvb-tauri` Rust crate | Tauri v2 plugin on crates.io, version 0.1.0 | -| Android | `packages/ffi` (UniFFI) | Kotlin bindings, consumed by the `webview-bundle-android` repo | -| iOS | `packages/ffi` (UniFFI) | Swift bindings, consumed by the `webview-bundle-ios` repo | -| Deno Desktop | `@wvb/deno` | Deno FFI over a prebuilt dylib (experimental) | - -### Electron and Node.js - -`@wvb/node` (0.1.0) wraps the core as an N-API native addon with prebuilt binaries — install it like any npm dependency. - -```sh -npm install @wvb/node -``` - -```ts title="main.ts" -import { readBundle, BundleSource } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles', - remoteDir: './remote', -}); -const bundle = await readBundle('./app.wvb'); -``` - -For Electron, use `@wvb/electron` (0.1.0) — it builds on `@wvb/node` and wires scheme privileges, protocol registration, and the IPC channel for you. - -```sh -npm install @wvb/electron -``` - -```js title="main.cjs" -const { app, BrowserWindow } = require('electron'); -const { bundleProtocol, wvb } = require('@wvb/electron'); - -const instance = wvb({ - source: { builtinDir: path.join(__dirname, 'bundles') }, - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow({ - webPreferences: { contextIsolation: true, nodeIntegration: false }, - }); - await window.loadURL('app://hacker-news.wvb'); -}); -``` - -The electron-builder and Electron Forge packaging helpers, `@wvb/electron-builder` and `@wvb/electron-forge`, wire bundle packaging into your build. See the [Electron guide](/docs/guide/platforms/electron) and the [Node API reference](/docs/references/node). - -### Tauri - -The Tauri integration is the Rust crate `wvb-tauri` (0.1.0 on crates.io) — there is no `@wvb/tauri` npm package. Add the crate and register it as a Tauri v2 plugin. - -```toml title="src-tauri/Cargo.toml" -[dependencies] -wvb-tauri = "0.1.0" -``` - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new().builtin_dir("bundles")) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .unwrap(); -``` - -A frontend that calls plugin commands must grant the ACL permission set in a capability file. - -```json title="src-tauri/capabilities/default.json" -{ - "permissions": ["core:default", "wvb-tauri:default"] -} -``` - -The same crate handles desktop and mobile. On Android, builtin bundles ship inside the APK as `asset://` resources, so an app that ships builtin bundles must also register `tauri_plugin_fs`. - -```rust -tauri::Builder::default() - .plugin(tauri_plugin_fs::init()) // required on Android for builtin bundles - .plugin(wvb_tauri::init(config)) -``` - -See the [Tauri guide](/docs/guide/platforms/tauri). - -### Android and iOS - -Android and iOS share one binding generator: `packages/ffi`, a Rust crate that uses [UniFFI](https://mozilla.github.io/uniffi-rs/) to generate Kotlin and Swift bindings from the core. The build produces three GitHub release assets: - -- `android.zip` — Kotlin bindings and `jniLibs` for Android, -- `apple.zip` — Swift bindings and static libraries for Apple platforms, -- `WebViewBundleFFI.xcframework.zip` — the xcframework for iOS. - -Two dedicated repositories consume these assets and resolve them by release tag `ffi/`. - -```kotlin title="Android — dev.wvb bindings" -import dev.wvb.BundleSource -import dev.wvb.BundleSourceConfig - -val source = BundleSource( - BundleSourceConfig( - builtinDir = "$filesDir/bundles", - remoteDir = "$cacheDir/remote", - builtinManifestFilepath = null, - remoteManifestFilepath = null, - ), -) -val bundle = source.fetchBundle("app") -``` - -```swift title="iOS — WebViewBundleLibrary bindings" -import WebViewBundleLibrary - -let source = BundleSource( - config: BundleSourceConfig( - builtinDir: "\(docs)/bundles", - remoteDir: "\(caches)/remote", - builtinManifestFilepath: nil, - remoteManifestFilepath: nil - ) -) -let bundle = try await source.fetchBundle(bundleName: "app") -``` - -```swift title="Package.swift" -.binaryTarget( - name: "WebViewBundleFFI", - url: "https://github.com/webview-bundle/webview-bundle/releases/download/ffi/0.1.0/WebViewBundleFFI.xcframework.zip", - checksum: "" -) -``` - -### Deno - -`@wvb/deno` reaches the core through Deno FFI over a prebuilt dynamic library. Install the dylib from GitHub Releases, then load it. - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb -``` - -It is experimental. See the [Deno guide](/docs/guide/platforms/deno) and the [Deno API reference](/docs/references/deno). - -## The bridge - -The bindings above run on the native side. The bridge, `@wvb/bridge` (0.1.0), is the web side. It lets JavaScript inside the webview call native source, remote, and updater operations through one uniform API, regardless of host. - -```sh -npm install @wvb/bridge -``` - -The single entry point is `invoke()`: - -```ts -import { invoke } from '@wvb/bridge'; - -const update = await invoke('updaterGetUpdate', { bundleName: 'app' }); -``` - -You rarely call `invoke()` by name. The bridge groups native commands into three typed objects — `source.*`, `remote.*`, and `updater.*` — each method a typed `invoke()` call: - -```ts -import { source, remote, updater } from '@wvb/bridge'; - -const bundles = await source.listBundles(); -const info = await updater.getUpdate('app'); -if (info.isAvailable) { - await updater.download('app'); - await updater.install('app', info.version); -} -``` - -- `source.*` — list bundles, resolve filepaths, read metadata, manage downloaded versions. -- `remote.*` — list, inspect, and download bundles from a remote server. -- `updater.*` — check for an update, download it, and install it. - -The bridge detects the host at runtime and routes each call over the right transport: - -| Platform | Transport | -| -------- | ------------------------------------------------------- | -| Electron | `window.wvbElectron.invoke(name, params)` | -| Tauri | Tauri `invoke('plugin:wvb-tauri\|', params)` | -| Android | `window.wvbAndroid.postMessage(...)` | -| iOS | `window.webkit.messageHandlers.wvbIos.postMessage(...)` | - -Your code uses the same `source`, `remote`, and `updater` methods everywhere. The `@wvb/bridge` client supports electron, tauri, android, and ios; Deno Desktop provides its own bindings through `@wvb/deno-desktop`. - -Test webview code without a native host by importing `@wvb/bridge/testing`: - -```ts title="app.test.ts" -import { mockBridge } from '@wvb/bridge/testing'; -import { updater } from '@wvb/bridge'; - -using bridge = mockBridge({ platform: 'electron' }); -bridge.mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, -})); - -const info = await updater.getUpdate('app'); -``` - - - `@wvb/bridge/testing` provides `mockInvoke`, `mockPlatform`, and `mockBridge` so you can stub - command responses in unit tests. - - -## Architecture at a glance - -A request from your web app flows down through the bridge to the native host, into the core, and out to a bundle source or a remote server. - -![A web app calls the native host through @wvb/bridge; the host embeds the wvb Rust core, which reads from a source and a remote server.](https://static.wvb.dev/diagrams/architecture.svg) - -The webview also loads its assets through the same core: the bundle protocol serves files straight out of the source, so the page bytes and the `invoke()` commands share one bundle source. - -## Where to go next - - - - - - - - - - - diff --git a/content/docs/guide/platform-support.ko.mdx b/content/docs/guide/platform-support.ko.mdx deleted file mode 100644 index 35f0c67..0000000 --- a/content/docs/guide/platform-support.ko.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: 플랫폼 지원 -description: Webview Bundle을 실행하는 플랫폼과 각 플랫폼에서 설치할 패키지를 정리합니다. ---- - -하나의 공유 Rust 코어가 모든 통합을 구동합니다. 따라서 한 번 패킹한 `.wvb`는 Electron, Tauri, Android, iOS, Deno Desktop에서 동일하게 로드됩니다. 코어가 각 호스트에 도달하는 방식과 웹뷰가 브리지를 통해 응답하는 방식은 [플랫폼 통합](/docs/guide/platform-integration)을 참고하세요. - -이 페이지는 지원 매트릭스입니다. 플랫폼별 패키지 또는 크레이트, 최소 호스트 버전, 상태를 정리합니다. - -## 지원 매트릭스 - -| 플랫폼 | 웹뷰 호스트 | 패키지 / 크레이트 | 최소 버전 | 상태 | -| -------------------------------------------- | -------------- | --------------------------------- | -------------------------------------------------------------------------------- | ------ | -| [Electron](/docs/guide/platforms/electron) | Chromium | `@wvb/electron` 0.1.0 (npm) | `electron >= 15` | 안정 | -| [Tauri desktop](/docs/guide/platforms/tauri) | System WebView | `wvb-tauri` 0.1.0 (crate) | Tauri v2 | 안정 | -| Tauri mobile | System WebView | `wvb-tauri` 0.1.0 (crate) | [Android](/docs/guide/platforms/android) / [iOS](/docs/guide/platforms/ios) 참고 | 안정 | -| [Android](/docs/guide/platforms/android) | System WebView | `webview-bundle-android` (Kotlin) | minSdk 24 / Android 7.0 | 안정 | -| [iOS](/docs/guide/platforms/ios) | WKWebView | `webview-bundle-ios` (Swift) | iOS 16 / macOS 12 | 안정 | -| [Deno Desktop](/docs/guide/platforms/deno) | Deno webview | `@wvb/deno-desktop` (JSR) | — | 실험적 | - -## 데스크톱 - -Electron과 Tauri 데스크톱은 가장 성숙한 통합입니다. 둘 다 배포되어 있으며, Rust 코어를 기반으로 호출자가 제공한 URL 스킴을 통해 번들을 제공합니다. - -### Electron - -npm 패키지를 설치하세요. - -```sh -npm install @wvb/electron -``` - -프로토콜을 등록하고 이름으로 번들을 로드하세요(`://.wvb`). - -```ts title="main.ts" -import { app, BrowserWindow } from 'electron'; -import { bundleProtocol, wvb } from '@wvb/electron'; - -const instance = wvb({ - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow({ - webPreferences: { contextIsolation: true, nodeIntegration: false }, - }); - await window.loadURL('app://hacker-news.wvb'); -}); -``` - -[Electron 가이드](/docs/guide/platforms/electron)에서 시작하세요. - -### Tauri - -Tauri 통합은 **Tauri v2**를 대상으로 하는 crates.io의 `wvb-tauri` Rust 크레이트입니다. `@wvb/tauri` npm 패키지는 없습니다. 동일한 크레이트가 Tauri의 모바일 타깃도 구동합니다. - -```toml title="src-tauri/Cargo.toml" -[dependencies] -wvb-tauri = "0.1.0" -``` - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new()) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -``` - -그러면 윈도우가 `bundle://hacker-news.wvb`를 로드합니다. [Tauri 가이드](/docs/guide/platforms/tauri)에서 시작하세요. - -## 모바일 - -Android와 iOS는 실제로 동작하는 통합입니다. - -### Android - -`webview-bundle-android` Kotlin 라이브러리(네임스페이스 `dev.wvb`)는 **minSdk 24(Android 7.0)**를 요구하며, `WebViewClient`를 통해 번들을 제공합니다. 네이티브 바인딩을 받은 다음 `WebView`에 설치하세요. - -```sh -node scripts/install.mjs -``` - -```kotlin title="MainActivity.kt" -val wvb = WebViewBundle.getInstance( - this, - WebViewBundleConfig(protocols = listOf(WebViewBundleProtocol.bundle())), -) -val webView = WebView(this) -webView.settings.javaScriptEnabled = true -wvb.install(webView) -webView.loadUrl("https://app.wvb/") -``` - -번들은 일반적인 `https://.wvb/...` URL로 제공됩니다(번들 이름이 첫 번째 호스트 레이블입니다). [Android 가이드](/docs/guide/platforms/android)를 참고하세요. - -### iOS - -`webview-bundle-ios` Swift 패키지는 **iOS 16**과 **macOS 12**를 대상으로 하며, `WKURLSchemeHandler`를 통해 번들을 제공합니다. 네이티브 xcframework 릴리스를 설치한 다음 `WKWebViewConfiguration`에 커스텀 스킴을 등록하세요. - -```sh -node scripts/install.mjs ffi/0.1.0 -``` - -```swift title="ContentView.swift" -let instance = try WebViewBundle.configure( - WebViewBundleConfig(protocols: [.bundle(scheme: "app")]) -) -let config = WKWebViewConfiguration() -instance.install(on: config) -let webView = WKWebView(frame: .zero, configuration: config) -webView.load(URLRequest(url: URL(string: "app://app.wvb")!)) -``` - -[iOS 가이드](/docs/guide/platforms/ios)를 참고하세요. - -## Deno Desktop - -Deno Desktop은 가장 최신 통합이며, JSR에 `@wvb/deno-desktop`으로 올라와 있습니다. 번들 소스를 빌드하고 `Deno.serve` 호환 핸들러를 노출하며, 윈도우마다 하나의 프로토콜을 사용합니다. - - - Deno Desktop은 실험적입니다. [Deno Desktop 가이드](/docs/guide/platforms/deno)와 [Deno API - 레퍼런스](/docs/references/deno)를 참고하세요. - - -## 다음 단계 - - - - - - - diff --git a/content/docs/guide/platform-support.mdx b/content/docs/guide/platform-support.mdx deleted file mode 100644 index cfbccef..0000000 --- a/content/docs/guide/platform-support.mdx +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: Platform Support -description: Which platforms run Webview Bundle and the package to install for each. ---- - -One shared Rust core powers every integration, so a `.wvb` you pack once loads identically on Electron, Tauri, Android, iOS, and Deno Desktop. For how the core reaches each host and how the webview talks back through the bridge, see [Platform integration](/docs/guide/platform-integration). - -This page is the support matrix: the package or crate per platform, its minimum host version, and its status. - -## Support matrix - -| Platform | Webview host | Package / crate | Min version | Status | -| -------------------------------------------- | -------------- | --------------------------------- | ------------------------------------------------------------------------------- | ------------ | -| [Electron](/docs/guide/platforms/electron) | Chromium | `@wvb/electron` 0.1.0 (npm) | `electron >= 15` | Stable | -| [Tauri desktop](/docs/guide/platforms/tauri) | System WebView | `wvb-tauri` 0.1.0 (crate) | Tauri v2 | Stable | -| Tauri mobile | System WebView | `wvb-tauri` 0.1.0 (crate) | See [Android](/docs/guide/platforms/android) / [iOS](/docs/guide/platforms/ios) | Stable | -| [Android](/docs/guide/platforms/android) | System WebView | `webview-bundle-android` (Kotlin) | minSdk 24 / Android 7.0 | Stable | -| [iOS](/docs/guide/platforms/ios) | WKWebView | `webview-bundle-ios` (Swift) | iOS 16 / macOS 12 | Stable | -| [Deno Desktop](/docs/guide/platforms/deno) | Deno webview | `@wvb/deno-desktop` (JSR) | — | Experimental | - -## Desktop - -Electron and Tauri desktop are the most mature integrations. Both are published and both serve bundles through a caller-supplied URL scheme backed by the Rust core. - -### Electron - -Install the npm package: - -```sh -npm install @wvb/electron -``` - -Register a protocol and load a bundle by name (`://.wvb`): - -```ts title="main.ts" -import { app, BrowserWindow } from 'electron'; -import { bundleProtocol, wvb } from '@wvb/electron'; - -const instance = wvb({ - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow({ - webPreferences: { contextIsolation: true, nodeIntegration: false }, - }); - await window.loadURL('app://hacker-news.wvb'); -}); -``` - -Start with the [Electron guide](/docs/guide/platforms/electron). - -### Tauri - -The Tauri integration is the `wvb-tauri` Rust crate on crates.io targeting **Tauri v2** — there is no `@wvb/tauri` npm package. The same crate also drives Tauri's mobile targets. - -```toml title="src-tauri/Cargo.toml" -[dependencies] -wvb-tauri = "0.1.0" -``` - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new()) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -``` - -The window then loads `bundle://hacker-news.wvb`. Start with the [Tauri guide](/docs/guide/platforms/tauri). - -## Mobile - -Android and iOS are real, functional integrations. - -### Android - -The `webview-bundle-android` Kotlin library (namespace `dev.wvb`) requires **minSdk 24 (Android 7.0)** and serves bundles through a `WebViewClient`. Pull the native bindings, then install on a `WebView`: - -```sh -node scripts/install.mjs -``` - -```kotlin title="MainActivity.kt" -val wvb = WebViewBundle.getInstance( - this, - WebViewBundleConfig(protocols = listOf(WebViewBundleProtocol.bundle())), -) -val webView = WebView(this) -webView.settings.javaScriptEnabled = true -wvb.install(webView) -webView.loadUrl("https://app.wvb/") -``` - -Bundles are served over ordinary `https://.wvb/...` URLs (the bundle name is the first host label). See the [Android guide](/docs/guide/platforms/android). - -### iOS - -The `webview-bundle-ios` Swift package targets **iOS 16** and **macOS 12** and serves bundles through a `WKURLSchemeHandler`. Install a release of the native xcframework, then register a custom scheme on `WKWebViewConfiguration`: - -```sh -node scripts/install.mjs ffi/0.1.0 -``` - -```swift title="ContentView.swift" -let instance = try WebViewBundle.configure( - WebViewBundleConfig(protocols: [.bundle(scheme: "app")]) -) -let config = WKWebViewConfiguration() -instance.install(on: config) -let webView = WKWebView(frame: .zero, configuration: config) -webView.load(URLRequest(url: URL(string: "app://app.wvb")!)) -``` - -See the [iOS guide](/docs/guide/platforms/ios). - -## Deno Desktop - -Deno Desktop is the newest integration, on JSR as `@wvb/deno-desktop`. It builds a bundle source and exposes a `Deno.serve`-compatible handler, with one protocol per window. - - - Deno Desktop is experimental. See the [Deno Desktop guide](/docs/guide/platforms/deno) and the - [Deno API reference](/docs/references/deno). - - -## Next steps - - - - - - - diff --git a/content/docs/guide/platforms/electron/meta.json b/content/docs/guide/platforms/electron/meta.json deleted file mode 100644 index 5c496a3..0000000 --- a/content/docs/guide/platforms/electron/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Electron", - "pages": ["index", "forge", "builder"] -} diff --git a/content/docs/guide/platforms/electron/meta.ko.json b/content/docs/guide/platforms/electron/meta.ko.json deleted file mode 100644 index 5c496a3..0000000 --- a/content/docs/guide/platforms/electron/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Electron", - "pages": ["index", "forge", "builder"] -} diff --git a/content/docs/guide/protocol-handling.ko.mdx b/content/docs/guide/protocol-handling.ko.mdx deleted file mode 100644 index f2c8409..0000000 --- a/content/docs/guide/protocol-handling.ko.mdx +++ /dev/null @@ -1,243 +0,0 @@ ---- -title: 프로토콜 처리 -description: 웹뷰 요청이 bundle 프로토콜과 local 프로토콜을 거쳐 .wvb 번들 안의 파일로 매핑되는 방식. ---- - -프로토콜 핸들러는 웹뷰 요청을 네트워크의 바이트가 아니라 번들의 바이트로 바꿉니다. Webview Bundle은 두 가지 프로토콜을 제공합니다. - -- **bundle 프로토콜** — `.wvb` 소스에서 파일을 그대로 제공합니다. -- **local 프로토콜** — 번들러의 개발 서버로 요청을 전달하는 개발용 프록시로, 핫 리로드를 유지합니다. - -Rust 코어는 스킴을 등록하거나 검증하지 않습니다. 코어는 URI의 **호스트**와 **경로**만 읽으며, 스킴 자체는 읽지 않습니다. 스킴을 정하고 OS에 등록하는 일은 플랫폼 통합의 몫입니다. - -## bundle 프로토콜 - -요청은 URI의 두 부분을 기준으로 번들 소스에서 해석됩니다. 하나는 **번들 이름**(첫 `.`까지의 호스트)이고, 다른 하나는 **경로**(퍼센트 디코딩된 값)입니다. - -```text -app://app.wvb/index.html -> bundle "app", file "/index.html" -app://myapp.wvb/assets/logo.png -> bundle "myapp", file "/assets/logo.png" -``` - -경로 해석은 정적 파일 서버처럼 `index.html`을 채워 넣습니다. - -```text -/ -> /index.html # 끝에 슬래시가 있는 경우 -/about -> /about/index.html # 마지막 세그먼트에 "."이 없는 경우 -/a.js -> /a.js # 마지막 세그먼트에 "."이 있으면 그대로 제공 -``` - -라벨이 없는 호스트는 bundle-not-found 오류로 실패합니다. 번들 인덱스에 없는 경로는 **404 Not Found**를 반환합니다. - -### 메서드, 헤더, 응답 - -`GET`과 `HEAD`만 제공하며, 그 외의 메서드는 **405 Method Not Allowed**를 반환합니다. `HEAD`는 본문이 비어 있는 헤더를 반환합니다. - -파일을 제공할 때 핸들러는 인덱스에 저장된 해당 파일의 헤더를 재생한 뒤, `Content-Type`(인덱스 엔트리에서 가져옴)과 `Content-Length`(압축 해제된 크기)를 덮어씁니다. - -```text -GET app://app.wvb/assets/app.js --> 200 OK - cache-control: public, max-age=31536000 # 인덱스에서 재생됨 - content-type: application/javascript # 인덱스 엔트리에서 가져옴 - content-length: 84213 # 압축 해제된 크기 -``` - - - 헤더는 번들을 빌드할 때 파일별로 저장됩니다. 따라서 빌드 시점에 설정한 캐싱 지시자와 그 밖의 응답 - 헤더가 제공되는 응답에까지 그대로 유지됩니다. - - -### 범위 요청 - -웹뷰는 미디어 탐색과 대용량 다운로드 재개를 위해 범위 요청에 의존합니다. `Range` 헤더가 있으면 `Accept-Ranges: bytes`와 `Content-Range`를 담아 **206 Partial Content**를 반환합니다. - -```text -GET app://app.wvb/media/clip.mp4 -Range: bytes=0-1023 --> 206 Partial Content - accept-ranges: bytes - content-range: bytes 0-1023/5242880 - content-length: 1024 -``` - -- 반환되는 각 범위는 `1000 * 1024` 바이트(약 1 MB)로 제한되며, 이보다 큰 요청 범위는 잘립니다. -- 여러 범위는 `multipart/byteranges` 응답으로 반환됩니다. -- 충족할 수 없는 범위는 `Content-Range: bytes */`와 함께 **416 Range Not Satisfiable**를 반환합니다. - -```text -Range: bytes=99999999- --> 416 Range Not Satisfiable - content-range: bytes */5242880 -``` - -범위는 압축 해제된 콘텐츠를 기준으로 계산됩니다. 따라서 오프셋은 빌드가 생성한 파일과 일치합니다. - -## local 프로토콜 - -개발용 프록시입니다. `.wvb` 파일을 읽는 대신, 호스트를 localhost 기준 URL로 매핑하고 요청을 전달합니다. 번들러가 핫 리로드되는 자산을 실시간으로 제공하는 동안, 커스텀 스킴은 프로덕션과 동일하게 유지됩니다. - -```text -app://myapp/api/data?foo=bar -> http://localhost:3000/api/data?foo=bar -``` - -호스트-URL 매핑을 제공하세요. Electron에서는 `localProtocol(scheme, { hosts })`를 전달합니다. - -```ts title="main.ts" -import { localProtocol, wvb } from '@wvb/electron'; - -const instance = wvb({ - protocols: [ - localProtocol('app', { - hosts: { 'myapp.wvb': 'http://localhost:3000' }, - }), - ], -}); -``` - -Tauri에서는 `Protocol::local`에 호스트 맵을 설정합니다. - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol}; - -Config::new().protocol( - Protocol::local("app").host("myapp.wvb", "http://localhost:3000"), -); -``` - -매핑이 없는 호스트로 들어온 요청은 resolve 오류로 실패합니다. 프록시는 응답을 캐시하며, 캐시된 응답을 재생하는 방식으로 업스트림의 `304 Not Modified`를 따릅니다. - - - 개발 중에는 local 프로토콜을 사용하고, 출시 빌드에는 bundle 프로토콜을 사용하세요. 두 프로토콜은 - 같은 스킴을 공유하므로, 웹 앱의 URL은 둘 사이에서 바뀌지 않습니다. - - -## 플랫폼별 스킴 - -Electron, Tauri, iOS는 실제 커스텀 스킴을 웹뷰에 등록합니다. 반면 Android는 `WebViewClient.shouldInterceptRequest`를 통해 일반 `https://.wvb` 요청을 가로채므로, 등록할 커스텀 스킴이 없습니다. 어느 경우든 번들 이름은 호스트 라벨에서, 경로는 URI 경로에서 가져옵니다. 예제는 `app`(Electron, iOS)과 `bundle`(Tauri)을 등록하지만, 이름은 원하는 대로 정할 수 있습니다. - - - - -`@wvb/electron`은 스킴을 privileged로 등록하고 핸들러를 연결합니다. `bundleProtocol(scheme)`을 `webviewBundle`에 전달하고, `whenProtocolRegistered()`를 await한 뒤 `://.wvb`를 로드하세요. - -```js title="main.cjs" -const path = require('path'); -const { app, BrowserWindow } = require('electron'); -const { bundleProtocol, wvb } = require('@wvb/electron'); - -const instance = wvb({ - source: { builtinDir: path.join(__dirname, 'bundles') }, - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow(); - await window.loadURL('app://app.wvb/index.html'); -}); -``` - -스킴은 다음의 기본 권한으로 등록됩니다(`bundleProtocol('app', { privileges })`로 프로토콜별로 재정의 가능). - -```ts -{ - standard: true, secure: true, bypassCSP: true, - allowServiceWorkers: true, supportFetchAPI: true, - corsEnabled: true, stream: false, codeCache: true, -} -``` - -권한과 렌더러 브리지는 [Electron 가이드](/docs/guide/platforms/electron)를 참고하세요. - - - - -`wvb-tauri` 크레이트는 설정된 `Protocol`마다 비동기 URI 스킴 프로토콜을 등록합니다. `Protocol::bundle(scheme)`을 플러그인 설정에 전달한 뒤, 윈도우가 `://.wvb`를 가리키게 하세요. - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new()) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .unwrap(); -``` - -`tauri.conf.json`에서 윈도우가 스킴을 가리키게 하세요. - -```json title="src-tauri/tauri.conf.json" -{ - "app": { - "windows": [{ "url": "bundle://app.wvb" }] - } -} -``` - -전체 플러그인 설정은 [Tauri 가이드](/docs/guide/platforms/tauri)를 참고하세요. - - - - -Android는 커스텀 스킴을 등록하지 않습니다. 통합은 `WebViewClient`에서 일반 `https://.wvb` 내비게이션을 가로챕니다. 동일한 호스트-경로 매핑이 적용되므로, `https://app.wvb/index.html`은 번들 `app`의 파일 `/index.html`로 해석됩니다. - -```kotlin -class BundleWebViewClient : WebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest, - ): WebResourceResponse? { - // 요청을 Webview Bundle 핸들러에 넘깁니다. - return webviewBundle.handle(request) - } -} -``` - -동작하는 설정은 [Android 가이드](/docs/guide/platforms/android)를 참고하세요. - - - - -iOS는 `WKURLSchemeHandler`를 통해 `WKWebView` 설정에 실제 커스텀 스킴을 등록한 뒤, `://.wvb`를 로드합니다. 최소 배포 대상은 iOS 16입니다. - -```swift -let configuration = WKWebViewConfiguration() -configuration.setURLSchemeHandler(bundleSchemeHandler, forURLScheme: "app") - -let webView = WKWebView(frame: .zero, configuration: configuration) -webView.load(URLRequest(url: URL(string: "app://app.wvb/index.html")!)) -``` - -동작하는 설정은 [iOS 가이드](/docs/guide/platforms/ios)를 참고하세요. - - - - -## 관련 문서 - - - - - - - diff --git a/content/docs/guide/protocol-handling.mdx b/content/docs/guide/protocol-handling.mdx deleted file mode 100644 index a0d92ca..0000000 --- a/content/docs/guide/protocol-handling.mdx +++ /dev/null @@ -1,243 +0,0 @@ ---- -title: Protocol handling -description: How a webview request maps to a file inside a .wvb bundle through the bundle and local protocols. ---- - -A protocol handler turns a webview request into bytes from a bundle instead of bytes from the network. Webview Bundle ships two: - -- **bundle protocol** — serves files straight out of a `.wvb` source. -- **local protocol** — a development proxy that forwards to your bundler's dev server, keeping hot reload. - -The Rust core does not register or validate the scheme. It reads only the URI **host** and **path** — never the scheme itself. Picking a scheme and registering it with the OS is the platform integration's job. - -## The bundle protocol - -A request resolves against a bundle source by two parts of the URI: the **bundle name** (host up to the first `.`) and the **path** (percent-decoded). - -```text -app://app.wvb/index.html -> bundle "app", file "/index.html" -app://myapp.wvb/assets/logo.png -> bundle "myapp", file "/assets/logo.png" -``` - -Path resolution fills in `index.html` like a static file server: - -```text -/ -> /index.html # trailing slash -/about -> /about/index.html # last segment has no "." -/a.js -> /a.js # last segment has a "." -> served as-is -``` - -A host with no label fails with a bundle-not-found error. A path missing from the bundle index returns **404 Not Found**. - -### Methods, headers, and responses - -Only `GET` and `HEAD` are served; anything else returns **405 Method Not Allowed**. `HEAD` returns headers with an empty body. - -For a served file, the handler replays the headers stored for that file in the index, then overrides `Content-Type` (from the index entry) and `Content-Length` (the uncompressed size): - -```text -GET app://app.wvb/assets/app.js --> 200 OK - cache-control: public, max-age=31536000 # replayed from the index - content-type: application/javascript # from the index entry - content-length: 84213 # uncompressed size -``` - - - Headers are stored per file when the bundle is built, so caching directives and other response - headers you set at build time survive into the served response. - - -### Range requests - -Webviews rely on range requests for seeking media and resuming large downloads. A `Range` header gets **206 Partial Content** with `Accept-Ranges: bytes` and a `Content-Range`: - -```text -GET app://app.wvb/media/clip.mp4 -Range: bytes=0-1023 --> 206 Partial Content - accept-ranges: bytes - content-range: bytes 0-1023/5242880 - content-length: 1024 -``` - -- Each returned range is capped at `1000 * 1024` bytes (~1 MB); larger requested ranges are truncated. -- Multiple ranges come back as a `multipart/byteranges` response. -- An unsatisfiable range returns **416 Range Not Satisfiable** with `Content-Range: bytes */`. - -```text -Range: bytes=99999999- --> 416 Range Not Satisfiable - content-range: bytes */5242880 -``` - -Ranges are computed over the uncompressed content, so offsets match the file your build produced. - -## The local protocol - -A development proxy: instead of reading a `.wvb` file, it maps a host to a localhost base URL and forwards the request. The custom scheme stays the same as production while your bundler serves live, hot-reloading assets. - -```text -app://myapp/api/data?foo=bar -> http://localhost:3000/api/data?foo=bar -``` - -Provide a host-to-URL map. In Electron, pass `localProtocol(scheme, { hosts })`: - -```ts title="main.ts" -import { localProtocol, wvb } from '@wvb/electron'; - -const instance = wvb({ - protocols: [ - localProtocol('app', { - hosts: { 'myapp.wvb': 'http://localhost:3000' }, - }), - ], -}); -``` - -In Tauri, configure the host map on `Protocol::local`: - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol}; - -Config::new().protocol( - Protocol::local("app").host("myapp.wvb", "http://localhost:3000"), -); -``` - -A request whose host has no mapping fails with a resolve error. The proxy caches responses and honors upstream `304 Not Modified` by replaying the cached response. - - - Use the local protocol during development and the bundle protocol for shipped builds. Because both - share the same scheme, your web app's URLs do not change between the two. - - -## Per-platform schemes - -Electron, Tauri, and iOS register a real custom scheme with the webview. Android instead intercepts ordinary `https://.wvb` requests through `WebViewClient.shouldInterceptRequest`, so there is no custom scheme to register. In every case the bundle name comes from the host label and the path comes from the URI path. The examples register `app` (Electron, iOS), `bundle` (Tauri); the name is yours to choose. - - - - -`@wvb/electron` registers the scheme as privileged and wires the handler. Pass `bundleProtocol(scheme)` to `webviewBundle`, await `whenProtocolRegistered()`, then load `://.wvb`. - -```js title="main.cjs" -const path = require('path'); -const { app, BrowserWindow } = require('electron'); -const { bundleProtocol, wvb } = require('@wvb/electron'); - -const instance = wvb({ - source: { builtinDir: path.join(__dirname, 'bundles') }, - protocols: [bundleProtocol('app', { onError: e => console.error('[wvb]', e) })], -}); - -app.whenReady().then(async () => { - await instance.whenProtocolRegistered(); - const window = new BrowserWindow(); - await window.loadURL('app://app.wvb/index.html'); -}); -``` - -The scheme is registered with these default privileges (override per protocol via `bundleProtocol('app', { privileges })`): - -```ts -{ - standard: true, secure: true, bypassCSP: true, - allowServiceWorkers: true, supportFetchAPI: true, - corsEnabled: true, stream: false, codeCache: true, -} -``` - -See the [Electron guide](/docs/guide/platforms/electron) for privileges and the renderer bridge. - - - - -The `wvb-tauri` crate registers an async URI-scheme protocol per configured `Protocol`. Pass `Protocol::bundle(scheme)` to the plugin config, then point the window at `://.wvb`. - -```rust title="src-tauri/src/lib.rs" -use wvb_tauri::{Config, Protocol, Source}; - -tauri::Builder::default() - .plugin(wvb_tauri::init( - Config::new() - .source(Source::new()) - .protocol(Protocol::bundle("bundle")), - )) - .run(tauri::generate_context!()) - .unwrap(); -``` - -Point the window at the scheme in `tauri.conf.json`: - -```json title="src-tauri/tauri.conf.json" -{ - "app": { - "windows": [{ "url": "bundle://app.wvb" }] - } -} -``` - -See the [Tauri guide](/docs/guide/platforms/tauri) for the full plugin setup. - - - - -Android does not register a custom scheme. The integration intercepts plain `https://.wvb` navigations from a `WebViewClient`. The same host-and-path mapping applies, so `https://app.wvb/index.html` resolves to bundle `app`, file `/index.html`. - -```kotlin -class BundleWebViewClient : WebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest, - ): WebResourceResponse? { - // Hand the request to the Webview Bundle handler. - return webviewBundle.handle(request) - } -} -``` - -See the [Android guide](/docs/guide/platforms/android) for the working setup. - - - - -iOS registers a real custom scheme on the `WKWebView` configuration through a `WKURLSchemeHandler`, then loads `://.wvb`. The minimum deployment target is iOS 16. - -```swift -let configuration = WKWebViewConfiguration() -configuration.setURLSchemeHandler(bundleSchemeHandler, forURLScheme: "app") - -let webView = WKWebView(frame: .zero, configuration: configuration) -webView.load(URLRequest(url: URL(string: "app://app.wvb/index.html")!)) -``` - -See the [iOS guide](/docs/guide/platforms/ios) for the working setup. - - - - -## Related - - - - - - - diff --git a/content/docs/guide/providers/aws.ko.mdx b/content/docs/guide/providers/aws.ko.mdx deleted file mode 100644 index 00dd16b..0000000 --- a/content/docs/guide/providers/aws.ko.mdx +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: AWS 프로바이더 -description: S3, CloudFront, Lambda@Edge와 선택적 KMS 서명을 사용해 AWS에서 원격 Webview Bundle을 호스팅하고, 제공하고, 프로비저닝합니다. ---- - -직접 관리하는 AWS 계정에 번들을 게시하고 글로벌 CDN으로 제공합니다. 번들은 **S3**에 저장되고, **CloudFront**가 이를 캐싱하며, 두 개의 **Lambda@Edge** 함수가 원격 HTTP 계약을 S3 읽기로 변환합니다. 그리고 **KMS**로 각 번들에 선택적으로 서명할 수 있습니다. -이 구성 요소들이 구현하는 계약과 프로바이더를 선택하는 방법은 [원격 구축하기](/docs/guide/remote)를 참고하세요. - -## 지원 서비스 - -| Service | Role | -| ----------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| S3 | 각 번들 객체와 현재 버전을 기록하는 `deployment.json` 포인터를 저장합니다. | -| CloudFront | S3 앞단의 CDN입니다. 배포기는 배포 후 캐시 무효화를 요청할 수 있습니다. | -| Lambda@Edge | 두 개의 함수입니다. origin-request는 URL을 번들 키로 재작성하고, origin-response는 S3 객체 메타데이터를 계약 응답 헤더로 변환합니다. | -| KMS | 선택 사항입니다. 각 번들의 무결성 문자열에 서명하여 클라이언트가 게시자를 검증할 수 있게 합니다. | - -## 패키지 - -| Package | Version | Role | -| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------- | -| `@wvb/remote-aws` | `0.1.0` | 게시 측입니다. `wvb.config.ts`에 사용할 `uploader`, `deployer`와 선택적 `signature`를 생성합니다. | -| `@wvb/remote-aws-provider` | `0.1.0` | Lambda@Edge 서버(Hono 앱)입니다. `./origin-request`와 `./origin-response` 하위 경로를 노출합니다. | -| `@wvb/remote-aws-provider-pulumi` | `0.1.0` | S3, CloudFront, 두 개의 Lambda@Edge 함수, IAM을 프로비저닝하는 Pulumi 컴포넌트입니다. | - - - - -```sh -npm install @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -```sh -pnpm add @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -```sh -yarn add @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -## 게시 측 설정 - -`awsRemote()`는 `{ uploader, deployer, signature? }`를 반환합니다. 이를 설정의 `remote` 블록에 펼쳐 넣으세요. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { awsRemote } from '@wvb/remote-aws'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - ...awsRemote({ - bucket: 'my-app-bundles', - aws: { - region: 'us-east-1', - profile: 'my-app-deploy', - }, - }), - }, -}); -``` - -`remote` 스키마 전체는 [원격, 무결성, 서명 설정](/docs/config/remote)을 참고하세요. - -### uploader와 deployer 커스터마이징 - -`uploader`와 `deployer`는 각자의 옵션을 받습니다. uploader는 멀티파트 업로드로 S3에 스트리밍하고, 계약 값을 S3 객체 메타데이터(`webview-bundle-name`, `webview-bundle-version`, 그리고 존재할 경우 `webview-bundle-integrity`와 `webview-bundle-signature`)로 기록합니다. deployer는 `deployment.json`을 GET한 뒤 PUT하며, 배포 후 CloudFront를 무효화할 수 있습니다. - -```ts title="wvb.config.ts" -...awsRemote({ - bucket: 'my-app-bundles', - aws: { region: 'us-east-1' }, - uploader: { - // 기본값: bundles/${name}/${name}_${version}.wvb - key: (name, version) => `bundles/${name}/${version}/${name}_${version}.wvb`, - cacheControl: 'public, max-age=31536000, immutable', - contentType: 'application/webview-bundle', - }, - deployer: { - invalidation: { - distributionId: 'E1ABCDEF2GHIJK', - }, - }, -}), -``` - -번들이 이미 존재하고 `force`를 전달하지 않으면, uploader는 `HeadObject` 사전 확인을 실행하고 `BundleAlreadyUploadedError`를 던집니다. - -```ts -import { isBundleAlreadyUploadedError } from '@wvb/remote-aws'; - -try { - await uploader.upload({ bundle, bundleName, version }); -} catch (err) { - if (isBundleAlreadyUploadedError(err)) { - // 덮어쓰려면 --force로 다시 실행하세요. - } - throw err; -} -``` - -### KMS로 서명하기 - -각 번들의 무결성 문자열에 서명하려면 `signature`에 KMS 서명자 설정을 지정하세요. 서명자는 `MessageType: 'DIGEST'`로 KMS를 호출하고 base64 서명을 반환합니다. - -```ts title="wvb.config.ts" -import { awsRemote } from '@wvb/remote-aws'; - -...awsRemote({ - bucket: 'my-app-bundles', - aws: { region: 'us-east-1' }, - signature: { - keyId: 'arn:aws:kms:us-east-1:111122223333:key/abcd-1234', - algorithm: 'ECDSA_SHA_256', - }, -}), -``` - -서명자를 단독으로 만들려면 `awsKmsSignatureSigner({ keyId, algorithm })`를 사용하세요. 서명 함수를 반환합니다. 서명을 비활성화하려면 `signature: false`를 전달하세요. - - - 기본 uploader 키와 프로바이더가 기대하는 키는 일치하지 않습니다. uploader는 `bundles/{name}/{name} - _{version}.wvb`에 기록하지만, Lambda@Edge 프로바이더는 요청을 `bundles/ - {name}/{version}/{name}_{version}.wvb`로 재작성합니다. 즉 `{version}` 디렉터리가 하나 더 있습니다. - 엔드투엔드 AWS 구성에서는 위 예시처럼 프로바이더의 레이아웃을 만드는 커스텀 uploader `key`를 - 설정하거나, 일치하는 S3 레이아웃을 마련하세요. 기본값에 의존하기 전에 패키지 소스와 대조해 - 확인하세요. - - -## Lambda@Edge에서 제공하기 - -`@wvb/remote-aws-provider`는 계약을 두 개의 CloudFront 이벤트 핸들러로 분리합니다. 각각을 별도의 Lambda에 연결하세요. - -```ts title="origin-request.ts" -import { originRequest } from '@wvb/remote-aws-provider/origin-request'; - -export const handler = originRequest({ - bucketName: 'my-app-bundles', - region: 'us-east-1', - // allowOtherVersions: true, // GET /bundles/{name}/{version} 허용, 그렇지 않으면 403 -}); -``` - -```ts title="origin-response.ts" -import { originResponse } from '@wvb/remote-aws-provider/origin-response'; - -export const handler = originResponse(); -``` - -origin-request 핸들러는 `deployment.json`을 읽고, 버전을 해석하며(채널에서 기본 버전으로 폴백), origin URI를 번들 키로 재작성하고, 요청에 태그를 붙입니다. origin-response 핸들러는 S3의 `x-amz-meta-webview-bundle-*` 메타데이터를 `webview-bundle-*` 응답 헤더로 변환합니다. `allowOtherVersions: true`가 아니면 `GET /bundles/{name}/{version}`은 `403`을 반환합니다. - -## Pulumi로 프로비저닝하기 - -`WebviewBundleRemoteProvider`(토큰 `webview-bundle:aws:RemoteProvider`)는 S3 버킷과 정책, Lambda@Edge용 IAM 역할, `us-east-1`에 두 개의 Lambda@Edge 함수(Lambda@Edge에 필수), 그리고 두 함수가 모두 연결된 CloudFront 배포를 생성합니다. - -```ts title="index.ts" -import { WebviewBundleRemoteProvider } from '@wvb/remote-aws-provider-pulumi'; - -const provider = new WebviewBundleRemoteProvider('webview-bundle', {}); - -export const bucketName = provider.bucketName; -export const distributionDomainName = provider.cloudfrontDistributionDomainName; -export const distributionId = provider.cloudfrontDistributionId; -export const originRequestArn = provider.lambdaOriginRequestArn; -export const originResponseArn = provider.lambdaOriginResponseArn; -``` - -이 컴포넌트는 배포 시점에 Lambda 코드를 번들링하고 각 함수에 `bucketName`, `region`, `allowOtherVersions`를 주입합니다. 함수는 기본적으로 `nodejs22.x` 런타임을 사용합니다. 함수별 설정은 `lambdaOriginRequest` / `lambdaOriginResponse`로 재정의하세요. - -```ts title="index.ts" -new WebviewBundleRemoteProvider('webview-bundle', { - lambdaOriginRequest: { - runtime: 'nodejs22.x', - architecture: 'arm64', - memorySize: 256, - }, -}); -``` - -Lambda@Edge는 `us-east-1`을 요구합니다. Pulumi AWS 프로바이더를 해당 리전으로 설정하세요. - -```ts title="index.ts" -import * as aws from '@pulumi/aws'; - -const usEast1 = new aws.Provider('us-east-1', { region: 'us-east-1' }); -``` - -## 관련 문서 - - - - - diff --git a/content/docs/guide/providers/aws.mdx b/content/docs/guide/providers/aws.mdx deleted file mode 100644 index dcf3bc3..0000000 --- a/content/docs/guide/providers/aws.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: AWS provider -description: Host, serve, and provision remote Webview Bundles on AWS using S3, CloudFront, Lambda@Edge, and optional KMS signing. ---- - -Publish bundles to your own AWS account and serve them over a global CDN. Bundles live in **S3**, **CloudFront** caches them, two **Lambda@Edge** functions translate the remote HTTP contract into S3 reads, and **KMS** can optionally sign each bundle. -For the contract these pieces implement and how to choose a provider, see [Building a remote](/docs/guide/remote). - -## Backing services - -| Service | Role | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| S3 | Stores each bundle object and the `deployment.json` pointer that records the current version. | -| CloudFront | CDN in front of S3; the deployer can issue cache invalidations after a deploy. | -| Lambda@Edge | Two functions — origin-request rewrites the URL to the bundle key, origin-response turns S3 object metadata into the contract response headers. | -| KMS | Optional. Signs the integrity string of each bundle so clients can verify who published it. | - -## Packages - -| Package | Version | Role | -| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------- | -| `@wvb/remote-aws` | `0.1.0` | Publish side. Produces `uploader`, `deployer`, and optional `signature` for `wvb.config.ts`. | -| `@wvb/remote-aws-provider` | `0.1.0` | The Lambda@Edge server (a Hono app). Exposes `./origin-request` and `./origin-response` subpaths. | -| `@wvb/remote-aws-provider-pulumi` | `0.1.0` | Pulumi component that provisions S3, CloudFront, the two Lambda@Edge functions, and IAM. | - - - - -```sh -npm install @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -```sh -pnpm add @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -```sh -yarn add @wvb/remote-aws @wvb/remote-aws-provider @wvb/remote-aws-provider-pulumi -``` - - - - -## Configure the publish side - -`awsRemote()` returns `{ uploader, deployer, signature? }`. Spread it into the `remote` block of your config: - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { awsRemote } from '@wvb/remote-aws'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - ...awsRemote({ - bucket: 'my-app-bundles', - aws: { - region: 'us-east-1', - profile: 'my-app-deploy', - }, - }), - }, -}); -``` - -See [Remote, integrity and signature config](/docs/config/remote) for the full `remote` schema. - -### Customize the uploader and deployer - -`uploader` and `deployer` accept their own options. The uploader streams to S3 with a multipart upload and writes the contract values as S3 object metadata (`webview-bundle-name`, `webview-bundle-version`, and, when present, `webview-bundle-integrity` and `webview-bundle-signature`). The deployer GET-then-PUTs `deployment.json` and can invalidate CloudFront after a deploy: - -```ts title="wvb.config.ts" -...awsRemote({ - bucket: 'my-app-bundles', - aws: { region: 'us-east-1' }, - uploader: { - // Default: bundles/${name}/${name}_${version}.wvb - key: (name, version) => `bundles/${name}/${version}/${name}_${version}.wvb`, - cacheControl: 'public, max-age=31536000, immutable', - contentType: 'application/webview-bundle', - }, - deployer: { - invalidation: { - distributionId: 'E1ABCDEF2GHIJK', - }, - }, -}), -``` - -If the bundle already exists and you do not pass `force`, the uploader runs a `HeadObject` pre-check and throws `BundleAlreadyUploadedError`: - -```ts -import { isBundleAlreadyUploadedError } from '@wvb/remote-aws'; - -try { - await uploader.upload({ bundle, bundleName, version }); -} catch (err) { - if (isBundleAlreadyUploadedError(err)) { - // Re-run with --force to overwrite. - } - throw err; -} -``` - -### Sign with KMS - -Set `signature` to a KMS signer config to sign each bundle's integrity string. The signer calls KMS with `MessageType: 'DIGEST'` and returns a base64 signature: - -```ts title="wvb.config.ts" -import { awsRemote } from '@wvb/remote-aws'; - -...awsRemote({ - bucket: 'my-app-bundles', - aws: { region: 'us-east-1' }, - signature: { - keyId: 'arn:aws:kms:us-east-1:111122223333:key/abcd-1234', - algorithm: 'ECDSA_SHA_256', - }, -}), -``` - -Build the signer standalone with `awsKmsSignatureSigner({ keyId, algorithm })`, which returns a signing function. Pass `signature: false` to disable signing. - - - The default uploader key and the provider's expected key do not match. The uploader writes to - `bundles/{name}/{name}_{version}.wvb`, but the Lambda@Edge provider rewrites requests to `bundles/ - {name}/{version}/{name}_{version}.wvb` — an extra `{version}` directory. For an end-to-end AWS - setup, set a custom uploader `key` that produces the provider's layout (as shown above), or - arrange a matching S3 layout. Verify against the package source before relying on the defaults. - - -## Serve from Lambda@Edge - -`@wvb/remote-aws-provider` splits the contract across two CloudFront event handlers. Wire each to its own Lambda: - -```ts title="origin-request.ts" -import { originRequest } from '@wvb/remote-aws-provider/origin-request'; - -export const handler = originRequest({ - bucketName: 'my-app-bundles', - region: 'us-east-1', - // allowOtherVersions: true, // allow GET /bundles/{name}/{version}; otherwise 403 -}); -``` - -```ts title="origin-response.ts" -import { originResponse } from '@wvb/remote-aws-provider/origin-response'; - -export const handler = originResponse(); -``` - -The origin-request handler reads `deployment.json`, resolves the version (falling back from a channel to the default version), rewrites the origin URI to the bundle key, and tags the request. The origin-response handler converts S3 `x-amz-meta-webview-bundle-*` metadata into the `webview-bundle-*` response headers. `GET /bundles/{name}/{version}` returns `403` unless `allowOtherVersions: true`. - -## Provision with Pulumi - -`WebviewBundleRemoteProvider` (token `webview-bundle:aws:RemoteProvider`) creates the S3 bucket and policy, an IAM role for Lambda@Edge, the two Lambda@Edge functions in `us-east-1` (required for Lambda@Edge), and a CloudFront distribution with both functions attached: - -```ts title="index.ts" -import { WebviewBundleRemoteProvider } from '@wvb/remote-aws-provider-pulumi'; - -const provider = new WebviewBundleRemoteProvider('webview-bundle', {}); - -export const bucketName = provider.bucketName; -export const distributionDomainName = provider.cloudfrontDistributionDomainName; -export const distributionId = provider.cloudfrontDistributionId; -export const originRequestArn = provider.lambdaOriginRequestArn; -export const originResponseArn = provider.lambdaOriginResponseArn; -``` - -The component bundles the Lambda code at deploy time and injects `bucketName`, `region`, and `allowOtherVersions` into each function. Functions default to the `nodejs22.x` runtime. Override per-function settings with `lambdaOriginRequest` / `lambdaOriginResponse`: - -```ts title="index.ts" -new WebviewBundleRemoteProvider('webview-bundle', { - lambdaOriginRequest: { - runtime: 'nodejs22.x', - architecture: 'arm64', - memorySize: 256, - }, -}); -``` - -Lambda@Edge requires `us-east-1`. Configure the Pulumi AWS provider for that region: - -```ts title="index.ts" -import * as aws from '@pulumi/aws'; - -const usEast1 = new aws.Provider('us-east-1', { region: 'us-east-1' }); -``` - -## Related - - - - - diff --git a/content/docs/guide/providers/cloudflare.ko.mdx b/content/docs/guide/providers/cloudflare.ko.mdx deleted file mode 100644 index 10e6be7..0000000 --- a/content/docs/guide/providers/cloudflare.ko.mdx +++ /dev/null @@ -1,231 +0,0 @@ ---- -title: Cloudflare 프로바이더 -description: R2, Workers KV, Cloudflare Workers를 사용해 Cloudflare에서 번들을 저장하고 배포하고 제공합니다. ---- - -Webview Bundle 원격을 Cloudflare 엣지에서 완전히 실행할 수 있습니다. 번들은 **R2**에, 버전 포인터는 **Workers KV**에 저장하고, **Cloudflare Worker**가 업데이터와 통신하는 HTTP 계약을 제공합니다. 관리할 서버 없이 전 세계에 낮은 지연으로 무선(OTA) 전달을 제공합니다. - -세 개의 패키지가 이 워크플로를 담당합니다. - -| Package | 역할 | 실행 위치 | -| ---------------------------------------- | --------------------------------------- | ------------------ | -| `@wvb/remote-cloudflare` | `wvb.config`용 업로더 + 디플로이어 | 로컬 머신 / CI | -| `@wvb/remote-cloudflare-provider` | 번들을 제공하는 Worker | Cloudflare Workers | -| `@wvb/remote-cloudflare-provider-pulumi` | 인프라를 프로비저닝하는 Pulumi 컴포넌트 | `pulumi up` | - -세 패키지 모두 `0.1.0`입니다. `wvb.config`에서 버전을 고정하세요. - -```sh -npm i -D @wvb/remote-cloudflare -``` - - - 원격이 처음이신가요? [원격 만들기](/docs/guide/remote)에서 계약과 로컬 테스트 루프를 먼저 - 살펴보세요. 전체 `wvb.config` 레퍼런스는 [원격, 무결성 및 서명 설정](/docs/config/remote)을 - 참고하세요. - - -## 각 구성 요소가 맞물리는 방식 - -각 역할은 하나의 Cloudflare 서비스에 매핑됩니다. - -- **R2**는 R2의 S3 호환 API를 통해 각 번들을 객체로 저장합니다. `@wvb/remote-cloudflare` 업로더는 `@wvb/remote-aws`의 S3 업로더를 R2로 향하게 하여 재사용합니다. -- **Workers KV**는 배포 포인터를 보관합니다. 번들마다(또는 번들과 채널마다) 키 하나를 두고, 그 값은 배포된 버전입니다. -- `@wvb/remote-cloudflare-provider`를 실행하는 **Cloudflare Worker**는 KV를 읽어 버전을 확인한 뒤, R2에서 일치하는 객체를 스트리밍합니다. - - - Cloudflare에는 기본 서명 기능이 없습니다. KMS에 해당하는 것이 없고, `@wvb/remote-cloudflare`도 - 서명자를 제공하지 않습니다. [서명](/docs/config/remote)이 필요하면 다른 곳에서 생성한 뒤, 결과로 - 나온 `signature` 문자열을 `wvb.config`를 통해 전달하세요. - - -## 업로더와 디플로이어 설정 - -`cloudflareRemote()`를 호출하고 그 결과를 `remote` 블록에 펼쳐 넣으세요. 이 함수는 `{ uploader, deployer }`를 반환합니다. 업로더는 번들을 R2에 쓰고, 디플로이어는 버전을 KV에 씁니다. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { cloudflareRemote } from '@wvb/remote-cloudflare'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - bundleName: 'my-app', - ...cloudflareRemote({ - bucket: 'webview-bundle', - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - kvNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID!, - }), - }, -}); -``` - -| Option | Type | 설명 | -| --------------- | -------- | ------------------------------------------------------------------------------------- | -| `bucket` | `string` | 번들 객체를 저장하는 R2 버킷. | -| `accountId` | `string` | Cloudflare 계정 ID. R2 엔드포인트와 KV 대상을 설정합니다. | -| `kvNamespaceId` | `string` | 배포 포인터를 보관하는 Workers KV 네임스페이스. | -| `uploader` | object | 내부 S3 업로더에 대한 선택적 재정의(`key`, `contentType`, `metadata` 등). | -| `deployer` | object | KV 디플로이어에 대한 선택적 재정의(`key`, `expiration`, `expirationTtl`, `metadata`). | - -기본값: 업로더는 `region: 'auto'`와 엔드포인트 `https://.r2.cloudflarestorage.com`을 설정한 다음, `bundles//_.wvb` 키에 객체를 저장하고 `webview-bundle-*` 커스텀 메타데이터를 붙입니다. 디플로이어는 버전을 KV의 `` 키(채널 배포일 때는 `/`)에 씁니다. - -레이아웃이 다르면 둘 중 어느 쪽이든 재정의하세요. - -```ts title="wvb.config.ts" -...cloudflareRemote({ - bucket: 'webview-bundle', - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - kvNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID!, - uploader: { - key: (name, version) => `releases/${name}/${version}.wvb`, - cacheControl: 'public, max-age=31536000, immutable', - }, - deployer: { - key: (name, _version, channel) => (channel ? `${name}/${channel}` : name), - expirationTtl: 60 * 60 * 24, - }, -}), -``` - -R2에는 S3 자격 증명이 필요합니다. AWS와 동일한 방식으로, S3 클라이언트가 읽는 환경변수를 통해 제공하세요. - -```sh -export AWS_ACCESS_KEY_ID= -export AWS_SECRET_ACCESS_KEY= -``` - -설정을 마쳤으면 번들을 게시하세요. - -```sh -wvb pack ./dist -wvb upload --version 1.2.0 -wvb deploy --version 1.2.0 -``` - -`wvb upload`는 기본적으로 배포하지 않습니다. `wvb deploy`를 실행하거나 `upload`에 `--deploy`를 전달해 해당 버전을 라이브 상태로 만드세요. - -```sh -wvb upload --version 1.2.0 --deploy -``` - -기본 포인터 대신 채널로 배포하려면 다음과 같이 합니다. - -```sh -wvb deploy --version 1.2.0 --channel beta -``` - -모든 플래그는 [CLI 레퍼런스](/docs/guide/cli)를 참고하세요. - -## Worker로 번들 제공하기 - -제공 측은 `@wvb/remote-cloudflare-provider`의 Hono 앱입니다. `wvbRemote()`로 생성한 뒤 Worker의 바인딩을 그 `Context`로 전달하세요. `KV` 바인딩은 `kv`가 되고, `BUCKET`(R2) 바인딩은 `r2`가 됩니다. - -```ts title="src/worker.ts" -import { wvbRemote } from '@wvb/remote-cloudflare-provider'; - -const remote = wvbRemote(); - -export interface Env { - KV: KVNamespace; - BUCKET: R2Bucket; -} - -export default { - fetch(request: Request, env: Env): Response | Promise { - return remote.fetch(request, { kv: env.KV, r2: env.BUCKET }); - }, -}; -``` - -`wvbRemote()`는 옵션 하나 `allowOtherVersions`(기본값 `false`)를 받습니다. `false`일 때 `GET /bundles/:name/:version`은 `403`을 반환합니다. 배포된 버전 외의 버전도 클라이언트가 다운로드하게 하려면 `true`로 설정하세요. - -```ts title="src/worker.ts" -const remote = wvbRemote({ allowOtherVersions: true }); -``` - -Worker는 KV에서 버전을 확인하고, R2에서 `bundles//_.wvb`를 읽어, 필수 헤더인 `Webview-Bundle-Name`과 `Webview-Bundle-Version`(객체 메타데이터에 있으면 `Webview-Bundle-Integrity`와 `Webview-Bundle-Signature`도 함께)을 붙여 반환합니다. - -이름이 `Context`와 맞도록 `wrangler.jsonc`에서 `KV`와 `BUCKET`을 바인딩하세요. - -```json title="wrangler.jsonc" -{ - "name": "webview-bundle", - "main": "src/worker.ts", - "compatibility_date": "2025-01-01", - "kv_namespaces": [{ "binding": "KV", "id": "" }], - "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "webview-bundle" }] -} -``` - -Worker를 배포하세요. - -```sh -npx wrangler deploy -``` - -라이브 엔드포인트에서 계약을 검증하세요. - -```sh -curl https://bundles.example.com/bundles -# [{ "name": "my-app", "version": "1.2.0" }] - -curl -I https://bundles.example.com/bundles/my-app -# Webview-Bundle-Name: my-app -# Webview-Bundle-Version: 1.2.0 -# Content-Type: application/webview-bundle -``` - -## Pulumi로 프로비저닝하기 - -`@wvb/remote-cloudflare-provider-pulumi`는 전체 스택을 하나의 컴포넌트 리소스로 프로비저닝합니다. R2 버킷, Workers KV 네임스페이스, Worker(버전과 배포 포함)를 만들고, `Context`가 기대하는 바인딩 `BUCKET`(R2)과 `KV`로 서로 연결합니다. - -```ts title="index.ts" -import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; - -const provider = new WebviewBundleRemoteProvider('webview-bundle', { - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - // Worker를 점진적으로 롤아웃합니다. 기본값은 100입니다. - workerDeploymentPercentage: 25, -}); - -export const bucketName = provider.bucketName; -export const kvNamespaceId = provider.kvNamespaceId; -export const workerId = provider.workerId; -export const workerVersionId = provider.workerVersionId; -export const workerDeploymentId = provider.workerDeploymentId; -``` - -이 컴포넌트는 Pulumi 리소스 토큰 `webview-bundle:cloudflare:RemoteProvider`를 등록합니다. `workerDeploymentPercentage`는 새 Worker 버전이 받는 트래픽 비율입니다(기본값 `100`). - -```sh -pulumi up -pulumi stack output kvNamespaceId -``` - -내보낸 `bucketName`과 `kvNamespaceId`를 위의 `cloudflareRemote()`에 넣은 다음, CLI로 게시하세요. - -## 다음 단계 - - - - - - - diff --git a/content/docs/guide/providers/cloudflare.mdx b/content/docs/guide/providers/cloudflare.mdx deleted file mode 100644 index fc53b3b..0000000 --- a/content/docs/guide/providers/cloudflare.mdx +++ /dev/null @@ -1,231 +0,0 @@ ---- -title: Cloudflare provider -description: Store, deploy, and serve bundles on Cloudflare using R2, Workers KV, and Cloudflare Workers. ---- - -Run a Webview Bundle remote entirely on Cloudflare's edge: bundles live in **R2**, version pointers live in **Workers KV**, and a **Cloudflare Worker** serves the HTTP contract the updater talks to. You get global, low-latency over-the-air (OTA) delivery with no servers to manage. - -Three packages cover the workflow: - -| Package | Role | Runs | -| ---------------------------------------- | --------------------------------------------------- | ------------------ | -| `@wvb/remote-cloudflare` | Uploader + deployer for `wvb.config` | Your machine / CI | -| `@wvb/remote-cloudflare-provider` | The Worker that serves bundles | Cloudflare Workers | -| `@wvb/remote-cloudflare-provider-pulumi` | Pulumi component that provisions the infrastructure | `pulumi up` | - -All three are `0.1.0`. Pin the version in your `wvb.config`. - -```sh -npm i -D @wvb/remote-cloudflare -``` - - - New to remotes? Start with [Building a remote](/docs/guide/remote) for the contract and the local - testing loop, and see [Remote, integrity & signature config](/docs/config/remote) for the full - `wvb.config` reference. - - -## How the pieces fit - -Each role maps onto a Cloudflare service: - -- **R2** stores each bundle as an object, via R2's S3-compatible API. The `@wvb/remote-cloudflare` uploader reuses the S3 uploader from `@wvb/remote-aws`, pointed at R2. -- **Workers KV** holds the deployment pointer — one key per bundle (or per bundle and channel) whose value is the deployed version. -- A **Cloudflare Worker** running `@wvb/remote-cloudflare-provider` reads KV to resolve the version, then streams the matching object from R2. - - - Cloudflare has no built-in signing — no KMS equivalent, and `@wvb/remote-cloudflare` exposes no - signer. If you need [signatures](/docs/config/remote), produce them elsewhere and pass the - resulting `signature` string through your `wvb.config`. - - -## Configure the uploader and deployer - -Call `cloudflareRemote()` and spread the result into the `remote` block. It returns `{ uploader, deployer }`: the uploader writes the bundle to R2, the deployer writes the version to KV. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { cloudflareRemote } from '@wvb/remote-cloudflare'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - bundleName: 'my-app', - ...cloudflareRemote({ - bucket: 'webview-bundle', - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - kvNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID!, - }), - }, -}); -``` - -| Option | Type | Notes | -| --------------- | -------- | ----------------------------------------------------------------------------------------------- | -| `bucket` | `string` | R2 bucket that stores bundle objects. | -| `accountId` | `string` | Cloudflare account ID; sets the R2 endpoint and the KV target. | -| `kvNamespaceId` | `string` | Workers KV namespace that holds deployment pointers. | -| `uploader` | object | Optional overrides for the underlying S3 uploader (`key`, `contentType`, `metadata`, and more). | -| `deployer` | object | Optional overrides for the KV deployer (`key`, `expiration`, `expirationTtl`, `metadata`). | - -Defaults: the uploader sets `region: 'auto'` and endpoint `https://.r2.cloudflarestorage.com`, then stores the object at key `bundles//_.wvb` with `webview-bundle-*` custom metadata. The deployer writes the version into KV under key `` (or `/` for a channel deploy). - -Override either when your layout differs: - -```ts title="wvb.config.ts" -...cloudflareRemote({ - bucket: 'webview-bundle', - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - kvNamespaceId: process.env.CLOUDFLARE_KV_NAMESPACE_ID!, - uploader: { - key: (name, version) => `releases/${name}/${version}.wvb`, - cacheControl: 'public, max-age=31536000, immutable', - }, - deployer: { - key: (name, _version, channel) => (channel ? `${name}/${channel}` : name), - expirationTtl: 60 * 60 * 24, - }, -}), -``` - -R2 needs S3 credentials. Provide them the same way as AWS — through environment variables the S3 client reads: - -```sh -export AWS_ACCESS_KEY_ID= -export AWS_SECRET_ACCESS_KEY= -``` - -With the config in place, publish a bundle: - -```sh -wvb pack ./dist -wvb upload --version 1.2.0 -wvb deploy --version 1.2.0 -``` - -`wvb upload` does not deploy by default. Run `wvb deploy` (or pass `--deploy` to `upload`) to make the version live: - -```sh -wvb upload --version 1.2.0 --deploy -``` - -Deploy to a channel instead of the default pointer: - -```sh -wvb deploy --version 1.2.0 --channel beta -``` - -See the [CLI reference](/docs/guide/cli) for every flag. - -## Serve bundles with a Worker - -The serving side is a Hono app from `@wvb/remote-cloudflare-provider`. Create it with `wvbRemote()` and forward the Worker's bindings into its `Context` — the `KV` binding becomes `kv`, the `BUCKET` (R2) binding becomes `r2`. - -```ts title="src/worker.ts" -import { wvbRemote } from '@wvb/remote-cloudflare-provider'; - -const remote = wvbRemote(); - -export interface Env { - KV: KVNamespace; - BUCKET: R2Bucket; -} - -export default { - fetch(request: Request, env: Env): Response | Promise { - return remote.fetch(request, { kv: env.KV, r2: env.BUCKET }); - }, -}; -``` - -`wvbRemote()` takes one option, `allowOtherVersions` (default `false`). When `false`, `GET /bundles/:name/:version` returns `403`; set it to `true` to let clients download versions other than the deployed one: - -```ts title="src/worker.ts" -const remote = wvbRemote({ allowOtherVersions: true }); -``` - -The Worker resolves the version from KV, reads `bundles//_.wvb` from R2, and returns it with the required `Webview-Bundle-Name` and `Webview-Bundle-Version` headers (plus `Webview-Bundle-Integrity` and `Webview-Bundle-Signature` when present in the object's metadata). - -Bind `KV` and `BUCKET` in `wrangler.jsonc` so the names match the `Context`: - -```json title="wrangler.jsonc" -{ - "name": "webview-bundle", - "main": "src/worker.ts", - "compatibility_date": "2025-01-01", - "kv_namespaces": [{ "binding": "KV", "id": "" }], - "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "webview-bundle" }] -} -``` - -Deploy the Worker: - -```sh -npx wrangler deploy -``` - -Verify the contract against the live endpoint: - -```sh -curl https://bundles.example.com/bundles -# [{ "name": "my-app", "version": "1.2.0" }] - -curl -I https://bundles.example.com/bundles/my-app -# Webview-Bundle-Name: my-app -# Webview-Bundle-Version: 1.2.0 -# Content-Type: application/webview-bundle -``` - -## Provision with Pulumi - -`@wvb/remote-cloudflare-provider-pulumi` provisions the whole stack as one component resource: an R2 bucket, a Workers KV namespace, and a Worker (with a version and a deployment), wired together with the bindings `BUCKET` (R2) and `KV` that the `Context` expects. - -```ts title="index.ts" -import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; - -const provider = new WebviewBundleRemoteProvider('webview-bundle', { - accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, - // Roll the Worker out gradually; defaults to 100. - workerDeploymentPercentage: 25, -}); - -export const bucketName = provider.bucketName; -export const kvNamespaceId = provider.kvNamespaceId; -export const workerId = provider.workerId; -export const workerVersionId = provider.workerVersionId; -export const workerDeploymentId = provider.workerDeploymentId; -``` - -The component registers the Pulumi resource token `webview-bundle:cloudflare:RemoteProvider`. `workerDeploymentPercentage` is the share of traffic the new Worker version receives (default `100`). - -```sh -pulumi up -pulumi stack output kvNamespaceId -``` - -Feed the exported `bucketName` and `kvNamespaceId` into `cloudflareRemote()` above, then publish with the CLI. - -## Next steps - - - - - - - diff --git a/content/docs/guide/providers/local.ko.mdx b/content/docs/guide/providers/local.ko.mdx deleted file mode 100644 index 65e307c..0000000 --- a/content/docs/guide/providers/local.ko.mdx +++ /dev/null @@ -1,242 +0,0 @@ ---- -title: 로컬 프로바이더 -description: 클라우드 프로바이더를 도입하기 전에, 내 컴퓨터에서 파일시스템 기반 원격 서버를 실행해 업로드, 배포, 무선(OTA) 업데이트로 이어지는 전체 흐름을 테스트하세요. ---- - -내 컴퓨터에서 실행되는 파일시스템 기반 원격 서버입니다. 클라우드 프로바이더와 동일한 HTTP 규약을 구현하므로, 인프라 없이도 전체 수명 주기(패킹, 업로드, 배포, 무선(OTA) 업데이트)를 실행할 수 있습니다. 이 서버로 개발한 다음, 프로덕션에서는 [클라우드 프로바이더](/docs/guide/remote)로 전환하세요. - - - 개발과 테스트 전용입니다. 번들을 한 컴퓨터에 일반 파일로 저장하며, `wvb remote local`은 - `localhost`에 바인딩됩니다. 프로덕션 트래픽에는 [AWS](/docs/guide/providers/aws) 또는 - [Cloudflare](/docs/guide/providers/cloudflare) 프로바이더를 사용하세요. - - -## 두 개의 패키지 - -둘 다 `0.0.0` 버전입니다. - -| Package | Role | -| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `@wvb/remote-local` | 게시(publish) 측 클라이언트입니다. `localRemote()`는 `wvb.config`에 연결하는 `uploader`와 `deployer`를 생성합니다. | -| `@wvb/remote-local-provider` | 서버입니다. 저장된 번들을 HTTP로 제공하는 [Hono](https://hono.dev) 앱입니다. CLI가 대신 실행해 줍니다. | - -이 분리 구조는 모든 백엔드에서 동일합니다. 일반 패키지는 번들을 푸시하고, `-provider` 패키지는 번들을 제공합니다. 공유 규약은 [원격 서버 만들기](/docs/guide/remote)를 참고하세요. - -## 게시 측 설정하기 - -`{ uploader, deployer }`를 반환하는 `localRemote()`를 `remote` 블록에 펼쳐 넣으세요. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote(), - }, -}); -``` - -`localRemote()`는 선택적 필드 하나를 받습니다. - -| Option | Type | Default | Description | -| --------- | -------- | -------------- | ---------------------------------------------------- | -| `baseDir` | `string` | `~/.wvb/local` | 번들과 배포 상태를 디스크에 저장하는 디렉터리입니다. | - -`baseDir`을 설정하면 프로젝트의 번들을 기본 저장소와 분리할 수 있습니다. - -```ts title="wvb.config.ts" -import os from 'node:os'; -import path from 'node:path'; -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote({ - baseDir: path.join(os.homedir(), '.wvb', 'my-app'), - }), - }, -}); -``` - -`remote` 블록 전체(`endpoint`, `bundleName`, `packBeforeUpload`, 무결성, 서명)는 [원격 설정 레퍼런스](/docs/config/remote)를 참고하세요. - -## 디스크 레이아웃 - -모든 것은 `{baseDir}/bundles` 아래에 기록되며, 번들 이름마다 디렉터리 하나가 만들어집니다. - -```text -{baseDir}/ -└── bundles/ - └── {name}/ - ├── {name}_{version}.wvb # 번들 바이너리 (업로드된 버전마다 하나) - ├── {name}_{version}.json # 버전별 메타데이터: { integrity?, signature? } - └── deployment.json # 채널별로 어떤 버전이 현재 버전인지 기록 -``` - -번들 `app`의 `1.1.0`을 기본 저장소에 업로드하고 배포한 뒤의 모습입니다. - -```text -~/.wvb/local/ -└── bundles/ - └── app/ - ├── app_1.1.0.wvb - ├── app_1.1.0.json - └── deployment.json -``` - -`deployment.json`은 현재 버전을 기록합니다. 채널 없이 배포하면 기본 `version`을 설정하고, 채널과 함께 배포하면 `channels[channel]`을 설정합니다. 따라서 채널마다 서로 다른 버전을 가리킬 수 있습니다. - -```json title="deployment.json" -{ - "name": "app", - "version": "1.1.0", - "channels": { - "beta": "1.2.0" - } -} -``` - -## 서버 실행하기 - -`@wvb/remote-local-provider`는 Hono 앱을 빌드하기만 하고 포트에 바인딩하지는 않습니다. `wvb remote local`이 그 앱을 Node HTTP 서버로 제공합니다. - -```sh -wvb remote local -``` - -기본적으로 `~/.wvb/local`을 `http://localhost:4313`에서 제공합니다. 플래그는 다음과 같습니다. - -| Flag | Default | Description | -| ------------------------ | -------------- | ------------------------------------------------------------------------- | -| `--base-dir` | `~/.wvb/local` | 제공할 디렉터리입니다. `localRemote()`에 설정한 `baseDir`과 일치시키세요. | -| `--port`, `-P` | `4313` | 수신 대기할 포트입니다(`PORT`도 읽습니다). | -| `--hostname`, `-H` | `localhost` | 바인딩할 호스트입니다(`HOSTNAME`도 읽습니다). | -| `--allow-other-versions` | `false` | 배포된 버전 외의 버전 다운로드를 허용합니다. | -| `--silent` | `false` | 서버 로깅을 억제합니다. | - -설정에 지정한 것과 같은 경로를 전달하세요. - -```sh -wvb remote local --base-dir ~/.wvb/my-app --port 4313 -``` - -`remote` 명령어 그룹 전체(`remote list`, 별칭 `remote ls`)는 [CLI 레퍼런스](/docs/guide/cli)를 참고하세요. - -### allowOtherVersions - -기본적으로 서버는 배포된 버전만 제공합니다. `GET /bundles/{name}/{version}`은 `403 Forbidden`을 반환하는데, 이는 클라우드 프로바이더와 동일하며 클라이언트를 특정 버전에 고정해 둡니다. - -```sh -curl -i http://localhost:4313/bundles/app/1.0.0 -# HTTP/1.1 403 Forbidden -``` - -이 옵션을 켜면 버전별 경로가 열립니다. 롤백을 테스트하거나 이전 번들을 살펴볼 때 유용합니다. - -```sh -wvb remote local --allow-other-versions -``` - -```sh -curl -i http://localhost:4313/bundles/app/1.0.0 -# HTTP/1.1 200 OK -# Webview-Bundle-Name: app -# Webview-Bundle-Version: 1.0.0 -``` - -프로바이더 앱을 직접 빌드할 때는 `allowOtherVersions: true`를 전달하세요. - -```ts title="server.ts" -import { wvbRemote } from '@wvb/remote-local-provider'; - -const app = wvbRemote({ - baseDir: '~/.wvb/local', - allowOtherVersions: true, -}); -``` - - - `allowOtherVersions`를 비활성화하면 배포된 버전만 `GET /bundles/{name}`으로 접근할 수 있습니다. - 버전별 경로는 이 옵션을 켜기 전까지 `403` 뒤에 숨겨진 상태로 유지됩니다. - - -## 전체 흐름 따라가기 - -로컬 서버를 상대로 OTA 업데이트를 처음부터 끝까지 테스트해 봅니다. - - - `wvb upload`는 기본적으로 배포하지 않습니다. `--deploy`를 전달하지 않으면 `false`입니다. `wvb - upload --deploy`로 업로드와 현재 버전 지정을 한 번에 하거나, `wvb deploy`를 따로 실행하세요. - - -위에서 보여준 대로 `localRemote()`를 연결한 다음, 패킹하고 업로드하고 배포하세요. `packBeforeUpload`가 기본값이면 `wvb upload`가 대신 패킹하므로, 명시적인 `wvb pack`은 선택 사항입니다. - -```sh -wvb pack ./dist -wvb upload --version 1.1.0 --deploy -``` - -같은 저장소를 상대로 서버를 시작하고, 번들이 목록에 나오는지 확인하세요. - -```sh -wvb remote local -``` - -```sh -curl http://localhost:4313/bundles -# [{"name":"app","version":"1.1.0"}] -``` - -```sh -curl -I http://localhost:4313/bundles/app -# Webview-Bundle-Name: app -# Webview-Bundle-Version: 1.1.0 -# Content-Type: application/webview-bundle -``` - -앱의 업데이터가 `http://localhost:4313`을 가리키게 하세요. 업데이터는 `GET /bundles/{name}`을 호출하고, 번들을 검증한 뒤, `remote` 소스에 설치합니다. 원격 대상은 플랫폼 통합에서 설정하세요. [Electron](/docs/guide/platforms/electron), [Tauri](/docs/guide/platforms/tauri), [Android](/docs/guide/platforms/android), [iOS](/docs/guide/platforms/ios) 가이드를 참고하세요. - -버전을 올리고 다시 배포하면 업데이트가 게시됩니다. 다음 업데이터 확인 시점에 이를 가져갑니다. - -```sh -wvb upload --version 1.2.0 --deploy -``` - -`--channel`을 전달하면 기본값 대신 채널로 배포합니다. - -```sh -wvb deploy --version 1.2.0 --channel beta -curl 'http://localhost:4313/bundles?channel=beta' -# [{"name":"app","version":"1.2.0"}] -``` - -## 다음으로 볼 내용 - - - - - - - diff --git a/content/docs/guide/providers/local.mdx b/content/docs/guide/providers/local.mdx deleted file mode 100644 index 855f22c..0000000 --- a/content/docs/guide/providers/local.mdx +++ /dev/null @@ -1,242 +0,0 @@ ---- -title: Local provider -description: Run a filesystem-backed remote on your own machine to test the full upload, deploy, and over-the-air update loop before reaching for a cloud provider. ---- - -A filesystem-backed remote that runs on your own machine. It implements the same HTTP contract as the cloud providers, so you can exercise the full lifecycle — pack, upload, deploy, and over-the-air (OTA) update — with no infrastructure. Develop against it, then switch to a [cloud provider](/docs/guide/remote) for production. - - - Development and testing only. It stores bundles as plain files on one machine and `wvb remote - local` binds to `localhost`. For production traffic, use the [AWS](/docs/guide/providers/aws) or - [Cloudflare](/docs/guide/providers/cloudflare) providers. - - -## Two packages - -Both at `0.0.0`: - -| Package | Role | -| ---------------------------- | --------------------------------------------------------------------------------------------------------- | -| `@wvb/remote-local` | Publish-side client. `localRemote()` produces the `uploader` and `deployer` you wire into `wvb.config`. | -| `@wvb/remote-local-provider` | The server. A [Hono](https://hono.dev) app that serves stored bundles over HTTP. The CLI runs it for you. | - -This split mirrors every backend: the plain package pushes bundles, the `-provider` package serves them. See [Building a remote](/docs/guide/remote) for the shared contract. - -## Configure the publish side - -Spread `localRemote()` — which returns `{ uploader, deployer }` — into the `remote` block: - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote(), - }, -}); -``` - -`localRemote()` accepts one optional field: - -| Option | Type | Default | Description | -| --------- | -------- | -------------- | ---------------------------------------------------------------- | -| `baseDir` | `string` | `~/.wvb/local` | Directory where bundles and deployment state are stored on disk. | - -Set `baseDir` to isolate a project's bundles from the default store: - -```ts title="wvb.config.ts" -import os from 'node:os'; -import path from 'node:path'; -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote({ - baseDir: path.join(os.homedir(), '.wvb', 'my-app'), - }), - }, -}); -``` - -For the full `remote` block — `endpoint`, `bundleName`, `packBeforeUpload`, integrity, and signature — see the [remote config reference](/docs/config/remote). - -## On-disk layout - -Everything is written under `{baseDir}/bundles`, one directory per bundle name: - -```text -{baseDir}/ -└── bundles/ - └── {name}/ - ├── {name}_{version}.wvb # bundle binary (one per uploaded version) - ├── {name}_{version}.json # per-version metadata: { integrity?, signature? } - └── deployment.json # which version is current, per channel -``` - -After uploading and deploying `1.1.0` of bundle `app` to the default store: - -```text -~/.wvb/local/ -└── bundles/ - └── app/ - ├── app_1.1.0.wvb - ├── app_1.1.0.json - └── deployment.json -``` - -`deployment.json` records the current version. Deploying without a channel sets the default `version`; with a channel it sets `channels[channel]`, so channels can point at different versions: - -```json title="deployment.json" -{ - "name": "app", - "version": "1.1.0", - "channels": { - "beta": "1.2.0" - } -} -``` - -## Run the server - -`@wvb/remote-local-provider` only builds the Hono app — it does not bind a port. `wvb remote local` serves that app over a Node HTTP server: - -```sh -wvb remote local -``` - -Defaults to serving `~/.wvb/local` on `http://localhost:4313`. Flags: - -| Flag | Default | Description | -| ------------------------ | -------------- | --------------------------------------------------------------- | -| `--base-dir` | `~/.wvb/local` | Directory to serve. Match the `baseDir` set in `localRemote()`. | -| `--port`, `-P` | `4313` | Port to listen on (also reads `PORT`). | -| `--hostname`, `-H` | `localhost` | Host to bind (also reads `HOSTNAME`). | -| `--allow-other-versions` | `false` | Allow downloading versions other than the deployed one. | -| `--silent` | `false` | Suppress server logging. | - -Pass the same path you set in your config: - -```sh -wvb remote local --base-dir ~/.wvb/my-app --port 4313 -``` - -See the [CLI reference](/docs/guide/cli) for the full `remote` command group (`remote list`, alias `remote ls`). - -### allowOtherVersions - -By default the server serves only the deployed version. `GET /bundles/{name}/{version}` returns `403 Forbidden`, matching the cloud providers and keeping clients pinned: - -```sh -curl -i http://localhost:4313/bundles/app/1.0.0 -# HTTP/1.1 403 Forbidden -``` - -Opt in to unlock the version-specific route — useful for testing rollbacks or inspecting an older bundle: - -```sh -wvb remote local --allow-other-versions -``` - -```sh -curl -i http://localhost:4313/bundles/app/1.0.0 -# HTTP/1.1 200 OK -# Webview-Bundle-Name: app -# Webview-Bundle-Version: 1.0.0 -``` - -When building the provider app directly, pass `allowOtherVersions: true`: - -```ts title="server.ts" -import { wvbRemote } from '@wvb/remote-local-provider'; - -const app = wvbRemote({ - baseDir: '~/.wvb/local', - allowOtherVersions: true, -}); -``` - - - With `allowOtherVersions` disabled, only the deployed version is reachable via `GET /bundles/ - {name}`. The version-specific route stays behind `403` until you opt in. - - -## Walk the full loop - -Test an OTA update against a local server end to end. - - - `wvb upload` does not deploy by default — `--deploy` is `false` unless passed. Use `wvb upload - --deploy` to upload and mark current in one step, or run `wvb deploy` separately. - - -Wire up `localRemote()` as shown above, then pack, upload, and deploy. With `packBeforeUpload` at its default, `wvb upload` packs for you, so the explicit `wvb pack` is optional: - -```sh -wvb pack ./dist -wvb upload --version 1.1.0 --deploy -``` - -Start the server against the same store and confirm the bundle is listed: - -```sh -wvb remote local -``` - -```sh -curl http://localhost:4313/bundles -# [{"name":"app","version":"1.1.0"}] -``` - -```sh -curl -I http://localhost:4313/bundles/app -# Webview-Bundle-Name: app -# Webview-Bundle-Version: 1.1.0 -# Content-Type: application/webview-bundle -``` - -Point your app's updater at `http://localhost:4313`. It calls `GET /bundles/{name}`, verifies the bundle, and installs it into the `remote` source. Configure the remote target in your platform integration — see the [Electron](/docs/guide/platforms/electron), [Tauri](/docs/guide/platforms/tauri), [Android](/docs/guide/platforms/android), and [iOS](/docs/guide/platforms/ios) guides. - -Publish an update by bumping the version and deploying again; the next updater check picks it up: - -```sh -wvb upload --version 1.2.0 --deploy -``` - -Deploy to a channel instead of the default by passing `--channel`: - -```sh -wvb deploy --version 1.2.0 --channel beta -curl 'http://localhost:4313/bundles?channel=beta' -# [{"name":"app","version":"1.2.0"}] -``` - -## Where to go next - - - - - - - diff --git a/content/docs/guide/providers/meta.json b/content/docs/guide/providers/meta.json deleted file mode 100644 index f9ea627..0000000 --- a/content/docs/guide/providers/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Providers", - "pages": ["local", "aws", "cloudflare"] -} diff --git a/content/docs/guide/providers/meta.ko.json b/content/docs/guide/providers/meta.ko.json deleted file mode 100644 index 087d8ac..0000000 --- a/content/docs/guide/providers/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "프로바이더", - "pages": ["local", "aws", "cloudflare"] -} diff --git a/content/docs/guide/remote-bundles.ko.mdx b/content/docs/guide/remote-bundles.ko.mdx deleted file mode 100644 index b7a4381..0000000 --- a/content/docs/guide/remote-bundles.ko.mdx +++ /dev/null @@ -1,259 +0,0 @@ ---- -title: 원격 번들과 업데이트 -description: 무선(OTA) 업데이트가 동작하는 방식 — 업데이트 라이프사이클, 업데이터 모델, HTTP 규약, 그리고 무결성과 서명이 각 번들을 보호하는 원리. ---- - -Webview Bundle은 새 번들을 무선(OTA)으로 다운로드합니다. 따라서 네이티브 앱스토어 배포 없이도 업데이트된 앱 코드를 배포할 수 있습니다. 이 페이지에서는 업데이트 라이프사이클, 언어에 종속되지 않는 업데이터 모델, 모든 원격 서버가 구현하는 HTTP 규약, 그리고 무결성과 서명이 각 번들의 신뢰성을 지키는 방법을 다룹니다. 서버를 구축하려면 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. 언어별 업데이터 API는 [Node](/docs/references/node)와 [Deno](/docs/references/deno) 레퍼런스를 참고하세요. - -## 업데이트 라이프사이클 - -번들은 빌드 산출물에서 기기까지 다섯 단계를 거칩니다. 앞의 세 단계는 사용자의 머신이나 CI에서 실행되고, 마지막 두 단계는 업데이터가 앱 내부에서 구동합니다. - -![개발자 머신이 번들을 원격 서버로 업로드하고 배포하면, 기기가 업데이트를 확인하고 다운로드하고 검증한 뒤 설치합니다.](https://static.wvb.dev/diagrams/update-lifecycle.svg) - -1. 빌드 산출물을 `.wvb` 아카이브로 **패킹**합니다. -2. 해당 아카이브를 **업로드**합니다. 무결성 해시와 서명을 함께 첨부할 수도 있습니다. -3. 업로드한 버전을 **배포**하면 해당 채널의 클라이언트가 이를 보기 시작합니다. -4. 기기에서 업데이터가 새 버전을 **확인**한 뒤 **다운로드**합니다. -5. 업데이터가 다운로드를 **검증**하고 이를 `remote` 소스에 **설치**합니다. - -1~3단계는 CLI 명령어입니다. - -```sh -wvb pack # 빌드 산출물 -> .wvb/ -wvb upload --version 1.2.0 # 서버로 푸시 (배포하지 않음) -wvb deploy --version 1.2.0 # 1.2.0을 현재 버전으로 지정 -``` - -`wvb upload --deploy`(기본값 `false`)는 업로드와 배포를 한 번에 처리합니다. 모든 플래그는 [CLI 레퍼런스](/docs/guide/cli)를 참고하세요. 4~5단계는 업데이터이며, 다음에서 다룹니다. - -## 업데이터 모델 - -업데이터는 [번들 소스](/docs/guide/bundle-sources)를 원격 엔드포인트에 연결하고 세 가지 작업을 제공합니다. 이 작업들은 분리되어 있어, 기기가 언제 바이트를 가져올지와 언제 버전을 전환할지를 정확히 제어할 수 있습니다. - -- **`getUpdate`** — 확인만 합니다. 서버에 현재 배포된 버전을 요청하고 이를 로컬 버전과 비교합니다. 로컬 버전이 없거나 로컬 버전이 배포된 버전과 다르면 `isAvailable`을 `true`로 보고합니다. 아무것도 다운로드하지 않습니다. -- **`download`** — 번들을 가져와 무결성 및 서명 정책에 따라 검증하고 `remote` 소스에 스테이징합니다. 새 버전을 활성화하지는 **않습니다**. -- **`install`** — 스테이징된 번들을 디스크에서 다시 검증하고, 현재 버전으로 활성화하고, 이전 디스크립터를 언로드하고, 더 이상 유지하지 않는 버전을 정리합니다. - -이렇게 분리하면 백그라운드에서 다운로드하고 다음 앱 실행 시 설치할 수 있습니다. - - - `builtin` 소스 — 앱 내부에 포함되어 배포되는 번들 — 는 항상 폴백으로 남아 있습니다. 버전이 - 설치되면 `remote` 소스가 우선하므로, 다운로드된 업데이트가 builtin 번들보다 우선합니다. 원격 - 버전이 없으면 앱은 builtin으로 폴백합니다. [번들 소스](/docs/guide/bundle-sources)를 참고하세요. - - -Rust 코어 흐름입니다. 플랫폼 래퍼는 이를 일대일로 그대로 따릅니다. - -```rust -use std::sync::Arc; -use wvb::updater::Updater; - -let updater = Updater::new(source, remote, None); // None -> UpdaterConfig::default() - -let update = updater.get_update("app").await?; -if update.is_available { - // download는 가져오고, 검증하고, 스테이징하지만 활성화하지는 않음 - let info = updater.download("app", None).await?; - // install은 다시 검증하고, 활성화하고, 이전 버전을 정리함 - updater.install("app", info.version).await?; - // 새 버전을 제공하도록 웹뷰를 리로드 -} -``` - -`get_update`는 `BundleUpdateInfo`를 반환합니다. - -```rust -pub struct BundleUpdateInfo { - pub name: String, - pub version: String, - pub local_version: Option, - pub is_available: bool, - pub etag: Option, - pub integrity: Option, - pub signature: Option, - pub last_modified: Option, -} -``` - -`download`에 `Some(version)`을 전달하면 현재 버전 대신 특정 버전을 가져옵니다. - -```rust -// 현재 배포된 버전을 가져옴 -let info = updater.download("app", None).await?; -// 정확한 버전을 가져옴 (GET /bundles/{name}/{version}) -let info = updater.download("app", Some("1.1.0".into())).await?; -``` - -플랫폼 가이드에는 각 대상에 맞는 관용적인 호출 방법이 나와 있습니다. -[Electron](/docs/guide/platforms/electron), [Tauri](/docs/guide/platforms/tauri), -[Android](/docs/guide/platforms/android), [iOS](/docs/guide/platforms/ios), -[Deno Desktop](/docs/guide/platforms/deno)을 참고하세요. - -## 원격 HTTP 규약 - -Webview Bundle 서버는 네 개의 엔드포인트를 구현합니다. 프로바이더 패키지(local, AWS, Cloudflare)가 이 명세를 구현하며, 이에 맞춰 직접 만들 수도 있습니다. - -| 메서드와 경로 | 목적 | -| ------------------------------- | ------------------------------------------- | -| `GET /bundles` | 배포된 번들 목록: `[{ "name", "version" }]` | -| `HEAD /bundles/{name}` | 현재 버전의 메타데이터 (헤더만, 본문 없음) | -| `GET /bundles/{name}` | 현재 버전 다운로드 | -| `GET /bundles/{name}/{version}` | 특정 버전 다운로드 | - -`GET /bundles`, `HEAD /bundles/{name}`, `GET /bundles/{name}`은 선택적인 `?channel=` 쿼리를 받습니다. `GET /bundles/{name}/{version}`은 받지 않습니다. 메타데이터는 응답 헤더로 전달됩니다. - -| 헤더 | 필수 여부 | 의미 | -| -------------------------- | --------- | ------------- | -| `Webview-Bundle-Name` | 예 | 번들 이름 | -| `Webview-Bundle-Version` | 예 | 버전 | -| `Webview-Bundle-Integrity` | 아니오 | 무결성 문자열 | -| `Webview-Bundle-Signature` | 아니오 | 서명 | -| `ETag`, `Last-Modified` | 아니오 | 표준 검증자 | - -다운로드 응답은 다음과 같습니다. - -```sh -$ curl -sI http://localhost:4313/bundles/app -HTTP/1.1 200 OK -Content-Type: application/webview-bundle -Webview-Bundle-Name: app -Webview-Bundle-Version: 1.2.0 -Webview-Bundle-Integrity: sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -ETag: "abc123" -``` - -목록 엔드포인트는 `application/json`을 반환합니다. - -```json -[ - { "name": "app", "version": "1.2.0" }, - { "name": "admin", "version": "0.4.1" } -] -``` - -번들이나 버전이 없으면 `404`를 반환합니다. 서버가 허용하지 않는 경우, 배포되지 않은 특정 버전을 요청하면 `403`을 반환합니다. 필수 헤더가 없거나 2xx가 아닌 응답이 오면 클라이언트는 다운로드를 거부합니다. 기본 요청 타임아웃은 120초입니다. 실제 서버 예제와 로컬 테스트는 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. - -## 무결성 - -무결성 해시를 사용하면 기기가 다운로드한 바이트가 게시한 바이트와 정확히 일치하는지 확인할 수 있습니다. Webview Bundle은 **SHA-2**를 사용합니다. `sha256`(기본값), `sha384`, `sha512`를 지원하며, `":"` 형식으로 직렬화됩니다. - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -``` - -게시자는 업로드 시점에 이 문자열을 첨부하고([원격 설정](/docs/config/remote)), 서버는 이를 `Webview-Bundle-Integrity`로 반환하며, 업데이터는 다운로드 시 **무결성 정책**에 따라 이를 적용합니다. - -| 정책 | 동작 | -| ---------- | ------------------------------------------------------------- | -| `Strict` | 무결성 값이 반드시 존재하고 일치해야 하며, 그렇지 않으면 거부 | -| `Optional` | **기본값** — 존재하면 검증하고, 첨부되지 않았으면 허용 | -| `None` | 무결성 검사를 완전히 건너뜀 | - -```rust -use wvb::integrity::IntegrityPolicy; -use wvb::updater::UpdaterConfig; - -let config = UpdaterConfig::new() - .integrity_policy(IntegrityPolicy::Strict); -``` - -## 서명 - -무결성이 바이트가 손상되지 않았음을 증명한다면, 서명은 _누가_ 게시했는지를 증명합니다. 서명은 원본 번들이 아니라 **무결성 문자열 바이트**(`":"`)를 대상으로 합니다. 따라서 서명 검증에는 무결성 값이 반드시 존재해야 합니다. 공개 키를 제공하면, 업데이터는 서명이 검증되지 않는 모든 다운로드를 거부합니다. - -검증 알고리즘은 다음과 같습니다. - -- **ECDSA** — 곡선 P-256(secp256r1) 또는 P-384(secp384r1) -- **Ed25519** -- **RSA** — PKCS#1 v1.5 또는 PSS 패딩, SHA-256으로 해싱 - -공개 키 형식은 다음과 같습니다. - -| 형식 | 로더 | 적용 대상 | -| ---------------- | ------------------------- | ------------ | -| SPKI PEM | `from_public_key_pem` | 전체 | -| SPKI DER | `from_public_key_der` | 전체 | -| PKCS#1 PEM/DER | `from_pkcs1_pem` / `_der` | RSA 전용 | -| SEC1 point bytes | `from_sec1_bytes` | ECDSA 전용 | -| Raw 32-byte key | `from_public_key_bytes` | Ed25519 전용 | - -일치하는 무결성 해시와 유효한 Ed25519 서명을 모두 요구하는 업데이터입니다. - -```rust -use wvb::integrity::IntegrityPolicy; -use wvb::signature::{Ed25519Verifier, SignatureVerifier}; -use wvb::updater::{Updater, UpdaterConfig}; -use std::sync::Arc; - -let verifier = SignatureVerifier::Ed25519(Arc::new( - Ed25519Verifier::from_public_key_pem(PUBLIC_KEY_PEM)?, -)); -let config = UpdaterConfig::new() - .integrity_policy(IntegrityPolicy::Strict) - .signature_verifier(verifier); -let updater = Updater::new(source, remote, Some(config)); -``` - -SPKI PEM에서 로드한 ECDSA P-256입니다. - -```rust -use wvb::signature::{EcdsaSecp256r1Verifier, SignatureVerifier}; -use std::sync::Arc; - -let verifier = SignatureVerifier::EcdsaSecp256r1(Arc::new( - EcdsaSecp256r1Verifier::from_public_key_pem(PUBLIC_KEY_PEM)?, -)); -``` - -게시 측 대응 항목 — 어떤 알고리즘으로 서명할지와 개인 키 형식 — 은 [원격 설정](/docs/config/remote)에서 지정합니다. - -## 채널 - -채널은 단계적 롤아웃을 위해 서로 다른 버전을 서로 다른 대상에게 라우팅합니다. 채널에 배포하면 해당 채널을 요청하는 클라이언트만 이를 수신합니다. 채널이 없는 클라이언트는 기본 배포를 받습니다. - -```sh -wvb upload --version 1.2.0 --deploy --channel beta # beta 클라이언트만 1.2.0을 봄 -wvb deploy --version 1.2.0 # 나중에 기본 채널로 승격 -``` - -기기에서 업데이터는 설정에 채널을 담아 `?channel=`로 전달합니다. - -```rust -let config = UpdaterConfig::new().channel("beta"); -let updater = Updater::new(source, remote, Some(config)); -// 이제 get_update / download는 ?channel=beta를 요청함 -``` - -채널은 자유 형식 문자열입니다. 하드코딩된 기본 이름은 없으며, 채널이 설정되지 않으면 쿼리 파라미터가 생략됩니다. - -## 다음으로 볼 내용 - - - - - - - - - diff --git a/content/docs/guide/remote-bundles.mdx b/content/docs/guide/remote-bundles.mdx deleted file mode 100644 index be7b4bb..0000000 --- a/content/docs/guide/remote-bundles.mdx +++ /dev/null @@ -1,260 +0,0 @@ ---- -title: Remote bundles & updates -description: How over-the-air updating works — the lifecycle, the updater model, the HTTP contract, and how integrity and signatures protect each bundle. ---- - -Webview Bundle downloads newer bundles over the air (OTA), so you ship updated app code without a native app-store release. This page covers the update lifecycle, the language-neutral updater model, the HTTP contract every remote server implements, and how integrity and signatures keep each bundle trustworthy. To stand up a server, see [Building a remote](/docs/guide/remote); for per-language updater APIs, see the [Node](/docs/references/node) and [Deno](/docs/references/deno) references. - -## The update lifecycle - -A bundle travels from build output to device in five stages. The first three run on your machine or CI; the last two run inside the app, driven by the updater. - -![The developer machine uploads and deploys a bundle to the remote server; the device checks, downloads, verifies, and installs the update.](https://static.wvb.dev/diagrams/update-lifecycle.svg) - -1. **Pack** your build into a `.wvb` archive. -2. **Upload** that archive, optionally attaching an integrity hash and a signature. -3. **Deploy** the uploaded version so clients on a channel start seeing it. -4. On the device, the updater **checks** for a newer version, then **downloads** it. -5. The updater **verifies** the download and **installs** it into the `remote` source. - -Stages 1–3 are CLI commands: - -```sh -wvb pack # build output -> .wvb/ -wvb upload --version 1.2.0 # push to the server (does not deploy) -wvb deploy --version 1.2.0 # make 1.2.0 the current version -``` - -`wvb upload --deploy` (default `false`) uploads and deploys in one step. See the [CLI reference](/docs/guide/cli) for every flag. Stages 4–5 are the updater, covered next. - -## The updater model - -The updater ties a [bundle source](/docs/guide/bundle-sources) to a remote endpoint and exposes three operations, kept separate so you control exactly when a device fetches bytes and when it switches versions. - -- **`getUpdate`** — a check only. Asks the server for the current deployed version and compares it to the local version. Reports `isAvailable` as `true` when there is no local version, or when the local version differs from the deployed version. Downloads nothing. -- **`download`** — fetches the bundle, verifies it against your integrity and signature policy, and stages it into the `remote` source. Does **not** activate the new version. -- **`install`** — re-verifies the staged bundle from disk, activates it as the current version, unloads the old descriptor, and prunes versions no longer retained. - -This split lets you download in the background and install on the next app launch. - - - The `builtin` source — the bundle shipped inside the app — always remains as a fallback. The - `remote` source takes priority once a version is installed, so a downloaded update wins over the - builtin bundle; if no remote version exists, the app falls back to builtin. See [Bundle - sources](/docs/guide/bundle-sources). - - -The Rust core flow; platform wrappers mirror it one-to-one: - -```rust -use std::sync::Arc; -use wvb::updater::Updater; - -let updater = Updater::new(source, remote, None); // None -> UpdaterConfig::default() - -let update = updater.get_update("app").await?; -if update.is_available { - // download fetches, verifies, and stages — but does not activate - let info = updater.download("app", None).await?; - // install re-verifies, activates, and prunes old versions - updater.install("app", info.version).await?; - // reload the webview to serve the new version -} -``` - -`get_update` returns a `BundleUpdateInfo`: - -```rust -pub struct BundleUpdateInfo { - pub name: String, - pub version: String, - pub local_version: Option, - pub is_available: bool, - pub etag: Option, - pub integrity: Option, - pub signature: Option, - pub last_modified: Option, -} -``` - -Pass `Some(version)` to `download` to fetch a specific version instead of the current one: - -```rust -// fetch current deployed version -let info = updater.download("app", None).await?; -// fetch an exact version (GET /bundles/{name}/{version}) -let info = updater.download("app", Some("1.1.0".into())).await?; -``` - -The platform guides show the idiomatic call for each target: -[Electron](/docs/guide/platforms/electron), [Tauri](/docs/guide/platforms/tauri), -[Android](/docs/guide/platforms/android), [iOS](/docs/guide/platforms/ios), and -[Deno Desktop](/docs/guide/platforms/deno). - -## The remote HTTP contract - -A Webview Bundle server implements four endpoints. The provider packages (local, AWS, Cloudflare) implement this spec, and you can build your own to match. - -| Method and path | Purpose | -| ------------------------------- | -------------------------------------------------- | -| `GET /bundles` | List deployed bundles: `[{ "name", "version" }]` | -| `HEAD /bundles/{name}` | Current version's metadata (headers only, no body) | -| `GET /bundles/{name}` | Download the current version | -| `GET /bundles/{name}/{version}` | Download a specific version | - -`GET /bundles`, `HEAD /bundles/{name}`, and `GET /bundles/{name}` accept an optional `?channel=` query. `GET /bundles/{name}/{version}` does not. Metadata travels in response headers: - -| Header | Required | Meaning | -| -------------------------- | -------- | ------------------- | -| `Webview-Bundle-Name` | yes | bundle name | -| `Webview-Bundle-Version` | yes | version | -| `Webview-Bundle-Integrity` | no | integrity string | -| `Webview-Bundle-Signature` | no | signature | -| `ETag`, `Last-Modified` | no | standard validators | - -A download response looks like: - -```sh -$ curl -sI http://localhost:4313/bundles/app -HTTP/1.1 200 OK -Content-Type: application/webview-bundle -Webview-Bundle-Name: app -Webview-Bundle-Version: 1.2.0 -Webview-Bundle-Integrity: sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -ETag: "abc123" -``` - -The list endpoint returns `application/json`: - -```json -[ - { "name": "app", "version": "1.2.0" }, - { "name": "admin", "version": "0.4.1" } -] -``` - -A missing bundle or version returns `404`; requesting a specific non-deployed version returns `403` when the server disallows it. Missing required headers, or any non-2xx response, cause the client to reject the download. The default request timeout is 120s. For a worked server and local testing, see [Building a remote](/docs/guide/remote). - -## Integrity - -An integrity hash lets a device confirm the downloaded bytes are exactly the bytes you published. Webview Bundle uses **SHA-2**: `sha256` (the default), `sha384`, or `sha512`, serialized as `":"`: - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -``` - -The publisher attaches this string at upload time ([Remote config](/docs/config/remote)), the server returns it in `Webview-Bundle-Integrity`, and the updater enforces it on download per an **integrity policy**: - -| Policy | Behavior | -| ---------- | ------------------------------------------------------------ | -| `Strict` | integrity value must be present and must match, or reject | -| `Optional` | **default** — verify if present; allow when none is attached | -| `None` | skip integrity checking entirely | - -```rust -use wvb::integrity::IntegrityPolicy; -use wvb::updater::UpdaterConfig; - -let config = UpdaterConfig::new() - .integrity_policy(IntegrityPolicy::Strict); -``` - -## Signatures - -Where integrity proves the bytes are intact, a signature proves _who_ published them. The signature covers the **integrity string bytes** (`":"`), not the raw bundle — so signature verification requires an integrity value to be present. Supply a public key, and the updater rejects any download whose signature does not verify. - -Verify algorithms: - -- **ECDSA** with curve P-256 (secp256r1) or P-384 (secp384r1) -- **Ed25519** -- **RSA** with PKCS#1 v1.5 or PSS padding, hashing with SHA-256 - -Public-key formats: - -| Format | Loader | Applies to | -| ---------------- | ------------------------- | ------------ | -| SPKI PEM | `from_public_key_pem` | all | -| SPKI DER | `from_public_key_der` | all | -| PKCS#1 PEM/DER | `from_pkcs1_pem` / `_der` | RSA only | -| SEC1 point bytes | `from_sec1_bytes` | ECDSA only | -| Raw 32-byte key | `from_public_key_bytes` | Ed25519 only | - -An updater requiring both a matching integrity hash and a valid Ed25519 signature: - -```rust -use wvb::integrity::IntegrityPolicy; -use wvb::signature::{Ed25519Verifier, SignatureVerifier}; -use wvb::updater::{Updater, UpdaterConfig}; -use std::sync::Arc; - -let verifier = SignatureVerifier::Ed25519(Arc::new( - Ed25519Verifier::from_public_key_pem(PUBLIC_KEY_PEM)?, -)); -let config = UpdaterConfig::new() - .integrity_policy(IntegrityPolicy::Strict) - .signature_verifier(verifier); -let updater = Updater::new(source, remote, Some(config)); -``` - -ECDSA P-256 from an SPKI PEM: - -```rust -use wvb::signature::{EcdsaSecp256r1Verifier, SignatureVerifier}; -use std::sync::Arc; - -let verifier = SignatureVerifier::EcdsaSecp256r1(Arc::new( - EcdsaSecp256r1Verifier::from_public_key_pem(PUBLIC_KEY_PEM)?, -)); -``` - -The publish-side counterpart — which algorithm signs, and the private-key format — is set in [Remote config](/docs/config/remote). - -## Channels - -A channel routes different versions to different audiences for staged rollouts. Deploy to a channel, and only clients requesting that channel receive it; clients with no channel get the default deployment. - -```sh -wvb upload --version 1.2.0 --deploy --channel beta # only beta clients see 1.2.0 -wvb deploy --version 1.2.0 # later, promote to the default channel -``` - -On the device, the updater carries the channel in its config and forwards it as `?channel=`: - -```rust -let config = UpdaterConfig::new().channel("beta"); -let updater = Updater::new(source, remote, Some(config)); -// get_update / download now request ?channel=beta -``` - -A channel is a free-form string; there is no hardcoded default name — when no channel is set, the query parameter is omitted. - -## Where to go next - - - - - - - - - diff --git a/content/docs/guide/remote.ko.mdx b/content/docs/guide/remote.ko.mdx deleted file mode 100644 index 3e356d3..0000000 --- a/content/docs/guide/remote.ko.mdx +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: 원격 서버 구축하기 -description: 무선(OTA) 번들 전송을 위한 업데이트 서버를 세우고, 전체 흐름을 로컬에서 실행한 뒤 프로바이더를 선택하세요. ---- - -원격 서버는 작고 고정된 규약에 따라 앱의 업데이터에 번들을 제공하는 HTTP 서버입니다. [원격 번들](/docs/guide/remote-bundles)의 서버 측 짝에 해당합니다. 원격 서버를 만들고, 클라우드 계정 없이 로컬에서 배포와 업데이트 전체 흐름을 실행한 다음, 프로덕션용 프로바이더를 고르세요. - -Webview Bundle은 `local`, `aws`, `cloudflare` 세 가지 백엔드용 원격 서버를 제공합니다. 각 백엔드는 역할에 따라 세 개의 패키지로 나뉩니다. 작업에 필요한 것만 사용하세요. - -## 패키지 역할 - -모든 백엔드는 최대 세 개의 패키지를 가집니다. - -| 역할 | 패키지 | 하는 일 | -| --------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| 설정 클라이언트 | `@wvb/remote-` | `{ uploader, deployer }`를 만드는 `wvb.config` 플러그인입니다. 퍼블리셔의 머신이나 CI에서 실행되어 번들을 푸시하고 배포합니다. | -| HTTP 서버 | `@wvb/remote--provider` | HTTP 규약을 구현하고 클라이언트에 번들을 제공하는 서버입니다. | -| Pulumi IaC | `@wvb/remote--provider-pulumi` | 클라우드 리소스를 프로비저닝하고 프로바이더를 배포하는 코드형 인프라입니다. | - -구체적으로, 백엔드별로 정리하면 다음과 같습니다. - -| 백엔드 | 설정 클라이언트 | HTTP 서버 | Pulumi IaC | -| ------------ | ------------------------ | --------------------------------- | ---------------------------------------- | -| `local` | `@wvb/remote-local` | `@wvb/remote-local-provider` | — | -| `aws` | `@wvb/remote-aws` | `@wvb/remote-aws-provider` | `@wvb/remote-aws-provider-pulumi` | -| `cloudflare` | `@wvb/remote-cloudflare` | `@wvb/remote-cloudflare-provider` | `@wvb/remote-cloudflare-provider-pulumi` | - -설정 클라이언트는 `wvb.config.ts`의 `remote` 블록에 연결하는 대상입니다. `uploader`는 패킹된 `.wvb`를 스토리지에 쓰고, `deployer`는 클라이언트가 받아야 할 버전을 지정합니다. 그 결과를 `remote`에 펼쳐 넣으세요. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { awsRemote } from '@wvb/remote-aws'; - -export default defineConfig({ - remote: { - endpoint: 'https://cdn.example.com', - bundleName: 'app', - ...awsRemote({ bucket: 'my-bundles', aws: { region: 'us-east-1' } }), - }, -}); -``` - -퍼블리시 측 무결성과 서명 옵션은 `uploader`/`deployer`와 함께 위치합니다. [원격, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. - -## HTTP 규약 - -모든 프로바이더는 동일한 네 개의 엔드포인트를 구현합니다. 따라서 클라이언트는 뒤에 어떤 백엔드가 있는지 신경 쓰지 않습니다. - -| 메서드와 경로 | 목적 | 특이 사항 | -| ------------------------------- | ------------------------------ | -------------------------------------------------------- | -| `GET /bundles` | 배포된 번들 목록 조회 | `200` JSON `[{ "name", "version" }]`; 선택적 `?channel=` | -| `HEAD /bundles/{name}` | 현재 버전의 메타데이터(헤더만) | 배포되지 않았으면 `404` | -| `GET /bundles/{name}` | 현재 버전 다운로드 | 배포되지 않았으면 `404` | -| `GET /bundles/{name}/{version}` | 특정 버전 다운로드 | 다른 버전 허용이 아니면 `403` | - -필수 응답 헤더는 `Webview-Bundle-Name`과 `Webview-Bundle-Version`입니다. 선택 헤더는 `Webview-Bundle-Integrity`, `Webview-Bundle-Signature`, `ETag`, `Last-Modified`입니다. 다운로드는 `Content-Type: application/webview-bundle`을 사용합니다. 상태 코드와 채널 쿼리 전체 명세는 [원격 번들](/docs/guide/remote-bundles)에 있습니다. - -이 네 개의 라우트에 응답하는 최소한의 핸들러도 유효한 원격 서버입니다. 프로바이더 패키지는 필요하지 않습니다. - -```ts title="minimal-remote.ts" -import { Hono } from 'hono'; - -const app = new Hono(); - -app.get('/bundles', c => c.json([{ name: 'app', version: '1.1.0' }])); - -app.get('/bundles/:name', c => { - const body = readCurrentBundle(c.req.param('name')); // 여러분의 스토리지 - return new Response(body, { - headers: { - 'Content-Type': 'application/webview-bundle', - 'Webview-Bundle-Name': 'app', - 'Webview-Bundle-Version': '1.1.0', - }, - }); -}); - -app.get( - '/bundles/:name/:version', - c => c.body(null, 403) // 기본적으로 특정 버전은 금지 -); - -export default app; -``` - -## 로컬에서 흐름 테스트하기 - -업데이트 전체 흐름을 여러분의 머신에서 실행하세요. `@wvb/remote-local`은 업로드를 로컬 디렉터리(기본값 `~/.wvb/local`)에 쓰고, `wvb remote local`은 프로덕션 서버가 구현하는 것과 동일한 규약으로 그 디렉터리를 제공합니다. - -### 설정을 로컬 프로바이더로 지정하기 - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - pack: { - outFile: 'app', // → app.wvb - }, - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote(), // ~/.wvb/local에 쓰는 { uploader, deployer } - }, -}); -``` - -홈 디렉터리 대신 프로젝트 안에 스토어를 두려면 다음과 같이 하세요. - -```ts -...localRemote({ baseDir: './.wvb/local' }), -``` - -로컬 스토어는 번들과 버전별로 파일을 배치합니다. - -```text -~/.wvb/local/bundles/app/app_1.1.0.wvb # 번들 바이너리 -~/.wvb/local/bundles/app/app_1.1.0.json # { integrity?, signature? } -~/.wvb/local/bundles/app/deployment.json # 배포 포인터 -``` - -### 버전 퍼블리시하기 - -```sh -wvb pack # 설정으로부터 .wvb 빌드 -wvb upload app --version 1.1.0 --deploy # app_1.1.0.wvb를 ~/.wvb/local에 쓰고 배포 -``` - -`wvb upload`는 기본적으로 업로드 전에 패킹하므로, 명시적인 `wvb pack`은 선택 사항입니다. `--deploy`의 기본값은 `false`입니다. 이 플래그를 전달하거나, 배포를 따로 실행하세요. - -```sh -wvb deploy app --version 1.1.0 # 1.1.0을 클라이언트가 받는 버전으로 지정 -``` - -버전은 위치 인자가 아니라 `--version,-V` 플래그입니다. 모든 플래그는 [CLI 레퍼런스](/docs/guide/cli)를 참고하세요. - -### 제공하기 - -```sh -wvb remote local # ~/.wvb/local을 http://localhost:4313에서 제공 -wvb remote local --base-dir ./.wvb/local --port 4313 --allow-other-versions -``` - -`wvb remote local`은 기본적으로 포트 `4313`(베이스 디렉터리 `~/.wvb/local`)에서 대기합니다. `--allow-other-versions`를 지정하면 클라이언트가 배포되지 않은 버전을 `403` 대신 `GET /bundles/{name}/{version}`로 가져올 수 있습니다. - -### 앱을 원격 서버로 지정하기 - -앱의 업데이터 엔드포인트를 `http://localhost:4313`으로 설정하세요. 이제 `getUpdate`, `download`, `install`이 로컬 서버에 요청하며, 프로덕션과 똑같이 번들을 다운로드하고 검증하고 활성화합니다. - -```rust -let remote = Remote::builder() - .endpoint("http://localhost:4313") - .build()?; -let updater = Updater::new(source, Arc::new(remote), None); - -let info = updater.get_update("app").await?; // HEAD /bundles/app -if info.is_available { - updater.download("app", None).await?; // GET /bundles/app - updater.install("app", &info.version).await?; // 다운로드한 버전 활성화 -} -``` - - - Android 에뮬레이터에서는 호스트 머신이 `localhost`가 아닙니다. 업데이터를 - `http://10.0.2.2:4313`으로 지정하세요. - - -### 클라이언트 명령어로 확인하기 - -CLI는 어떤 원격 서버에 대해서도 클라이언트로 동작할 수 있습니다. 앱을 지정하기 전에 서버가 제대로 동작하는지 확인할 때 유용합니다. - -```sh -wvb remote list --endpoint http://localhost:4313 # GET /bundles (별칭: remote ls) -wvb remote current app --endpoint http://localhost:4313 # HEAD /bundles/app -wvb remote download app --endpoint http://localhost:4313 --outfile ./app.wvb # GET /bundles/app -``` - -### 단일 번들 미리 보기 - -서버나 배포 없이 `.wvb`를 살펴보려면, 그 파일들을 직접 제공하세요. - -```sh -wvb serve ./app.wvb # 번들의 파일들을 http://localhost:4312에서 제공 -wvb serve ./app.wvb --port 8080 -``` - -`wvb serve`는 기본적으로 포트 `4312`에서 대기합니다. 업로드하기 전에 패킹이 기대한 파일과 헤더를 만들었는지 확인할 때 사용하세요. - -## 프로바이더 선택하기 - -로컬 테스트를 넘어서면 `localRemote()`를 클라우드 백엔드로 바꾸세요. 각 프로바이더는 `wvb.config.ts`용으로 호환되는 `uploader`와 `deployer`, 위 규약을 구현하는 서버, 그리고 이를 프로비저닝하는 Pulumi 패키지를 제공합니다. - -```ts title="wvb.config.ts — Cloudflare" -import { cloudflareRemote } from '@wvb/remote-cloudflare'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - bundleName: 'app', - ...cloudflareRemote({ - accountId: process.env.CF_ACCOUNT_ID!, - bucket: 'webview-bundle', - kvNamespaceId: process.env.CF_KV_NAMESPACE_ID!, - }), - }, -}); -``` - -Pulumi로 그에 맞는 인프라를 프로비저닝하세요. - -```ts title="index.ts — pulumi up" -import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; - -export const remote = new WebviewBundleRemoteProvider('app', { - accountId: process.env.CF_ACCOUNT_ID!, -}); -``` - - - - - - diff --git a/content/docs/guide/remote.mdx b/content/docs/guide/remote.mdx deleted file mode 100644 index 64cbcc5..0000000 --- a/content/docs/guide/remote.mdx +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: Building a remote -description: Stand up an update server for over-the-air bundle delivery, run the whole loop locally, and choose a provider. ---- - -A remote is any HTTP server that serves bundles to your app's updater over a small, fixed contract. This is the server-side companion to [Remote bundles](/docs/guide/remote-bundles): build a remote, run the full publish-and-update loop locally with no cloud account, then pick a provider for production. - -Webview Bundle ships remotes for three backends — `local`, `aws`, and `cloudflare` — each split into three packages by role. Use only what the job needs. - -## Package roles - -Every backend has up to three packages: - -| Role | Package | What it does | -| ------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Config client | `@wvb/remote-` | A `wvb.config` plug-in that produces `{ uploader, deployer }`. Runs on the publisher's machine or CI to push and deploy bundles. | -| HTTP server | `@wvb/remote--provider` | The server that implements the HTTP contract and serves bundles to clients. | -| Pulumi IaC | `@wvb/remote--provider-pulumi` | Infrastructure-as-code that provisions the cloud resources and deploys the provider. | - -Concretely, per backend: - -| Backend | Config client | HTTP server | Pulumi IaC | -| ------------ | ------------------------ | --------------------------------- | ---------------------------------------- | -| `local` | `@wvb/remote-local` | `@wvb/remote-local-provider` | — | -| `aws` | `@wvb/remote-aws` | `@wvb/remote-aws-provider` | `@wvb/remote-aws-provider-pulumi` | -| `cloudflare` | `@wvb/remote-cloudflare` | `@wvb/remote-cloudflare-provider` | `@wvb/remote-cloudflare-provider-pulumi` | - -The config client is what you wire into the `remote` block of `wvb.config.ts`. The `uploader` writes a packed `.wvb` to storage; the `deployer` marks a version as the one clients should receive. Spread its result into `remote`: - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { awsRemote } from '@wvb/remote-aws'; - -export default defineConfig({ - remote: { - endpoint: 'https://cdn.example.com', - bundleName: 'app', - ...awsRemote({ bucket: 'my-bundles', aws: { region: 'us-east-1' } }), - }, -}); -``` - -Publish-side integrity and signature options live alongside `uploader`/`deployer` — see [Remote, integrity & signature config](/docs/config/remote). - -## The HTTP contract - -Every provider implements the same four endpoints, so the client never cares which backend is behind it. - -| Method and path | Purpose | Notable | -| ------------------------------- | ----------------------------------------- | ---------------------------------------------------------- | -| `GET /bundles` | List deployed bundles | `200` JSON `[{ "name", "version" }]`; optional `?channel=` | -| `HEAD /bundles/{name}` | Current version's metadata (headers only) | `404` if undeployed | -| `GET /bundles/{name}` | Download the current version | `404` if undeployed | -| `GET /bundles/{name}/{version}` | Download a specific version | `403` unless other-versions allowed | - -Required response headers are `Webview-Bundle-Name` and `Webview-Bundle-Version`. Optional: `Webview-Bundle-Integrity`, `Webview-Bundle-Signature`, `ETag`, `Last-Modified`. Downloads use `Content-Type: application/webview-bundle`. The full status-code and channel-query spec is in [Remote bundles](/docs/guide/remote-bundles). - -A minimal handler that answers these four routes is a valid remote — no provider package required: - -```ts title="minimal-remote.ts" -import { Hono } from 'hono'; - -const app = new Hono(); - -app.get('/bundles', c => c.json([{ name: 'app', version: '1.1.0' }])); - -app.get('/bundles/:name', c => { - const body = readCurrentBundle(c.req.param('name')); // your storage - return new Response(body, { - headers: { - 'Content-Type': 'application/webview-bundle', - 'Webview-Bundle-Name': 'app', - 'Webview-Bundle-Version': '1.1.0', - }, - }); -}); - -app.get( - '/bundles/:name/:version', - c => c.body(null, 403) // forbid specific versions by default -); - -export default app; -``` - -## Test the loop locally - -Run the entire update loop on your machine. `@wvb/remote-local` writes uploads to a local directory (default `~/.wvb/local`), and `wvb remote local` serves that directory over the same contract a production server implements. - -### Point your config at the local provider - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; -import { localRemote } from '@wvb/remote-local'; - -export default defineConfig({ - pack: { - outFile: 'app', // → app.wvb - }, - remote: { - endpoint: 'http://localhost:4313', - bundleName: 'app', - ...localRemote(), // { uploader, deployer } writing to ~/.wvb/local - }, -}); -``` - -Keep the store inside your project instead of the home directory: - -```ts -...localRemote({ baseDir: './.wvb/local' }), -``` - -The local store lays out files per bundle and version: - -```text -~/.wvb/local/bundles/app/app_1.1.0.wvb # bundle binary -~/.wvb/local/bundles/app/app_1.1.0.json # { integrity?, signature? } -~/.wvb/local/bundles/app/deployment.json # deployment pointer -``` - -### Publish a version - -```sh -wvb pack # build the .wvb from your config -wvb upload app --version 1.1.0 --deploy # write app_1.1.0.wvb into ~/.wvb/local and deploy it -``` - -`wvb upload` packs before uploading by default, so the explicit `wvb pack` is optional. `--deploy` defaults to `false`; pass it, or deploy separately: - -```sh -wvb deploy app --version 1.1.0 # make 1.1.0 the version clients receive -``` - -The version is the `--version,-V` flag, not a positional. See the [CLI reference](/docs/guide/cli) for every flag. - -### Serve it - -```sh -wvb remote local # serves ~/.wvb/local on http://localhost:4313 -wvb remote local --base-dir ./.wvb/local --port 4313 --allow-other-versions -``` - -`wvb remote local` listens on port `4313` by default (base dir `~/.wvb/local`). `--allow-other-versions` lets clients fetch a non-deployed version through `GET /bundles/{name}/{version}` instead of getting a `403`. - -### Point your app at it - -Set the app's updater endpoint to `http://localhost:4313`. Now `getUpdate`, `download`, and `install` hit your local server and download, verify, and activate the bundle exactly as production would: - -```rust -let remote = Remote::builder() - .endpoint("http://localhost:4313") - .build()?; -let updater = Updater::new(source, Arc::new(remote), None); - -let info = updater.get_update("app").await?; // HEAD /bundles/app -if info.is_available { - updater.download("app", None).await?; // GET /bundles/app - updater.install("app", &info.version).await?; // activate the downloaded version -} -``` - - - On the Android emulator, the host machine is not `localhost`. Point the updater at - `http://10.0.2.2:4313` instead. - - -### Inspect with the client commands - -The CLI can act as a client against any remote — handy for confirming a server behaves before pointing an app at it. - -```sh -wvb remote list --endpoint http://localhost:4313 # GET /bundles (alias: remote ls) -wvb remote current app --endpoint http://localhost:4313 # HEAD /bundles/app -wvb remote download app --endpoint http://localhost:4313 --outfile ./app.wvb # GET /bundles/app -``` - -### Preview a single bundle - -To inspect a `.wvb` without a server or deployment, serve its files directly: - -```sh -wvb serve ./app.wvb # serves the bundle's files on http://localhost:4312 -wvb serve ./app.wvb --port 8080 -``` - -`wvb serve` listens on port `4312` by default. Use it to confirm a pack produced the files and headers you expect before you upload. - -## Choose a provider - -When you move past local testing, swap `localRemote()` for a cloud backend. Each provider exposes a compatible `uploader` and `deployer` for `wvb.config.ts`, a server that implements the contract above, and a Pulumi package to provision it. - -```ts title="wvb.config.ts — Cloudflare" -import { cloudflareRemote } from '@wvb/remote-cloudflare'; - -export default defineConfig({ - remote: { - endpoint: 'https://bundles.example.com', - bundleName: 'app', - ...cloudflareRemote({ - accountId: process.env.CF_ACCOUNT_ID!, - bucket: 'webview-bundle', - kvNamespaceId: process.env.CF_KV_NAMESPACE_ID!, - }), - }, -}); -``` - -Provision the matching infrastructure with Pulumi: - -```ts title="index.ts — pulumi up" -import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; - -export const remote = new WebviewBundleRemoteProvider('app', { - accountId: process.env.CF_ACCOUNT_ID!, -}); -``` - - - - - - diff --git a/content/docs/guide/spec/get-current.ko.mdx b/content/docs/guide/remote/http-spec/endpoints/get-current.ko.mdx similarity index 73% rename from content/docs/guide/spec/get-current.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-current.ko.mdx index 1280941..3bc33c2 100644 --- a/content/docs/guide/spec/get-current.ko.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/get-current.ko.mdx @@ -3,7 +3,7 @@ title: 현재 번들 다운로드 description: 배포된 번들의 현재 버전을 원본 .wvb 바이트로 다운로드합니다. 채널을 선택할 수도 있습니다. --- -배포된 번들의 현재 버전을 다운로드합니다. 서버는 `.wvb` 아카이브를 응답 본문으로 스트리밍하고, 번들의 메타데이터를 `Webview-Bundle-*` 헤더에 담아 전달합니다. [업데이터](/docs/references/node/updater)는 검사 결과 새 버전이 있다고 확인되면 이 호출로 새 버전을 가져옵니다. +배포된 번들의 현재 버전을 다운로드합니다. 서버는 `.wvb` 아카이브를 응답 본문으로 스트리밍하고, 번들의 메타데이터를 `Webview-Bundle-*` 헤더에 담아 전달합니다. [업데이터](/docs/references/api/node/updater)는 검사 결과 새 버전이 있다고 확인되면 이 호출로 새 버전을 가져옵니다. ```http GET /bundles/{name} @@ -25,11 +25,11 @@ GET /bundles/{name} | `200` | 번들을 찾았습니다. 본문은 `.wvb` 아카이브입니다. | | `404` | 해당 이름의 번들이 배포되지 않았습니다. | -`404` 형태와 클라이언트가 그 밖의 non-2xx 응답을 매핑하는 방식은 [오류](/docs/guide/spec/errors)를 참고하세요. +`404` 형태와 클라이언트가 그 밖의 non-2xx 응답을 매핑하는 방식은 [오류](/docs/guide/remote/http-spec/errors)를 참고하세요. ### 응답 헤더 -`200` 응답은 [HEAD /bundles/{name}](/docs/guide/spec/head-current)과 동일한 번들 메타데이터 헤더를 담습니다. 클라이언트는 헤더를 대소문자 구분 없이 읽습니다. 여기서는 표준 표기를 보여줍니다. +`200` 응답은 [HEAD /bundles/{name}](/docs/guide/remote/http-spec/endpoints/head-current)과 동일한 번들 메타데이터 헤더를 담습니다. 클라이언트는 헤더를 대소문자 구분 없이 읽습니다. 여기서는 표준 표기를 보여줍니다. | Header | Required | Description | | -------------------------- | -------- | --------------------------------- | @@ -77,23 +77,3 @@ curl -sI http://localhost:4313/bundles/unknown ```http HTTP/1.1 404 Not Found ``` - -## 관련 항목 - - - - - - diff --git a/content/docs/guide/spec/get-current.mdx b/content/docs/guide/remote/http-spec/endpoints/get-current.mdx similarity index 78% rename from content/docs/guide/spec/get-current.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-current.mdx index e182f6f..50739e4 100644 --- a/content/docs/guide/spec/get-current.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/get-current.mdx @@ -3,7 +3,7 @@ title: Download current bundle description: Download the current deployed version of a bundle as raw .wvb bytes, optionally selecting a channel. --- -Download the current deployed version of a bundle. The server streams the `.wvb` archive as the response body and carries the bundle's metadata in `Webview-Bundle-*` headers. This is the call the [updater](/docs/references/node/updater) makes to fetch a new version after a check reports one is available. +Download the current deployed version of a bundle. The server streams the `.wvb` archive as the response body and carries the bundle's metadata in `Webview-Bundle-*` headers. This is the call the [updater](/docs/references/api/node/updater) makes to fetch a new version after a check reports one is available. ```http GET /bundles/{name} @@ -25,11 +25,11 @@ Pass `?channel=` to download the current version for a specific channel instead | `200` | The bundle was found; the body is the `.wvb` archive. | | `404` | The named bundle is not deployed. | -For the `404` shape and how the client maps other non-2xx responses, see [Errors](/docs/guide/spec/errors). +For the `404` shape and how the client maps other non-2xx responses, see [Errors](/docs/guide/remote/http-spec/errors). ### Response headers -A `200` response carries the same bundle metadata headers as [HEAD /bundles/{name}](/docs/guide/spec/head-current). The client reads them case-insensitively; the canonical casing is shown here. +A `200` response carries the same bundle metadata headers as [HEAD /bundles/{name}](/docs/guide/remote/http-spec/endpoints/head-current). The client reads them case-insensitively; the canonical casing is shown here. | Header | Required | Description | | -------------------------- | -------- | ------------------------------------ | @@ -77,23 +77,3 @@ curl -sI http://localhost:4313/bundles/unknown ```http HTTP/1.1 404 Not Found ``` - -## Related - - - - - - diff --git a/content/docs/guide/spec/get-version.ko.mdx b/content/docs/guide/remote/http-spec/endpoints/get-version.ko.mdx similarity index 80% rename from content/docs/guide/spec/get-version.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-version.ko.mdx index 6a8e904..5558e6f 100644 --- a/content/docs/guide/spec/get-version.ko.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/get-version.ko.mdx @@ -28,7 +28,7 @@ GET /bundles/{name}/{version} | `403` | 서버가 다른 버전 다운로드를 비활성화한 경우. | | `404` | 이름이 지정된 번들 또는 버전이 배포되지 않은 경우. | -클라이언트는 `403`을 "forbidden" 오류로, `404`를 "bundle not found" 오류로 매핑합니다. 전체 매핑과 JSON 오류 본문 형태는 [오류](/docs/guide/spec/errors)를 참고하세요. +클라이언트는 `403`을 "forbidden" 오류로, `404`를 "bundle not found" 오류로 매핑합니다. 전체 매핑과 JSON 오류 본문 형태는 [오류](/docs/guide/remote/http-spec/errors)를 참고하세요. ### 응답 헤더 @@ -47,7 +47,7 @@ GET /bundles/{name}/{version} 이 경로는 채널을 선택하지 않습니다. 채널 지정 여부와 관계없이 현재 버전을 다운로드하려면 [GET - /bundles/{name}](/docs/guide/spec/get-current)을 사용하세요. + /bundles/{name}](/docs/guide/remote/http-spec/endpoints/get-current)을 사용하세요. ## 예제 @@ -79,28 +79,3 @@ HTTP/1.1 403 Forbidden $ curl -sI http://localhost:4313/bundles/app/9.9.9 HTTP/1.1 404 Not Found ``` - -## 관련 문서 - - - - - - - diff --git a/content/docs/guide/spec/get-version.mdx b/content/docs/guide/remote/http-spec/endpoints/get-version.mdx similarity index 79% rename from content/docs/guide/spec/get-version.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-version.mdx index 86e0fb3..3f14f36 100644 --- a/content/docs/guide/spec/get-version.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/get-version.mdx @@ -28,7 +28,7 @@ This route does not accept a `channel` query parameter. | `403` | The server disables other-version downloads. | | `404` | The named bundle or version is not deployed. | -The client maps `403` to a "forbidden" error and `404` to a "bundle not found" error. See [Errors](/docs/guide/spec/errors) for the full mapping and the JSON error body shape. +The client maps `403` to a "forbidden" error and `404` to a "bundle not found" error. See [Errors](/docs/guide/remote/http-spec/errors) for the full mapping and the JSON error body shape. ### Response headers @@ -47,7 +47,7 @@ The `200` body is the raw `.wvb` bytes, served with `Content-Type: application/w This route never selects a channel. To download the current version, with or without a channel, - use [GET /bundles/{name}](/docs/guide/spec/get-current). + use [GET /bundles/{name}](/docs/guide/remote/http-spec/endpoints/get-current). ## Example @@ -79,28 +79,3 @@ A version that is not deployed returns `404`: $ curl -sI http://localhost:4313/bundles/app/9.9.9 HTTP/1.1 404 Not Found ``` - -## Related - - - - - - - diff --git a/content/docs/guide/spec/head-current.ko.mdx b/content/docs/guide/remote/http-spec/endpoints/head-current.ko.mdx similarity index 80% rename from content/docs/guide/spec/head-current.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/head-current.ko.mdx index 5133a60..0e937c8 100644 --- a/content/docs/guide/spec/head-current.ko.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/head-current.ko.mdx @@ -23,7 +23,7 @@ HEAD /bundles/{name} | `204` | 번들이 배포되어 있습니다. 메타데이터가 헤더로 반환됩니다. | | `404` | 지정한 번들이 배포되어 있지 않습니다. | -성공은 `204 No Content`입니다. 응답은 메타데이터 헤더만 담으며 본문은 절대 포함하지 않습니다. `404`는 지정한 번들에 현재 배포된 버전이 없다는 뜻입니다. 오류 본문 형태와 다른 상태 코드는 [오류](/docs/guide/spec/errors)를 참고하세요. +성공은 `204 No Content`입니다. 응답은 메타데이터 헤더만 담으며 본문은 절대 포함하지 않습니다. `404`는 지정한 번들에 현재 배포된 버전이 없다는 뜻입니다. 오류 본문 형태와 다른 상태 코드는 [오류](/docs/guide/remote/http-spec/errors)를 참고하세요. ### 응답 헤더 @@ -40,7 +40,7 @@ HEAD /bundles/{name} 이 경로는 메타데이터만 반환합니다. 번들 바이트를 가져오려면 [GET /bundles/{name} - ](/docs/guide/spec/get-current)을 사용하세요. + ](/docs/guide/remote/http-spec/endpoints/get-current)을 사용하세요. ## 예시 @@ -63,28 +63,3 @@ Last-Modified: Tue, 01 Jul 2026 00:00:00 GMT ```sh $ curl -sI 'http://localhost:4313/bundles/app?channel=beta' ``` - -## 관련 문서 - - - - - - - diff --git a/content/docs/guide/spec/head-current.mdx b/content/docs/guide/remote/http-spec/endpoints/head-current.mdx similarity index 79% rename from content/docs/guide/spec/head-current.mdx rename to content/docs/guide/remote/http-spec/endpoints/head-current.mdx index 9a2b63d..2ed04fb 100644 --- a/content/docs/guide/spec/head-current.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/head-current.mdx @@ -23,7 +23,7 @@ HEAD /bundles/{name} | `204` | The bundle is deployed. Metadata is returned in the headers. | | `404` | The named bundle is not deployed. | -Success is `204 No Content`: the response carries metadata headers only and never a body. A `404` means the named bundle has no current deployment. See [Errors](/docs/guide/spec/errors) for the error body shape and other status codes. +Success is `204 No Content`: the response carries metadata headers only and never a body. A `404` means the named bundle has no current deployment. See [Errors](/docs/guide/remote/http-spec/errors) for the error body shape and other status codes. ### Response headers @@ -40,7 +40,7 @@ The client reads these headers case-insensitively and rejects a response that om This route returns metadata only. To fetch the bundle bytes, use [GET /bundles/{name} - ](/docs/guide/spec/get-current). + ](/docs/guide/remote/http-spec/endpoints/get-current). ## Example @@ -63,28 +63,3 @@ Read a specific channel's current version with `?channel=`: ```sh $ curl -sI 'http://localhost:4313/bundles/app?channel=beta' ``` - -## Related - - - - - - - diff --git a/content/docs/guide/spec/list-bundles.ko.mdx b/content/docs/guide/remote/http-spec/endpoints/list-bundles.ko.mdx similarity index 73% rename from content/docs/guide/spec/list-bundles.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/list-bundles.ko.mdx index 153a9e6..c73f7f0 100644 --- a/content/docs/guide/spec/list-bundles.ko.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/list-bundles.ko.mdx @@ -21,7 +21,7 @@ GET /bundles | ------ | ----------------------------------- | | `200` | 현재 배포된 번들의 JSON 배열입니다. | -4xx 응답은 [오류](/docs/guide/spec/errors)를 참고하세요. +4xx 응답은 [오류](/docs/guide/remote/http-spec/errors)를 참고하세요. ### 응답 헤더 @@ -68,30 +68,5 @@ curl -s 'http://localhost:4313/bundles?channel=beta' 이 작업은 원격 HTTP 계약의 진입점입니다. 번들의 현재 메타데이터를 읽거나 바이트를 다운로드하려면 - [원격 HTTP 명세](/docs/guide/spec) 개요를 참고하세요. + [원격 HTTP 명세](/docs/guide/remote/http-spec) 개요를 참고하세요. - -## 다음으로 볼 내용 - - - - - - - diff --git a/content/docs/guide/spec/list-bundles.mdx b/content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx similarity index 75% rename from content/docs/guide/spec/list-bundles.mdx rename to content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx index c3fc6fc..f5b9a3e 100644 --- a/content/docs/guide/spec/list-bundles.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx @@ -21,7 +21,7 @@ GET /bundles | ------ | ----------------------------------------------- | | `200` | A JSON array of the currently deployed bundles. | -For 4xx responses, see [Errors](/docs/guide/spec/errors). +For 4xx responses, see [Errors](/docs/guide/remote/http-spec/errors). ### Response headers @@ -68,30 +68,5 @@ curl -s 'http://localhost:4313/bundles?channel=beta' This operation is the entry point of the remote HTTP contract. To read a bundle's current metadata - or download its bytes, see the [Remote HTTP Spec](/docs/guide/spec) overview. + or download its bytes, see the [Remote HTTP Spec](/docs/guide/remote/http-spec) overview. - -## Where to go next - - - - - - - diff --git a/content/docs/guide/remote/http-spec/endpoints/meta.json b/content/docs/guide/remote/http-spec/endpoints/meta.json new file mode 100644 index 0000000..52349f4 --- /dev/null +++ b/content/docs/guide/remote/http-spec/endpoints/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Endpoints", + "pages": ["list-bundles", "head-current", "get-current", "get-version"] +} diff --git a/content/docs/guide/remote/http-spec/endpoints/meta.ko.json b/content/docs/guide/remote/http-spec/endpoints/meta.ko.json new file mode 100644 index 0000000..d5f17d6 --- /dev/null +++ b/content/docs/guide/remote/http-spec/endpoints/meta.ko.json @@ -0,0 +1,4 @@ +{ + "title": "엔드포인트", + "pages": ["list-bundles", "head-current", "get-current", "get-version"] +} diff --git a/content/docs/guide/remote/http-spec/errors.ko.mdx b/content/docs/guide/remote/http-spec/errors.ko.mdx new file mode 100644 index 0000000..f15cc78 --- /dev/null +++ b/content/docs/guide/remote/http-spec/errors.ko.mdx @@ -0,0 +1,31 @@ +--- +title: 오류 +description: 리모트가 non-2xx 응답으로 반환하는 상태 코드와, 클라이언트가 이를 매핑하는 방식입니다. +--- + +리모트는 실패를 HTTP 상태 코드로 알립니다. 클라이언트는 그중 두 가지를 특별히 처리하고, 나머지는 일반 HTTP 오류로 취급합니다. + +| Status | Meaning | Client error message | +| --------------- | ----------------------------------------------------------- | -------------------------------------- | +| `403` | 다른 버전 다운로드가 비활성화됨(`allowOtherVersions` 꺼짐). | `remote forbidden` | +| `404` | 요청한 번들 또는 버전이 배포되지 않음. | `remote bundle not found` | +| 그 밖의 non-2xx | 그 외의 실패. | `remote http error with status ` | + +`403`과 `404`에서는 클라이언트가 응답 본문을 읽지 않습니다. 그 밖의 non-2xx 상태에서는 아래 형태의 JSON 본문을 읽어 `message`을 오류에 노출합니다. + +```json +{ "message": "요청이 실패한 이유" } +``` + + + 이 오류들은 일반 JavaScript `Error`로 전달됩니다. 메시지는 위 텍스트와 같으며, 별도의 오류 + 클래스나 상태 필드는 없습니다. `403`과 `404`은 메시지 문자열로 구분하세요. + + +## 잘못된 응답 + +필수 메타데이터가 빠지면 `2xx` 응답이라도 거부됩니다. 번들 다운로드에서 `Webview-Bundle-Name` 또는 `Webview-Bundle-Version`이 없으면, 클라이언트는 설치하지 않고 `invalid remote bundle`로 실패합니다. + +## 업로드 충돌 + +게시는 위의 다운로드 규약과 별개입니다. 이미 존재하는 버전을 업로드하면 `BundleAlreadyUploadedError`을 던집니다. 덮어쓰려면 `--force`(또는 `force: true`)을 전달하세요. [리모트에 업로드](/docs/guide/frontend/upload)를 참고하세요. diff --git a/content/docs/guide/remote/http-spec/errors.mdx b/content/docs/guide/remote/http-spec/errors.mdx new file mode 100644 index 0000000..3948661 --- /dev/null +++ b/content/docs/guide/remote/http-spec/errors.mdx @@ -0,0 +1,31 @@ +--- +title: Errors +description: The status codes a remote returns for non-2xx responses and how the client maps them. +--- + +The remote signals failures with HTTP status codes. The client handles two of them specially and treats the rest as generic HTTP errors. + +| Status | Meaning | Client error message | +| ------------- | ------------------------------------------------------------------- | -------------------------------------- | +| `403` | Other-version downloads are disabled (`allowOtherVersions` is off). | `remote forbidden` | +| `404` | The requested bundle or version is not deployed. | `remote bundle not found` | +| other non-2xx | Any other failure. | `remote http error with status ` | + +For `403` and `404` the client does not read the response body. For any other non-2xx status it reads a JSON body of the shape below and surfaces `message` on the error. + +```json +{ "message": "why the request failed" } +``` + + + These surface as ordinary JavaScript `Error`s — the message equals the text above, with no typed + error class or status field. Tell `403` from `404` by the message string. + + +## Invalid responses + +A `2xx` response is still rejected when required metadata is missing. If `Webview-Bundle-Name` or `Webview-Bundle-Version` is absent from a bundle download, the client fails with `invalid remote bundle` rather than installing it. + +## Upload conflicts + +Publishing is separate from the download contract above. Uploading a version that already exists throws `BundleAlreadyUploadedError`; pass `--force` (or `force: true`) to overwrite it. See [Upload to remote](/docs/guide/frontend/upload). diff --git a/content/docs/guide/remote/http-spec/index.ko.mdx b/content/docs/guide/remote/http-spec/index.ko.mdx new file mode 100644 index 0000000..b3a3abc --- /dev/null +++ b/content/docs/guide/remote/http-spec/index.ko.mdx @@ -0,0 +1,46 @@ +--- +title: 요약 +--- + +이 HTTP 규약을 구현한 서버라면 무엇이든 리모트로 동작할 수 있습니다. [프로바이더](/docs/guide/remote/providers/local)는 이 규약을 대신 구현하므로, 직접 서버를 구축할 때만 읽으면 됩니다. + +클라이언트는 세 단계로 번들을 읽습니다. + +1. [`GET /bundles`](/docs/guide/remote/http-spec/endpoints/list-bundles) — 배포된 번들을 나열합니다. +2. [`HEAD /bundles/{name}`](/docs/guide/remote/http-spec/endpoints/head-current) — 현재 버전의 메타데이터를 읽어 업데이트를 확인합니다. +3. [`GET /bundles/{name}`](/docs/guide/remote/http-spec/endpoints/get-current) — 현재 버전의 바이트를 다운로드합니다. [`GET /bundles/{name}/{version}`](/docs/guide/remote/http-spec/endpoints/get-version)은 특정 버전을 다운로드합니다. + +## 메타데이터 헤더 + +번들 응답은 메타데이터를 본문이 아니라 `Webview-Bundle-*` 헤더에 담습니다. `Webview-Bundle-Name`과 `Webview-Bundle-Version`은 필수이며, 둘 중 하나라도 빠지면 클라이언트가 응답을 거부합니다. `Webview-Bundle-Integrity`과 `Webview-Bundle-Signature`은 선택입니다. 번들 바이트는 `application/webview-bundle`로 제공됩니다. + +## 채널 + +채널은 별도의 배포 트랙을 선택합니다. 목록, 메타데이터, 현재 버전 다운로드 경로는 선택적인 `?channel=` 쿼리를 받으며, 생략하면 기본 배포를 대상으로 합니다. 특정 버전 경로는 채널을 받지 않습니다. + +## 엔드포인트 + + + + + + + + +non-2xx 응답은 [오류](/docs/guide/remote/http-spec/errors)를 참고하세요. diff --git a/content/docs/guide/remote/http-spec/index.mdx b/content/docs/guide/remote/http-spec/index.mdx new file mode 100644 index 0000000..aa0d769 --- /dev/null +++ b/content/docs/guide/remote/http-spec/index.mdx @@ -0,0 +1,46 @@ +--- +title: Overview +--- + +Any server that implements this HTTP contract can act as a remote. The [providers](/docs/guide/remote/providers/local) implement it for you; read this only when you build your own. + +A client reads bundles in three steps: + +1. [`GET /bundles`](/docs/guide/remote/http-spec/endpoints/list-bundles) — list the deployed bundles. +2. [`HEAD /bundles/{name}`](/docs/guide/remote/http-spec/endpoints/head-current) — read the current version's metadata to check for an update. +3. [`GET /bundles/{name}`](/docs/guide/remote/http-spec/endpoints/get-current) — download the current version's bytes. [`GET /bundles/{name}/{version}`](/docs/guide/remote/http-spec/endpoints/get-version) downloads an exact version. + +## Metadata headers + +Bundle responses carry metadata in `Webview-Bundle-*` headers, not the body. `Webview-Bundle-Name` and `Webview-Bundle-Version` are required — the client rejects a response missing either. `Webview-Bundle-Integrity` and `Webview-Bundle-Signature` are optional. Bundle bytes are served as `application/webview-bundle`. + +## Channels + +A channel selects a separate deployment track. The list, metadata, and current-download routes accept an optional `?channel=` query; omit it to target the default deployment. The exact-version route takes no channel. + +## Endpoints + + + + + + + + +For non-2xx responses, see [Errors](/docs/guide/remote/http-spec/errors). diff --git a/content/docs/guide/remote/http-spec/meta.json b/content/docs/guide/remote/http-spec/meta.json new file mode 100644 index 0000000..eee67ba --- /dev/null +++ b/content/docs/guide/remote/http-spec/meta.json @@ -0,0 +1,4 @@ +{ + "title": "HTTP Spec", + "pages": ["index", "endpoints", "errors"] +} diff --git a/content/docs/guide/remote/http-spec/meta.ko.json b/content/docs/guide/remote/http-spec/meta.ko.json new file mode 100644 index 0000000..39e6b55 --- /dev/null +++ b/content/docs/guide/remote/http-spec/meta.ko.json @@ -0,0 +1,4 @@ +{ + "title": "HTTP 스펙", + "pages": ["index", "endpoints", "errors"] +} diff --git a/content/docs/guide/remote/index.ko.mdx b/content/docs/guide/remote/index.ko.mdx new file mode 100644 index 0000000..7617591 --- /dev/null +++ b/content/docs/guide/remote/index.ko.mdx @@ -0,0 +1,40 @@ +--- +title: 개요 +--- + +리모트는 앱이 새 번들을 원격(Over-The-Air)으로 다운로드하는 HTTP 서버입니다. 패킹한 `.wvb`을 리모트에 업로드하고 현재 버전을 지정하면, 클라이언트는 네이티브 앱을 새로 배포하지 않고도 해당 버전을 받아옵니다. + +## 게시와 다운로드 + +게시는 두 단계로 이뤄지며, 모두 [CLI](/docs/guide/remote/using-cli)에서 실행합니다. + +1. **업로드**은 패킹한 버전을 리모트에 저장합니다. +2. **배포**은 클라이언트가 받을 현재 버전을 지정합니다. + +이후 클라이언트는 업데이터를 통해 배포된 버전을 확인하고 다운로드합니다. 클라이언트 쪽 흐름은 [Over-The-Air](/docs/guide/core-concepts/over-the-air) 문서를, 이를 연결하는 `remote` 블록은 [리모트 설정](/docs/references/configuration/remote)을 참고하세요. + +## 리모트 실행 + +리모트를 실행하는 방법은 두 가지입니다. + +**프로바이더 사용하기.** 프로바이더는 배포할 서버와, 설정에 넣을 `uploader`/`deployer` 쌍을 제공합니다. HTTP 규약을 직접 구현할 필요가 없습니다. + + + + + + + +**직접 구축하기.** 리모트 HTTP 규약을 구현한 서버라면 무엇이든 동작합니다. [HTTP 규약](/docs/guide/remote/http-spec)을 참고하세요. diff --git a/content/docs/guide/remote/index.mdx b/content/docs/guide/remote/index.mdx new file mode 100644 index 0000000..c85c76a --- /dev/null +++ b/content/docs/guide/remote/index.mdx @@ -0,0 +1,40 @@ +--- +title: Overview +--- + +A remote is the HTTP server your app downloads new bundles from over the air. You publish a packed `.wvb` to it, mark a version as current, and clients fetch that version without a native app release. + +## Publish and download + +Publishing is a two-step model, both run from the [CLI](/docs/guide/remote/using-cli): + +1. **Upload** stores a packed version on the remote. +2. **Deploy** marks which version is current — the one clients receive. + +Clients then check for and download the deployed version through the updater. See [Over-the-air](/docs/guide/core-concepts/over-the-air) for the client-side flow, and [Remote configuration](/docs/references/configuration/remote) for the `remote` block that wires it up. + +## Run a remote + +There are two ways to run a remote. + +**Use a provider.** A provider gives you a server to deploy and an `uploader`/`deployer` pair to drop into your config — no HTTP contract to implement. + + + + + + + +**Build your own.** Any server that implements the remote HTTP contract works. See the [HTTP spec](/docs/guide/remote/http-spec). diff --git a/content/docs/guide/remote/providers/aws.ko.mdx b/content/docs/guide/remote/providers/aws.ko.mdx new file mode 100644 index 0000000..8fea998 --- /dev/null +++ b/content/docs/guide/remote/providers/aws.ko.mdx @@ -0,0 +1,59 @@ +--- +title: AWS +description: S3와 CloudFront에서 번들을 제공하며, KMS 서명을 선택적으로 사용합니다. +--- + +AWS 프로바이더는 CloudFront 배포 뒤의 S3 버킷에서 번들을 제공합니다. 두 개의 Lambda@Edge 함수가 엣지에서 리모트 HTTP 규약을 구현합니다. + +## 서버 프로비저닝 + +`@wvb/remote-aws-provider-pulumi`은 버킷, 배포, 엣지 함수까지 전체 스택을 [Pulumi](https://www.pulumi.com) 컴포넌트로 프로비저닝합니다. + +```sh +npm install @wvb/remote-aws-provider-pulumi +``` + +```ts title="index.ts" +import { WebviewBundleRemoteProvider } from '@wvb/remote-aws-provider-pulumi'; + +const remote = new WebviewBundleRemoteProvider('wvb', { + bucketName: 'my-bundles', + // allowOtherVersions: true, // GET /bundles/{name}/{version}도 제공 +}); + +export const endpoint = remote.cloudfrontDistributionDomainName; +``` + +요청 핸들러 자체는 `@wvb/remote-aws-provider`(`webviewBundleRemote`)이며, 컴포넌트가 대신 배포합니다. 엣지 함수를 직접 연결하려면 [패키지 레퍼런스](/docs/references/api/remotes/remote-aws-provider)를 참고하세요. + +## 게시하기 + +`@wvb/remote-aws`은 `uploader`과 `deployer`, 그리고 선택적인 AWS KMS `signature` 서명기를 설정용으로 제공합니다. + +```sh +npm install -D @wvb/remote-aws +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { awsRemote } from '@wvb/remote-aws'; + +export default defineConfig({ + remote: { + endpoint: 'https://d111111abcdef8.cloudfront.net', + integrity: { algorithm: 'sha256' }, + ...awsRemote({ + bucket: 'my-bundles', + // deployer: { invalidation: { distributionId: 'E1234567890ABC' } }, + // signature: { keyId: 'arn:aws:kms:…', algorithm: 'ECDSA_SHA_256' }, + }), + }, +}); +``` + +`awsRemote()`은 `{ uploader, deployer, signature? }`을 반환합니다. 자격 증명과 리전은 표준 AWS SDK 체인을 따르며, `aws` 옵션으로 재정의할 수 있습니다. 모든 옵션은 [AWS 클라이언트 레퍼런스](/docs/references/api/remotes/remote-aws)를 참고하세요. + + + 서버 옵션은 `bucketName`, 클라이언트 옵션은 `bucket`입니다. 둘 다 같은 버킷을 가리켜야 합니다. + Lambda@Edge는 버킷 리전과 무관하게 항상 `us-east-1`에서 실행됩니다. + diff --git a/content/docs/guide/remote/providers/aws.mdx b/content/docs/guide/remote/providers/aws.mdx new file mode 100644 index 0000000..7a0f5b2 --- /dev/null +++ b/content/docs/guide/remote/providers/aws.mdx @@ -0,0 +1,59 @@ +--- +title: AWS +description: Serve bundles from S3 behind CloudFront, with optional KMS signing. +--- + +The AWS provider serves bundles from an S3 bucket behind a CloudFront distribution. Two Lambda@Edge functions implement the remote HTTP contract at the edge. + +## Provision the server + +`@wvb/remote-aws-provider-pulumi` provisions the whole stack — bucket, distribution, and edge functions — as a [Pulumi](https://www.pulumi.com) component: + +```sh +npm install @wvb/remote-aws-provider-pulumi +``` + +```ts title="index.ts" +import { WebviewBundleRemoteProvider } from '@wvb/remote-aws-provider-pulumi'; + +const remote = new WebviewBundleRemoteProvider('wvb', { + bucketName: 'my-bundles', + // allowOtherVersions: true, // also serve GET /bundles/{name}/{version} +}); + +export const endpoint = remote.cloudfrontDistributionDomainName; +``` + +The request handler itself is `@wvb/remote-aws-provider` (`webviewBundleRemote`), which the component deploys for you. To wire the edge functions manually, see the [package reference](/docs/references/api/remotes/remote-aws-provider). + +## Publish to it + +`@wvb/remote-aws` provides the `uploader` and `deployer` — and an optional AWS KMS `signature` signer — for your config: + +```sh +npm install -D @wvb/remote-aws +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { awsRemote } from '@wvb/remote-aws'; + +export default defineConfig({ + remote: { + endpoint: 'https://d111111abcdef8.cloudfront.net', + integrity: { algorithm: 'sha256' }, + ...awsRemote({ + bucket: 'my-bundles', + // deployer: { invalidation: { distributionId: 'E1234567890ABC' } }, + // signature: { keyId: 'arn:aws:kms:…', algorithm: 'ECDSA_SHA_256' }, + }), + }, +}); +``` + +`awsRemote()` returns `{ uploader, deployer, signature? }`. Credentials and region come from the standard AWS SDK chain; override them with the `aws` option. See the [AWS client reference](/docs/references/api/remotes/remote-aws) for every option. + + + The server option is `bucketName`; the client option is `bucket`. Both must point at the same + bucket. Lambda@Edge always runs in `us-east-1`, independent of the bucket's region. + diff --git a/content/docs/guide/remote/providers/cloudflare.ko.mdx b/content/docs/guide/remote/providers/cloudflare.ko.mdx new file mode 100644 index 0000000..5d72330 --- /dev/null +++ b/content/docs/guide/remote/providers/cloudflare.ko.mdx @@ -0,0 +1,70 @@ +--- +title: Cloudflare +description: Cloudflare Worker 뒤의 R2에서 번들을 제공합니다. +--- + +Cloudflare 프로바이더는 Worker를 통해 R2 버킷에서 번들을 제공하며, KV 네임스페이스로 어떤 버전이 배포되었는지 기록합니다. + +## 서버 프로비저닝 + +`@wvb/remote-cloudflare-provider-pulumi`은 Worker, R2 버킷, KV 네임스페이스를 [Pulumi](https://www.pulumi.com) 컴포넌트로 프로비저닝합니다. + +```sh +npm install @wvb/remote-cloudflare-provider-pulumi +``` + +```ts title="index.ts" +import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; + +const remote = new WebviewBundleRemoteProvider('wvb', { + accountId: '', +}); + +export const bucketName = remote.bucketName; +export const kvNamespaceId = remote.kvNamespaceId; +``` + +Worker를 직접 작성하려면 `@wvb/remote-cloudflare-provider`이 [Hono](https://hono.dev) 앱을 내보냅니다. R2 버킷을 `BUCKET`으로, KV 네임스페이스를 `KV`로 바인딩한 뒤 핸들러에 매핑하세요. + +```ts title="src/worker.ts" +import { wvbRemote } from '@wvb/remote-cloudflare-provider'; + +const app = wvbRemote(); + +export default { + fetch(req: Request, env: { KV: KVNamespace; BUCKET: R2Bucket }) { + return app.fetch(req, { kv: env.KV, r2: env.BUCKET }); + }, +}; +``` + +## 게시하기 + +`@wvb/remote-cloudflare`은 `uploader`과 `deployer`을 설정용으로 제공합니다. + +```sh +npm install -D @wvb/remote-cloudflare +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { cloudflareRemote } from '@wvb/remote-cloudflare'; + +export default defineConfig({ + remote: { + endpoint: 'https://updates.example.com', + ...cloudflareRemote({ + accountId: '', + bucket: 'webview-bundle', + kvNamespaceId: '', + }), + }, +}); +``` + +업로더는 S3 호환 API로 R2에 기록하므로 R2 액세스 키를 `s3ClientConfig.credentials`로 전달하고, 디플로이어는 Cloudflare API 토큰으로 KV를 갱신합니다. 전체 설정은 [Cloudflare 클라이언트 레퍼런스](/docs/references/api/remotes/remote-cloudflare)를 참고하세요. + + + Worker는 `BUCKET`이라는 R2 바인딩과 `KV`라는 KV 바인딩을 읽습니다. `wrangler.jsonc`에서 이 이름을 + 맞춰주세요. + diff --git a/content/docs/guide/remote/providers/cloudflare.mdx b/content/docs/guide/remote/providers/cloudflare.mdx new file mode 100644 index 0000000..4f878ac --- /dev/null +++ b/content/docs/guide/remote/providers/cloudflare.mdx @@ -0,0 +1,70 @@ +--- +title: Cloudflare +description: Serve bundles from R2 behind a Cloudflare Worker. +--- + +The Cloudflare provider serves bundles from an R2 bucket through a Worker, using a KV namespace to record which version is deployed. + +## Provision the server + +`@wvb/remote-cloudflare-provider-pulumi` provisions the Worker, R2 bucket, and KV namespace as a [Pulumi](https://www.pulumi.com) component: + +```sh +npm install @wvb/remote-cloudflare-provider-pulumi +``` + +```ts title="index.ts" +import { WebviewBundleRemoteProvider } from '@wvb/remote-cloudflare-provider-pulumi'; + +const remote = new WebviewBundleRemoteProvider('wvb', { + accountId: '', +}); + +export const bucketName = remote.bucketName; +export const kvNamespaceId = remote.kvNamespaceId; +``` + +To write the Worker yourself, `@wvb/remote-cloudflare-provider` exports a [Hono](https://hono.dev) app. Bind an R2 bucket as `BUCKET` and a KV namespace as `KV`, then map them into the handler: + +```ts title="src/worker.ts" +import { wvbRemote } from '@wvb/remote-cloudflare-provider'; + +const app = wvbRemote(); + +export default { + fetch(req: Request, env: { KV: KVNamespace; BUCKET: R2Bucket }) { + return app.fetch(req, { kv: env.KV, r2: env.BUCKET }); + }, +}; +``` + +## Publish to it + +`@wvb/remote-cloudflare` provides the `uploader` and `deployer` for your config: + +```sh +npm install -D @wvb/remote-cloudflare +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { cloudflareRemote } from '@wvb/remote-cloudflare'; + +export default defineConfig({ + remote: { + endpoint: 'https://updates.example.com', + ...cloudflareRemote({ + accountId: '', + bucket: 'webview-bundle', + kvNamespaceId: '', + }), + }, +}); +``` + +The uploader writes to R2 over the S3-compatible API, so pass R2 access keys through its `s3ClientConfig.credentials`; the deployer updates KV with a Cloudflare API token. See the [Cloudflare client reference](/docs/references/api/remotes/remote-cloudflare) for the full config. + + + The Worker reads an R2 binding named `BUCKET` and a KV binding named `KV`. Match those names in + your `wrangler.jsonc`. + diff --git a/content/docs/guide/remote/providers/local.ko.mdx b/content/docs/guide/remote/providers/local.ko.mdx new file mode 100644 index 0000000..858d8a6 --- /dev/null +++ b/content/docs/guide/remote/providers/local.ko.mdx @@ -0,0 +1,62 @@ +--- +title: Local +description: 개발과 테스트를 위해 로컬 디렉터리에서 번들을 제공합니다. +--- + +로컬 프로바이더는 디렉터리를 기반으로 내 컴퓨터에서 리모트를 실행합니다. 호스팅형 프로바이더를 연동하기 전에 업로드 → 배포 → 다운로드 흐름 전체를 오프라인에서 실행해 볼 때 사용합니다. + +## 서버 실행 + +가장 빠른 방법은 코드가 필요 없는 CLI입니다. 서버가 함께 제공됩니다. + +```sh +wvb remote local +``` + +`~/.wvb/local`을 `http://localhost:4313`에서 제공합니다. 플래그(`--base-dir`, `--port`, `--allow-other-versions`)는 [`wvb remote local`](/docs/references/cli/remote)을 참고하세요. + +서버를 직접 임베드하려면 `@wvb/remote-local-provider`이 [Hono](https://hono.dev) 앱을 내보냅니다. 아무 Hono 어댑터로 실행하세요. + +```sh +npm install @wvb/remote-local-provider @hono/node-server +``` + +```ts title="server.ts" +import { serve } from '@hono/node-server'; +import { wvbRemote } from '@wvb/remote-local-provider'; + +const app = wvbRemote({ baseDir: '~/.wvb/local' }); +serve({ fetch: app.fetch, port: 4313 }); +``` + +| Option | Type | Default | Description | +| -------------------- | --------- | -------------- | ------------------------------------------------- | +| `baseDir` | `string` | `~/.wvb/local` | 번들과 배포 정보를 저장하는 디렉터리. | +| `allowOtherVersions` | `boolean` | `false` | `GET /bundles/{name}/{version}`로 특정 버전 제공. | + +## 게시하기 + +`@wvb/remote-local`은 서버가 읽는 디렉터리에 그대로 기록하는 `uploader`과 `deployer`을 설정용으로 제공합니다. + +```sh +npm install -D @wvb/remote-local +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { localRemote } from '@wvb/remote-local'; + +export default defineConfig({ + remote: { + endpoint: 'http://localhost:4313', + ...localRemote({ baseDir: '~/.wvb/local' }), + }, +}); +``` + +`localRemote()`은 `{ uploader, deployer }`을 반환합니다. 이를 `remote`에 펼쳐 넣으면 `wvb upload`과 `wvb deploy`이 로컬 서버가 제공할 수 있는 번들을 기록합니다. + + + 서버와 클라이언트가 같은 `baseDir`을 가리키게 하세요. [CLI + 가이드](/docs/guide/remote/using-cli)에서 이 프로바이더를 처음부터 끝까지 사용합니다. + diff --git a/content/docs/guide/remote/providers/local.mdx b/content/docs/guide/remote/providers/local.mdx new file mode 100644 index 0000000..244d39a --- /dev/null +++ b/content/docs/guide/remote/providers/local.mdx @@ -0,0 +1,62 @@ +--- +title: Local +description: Serve bundles from a local directory for development and testing. +--- + +The local provider runs a remote on your machine, backed by a directory. Use it to exercise the full upload → deploy → download loop offline before wiring in a hosted provider. + +## Run the server + +The quickest way needs no code — the CLI ships the server: + +```sh +wvb remote local +``` + +This serves `~/.wvb/local` on `http://localhost:4313`. See [`wvb remote local`](/docs/references/cli/remote) for its flags (`--base-dir`, `--port`, `--allow-other-versions`). + +To embed the server yourself, `@wvb/remote-local-provider` exports a [Hono](https://hono.dev) app. Serve it with any Hono adapter: + +```sh +npm install @wvb/remote-local-provider @hono/node-server +``` + +```ts title="server.ts" +import { serve } from '@hono/node-server'; +import { wvbRemote } from '@wvb/remote-local-provider'; + +const app = wvbRemote({ baseDir: '~/.wvb/local' }); +serve({ fetch: app.fetch, port: 4313 }); +``` + +| Option | Type | Default | Description | +| -------------------- | --------- | -------------- | --------------------------------------------------------- | +| `baseDir` | `string` | `~/.wvb/local` | Directory that stores bundles and deployments. | +| `allowOtherVersions` | `boolean` | `false` | Serve exact versions via `GET /bundles/{name}/{version}`. | + +## Publish to it + +`@wvb/remote-local` provides the `uploader` and `deployer` for your config, writing to the same directory the server reads. + +```sh +npm install -D @wvb/remote-local +``` + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { localRemote } from '@wvb/remote-local'; + +export default defineConfig({ + remote: { + endpoint: 'http://localhost:4313', + ...localRemote({ baseDir: '~/.wvb/local' }), + }, +}); +``` + +`localRemote()` returns `{ uploader, deployer }`. Spread it into `remote` so `wvb upload` and `wvb deploy` write bundles the local server can serve. + + + Point the server and the client at the same `baseDir`. The [CLI + walkthrough](/docs/guide/remote/using-cli) uses this provider end to end. + diff --git a/content/docs/guide/remote/providers/meta.json b/content/docs/guide/remote/providers/meta.json new file mode 100644 index 0000000..3545a1e --- /dev/null +++ b/content/docs/guide/remote/providers/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Providers", + "pages": ["aws", "cloudflare", "local"] +} diff --git a/content/docs/guide/remote/providers/meta.ko.json b/content/docs/guide/remote/providers/meta.ko.json new file mode 100644 index 0000000..152646c --- /dev/null +++ b/content/docs/guide/remote/providers/meta.ko.json @@ -0,0 +1,4 @@ +{ + "title": "프로바이더", + "pages": ["aws", "cloudflare", "local"] +} diff --git a/content/docs/guide/remote/using-cli.ko.mdx b/content/docs/guide/remote/using-cli.ko.mdx new file mode 100644 index 0000000..1d586e5 --- /dev/null +++ b/content/docs/guide/remote/using-cli.ko.mdx @@ -0,0 +1,59 @@ +--- +title: CLI 사용하기 +description: 로컬 리모트를 실행하고, 버전을 게시하고, 다시 다운로드하는 전체 업데이트 흐름을 터미널에서 실행합니다. +--- + +`wvb` CLI는 리모트 작업 전체를 처리합니다. 로컬 서버를 실행하고, 번들을 게시하고, 다시 다운로드할 수 있습니다. 이 가이드는 호스팅형 [프로바이더](/docs/guide/remote/providers/local)를 배포하기 전에 전체 흐름을 로컬에서 실행해 봅니다. + +## 리모트 설정 + +설정에서 리모트를 지정하고 로컬 `uploader`/`deployer`을 넣습니다. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { localRemote } from '@wvb/remote-local'; + +export default defineConfig({ + remote: { + endpoint: 'http://localhost:4313', + integrity: { algorithm: 'sha256' }, + ...localRemote({ baseDir: '.wvb/local' }), + }, +}); +``` + +`endpoint`은 다운로드를 읽어오는 위치이며, `uploader`과 `deployer`은 `wvb upload`·`wvb deploy` 명령이 게시할 때 사용합니다. 모든 필드는 [리모트 설정](/docs/references/configuration/remote)을 참고하세요. + +## 로컬 리모트 실행 + +같은 디렉터리를 사용하는 서버를 실행하고, 별도 터미널에서 켜 둡니다. + +```sh +wvb remote local --base-dir .wvb/local +``` + +`http://localhost:4313`에서 제공합니다. 플래그는 [`wvb remote local`](/docs/references/cli/remote)을 참고하세요. + +## 버전 게시 + +패킹, 업로드, 배포를 한 명령으로 실행합니다. + +```sh +wvb upload app --version 1.0.0 --deploy +``` + +`--deploy` 없이 실행하면 버전은 스테이징만 됩니다. 현재 버전으로 만들려면 [`wvb deploy app --version 1.0.0`](/docs/references/cli/remote-deploy)을 실행하세요. 채널에 게시하려면 `--channel beta`도 함께 사용합니다. + +## 조회와 다운로드 + +리모트가 제공하는 내용을 확인합니다. + +```sh +wvb remote current app # 현재 버전과 메타데이터 +wvb remote list # 리모트의 모든 번들 +wvb download app # 현재 .wvb을 디스크에 다운로드 +``` + +## 앱에 설치 + +CLI 다운로드는 리모트가 제공하는 내용을 확인하는 용도이며, 실제 앱은 CLI가 아니라 업데이터로 업데이트를 설치합니다. 앱의 리모트를 같은 엔드포인트(`http://localhost:4313`)로 지정한 뒤 확인 → 다운로드 → 설치 흐름을 실행하세요. 클라이언트 API는 [Over-The-Air](/docs/guide/core-concepts/over-the-air) 문서를, 연동 방법은 **네이티브** 섹션의 플랫폼별 가이드를 참고하세요. diff --git a/content/docs/guide/remote/using-cli.mdx b/content/docs/guide/remote/using-cli.mdx new file mode 100644 index 0000000..b20374e --- /dev/null +++ b/content/docs/guide/remote/using-cli.mdx @@ -0,0 +1,59 @@ +--- +title: Using the CLI +description: Run a local remote, publish a version, and download it — the whole update loop from the terminal. +--- + +The `wvb` CLI drives the entire remote workflow: run a local server, publish bundles, and download them back. This walkthrough exercises the full loop on your machine before you deploy a hosted [provider](/docs/guide/remote/providers/local). + +## Configure the remote + +Point your config at a remote and give it the local `uploader`/`deployer`: + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; +import { localRemote } from '@wvb/remote-local'; + +export default defineConfig({ + remote: { + endpoint: 'http://localhost:4313', + integrity: { algorithm: 'sha256' }, + ...localRemote({ baseDir: '.wvb/local' }), + }, +}); +``` + +`endpoint` is where downloads read from; `uploader` and `deployer` are how `wvb upload` and `wvb deploy` publish. See [Remote configuration](/docs/references/configuration/remote) for every field. + +## Run a local remote + +Start a server backed by the same directory, and leave it running in its own terminal: + +```sh +wvb remote local --base-dir .wvb/local +``` + +It serves `http://localhost:4313`. See [`wvb remote local`](/docs/references/cli/remote) for its flags. + +## Publish a version + +Pack, upload, and deploy in one command: + +```sh +wvb upload app --version 1.0.0 --deploy +``` + +Without `--deploy`, the version is only staged; run [`wvb deploy app --version 1.0.0`](/docs/references/cli/remote-deploy) to make it current. Add `--channel beta` to publish to a channel. + +## Inspect and download + +Check what the remote serves: + +```sh +wvb remote current app # current version and its metadata +wvb remote list # every bundle on the remote +wvb download app # download the current .wvb to disk +``` + +## Install in the app + +Downloading with the CLI verifies what the remote serves; your app installs updates through the updater, not the CLI. Point its remote at the same endpoint (`http://localhost:4313`) and run the check → download → install flow. See [Over-the-air](/docs/guide/core-concepts/over-the-air) for the client API and your platform's guide in the **Native** section for wiring it up. diff --git a/content/docs/guide/spec/errors.ko.mdx b/content/docs/guide/spec/errors.ko.mdx deleted file mode 100644 index 92f7ce5..0000000 --- a/content/docs/guide/spec/errors.ko.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: 오류 -description: Webview Bundle 원격 서버가 HTTP로 실패를 알리는 방식과, 클라이언트가 각 상태 코드를 오류로 매핑하는 방식을 설명합니다. ---- - -원격 서버는 표준 HTTP 상태 코드로 실패를 알립니다. Webview Bundle 클라이언트는 모든 `2xx` 응답을 성공으로 처리하고, 문서에 명시된 `2xx` 외의 코드를 특정 오류로 매핑합니다. 클라이언트와 업데이터가 의도대로 반응하도록 이 응답들을 정확히 구현하세요. 이 오류들이 속한 전체 요청 계약은 [Remote HTTP 명세](/docs/guide/spec)와 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. - -## 상태 코드 - -| 상태 | 의미 | 시점 | -| ------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `403 Forbidden` | 특정 버전 다운로드 비활성화 | 클라이언트가 `GET /bundles/{name}/{version}`을 요청했지만 서버가 다른 버전의 다운로드를 비활성화한 경우입니다. 클라이언트는 이를 `RemoteForbidden`으로 매핑합니다. | -| `404 Not Found` | 번들 또는 버전이 배포되지 않음 | 지정한 번들 또는 요청한 버전이 배포되지 않은 경우입니다. 클라이언트는 이를 `RemoteBundleNotFound`로 매핑합니다. | -| 그 외 `2xx` 외 코드 | 원격 HTTP 오류 | 그 밖의 모든 실패입니다. 본문은 JSON `{ "message": string }`으로 파싱되어, 상태 코드와 메시지를 담은 원격 HTTP 오류로 표면화됩니다. | - -클라이언트는 모든 `2xx` 상태를 성공으로 처리합니다. 따라서 원격 서버는 실제 실패에만 `2xx` 외의 코드를 사용해야 합니다. - -## Forbidden (403) - -`403`은 특정 버전 다운로드가 비활성화되었음을 알리는 의도적인 신호입니다. 프로바이더는 이를 `allowOtherVersions` 옵션으로 노출하며, 기본값은 `false`입니다. 비활성화된 동안에는 해당 버전이 서버에 존재하더라도 `GET /bundles/{name}/{version}`이 `403`을 반환합니다. 현재 버전 경로(`GET /bundles/{name}`과 `HEAD /bundles/{name}`)는 영향을 받지 않습니다. - -```http -HTTP/1.1 403 Forbidden -``` - -클라이언트는 이 상태에 대해 `RemoteForbidden`을 발생시킵니다. - -## Not Found (404) - -`404`는 지정한 번들 또는 요청한 버전이 배포되지 않았음을 의미합니다. 목록 응답은 배포되지 않은 번들을 제외하므로, 그중 하나를 이어서 요청하면 `404`가 반환됩니다. - -```http -HTTP/1.1 404 Not Found -``` - -클라이언트는 이 상태에 대해 `RemoteBundleNotFound`를 발생시킵니다. - -## 그 밖의 오류 - -그 밖의 `2xx` 외 상태에는 `{ "message": string }` 형태의 JSON 본문을 반환하세요. 클라이언트는 이 본문을 파싱하여, 상태 코드와 메시지를 모두 담은 원격 HTTP 오류를 발생시킵니다. - -```http -HTTP/1.1 500 Internal Server Error -Content-Type: application/json -``` - -```json -{ "message": "failed to read bundle from storage" } -``` - - - 이 응답들에는 `{ "message": string }` 본문을 보내세요. 클라이언트가 표면화하는 값이 바로 이 `message`이므로, - 실패를 진단할 수 있을 만큼 설명적으로 작성하세요. - - -## 필수 헤더 누락 - -성공적인 다운로드나 메타데이터 응답이라도 필수 헤더는 반드시 담고 있어야 합니다. 클라이언트는 `Webview-Bundle-Name` 또는 `Webview-Bundle-Version`이 빠진 `2xx` 응답을 거부하며, 이를 전송 오류가 아니라 클라이언트 쪽의 잘못된 번들로 처리합니다. 이는 서버가 반환하는 상태가 아니라 클라이언트 쪽 실패이므로, 원격 서버는 다운로드와 메타데이터 응답에 두 헤더를 항상 설정해야 합니다. 전체 헤더 목록은 [GET /bundles/{name}](/docs/guide/spec/get-current)과 [HEAD /bundles/{name}](/docs/guide/spec/head-current)을 참고하세요. - -## 다음으로 볼 내용 - - - - - - - - - diff --git a/content/docs/guide/spec/errors.mdx b/content/docs/guide/spec/errors.mdx deleted file mode 100644 index 0c37035..0000000 --- a/content/docs/guide/spec/errors.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Errors -description: How the Webview Bundle remote signals failures over HTTP and how the client maps each status to an error. ---- - -A remote signals failure through standard HTTP status codes. The Webview Bundle client treats any `2xx` response as success and maps the documented non-`2xx` codes to specific errors. Implement these responses exactly so the client and updater react the way they should. For the full request contract these errors sit alongside, see the [Remote HTTP Spec](/docs/guide/spec) and [Building a remote](/docs/guide/remote). - -## Status codes - -| Status | Meaning | When | -| --------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `403 Forbidden` | Version-specific download disabled | A client requests `GET /bundles/{name}/{version}` but the server disables other-version downloads. The client maps this to `RemoteForbidden`. | -| `404 Not Found` | Bundle or version not deployed | The named bundle or the requested version is not deployed. The client maps this to `RemoteBundleNotFound`. | -| Other non-`2xx` | Remote HTTP error | Any other failure. The body is parsed as JSON `{ "message": string }` and surfaced as a remote HTTP error carrying the status and message. | - -The client treats every `2xx` status as success, so a remote should reserve non-`2xx` codes for genuine failures. - -## Forbidden (403) - -`403` is the deliberate signal that a version-specific download is disabled. Providers expose this as an `allowOtherVersions` option, which defaults to `false`. While disabled, `GET /bundles/{name}/{version}` returns `403` even when that version exists on the server. The current-version routes (`GET /bundles/{name}` and `HEAD /bundles/{name}`) are unaffected. - -```http -HTTP/1.1 403 Forbidden -``` - -The client raises `RemoteForbidden` for this status. - -## Not Found (404) - -`404` means the named bundle or the requested version is not deployed. List responses exclude undeployed bundles, so a follow-up request for one of them returns `404`. - -```http -HTTP/1.1 404 Not Found -``` - -The client raises `RemoteBundleNotFound` for this status. - -## Other errors - -For any other non-`2xx` status, return a JSON body of the shape `{ "message": string }`. The client parses this body and raises a remote HTTP error that carries both the status and the message. - -```http -HTTP/1.1 500 Internal Server Error -Content-Type: application/json -``` - -```json -{ "message": "failed to read bundle from storage" } -``` - - - Send a `{ "message": string }` body on these responses. The `message` is what the client surfaces, - so make it descriptive enough to diagnose the failure. - - -## Missing required headers - -A successful download or metadata response must still carry the required headers. The client rejects a `2xx` response that omits `Webview-Bundle-Name` or `Webview-Bundle-Version`, treating it as an invalid bundle on the client side rather than a transport error. This is a client-side failure, not a status the server returns, so a remote must always set both headers on download and metadata responses. See [GET /bundles/{name}](/docs/guide/spec/get-current) and [HEAD /bundles/{name}](/docs/guide/spec/head-current) for the full header set. - -## Where to go next - - - - - - - - - diff --git a/content/docs/guide/spec/index.ko.mdx b/content/docs/guide/spec/index.ko.mdx deleted file mode 100644 index e6027ed..0000000 --- a/content/docs/guide/spec/index.ko.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: 원격 HTTP 명세 -description: Webview Bundle 클라이언트와 업데이터가 번들을 나열하고 조회하고 다운로드할 수 있도록 서버가 구현하는 HTTP 계약입니다. ---- - -여기서 설명하는 엔드포인트를 구현하는 HTTP 서버는 모두 유효한 Webview Bundle 원격입니다. 클라이언트와 업데이터는 이 계약을 통해 배포된 번들을 나열하고, 번들의 현재 메타데이터를 읽고, 번들 바이트를 다운로드합니다. 직접 구현하거나, 이미 이 계약을 구현한 프로바이더를 사용하세요. [Local](/docs/guide/providers/local), [AWS](/docs/guide/providers/aws), [Cloudflare](/docs/guide/providers/cloudflare) 프로바이더는 모두 정확히 이 계약을 제공합니다. 서버를 구축하고 프로바이더를 선택하려면 [원격 구축](/docs/guide/remote)을 참고하세요. 이 호출들을 이끄는 업데이트 흐름의 개념은 [원격 번들 및 업데이트](/docs/guide/remote-bundles)를 참고하세요. - -## Base URL - -원격은 단일 `endpoint` base URL로 설정합니다. 모든 라우트는 이 base 아래의 경로이므로, `endpoint`가 `https://cdn.example.com`이면 `GET https://cdn.example.com/bundles`, `HEAD https://cdn.example.com/bundles/{name}` 등을 제공합니다. 클라이언트는 설정된 `endpoint`에 라우트 경로와 쿼리 문자열을 결합해 각 요청 URL을 만듭니다. - -## 규약 - -이 규약은 모든 엔드포인트에 공통으로 적용됩니다. 각 엔드포인트 페이지는 해당 라우트에 고유한 내용만 반복합니다. - -### 응답 헤더 - -메타데이터는 다운로드 및 메타데이터 응답의 `Webview-Bundle-*` 응답 헤더에 담겨 전달됩니다. 클라이언트는 대소문자를 구분하지 않고 읽으며, 여기 표시한 것이 표준 표기입니다. - -| 헤더 | 필수 | 의미 | -| -------------------------- | ------ | -------------------------------- | -| `Webview-Bundle-Name` | 예 | 번들 이름 | -| `Webview-Bundle-Version` | 예 | 번들 버전 | -| `Webview-Bundle-Integrity` | 아니오 | 무결성 문자열 `":"` | -| `Webview-Bundle-Signature` | 아니오 | Base64 서명 | -| `ETag` | 아니오 | 표준 검증자 | -| `Last-Modified` | 아니오 | 표준 검증자 | - -클라이언트는 `Webview-Bundle-Name` 또는 `Webview-Bundle-Version`이 없는 응답을 모두 거부합니다. - -### Content type - -다운로드 응답은 `Content-Type: application/webview-bundle`을 설정합니다. 목록 응답은 `Content-Type: application/json`을 설정합니다. - -### 채널 - -목록, 메타데이터, 현재 버전 다운로드 라우트는 선택적 `?channel=` 쿼리를 받아 해당 채널의 배포를 선택합니다. 버전별 다운로드는 채널을 받지 않습니다. 기본 배포를 대상으로 하려면 쿼리를 생략하세요. - -### 요청 타임아웃 - -클라이언트는 모든 호출에 기본 전체 요청 타임아웃 120초를 적용하므로, 서버는 그 안에 여유 있게 응답해야 합니다. - - - 120초 타임아웃은 클라이언트의 HTTP 설정에서 재정의할 수 있지만, 변경하지 않으면 기본값이 모든 - 요청에 적용됩니다. - - -## 엔드포인트 - - - - - - - - diff --git a/content/docs/guide/spec/index.mdx b/content/docs/guide/spec/index.mdx deleted file mode 100644 index a2cb3e8..0000000 --- a/content/docs/guide/spec/index.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Remote HTTP Spec -description: The HTTP contract a server implements so the Webview Bundle client and updater can list, inspect, and download bundles. ---- - -Any HTTP server that implements the endpoints described here is a valid Webview Bundle remote. The client and updater speak this contract to list deployed bundles, read a bundle's current metadata, and download bundle bytes. Implement it directly, or use a provider that already does: the [Local](/docs/guide/providers/local), [AWS](/docs/guide/providers/aws), and [Cloudflare](/docs/guide/providers/cloudflare) providers all serve this exact contract. To stand up a server and choose a provider, see [Building a remote](/docs/guide/remote); for the conceptual update flow that drives these calls, see [Remote bundles & updates](/docs/guide/remote-bundles). - -## Base URL - -A remote is configured with a single `endpoint` base URL. Every route is a path under that base, so an `endpoint` of `https://cdn.example.com` serves `GET https://cdn.example.com/bundles`, `HEAD https://cdn.example.com/bundles/{name}`, and so on. The client builds each request URL by joining the configured `endpoint` with the route path and any query string. - -## Conventions - -These conventions hold across every endpoint. Each endpoint page repeats only what is specific to its route. - -### Response headers - -Metadata travels in `Webview-Bundle-*` response headers on download and metadata responses. The client reads them case-insensitively; the canonical casing is shown here. - -| Header | Required | Meaning | -| -------------------------- | -------- | ----------------------------------- | -| `Webview-Bundle-Name` | yes | Bundle name | -| `Webview-Bundle-Version` | yes | Bundle version | -| `Webview-Bundle-Integrity` | no | Integrity string `":"` | -| `Webview-Bundle-Signature` | no | Base64 signature | -| `ETag` | no | Standard validator | -| `Last-Modified` | no | Standard validator | - -The client rejects any response that omits `Webview-Bundle-Name` or `Webview-Bundle-Version`. - -### Content type - -Download responses set `Content-Type: application/webview-bundle`. The list response sets `Content-Type: application/json`. - -### Channels - -The list, metadata, and current-download routes accept an optional `?channel=` query that selects a channel's deployment. The version-specific download does not accept a channel. Omit the query to target the default deployment. - -### Request timeout - -The client applies a default total request timeout of 120s to every call, so a server should respond well within it. - - - The 120s timeout can be overridden in the client's HTTP configuration, but the default applies to - every request unless changed. - - -## Endpoints - - - - - - - - diff --git a/content/docs/guide/spec/meta.json b/content/docs/guide/spec/meta.json deleted file mode 100644 index f8023a5..0000000 --- a/content/docs/guide/spec/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Spec", - "pages": ["index", "list-bundles", "head-current", "get-current", "get-version", "errors"] -} diff --git a/content/docs/guide/spec/meta.ko.json b/content/docs/guide/spec/meta.ko.json deleted file mode 100644 index f208535..0000000 --- a/content/docs/guide/spec/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "명세", - "pages": ["index", "list-bundles", "head-current", "get-current", "get-version", "errors"] -} diff --git a/content/docs/guide/why-webview-bundle.ko.mdx b/content/docs/guide/why-webview-bundle.ko.mdx deleted file mode 100644 index 7a29c4d..0000000 --- a/content/docs/guide/why-webview-bundle.ko.mdx +++ /dev/null @@ -1,189 +0,0 @@ ---- -title: Webview Bundle을 선택하는 이유 -description: 웹 앱을 오프라인 우선 .wvb 번들로 출시하고 무선(OTA) 업데이트로 배포하는 방법을 설명합니다. ---- - -Webview Bundle은 빌드된 웹 앱을 하나의 압축된, 무결성이 검증되는 아카이브(`.wvb`)로 묶고, 이를 커스텀 URL 스킴을 통해 웹뷰에 제공합니다. 웹뷰는 HTML, JS, CSS, 미디어를 네트워크가 아닌 로컬 저장소에서 읽습니다. 원격을 설정하면 앱이 더 새로운 번들을 무선으로 다운로드하며, 네이티브 앱스토어 출시 없이도 갱신된 코드를 배포합니다. - -팀이 Webview Bundle을 선택하는 이유는 세 가지입니다. 오프라인 우선 전달, 무선(OTA) 업데이트, 그리고 모든 웹뷰 플랫폼을 아우르는 하나의 워크플로입니다. - -## 기본으로 오프라인 우선 - -원격 URL을 사용하면 첫 화면이 네트워크를 기다려야 합니다. 캐시가 비어 있거나, 캡티브 포털에 걸리거나, 연결이 끊기면 빈 화면이 나타납니다. - -Webview Bundle은 앱과 함께 출시되는 로컬 `.wvb`에서 자산을 곧바로 제공합니다. 프로토콜은 각 요청을 번들 안의 파일로 매핑하고 그 파일을 반환합니다. - -```text -bundle://app/index.html -> index.html inside the app bundle -bundle://app/assets/x.js -> assets/x.js inside the app bundle -``` - -```ts title="번들을 로컬에서 제공" -import { BundleSource, BundleProtocol } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: '/path/to/builtin', - remoteDir: '/path/to/remote', -}); - -const protocol = new BundleProtocol(source); - -// 플랫폼 통합이 스킴 요청을 여기로 라우팅합니다. -const res = await protocol.handle('get', 'bundle://app/index.html'); -// res: { status, headers, body } -``` - -첫 화면은 절대 네트워크를 기다리지 않습니다. 지하철에서든, 비행기에서든, 세션 도중 연결이 끊겨도 앱은 로컬 번들에서 화면을 그립니다. 네트워크는 최적화 수단이 됩니다. - - - 번들 프로토콜은 `GET`과 `HEAD`를 지원하고, 각 파일에 저장된 헤더를 그대로 재현하며, 미디어에 대한 - `Range` 요청에는 `206`으로 응답합니다. 전체 요청 매핑은 [프로토콜 - 처리](/docs/guide/protocol-handling)를 참고하세요. - - -## 무선(OTA) 업데이트 - -앱스토어 출시는 느리고 심사에 막힙니다. 문구 오타 하나, 동작하지 않는 버튼 하나가 며칠에 걸친 심사에 묶일 수 있습니다. 앱 코드가 `.wvb` 안에 있기 때문에, 새 번들 버전을 배포하고 설치된 앱이 이를 무선(OTA)으로 다운로드하게 하면 문제를 고칠 수 있습니다. 네이티브 바이너리는 전혀 바뀌지 않습니다. - -두 개의 소스가 업데이트 모델을 뒷받침합니다. `builtin` 소스는 출시 시 함께 담은 읽기 전용 번들입니다. `remote` 소스는 다운로드한 번들을 담으며 우선권을 가집니다. 다운로드나 활성화가 실패하면 `builtin`으로 되돌아가므로, OTA가 기기를 동작하는 앱이 없는 상태로 만드는 일은 결코 없습니다. - -```ts title="두 소스: remote가 우선, builtin이 최후의 보루" -new BundleSource({ - builtinDir: '/path/to/builtin', // 앱에 함께 담긴 읽기 전용 - remoteDir: '/path/to/remote', // OTA 다운로드, 우선권을 가짐 -}); -``` - -업데이트는 두 개의 명시적인 단계로 실행되어, 다운로드가 절반만 된 번들이 제공되는 일이 없습니다. `download`는 원격 소스에 스테이징하고 검증합니다. `install`은 스테이징된 버전을 활성화합니다. - -```ts title="확인, 다운로드, 설치" -import { Updater } from '@wvb/node'; - -const updater = new Updater(source, remote); - -const info = await updater.getUpdate('app'); -if (info.isAvailable) { - await updater.download('app'); // 스테이징 + 검증 - await updater.install('app', info.version); // 활성화 -} -``` - -### 채널을 이용한 단계적 롤아웃 - -채널은 목록 및 다운로드 요청에 `?channel=`로 전달되는 선택적 문자열입니다. `UpdaterOptions`를 통해 전달하면 특정 기기 집단을 `beta`로 지정한 뒤 승격할 수 있습니다. 생략하면 이 파라미터는 빠지며, 하드코딩된 채널 이름은 없습니다. - -```ts title="이 기기를 beta 채널에 고정" -const updater = new Updater(source, remote, { channel: 'beta' }); -``` - -```http -GET /bundles/app?channel=beta -``` - -### 무결성과 서명을 통한 진위 확인 - -모든 다운로드는 활성화 전에 검증됩니다. 무결성은 SHA-2(`sha256` 기본값, 그 외 `sha384`, `sha512`)를 사용하며 `":"` 형식으로 직렬화됩니다. - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -``` - -기본 정책은 `optional`(값이 있으면 검증)입니다. `strict`로 설정하면 무결성을 필수로 요구합니다. 여기에 선언적 `signatureVerifier`를 추가하면 누가 게시했는지 증명할 수 있습니다. 서명은 무결성 문자열의 바이트를 대상으로 합니다. - -```ts title="무결성 요구, Ed25519 서명 검증" -const updater = new Updater(source, remote, { - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { format: 'raw', data: publicKeyBytes }, // 32바이트 Ed25519 키 - }, -}); -``` - -검증은 ECDSA(`ecdsaSecp256R1`, `ecdsaSecp384R1`), `ed25519`, RSA(`rsaPkcs1V15`, `rsaPss`)를 지원합니다. - - - 무결성은 SHA-3가 아니라 SHA-2입니다. 서명은 원시 아카이브 바이트가 아니라 무결성 문자열을 대상으로 - 하며, 무결성 값이 반드시 존재해야 합니다. 검증 흐름은 [원격 번들](/docs/guide/remote-bundles)을, - 스키마는 [원격, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. - - -## 웹 개발자를 위해 - -이미 알고 있는 방식 그대로 앱을 작성하세요. Webview Bundle은 소스가 아니라 번들러의 산출물을 대상으로 동작하므로 React, Vue, Svelte, 순수 HTML 모두 그대로 동작합니다. 커스텀 프레임워크도, 특별한 빌드 단계도 필요 없습니다. `wvb pack`을 빌드 디렉터리로 향하게 하세요. - -```sh title="빌드 산출물을 하나의 .wvb로 패킹" -npm install --save-dev @wvb/cli -npx wvb pack ./dist -``` - -패킹된 파일은 기본적으로 `.wvb/`에 생성되며 `.wvb` 확장자는 자동으로 붙습니다. `outFile` 필드(단일 경로)로 이를 재정의할 수 있습니다. - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - outFile: '.wvb/app', - }, -}); -``` - -웹뷰 안에서 코드는 `@wvb/bridge`를 통해 네이티브 호스트와 통신합니다. 하나의 `invoke()` API가 소스, 원격, 업데이터 명령을 노출하고, 그 아래에서 Electron, Tauri, Android, iOS에 맞는 전송 방식을 선택합니다. 같은 업데이트 코드가 어디서나 실행됩니다. - -```ts title="웹 코드에서 업데이트 실행" -import { updater } from '@wvb/bridge'; - -const update = await updater.getUpdate('app'); -if (update.isAvailable) { - await updater.download('app'); - await updater.install('app', update.version); -} -``` - -브리지는 호출 시점에 플랫폼을 확인하므로, 직접 플랫폼별로 분기할 필요가 없습니다. - -```ts title="플랫폼이 자동으로 감지됩니다" -import { platform } from '@wvb/bridge'; - -platform.type; // 'electron' | 'tauri' | 'android' | 'ios' -``` - -### 다른 방식과의 비교 - -흔히 쓰이는 두 가지 대안이 같은 문제의 일부를 해결합니다. - -- **플랫폼별 네이티브 자산 번들링.** 웹 빌드를 각 셸에 임베드하고, 서빙 및 업데이트 로직을 플랫폼마다 한 번씩 작성하는 방식입니다. 동작은 하지만 툴체인, 자산 포맷, 업데이트 코드가 플랫폼마다 다르고 그 모두를 직접 유지보수해야 합니다. Webview Bundle은 공유된 Rust 코어를 기반으로 Electron, Tauri, Android, iOS 전반에 하나의 `.wvb`와 하나의 사고 모델을 제공합니다. -- **CodePush 방식의 OTA.** 호스팅형 OTA 서비스는 스토어 심사 없이 JS 업데이트를 배포합니다. 원격 소스와 같은 이점을 줍니다. 다만 절충점이 다릅니다. Webview Bundle은 관리형 서드파티 서비스가 아니라, builtin 폴백, 채널 롤아웃, 무결성과 선택적 서명을 갖춘, 직접 호스팅하는 검증 가능한 번들 포맷입니다. 서버와 아티팩트를 여러분이 소유합니다. - -솔직한 절충점은 이렇습니다. 번들 포맷과 패킹 단계를 도입하고 원격을 직접 운영해야 합니다. 그 대가로 오프라인 우선 전달, 스토어 심사 없는 OTA, 그리고 플랫폼을 추가해도 변하지 않는 하나의 워크플로를 얻습니다. - - - Android와 iOS는 구현되어 출시된 상태입니다. iOS 최소 버전은 iOS 16입니다. Deno Desktop은 - 실험적입니다. 현재 상태는 [플랫폼 지원](/docs/guide/platform-support)을 참고하세요. - - -## 다음으로 볼 내용 - - - - - - - diff --git a/content/docs/guide/why-webview-bundle.mdx b/content/docs/guide/why-webview-bundle.mdx deleted file mode 100644 index 332774d..0000000 --- a/content/docs/guide/why-webview-bundle.mdx +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Why Webview Bundle? -description: The case for shipping your web app as an offline-first .wvb bundle with over-the-air updates. ---- - -Webview Bundle packs your built web app into one compressed, integrity-checked archive (`.wvb`) and serves it to the webview over a custom URL scheme. The webview reads HTML, JS, CSS, and media from local storage instead of the network. Configure a remote and the app downloads newer bundles over the air, shipping updated code without a native app-store release. - -Three reasons teams reach for it: offline-first delivery, over-the-air updates, and one workflow across every webview platform. - -## Offline-first by default - -A remote URL makes first paint wait on the network. A cold cache, a captive portal, or a dropped connection becomes a blank screen. - -Webview Bundle serves your assets straight from the local `.wvb` that ships with the app. The protocol maps each request to a file inside the bundle and returns it: - -```text -bundle://app/index.html -> index.html inside the app bundle -bundle://app/assets/x.js -> assets/x.js inside the app bundle -``` - -```ts title="Serving a bundle locally" -import { BundleSource, BundleProtocol } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: '/path/to/builtin', - remoteDir: '/path/to/remote', -}); - -const protocol = new BundleProtocol(source); - -// Your platform integration routes scheme requests here. -const res = await protocol.handle('get', 'bundle://app/index.html'); -// res: { status, headers, body } -``` - -First paint never waits on the network. The app paints from the local bundle on the subway, on a plane, or mid-session when the connection drops; the network becomes an optimization. - - - The bundle protocol supports `GET` and `HEAD`, replays the headers stored for each file, and - answers `Range` requests for media with `206`. See [Protocol - handling](/docs/guide/protocol-handling) for the full request mapping. - - -## Over-the-air updates - -An app-store release is slow and gated: a copy typo or a broken button can sit behind a multi-day review. Because your app code lives in a `.wvb`, you fix it by deploying a new bundle version and letting installed apps download it over the air (OTA). The native binary never changes. - -Two sources back the update model. The `builtin` source is the read-only bundle you shipped; the `remote` source holds downloaded bundles and takes priority. A failed download or activation falls back to `builtin`, so OTA can never leave a device without a working app. - -```ts title="Two sources: remote wins, builtin is the floor" -new BundleSource({ - builtinDir: '/path/to/builtin', // shipped in the app, read-only - remoteDir: '/path/to/remote', // OTA downloads, takes priority -}); -``` - -Updates run in two explicit steps so a half-downloaded bundle is never served: `download` stages into the remote source and verifies; `install` activates the staged version. - -```ts title="Check, download, install" -import { Updater } from '@wvb/node'; - -const updater = new Updater(source, remote); - -const info = await updater.getUpdate('app'); -if (info.isAvailable) { - await updater.download('app'); // stage + verify - await updater.install('app', info.version); // activate -} -``` - -### Staged rollouts with channels - -A channel is an optional string sent as `?channel=` on list and download requests. Pass it through `UpdaterOptions` to point a device cohort at `beta`, then promote. Omit it and the parameter is dropped — there is no hardcoded channel name. - -```ts title="Pin this device to the beta channel" -const updater = new Updater(source, remote, { channel: 'beta' }); -``` - -```http -GET /bundles/app?channel=beta -``` - -### Authenticity with integrity and signatures - -Every download is verified before activation. Integrity uses SHA-2 (`sha256` default, plus `sha384` and `sha512`), serialized as `":"`: - -```text -sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= -``` - -The default policy is `optional` (verify if present). Set `strict` to require integrity, and add a declarative `signatureVerifier` to prove who published — the signature covers the integrity string bytes: - -```ts title="Require integrity, verify an Ed25519 signature" -const updater = new Updater(source, remote, { - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { format: 'raw', data: publicKeyBytes }, // 32-byte Ed25519 key - }, -}); -``` - -Verification supports ECDSA (`ecdsaSecp256R1`, `ecdsaSecp384R1`), `ed25519`, and RSA (`rsaPkcs1V15`, `rsaPss`). - - - Integrity is SHA-2, not SHA-3. The signature covers the integrity string, not the raw archive - bytes, and requires an integrity value to be present. See [Remote - bundles](/docs/guide/remote-bundles) for the verification flow and [Remote, integrity & signature - config](/docs/config/remote) for the schema. - - -## For web developers - -Keep writing the app you already know. Webview Bundle operates on your bundler's output, not your source, so React, Vue, Svelte, or plain HTML all work — no custom framework, no special build step. Point `wvb pack` at your build directory: - -```sh title="Pack your build output into one .wvb" -npm install --save-dev @wvb/cli -npx wvb pack ./dist -``` - -The packed file defaults to `.wvb/`; the `.wvb` extension is appended for you. Override it with the `outFile` field (a single path): - -```ts title="wvb.config.ts" -import { defineConfig } from '@wvb/config'; - -export default defineConfig({ - pack: { - outFile: '.wvb/app', - }, -}); -``` - -Inside the webview, your code talks to the native host through `@wvb/bridge`. One `invoke()` API exposes the source, remote, and updater commands and picks the transport for Electron, Tauri, Android, or iOS underneath. The same update code runs everywhere: - -```ts title="Driving updates from web code" -import { updater } from '@wvb/bridge'; - -const update = await updater.getUpdate('app'); -if (update.isAvailable) { - await updater.download('app'); - await updater.install('app', update.version); -} -``` - -The bridge resolves the platform at call time, so you never branch on it yourself: - -```ts title="Platform is detected for you" -import { platform } from '@wvb/bridge'; - -platform.type; // 'electron' | 'tauri' | 'android' | 'ios' -``` - -### How this compares - -Two common alternatives solve part of the same problem: - -- **Per-platform native asset bundling.** Embed your web build in each shell and write serving and update logic once per platform. It works, but the toolchain, asset format, and update code differ on each platform and you maintain all of them. Webview Bundle gives you one `.wvb` and one mental model across Electron, Tauri, Android, and iOS, backed by a shared Rust core. -- **CodePush-style OTA.** Hosted OTA services ship JS updates without store review — the same payoff as the remote source. The tradeoff differs: Webview Bundle is a verifiable bundle format you host yourself, with a builtin fallback, channel rollouts, and integrity plus optional signatures, rather than a managed third-party service. You own the server and the artifact. - -The honest tradeoff: you adopt a bundle format and a pack step, and you run the remote. In return you get offline-first delivery, OTA without store review, and one workflow that does not change as you add platforms. - - - Android and iOS are implemented and shipping. The iOS minimum is iOS 16. Deno Desktop is - experimental. See [Platform support](/docs/guide/platform-support) for current status. - - -## Where to go next - - - - - - - diff --git a/content/docs/meta.json b/content/docs/meta.json index 5c003f3..9501dae 100644 --- a/content/docs/meta.json +++ b/content/docs/meta.json @@ -1,3 +1,3 @@ { - "pages": ["guide", "references", "config", "changelog"] + "pages": ["guide", "references", "changelog"] } diff --git a/content/docs/meta.ko.json b/content/docs/meta.ko.json index 5c003f3..9501dae 100644 --- a/content/docs/meta.ko.json +++ b/content/docs/meta.ko.json @@ -1,3 +1,3 @@ { - "pages": ["guide", "references", "config", "changelog"] + "pages": ["guide", "references", "changelog"] } diff --git a/content/docs/references/api/bridge/bridge-error-code.mdx b/content/docs/references/api/bridge/bridge-error-code.mdx new file mode 100644 index 0000000..28d0785 --- /dev/null +++ b/content/docs/references/api/bridge/bridge-error-code.mdx @@ -0,0 +1,48 @@ +--- +title: 'BridgeErrorCode' +description: 'BridgeErrorCode — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/error.ts#L9) + +## Properties + + + +--- + +## Type + +```ts +type BridgeErrorCode = (typeof BridgeErrorCode)[keyof typeof BridgeErrorCode]; +``` diff --git a/content/docs/references/api/bridge/bridge-error-data.mdx b/content/docs/references/api/bridge/bridge-error-data.mdx new file mode 100644 index 0000000..23488bf --- /dev/null +++ b/content/docs/references/api/bridge/bridge-error-data.mdx @@ -0,0 +1,16 @@ +--- +title: 'BridgeErrorData' +description: 'Bridge error data that respond from native.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bridge error data that respond from native. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/error.ts#L4) + +## Properties + + diff --git a/content/docs/references/api/bridge/bridge-error.mdx b/content/docs/references/api/bridge/bridge-error.mdx new file mode 100644 index 0000000..b826849 --- /dev/null +++ b/content/docs/references/api/bridge/bridge-error.mdx @@ -0,0 +1,52 @@ +--- +title: 'BridgeError' +description: 'Error thrown by @wvb/bridge when an invoke() command rejects, regardless of platform.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Error thrown by `@wvb/bridge` when an `invoke()` command rejects, regardless of +platform. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/error.ts#L28) + +**Extends:** `Error` + +## Constructor + +```ts +new BridgeError(data: BridgeErrorData); +``` + + + +## Properties + + + +## Methods + + BridgeError', + required: true, + parameters: [{ name: 'value', description: 'unknown' }], + returns: 'BridgeError', + }, + of: { + type: "(code: string, message: string = '') => BridgeError", + required: true, + parameters: [ + { name: 'code', description: 'string' }, + { name: 'message', description: 'string' }, + ], + returns: 'BridgeError', + }, + }} +/> diff --git a/content/docs/references/api/bridge/bundle-manifest-metadata.mdx b/content/docs/references/api/bridge/bundle-manifest-metadata.mdx new file mode 100644 index 0000000..2eefc8f --- /dev/null +++ b/content/docs/references/api/bridge/bundle-manifest-metadata.mdx @@ -0,0 +1,33 @@ +--- +title: 'BundleManifestMetadata' +description: 'Bundle manifest metadata.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle manifest metadata. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L12) + +## Properties + + diff --git a/content/docs/references/api/bridge/bundle-source-type.mdx b/content/docs/references/api/bridge/bundle-source-type.mdx new file mode 100644 index 0000000..6d218f4 --- /dev/null +++ b/content/docs/references/api/bridge/bundle-source-type.mdx @@ -0,0 +1,17 @@ +--- +title: 'BundleSourceType' +description: 'BundleSourceType — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +- builtin: Built-in bundle which is included in the application. +- remote: Remote bundle which is downloaded from a remote server. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L7) + +## Type + +```ts +type BundleSourceType = 'builtin' | 'remote'; +``` diff --git a/content/docs/references/api/bridge/bundle-source-version.mdx b/content/docs/references/api/bridge/bundle-source-version.mdx new file mode 100644 index 0000000..8d03036 --- /dev/null +++ b/content/docs/references/api/bridge/bundle-source-version.mdx @@ -0,0 +1,23 @@ +--- +title: 'BundleSourceVersion' +description: 'Bundle source version.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle source version. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L50) + +## Properties + + diff --git a/content/docs/references/api/bridge/bundle-update-info.mdx b/content/docs/references/api/bridge/bundle-update-info.mdx new file mode 100644 index 0000000..ccb97ee --- /dev/null +++ b/content/docs/references/api/bridge/bundle-update-info.mdx @@ -0,0 +1,49 @@ +--- +title: 'BundleUpdateInfo' +description: 'Information of an available bundle update.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Information of an available bundle update. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/updater.ts#L9) + +## Properties + + diff --git a/content/docs/references/api/bridge/index.mdx b/content/docs/references/api/bridge/index.mdx new file mode 100644 index 0000000..dbdf4e1 --- /dev/null +++ b/content/docs/references/api/bridge/index.mdx @@ -0,0 +1,94 @@ +--- +title: '@wvb/bridge' +description: 'WebView bridge' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Functions + + + + + + + +## Interfaces + + + + + + + + + + + + + + + + +## Type Aliases + + + + + + + +## Variables + + + + + + + diff --git a/content/docs/references/api/bridge/invoke-params.mdx b/content/docs/references/api/bridge/invoke-params.mdx new file mode 100644 index 0000000..c29461c --- /dev/null +++ b/content/docs/references/api/bridge/invoke-params.mdx @@ -0,0 +1,14 @@ +--- +title: 'InvokeParams' +description: 'InvokeParams — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/invoke.ts#L15) + +## Index Signature + +```ts +[key: string | number]: any; +``` diff --git a/content/docs/references/api/bridge/invoke.mdx b/content/docs/references/api/bridge/invoke.mdx new file mode 100644 index 0000000..ea6c1e7 --- /dev/null +++ b/content/docs/references/api/bridge/invoke.mdx @@ -0,0 +1,30 @@ +--- +title: 'invoke' +description: 'Invokes a native bridge command and resolves its result.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Invokes a native bridge command and resolves its result. +Before using the bridge, make sure native supports webview-bundle. + +Throws a [`BridgeError`](/docs/references/api/bridge/bridge-error) on failure. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/invoke.ts#L27) + +```ts +function invoke(name: string, params?: InvokeParams): Promise; +``` + +## Parameters + + + +## Returns + +`Promise` diff --git a/content/docs/references/api/bridge/is-bridge-error-data.mdx b/content/docs/references/api/bridge/is-bridge-error-data.mdx new file mode 100644 index 0000000..d8eefe0 --- /dev/null +++ b/content/docs/references/api/bridge/is-bridge-error-data.mdx @@ -0,0 +1,20 @@ +--- +title: 'isBridgeErrorData' +description: 'isBridgeErrorData — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/error.ts#L62) + +```ts +function isBridgeErrorData(value: unknown): value is BridgeErrorData; +``` + +## Parameters + + + +## Returns + +`value is BridgeErrorData` diff --git a/content/docs/references/api/bridge/is-bridge-error.mdx b/content/docs/references/api/bridge/is-bridge-error.mdx new file mode 100644 index 0000000..2d0dfbc --- /dev/null +++ b/content/docs/references/api/bridge/is-bridge-error.mdx @@ -0,0 +1,20 @@ +--- +title: 'isBridgeError' +description: 'isBridgeError — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/error.ts#L58) + +```ts +function isBridgeError(e: unknown): e is BridgeError; +``` + +## Parameters + + + +## Returns + +`e is BridgeError` diff --git a/content/docs/references/api/bridge/list-bundle-item.mdx b/content/docs/references/api/bridge/list-bundle-item.mdx new file mode 100644 index 0000000..613ffac --- /dev/null +++ b/content/docs/references/api/bridge/list-bundle-item.mdx @@ -0,0 +1,19 @@ +--- +title: 'ListBundleItem' +description: 'List item of bundles.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +List item of bundles. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L40) + +## Properties + + diff --git a/content/docs/references/api/bridge/list-bundle-manifest-item.mdx b/content/docs/references/api/bridge/list-bundle-manifest-item.mdx new file mode 100644 index 0000000..d72ccec --- /dev/null +++ b/content/docs/references/api/bridge/list-bundle-manifest-item.mdx @@ -0,0 +1,29 @@ +--- +title: 'ListBundleManifestItem' +description: 'List item of bundle manifests.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +List item of bundle manifests. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L26) + +## Properties + + diff --git a/content/docs/references/api/bridge/list-remote-bundle-info.mdx b/content/docs/references/api/bridge/list-remote-bundle-info.mdx new file mode 100644 index 0000000..96e4328 --- /dev/null +++ b/content/docs/references/api/bridge/list-remote-bundle-info.mdx @@ -0,0 +1,19 @@ +--- +title: 'ListRemoteBundleInfo' +description: 'List item of remote bundles.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +List item of remote bundles. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/remote.ts#L4) + +## Properties + + diff --git a/content/docs/references/api/bridge/meta.json b/content/docs/references/api/bridge/meta.json new file mode 100644 index 0000000..8c58dfb --- /dev/null +++ b/content/docs/references/api/bridge/meta.json @@ -0,0 +1,33 @@ +{ + "title": "@wvb/bridge", + "pages": [ + "---Classes---", + "bridge-error", + "---Functions---", + "invoke", + "is-bridge-error", + "is-bridge-error-data", + "---Interfaces---", + "bridge-error-data", + "bundle-manifest-metadata", + "bundle-source-version", + "bundle-update-info", + "invoke-params", + "list-bundle-item", + "list-bundle-manifest-item", + "list-remote-bundle-info", + "remote-api", + "remote-bundle-info", + "source-api", + "updater-api", + "---Type Aliases---", + "bridge-error-code", + "bundle-source-type", + "platform-type", + "---Variables---", + "platform", + "remote", + "source", + "updater" + ] +} diff --git a/content/docs/references/api/bridge/meta.ko.json b/content/docs/references/api/bridge/meta.ko.json new file mode 100644 index 0000000..3947637 --- /dev/null +++ b/content/docs/references/api/bridge/meta.ko.json @@ -0,0 +1,33 @@ +{ + "title": "@wvb/bridge", + "pages": [ + "---클래스---", + "bridge-error", + "---함수---", + "invoke", + "is-bridge-error", + "is-bridge-error-data", + "---인터페이스---", + "bridge-error-data", + "bundle-manifest-metadata", + "bundle-source-version", + "bundle-update-info", + "invoke-params", + "list-bundle-item", + "list-bundle-manifest-item", + "list-remote-bundle-info", + "remote-api", + "remote-bundle-info", + "source-api", + "updater-api", + "---타입---", + "bridge-error-code", + "bundle-source-type", + "platform-type", + "---변수---", + "platform", + "remote", + "source", + "updater" + ] +} diff --git a/content/docs/references/api/bridge/platform-type.mdx b/content/docs/references/api/bridge/platform-type.mdx new file mode 100644 index 0000000..9114235 --- /dev/null +++ b/content/docs/references/api/bridge/platform-type.mdx @@ -0,0 +1,14 @@ +--- +title: 'PlatformType' +description: 'PlatformType — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/platform.ts#L5) + +## Type + +```ts +type PlatformType = 'electron' | 'tauri' | 'deno' | 'android' | 'ios'; +``` diff --git a/content/docs/references/api/bridge/platform.mdx b/content/docs/references/api/bridge/platform.mdx new file mode 100644 index 0000000..6b16a0f --- /dev/null +++ b/content/docs/references/api/bridge/platform.mdx @@ -0,0 +1,21 @@ +--- +title: 'platform' +description: 'platform — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/platform.ts#L74) + +## Properties + + diff --git a/content/docs/references/api/bridge/remote-api.mdx b/content/docs/references/api/bridge/remote-api.mdx new file mode 100644 index 0000000..c3e6333 --- /dev/null +++ b/content/docs/references/api/bridge/remote-api.mdx @@ -0,0 +1,48 @@ +--- +title: 'RemoteApi' +description: 'RemoteApi — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/remote.ts#L43) + +## Methods + + Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'channel?', description: 'string' }, + ], + returns: 'Promise', + }, + downloadVersion: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + getInfo: { + type: '(bundleName: string, channel?: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'channel?', description: 'string' }, + ], + returns: 'Promise', + }, + listBundles: { + type: '(channel?: string) => Promise', + required: true, + parameters: [{ name: 'channel?', description: 'string' }], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/bridge/remote-bundle-info.mdx b/content/docs/references/api/bridge/remote-bundle-info.mdx new file mode 100644 index 0000000..7f66c2f --- /dev/null +++ b/content/docs/references/api/bridge/remote-bundle-info.mdx @@ -0,0 +1,35 @@ +--- +title: 'RemoteBundleInfo' +description: 'Information of a remote bundle.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Information of a remote bundle. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/remote.ts#L16) + +## Properties + + diff --git a/content/docs/references/api/bridge/remote.mdx b/content/docs/references/api/bridge/remote.mdx new file mode 100644 index 0000000..af7fb93 --- /dev/null +++ b/content/docs/references/api/bridge/remote.mdx @@ -0,0 +1,12 @@ +--- +title: 'remote' +description: 'remote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/remote.ts#L50) + +**Type:** [`RemoteApi`](/docs/references/api/bridge/remote-api) + +See [`RemoteApi`](/docs/references/api/bridge/remote-api) for the full member list. diff --git a/content/docs/references/api/bridge/source-api.mdx b/content/docs/references/api/bridge/source-api.mdx new file mode 100644 index 0000000..4f3065c --- /dev/null +++ b/content/docs/references/api/bridge/source-api.mdx @@ -0,0 +1,104 @@ +--- +title: 'SourceApi' +description: 'SourceApi — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L117) + +## Methods + + Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + getRemoteBundleFilepath: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + listBundles: { + type: '() => Promise', + required: true, + returns: 'Promise', + }, + loadBuiltinMetadata: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + loadRemoteMetadata: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + loadVersion: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + pruneRemoteBundles: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + remoteRetainedVersions: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + removeRemoteBundle: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + resolveFilepath: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + unloadDescriptor: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + updateVersion: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/bridge/source.mdx b/content/docs/references/api/bridge/source.mdx new file mode 100644 index 0000000..b0353b3 --- /dev/null +++ b/content/docs/references/api/bridge/source.mdx @@ -0,0 +1,12 @@ +--- +title: 'source' +description: 'source — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/source.ts#L132) + +**Type:** [`SourceApi`](/docs/references/api/bridge/source-api) + +See [`SourceApi`](/docs/references/api/bridge/source-api) for the full member list. diff --git a/content/docs/references/api/bridge/updater-api.mdx b/content/docs/references/api/bridge/updater-api.mdx new file mode 100644 index 0000000..53e827c --- /dev/null +++ b/content/docs/references/api/bridge/updater-api.mdx @@ -0,0 +1,44 @@ +--- +title: 'UpdaterApi' +description: 'UpdaterApi — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/updater.ts#L40) + +## Methods + + Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version?', description: 'string' }, + ], + returns: 'Promise', + }, + getUpdate: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + install: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + listRemotes: { + type: '() => Promise', + required: true, + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/bridge/updater.mdx b/content/docs/references/api/bridge/updater.mdx new file mode 100644 index 0000000..4bfb50d --- /dev/null +++ b/content/docs/references/api/bridge/updater.mdx @@ -0,0 +1,12 @@ +--- +title: 'updater' +description: 'updater — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/bridge/src/updater.ts#L47) + +**Type:** [`UpdaterApi`](/docs/references/api/bridge/updater-api) + +See [`UpdaterApi`](/docs/references/api/bridge/updater-api) for the full member list. diff --git a/content/docs/references/api/cli/define-config.mdx b/content/docs/references/api/cli/define-config.mdx new file mode 100644 index 0000000..af1d1bb --- /dev/null +++ b/content/docs/references/api/cli/define-config.mdx @@ -0,0 +1,90 @@ +--- +title: 'defineConfig' +description: 'defineConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Overload 1 + +```ts +function defineConfig(config: Config): Config; +``` + +## Parameters + + + +## Returns + +`Config` + +## Overload 2 + +```ts +function defineConfig(config: Promise): Promise; +``` + +## Parameters + +', required: true } }} /> + +## Returns + +`Promise` + +## Overload 3 + +```ts +function defineConfig(config: ConfigInputFnObj): ConfigInputFnObj; +``` + +## Parameters + + + +## Returns + +`ConfigInputFnObj` + +## Overload 4 + +```ts +function defineConfig(config: ConfigInputFnPromise): ConfigInputFnPromise; +``` + +## Parameters + + + +## Returns + +`ConfigInputFnPromise` + +## Overload 5 + +```ts +function defineConfig(config: ConfigInputFn): ConfigInputFn; +``` + +## Parameters + + + +## Returns + +`ConfigInputFn` + +## Overload 6 + +```ts +function defineConfig(config: ConfigInput): ConfigInput; +``` + +## Parameters + + + +## Returns + +`ConfigInput` diff --git a/content/docs/references/api/cli/index.mdx b/content/docs/references/api/cli/index.mdx new file mode 100644 index 0000000..d833892 --- /dev/null +++ b/content/docs/references/api/cli/index.mdx @@ -0,0 +1,21 @@ +--- +title: '@wvb/cli' +description: 'API reference for @wvb/cli.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + + + +## Interfaces + + + + + diff --git a/content/docs/references/api/cli/inline-config.mdx b/content/docs/references/api/cli/inline-config.mdx new file mode 100644 index 0000000..c1d74cf --- /dev/null +++ b/content/docs/references/api/cli/inline-config.mdx @@ -0,0 +1,29 @@ +--- +title: 'InlineConfig' +description: 'InlineConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/cli/src/config.ts#L174) + +**Extends:** `Config` + +## Properties + + diff --git a/content/docs/references/api/cli/load-config-file.mdx b/content/docs/references/api/cli/load-config-file.mdx new file mode 100644 index 0000000..35dab16 --- /dev/null +++ b/content/docs/references/api/cli/load-config-file.mdx @@ -0,0 +1,25 @@ +--- +title: 'loadConfigFile' +description: 'loadConfigFile — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/cli/src/config.ts#L26) + +```ts +function loadConfigFile(filePath?: string, cwd: string = ...): Promise<{ config: Config; configFile: string; configFileDependencies: string[] } | null>; +``` + +## Parameters + + + +## Returns + +`Promise<{ config: Config; configFile: string; configFileDependencies: string[] } | null>` diff --git a/content/docs/references/api/cli/meta.json b/content/docs/references/api/cli/meta.json new file mode 100644 index 0000000..82860db --- /dev/null +++ b/content/docs/references/api/cli/meta.json @@ -0,0 +1,12 @@ +{ + "title": "@wvb/cli", + "pages": [ + "---Functions---", + "define-config", + "load-config-file", + "resolve-config", + "---Interfaces---", + "inline-config", + "resolved-config" + ] +} diff --git a/content/docs/references/api/cli/meta.ko.json b/content/docs/references/api/cli/meta.ko.json new file mode 100644 index 0000000..05b96ba --- /dev/null +++ b/content/docs/references/api/cli/meta.ko.json @@ -0,0 +1,12 @@ +{ + "title": "@wvb/cli", + "pages": [ + "---함수---", + "define-config", + "load-config-file", + "resolve-config", + "---인터페이스---", + "inline-config", + "resolved-config" + ] +} diff --git a/content/docs/references/api/cli/resolve-config.mdx b/content/docs/references/api/cli/resolve-config.mdx new file mode 100644 index 0000000..a63f1ee --- /dev/null +++ b/content/docs/references/api/cli/resolve-config.mdx @@ -0,0 +1,20 @@ +--- +title: 'resolveConfig' +description: 'resolveConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/cli/src/config.ts#L189) + +```ts +function resolveConfig(inlineConfig: InlineConfig): Promise; +``` + +## Parameters + + + +## Returns + +`Promise` diff --git a/content/docs/references/api/cli/resolved-config.mdx b/content/docs/references/api/cli/resolved-config.mdx new file mode 100644 index 0000000..b760505 --- /dev/null +++ b/content/docs/references/api/cli/resolved-config.mdx @@ -0,0 +1,26 @@ +--- +title: 'ResolvedConfig' +description: 'ResolvedConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/cli/src/config.ts#L178) + +**Extends:** `Readonly & { configFile: string | undefined; configFileDependencies: string[] | undefined; inlineConfig: InlineConfig; packageJson: PackageJson | undefined; root: string }>` + +## Properties + + diff --git a/content/docs/references/api/config/builtin-bundle-matches.mdx b/content/docs/references/api/config/builtin-bundle-matches.mdx new file mode 100644 index 0000000..1e1b213 --- /dev/null +++ b/content/docs/references/api/config/builtin-bundle-matches.mdx @@ -0,0 +1,18 @@ +--- +title: 'BuiltinBundleMatches' +description: 'BuiltinBundleMatches — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L5) + +## Type + +```ts +type BuiltinBundleMatches = + | string + | RegExp + | (string | RegExp)[] + | ((info: { name: string; version: string }) => boolean | Promise); +``` diff --git a/content/docs/references/api/config/builtin-config.mdx b/content/docs/references/api/config/builtin-config.mdx new file mode 100644 index 0000000..c678d73 --- /dev/null +++ b/content/docs/references/api/config/builtin-config.mdx @@ -0,0 +1,43 @@ +--- +title: 'BuiltinConfig' +description: 'BuiltinConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L54) + +## Properties + + diff --git a/content/docs/references/api/config/builtin-download-config.mdx b/content/docs/references/api/config/builtin-download-config.mdx new file mode 100644 index 0000000..9984571 --- /dev/null +++ b/content/docs/references/api/config/builtin-download-config.mdx @@ -0,0 +1,21 @@ +--- +title: 'BuiltinDownloadConfig' +description: 'BuiltinDownloadConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L11) + +## Properties + + diff --git a/content/docs/references/api/config/builtin-local-target-config.mdx b/content/docs/references/api/config/builtin-local-target-config.mdx new file mode 100644 index 0000000..55bda17 --- /dev/null +++ b/content/docs/references/api/config/builtin-local-target-config.mdx @@ -0,0 +1,34 @@ +--- +title: 'BuiltinLocalTargetConfig' +description: 'Install builtin bundles from a local target.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Install builtin bundles from a local target. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L29) + +## Properties + + string[] | Promise)', + description: + 'Workspace directory path to install builtin bundles. Glob pattern is supported. Automatically resolve the workspace directory which includes a config file inside. (e.g. wvb.config.ts)', + required: true, + }, + }} +/> diff --git a/content/docs/references/api/config/builtin-remote-target-config.mdx b/content/docs/references/api/config/builtin-remote-target-config.mdx new file mode 100644 index 0000000..0169f94 --- /dev/null +++ b/content/docs/references/api/config/builtin-remote-target-config.mdx @@ -0,0 +1,21 @@ +--- +title: 'BuiltinRemoteTargetConfig' +description: 'Install builtin bundles from a remote target.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Install builtin bundles from a remote target. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L22) + +**Extends:** `Pick` + +## Properties + + diff --git a/content/docs/references/api/config/builtin-target.mdx b/content/docs/references/api/config/builtin-target.mdx new file mode 100644 index 0000000..362a2ae --- /dev/null +++ b/content/docs/references/api/config/builtin-target.mdx @@ -0,0 +1,16 @@ +--- +title: 'BuiltinTarget' +description: 'BuiltinTarget — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/builtin.ts#L50) + +## Type + +```ts +type BuiltinTarget = + | ({ type: 'remote' } & BuiltinRemoteTargetConfig) + | ({ type: 'local' } & BuiltinLocalTargetConfig); +``` diff --git a/content/docs/references/api/config/bundle-info-resolver-params.mdx b/content/docs/references/api/config/bundle-info-resolver-params.mdx new file mode 100644 index 0000000..7764668 --- /dev/null +++ b/content/docs/references/api/config/bundle-info-resolver-params.mdx @@ -0,0 +1,26 @@ +--- +title: 'BundleInfoResolverParams' +description: 'BundleInfoResolverParams — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/common.ts#L3) + +## Properties + + diff --git a/content/docs/references/api/config/bundle-name-resolver.mdx b/content/docs/references/api/config/bundle-name-resolver.mdx new file mode 100644 index 0000000..86bd093 --- /dev/null +++ b/content/docs/references/api/config/bundle-name-resolver.mdx @@ -0,0 +1,25 @@ +--- +title: 'BundleNameResolver' +description: 'Bundle name resolver to determine the name of the bundle to be used in remote or builtin.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle name resolver to determine the name of the bundle to be used in remote or builtin. + +From "package.json", will use the "name" field from "package.json", +if not specified, will be throw error. +If "name" is with a scope prefix, it will be removed. + +Or, specify a custom bundle name string or a function that returns a bundle name string. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/common.ts#L47) + +## Type + +```ts +type BundleNameResolver = + | { from: 'package.json' } + | string + | ((params: BundleInfoResolverParams) => string | Promise); +``` diff --git a/content/docs/references/api/config/config-input-fn-obj.mdx b/content/docs/references/api/config/config-input-fn-obj.mdx new file mode 100644 index 0000000..c711acb --- /dev/null +++ b/content/docs/references/api/config/config-input-fn-obj.mdx @@ -0,0 +1,14 @@ +--- +title: 'ConfigInputFnObj' +description: 'ConfigInputFnObj — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L6) + +## Type + +```ts +type ConfigInputFnObj = () => Config; +``` diff --git a/content/docs/references/api/config/config-input-fn-promise.mdx b/content/docs/references/api/config/config-input-fn-promise.mdx new file mode 100644 index 0000000..4ef9813 --- /dev/null +++ b/content/docs/references/api/config/config-input-fn-promise.mdx @@ -0,0 +1,14 @@ +--- +title: 'ConfigInputFnPromise' +description: 'ConfigInputFnPromise — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L7) + +## Type + +```ts +type ConfigInputFnPromise = () => Promise; +``` diff --git a/content/docs/references/api/config/config-input-fn.mdx b/content/docs/references/api/config/config-input-fn.mdx new file mode 100644 index 0000000..addd04f --- /dev/null +++ b/content/docs/references/api/config/config-input-fn.mdx @@ -0,0 +1,14 @@ +--- +title: 'ConfigInputFn' +description: 'ConfigInputFn — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L8) + +## Type + +```ts +type ConfigInputFn = () => Config | Promise; +``` diff --git a/content/docs/references/api/config/config-input.mdx b/content/docs/references/api/config/config-input.mdx new file mode 100644 index 0000000..50b0b21 --- /dev/null +++ b/content/docs/references/api/config/config-input.mdx @@ -0,0 +1,19 @@ +--- +title: 'ConfigInput' +description: 'ConfigInput — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L10) + +## Type + +```ts +type ConfigInput = + | Config + | Promise + | ConfigInputFnObj + | ConfigInputFnPromise + | ConfigInputFn; +``` diff --git a/content/docs/references/api/config/config.mdx b/content/docs/references/api/config/config.mdx new file mode 100644 index 0000000..08f3f4a --- /dev/null +++ b/content/docs/references/api/config/config.mdx @@ -0,0 +1,26 @@ +--- +title: 'Config' +description: 'Config — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L27) + +## Properties + + diff --git a/content/docs/references/api/config/define-config.mdx b/content/docs/references/api/config/define-config.mdx new file mode 100644 index 0000000..16ee46c --- /dev/null +++ b/content/docs/references/api/config/define-config.mdx @@ -0,0 +1,92 @@ +--- +title: 'defineConfig' +description: 'defineConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/config.ts#L17) + +## Overload 1 + +```ts +function defineConfig(config: Config): Config; +``` + +## Parameters + + + +## Returns + +`Config` + +## Overload 2 + +```ts +function defineConfig(config: Promise): Promise; +``` + +## Parameters + +', required: true } }} /> + +## Returns + +`Promise` + +## Overload 3 + +```ts +function defineConfig(config: ConfigInputFnObj): ConfigInputFnObj; +``` + +## Parameters + + + +## Returns + +`ConfigInputFnObj` + +## Overload 4 + +```ts +function defineConfig(config: ConfigInputFnPromise): ConfigInputFnPromise; +``` + +## Parameters + + + +## Returns + +`ConfigInputFnPromise` + +## Overload 5 + +```ts +function defineConfig(config: ConfigInputFn): ConfigInputFn; +``` + +## Parameters + + + +## Returns + +`ConfigInputFn` + +## Overload 6 + +```ts +function defineConfig(config: ConfigInput): ConfigInput; +``` + +## Parameters + + + +## Returns + +`ConfigInput` diff --git a/content/docs/references/api/config/headers-config.mdx b/content/docs/references/api/config/headers-config.mdx new file mode 100644 index 0000000..165cc7e --- /dev/null +++ b/content/docs/references/api/config/headers-config.mdx @@ -0,0 +1,17 @@ +--- +title: 'HeadersConfig' +description: 'HeadersConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/pack.ts#L2) + +## Type + +```ts +type HeadersConfig = + | Record + | [string, HeadersInit][] + | ((file: string) => HeadersInit | null | undefined | Promise); +``` diff --git a/content/docs/references/api/config/ignore-config.mdx b/content/docs/references/api/config/ignore-config.mdx new file mode 100644 index 0000000..371aa8a --- /dev/null +++ b/content/docs/references/api/config/ignore-config.mdx @@ -0,0 +1,14 @@ +--- +title: 'IgnoreConfig' +description: 'IgnoreConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/pack.ts#L1) + +## Type + +```ts +type IgnoreConfig = (string | RegExp)[] | ((file: string) => boolean | Promise); +``` diff --git a/content/docs/references/api/config/index.mdx b/content/docs/references/api/config/index.mdx new file mode 100644 index 0000000..60bfc48 --- /dev/null +++ b/content/docs/references/api/config/index.mdx @@ -0,0 +1,63 @@ +--- +title: '@wvb/config' +description: 'Configuration for webview bundle' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + +## Interfaces + + + + + + + + + + + + +## Type Aliases + + + + + + + + + + + + + diff --git a/content/docs/references/api/config/meta.json b/content/docs/references/api/config/meta.json new file mode 100644 index 0000000..0f77a4f --- /dev/null +++ b/content/docs/references/api/config/meta.json @@ -0,0 +1,27 @@ +{ + "title": "@wvb/config", + "pages": [ + "---Functions---", + "define-config", + "---Interfaces---", + "builtin-config", + "builtin-download-config", + "builtin-local-target-config", + "builtin-remote-target-config", + "bundle-info-resolver-params", + "config", + "pack-config", + "serve-config", + "---Type Aliases---", + "builtin-bundle-matches", + "builtin-target", + "bundle-name-resolver", + "config-input", + "config-input-fn", + "config-input-fn-obj", + "config-input-fn-promise", + "headers-config", + "ignore-config", + "version-resolver" + ] +} diff --git a/content/docs/references/api/config/meta.ko.json b/content/docs/references/api/config/meta.ko.json new file mode 100644 index 0000000..fe2ed2d --- /dev/null +++ b/content/docs/references/api/config/meta.ko.json @@ -0,0 +1,27 @@ +{ + "title": "@wvb/config", + "pages": [ + "---함수---", + "define-config", + "---인터페이스---", + "builtin-config", + "builtin-download-config", + "builtin-local-target-config", + "builtin-remote-target-config", + "bundle-info-resolver-params", + "config", + "pack-config", + "serve-config", + "---타입---", + "builtin-bundle-matches", + "builtin-target", + "bundle-name-resolver", + "config-input", + "config-input-fn", + "config-input-fn-obj", + "config-input-fn-promise", + "headers-config", + "ignore-config", + "version-resolver" + ] +} diff --git a/content/docs/references/api/config/pack-config.mdx b/content/docs/references/api/config/pack-config.mdx new file mode 100644 index 0000000..253a728 --- /dev/null +++ b/content/docs/references/api/config/pack-config.mdx @@ -0,0 +1,59 @@ +--- +title: 'PackConfig' +description: 'Webview Bundle pack config.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Webview Bundle pack config. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/pack.ts#L10) + +## Properties + +", where is derived from the "name" field in "package.json" (scope stripped).', + required: false, + }, + overwrite: { + type: 'boolean', + description: 'Overwrite out-file if file is already exists', + required: false, + default: 'true', + }, + srcDir: { + type: 'string', + description: + 'Path to the source directory. All files under this directory will be included in the Webview Bundle. Use "ignore" to exclude files you don\'t want to pack.', + required: false, + default: '"./dist"', + }, + }} +/> + +## Examples + +### headers + +```ts +{ + * "*.html": { + * "cache-control": "max-age=3600", + * }, + * "*.js": ["cache-control", "max-age=0"] + * } +``` diff --git a/content/docs/references/api/config/serve-config.mdx b/content/docs/references/api/config/serve-config.mdx new file mode 100644 index 0000000..ddfcc12 --- /dev/null +++ b/content/docs/references/api/config/serve-config.mdx @@ -0,0 +1,28 @@ +--- +title: 'ServeConfig' +description: 'ServeConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/serve.ts#L1) + +## Properties + + diff --git a/content/docs/references/api/config/version-resolver.mdx b/content/docs/references/api/config/version-resolver.mdx new file mode 100644 index 0000000..5d0d524 --- /dev/null +++ b/content/docs/references/api/config/version-resolver.mdx @@ -0,0 +1,27 @@ +--- +title: 'VersionResolver' +description: 'Version resolver to determine the version of the bundle to be used in remote or builtin.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Version resolver to determine the version of the bundle to be used in remote or builtin. + +From "package.json", will use the "version" field from "package.json", +if not specified, will be throw error. + +From "git", will use the "HEAD" commit hash. + +Or, specify a custom version string or a function that returns a version string. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/config/src/common.ts#L30) + +## Type + +```ts +type VersionResolver = + | { from: 'package.json' } + | { from: 'git' } + | string + | ((params: BundleInfoResolverParams) => string | Promise); +``` diff --git a/content/docs/references/api/deno-desktop/app-data-dir.mdx b/content/docs/references/api/deno-desktop/app-data-dir.mdx new file mode 100644 index 0000000..432ad8c --- /dev/null +++ b/content/docs/references/api/deno-desktop/app-data-dir.mdx @@ -0,0 +1,20 @@ +--- +title: 'appDataDir' +description: 'The OS application-data base directory where downloaded bundles persist: macOS ~/Library/Application Support, Windows %APPDATA%, Linux $XDG_DATA_HOME (or ~/.…' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +The OS application-data base directory where downloaded bundles persist: +macOS `~/Library/Application Support`, Windows `%APPDATA%`, Linux `$XDG_DATA_HOME` (or +`~/.local/share`). Override with the `WVB_APP_DATA_DIR` env var. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/source.ts#L48) + +```ts +function appDataDir(): string; +``` + +## Returns + +`string` diff --git a/content/docs/references/api/deno-desktop/bridge-error-code.mdx b/content/docs/references/api/deno-desktop/bridge-error-code.mdx new file mode 100644 index 0000000..4852ac5 --- /dev/null +++ b/content/docs/references/api/deno-desktop/bridge-error-code.mdx @@ -0,0 +1,18 @@ +--- +title: 'BridgeErrorCode' +description: 'BridgeErrorCode — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L24) + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/bridge-error-data.mdx b/content/docs/references/api/deno-desktop/bridge-error-data.mdx new file mode 100644 index 0000000..1a03cdd --- /dev/null +++ b/content/docs/references/api/deno-desktop/bridge-error-data.mdx @@ -0,0 +1,16 @@ +--- +title: 'BridgeErrorData' +description: 'Error payload returned to @wvb/bridge (becomes a BridgeError there, preserving code).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Error payload returned to `@wvb/bridge` (becomes a `BridgeError` there, preserving `code`). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L12) + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/bundle-protocol-config.mdx b/content/docs/references/api/deno-desktop/bundle-protocol-config.mdx new file mode 100644 index 0000000..3ec2d7f --- /dev/null +++ b/content/docs/references/api/deno-desktop/bundle-protocol-config.mdx @@ -0,0 +1,23 @@ +--- +title: 'BundleProtocolConfig' +description: 'BundleProtocolConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L60) + +**Extends:** `ProtocolOptions` + +## Methods + + void', + required: false, + parameters: [{ name: 'e', description: 'Error' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/bundle-protocol.mdx b/content/docs/references/api/deno-desktop/bundle-protocol.mdx new file mode 100644 index 0000000..8033247 --- /dev/null +++ b/content/docs/references/api/deno-desktop/bundle-protocol.mdx @@ -0,0 +1,27 @@ +--- +title: 'bundleProtocol' +description: 'Serve a builtin bundle (named scheme) at the HTTP root.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Serve a builtin bundle (named `scheme`) at the HTTP root. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L63) + +```ts +function bundleProtocol(scheme: string, config: BundleProtocolConfig = {}): Protocol; +``` + +## Parameters + + + +## Returns + +`Protocol` diff --git a/content/docs/references/api/deno-desktop/bundle-source.mdx b/content/docs/references/api/deno-desktop/bundle-source.mdx new file mode 100644 index 0000000..0d59d61 --- /dev/null +++ b/content/docs/references/api/deno-desktop/bundle-source.mdx @@ -0,0 +1,20 @@ +--- +title: 'bundleSource' +description: 'bundleSource — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/source.ts#L25) + +```ts +function bundleSource(options: SourceOptions = {}): BundleSource; +``` + +## Parameters + + + +## Returns + +`BundleSource` diff --git a/content/docs/references/api/deno-desktop/deno-browser-window.mdx b/content/docs/references/api/deno-desktop/deno-browser-window.mdx new file mode 100644 index 0000000..49ab384 --- /dev/null +++ b/content/docs/references/api/deno-desktop/deno-browser-window.mdx @@ -0,0 +1,32 @@ +--- +title: 'DenoBrowserWindow' +description: 'A Deno.BrowserWindow (only the binding methods we use; the full type ships with Deno desktop).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A `Deno.BrowserWindow` (only the binding methods we use; the full type ships with Deno desktop). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L134) + +## Methods + + unknown) => void', + required: true, + parameters: [ + { name: 'name', description: 'string' }, + { name: 'handler', description: '(args: any[]) => unknown' }, + ], + returns: 'void', + }, + unbind: { + type: '(name: string) => void', + required: false, + parameters: [{ name: 'name', description: 'string' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/dispatch.mdx b/content/docs/references/api/deno-desktop/dispatch.mdx new file mode 100644 index 0000000..8699f10 --- /dev/null +++ b/content/docs/references/api/deno-desktop/dispatch.mdx @@ -0,0 +1,28 @@ +--- +title: 'dispatch' +description: 'Run one @wvb/bridge command and return its JSON-serializable result envelope (never throws).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Run one `@wvb/bridge` command and return its JSON-serializable result envelope (never throws). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L110) + +```ts +function dispatch(wvb: WebviewBundle, name: string, params?: Params): Promise; +``` + +## Parameters + + + +## Returns + +`Promise` diff --git a/content/docs/references/api/deno-desktop/handler-names.mdx b/content/docs/references/api/deno-desktop/handler-names.mdx new file mode 100644 index 0000000..52dcb7b --- /dev/null +++ b/content/docs/references/api/deno-desktop/handler-names.mdx @@ -0,0 +1,12 @@ +--- +title: 'handlerNames' +description: 'Names of every command this host can serve.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Names of every command this host can serve. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L107) + +**Type:** `readonly string[]` diff --git a/content/docs/references/api/deno-desktop/http-response.mdx b/content/docs/references/api/deno-desktop/http-response.mdx new file mode 100644 index 0000000..49610ca --- /dev/null +++ b/content/docs/references/api/deno-desktop/http-response.mdx @@ -0,0 +1,12 @@ +--- +title: 'HttpResponse' +description: 'HttpResponse — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Type + +```ts +type HttpResponse = any; +``` diff --git a/content/docs/references/api/deno-desktop/index.mdx b/content/docs/references/api/deno-desktop/index.mdx new file mode 100644 index 0000000..bd3221e --- /dev/null +++ b/content/docs/references/api/deno-desktop/index.mdx @@ -0,0 +1,116 @@ +--- +title: '@wvb/deno-desktop' +description: 'API reference for @wvb/deno-desktop.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + + + + + + + + +## Interfaces + + + + + + + + + + + + + + + + + + +## Type Aliases + + + + + + + +## Variables + + + + + + + diff --git a/content/docs/references/api/deno-desktop/invoke-binding.mdx b/content/docs/references/api/deno-desktop/invoke-binding.mdx new file mode 100644 index 0000000..96e0481 --- /dev/null +++ b/content/docs/references/api/deno-desktop/invoke-binding.mdx @@ -0,0 +1,12 @@ +--- +title: 'INVOKE_BINDING' +description: 'The single binding name the @wvb/bridge deno transport calls.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +The single binding name the `@wvb/bridge` `deno` transport calls. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L9) + +**Type:** `"wvbInvoke"` diff --git a/content/docs/references/api/deno-desktop/invoke-result.mdx b/content/docs/references/api/deno-desktop/invoke-result.mdx new file mode 100644 index 0000000..3caa908 --- /dev/null +++ b/content/docs/references/api/deno-desktop/invoke-result.mdx @@ -0,0 +1,18 @@ +--- +title: 'InvokeResult' +description: 'Result envelope.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Result envelope. Deno desktop delivers a thrown handler error as `{ name, message, stack }` +(dropping our `code`), so handlers never throw across the binding — they return this instead and +`@wvb/bridge` unwraps it (mirrors @wvb/electron's preload). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L22) + +## Type + +```ts +type InvokeResult = { ok: true; value: unknown } | { error: BridgeErrorData; ok: false }; +``` diff --git a/content/docs/references/api/deno-desktop/local-protocol-config.mdx b/content/docs/references/api/deno-desktop/local-protocol-config.mdx new file mode 100644 index 0000000..b76b0c3 --- /dev/null +++ b/content/docs/references/api/deno-desktop/local-protocol-config.mdx @@ -0,0 +1,27 @@ +--- +title: 'LocalProtocolConfig' +description: 'LocalProtocolConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L90) + +**Extends:** `ProtocolOptions` + +## Properties + + Hosts | Promise)', required: true } }} /> + +## Methods + + void', + required: false, + parameters: [{ name: 'e', description: 'Error' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/local-protocol.mdx b/content/docs/references/api/deno-desktop/local-protocol.mdx new file mode 100644 index 0000000..a093957 --- /dev/null +++ b/content/docs/references/api/deno-desktop/local-protocol.mdx @@ -0,0 +1,27 @@ +--- +title: 'localProtocol' +description: 'Proxy to a local dev server (hot reload), mapping scheme host → URL.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Proxy to a local dev server (hot reload), mapping `scheme` host → URL. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L95) + +```ts +function localProtocol(scheme: string, config: LocalProtocolConfig): Protocol; +``` + +## Parameters + + + +## Returns + +`Protocol` diff --git a/content/docs/references/api/deno-desktop/meta.json b/content/docs/references/api/deno-desktop/meta.json new file mode 100644 index 0000000..ea92bae --- /dev/null +++ b/content/docs/references/api/deno-desktop/meta.json @@ -0,0 +1,38 @@ +{ + "title": "@wvb/deno-desktop", + "pages": [ + "---Functions---", + "app-data-dir", + "bundle-protocol", + "bundle-source", + "dispatch", + "local-protocol", + "register-bindings", + "remote", + "webview-bundle", + "---Interfaces---", + "bridge-error-data", + "bundle-protocol-config", + "deno-browser-window", + "local-protocol-config", + "protocol", + "protocol-handler", + "protocol-handler-build-context", + "protocol-options", + "remote-options", + "source-options", + "webview-bundle-interface", + "webview-bundle-config", + "webview-bundle-remote-config", + "webview-bundle-updater-config", + "---Type Aliases---", + "http-response", + "invoke-result", + "protocol-handler-build", + "---Variables---", + "bridge-error-code", + "handler-names", + "invoke-binding", + "wvb" + ] +} diff --git a/content/docs/references/api/deno-desktop/meta.ko.json b/content/docs/references/api/deno-desktop/meta.ko.json new file mode 100644 index 0000000..0f0f22f --- /dev/null +++ b/content/docs/references/api/deno-desktop/meta.ko.json @@ -0,0 +1,38 @@ +{ + "title": "@wvb/deno-desktop", + "pages": [ + "---함수---", + "app-data-dir", + "bundle-protocol", + "bundle-source", + "dispatch", + "local-protocol", + "register-bindings", + "remote", + "webview-bundle", + "---인터페이스---", + "bridge-error-data", + "bundle-protocol-config", + "deno-browser-window", + "local-protocol-config", + "protocol", + "protocol-handler", + "protocol-handler-build-context", + "protocol-options", + "remote-options", + "source-options", + "webview-bundle-interface", + "webview-bundle-config", + "webview-bundle-remote-config", + "webview-bundle-updater-config", + "---타입---", + "http-response", + "invoke-result", + "protocol-handler-build", + "---변수---", + "bridge-error-code", + "handler-names", + "invoke-binding", + "wvb" + ] +} diff --git a/content/docs/references/api/deno-desktop/protocol-handler-build-context.mdx b/content/docs/references/api/deno-desktop/protocol-handler-build-context.mdx new file mode 100644 index 0000000..5871644 --- /dev/null +++ b/content/docs/references/api/deno-desktop/protocol-handler-build-context.mdx @@ -0,0 +1,12 @@ +--- +title: 'ProtocolHandlerBuildContext' +description: 'ProtocolHandlerBuildContext — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L16) + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/protocol-handler-build.mdx b/content/docs/references/api/deno-desktop/protocol-handler-build.mdx new file mode 100644 index 0000000..769a067 --- /dev/null +++ b/content/docs/references/api/deno-desktop/protocol-handler-build.mdx @@ -0,0 +1,16 @@ +--- +title: 'ProtocolHandlerBuild' +description: 'ProtocolHandlerBuild — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L19) + +## Type + +```ts +type ProtocolHandlerBuild = ( + ctx: ProtocolHandlerBuildContext +) => ProtocolHandler | Promise; +``` diff --git a/content/docs/references/api/deno-desktop/protocol-handler.mdx b/content/docs/references/api/deno-desktop/protocol-handler.mdx new file mode 100644 index 0000000..4af2cd1 --- /dev/null +++ b/content/docs/references/api/deno-desktop/protocol-handler.mdx @@ -0,0 +1,21 @@ +--- +title: 'ProtocolHandler' +description: 'ProtocolHandler — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L12) + +## Methods + + Promise', + required: true, + parameters: [{ name: 'req', description: 'Request' }], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/protocol-options.mdx b/content/docs/references/api/deno-desktop/protocol-options.mdx new file mode 100644 index 0000000..a0e1b34 --- /dev/null +++ b/content/docs/references/api/deno-desktop/protocol-options.mdx @@ -0,0 +1,21 @@ +--- +title: 'ProtocolOptions' +description: 'ProtocolOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L23) + +## Methods + + void', + required: false, + parameters: [{ name: 'e', description: 'Error' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/protocol.mdx b/content/docs/references/api/deno-desktop/protocol.mdx new file mode 100644 index 0000000..1e4fa01 --- /dev/null +++ b/content/docs/references/api/deno-desktop/protocol.mdx @@ -0,0 +1,23 @@ +--- +title: 'Protocol' +description: 'Protocol — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/protocol.ts#L27) + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/register-bindings.mdx b/content/docs/references/api/deno-desktop/register-bindings.mdx new file mode 100644 index 0000000..5776c10 --- /dev/null +++ b/content/docs/references/api/deno-desktop/register-bindings.mdx @@ -0,0 +1,35 @@ +--- +title: 'registerBindings' +description: 'Register the @wvb/bridge transport on a Deno desktop window: a single wvbInvoke(name, params) binding that dispatches to wvb.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Register the `@wvb/bridge` transport on a Deno desktop window: a single `wvbInvoke(name, params)` +binding that dispatches to `wvb`. Call after creating the window and the app, e.g. + +```ts +const win = new Deno.BrowserWindow(); +const app = webviewBundle({ source: { appName: 'myapp' }, protocols: [bundleProtocol('app')] }); +registerBindings(win, app); +Deno.serve(app.fetch); +``` + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/bindings.ts#L150) + +```ts +function registerBindings(win: DenoBrowserWindow, wvb: WebviewBundle): void; +``` + +## Parameters + + + +## Returns + +`void` diff --git a/content/docs/references/api/deno-desktop/remote-options.mdx b/content/docs/references/api/deno-desktop/remote-options.mdx new file mode 100644 index 0000000..5b362f8 --- /dev/null +++ b/content/docs/references/api/deno-desktop/remote-options.mdx @@ -0,0 +1,10 @@ +--- +title: 'RemoteOptions' +description: 'RemoteOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/remote.ts#L4) + +**Extends:** `HttpResponse` diff --git a/content/docs/references/api/deno-desktop/remote.mdx b/content/docs/references/api/deno-desktop/remote.mdx new file mode 100644 index 0000000..f8bc745 --- /dev/null +++ b/content/docs/references/api/deno-desktop/remote.mdx @@ -0,0 +1,25 @@ +--- +title: 'remote' +description: 'remote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/remote.ts#L6) + +```ts +function remote(endpoint: string, options?: RemoteOptions): Remote; +``` + +## Parameters + + + +## Returns + +`Remote` diff --git a/content/docs/references/api/deno-desktop/source-options.mdx b/content/docs/references/api/deno-desktop/source-options.mdx new file mode 100644 index 0000000..b5f7a29 --- /dev/null +++ b/content/docs/references/api/deno-desktop/source-options.mdx @@ -0,0 +1,42 @@ +--- +title: 'SourceOptions' +description: 'SourceOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/source.ts#L10) + +**Extends:** `Omit` + +## Properties + +//bundles (see appDataDir) so updates persist across runs.', + required: false, + }, + }} +/> + +## Index Signature + +```ts +[key: string]: BundleSourceConfig; +[key: number]: BundleSourceConfig; +[key: symbol]: BundleSourceConfig; +``` diff --git a/content/docs/references/api/deno-desktop/webview-bundle-config.mdx b/content/docs/references/api/deno-desktop/webview-bundle-config.mdx new file mode 100644 index 0000000..adde83a --- /dev/null +++ b/content/docs/references/api/deno-desktop/webview-bundle-config.mdx @@ -0,0 +1,24 @@ +--- +title: 'WebviewBundleConfig' +description: 'WebviewBundleConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L14) + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/webview-bundle-interface.mdx b/content/docs/references/api/deno-desktop/webview-bundle-interface.mdx new file mode 100644 index 0000000..40054c9 --- /dev/null +++ b/content/docs/references/api/deno-desktop/webview-bundle-interface.mdx @@ -0,0 +1,33 @@ +--- +title: 'WebviewBundle' +description: 'WebviewBundle — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L48) + +## Properties + + + +## Methods + + Promise', + description: 'A Deno.serve-compatible handler: Deno.serve(wvb.fetch).', + required: true, + parameters: [{ name: 'req', description: 'Request' }], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno-desktop/webview-bundle-remote-config.mdx b/content/docs/references/api/deno-desktop/webview-bundle-remote-config.mdx new file mode 100644 index 0000000..dc8200f --- /dev/null +++ b/content/docs/references/api/deno-desktop/webview-bundle-remote-config.mdx @@ -0,0 +1,14 @@ +--- +title: 'WebviewBundleRemoteConfig' +description: 'WebviewBundleRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L6) + +**Extends:** `RemoteOptions` + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/webview-bundle-updater-config.mdx b/content/docs/references/api/deno-desktop/webview-bundle-updater-config.mdx new file mode 100644 index 0000000..3246b99 --- /dev/null +++ b/content/docs/references/api/deno-desktop/webview-bundle-updater-config.mdx @@ -0,0 +1,14 @@ +--- +title: 'WebviewBundleUpdaterConfig' +description: 'WebviewBundleUpdaterConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L10) + +**Extends:** `HttpResponse` + +## Properties + + diff --git a/content/docs/references/api/deno-desktop/webview-bundle.mdx b/content/docs/references/api/deno-desktop/webview-bundle.mdx new file mode 100644 index 0000000..533c812 --- /dev/null +++ b/content/docs/references/api/deno-desktop/webview-bundle.mdx @@ -0,0 +1,20 @@ +--- +title: 'webviewBundle' +description: 'webviewBundle — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L98) + +```ts +function webviewBundle(config: WebviewBundleConfig): WebviewBundle; +``` + +## Parameters + + + +## Returns + +`WebviewBundle` diff --git a/content/docs/references/api/deno-desktop/wvb.mdx b/content/docs/references/api/deno-desktop/wvb.mdx new file mode 100644 index 0000000..c9cb879 --- /dev/null +++ b/content/docs/references/api/deno-desktop/wvb.mdx @@ -0,0 +1,12 @@ +--- +title: 'wvb' +description: 'wvb — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno-desktop/lib/webview-bundle.ts#L102) + +**Type:** [`typeof webviewBundle`](/docs/references/api/deno-desktop/webview-bundle) + +See [`typeof webviewBundle`](/docs/references/api/deno-desktop/webview-bundle) for the full member list. diff --git a/content/docs/references/api/deno/bundle-manifest-metadata.mdx b/content/docs/references/api/deno/bundle-manifest-metadata.mdx new file mode 100644 index 0000000..ca1521e --- /dev/null +++ b/content/docs/references/api/deno/bundle-manifest-metadata.mdx @@ -0,0 +1,21 @@ +--- +title: 'BundleManifestMetadata' +description: 'Cache-validation / integrity metadata for a bundle version.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Cache-validation / integrity metadata for a bundle version. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L15) + +## Properties + + diff --git a/content/docs/references/api/deno/bundle-protocol.mdx b/content/docs/references/api/deno/bundle-protocol.mdx new file mode 100644 index 0000000..2ff6830 --- /dev/null +++ b/content/docs/references/api/deno/bundle-protocol.mdx @@ -0,0 +1,39 @@ +--- +title: 'BundleProtocol' +description: 'Serves files from a BundleSource as HTTP responses — GET/HEAD, content-type, HTTP Range (206), and index.html directory-index fallback.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Serves files from a [`BundleSource`](/docs/references/api/deno/bundle-source) as HTTP responses — GET/HEAD, content-type, HTTP Range +(206), and `index.html` directory-index fallback. Resolves the bundle name from the request URI +host (`bundle://app/index.html` → bundle `app`). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/protocol.ts#L30) + +## Constructor + +```ts +new BundleProtocol(source: BundleSource); +``` + + + +## Methods + + void', required: true, returns: 'void' }, + free: { type: '() => void', required: true, returns: 'void' }, + handle: { + type: '(method: HttpMethod, uri: string, headers?: Record) => Promise', + required: true, + parameters: [ + { name: 'method', description: 'HttpMethod' }, + { name: 'uri', description: 'string' }, + { name: 'headers?', description: 'Record' }, + ], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno/bundle-source-config.mdx b/content/docs/references/api/deno/bundle-source-config.mdx new file mode 100644 index 0000000..05c9c60 --- /dev/null +++ b/content/docs/references/api/deno/bundle-source-config.mdx @@ -0,0 +1,26 @@ +--- +title: 'BundleSourceConfig' +description: 'BundleSourceConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L4) + +## Properties + +/_.wvb).', + required: true, + }, + remoteDir: { + type: 'string', + description: 'Writable directory for downloaded (remote) bundles.', + required: true, + }, + }} +/> diff --git a/content/docs/references/api/deno/bundle-source-type.mdx b/content/docs/references/api/deno/bundle-source-type.mdx new file mode 100644 index 0000000..88af70c --- /dev/null +++ b/content/docs/references/api/deno/bundle-source-type.mdx @@ -0,0 +1,16 @@ +--- +title: 'BundleSourceType' +description: 'Which source a bundle version comes from.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Which source a bundle version comes from. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L12) + +## Type + +```ts +type BundleSourceType = 'builtin' | 'remote'; +``` diff --git a/content/docs/references/api/deno/bundle-source-version.mdx b/content/docs/references/api/deno/bundle-source-version.mdx new file mode 100644 index 0000000..532c987 --- /dev/null +++ b/content/docs/references/api/deno/bundle-source-version.mdx @@ -0,0 +1,19 @@ +--- +title: 'BundleSourceVersion' +description: 'The current version of a bundle and which source provides it.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +The current version of a bundle and which source provides it. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L23) + +## Properties + + diff --git a/content/docs/references/api/deno/bundle-source.mdx b/content/docs/references/api/deno/bundle-source.mdx new file mode 100644 index 0000000..d982764 --- /dev/null +++ b/content/docs/references/api/deno/bundle-source.mdx @@ -0,0 +1,136 @@ +--- +title: 'BundleSource' +description: 'A bundle source over a builtinDir (read-only) and remoteDir (writable).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A bundle source over a `builtinDir` (read-only) and `remoteDir` (writable). Pass it to +[`BundleProtocol`](/docs/references/api/deno/bundle-protocol). Free it with `using` or `.free()` once no longer needed (the protocol +keeps its own reference, so the source may be freed after the protocol is created). + +The data methods mirror `@wvb/node`'s `BundleSource` so `@wvb/deno-desktop` can serve the +`@wvb/bridge` `source.*` commands. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L45) + +## Constructor + +```ts +new BundleSource(config: BundleSourceConfig); +``` + + + +## Methods + + void', required: true, returns: 'void' }, + free: { type: '() => void', required: true, returns: 'void' }, + getBuiltinBundleFilepath: { + type: '(bundleName: string, version: string) => string', + description: 'Absolute path to a specific builtin bundle version.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'string', + }, + getRemoteBundleFilepath: { + type: '(bundleName: string, version: string) => string', + description: 'Absolute path to a specific remote bundle version.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'string', + }, + listBundles: { + type: '() => Promise', + description: 'List every bundle version across the builtin + remote directories.', + required: true, + returns: 'Promise', + }, + loadBuiltinMetadata: { + type: '(bundleName: string, version: string) => Promise', + description: 'Manifest metadata for a builtin bundle version, or null.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + loadRemoteMetadata: { + type: '(bundleName: string, version: string) => Promise', + description: 'Manifest metadata for a remote bundle version, or null.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + loadVersion: { + type: '(bundleName: string) => Promise', + description: + 'The current version for bundleName (remote takes priority over builtin), or null.', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + pruneRemoteBundles: { + type: '(bundleName: string) => Promise', + description: + 'Remove every staged remote version except the retained set. Returns the removed versions.', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + remoteRetainedVersions: { + type: '(bundleName: string) => Promise', + description: 'The remote versions that pruning retains (current + previous).', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + removeRemoteBundle: { + type: '(bundleName: string, version: string) => Promise', + description: 'Remove a single staged remote bundle version (manifest entry + file).', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + resolveFilepath: { + type: '(bundleName: string) => Promise', + description: 'Absolute path to the .wvb for the current version (remote over builtin).', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + unloadDescriptor: { + type: '(bundleName: string) => boolean', + description: + 'Drop the cached descriptor for a bundle, if present. Returns whether one was removed.', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'boolean', + }, + updateRemoteVersion: { + type: '(bundleName: string, version: string) => Promise', + description: 'Set the current version of a remote bundle in the manifest.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno/bundle-update-info.mdx b/content/docs/references/api/deno/bundle-update-info.mdx new file mode 100644 index 0000000..ac8c490 --- /dev/null +++ b/content/docs/references/api/deno/bundle-update-info.mdx @@ -0,0 +1,25 @@ +--- +title: 'BundleUpdateInfo' +description: 'Information about an available update.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Information about an available update. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L64) + +## Properties + + diff --git a/content/docs/references/api/deno/http-method.mdx b/content/docs/references/api/deno/http-method.mdx new file mode 100644 index 0000000..7f00924 --- /dev/null +++ b/content/docs/references/api/deno/http-method.mdx @@ -0,0 +1,16 @@ +--- +title: 'HttpMethod' +description: 'HTTP method accepted by a protocol handler (case-insensitive on the wire).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +HTTP method accepted by a protocol handler (case-insensitive on the wire). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/protocol.ts#L7) + +## Type + +```ts +type HttpMethod = 'get' | 'head' | 'options' | 'post' | 'put' | 'patch' | 'delete'; +``` diff --git a/content/docs/references/api/deno/http-options.mdx b/content/docs/references/api/deno/http-options.mdx new file mode 100644 index 0000000..ebe7919 --- /dev/null +++ b/content/docs/references/api/deno/http-options.mdx @@ -0,0 +1,26 @@ +--- +title: 'HttpOptions' +description: "HTTP client options (mirrors @wvb/node's HttpOptions; defaultHeaders not yet supported)." +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +HTTP client options (mirrors `@wvb/node`'s `HttpOptions`; `defaultHeaders` not yet supported). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L5) + +## Properties + + diff --git a/content/docs/references/api/deno/http-response.mdx b/content/docs/references/api/deno/http-response.mdx new file mode 100644 index 0000000..6da10a4 --- /dev/null +++ b/content/docs/references/api/deno/http-response.mdx @@ -0,0 +1,20 @@ +--- +title: 'HttpResponse' +description: "A served HTTP response (mirrors @wvb/node's HttpResponse, with Uint8Array for the body)." +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A served HTTP response (mirrors `@wvb/node`'s `HttpResponse`, with `Uint8Array` for the body). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/ffi.ts#L265) + +## Properties + +', required: true }, + headers: { type: 'Record', required: true }, + status: { type: 'number', required: true }, + }} +/> diff --git a/content/docs/references/api/deno/index.mdx b/content/docs/references/api/deno/index.mdx new file mode 100644 index 0000000..0a39b7c --- /dev/null +++ b/content/docs/references/api/deno/index.mdx @@ -0,0 +1,155 @@ +--- +title: '@wvb/deno' +description: 'API reference for @wvb/deno.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + + + + + +## Functions + + + + + + + + +## Interfaces + + + + + + + + + + + + + + + + + + + +## Type Aliases + + + + + + + + diff --git a/content/docs/references/api/deno/integrity-policy.mdx b/content/docs/references/api/deno/integrity-policy.mdx new file mode 100644 index 0000000..09ca1b2 --- /dev/null +++ b/content/docs/references/api/deno/integrity-policy.mdx @@ -0,0 +1,16 @@ +--- +title: 'IntegrityPolicy' +description: 'Integrity verification policy.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Integrity verification policy. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L8) + +## Type + +```ts +type IntegrityPolicy = 'strict' | 'optional' | 'none'; +``` diff --git a/content/docs/references/api/deno/list-bundle-item.mdx b/content/docs/references/api/deno/list-bundle-item.mdx new file mode 100644 index 0000000..78f7360 --- /dev/null +++ b/content/docs/references/api/deno/list-bundle-item.mdx @@ -0,0 +1,22 @@ +--- +title: 'ListBundleItem' +description: 'A bundle version from BundleSource.listBundles (flat shape, matching @wvb/node).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A bundle version from `BundleSource.listBundles` (flat shape, matching `@wvb/node`). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/source.ts#L29) + +## Properties + + diff --git a/content/docs/references/api/deno/list-remote-bundle-info.mdx b/content/docs/references/api/deno/list-remote-bundle-info.mdx new file mode 100644 index 0000000..713b164 --- /dev/null +++ b/content/docs/references/api/deno/list-remote-bundle-info.mdx @@ -0,0 +1,16 @@ +--- +title: 'ListRemoteBundleInfo' +description: 'Bundle info from list operations.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle info from list operations. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L22) + +## Properties + + diff --git a/content/docs/references/api/deno/load-lib-via-plug-options.mdx b/content/docs/references/api/deno/load-lib-via-plug-options.mdx new file mode 100644 index 0000000..adad92b --- /dev/null +++ b/content/docs/references/api/deno/load-lib-via-plug-options.mdx @@ -0,0 +1,33 @@ +--- +title: 'LoadLibViaPlugOptions' +description: 'LoadLibViaPlugOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/ffi.ts#L162) + +## Properties + +/releases/download/deno//.', + required: false, + }, + version: { + type: 'string', + description: + "Release version, used to build the default url. Defaults to this package's version.", + required: false, + }, + }} +/> diff --git a/content/docs/references/api/deno/load-lib-via-plug.mdx b/content/docs/references/api/deno/load-lib-via-plug.mdx new file mode 100644 index 0000000..49db353 --- /dev/null +++ b/content/docs/references/api/deno/load-lib-via-plug.mdx @@ -0,0 +1,26 @@ +--- +title: 'loadLibViaPlug' +description: 'Download the platform cdylib from a release via @denosaurs/plug, verify it, cache it, and load it.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Download the platform cdylib from a release via `@denosaurs/plug`, verify it, cache it, and load +it. For `deno run` / library use where the dylib isn't bundled. NOT for self-contained +`deno desktop` builds — there, vendor + `--include` the dylib and use [`loadLib`](/docs/references/api/deno/load-lib). + +Requires `--allow-net --allow-read --allow-write --allow-env --allow-ffi`. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/ffi.ts#L178) + +```ts +function loadLibViaPlug(options: LoadLibViaPlugOptions = {}): Promise>; +``` + +## Parameters + + + +## Returns + +`Promise>` diff --git a/content/docs/references/api/deno/load-lib.mdx b/content/docs/references/api/deno/load-lib.mdx new file mode 100644 index 0000000..435e119 --- /dev/null +++ b/content/docs/references/api/deno/load-lib.mdx @@ -0,0 +1,22 @@ +--- +title: 'loadLib' +description: 'Load the native library from an explicit path and cache it' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Load the native library from an explicit path and cache it + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/ffi.ts#L157) + +```ts +function loadLib(libPath: string | URL): DynamicLibrary; +``` + +## Parameters + + + +## Returns + +`DynamicLibrary` diff --git a/content/docs/references/api/deno/local-protocol.mdx b/content/docs/references/api/deno/local-protocol.mdx new file mode 100644 index 0000000..dbf8a0d --- /dev/null +++ b/content/docs/references/api/deno/local-protocol.mdx @@ -0,0 +1,39 @@ +--- +title: 'LocalProtocol' +description: 'Proxies requests for custom hosts to localhost URLs (for dev servers with hot reload).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Proxies requests for custom hosts to localhost URLs (for dev servers with hot reload). Maps a +custom host to a base URL — e.g. `{ app: 'http://localhost:5173' }` serves `app://app/index.html` +from the dev server. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/protocol.ts#L62) + +## Constructor + +```ts +new LocalProtocol(hosts: Record); +``` + +', required: true } }} /> + +## Methods + + void', required: true, returns: 'void' }, + free: { type: '() => void', required: true, returns: 'void' }, + handle: { + type: '(method: HttpMethod, uri: string, headers?: Record) => Promise', + required: true, + parameters: [ + { name: 'method', description: 'HttpMethod' }, + { name: 'uri', description: 'string' }, + { name: 'headers?', description: 'Record' }, + ], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno/meta.json b/content/docs/references/api/deno/meta.json new file mode 100644 index 0000000..85cba1a --- /dev/null +++ b/content/docs/references/api/deno/meta.json @@ -0,0 +1,38 @@ +{ + "title": "@wvb/deno", + "pages": [ + "---Classes---", + "bundle-protocol", + "bundle-source", + "local-protocol", + "remote", + "updater", + "---Functions---", + "load-lib", + "load-lib-via-plug", + "platform-lib-file-name", + "to-response", + "---Interfaces---", + "bundle-manifest-metadata", + "bundle-source-config", + "bundle-source-version", + "bundle-update-info", + "http-options", + "http-response", + "list-bundle-item", + "list-remote-bundle-info", + "load-lib-via-plug-options", + "remote-bundle-info", + "remote-download", + "remote-options", + "signature-verifier-options", + "signature-verifying-key-options", + "updater-options", + "---Type Aliases---", + "bundle-source-type", + "http-method", + "integrity-policy", + "signature-algorithm", + "verifying-key-format" + ] +} diff --git a/content/docs/references/api/deno/meta.ko.json b/content/docs/references/api/deno/meta.ko.json new file mode 100644 index 0000000..926dc10 --- /dev/null +++ b/content/docs/references/api/deno/meta.ko.json @@ -0,0 +1,38 @@ +{ + "title": "@wvb/deno", + "pages": [ + "---클래스---", + "bundle-protocol", + "bundle-source", + "local-protocol", + "remote", + "updater", + "---함수---", + "load-lib", + "load-lib-via-plug", + "platform-lib-file-name", + "to-response", + "---인터페이스---", + "bundle-manifest-metadata", + "bundle-source-config", + "bundle-source-version", + "bundle-update-info", + "http-options", + "http-response", + "list-bundle-item", + "list-remote-bundle-info", + "load-lib-via-plug-options", + "remote-bundle-info", + "remote-download", + "remote-options", + "signature-verifier-options", + "signature-verifying-key-options", + "updater-options", + "---타입---", + "bundle-source-type", + "http-method", + "integrity-policy", + "signature-algorithm", + "verifying-key-format" + ] +} diff --git a/content/docs/references/api/deno/platform-lib-file-name.mdx b/content/docs/references/api/deno/platform-lib-file-name.mdx new file mode 100644 index 0000000..b24bbea --- /dev/null +++ b/content/docs/references/api/deno/platform-lib-file-name.mdx @@ -0,0 +1,22 @@ +--- +title: 'platformLibFileName' +description: 'Platform cdylib filename: libwvb_deno.dylib (macOS) / libwvb_deno.so (Linux) / wvb_deno.dll (Windows).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Platform cdylib filename: `libwvb_deno.dylib` (macOS) / `libwvb_deno.so` (Linux) / `wvb_deno.dll` (Windows). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/ffi.ts#L136) + +```ts +function platformLibFileName(os: any = Deno.build.os): string; +``` + +## Parameters + + + +## Returns + +`string` diff --git a/content/docs/references/api/deno/remote-bundle-info.mdx b/content/docs/references/api/deno/remote-bundle-info.mdx new file mode 100644 index 0000000..3ccc836 --- /dev/null +++ b/content/docs/references/api/deno/remote-bundle-info.mdx @@ -0,0 +1,23 @@ +--- +title: 'RemoteBundleInfo' +description: 'Complete bundle info from the remote server.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Complete bundle info from the remote server. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L28) + +## Properties + + diff --git a/content/docs/references/api/deno/remote-download.mdx b/content/docs/references/api/deno/remote-download.mdx new file mode 100644 index 0000000..fca45d3 --- /dev/null +++ b/content/docs/references/api/deno/remote-download.mdx @@ -0,0 +1,19 @@ +--- +title: 'RemoteDownload' +description: 'Result of a remote bundle download: info + raw .wvb bytes.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Result of a remote bundle download: info + raw `.wvb` bytes. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L38) + +## Properties + +', required: true }, + info: { type: 'RemoteBundleInfo', required: true }, + }} +/> diff --git a/content/docs/references/api/deno/remote-options.mdx b/content/docs/references/api/deno/remote-options.mdx new file mode 100644 index 0000000..4fda34b --- /dev/null +++ b/content/docs/references/api/deno/remote-options.mdx @@ -0,0 +1,12 @@ +--- +title: 'RemoteOptions' +description: 'RemoteOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L17) + +## Properties + + diff --git a/content/docs/references/api/deno/remote.mdx b/content/docs/references/api/deno/remote.mdx new file mode 100644 index 0000000..76ae373 --- /dev/null +++ b/content/docs/references/api/deno/remote.mdx @@ -0,0 +1,69 @@ +--- +title: 'Remote' +description: 'HTTP client for a remote bundle server — list, get metadata, and download bundles.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +HTTP client for a remote bundle server — list, get metadata, and download bundles. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/remote.ts#L46) + +## Constructor + +```ts +new Remote(endpoint: string, options?: RemoteOptions); +``` + + + +## Properties + + + +## Methods + + void', required: true, returns: 'void' }, + download: { + type: '(bundleName: string, channel?: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'channel?', description: 'string' }, + ], + returns: 'Promise', + }, + downloadVersion: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + free: { type: '() => void', required: true, returns: 'void' }, + getInfo: { + type: '(bundleName: string, channel?: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'channel?', description: 'string' }, + ], + returns: 'Promise', + }, + listBundles: { + type: '(channel?: string) => Promise', + required: true, + parameters: [{ name: 'channel?', description: 'string' }], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno/signature-algorithm.mdx b/content/docs/references/api/deno/signature-algorithm.mdx new file mode 100644 index 0000000..d0dd847 --- /dev/null +++ b/content/docs/references/api/deno/signature-algorithm.mdx @@ -0,0 +1,21 @@ +--- +title: 'SignatureAlgorithm' +description: 'Digital signature algorithm for bundle verification (mirrors @wvb/node).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Digital signature algorithm for bundle verification (mirrors `@wvb/node`). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L11) + +## Type + +```ts +type SignatureAlgorithm = + | 'ecdsaSecp256R1' + | 'ecdsaSecp384R1' + | 'ed25519' + | 'rsaPkcs1V15' + | 'rsaPss'; +``` diff --git a/content/docs/references/api/deno/signature-verifier-options.mdx b/content/docs/references/api/deno/signature-verifier-options.mdx new file mode 100644 index 0000000..ad35d2b --- /dev/null +++ b/content/docs/references/api/deno/signature-verifier-options.mdx @@ -0,0 +1,19 @@ +--- +title: 'SignatureVerifierOptions' +description: 'Declarative signature verifier: an algorithm + the public key to verify bundle signatures with.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Declarative signature verifier: an algorithm + the public key to verify bundle signatures with. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L30) + +## Properties + + diff --git a/content/docs/references/api/deno/signature-verifying-key-options.mdx b/content/docs/references/api/deno/signature-verifying-key-options.mdx new file mode 100644 index 0000000..22f7150 --- /dev/null +++ b/content/docs/references/api/deno/signature-verifying-key-options.mdx @@ -0,0 +1,19 @@ +--- +title: 'SignatureVerifyingKeyOptions' +description: 'Public key configuration: data is the PEM text for PEM formats, or the key bytes otherwise.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Public key configuration: `data` is the PEM text for PEM formats, or the key bytes otherwise. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L24) + +## Properties + +', required: true }, + format: { type: 'VerifyingKeyFormat', required: true }, + }} +/> diff --git a/content/docs/references/api/deno/to-response.mdx b/content/docs/references/api/deno/to-response.mdx new file mode 100644 index 0000000..ef16aca --- /dev/null +++ b/content/docs/references/api/deno/to-response.mdx @@ -0,0 +1,22 @@ +--- +title: 'toResponse' +description: 'Convert an HttpResponse to a web Response (for use inside a Deno.serve handler).' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Convert an [`HttpResponse`](/docs/references/api/deno/http-response) to a web `Response` (for use inside a `Deno.serve` handler). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/protocol.ts#L90) + +```ts +function toResponse(res: HttpResponse): Response; +``` + +## Parameters + + + +## Returns + +`Response` diff --git a/content/docs/references/api/deno/updater-options.mdx b/content/docs/references/api/deno/updater-options.mdx new file mode 100644 index 0000000..7cb735c --- /dev/null +++ b/content/docs/references/api/deno/updater-options.mdx @@ -0,0 +1,23 @@ +--- +title: 'UpdaterOptions' +description: 'UpdaterOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L35) + +## Properties + + diff --git a/content/docs/references/api/deno/updater.mdx b/content/docs/references/api/deno/updater.mdx new file mode 100644 index 0000000..bdfabc6 --- /dev/null +++ b/content/docs/references/api/deno/updater.mdx @@ -0,0 +1,67 @@ +--- +title: 'Updater' +description: 'Coordinates updates between a BundleSource and a Remote: check, download to the remote dir, and activate.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Coordinates updates between a [`BundleSource`](/docs/references/api/deno/bundle-source) and a [`Remote`](/docs/references/api/deno/remote): check, download to the +remote dir, and activate. + +Supports `channel`, `integrityPolicy`, and a declarative `signatureVerifier`. The custom-function +callback options of `@wvb/node` (`integrityChecker`, custom `signatureVerifier`) are not yet +supported over FFI. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L83) + +## Constructor + +```ts +new Updater(source: BundleSource, remote: Remote, options?: UpdaterOptions); +``` + + + +## Methods + + void', required: true, returns: 'void' }, + download: { + type: '(bundleName: string, version?: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version?', description: 'string' }, + ], + returns: 'Promise', + }, + free: { type: '() => void', required: true, returns: 'void' }, + getUpdate: { + type: '(bundleName: string) => Promise', + required: true, + parameters: [{ name: 'bundleName', description: 'string' }], + returns: 'Promise', + }, + install: { + type: '(bundleName: string, version: string) => Promise', + required: true, + parameters: [ + { name: 'bundleName', description: 'string' }, + { name: 'version', description: 'string' }, + ], + returns: 'Promise', + }, + listRemotes: { + type: '() => Promise', + required: true, + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/deno/verifying-key-format.mdx b/content/docs/references/api/deno/verifying-key-format.mdx new file mode 100644 index 0000000..ed6b9d8 --- /dev/null +++ b/content/docs/references/api/deno/verifying-key-format.mdx @@ -0,0 +1,18 @@ +--- +title: 'VerifyingKeyFormat' +description: 'Format of the public key.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Format of the public key. Binary formats (`spkiDer`/`pkcs1Der`/`sec1`/`raw`) take `Uint8Array` +data; the PEM formats take the PEM text. `pkcs1*` is RSA-only, `sec1` is ECDSA-only, `raw` is +Ed25519-only (32 bytes). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/deno/lib/updater.ts#L21) + +## Type + +```ts +type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; +``` diff --git a/content/docs/references/api/electron-builder/after-pack-context.mdx b/content/docs/references/api/electron-builder/after-pack-context.mdx new file mode 100644 index 0000000..27edb57 --- /dev/null +++ b/content/docs/references/api/electron-builder/after-pack-context.mdx @@ -0,0 +1,38 @@ +--- +title: 'AfterPackContext' +description: "Electron builder's after pack context type." +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Electron builder's after pack context type. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/config.ts#L6) + +## Properties + + diff --git a/content/docs/references/api/electron-builder/after-pack-hook.mdx b/content/docs/references/api/electron-builder/after-pack-hook.mdx new file mode 100644 index 0000000..83262de --- /dev/null +++ b/content/docs/references/api/electron-builder/after-pack-hook.mdx @@ -0,0 +1,16 @@ +--- +title: 'AfterPackHook' +description: 'An electron-builder afterPack lifecycle hook.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +An electron-builder `afterPack` lifecycle hook. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/config.ts#L26) + +## Type + +```ts +type AfterPackHook = (context: AfterPackContext) => void | Promise; +``` diff --git a/content/docs/references/api/electron-builder/index.mdx b/content/docs/references/api/electron-builder/index.mdx new file mode 100644 index 0000000..a19f29c --- /dev/null +++ b/content/docs/references/api/electron-builder/index.mdx @@ -0,0 +1,58 @@ +--- +title: '@wvb/electron-builder' +description: 'electron-builder integration for webview bundle' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + + + +## Interfaces + + + + + + +## Type Aliases + + + + + +## Variables + + + + + diff --git a/content/docs/references/api/electron-builder/meta.json b/content/docs/references/api/electron-builder/meta.json new file mode 100644 index 0000000..6801cca --- /dev/null +++ b/content/docs/references/api/electron-builder/meta.json @@ -0,0 +1,17 @@ +{ + "title": "@wvb/electron-builder", + "pages": [ + "---Functions---", + "resolve-resources-path", + "webview-bundle-after-pack", + "with-webview-bundle", + "---Interfaces---", + "after-pack-context", + "webview-bundle-options", + "---Type Aliases---", + "after-pack-hook", + "---Variables---", + "with-wvb", + "wvb-after-pack" + ] +} diff --git a/content/docs/references/api/electron-builder/meta.ko.json b/content/docs/references/api/electron-builder/meta.ko.json new file mode 100644 index 0000000..afee1b7 --- /dev/null +++ b/content/docs/references/api/electron-builder/meta.ko.json @@ -0,0 +1,17 @@ +{ + "title": "@wvb/electron-builder", + "pages": [ + "---함수---", + "resolve-resources-path", + "webview-bundle-after-pack", + "with-webview-bundle", + "---인터페이스---", + "after-pack-context", + "webview-bundle-options", + "---타입---", + "after-pack-hook", + "---변수---", + "with-wvb", + "wvb-after-pack" + ] +} diff --git a/content/docs/references/api/electron-builder/resolve-resources-path.mdx b/content/docs/references/api/electron-builder/resolve-resources-path.mdx new file mode 100644 index 0000000..47a850e --- /dev/null +++ b/content/docs/references/api/electron-builder/resolve-resources-path.mdx @@ -0,0 +1,22 @@ +--- +title: 'resolveResourcesPath' +description: "Resolve the packaged app's Resources directory from an electron-builder afterPack context." +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Resolve the packaged app's `Resources` directory from an electron-builder `afterPack` context. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/setup.ts#L23) + +```ts +function resolveResourcesPath(context: AfterPackContext): string; +``` + +## Parameters + + + +## Returns + +`string` diff --git a/content/docs/references/api/electron-builder/webview-bundle-after-pack.mdx b/content/docs/references/api/electron-builder/webview-bundle-after-pack.mdx new file mode 100644 index 0000000..a8818b9 --- /dev/null +++ b/content/docs/references/api/electron-builder/webview-bundle-after-pack.mdx @@ -0,0 +1,27 @@ +--- +title: 'webviewBundleAfterPack' +description: 'Build an electron-builder afterPack hook that installs builtin Webview Bundles — downloaded from the remote and/or packed from local workspaces as configured…' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Build an electron-builder `afterPack` hook that installs builtin Webview Bundles — downloaded from +the remote and/or packed from local workspaces as configured in your webview-bundle config — and +embeds them into the packaged app's `Resources/`, where `@wvb/electron` looks for them +at runtime. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/setup.ts#L38) + +```ts +function webviewBundleAfterPack( + options: WebviewBundleOptions = {} +): (context: AfterPackContext) => Promise; +``` + +## Parameters + + + +## Returns + +`(context: AfterPackContext) => Promise` diff --git a/content/docs/references/api/electron-builder/webview-bundle-options.mdx b/content/docs/references/api/electron-builder/webview-bundle-options.mdx new file mode 100644 index 0000000..7702732 --- /dev/null +++ b/content/docs/references/api/electron-builder/webview-bundle-options.mdx @@ -0,0 +1,52 @@ +--- +title: 'WebviewBundleOptions' +description: 'Webview bundle options.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Webview bundle options. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/config.ts#L31) + +**Extends:** `Pick` + +## Properties + +/bundles.", + required: false, + default: "'bundles'", + }, + channel: { + type: 'string', + description: 'Release channel to install builtin bundles from (e.g. "beta", "alpha").', + required: false, + }, + configFile: { + type: 'string | boolean', + description: + 'Webview bundle config file option. ; true: auto-discover a config file from the project root and merge it with the inline options below.; string: load the config file at this explicit path (merged with inline).; false: skip config-file loading entirely and use only the inline options.', + required: false, + default: 'true', + }, + root: { + type: 'string', + description: + 'Project root directory. Can be an absolute path, or a path relative from the current file.', + required: false, + default: 'process.cwd()', + }, + throwWhenBuiltinIsEmpty: { + type: 'boolean', + description: 'Throw when no builtin bundles end up installed.', + required: false, + default: 'true', + }, + }} +/> diff --git a/content/docs/references/api/electron-builder/with-webview-bundle.mdx b/content/docs/references/api/electron-builder/with-webview-bundle.mdx new file mode 100644 index 0000000..0863b68 --- /dev/null +++ b/content/docs/references/api/electron-builder/with-webview-bundle.mdx @@ -0,0 +1,38 @@ +--- +title: 'withWebviewBundle' +description: 'Wrap an electron-builder configuration so it installs builtin Webview Bundles at package time.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Wrap an electron-builder configuration so it installs builtin Webview Bundles at package time. + +```ts +// electron-builder.config.ts +import { withWebViewBundle } from '@wvb/electron-builder'; + +export default withWebViewBundle({ + appId: 'com.example.app', + asar: true, + mac: { target: 'dmg' }, +}); +``` + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/setup.ts#L125) + +```ts +function withWebviewBundle(config: C, options: WebviewBundleOptions = {}): C; +``` + +## Parameters + + + +## Returns + +`C` diff --git a/content/docs/references/api/electron-builder/with-wvb.mdx b/content/docs/references/api/electron-builder/with-wvb.mdx new file mode 100644 index 0000000..4aed3cc --- /dev/null +++ b/content/docs/references/api/electron-builder/with-wvb.mdx @@ -0,0 +1,12 @@ +--- +title: 'withWvb' +description: 'withWvb — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/setup.ts#L153) + +**Type:** [`typeof withWebviewBundle`](/docs/references/api/electron-builder/with-webview-bundle) + +See [`typeof withWebviewBundle`](/docs/references/api/electron-builder/with-webview-bundle) for the full member list. diff --git a/content/docs/references/api/electron-builder/wvb-after-pack.mdx b/content/docs/references/api/electron-builder/wvb-after-pack.mdx new file mode 100644 index 0000000..94dffc8 --- /dev/null +++ b/content/docs/references/api/electron-builder/wvb-after-pack.mdx @@ -0,0 +1,12 @@ +--- +title: 'wvbAfterPack' +description: 'wvbAfterPack — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-builder/src/setup.ts#L109) + +**Type:** [`typeof webviewBundleAfterPack`](/docs/references/api/electron-builder/webview-bundle-after-pack) + +See [`typeof webviewBundleAfterPack`](/docs/references/api/electron-builder/webview-bundle-after-pack) for the full member list. diff --git a/content/docs/references/api/electron-forge/index.mdx b/content/docs/references/api/electron-forge/index.mdx new file mode 100644 index 0000000..13fc1e8 --- /dev/null +++ b/content/docs/references/api/electron-forge/index.mdx @@ -0,0 +1,30 @@ +--- +title: '@wvb/electron-forge' +description: 'Electron forge plugin for webview bundle' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Interfaces + + + + + +## Variables + + + + diff --git a/content/docs/references/api/electron-forge/meta.json b/content/docs/references/api/electron-forge/meta.json new file mode 100644 index 0000000..7733ee0 --- /dev/null +++ b/content/docs/references/api/electron-forge/meta.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/electron-forge", + "pages": [ + "---Classes---", + "webview-bundle-plugin", + "---Interfaces---", + "webview-bundle-plugin-config", + "---Variables---", + "wvb-plugin" + ] +} diff --git a/content/docs/references/api/electron-forge/meta.ko.json b/content/docs/references/api/electron-forge/meta.ko.json new file mode 100644 index 0000000..1899570 --- /dev/null +++ b/content/docs/references/api/electron-forge/meta.ko.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/electron-forge", + "pages": [ + "---클래스---", + "webview-bundle-plugin", + "---인터페이스---", + "webview-bundle-plugin-config", + "---변수---", + "wvb-plugin" + ] +} diff --git a/content/docs/references/api/electron-forge/webview-bundle-plugin-config.mdx b/content/docs/references/api/electron-forge/webview-bundle-plugin-config.mdx new file mode 100644 index 0000000..b6cfb7d --- /dev/null +++ b/content/docs/references/api/electron-forge/webview-bundle-plugin-config.mdx @@ -0,0 +1,50 @@ +--- +title: 'WebviewBundlePluginConfig' +description: 'WebviewBundlePluginConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-forge/src/config.ts#L3) + +**Extends:** `Pick` + +## Properties + + diff --git a/content/docs/references/api/electron-forge/webview-bundle-plugin.mdx b/content/docs/references/api/electron-forge/webview-bundle-plugin.mdx new file mode 100644 index 0000000..c6ffaac --- /dev/null +++ b/content/docs/references/api/electron-forge/webview-bundle-plugin.mdx @@ -0,0 +1,44 @@ +--- +title: 'WebviewBundlePlugin' +description: 'WebviewBundlePlugin — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-forge/src/plugin.ts#L27) + +**Extends:** `default` + +## Constructor + +```ts +new WebviewBundlePlugin(config: WebviewBundlePluginConfig = {}); +``` + + + +## Properties + + + +## Methods + + ForgeMultiHookMap', required: true, returns: 'ForgeMultiHookMap' }, + packageAfterCopy: { + type: '(_forgeConfig: ResolvedForgeConfig, buildPath: string, _electronVersion: string, _platform: string, _arch: string) => Promise', + required: true, + parameters: [ + { name: '_forgeConfig', description: 'ResolvedForgeConfig' }, + { name: 'buildPath', description: 'string' }, + { name: '_electronVersion', description: 'string' }, + { name: '_platform', description: 'string' }, + { name: '_arch', description: 'string' }, + ], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/electron-forge/wvb-plugin.mdx b/content/docs/references/api/electron-forge/wvb-plugin.mdx new file mode 100644 index 0000000..b9fc03e --- /dev/null +++ b/content/docs/references/api/electron-forge/wvb-plugin.mdx @@ -0,0 +1,12 @@ +--- +title: 'WvbPlugin' +description: 'WvbPlugin — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron-forge/src/plugin.ts#L116) + +**Type:** [`typeof WebviewBundlePlugin`](/docs/references/api/electron-forge/webview-bundle-plugin) + +See [`typeof WebviewBundlePlugin`](/docs/references/api/electron-forge/webview-bundle-plugin) for the full member list. diff --git a/content/docs/references/api/electron/bundle-protocol.mdx b/content/docs/references/api/electron/bundle-protocol.mdx new file mode 100644 index 0000000..9c691e0 --- /dev/null +++ b/content/docs/references/api/electron/bundle-protocol.mdx @@ -0,0 +1,25 @@ +--- +title: 'bundleProtocol' +description: 'bundleProtocol — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/protocol.ts#L122) + +```ts +function bundleProtocol(scheme: string, config: BundleProtocolConfig = {}): Protocol; +``` + +## Parameters + + + +## Returns + +`Protocol` diff --git a/content/docs/references/api/electron/index.mdx b/content/docs/references/api/electron/index.mdx new file mode 100644 index 0000000..ea3419e --- /dev/null +++ b/content/docs/references/api/electron/index.mdx @@ -0,0 +1,36 @@ +--- +title: '@wvb/electron' +description: 'Webview Bundle API for Electron' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + + + +## Interfaces + + + + + + + + + + + + +## Variables + + + + diff --git a/content/docs/references/api/electron/local-protocol.mdx b/content/docs/references/api/electron/local-protocol.mdx new file mode 100644 index 0000000..6c93fb6 --- /dev/null +++ b/content/docs/references/api/electron/local-protocol.mdx @@ -0,0 +1,25 @@ +--- +title: 'localProtocol' +description: 'localProtocol — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/protocol.ts#L100) + +```ts +function localProtocol(scheme: string, config: LocalProtocolConfig): Protocol; +``` + +## Parameters + + + +## Returns + +`Protocol` diff --git a/content/docs/references/api/electron/meta.json b/content/docs/references/api/electron/meta.json new file mode 100644 index 0000000..e064912 --- /dev/null +++ b/content/docs/references/api/electron/meta.json @@ -0,0 +1,20 @@ +{ + "title": "@wvb/electron", + "pages": [ + "---Functions---", + "bundle-protocol", + "local-protocol", + "webview-bundle", + "---Interfaces---", + "protocol", + "protocol-handler", + "protocol-options", + "remote-options", + "source-options", + "webview-bundle-interface", + "webview-bundle-config", + "webview-bundle-remote-config", + "---Variables---", + "wvb" + ] +} diff --git a/content/docs/references/api/electron/meta.ko.json b/content/docs/references/api/electron/meta.ko.json new file mode 100644 index 0000000..dd42689 --- /dev/null +++ b/content/docs/references/api/electron/meta.ko.json @@ -0,0 +1,20 @@ +{ + "title": "@wvb/electron", + "pages": [ + "---함수---", + "bundle-protocol", + "local-protocol", + "webview-bundle", + "---인터페이스---", + "protocol", + "protocol-handler", + "protocol-options", + "remote-options", + "source-options", + "webview-bundle-interface", + "webview-bundle-config", + "webview-bundle-remote-config", + "---변수---", + "wvb" + ] +} diff --git a/content/docs/references/api/electron/protocol-handler.mdx b/content/docs/references/api/electron/protocol-handler.mdx new file mode 100644 index 0000000..033631e --- /dev/null +++ b/content/docs/references/api/electron/protocol-handler.mdx @@ -0,0 +1,21 @@ +--- +title: 'ProtocolHandler' +description: 'ProtocolHandler — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/protocol.ts#L13) + +## Methods + + Promise', + required: true, + parameters: [{ name: 'req', description: 'Request' }], + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/electron/protocol-options.mdx b/content/docs/references/api/electron/protocol-options.mdx new file mode 100644 index 0000000..2c4314a --- /dev/null +++ b/content/docs/references/api/electron/protocol-options.mdx @@ -0,0 +1,26 @@ +--- +title: 'ProtocolOptions' +description: 'ProtocolOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/protocol.ts#L17) + +## Properties + + + +## Methods + + void', + required: false, + parameters: [{ name: 'e', description: 'Error' }], + returns: 'void', + }, + protocol: { type: '() => Protocol', required: false, returns: 'Protocol' }, + }} +/> diff --git a/content/docs/references/api/electron/protocol.mdx b/content/docs/references/api/electron/protocol.mdx new file mode 100644 index 0000000..d16ea7d --- /dev/null +++ b/content/docs/references/api/electron/protocol.mdx @@ -0,0 +1,18 @@ +--- +title: 'Protocol' +description: 'Protocol — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/protocol.ts#L30) + +## Properties + + diff --git a/content/docs/references/api/electron/remote-options.mdx b/content/docs/references/api/electron/remote-options.mdx new file mode 100644 index 0000000..e4102d0 --- /dev/null +++ b/content/docs/references/api/electron/remote-options.mdx @@ -0,0 +1,27 @@ +--- +title: 'RemoteOptions' +description: 'RemoteOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/remote.ts#L3) + +**Extends:** `RemoteOptions` + +## Properties + + + +## Methods + + void', + required: false, + parameters: [{ name: 'data', description: 'RemoteOnDownloadData' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/electron/source-options.mdx b/content/docs/references/api/electron/source-options.mdx new file mode 100644 index 0000000..15e1bc4 --- /dev/null +++ b/content/docs/references/api/electron/source-options.mdx @@ -0,0 +1,21 @@ +--- +title: 'SourceOptions' +description: 'SourceOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/source.ts#L5) + +**Extends:** `Omit` + +## Properties + + diff --git a/content/docs/references/api/electron/webview-bundle-config.mdx b/content/docs/references/api/electron/webview-bundle-config.mdx new file mode 100644 index 0000000..a6f050e --- /dev/null +++ b/content/docs/references/api/electron/webview-bundle-config.mdx @@ -0,0 +1,18 @@ +--- +title: 'WebviewBundleConfig' +description: 'WebviewBundleConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/webview-bundle.ts#L15) + +## Properties + + diff --git a/content/docs/references/api/electron/webview-bundle-interface.mdx b/content/docs/references/api/electron/webview-bundle-interface.mdx new file mode 100644 index 0000000..d9b90fb --- /dev/null +++ b/content/docs/references/api/electron/webview-bundle-interface.mdx @@ -0,0 +1,31 @@ +--- +title: 'WebviewBundle' +description: 'WebviewBundle — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/webview-bundle.ts#L21) + +## Properties + + + +## Methods + + Promise', + required: true, + returns: 'Promise', + }, + }} +/> diff --git a/content/docs/references/api/electron/webview-bundle-remote-config.mdx b/content/docs/references/api/electron/webview-bundle-remote-config.mdx new file mode 100644 index 0000000..cba9f17 --- /dev/null +++ b/content/docs/references/api/electron/webview-bundle-remote-config.mdx @@ -0,0 +1,32 @@ +--- +title: 'WebviewBundleRemoteConfig' +description: 'WebviewBundleRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/webview-bundle.ts#L7) + +**Extends:** `RemoteOptions` + +## Properties + + + +## Methods + + void', + required: false, + parameters: [{ name: 'data', description: 'RemoteOnDownloadData' }], + returns: 'void', + }, + }} +/> diff --git a/content/docs/references/api/electron/webview-bundle.mdx b/content/docs/references/api/electron/webview-bundle.mdx new file mode 100644 index 0000000..110e06c --- /dev/null +++ b/content/docs/references/api/electron/webview-bundle.mdx @@ -0,0 +1,20 @@ +--- +title: 'webviewBundle' +description: 'webviewBundle — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/webview-bundle.ts#L63) + +```ts +function webviewBundle(config: WebviewBundleConfig): WebviewBundle; +``` + +## Parameters + + + +## Returns + +`WebviewBundle` diff --git a/content/docs/references/api/electron/wvb.mdx b/content/docs/references/api/electron/wvb.mdx new file mode 100644 index 0000000..cd6bee0 --- /dev/null +++ b/content/docs/references/api/electron/wvb.mdx @@ -0,0 +1,12 @@ +--- +title: 'wvb' +description: 'wvb — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/electron/src/webview-bundle.ts#L69) + +**Type:** [`typeof webviewBundle`](/docs/references/api/electron/webview-bundle) + +See [`typeof webviewBundle`](/docs/references/api/electron/webview-bundle) for the full member list. diff --git a/content/docs/references/api/meta.json b/content/docs/references/api/meta.json new file mode 100644 index 0000000..f680b12 --- /dev/null +++ b/content/docs/references/api/meta.json @@ -0,0 +1,15 @@ +{ + "title": "API", + "pages": [ + "node", + "bridge", + "cli", + "config", + "electron", + "electron-builder", + "electron-forge", + "deno", + "deno-desktop", + "remotes" + ] +} diff --git a/content/docs/references/api/meta.ko.json b/content/docs/references/api/meta.ko.json new file mode 100644 index 0000000..f680b12 --- /dev/null +++ b/content/docs/references/api/meta.ko.json @@ -0,0 +1,15 @@ +{ + "title": "API", + "pages": [ + "node", + "bridge", + "cli", + "config", + "electron", + "electron-builder", + "electron-forge", + "deno", + "deno-desktop", + "remotes" + ] +} diff --git a/content/docs/references/api/node/build-header-options.mdx b/content/docs/references/api/node/build-header-options.mdx new file mode 100644 index 0000000..234fd58 --- /dev/null +++ b/content/docs/references/api/node/build-header-options.mdx @@ -0,0 +1,22 @@ +--- +title: 'BuildHeaderOptions' +description: 'Options for bundle header generation.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Options for bundle header generation. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1040) + +## Properties + + diff --git a/content/docs/references/api/node/build-index-options.mdx b/content/docs/references/api/node/build-index-options.mdx new file mode 100644 index 0000000..ce78c5b --- /dev/null +++ b/content/docs/references/api/node/build-index-options.mdx @@ -0,0 +1,22 @@ +--- +title: 'BuildIndexOptions' +description: 'Options for bundle index generation.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Options for bundle index generation. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1049) + +## Properties + + diff --git a/content/docs/references/api/node/build-options.mdx b/content/docs/references/api/node/build-options.mdx new file mode 100644 index 0000000..5ed53fb --- /dev/null +++ b/content/docs/references/api/node/build-options.mdx @@ -0,0 +1,28 @@ +--- +title: 'BuildOptions' +description: 'Options for building a bundle.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Options for building a bundle. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1060) + +## Properties + + diff --git a/content/docs/references/api/node/bundle-builder.mdx b/content/docs/references/api/node/bundle-builder.mdx new file mode 100644 index 0000000..9e46c56 --- /dev/null +++ b/content/docs/references/api/node/bundle-builder.mdx @@ -0,0 +1,152 @@ +--- +title: 'BundleBuilder' +description: 'Builder for creating bundle files.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Builder for creating bundle files. + +Allows you to add files, set options, and generate a complete bundle. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L80) + +## Constructor + +```ts +new BundleBuilder(version?: "v1" | null); +``` + + + +## Properties + + + +## Methods + + Bundle', + description: + "Builds the bundle with all added files. This consumes the builder's entries and creates a complete bundle with compressed data.", + required: true, + parameters: [{ name: 'options?', description: 'BuildOptions | null — Build options' }], + returns: 'Bundle — The built bundle', + }, + containsEntry: { + type: '(path: string) => boolean', + description: 'Checks if a file path exists in the builder.', + required: true, + parameters: [{ name: 'path', description: 'string — File path to check' }], + returns: 'boolean — True if the path exists', + }, + entryPaths: { + type: '() => string[]', + description: 'Returns all entry paths currently in the builder.', + required: true, + returns: 'string[] — Array of file paths', + }, + insertEntry: { + type: '(path: string, data: Buffer, contentType?: string | null, headers?: Record | null) => boolean', + description: + 'Adds or updates a file in the bundle. If contentType is not provided, it will be auto-detected from the file extension and content.', + required: true, + parameters: [ + { name: 'path', description: 'string — File path (must start with "/")' }, + { name: 'data', description: 'Buffer — File contents' }, + { + name: 'contentType?', + description: 'string | null — MIME type (auto-detected if not provided)', + }, + { name: 'headers?', description: 'Record | null — Optional HTTP headers' }, + ], + returns: 'boolean — True if a file was replaced, false if newly added', + }, + removeEntry: { + type: '(path: string) => boolean', + description: 'Removes a file from the bundle.', + required: true, + parameters: [{ name: 'path', description: 'string — File path to remove' }], + returns: 'boolean — True if the file was removed, false if not found', + }, + }} +/> + +## Examples + +```typescript +const builder = new BundleBuilder(); + +// Add files +builder.insertEntry('/index.html', Buffer.from('...')); +builder.insertEntry('/app.js', Buffer.from("console.log('hello');")); + +// Build the bundle +const bundle = builder.build(); + +// Write to file +await writeBundle(bundle, 'app.wvb'); +``` + +### constructor + +```typescript +const builder = new BundleBuilder(); +``` + +### build + +```typescript +const bundle = builder.build(); +await writeBundle(bundle, 'output.wvb'); +``` + +### containsEntry + +```typescript +if (builder.containsEntry('/index.html')) { + console.log('index.html already added'); +} +``` + +### entryPaths + +```typescript +const paths = builder.entryPaths(); +console.log(paths); // ["/index.html", "/app.js"] +``` + +### insertEntry + +```typescript +// Auto-detect MIME type +builder.insertEntry('/index.html', Buffer.from('')); + +// Specify MIME type +builder.insertEntry('/data.bin', buffer, 'application/octet-stream'); + +// With custom headers +builder.insertEntry('/style.css', cssBuffer, 'text/css', { + 'Cache-Control': 'max-age=3600', +}); +``` + +### removeEntry + +```typescript +builder.removeEntry('/old-file.js'); +``` diff --git a/content/docs/references/api/node/bundle-descriptor.mdx b/content/docs/references/api/node/bundle-descriptor.mdx new file mode 100644 index 0000000..502d7aa --- /dev/null +++ b/content/docs/references/api/node/bundle-descriptor.mdx @@ -0,0 +1,88 @@ +--- +title: 'BundleDescriptor' +description: 'Bundle metadata including header and index information.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle metadata including header and index information. + +A descriptor contains only the metadata without loading the actual file data, +making it efficient for inspecting bundle contents. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L195) + +## Constructor + +```ts +new BundleDescriptor(); +``` + +## Methods + + Promise | null>', + description: 'Asynchronously read data from the bundle.', + required: true, + parameters: [ + { name: 'filepath', description: 'string — File path for the bundle' }, + { name: 'path', description: 'string — Path to the bundle data' }, + ], + returns: + 'Promise | null> — Data from the bundle or null if not found', + }, + asyncGetDataChecksum: { + type: '(filepath: string, path: string) => Promise', + description: 'Asynchronously read checksum from the bundle.', + required: true, + parameters: [ + { name: 'filepath', description: 'string — File path for the bundle' }, + { name: 'path', description: 'string — Path to the bundle data' }, + ], + returns: 'Promise — Checksum for the bundle data or null if not found', + }, + getData: { + type: '(filepath: string, path: string) => Buffer | null', + description: 'Read data from the bundle.', + required: true, + parameters: [ + { name: 'filepath', description: 'string — File path for the bundle' }, + { name: 'path', description: 'string — Path to the bundle data' }, + ], + returns: 'Buffer | null — Data from the bundle or null if not found', + }, + getDataChecksum: { + type: '(filepath: string, path: string) => number | null', + description: 'Read checksum from the bundle.', + required: true, + parameters: [ + { name: 'filepath', description: 'string — File path for the bundle' }, + { name: 'path', description: 'string — Path to the bundle data' }, + ], + returns: 'number | null — Checksum for the bundle data or null if not found', + }, + header: { + type: '() => Header', + description: 'Returns the bundle header.', + required: true, + returns: 'Header — Bundle header with format metadata', + }, + index: { + type: '() => Index', + description: 'Returns the bundle index.', + required: true, + returns: 'Index — Bundle index with file metadata', + }, + }} +/> + +## Examples + +```typescript +const bundle = await readBundle('app.wvb'); +const descriptor = bundle.descriptor(); +const header = descriptor.header(); +const index = descriptor.index(); +``` diff --git a/content/docs/references/api/node/bundle-manifest-data.mdx b/content/docs/references/api/node/bundle-manifest-data.mdx new file mode 100644 index 0000000..83378f3 --- /dev/null +++ b/content/docs/references/api/node/bundle-manifest-data.mdx @@ -0,0 +1,29 @@ +--- +title: 'BundleManifestData' +description: 'Complete manifest data structure.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Complete manifest data structure. + +The manifest tracks all bundle versions and metadata. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1074) + +## Properties + +', + description: 'Bundle entries by name', + required: true, + }, + manifestVersion: { + type: '1', + description: 'Manifest format version (always 1)', + required: true, + }, + }} +/> diff --git a/content/docs/references/api/node/bundle-manifest-entry.mdx b/content/docs/references/api/node/bundle-manifest-entry.mdx new file mode 100644 index 0000000..51bc4d2 --- /dev/null +++ b/content/docs/references/api/node/bundle-manifest-entry.mdx @@ -0,0 +1,25 @@ +--- +title: 'BundleManifestEntry' +description: 'Entry for a single bundle in the manifest.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Entry for a single bundle in the manifest. + +Contains all versions and the current active version. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1087) + +## Properties + +', + description: 'Available versions', + required: true, + }, + }} +/> diff --git a/content/docs/references/api/node/bundle-manifest-metadata.mdx b/content/docs/references/api/node/bundle-manifest-metadata.mdx new file mode 100644 index 0000000..d127454 --- /dev/null +++ b/content/docs/references/api/node/bundle-manifest-metadata.mdx @@ -0,0 +1,31 @@ +--- +title: 'BundleManifestMetadata' +description: 'Metadata for a bundle version in the manifest.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Metadata for a bundle version in the manifest. + +Contains cache validation and integrity information. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1102) + +## Properties + + diff --git a/content/docs/references/api/node/bundle-manifest-version.mdx b/content/docs/references/api/node/bundle-manifest-version.mdx new file mode 100644 index 0000000..dadc91e --- /dev/null +++ b/content/docs/references/api/node/bundle-manifest-version.mdx @@ -0,0 +1,14 @@ +--- +title: 'BundleManifestVersion' +description: 'Manifest format version.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Manifest format version. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1114) + +## Members + + diff --git a/content/docs/references/api/node/bundle-protocol.mdx b/content/docs/references/api/node/bundle-protocol.mdx new file mode 100644 index 0000000..cb5aecf --- /dev/null +++ b/content/docs/references/api/node/bundle-protocol.mdx @@ -0,0 +1,96 @@ +--- +title: 'BundleProtocol' +description: 'Protocol handler for serving files from bundle sources.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Protocol handler for serving files from bundle sources. + +Serves web resources from `.wvb` bundle files, supporting: + +- GET and HEAD HTTP methods +- HTTP Range requests for streaming +- Content-Type and custom HTTP headers + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L265) + +## Constructor + +```ts +new BundleProtocol(source: BundleSource); +``` + + + +## Methods + + | null) => Promise', + description: + 'Handles an HTTP request and returns a response. Processes requests in the format scheme://bundle_name/path/to/file.', + required: true, + parameters: [ + { name: 'method', description: 'HttpMethod — HTTP method (GET or HEAD)' }, + { name: 'uri', description: 'string — Request URI (e.g., "bundle://app/index.html")' }, + { + name: 'headers?', + description: 'Record | null — Optional request headers', + }, + ], + returns: 'Promise — HTTP response', + }, + }} +/> + +## Examples + +```typescript +const source = new BundleSource({ + builtinDir: './bundles/builtin', + remoteDir: './bundles/remote', +}); + +const protocol = new BundleProtocol(source); + +// Handle a request +const response = await protocol.handle('get', 'bundle://app/index.html'); +console.log(`Status: ${response.status}`); +console.log(`Content-Type: ${response.headers['content-type']}`); +``` + +### constructor + +```typescript +const source = new BundleSource({ + builtinDir: './bundles', + remoteDir: './remote', +}); +const protocol = new BundleProtocol(source); +``` + +### handle + +```typescript +// GET request +const response = await protocol.handle('get', 'bundle://app/index.html'); +if (response.status === 200) { + console.log(response.body.toString('utf-8')); +} +``` + +```typescript +// Range request for streaming +const response = await protocol.handle('get', 'bundle://app/video.mp4', { Range: 'bytes=0-1023' }); +console.log(`Status: ${response.status}`); // 206 Partial Content +``` diff --git a/content/docs/references/api/node/bundle-source-config.mdx b/content/docs/references/api/node/bundle-source-config.mdx new file mode 100644 index 0000000..39a9963 --- /dev/null +++ b/content/docs/references/api/node/bundle-source-config.mdx @@ -0,0 +1,47 @@ +--- +title: 'BundleSourceConfig' +description: 'Configuration for creating a bundle source.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Configuration for creating a bundle source. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1135) + +## Properties + + + +## Examples + +```typescript +const config = { + builtinDir: './bundles/builtin', + remoteDir: './bundles/remote', +}; +const source = new BundleSource(config); +``` diff --git a/content/docs/references/api/node/bundle-source-kind.mdx b/content/docs/references/api/node/bundle-source-kind.mdx new file mode 100644 index 0000000..5c36722 --- /dev/null +++ b/content/docs/references/api/node/bundle-source-kind.mdx @@ -0,0 +1,16 @@ +--- +title: 'BundleSourceKind' +description: 'The type of bundle source: builtin or remote.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +The type of bundle source: builtin or remote. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1147) + +## Type + +```ts +type BundleSourceKind = 'builtin' | 'remote'; +``` diff --git a/content/docs/references/api/node/bundle-source-version.mdx b/content/docs/references/api/node/bundle-source-version.mdx new file mode 100644 index 0000000..30a8bed --- /dev/null +++ b/content/docs/references/api/node/bundle-source-version.mdx @@ -0,0 +1,21 @@ +--- +title: 'BundleSourceVersion' +description: 'Bundle version with source kind information.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle version with source kind information. + +Indicates which source (builtin or remote) provides a bundle version. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1160) + +## Properties + + diff --git a/content/docs/references/api/node/bundle-source.mdx b/content/docs/references/api/node/bundle-source.mdx new file mode 100644 index 0000000..625a6a0 --- /dev/null +++ b/content/docs/references/api/node/bundle-source.mdx @@ -0,0 +1,290 @@ +--- +title: 'BundleSource' +description: 'Bundle source for managing multiple bundle versions.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle source for managing multiple bundle versions. + +A source manages bundles in two directories: + +- **builtin**: Bundles shipped with the app (read-only, fallback) +- **remote**: Downloaded bundles (takes priority) + +The source automatically handles version selection, with remote bundles +taking priority over builtin ones. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L337) + +## Constructor + +```ts +new BundleSource(config: BundleSourceConfig); +``` + + + +## Methods + + Promise', + description: 'Fetches a builtin bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'Promise — Fetched bundle', + }, + fetchBundle: { + type: '(bundleName: string) => Promise', + description: 'Fetches a bundle.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Fetched bundle', + }, + fetchDescriptor: { + type: '(bundleName: string) => Promise', + description: 'Fetches only the bundle descriptor.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Bundle descriptor', + }, + fetchRemoteBundle: { + type: '(bundleName: string, version: string) => Promise', + description: 'Fetches a remote bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'Promise — Fetched bundle', + }, + getBuiltinBundleFilepath: { + type: '(bundleName: string, version: string) => string', + description: 'Get the file path for a builtin bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'string — Absolute file path', + }, + getRemoteBundleFilepath: { + type: '(bundleName: string, version: string) => string', + description: 'Get the file path for a remote bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'string — Absolute file path', + }, + listBundles: { + type: '() => Promise', + description: + 'Lists all available bundles from both sources. Returns bundles from both builtin and remote directories, including all versions and metadata.', + required: true, + returns: 'Promise — List of bundle items', + }, + loadBuiltinMetadata: { + type: '(bundleName: string, version: string) => Promise', + description: 'Load builtin bundle metadata.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'Promise — Loaded metadata', + }, + loadDescriptor: { + type: '(bundleName: string) => Promise', + description: + 'Loads (and caches) the descriptor for the current version of a bundle. The descriptor reads entry data lazily from disk via LoadedDescriptor.getData, avoiding loading the full bundle into memory. Concurrent calls for the same bundle share a single load (single-flight) and return the cached descriptor until the active version changes or BundleSource.unloadDescriptor is called.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Loaded descriptor', + }, + loadRemoteMetadata: { + type: '(bundleName: string, version: string) => Promise', + description: 'Load remote bundle metadata.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version of the bundle' }, + ], + returns: 'Promise — Loaded metadata', + }, + loadVersion: { + type: '(bundleName: string) => Promise', + description: + 'Loads the current version for a bundle. Returns the version from remote if available, otherwise from builtin.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Version info or null if not found', + }, + pruneRemoteBundles: { + type: '(bundleName: string) => Promise', + description: + 'Removes every staged remote version except the retained set (current and previous).', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Versions that were removed', + }, + remoteRetainedVersions: { + type: '(bundleName: string) => Promise', + description: + 'Returns the remote versions that pruning retains (the current and previous versions).', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Retained version strings', + }, + removeRemoteBundle: { + type: '(bundleName: string, version: string) => Promise', + description: + 'Removes a single staged remote bundle version. Drops its manifest entry and deletes its file from disk.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version to remove' }, + ], + returns: 'Promise — True if the entry existed and was removed', + }, + resolveFilepath: { + type: '(bundleName: string) => Promise', + description: + 'Gets the file path for a bundle. Returns the path to the .wvb file for the current version, preferring remote over builtin.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'Promise — Absolute file path', + }, + unloadDescriptor: { + type: '(bundleName: string) => boolean', + description: + 'Drops the cached descriptor for a bundle, if present. Already-returned LoadedDescriptor handles keep working; they hold their own reference and are unaffected. The next BundleSource.loadDescriptor reloads from disk.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle' }], + returns: 'boolean — True if a cached descriptor was removed', + }, + updateRemoteVersion: { + type: '(bundleName: string, version: string) => Promise', + description: + 'Updates the current version for a remote bundle. Changes which version is considered "current" in the manifest.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version to set as current' }, + ], + returns: 'Promise', + }, + writeRemoteBundle: { + type: '(bundleName: string, version: string, bundle: Bundle, metadata: BundleManifestMetadata) => Promise', + description: + 'Writes a bundle to the remote directory. Installs a new bundle version to the remote directory and updates the manifest.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Version string' }, + { name: 'bundle', description: 'Bundle — Bundle to write' }, + { name: 'metadata', description: 'BundleManifestMetadata — Bundle metadata' }, + ], + returns: 'Promise', + }, + }} +/> + +## Examples + +```typescript +const source = new BundleSource({ + builtinDir: './bundles/builtin', + remoteDir: './bundles/remote', +}); + +// List all bundles +const bundles = await source.listBundles(); + +// Load current version +const version = await source.loadVersion('app'); + +// Fetch bundle +const bundle = await source.fetch('app'); +``` + +### constructor + +```typescript +const source = new BundleSource({ + builtinDir: './builtin', + remoteDir: './remote', +}); +``` + +### fetchBundle + +```typescript +const bundle = await source.fetchBundle('app'); +const html = bundle.getData('/index.html'); +``` + +### fetchDescriptor + +```typescript +const descriptor = await source.fetchDescriptor('app'); +const index = descriptor.index(); +console.log(`Files: ${Object.keys(index.entries()).length}`); +``` + +### listBundles + +```typescript +const bundles = await source.listBundles(); +for (const bundle of bundles) { + console.log(`${bundle.name}@${bundle.version} (${bundle.type})`); +} +``` + +### loadDescriptor + +```typescript +const loaded = await source.loadDescriptor('app'); +const html = await loaded.getData('/index.html'); +``` + +### loadVersion + +```typescript +const version = await source.loadVersion('app'); +if (version) { + console.log(`Current version: ${version.version} (${version.type})`); +} +``` + +### resolveFilepath + +```typescript +const path = await source.resolveFilepath('app'); +console.log(`Bundle at: ${path}`); +``` + +### updateRemoteVersion + +```typescript +await source.updateRemoteVersion('app', '1.2.0'); +``` + +### writeRemoteBundle + +```typescript +await source.writeRemoteBundle('app', '1.2.0', bundle, { + integrity: 'sha3-384-...', + etag: 'abc123', +}); +``` diff --git a/content/docs/references/api/node/bundle-update-info.mdx b/content/docs/references/api/node/bundle-update-info.mdx new file mode 100644 index 0000000..a3df29a --- /dev/null +++ b/content/docs/references/api/node/bundle-update-info.mdx @@ -0,0 +1,39 @@ +--- +title: 'BundleUpdateInfo' +description: 'Information about a bundle update.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Information about a bundle update. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1186) + +## Properties + + + +## Examples + +```typescript +const updateInfo = await updater.getUpdate('app'); +if (updateInfo.isAvailable) { + console.log(`Update available: ${updateInfo.localVersion} → ${updateInfo.version}`); + await updater.download('app'); +} +``` diff --git a/content/docs/references/api/node/bundle.mdx b/content/docs/references/api/node/bundle.mdx new file mode 100644 index 0000000..e690219 --- /dev/null +++ b/content/docs/references/api/node/bundle.mdx @@ -0,0 +1,78 @@ +--- +title: 'Bundle' +description: 'A complete bundle including metadata and file data.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A complete bundle including metadata and file data. + +Represents a `.wvb` bundle file loaded entirely into memory. +Use this when you need to access multiple files or build new bundles. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L21) + +## Constructor + +```ts +new Bundle(); +``` + +## Methods + + BundleDescriptor', + description: 'Returns the bundle descriptor (header and index).', + required: true, + returns: 'BundleDescriptor — Bundle metadata', + }, + getData: { + type: '(path: string) => Buffer | null', + description: + "Retrieves file data by path. Returns the decompressed file contents, or null if the path doesn't exist.", + required: true, + parameters: [ + { name: 'path', description: 'string — File path in the bundle (e.g., "/index.html")' }, + ], + returns: 'Buffer | null — File contents or null if not found', + }, + getDataChecksum: { + type: '(path: string) => number | null', + description: 'Retrieves the checksum of file data by path.', + required: true, + parameters: [{ name: 'path', description: 'string — File path in the bundle' }], + returns: 'number | null — xxHash-32 checksum or null if not found', + }, + }} +/> + +## Examples + +```typescript +// Read a bundle from file +const bundle = await readBundle('app.wvb'); + +// Access files +const html = bundle.getData('/index.html'); +if (html) { + console.log(html.toString('utf-8')); +} +``` + +### descriptor + +```typescript +const descriptor = bundle.descriptor(); +const index = descriptor.index(); +``` + +### getData + +```typescript +const data = bundle.getData('/index.html'); +if (data) { + console.log(data.toString('utf-8')); +} +``` diff --git a/content/docs/references/api/node/header.mdx b/content/docs/references/api/node/header.mdx new file mode 100644 index 0000000..4fb6926 --- /dev/null +++ b/content/docs/references/api/node/header.mdx @@ -0,0 +1,58 @@ +--- +title: 'Header' +description: 'Bundle header containing format metadata.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle header containing format metadata. + +The header is the first 17 bytes of a `.wvb` file and includes: + +- Magic number (🌐🎁) +- Format version +- Index size +- Header checksum + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L576) + +## Constructor + +```ts +new Header(); +``` + +## Methods + + bigint', + description: + 'Returns the byte offset where the index section ends. This marks the start of the data section.', + required: true, + returns: 'bigint — Byte offset', + }, + indexSize: { + type: '() => number', + description: 'Returns the size of the index section in bytes.', + required: true, + returns: 'number — Index size in bytes', + }, + version: { + type: '() => "v1"', + description: 'Returns the bundle format version.', + required: true, + returns: '"v1" — The format version (e.g., V1)', + }, + }} +/> + +## Examples + +### version + +```typescript +const header = bundle.descriptor().header(); +console.log(header.version()); // 'v1' +``` diff --git a/content/docs/references/api/node/http-method.mdx b/content/docs/references/api/node/http-method.mdx new file mode 100644 index 0000000..a0b3bdd --- /dev/null +++ b/content/docs/references/api/node/http-method.mdx @@ -0,0 +1,23 @@ +--- +title: 'HttpMethod' +description: 'HttpMethod — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1197) + +## Type + +```ts +type HttpMethod = + | 'get' + | 'head' + | 'options' + | 'post' + | 'put' + | 'patch' + | 'delete' + | 'trace' + | 'connect'; +``` diff --git a/content/docs/references/api/node/http-options.mdx b/content/docs/references/api/node/http-options.mdx new file mode 100644 index 0000000..9a8b506 --- /dev/null +++ b/content/docs/references/api/node/http-options.mdx @@ -0,0 +1,25 @@ +--- +title: 'HttpOptions' +description: 'HttpOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1207) + +## Properties + +', required: false }, + hickoryDns: { type: 'boolean', required: false }, + poolIdleTimeout: { type: 'number', required: false }, + poolMaxIdlePerHost: { type: 'number', required: false }, + readTimeout: { type: 'number', required: false }, + referer: { type: 'boolean', required: false }, + tcpNodelay: { type: 'boolean', required: false }, + timeout: { type: 'number', required: false }, + userAgent: { type: 'string', required: false }, + }} +/> diff --git a/content/docs/references/api/node/http-response.mdx b/content/docs/references/api/node/http-response.mdx new file mode 100644 index 0000000..21f0bec --- /dev/null +++ b/content/docs/references/api/node/http-response.mdx @@ -0,0 +1,18 @@ +--- +title: 'HttpResponse' +description: 'HttpResponse — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1220) + +## Properties + +', required: true }, + status: { type: 'number', required: true }, + }} +/> diff --git a/content/docs/references/api/node/index-class.mdx b/content/docs/references/api/node/index-class.mdx new file mode 100644 index 0000000..ed2ae3b --- /dev/null +++ b/content/docs/references/api/node/index-class.mdx @@ -0,0 +1,77 @@ +--- +title: 'Index' +description: 'Bundle index mapping file paths to their metadata.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle index mapping file paths to their metadata. + +The index is stored as binary data in the bundle file and maps file paths +to their metadata (offset, length, content-type, headers, etc.). + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L611) + +## Constructor + +```ts +new Index(); +``` + +## Methods + + boolean', + description: 'Checks if a file path exists in the bundle.', + required: true, + parameters: [{ name: 'path', description: 'string — File path to check' }], + returns: 'boolean — True if the path exists', + }, + entries: { + type: '() => Record', + description: 'Returns all index entries as a map of path to metadata.', + required: true, + returns: 'Record — Map of file paths to entry metadata', + }, + getEntry: { + type: '(path: string) => IndexEntry | null', + description: 'Gets the index entry for a specific file path.', + required: true, + parameters: [ + { name: 'path', description: 'string — File path in the bundle (e.g., "/index.html")' }, + ], + returns: 'IndexEntry | null — Entry metadata or null if not found', + }, + }} +/> + +## Examples + +### containsPath + +```typescript +if (index.containsPath('/app.js')) { + console.log('app.js is in the bundle'); +} +``` + +### entries + +```typescript +const index = bundle.descriptor().index(); +const entries = index.entries(); +for (const [path, entry] of Object.entries(entries)) { + console.log(`${path}: ${entry.contentType}`); +} +``` + +### getEntry + +```typescript +const entry = index.getEntry('/index.html'); +if (entry) { + console.log(`Content-Type: ${entry.contentType}`); +} +``` diff --git a/content/docs/references/api/node/index-entry.mdx b/content/docs/references/api/node/index-entry.mdx new file mode 100644 index 0000000..4603a33 --- /dev/null +++ b/content/docs/references/api/node/index-entry.mdx @@ -0,0 +1,41 @@ +--- +title: 'IndexEntry' +description: 'Metadata for a single file in the bundle.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Metadata for a single file in the bundle. + +Contains information about file location, size, MIME type, and HTTP headers. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1238) + +## Properties + +', + description: 'HTTP headers for this file', + required: true, + }, + isEmpty: { + type: 'boolean', + description: 'Whether the compressed data is empty', + required: true, + }, + len: { type: 'number', description: 'Length of compressed data in bytes', required: true }, + offset: { + type: 'number', + description: 'Byte offset in the data section (compressed)', + required: true, + }, + }} +/> diff --git a/content/docs/references/api/node/index.mdx b/content/docs/references/api/node/index.mdx new file mode 100644 index 0000000..6e73828 --- /dev/null +++ b/content/docs/references/api/node/index.mdx @@ -0,0 +1,230 @@ +--- +title: '@wvb/node' +description: 'Webview bundle API for Node.js' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + + + + + + + + + + + +## Functions + + + + + + + + +## Interfaces + + + + + + + + + + + + + + + + + + + + + + + + +## Enumerations + + + + + +## Type Aliases + + + + + + + + + + diff --git a/content/docs/references/api/node/integrity-algorithm.mdx b/content/docs/references/api/node/integrity-algorithm.mdx new file mode 100644 index 0000000..7eb6661 --- /dev/null +++ b/content/docs/references/api/node/integrity-algorithm.mdx @@ -0,0 +1,28 @@ +--- +title: 'IntegrityAlgorithm' +description: 'Hash algorithm for bundle integrity verification.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Hash algorithm for bundle integrity verification. + +Supports SHA-2 family hash algorithms for cryptographic verification +following the Subresource Integrity specification. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1261) + +## Type + +```ts +type IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'; +``` + +## Examples + +```typescript +// Integrity strings use these algorithms: +// "sha256-abc123..." - SHA-256 +// "sha384-def456..." - SHA-384 (recommended) +// "sha512-ghi789..." - SHA-512 +``` diff --git a/content/docs/references/api/node/integrity-policy.mdx b/content/docs/references/api/node/integrity-policy.mdx new file mode 100644 index 0000000..ab02e3f --- /dev/null +++ b/content/docs/references/api/node/integrity-policy.mdx @@ -0,0 +1,34 @@ +--- +title: 'IntegrityPolicy' +description: 'Policy for enforcing integrity verification during bundle operations.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Policy for enforcing integrity verification during bundle operations. + +Controls when integrity hashes are required and how missing hashes are handled. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1288) + +## Type + +```ts +type IntegrityPolicy = 'strict' | 'optional' | 'none'; +``` + +## Examples + +```typescript +import { Updater } from '@wvb/node'; + +// Require integrity for all bundles +const updater = new Updater(source, remote, { + integrityPolicy: 'strict', +}); + +// Optional integrity (warn if missing) +const updater2 = new Updater(source, remote, { + integrityPolicy: 'optional', +}); +``` diff --git a/content/docs/references/api/node/list-bundle-item.mdx b/content/docs/references/api/node/list-bundle-item.mdx new file mode 100644 index 0000000..a0fa2a7 --- /dev/null +++ b/content/docs/references/api/node/list-bundle-item.mdx @@ -0,0 +1,30 @@ +--- +title: 'ListBundleItem' +description: 'Information about a bundle from list operations.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Information about a bundle from list operations. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1304) + +## Properties + + diff --git a/content/docs/references/api/node/list-remote-bundle-info.mdx b/content/docs/references/api/node/list-remote-bundle-info.mdx new file mode 100644 index 0000000..ea4fcba --- /dev/null +++ b/content/docs/references/api/node/list-remote-bundle-info.mdx @@ -0,0 +1,19 @@ +--- +title: 'ListRemoteBundleInfo' +description: 'Bundle information from list operations.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle information from list operations. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1318) + +## Properties + + diff --git a/content/docs/references/api/node/loaded-descriptor.mdx b/content/docs/references/api/node/loaded-descriptor.mdx new file mode 100644 index 0000000..ee971df --- /dev/null +++ b/content/docs/references/api/node/loaded-descriptor.mdx @@ -0,0 +1,77 @@ +--- +title: 'LoadedDescriptor' +description: 'A descriptor loaded (and cached) by a BundleSource.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +A descriptor loaded (and cached) by a [`BundleSource`](/docs/references/api/node/bundle-source). + +Holds the parsed header/index together with the filepath it was loaded from, so +reading entry data always targets the exact bundle version that produced this +descriptor — even if the source's active version is swapped concurrently. + +The instance owns a reference-counted handle to the cached descriptor. When the +JavaScript object is garbage-collected, the handle is released automatically; the +underlying descriptor stays alive only while the source cache (see +`BundleSource.loadDescriptor`) or another `LoadedDescriptor` references it. +No manual disposal is required and no memory is leaked. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L671) + +## Constructor + +```ts +new LoadedDescriptor(); +``` + +## Methods + + BundleDescriptor', + description: + 'Returns the bundle descriptor The returned descriptor shares the same in-memory metadata and carries no reference back to the source, so it can outlive this LoadedDescriptor.', + required: true, + returns: 'BundleDescriptor — Bundle metadata', + }, + getData: { + type: '(path: string) => Promise | null>', + description: + "Reads file data for path, loading it lazily from disk. The read targets the bundle file this descriptor was loaded from, so the data is always consistent with LoadedDescriptor.descriptor even if the source's active version changes meanwhile. Returns null if the path does not exist in the bundle.", + required: true, + parameters: [ + { name: 'path', description: 'string — File path in the bundle (e.g., "/index.html")' }, + ], + returns: 'Promise | null> — File contents or null if not found', + }, + getDataChecksum: { + type: '(path: string) => Promise', + description: 'Reads the checksum of file data for path, loading it lazily from disk.', + required: true, + parameters: [{ name: 'path', description: 'string — File path in the bundle' }], + returns: 'Promise — xxHash-32 checksum or null if not found', + }, + }} +/> + +## Examples + +### descriptor + +```typescript +const loaded = await source.loadDescriptor('app'); +const index = loaded.descriptor().index(); +console.log(index.containsPath('/index.html')); +``` + +### getData + +```typescript +const loaded = await source.loadDescriptor('app'); +const html = await loaded.getData('/index.html'); +if (html) { + console.log(html.toString('utf-8')); +} +``` diff --git a/content/docs/references/api/node/local-protocol.mdx b/content/docs/references/api/node/local-protocol.mdx new file mode 100644 index 0000000..0ce77bb --- /dev/null +++ b/content/docs/references/api/node/local-protocol.mdx @@ -0,0 +1,87 @@ +--- +title: 'LocalProtocol' +description: 'Protocol handler that proxies requests to localhost servers.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Protocol handler that proxies requests to localhost servers. + +Forwards requests to local development servers for hot-reloading workflows. +Features response caching and 304 Not Modified support. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L735) + +## Constructor + +```ts +new LocalProtocol(hosts: Record); +``` + +', + description: 'Map of custom hosts to localhost URLs', + required: true, + }, + }} +/> + +## Methods + + | null) => Promise', + description: + 'Handles an HTTP request by proxying to localhost. Maps custom protocol URIs to localhost URLs and forwards the request.', + required: true, + parameters: [ + { name: 'method', description: 'HttpMethod — HTTP method' }, + { name: 'uri', description: 'string — Request URI (e.g., "app://myapp/api/data")' }, + { + name: 'headers?', + description: 'Record | null — Optional request headers', + }, + ], + returns: 'Promise — HTTP response from localhost', + }, + }} +/> + +## Examples + +```typescript +const protocol = new LocalProtocol({ + myapp: 'http://localhost:3000', + api: 'http://localhost:8080', +}); + +// This proxies to http://localhost:3000/index.html +const response = await protocol.handle('get', 'app://myapp/index.html'); +``` + +### constructor + +```typescript +const protocol = new LocalProtocol({ + myapp: 'http://localhost:3000', + api: 'http://localhost:8080', +}); +``` + +### handle + +```typescript +// Proxies to http://localhost:3000/api/data?foo=bar +const response = await protocol.handle('get', 'app://myapp/api/data?foo=bar'); +console.log(response.status); +``` + +```typescript +// POST with headers +const response = await protocol.handle('post', 'app://api/submit', { + 'Content-Type': 'application/json', +}); +``` diff --git a/content/docs/references/api/node/meta.json b/content/docs/references/api/node/meta.json new file mode 100644 index 0000000..7bdcffe --- /dev/null +++ b/content/docs/references/api/node/meta.json @@ -0,0 +1,53 @@ +{ + "title": "@wvb/node", + "pages": [ + "---Classes---", + "bundle", + "bundle-builder", + "bundle-descriptor", + "bundle-protocol", + "bundle-source", + "header", + "index-class", + "loaded-descriptor", + "local-protocol", + "remote", + "updater", + "---Functions---", + "read-bundle", + "read-bundle-from-buffer", + "write-bundle", + "write-bundle-into-buffer", + "---Interfaces---", + "build-header-options", + "build-index-options", + "build-options", + "bundle-manifest-data", + "bundle-manifest-entry", + "bundle-manifest-metadata", + "bundle-source-config", + "bundle-source-version", + "bundle-update-info", + "http-options", + "http-response", + "index-entry", + "list-bundle-item", + "list-remote-bundle-info", + "remote-bundle-info", + "remote-on-download-data", + "remote-options", + "signature-verifier-options", + "signature-verifying-key-options", + "updater-options", + "---Enumerations---", + "bundle-manifest-version", + "---Type Aliases---", + "bundle-source-kind", + "http-method", + "integrity-algorithm", + "integrity-policy", + "signature-algorithm", + "verifying-key-format", + "version" + ] +} diff --git a/content/docs/references/api/node/meta.ko.json b/content/docs/references/api/node/meta.ko.json new file mode 100644 index 0000000..13721e0 --- /dev/null +++ b/content/docs/references/api/node/meta.ko.json @@ -0,0 +1,53 @@ +{ + "title": "@wvb/node", + "pages": [ + "---클래스---", + "bundle", + "bundle-builder", + "bundle-descriptor", + "bundle-protocol", + "bundle-source", + "header", + "index-class", + "loaded-descriptor", + "local-protocol", + "remote", + "updater", + "---함수---", + "read-bundle", + "read-bundle-from-buffer", + "write-bundle", + "write-bundle-into-buffer", + "---인터페이스---", + "build-header-options", + "build-index-options", + "build-options", + "bundle-manifest-data", + "bundle-manifest-entry", + "bundle-manifest-metadata", + "bundle-source-config", + "bundle-source-version", + "bundle-update-info", + "http-options", + "http-response", + "index-entry", + "list-bundle-item", + "list-remote-bundle-info", + "remote-bundle-info", + "remote-on-download-data", + "remote-options", + "signature-verifier-options", + "signature-verifying-key-options", + "updater-options", + "---열거형---", + "bundle-manifest-version", + "---타입---", + "bundle-source-kind", + "http-method", + "integrity-algorithm", + "integrity-policy", + "signature-algorithm", + "verifying-key-format", + "version" + ] +} diff --git a/content/docs/references/api/node/read-bundle-from-buffer.mdx b/content/docs/references/api/node/read-bundle-from-buffer.mdx new file mode 100644 index 0000000..0558191 --- /dev/null +++ b/content/docs/references/api/node/read-bundle-from-buffer.mdx @@ -0,0 +1,34 @@ +--- +title: 'readBundleFromBuffer' +description: 'Reads a bundle from a buffer synchronously.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Reads a bundle from a buffer synchronously. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1352) + +```ts +function readBundleFromBuffer(buffer: Buffer): Bundle; +``` + +## Parameters + + + +## Returns + +`Bundle` — Parsed bundle + +## Examples + +```typescript +import { readFileSync } from 'fs'; +const buffer = readFileSync('app.wvb'); +const bundle = readBundleFromBuffer(buffer); +``` diff --git a/content/docs/references/api/node/read-bundle.mdx b/content/docs/references/api/node/read-bundle.mdx new file mode 100644 index 0000000..99d0dc7 --- /dev/null +++ b/content/docs/references/api/node/read-bundle.mdx @@ -0,0 +1,31 @@ +--- +title: 'readBundle' +description: 'Reads a bundle from a file asynchronously.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Reads a bundle from a file asynchronously. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1336) + +```ts +function readBundle(filepath: string): Promise; +``` + +## Parameters + + + +## Returns + +`Promise` — Parsed bundle + +## Examples + +```typescript +const bundle = await readBundle('app.wvb'); +const html = bundle.getData('/index.html'); +``` diff --git a/content/docs/references/api/node/remote-bundle-info.mdx b/content/docs/references/api/node/remote-bundle-info.mdx new file mode 100644 index 0000000..9b7b085 --- /dev/null +++ b/content/docs/references/api/node/remote-bundle-info.mdx @@ -0,0 +1,25 @@ +--- +title: 'RemoteBundleInfo' +description: 'Complete bundle information from remote server.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Complete bundle information from remote server. + +Contains version, cache validation, and integrity data. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1366) + +## Properties + + diff --git a/content/docs/references/api/node/remote-on-download-data.mdx b/content/docs/references/api/node/remote-on-download-data.mdx new file mode 100644 index 0000000..cbc10e1 --- /dev/null +++ b/content/docs/references/api/node/remote-on-download-data.mdx @@ -0,0 +1,20 @@ +--- +title: 'RemoteOnDownloadData' +description: 'Download progress data.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Download progress data. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1382) + +## Properties + + diff --git a/content/docs/references/api/node/remote-options.mdx b/content/docs/references/api/node/remote-options.mdx new file mode 100644 index 0000000..6e4b559 --- /dev/null +++ b/content/docs/references/api/node/remote-options.mdx @@ -0,0 +1,44 @@ +--- +title: 'RemoteOptions' +description: 'Options for creating a remote client.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Options for creating a remote client. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1405) + +## Properties + + + +## Methods + + void', + description: 'Download progress callback', + required: false, + parameters: [{ name: 'data', description: 'RemoteOnDownloadData' }], + returns: 'void', + }, + }} +/> + +## Examples + +```typescript +const options = { + http: { timeout: 30000 }, + onDownload: data => { + console.log(`Downloaded ${data.downloadedBytes}/${data.totalBytes}`); + }, +}; +const remote = new Remote('https://updates.example.com', options); +``` diff --git a/content/docs/references/api/node/remote.mdx b/content/docs/references/api/node/remote.mdx new file mode 100644 index 0000000..222317e --- /dev/null +++ b/content/docs/references/api/node/remote.mdx @@ -0,0 +1,148 @@ +--- +title: 'Remote' +description: 'HTTP client for downloading bundles from a remote server.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +HTTP client for downloading bundles from a remote server. + +The remote client implements the bundle HTTP protocol, allowing you to: + +- List available bundles +- Get bundle metadata +- Download specific versions +- Track download progress + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L802) + +## Constructor + +```ts +new Remote(endpoint: string, options?: RemoteOptions | null); +``` + + + +## Methods + + Promise<[RemoteBundleInfo, Bundle, Buffer]>', + description: + 'Downloads the current version of a bundle. Returns bundle info, parsed bundle, and raw data.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'channel?', description: 'string | null — Optional channel filter' }, + ], + returns: + 'Promise<[RemoteBundleInfo, Bundle, Buffer]> — Tuple of info, bundle, and data', + }, + downloadVersion: { + type: '(bundleName: string, version: string) => Promise<[RemoteBundleInfo, Bundle, Buffer]>', + description: 'Downloads a specific version of a bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'version', description: 'string — Specific version to download' }, + ], + returns: + 'Promise<[RemoteBundleInfo, Bundle, Buffer]> — Tuple of info, bundle, and data', + }, + getInfo: { + type: '(bundleName: string, channel?: string | null) => Promise', + description: + 'Gets bundle metadata for the current version. Fetches metadata without downloading the bundle itself.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle' }, + { name: 'channel?', description: 'string | null — Optional channel filter' }, + ], + returns: 'Promise — Bundle information', + }, + listBundles: { + type: '(channel?: string | null) => Promise', + description: 'Lists all available bundles on the server.', + required: true, + parameters: [{ name: 'channel?', description: 'string | null — Optional channel filter' }], + returns: 'Promise — List of bundles', + }, + }} +/> + +## Examples + +```typescript +const remote = new Remote('https://updates.example.com'); + +// List all bundles +const bundles = await remote.listBundles(); + +// Get current version info +const info = await remote.getInfo('app'); +console.log(`Latest version: ${info.version}`); + +// Download bundle +const [bundleInfo, bundle, data] = await remote.download('app'); +``` + +### constructor + +```typescript +const remote = new Remote('https://updates.example.com'); +``` + +```typescript +// With options +const remote = new Remote('https://updates.example.com', { + http: { timeout: 60000 }, + onDownload: data => { + const percent = (data.downloadedBytes / data.totalBytes) * 100; + console.log(`Progress: ${percent.toFixed(1)}%`); + }, +}); +``` + +### download + +```typescript +const [info, bundle, data] = await remote.download('app'); +console.log(`Downloaded ${info.name}@${info.version}`); +console.log(`Size: ${data.length} bytes`); + +// Save to file +await writeBundle(bundle, 'app.wvb'); +``` + +### downloadVersion + +```typescript +const [info, bundle, data] = await remote.downloadVersion('app', '1.0.0'); +console.log(`Downloaded specific version: ${info.version}`); +``` + +### getInfo + +```typescript +const info = await remote.getInfo('app'); +console.log(`Current version: ${info.version}`); +if (info.integrity) { + console.log(`Integrity: ${info.integrity}`); +} +``` + +### listBundles + +```typescript +const bundles = await remote.listBundles(); +for (const bundle of bundles) { + console.log(`${bundle.name}@${bundle.version}`); +} +``` diff --git a/content/docs/references/api/node/signature-algorithm.mdx b/content/docs/references/api/node/signature-algorithm.mdx new file mode 100644 index 0000000..70c4b84 --- /dev/null +++ b/content/docs/references/api/node/signature-algorithm.mdx @@ -0,0 +1,39 @@ +--- +title: 'SignatureAlgorithm' +description: 'Digital signature algorithm for bundle verification.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Digital signature algorithm for bundle verification. + +Supports multiple signature schemes for cryptographic verification of bundle authenticity. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1430) + +## Type + +```ts +type SignatureAlgorithm = + | 'ecdsaSecp256R1' + | 'ecdsaSecp384R1' + | 'ed25519' + | 'rsaPkcs1V15' + | 'rsaPss'; +``` + +## Examples + +```typescript +import { Updater } from '@wvb/node'; + +const updater = new Updater(source, remote, { + signatureVerifier: { + algorithm: 'ed25519', + key: { + format: 'spkiPem', + data: publicKeyPem, + }, + }, +}); +``` diff --git a/content/docs/references/api/node/signature-verifier-options.mdx b/content/docs/references/api/node/signature-verifier-options.mdx new file mode 100644 index 0000000..14c8c61 --- /dev/null +++ b/content/docs/references/api/node/signature-verifier-options.mdx @@ -0,0 +1,39 @@ +--- +title: 'SignatureVerifierOptions' +description: 'Configuration for signature verification.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Configuration for signature verification. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1458) + +## Properties + + + +## Examples + +```typescript +const verifierOptions = { + algorithm: 'ed25519', + key: { + format: 'spkiPem', + data: publicKeyPem, + }, +}; +``` diff --git a/content/docs/references/api/node/signature-verifying-key-options.mdx b/content/docs/references/api/node/signature-verifying-key-options.mdx new file mode 100644 index 0000000..382c9d0 --- /dev/null +++ b/content/docs/references/api/node/signature-verifying-key-options.mdx @@ -0,0 +1,43 @@ +--- +title: 'SignatureVerifyingKeyOptions' +description: 'Public key configuration for signature verification.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Public key configuration for signature verification. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1484) + +## Properties + +', + description: 'The key data (string for PEM, Uint8Array for DER/Raw)', + required: true, + }, + format: { + type: 'VerifyingKeyFormat', + description: 'The format of the public key', + required: true, + }, + }} +/> + +## Examples + +```typescript +// PEM format (string) +const pemKey = { + format: 'spkiPem', + data: publicKeyPem, +}; + +// DER format (binary) +const derKey = { + format: 'spkiDer', + data: new Uint8Array(derKeyBytes), +}; +``` diff --git a/content/docs/references/api/node/updater-options.mdx b/content/docs/references/api/node/updater-options.mdx new file mode 100644 index 0000000..84e5481 --- /dev/null +++ b/content/docs/references/api/node/updater-options.mdx @@ -0,0 +1,79 @@ +--- +title: 'UpdaterOptions' +description: 'Configuration options for the updater.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Configuration options for the updater. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1527) + +## Properties + + Promise)', + description: 'Signature verification config or custom function', + required: false, + }, + }} +/> + +## Methods + + Promise', + description: 'Custom integrity verification function', + required: false, + parameters: [ + { name: 'data', description: 'Uint8Array' }, + { name: 'integrity', description: 'string' }, + ], + returns: 'Promise', + }, + }} +/> + +## Examples + +```typescript +const updater = new Updater(source, remote, { + channel: 'stable', + integrityPolicy: 'strict', + signatureVerifier: { + algorithm: 'ed25519', + key: { + format: 'spkiPem', + data: publicKeyPem, + }, + }, +}); +``` + +```typescript +// Custom verification functions +const updater = new Updater(source, remote, { + integrityChecker: async (data, integrity) => { + // Custom integrity verification + return true; + }, + signatureVerifier: async (data, signature) => { + // Custom signature verification + return true; + }, +}); +``` diff --git a/content/docs/references/api/node/updater.mdx b/content/docs/references/api/node/updater.mdx new file mode 100644 index 0000000..1a87b6a --- /dev/null +++ b/content/docs/references/api/node/updater.mdx @@ -0,0 +1,165 @@ +--- +title: 'Updater' +description: 'Bundle updater for managing updates from a remote server.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Bundle updater for managing updates from a remote server. + +The updater coordinates between a local bundle source and remote server, +handling update checks, downloads, integrity verification, and signature validation. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L935) + +## Constructor + +```ts +new Updater(source: BundleSource, remote: Remote, options?: UpdaterOptions | null); +``` + + + +## Methods + + Promise', + description: + 'Downloads a bundle update from remote server. Downloads the specified bundle version (or the latest if not specified), verifies integrity and signature if configured, and download it to the remote directory.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle to download' }, + { + name: 'version?', + description: 'string | null — Specific version to download (defaults to latest)', + }, + ], + returns: 'Promise — Information about the downloaded bundle', + }, + getUpdate: { + type: '(bundleName: string) => Promise', + description: + 'Checks if an update is available for a specific bundle. Compares the local version with the remote version to determine if an update exists.', + required: true, + parameters: [{ name: 'bundleName', description: 'string — Name of the bundle to check' }], + returns: 'Promise — Update information', + }, + install: { + type: '(bundleName: string, version: string) => Promise', + description: + 'Activates a previously downloaded bundle version. The version must already be staged in the remote source (via Updater.download). When integrity/signature verification is configured, the staged bundle is verified before activation. On success the current version is updated so the protocol begins serving it, the cached descriptor is dropped, and stale staged versions are pruned. Concurrent install/download calls for the same bundle are serialized, so this never races a download or another install of the same bundle.', + required: true, + parameters: [ + { name: 'bundleName', description: 'string — Name of the bundle to activate' }, + { name: 'version', description: 'string — The downloaded version to activate' }, + ], + returns: 'Promise', + }, + listRemotes: { + type: '() => Promise', + description: 'Lists all available bundles on the remote server.', + required: true, + returns: 'Promise — Array of remote bundle information', + }, + }} +/> + +## Examples + +```typescript +import { Updater, BundleSource, Remote } from '@wvb/node'; + +const source = new BundleSource({ + builtinDir: './bundles/builtin', + remoteDir: './bundles/remote', +}); + +const remote = new Remote('https://updates.example.com'); + +const updater = new Updater(source, remote, { + channel: 'stable', + integrityPolicy: 'strict', + signatureVerifier: { + algorithm: 'ed25519', + key: { + format: 'spkiPem', + data: publicKeyPem, + }, + }, +}); + +// Check for updates +const updateInfo = await updater.getUpdate('app'); +if (updateInfo.isAvailable) { + console.log(`Update available: ${updateInfo.version}`); + await updater.download('app'); + await updater.install('app', updateInfo.version); +} +``` + +### constructor + +```typescript +const updater = new Updater(source, remote, { + channel: 'stable', + integrityPolicy: 'strict', +}); +``` + +### download + +```typescript +// Download latest version +const info = await updater.download('app'); +console.log(`Downloaded ${info.name} v${info.version}`); +``` + +```typescript +// Download specific version +const info = await updater.download('app', '1.2.3'); +console.log(`Downloaded ${info.name} v${info.version}`); +``` + +### getUpdate + +```typescript +const updateInfo = await updater.getUpdate('app'); +if (updateInfo.isAvailable) { + console.log(`Update available: ${updateInfo.localVersion} → ${updateInfo.version}`); +} else { + console.log('Already up to date'); +} +``` + +### install + +```typescript +await updater.download('app', '1.2.0'); +// ...later, active the latest version: +await updater.install('app', '1.2.0'); +``` + +### listRemotes + +```typescript +const remotes = await updater.listRemotes(); +for (const bundle of remotes) { + console.log(`${bundle.name}: ${bundle.version}`); +} +``` diff --git a/content/docs/references/api/node/verifying-key-format.mdx b/content/docs/references/api/node/verifying-key-format.mdx new file mode 100644 index 0000000..c92dfd6 --- /dev/null +++ b/content/docs/references/api/node/verifying-key-format.mdx @@ -0,0 +1,45 @@ +--- +title: 'VerifyingKeyFormat' +description: 'Format of the public key used for signature verification.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Format of the public key used for signature verification. + +Different algorithms support different key formats. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1565) + +## Type + +```ts +type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; +``` + +## Examples + +```typescript +import fs from 'fs'; + +// PEM format (text) +const pemKey = fs.readFileSync('./public-key.pem', 'utf8'); +const config1 = { + format: 'spkiPem', + data: pemKey, +}; + +// DER format (binary) +const derKey = fs.readFileSync('./public-key.der'); +const config2 = { + format: 'spkiDer', + data: derKey, +}; + +// Raw bytes (Ed25519 only) +const rawKey = new Uint8Array(32); +const config3 = { + format: 'raw', + data: rawKey, +}; +``` diff --git a/content/docs/references/api/node/version.mdx b/content/docs/references/api/node/version.mdx new file mode 100644 index 0000000..a868d55 --- /dev/null +++ b/content/docs/references/api/node/version.mdx @@ -0,0 +1,14 @@ +--- +title: 'Version' +description: 'Version — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1578) + +## Type + +```ts +type Version = 'v1'; +``` diff --git a/content/docs/references/api/node/write-bundle-into-buffer.mdx b/content/docs/references/api/node/write-bundle-into-buffer.mdx new file mode 100644 index 0000000..f551ee5 --- /dev/null +++ b/content/docs/references/api/node/write-bundle-into-buffer.mdx @@ -0,0 +1,30 @@ +--- +title: 'writeBundleIntoBuffer' +description: 'Writes a bundle to a buffer synchronously.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Writes a bundle to a buffer synchronously. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1611) + +```ts +function writeBundleIntoBuffer(bundle: Bundle): Buffer; +``` + +## Parameters + + + +## Returns + +`Buffer` — Bundle data as a buffer + +## Examples + +```typescript +const bundle = builder.build(); +const buffer = writeBundleIntoBuffer(bundle); +console.log(`Bundle size: ${buffer.length} bytes`); +``` diff --git a/content/docs/references/api/node/write-bundle.mdx b/content/docs/references/api/node/write-bundle.mdx new file mode 100644 index 0000000..3a252dc --- /dev/null +++ b/content/docs/references/api/node/write-bundle.mdx @@ -0,0 +1,36 @@ +--- +title: 'writeBundle' +description: 'Writes a bundle to a file asynchronously.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +Writes a bundle to a file asynchronously. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/node/index.d.ts#L1596) + +```ts +function writeBundle(bundle: Bundle, filepath: string): Promise; +``` + +## Parameters + + + +## Returns + +`Promise` — Number of bytes written + +## Examples + +```typescript +const builder = new BundleBuilder(); +builder.insertEntry('/index.html', Buffer.from('')); +const bundle = builder.build(); +await writeBundle(bundle, 'output.wvb'); +``` diff --git a/content/docs/references/api/remotes/meta.json b/content/docs/references/api/remotes/meta.json new file mode 100644 index 0000000..207f906 --- /dev/null +++ b/content/docs/references/api/remotes/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Remotes", + "pages": [ + "remote-aws", + "remote-aws-provider", + "remote-aws-provider-pulumi", + "remote-cloudflare", + "remote-cloudflare-provider", + "remote-cloudflare-provider-pulumi", + "remote-local", + "remote-local-provider" + ] +} diff --git a/content/docs/references/api/remotes/meta.ko.json b/content/docs/references/api/remotes/meta.ko.json new file mode 100644 index 0000000..144b5cf --- /dev/null +++ b/content/docs/references/api/remotes/meta.ko.json @@ -0,0 +1,13 @@ +{ + "title": "리모트", + "pages": [ + "remote-aws", + "remote-aws-provider", + "remote-aws-provider-pulumi", + "remote-cloudflare", + "remote-cloudflare-provider", + "remote-cloudflare-provider-pulumi", + "remote-local", + "remote-local-provider" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/index.mdx b/content/docs/references/api/remotes/remote-aws-provider-pulumi/index.mdx new file mode 100644 index 0000000..474c251 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/index.mdx @@ -0,0 +1,33 @@ +--- +title: '@wvb/remote-aws-provider-pulumi' +description: 'Webview Bundle remote pulumi configuration for AWS' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Interfaces + + + + + +## Variables + + + + diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.json b/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.json new file mode 100644 index 0000000..00ec60d --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/remote-aws-provider-pulumi", + "pages": [ + "---Classes---", + "webview-bundle-remote-provider", + "---Interfaces---", + "webview-bundle-remote-provider-config", + "---Variables---", + "wvb-remote-provider" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.ko.json b/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.ko.json new file mode 100644 index 0000000..f8008ab --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/meta.ko.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/remote-aws-provider-pulumi", + "pages": [ + "---클래스---", + "webview-bundle-remote-provider", + "---인터페이스---", + "webview-bundle-remote-provider-config", + "---변수---", + "wvb-remote-provider" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider-config.mdx b/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider-config.mdx new file mode 100644 index 0000000..69f9d80 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider-config.mdx @@ -0,0 +1,36 @@ +--- +title: 'WebviewBundleRemoteProviderConfig' +description: 'WebviewBundleRemoteProviderConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider-pulumi/src/provider.ts#L26) + +## Properties + +', required: false }, + bucketName: { type: 'string', required: false }, + bucketTags: { type: 'Input<{ [key: string]: Input }>', required: false }, + cloudfrontAliases: { type: 'Input[]>', required: false }, + cloudfrontComment: { type: 'Input', required: false }, + cloudfrontEnabled: { type: 'Input', required: false }, + cloudfrontHttpVersion: { + type: 'Input<"http1.1" | "http2" | "http2and3" | "http3">', + required: false, + }, + cloudfrontTags: { type: 'Input<{ [key: string]: Input }>', required: false }, + cloudfrontViewerCertificate: { type: 'Input', required: false }, + cloudfrontWaitForDeployment: { type: 'Input', required: false }, + lambdaOriginRequest: { type: 'WebviewBundleRemoteLambdaCodeConfig', required: false }, + lambdaOriginResponse: { type: 'WebviewBundleRemoteLambdaCodeConfig', required: false }, + lambdaPolicyActions: { type: 'string[]', required: false }, + lambdaRoleName: { type: 'Input', required: false }, + lambdaRolePolicyName: { type: 'Input', required: false }, + name: { type: 'string', required: false }, + region: { type: 'Input', required: false }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider.mdx b/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider.mdx new file mode 100644 index 0000000..d913887 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider.mdx @@ -0,0 +1,39 @@ +--- +title: 'WebviewBundleRemoteProvider' +description: 'WebviewBundleRemoteProvider — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider-pulumi/src/provider.ts#L47) + +**Extends:** `ComponentResource` + +## Constructor + +```ts +new WebviewBundleRemoteProvider(name: string, config: WebviewBundleRemoteProviderConfig = {}, opts?: ComponentResourceOptions); +``` + + + +## Properties + +', required: true }, + bucketId: { type: 'readonly Output', required: true }, + bucketName: { type: 'readonly Output', required: true }, + cloudfrontDistributionArn: { type: 'readonly Output', required: true }, + cloudfrontDistributionDomainName: { type: 'readonly Output', required: true }, + cloudfrontDistributionId: { type: 'readonly Output', required: true }, + lambdaOriginRequestArn: { type: 'readonly Output', required: true }, + lambdaOriginResponseArn: { type: 'readonly Output', required: true }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-aws-provider-pulumi/wvb-remote-provider.mdx b/content/docs/references/api/remotes/remote-aws-provider-pulumi/wvb-remote-provider.mdx new file mode 100644 index 0000000..a762ed7 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider-pulumi/wvb-remote-provider.mdx @@ -0,0 +1,12 @@ +--- +title: 'WvbRemoteProvider' +description: 'WvbRemoteProvider — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider-pulumi/src/provider.ts#L372) + +**Type:** [`typeof WebviewBundleRemoteProvider`](/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider) + +See [`typeof WebviewBundleRemoteProvider`](/docs/references/api/remotes/remote-aws-provider-pulumi/webview-bundle-remote-provider) for the full member list. diff --git a/content/docs/references/api/remotes/remote-aws-provider/index.mdx b/content/docs/references/api/remotes/remote-aws-provider/index.mdx new file mode 100644 index 0000000..3473a03 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/index.mdx @@ -0,0 +1,39 @@ +--- +title: '@wvb/remote-aws-provider' +description: 'Webview Bundle remote provider for AWS' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + +## Interfaces + + + + + +## Type Aliases + + + + + +## Variables + + + + diff --git a/content/docs/references/api/remotes/remote-aws-provider/meta.json b/content/docs/references/api/remotes/remote-aws-provider/meta.json new file mode 100644 index 0000000..e06aafe --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/meta.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-aws-provider", + "pages": [ + "---Functions---", + "webview-bundle-remote", + "---Interfaces---", + "webview-bundle-remote-config", + "---Type Aliases---", + "webview-bundle-remote-type-alias", + "---Variables---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws-provider/meta.ko.json b/content/docs/references/api/remotes/remote-aws-provider/meta.ko.json new file mode 100644 index 0000000..dcbe8c5 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/meta.ko.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-aws-provider", + "pages": [ + "---함수---", + "webview-bundle-remote", + "---인터페이스---", + "webview-bundle-remote-config", + "---타입---", + "webview-bundle-remote-type-alias", + "---변수---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-config.mdx b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-config.mdx new file mode 100644 index 0000000..0c1d736 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-config.mdx @@ -0,0 +1,23 @@ +--- +title: 'WebviewBundleRemoteConfig' +description: 'WebviewBundleRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider/src/remote.ts#L10) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-type-alias.mdx b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-type-alias.mdx new file mode 100644 index 0000000..e236e27 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote-type-alias.mdx @@ -0,0 +1,14 @@ +--- +title: 'WebviewBundleRemote' +description: 'WebviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider/src/remote.ts#L8) + +## Type + +```ts +type WebviewBundleRemote = Hono<{ Bindings: Bindings; Variables: Context }>; +``` diff --git a/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote.mdx b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote.mdx new file mode 100644 index 0000000..69b49c4 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/webview-bundle-remote.mdx @@ -0,0 +1,20 @@ +--- +title: 'webviewBundleRemote' +description: 'webviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider/src/remote.ts#L18) + +```ts +function webviewBundleRemote(config: WebviewBundleRemoteConfig): WebviewBundleRemote; +``` + +## Parameters + + + +## Returns + +`WebviewBundleRemote` diff --git a/content/docs/references/api/remotes/remote-aws-provider/wvb-remote.mdx b/content/docs/references/api/remotes/remote-aws-provider/wvb-remote.mdx new file mode 100644 index 0000000..7730b93 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws-provider/wvb-remote.mdx @@ -0,0 +1,20 @@ +--- +title: 'wvbRemote' +description: 'wvbRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws-provider/src/remote.ts#L81) + +```ts +const wvbRemote: (config: WebviewBundleRemoteConfig) => WebviewBundleRemote; +``` + +## Parameters + + + +## Returns + +`WebviewBundleRemote` diff --git a/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer-config.mdx b/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer-config.mdx new file mode 100644 index 0000000..33873af --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer-config.mdx @@ -0,0 +1,21 @@ +--- +title: 'AwsKmsSignatureSignerConfig' +description: 'AwsKmsSignatureSignerConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/signature.ts#L6) + +**Extends:** `AwsKmsClientConfigLike` + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer.mdx b/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer.mdx new file mode 100644 index 0000000..84ab0f6 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-kms-signature-signer.mdx @@ -0,0 +1,20 @@ +--- +title: 'awsKmsSignatureSigner' +description: 'awsKmsSignatureSigner — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/signature.ts#L11) + +```ts +function awsKmsSignatureSigner(config: AwsKmsSignatureSignerConfig): SignatureSignFn; +``` + +## Parameters + + + +## Returns + +`SignatureSignFn` diff --git a/content/docs/references/api/remotes/remote-aws/aws-remote-config.mdx b/content/docs/references/api/remotes/remote-aws/aws-remote-config.mdx new file mode 100644 index 0000000..e79f8f9 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-remote-config.mdx @@ -0,0 +1,20 @@ +--- +title: 'AwsRemoteConfig' +description: 'AwsRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/index.ts#L15) + +## Properties + +', required: false }, + signature: { type: 'false | AwsKmsSignatureSignerConfig', required: false }, + uploader: { type: 'Omit', required: false }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-aws/aws-remote-deployer-config.mdx b/content/docs/references/api/remotes/remote-aws/aws-remote-deployer-config.mdx new file mode 100644 index 0000000..8a8a708 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-remote-deployer-config.mdx @@ -0,0 +1,31 @@ +--- +title: 'AwsRemoteDeployerConfig' +description: 'AwsRemoteDeployerConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/deployer.ts#L16) + +**Extends:** `AwsS3ClientConfigLike`, `AwsCloudFrontClientConfigLike` + +## Properties + + string); distributionId: string }', + required: false, + }, + key: { + type: 'string | ((bundleName: string, version: string, channel?: string) => string)', + required: false, + }, + s3Client: { type: 'S3Client', required: false }, + s3ClientConfig: { type: 'S3ClientConfig', required: false }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-aws/aws-remote-deployer.mdx b/content/docs/references/api/remotes/remote-aws/aws-remote-deployer.mdx new file mode 100644 index 0000000..da4aca0 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-remote-deployer.mdx @@ -0,0 +1,20 @@ +--- +title: 'awsRemoteDeployer' +description: 'awsRemoteDeployer — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/deployer.ts#L148) + +```ts +function awsRemoteDeployer(config: AwsRemoteDeployerConfig): BaseRemoteDeployer; +``` + +## Parameters + + + +## Returns + +`BaseRemoteDeployer` diff --git a/content/docs/references/api/remotes/remote-aws/aws-remote-interface.mdx b/content/docs/references/api/remotes/remote-aws/aws-remote-interface.mdx new file mode 100644 index 0000000..cb8a842 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-remote-interface.mdx @@ -0,0 +1,18 @@ +--- +title: 'AwsRemote' +description: 'AwsRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/index.ts#L23) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-aws/aws-remote.mdx b/content/docs/references/api/remotes/remote-aws/aws-remote.mdx new file mode 100644 index 0000000..959b8e2 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-remote.mdx @@ -0,0 +1,22 @@ +--- +title: 'awsRemote' +description: 'AWS remote configuration.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +AWS remote configuration. + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/index.ts#L32) + +```ts +function awsRemote(config: AwsRemoteConfig): AwsRemote; +``` + +## Parameters + + + +## Returns + +`AwsRemote` diff --git a/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader-config.mdx b/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader-config.mdx new file mode 100644 index 0000000..4c8ff1e --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader-config.mdx @@ -0,0 +1,26 @@ +--- +title: 'AwsS3RemoteUploaderConfig' +description: 'AwsS3RemoteUploaderConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/uploader.ts#L12) + +**Extends:** `AwsS3ClientConfigLike` + +## Properties + + string)', required: false }, + metadata: { type: 'Record', required: false }, + s3Client: { type: 'S3Client', required: false }, + s3ClientConfig: { type: 'S3ClientConfig', required: false }, + upload: { type: 'Configuration', required: false }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader.mdx b/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader.mdx new file mode 100644 index 0000000..024d2ed --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/aws-s3-remote-uploader.mdx @@ -0,0 +1,20 @@ +--- +title: 'awsS3RemoteUploader' +description: 'awsS3RemoteUploader — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/uploader.ts#L76) + +```ts +function awsS3RemoteUploader(config: AwsS3RemoteUploaderConfig): BaseRemoteUploader; +``` + +## Parameters + + + +## Returns + +`BaseRemoteUploader` diff --git a/content/docs/references/api/remotes/remote-aws/bundle-already-uploaded-error.mdx b/content/docs/references/api/remotes/remote-aws/bundle-already-uploaded-error.mdx new file mode 100644 index 0000000..09d6af9 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/bundle-already-uploaded-error.mdx @@ -0,0 +1,33 @@ +--- +title: 'BundleAlreadyUploadedError' +description: 'BundleAlreadyUploadedError — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/errors.ts#L1) + +**Extends:** `Error` + +## Constructor + +```ts +new BundleAlreadyUploadedError(bundleName: string, version: string); +``` + + + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-aws/index.mdx b/content/docs/references/api/remotes/remote-aws/index.mdx new file mode 100644 index 0000000..022c541 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/index.mdx @@ -0,0 +1,60 @@ +--- +title: '@wvb/remote-aws' +description: 'Webview Bundle remote configuration for AWS' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Functions + + + + + + + + + +## Interfaces + + + + + + + + diff --git a/content/docs/references/api/remotes/remote-aws/is-bundle-already-uploaded-error.mdx b/content/docs/references/api/remotes/remote-aws/is-bundle-already-uploaded-error.mdx new file mode 100644 index 0000000..06b5265 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/is-bundle-already-uploaded-error.mdx @@ -0,0 +1,20 @@ +--- +title: 'isBundleAlreadyUploadedError' +description: 'isBundleAlreadyUploadedError — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/aws/src/errors.ts#L15) + +```ts +function isBundleAlreadyUploadedError(e: unknown): e is BundleAlreadyUploadedError; +``` + +## Parameters + + + +## Returns + +`e is BundleAlreadyUploadedError` diff --git a/content/docs/references/api/remotes/remote-aws/meta.json b/content/docs/references/api/remotes/remote-aws/meta.json new file mode 100644 index 0000000..0613bbe --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/meta.json @@ -0,0 +1,19 @@ +{ + "title": "@wvb/remote-aws", + "pages": [ + "---Classes---", + "bundle-already-uploaded-error", + "---Functions---", + "aws-kms-signature-signer", + "aws-remote", + "aws-remote-deployer", + "aws-s3-remote-uploader", + "is-bundle-already-uploaded-error", + "---Interfaces---", + "aws-kms-signature-signer-config", + "aws-remote-interface", + "aws-remote-config", + "aws-remote-deployer-config", + "aws-s3-remote-uploader-config" + ] +} diff --git a/content/docs/references/api/remotes/remote-aws/meta.ko.json b/content/docs/references/api/remotes/remote-aws/meta.ko.json new file mode 100644 index 0000000..f0dab16 --- /dev/null +++ b/content/docs/references/api/remotes/remote-aws/meta.ko.json @@ -0,0 +1,19 @@ +{ + "title": "@wvb/remote-aws", + "pages": [ + "---클래스---", + "bundle-already-uploaded-error", + "---함수---", + "aws-kms-signature-signer", + "aws-remote", + "aws-remote-deployer", + "aws-s3-remote-uploader", + "is-bundle-already-uploaded-error", + "---인터페이스---", + "aws-kms-signature-signer-config", + "aws-remote-interface", + "aws-remote-config", + "aws-remote-deployer-config", + "aws-s3-remote-uploader-config" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/index.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/index.mdx new file mode 100644 index 0000000..a00b84b --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/index.mdx @@ -0,0 +1,33 @@ +--- +title: '@wvb/remote-cloudflare-provider-pulumi' +description: 'Webview Bundle remote pulumi configuration for Cloudflare' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Interfaces + + + + + +## Variables + + + + diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.json b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.json new file mode 100644 index 0000000..2ac1c06 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/remote-cloudflare-provider-pulumi", + "pages": [ + "---Classes---", + "webview-bundle-remote-provider", + "---Interfaces---", + "webview-bundle-remote-provider-config", + "---Variables---", + "wvb-remote-provider" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.ko.json b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.ko.json new file mode 100644 index 0000000..f839c74 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/meta.ko.json @@ -0,0 +1,11 @@ +{ + "title": "@wvb/remote-cloudflare-provider-pulumi", + "pages": [ + "---클래스---", + "webview-bundle-remote-provider", + "---인터페이스---", + "webview-bundle-remote-provider-config", + "---변수---", + "wvb-remote-provider" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider-config.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider-config.mdx new file mode 100644 index 0000000..c385fd2 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider-config.mdx @@ -0,0 +1,25 @@ +--- +title: 'WebviewBundleRemoteProviderConfig' +description: 'WebviewBundleRemoteProviderConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider-pulumi/src/provider.ts#L16) + +## Properties + +', required: true }, + bucket: { type: 'Optional', required: false }, + kv: { type: 'Optional', required: false }, + worker: { type: 'Optional', required: false }, + workerDeploymentAnnotations: { type: 'Input', required: false }, + workerDeploymentPercentage: { type: 'Input', required: false }, + workerVersion: { + type: 'Optional, "accountId">', + required: false, + }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider.mdx new file mode 100644 index 0000000..fa94652 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider.mdx @@ -0,0 +1,36 @@ +--- +title: 'WebviewBundleRemoteProvider' +description: 'WebviewBundleRemoteProvider — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider-pulumi/src/provider.ts#L26) + +**Extends:** `ComponentResource` + +## Constructor + +```ts +new WebviewBundleRemoteProvider(name: string, config: WebviewBundleRemoteProviderConfig, opts?: ComponentResourceOptions); +``` + + + +## Properties + +', required: true }, + kvNamespaceId: { type: 'readonly Output', required: true }, + workerDeploymentId: { type: 'readonly Output', required: true }, + workerId: { type: 'readonly Output', required: true }, + workerVersionId: { type: 'readonly Output', required: true }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/wvb-remote-provider.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/wvb-remote-provider.mdx new file mode 100644 index 0000000..3697686 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider-pulumi/wvb-remote-provider.mdx @@ -0,0 +1,12 @@ +--- +title: 'WvbRemoteProvider' +description: 'WvbRemoteProvider — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider-pulumi/src/provider.ts#L164) + +**Type:** [`typeof WebviewBundleRemoteProvider`](/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider) + +See [`typeof WebviewBundleRemoteProvider`](/docs/references/api/remotes/remote-cloudflare-provider-pulumi/webview-bundle-remote-provider) for the full member list. diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/context.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/context.mdx new file mode 100644 index 0000000..830ff41 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/context.mdx @@ -0,0 +1,14 @@ +--- +title: 'Context' +description: 'Context — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider/src/context.ts#L1) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/index.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/index.mdx new file mode 100644 index 0000000..727eafd --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/index.mdx @@ -0,0 +1,43 @@ +--- +title: '@wvb/remote-cloudflare-provider' +description: 'Webview Bundle remote provider for Cloudflare' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + +## Interfaces + + + + + + +## Type Aliases + + + + + +## Variables + + + + diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/meta.json b/content/docs/references/api/remotes/remote-cloudflare-provider/meta.json new file mode 100644 index 0000000..718748e --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/meta.json @@ -0,0 +1,14 @@ +{ + "title": "@wvb/remote-cloudflare-provider", + "pages": [ + "---Functions---", + "webview-bundle-remote", + "---Interfaces---", + "context", + "webview-bundle-remote-options", + "---Type Aliases---", + "webview-bundle-remote-type-alias", + "---Variables---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/meta.ko.json b/content/docs/references/api/remotes/remote-cloudflare-provider/meta.ko.json new file mode 100644 index 0000000..1d8b7a7 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/meta.ko.json @@ -0,0 +1,14 @@ +{ + "title": "@wvb/remote-cloudflare-provider", + "pages": [ + "---함수---", + "webview-bundle-remote", + "---인터페이스---", + "context", + "webview-bundle-remote-options", + "---타입---", + "webview-bundle-remote-type-alias", + "---변수---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-options.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-options.mdx new file mode 100644 index 0000000..faee67c --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-options.mdx @@ -0,0 +1,20 @@ +--- +title: 'WebviewBundleRemoteOptions' +description: 'WebviewBundleRemoteOptions — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider/src/remote.ts#L10) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-type-alias.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-type-alias.mdx new file mode 100644 index 0000000..3151f70 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote-type-alias.mdx @@ -0,0 +1,14 @@ +--- +title: 'WebviewBundleRemote' +description: 'WebviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider/src/remote.ts#L8) + +## Type + +```ts +type WebviewBundleRemote = Hono<{ Bindings: Context }>; +``` diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote.mdx new file mode 100644 index 0000000..d804616 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/webview-bundle-remote.mdx @@ -0,0 +1,22 @@ +--- +title: 'webviewBundleRemote' +description: 'webviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider/src/remote.ts#L15) + +```ts +function webviewBundleRemote(options: WebviewBundleRemoteOptions = {}): WebviewBundleRemote; +``` + +## Parameters + + + +## Returns + +`WebviewBundleRemote` diff --git a/content/docs/references/api/remotes/remote-cloudflare-provider/wvb-remote.mdx b/content/docs/references/api/remotes/remote-cloudflare-provider/wvb-remote.mdx new file mode 100644 index 0000000..b21ce46 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare-provider/wvb-remote.mdx @@ -0,0 +1,22 @@ +--- +title: 'wvbRemote' +description: 'wvbRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare-provider/src/remote.ts#L65) + +```ts +const wvbRemote: (options: WebviewBundleRemoteOptions = {}) => WebviewBundleRemote; +``` + +## Parameters + + + +## Returns + +`WebviewBundleRemote` diff --git a/content/docs/references/api/remotes/remote-cloudflare/bundle-already-uploaded-error.mdx b/content/docs/references/api/remotes/remote-cloudflare/bundle-already-uploaded-error.mdx new file mode 100644 index 0000000..552f48d --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/bundle-already-uploaded-error.mdx @@ -0,0 +1,31 @@ +--- +title: 'BundleAlreadyUploadedError' +description: 'BundleAlreadyUploadedError — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +**Extends:** `Error` + +## Constructor + +```ts +new BundleAlreadyUploadedError(bundleName: string, version: string); +``` + + + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-config.mdx b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-config.mdx new file mode 100644 index 0000000..65933b5 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-config.mdx @@ -0,0 +1,31 @@ +--- +title: 'CloudflareRemoteConfig' +description: 'CloudflareRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare/src/index.ts#L9) + +**Extends:** `CloudflareClientConfigLike`, `Pick` + +## Properties + +', + required: false, + }, + kvNamespaceId: { type: 'string', required: true }, + uploader: { + type: 'Omit', + required: false, + }, + }} +/> diff --git a/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-interface.mdx b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-interface.mdx new file mode 100644 index 0000000..19568c9 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote-interface.mdx @@ -0,0 +1,17 @@ +--- +title: 'CloudflareRemote' +description: 'CloudflareRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare/src/index.ts#L19) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote.mdx b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote.mdx new file mode 100644 index 0000000..0f6ba8b --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/cloudflare-remote.mdx @@ -0,0 +1,20 @@ +--- +title: 'cloudflareRemote' +description: 'cloudflareRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/cloudflare/src/index.ts#L24) + +```ts +function cloudflareRemote(config: CloudflareRemoteConfig): CloudflareRemote; +``` + +## Parameters + + + +## Returns + +`CloudflareRemote` diff --git a/content/docs/references/api/remotes/remote-cloudflare/index.mdx b/content/docs/references/api/remotes/remote-cloudflare/index.mdx new file mode 100644 index 0000000..36e8f7e --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/index.mdx @@ -0,0 +1,41 @@ +--- +title: '@wvb/remote-cloudflare' +description: 'Webview Bundle remote configuration for Cloudflare' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Classes + + + + + +## Functions + + + + + + +## Interfaces + + + + + diff --git a/content/docs/references/api/remotes/remote-cloudflare/is-bundle-already-uploaded-error.mdx b/content/docs/references/api/remotes/remote-cloudflare/is-bundle-already-uploaded-error.mdx new file mode 100644 index 0000000..030a453 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/is-bundle-already-uploaded-error.mdx @@ -0,0 +1,18 @@ +--- +title: 'isBundleAlreadyUploadedError' +description: 'isBundleAlreadyUploadedError — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +```ts +function isBundleAlreadyUploadedError(e: unknown): e is BundleAlreadyUploadedError; +``` + +## Parameters + + + +## Returns + +`e is BundleAlreadyUploadedError` diff --git a/content/docs/references/api/remotes/remote-cloudflare/meta.json b/content/docs/references/api/remotes/remote-cloudflare/meta.json new file mode 100644 index 0000000..a08c4c0 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/meta.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-cloudflare", + "pages": [ + "---Classes---", + "bundle-already-uploaded-error", + "---Functions---", + "cloudflare-remote", + "is-bundle-already-uploaded-error", + "---Interfaces---", + "cloudflare-remote-interface", + "cloudflare-remote-config" + ] +} diff --git a/content/docs/references/api/remotes/remote-cloudflare/meta.ko.json b/content/docs/references/api/remotes/remote-cloudflare/meta.ko.json new file mode 100644 index 0000000..d630d93 --- /dev/null +++ b/content/docs/references/api/remotes/remote-cloudflare/meta.ko.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-cloudflare", + "pages": [ + "---클래스---", + "bundle-already-uploaded-error", + "---함수---", + "cloudflare-remote", + "is-bundle-already-uploaded-error", + "---인터페이스---", + "cloudflare-remote-interface", + "cloudflare-remote-config" + ] +} diff --git a/content/docs/references/api/remotes/remote-local-provider/index.mdx b/content/docs/references/api/remotes/remote-local-provider/index.mdx new file mode 100644 index 0000000..ecd2f0f --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/index.mdx @@ -0,0 +1,39 @@ +--- +title: '@wvb/remote-local-provider' +description: 'Webview Bundle remote provider for local simulation' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + +## Interfaces + + + + + +## Type Aliases + + + + + +## Variables + + + + diff --git a/content/docs/references/api/remotes/remote-local-provider/meta.json b/content/docs/references/api/remotes/remote-local-provider/meta.json new file mode 100644 index 0000000..70b252c --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/meta.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-local-provider", + "pages": [ + "---Functions---", + "webview-bundle-remote", + "---Interfaces---", + "webview-bundle-remote-config", + "---Type Aliases---", + "webview-bundle-remote-type-alias", + "---Variables---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-local-provider/meta.ko.json b/content/docs/references/api/remotes/remote-local-provider/meta.ko.json new file mode 100644 index 0000000..59a9239 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/meta.ko.json @@ -0,0 +1,13 @@ +{ + "title": "@wvb/remote-local-provider", + "pages": [ + "---함수---", + "webview-bundle-remote", + "---인터페이스---", + "webview-bundle-remote-config", + "---타입---", + "webview-bundle-remote-type-alias", + "---변수---", + "wvb-remote" + ] +} diff --git a/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-config.mdx b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-config.mdx new file mode 100644 index 0000000..108dc8a --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-config.mdx @@ -0,0 +1,26 @@ +--- +title: 'WebviewBundleRemoteConfig' +description: 'WebviewBundleRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local-provider/src/remote.ts#L24) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-type-alias.mdx b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-type-alias.mdx new file mode 100644 index 0000000..b9241c7 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote-type-alias.mdx @@ -0,0 +1,14 @@ +--- +title: 'WebviewBundleRemote' +description: 'WebviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local-provider/src/remote.ts#L22) + +## Type + +```ts +type WebviewBundleRemote = Hono; +``` diff --git a/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote.mdx b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote.mdx new file mode 100644 index 0000000..e3ef032 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/webview-bundle-remote.mdx @@ -0,0 +1,22 @@ +--- +title: 'webviewBundleRemote' +description: 'webviewBundleRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local-provider/src/remote.ts#L34) + +```ts +function webviewBundleRemote( + __namedParameters: WebviewBundleRemoteConfig +): Hono; +``` + +## Parameters + + + +## Returns + +`Hono` diff --git a/content/docs/references/api/remotes/remote-local-provider/wvb-remote.mdx b/content/docs/references/api/remotes/remote-local-provider/wvb-remote.mdx new file mode 100644 index 0000000..ed9391f --- /dev/null +++ b/content/docs/references/api/remotes/remote-local-provider/wvb-remote.mdx @@ -0,0 +1,20 @@ +--- +title: 'wvbRemote' +description: 'wvbRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local-provider/src/remote.ts#L118) + +```ts +const wvbRemote: (__namedParameters: WebviewBundleRemoteConfig) => Hono; +``` + +## Parameters + + + +## Returns + +`Hono` diff --git a/content/docs/references/api/remotes/remote-local/index.mdx b/content/docs/references/api/remotes/remote-local/index.mdx new file mode 100644 index 0000000..0357e90 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/index.mdx @@ -0,0 +1,25 @@ +--- +title: '@wvb/remote-local' +description: 'Webview Bundle remote config for local simulation' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +## Functions + + + + + +## Interfaces + + + + + diff --git a/content/docs/references/api/remotes/remote-local/local-remote-config.mdx b/content/docs/references/api/remotes/remote-local/local-remote-config.mdx new file mode 100644 index 0000000..fdc2791 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/local-remote-config.mdx @@ -0,0 +1,12 @@ +--- +title: 'LocalRemoteConfig' +description: 'LocalRemoteConfig — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local/src/config.ts#L7) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-local/local-remote-interface.mdx b/content/docs/references/api/remotes/remote-local/local-remote-interface.mdx new file mode 100644 index 0000000..448ea11 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/local-remote-interface.mdx @@ -0,0 +1,17 @@ +--- +title: 'LocalRemote' +description: 'LocalRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local/src/config.ts#L14) + +## Properties + + diff --git a/content/docs/references/api/remotes/remote-local/local-remote.mdx b/content/docs/references/api/remotes/remote-local/local-remote.mdx new file mode 100644 index 0000000..37c506d --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/local-remote.mdx @@ -0,0 +1,20 @@ +--- +title: 'localRemote' +description: 'localRemote — API reference.' +--- + +{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */} + +[View source ↗](https://github.com/webview-bundle/webview-bundle/blob/262b4d90c5f036ab9d52086913752b990875ac84/packages/remote/local/src/config.ts#L19) + +```ts +function localRemote(config: LocalRemoteConfig): LocalRemote; +``` + +## Parameters + + + +## Returns + +`LocalRemote` diff --git a/content/docs/references/api/remotes/remote-local/meta.json b/content/docs/references/api/remotes/remote-local/meta.json new file mode 100644 index 0000000..72ccdcc --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/meta.json @@ -0,0 +1,10 @@ +{ + "title": "@wvb/remote-local", + "pages": [ + "---Functions---", + "local-remote", + "---Interfaces---", + "local-remote-interface", + "local-remote-config" + ] +} diff --git a/content/docs/references/api/remotes/remote-local/meta.ko.json b/content/docs/references/api/remotes/remote-local/meta.ko.json new file mode 100644 index 0000000..d293d24 --- /dev/null +++ b/content/docs/references/api/remotes/remote-local/meta.ko.json @@ -0,0 +1,10 @@ +{ + "title": "@wvb/remote-local", + "pages": [ + "---함수---", + "local-remote", + "---인터페이스---", + "local-remote-interface", + "local-remote-config" + ] +} diff --git a/content/docs/references/bridge/errors.ko.mdx b/content/docs/references/bridge/errors.ko.mdx deleted file mode 100644 index 4cbe533..0000000 --- a/content/docs/references/bridge/errors.ko.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: 오류 -description: '@wvb/bridge가 BridgeError와 BridgeErrorCode 값을 통해 실패를 표현하는 방식.' ---- - -`@wvb/bridge`에서 발생하는 모든 실패는 `BridgeError`로 표현됩니다. 이 클래스는 `Error`의 서브클래스이며, `name`은 `'BridgeError'`로 설정되고 `BridgeErrorCode`에서 가져온 선택적 `code`를 가집니다. `invoke()`를 직접 호출하든 타입이 지정된 명령어 그룹을 호출하든, 모든 거부(rejection)는 이 단일 오류 타입으로 감싸집니다. 따라서 잡아서 검사할 형태가 하나로 통일됩니다. - -## BridgeError - -`BridgeError`는 내장 `Error`를 확장합니다. 사람이 읽을 수 있는 설명은 `message`에서 확인하고, 특정 실패에 따라 분기하려면 `code`를 확인하세요. 이 패키지는 타입 가드 `isBridgeError`와 `isBridgeErrorData`, 그리고 정적 헬퍼 `BridgeError.of(code, message?)`와 `BridgeError.from(value)`도 내보냅니다. - -## BridgeErrorCode - -`code` 속성은 존재할 경우 다음 값 중 하나입니다. 이 값은 전송 시 실려 가는 문자열입니다. - -| Code | Value | 발생 상황 | -| ----------------------- | ------------------------- | ------------------------------------------------ | -| `InvalidParams` | `invalid_params` | 명령어 파라미터가 거부되었습니다. | -| `RemoteNotInitialized` | `remote_not_initialized` | 호스트에 원격이 설정되지 않았습니다. | -| `UpdaterNotInitialized` | `updater_not_initialized` | 호스트에 업데이터가 설정되지 않았습니다. | -| `HandlerNotFound` | `handler_not_found` | 호스트에 해당 명령어를 처리할 핸들러가 없습니다. | -| `UnencodableResult` | `unencodable_result` | 결과를 반환용으로 인코딩할 수 없습니다. | - -## 오류 처리 - -오류를 잡은 뒤 `isBridgeError`로 타입을 좁히고, `code`를 `BridgeErrorCode`와 비교하세요. - -```ts -import { BridgeError, BridgeErrorCode, isBridgeError, updater } from '@wvb/bridge'; - -try { - const update = await updater.getUpdate('app'); - // update 사용... -} catch (err) { - if (isBridgeError(err) && err.code === BridgeErrorCode.UpdaterNotInitialized) { - // 네이티브 호스트에 업데이터가 설정되지 않았습니다. - return; - } - throw err; -} -``` - - - `err.code`는 선택적입니다. `BridgeError`는 `code` 없이 던져질 수 있습니다(예를 들어 웹뷰가 - 전송(transport)이 없는 환경에서 실행되는 경우). 따라서 분기하기 전에 `undefined`인지 확인하세요. - - -## 함께 보기 - - - - - diff --git a/content/docs/references/bridge/errors.mdx b/content/docs/references/bridge/errors.mdx deleted file mode 100644 index 5cc93d0..0000000 --- a/content/docs/references/bridge/errors.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Errors -description: How @wvb/bridge surfaces failures through BridgeError and its BridgeErrorCode values. ---- - -Every failure from `@wvb/bridge` surfaces as a `BridgeError`. It is an `Error` subclass with `name` set to `'BridgeError'` and an optional `code` drawn from `BridgeErrorCode`. Whether you call `invoke()` directly or a typed command group, any rejection is wrapped in this single error type, so you have one shape to catch and inspect. - -## BridgeError - -`BridgeError` extends the built-in `Error`. Read its `message` for a human-readable description and its `code` to branch on a specific failure. The package also exports the type guards `isBridgeError` and `isBridgeErrorData`, plus the static helpers `BridgeError.of(code, message?)` and `BridgeError.from(value)`. - -## BridgeErrorCode - -The `code` property, when present, is one of the following values. The value is the string carried on the wire. - -| Code | Value | When it occurs | -| ----------------------- | ------------------------- | ------------------------------------------- | -| `InvalidParams` | `invalid_params` | The command parameters were rejected. | -| `RemoteNotInitialized` | `remote_not_initialized` | No remote is configured on the host. | -| `UpdaterNotInitialized` | `updater_not_initialized` | No updater is configured on the host. | -| `HandlerNotFound` | `handler_not_found` | The host has no handler for the command. | -| `UnencodableResult` | `unencodable_result` | The result could not be encoded for return. | - -## Handling errors - -Catch the error, narrow it with `isBridgeError`, then compare `code` against `BridgeErrorCode`: - -```ts -import { BridgeError, BridgeErrorCode, isBridgeError, updater } from '@wvb/bridge'; - -try { - const update = await updater.getUpdate('app'); - // Use update... -} catch (err) { - if (isBridgeError(err) && err.code === BridgeErrorCode.UpdaterNotInitialized) { - // The native host has no updater configured. - return; - } - throw err; -} -``` - - - `err.code` is optional. A `BridgeError` may be thrown without a `code` (for example, when the - webview runs somewhere no transport is present), so guard for `undefined` before branching on it. - - -## See also - - - - - diff --git a/content/docs/references/bridge/index.ko.mdx b/content/docs/references/bridge/index.ko.mdx deleted file mode 100644 index 57a31c9..0000000 --- a/content/docs/references/bridge/index.ko.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -title: Bridge -description: 웹뷰 안의 JavaScript가 네이티브 호스트를 호출하게 해주는 웹 측 @wvb/bridge 라이브러리입니다. ---- - -`@wvb/bridge`는 Webview Bundle의 웹 측 라이브러리입니다. **웹뷰 안에서** 실행되는 JavaScript가 번들 소스, 원격, 업데이터를 관리하는 네이티브 호스트를 호출하게 해줍니다. 하나의 통일된 `invoke()`가 Electron, Tauri, Android, iOS에서 모두 동작합니다. 플랫폼별 전송 방식은 추상화되어 있으므로 웹 코드는 어떤 플랫폼에서도 동일하게 유지됩니다. - -이 패키지는 순수 TypeScript이며, 타입이 지정된 명령어 그룹(`source`, `remote`, `updater`)을 제공합니다. 따라서 `invoke()`를 직접 호출하는 경우는 드뭅니다. 네이티브 호스트가 이 명령어들을 노출하는 방법은 [플랫폼 연동](/docs/guide/platform-integration)을 참고하세요. 대응하는 네이티브 API는 [Node API 레퍼런스](/docs/references/node)를 참고하세요. - -## Install - -`@wvb/bridge`의 버전은 `0.1.0`입니다. - - - - -```sh -npm install @wvb/bridge -``` - - - - -```sh -pnpm add @wvb/bridge -``` - - - - -```sh -yarn add @wvb/bridge -``` - - - - -## invoke - -`invoke()`는 저수준 진입점입니다. 타입이 지정된 모든 명령어 헬퍼가 이를 기반으로 만들어져 있으므로, 보통은 `source`, `remote`, `updater`를 사용합니다. 타입이 지정된 그룹이 다루지 않는 명령어에만 `invoke()`를 직접 사용하세요. - -```ts -import { invoke } from '@wvb/bridge'; - -const result = await invoke('sourceResolveFilepath', { bundleName: 'app' }); -``` - -시그니처는 `invoke(name: string, params?: InvokeParams): Promise`입니다. 모든 실패는 `BridgeError`로 감싸집니다. `InvokeParams`는 열린 레코드입니다. - -```ts -type InvokeParams = { [key: string | number]: any }; -``` - -## platform - -`platform` 객체는 웹뷰가 어떤 네이티브 호스트에서 실행 중인지 알려줍니다. 모든 속성은 접근할 때마다 실시간으로 계산되므로, 감지 조건이 바뀌어도 정확한 값을 유지합니다. - -```ts -import { platform } from '@wvb/bridge'; - -if (platform.isElectron) { - // Electron 전용 동작 -} - -console.log(platform.type); // 'electron' | 'tauri' | 'android' | 'ios' -``` - -| Member | Type | Description | -| ------------ | --------------------------------------------- | ---------------------------------- | -| `type` | `'electron' \| 'tauri' \| 'android' \| 'ios'` | 감지된 호스트 플랫폼입니다. | -| `isElectron` | `boolean` | Electron에서 실행 중이면 참입니다. | -| `isTauri` | `boolean` | Tauri에서 실행 중이면 참입니다. | -| `isAndroid` | `boolean` | Android에서 실행 중이면 참입니다. | -| `isIos` | `boolean` | iOS에서 실행 중이면 참입니다. | - - - `@wvb/bridge`는 이 네 가지 플랫폼을 감지합니다. Deno Desktop은 실험적이며, `@wvb/bridge`가 아니라 - `@wvb/deno-desktop`을 통해 브리지 지원을 제공합니다. - - -## Transports - -각 플랫폼에는 저마다의 전송 방식이 있으며, 모두 `invoke()` 뒤에 숨겨져 있습니다. - -- **Electron**은 `window.wvbElectron.invoke(name, params)`를 호출합니다. -- **Tauri**는 `wvb-tauri` 플러그인 명령어를 호출하며, 명령어 이름을 snake case로 변환합니다. -- **Android**는 `window.wvbAndroid`로 JSON 메시지를 전송하고, 임시 전역 콜백을 통해 결과를 반환합니다. -- **iOS**는 `wvbIos` WebKit 메시지 핸들러로 메시지를 전송하고, 임시 전역 콜백을 통해 결과를 반환합니다. - -이 중 어느 것도 존재하지 않는 곳에서 웹뷰가 실행되면, `invoke()`는 네이티브 웹뷰가 Webview Bundle을 지원해야 한다는 오류를 던집니다. - -## Command groups - -브리지는 타입이 지정된 세 가지 명령어 그룹을 노출합니다. 각 메서드는 타입이 지정된 `invoke()` 호출입니다. 따라서 호출은 타입 검사를 거쳐 대응하는 네이티브 핸들러로 전달됩니다. - - - - - - - - - -## Updater flow - -일반적인 무선(OTA) 확인은 더 새로운 번들을 다운로드하고 설치한 뒤, 웹뷰를 다시 로드하여 이를 반영합니다. - -```ts -import { updater } from '@wvb/bridge'; - -async function checkForUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return; - } - - await updater.download(bundleName); - await updater.install(bundleName, update.version); - - // 웹뷰가 새로 설치된 번들을 제공하도록 다시 로드합니다. - window.location.reload(); -} -``` - -업데이터의 전체 기능은 [업데이터 레퍼런스](/docs/references/bridge/updater)를 참고하세요. - -## See also - - - - - diff --git a/content/docs/references/bridge/index.mdx b/content/docs/references/bridge/index.mdx deleted file mode 100644 index 9bc40ef..0000000 --- a/content/docs/references/bridge/index.mdx +++ /dev/null @@ -1,160 +0,0 @@ ---- -title: Bridge -description: The web-side @wvb/bridge library that lets JavaScript inside the webview call the native host. ---- - -`@wvb/bridge` is the web-side companion to Webview Bundle. It lets JavaScript running **inside the webview** call the native host that manages your bundle source, remote, and updater. One uniform `invoke()` works across Electron, Tauri, Android, and iOS. The bridge hides the per-platform transport, so your web code stays the same on every platform. - -The package is pure TypeScript and ships typed command groups (`source`, `remote`, `updater`) so you rarely call `invoke()` directly. For how the native host exposes these commands, see [Platform integration](/docs/guide/platform-integration); for the matching native API, see the [Node API reference](/docs/references/node). - -## Install - -`@wvb/bridge` is version `0.1.0`. - - - - -```sh -npm install @wvb/bridge -``` - - - - -```sh -pnpm add @wvb/bridge -``` - - - - -```sh -yarn add @wvb/bridge -``` - - - - -## invoke - -`invoke()` is the low-level entry point. Every typed command helper is built on top of it, so you usually reach for `source`, `remote`, or `updater` instead. Use `invoke()` directly only for commands the typed groups do not cover. - -```ts -import { invoke } from '@wvb/bridge'; - -const result = await invoke('sourceResolveFilepath', { bundleName: 'app' }); -``` - -The signature is `invoke(name: string, params?: InvokeParams): Promise`. Any failure is wrapped in a `BridgeError`. `InvokeParams` is an open record: - -```ts -type InvokeParams = { [key: string | number]: any }; -``` - -## platform - -The `platform` object reports which native host the webview is running under. Every property is computed live on access, so it stays correct even if detection conditions change. - -```ts -import { platform } from '@wvb/bridge'; - -if (platform.isElectron) { - // Electron-specific behavior -} - -console.log(platform.type); // 'electron' | 'tauri' | 'android' | 'ios' -``` - -| Member | Type | Description | -| ------------ | --------------------------------------------- | --------------------------------- | -| `type` | `'electron' \| 'tauri' \| 'android' \| 'ios'` | The detected host platform. | -| `isElectron` | `boolean` | True when running under Electron. | -| `isTauri` | `boolean` | True when running under Tauri. | -| `isAndroid` | `boolean` | True when running under Android. | -| `isIos` | `boolean` | True when running under iOS. | - - - `@wvb/bridge` detects these four platforms. Deno Desktop is experimental and provides its bridge - support through `@wvb/deno-desktop` rather than `@wvb/bridge`. - - -## Transports - -Each platform has its own transport, all hidden behind `invoke()`: - -- **Electron** calls `window.wvbElectron.invoke(name, params)`. -- **Tauri** calls the `wvb-tauri` plugin command, converting the command name to snake case. -- **Android** posts a JSON message to `window.wvbAndroid` and resolves through a temporary global callback. -- **iOS** posts a message to the `wvbIos` WebKit message handler and resolves through a temporary global callback. - -If the webview runs somewhere none of these are present, `invoke()` throws an error explaining that the native webview must support Webview Bundle. - -## Command groups - -The bridge exposes three typed command groups. Each method is a typed `invoke()` call, so calls are type-checked and route to the matching native handler. - - - - - - - - - -## Updater flow - -A typical over-the-air (OTA) check downloads and installs a newer bundle, then reloads the webview to pick it up: - -```ts -import { updater } from '@wvb/bridge'; - -async function checkForUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return; - } - - await updater.download(bundleName); - await updater.install(bundleName, update.version); - - // Reload so the webview serves the newly installed bundle. - window.location.reload(); -} -``` - -For the full updater surface, see the [updater reference](/docs/references/bridge/updater). - -## See also - - - - - diff --git a/content/docs/references/bridge/meta.json b/content/docs/references/bridge/meta.json deleted file mode 100644 index ea18165..0000000 --- a/content/docs/references/bridge/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Bridge", - "pages": ["index", "source", "remote", "updater", "errors", "testing"] -} diff --git a/content/docs/references/bridge/meta.ko.json b/content/docs/references/bridge/meta.ko.json deleted file mode 100644 index ea18165..0000000 --- a/content/docs/references/bridge/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Bridge", - "pages": ["index", "source", "remote", "updater", "errors", "testing"] -} diff --git a/content/docs/references/bridge/remote.ko.mdx b/content/docs/references/bridge/remote.ko.mdx deleted file mode 100644 index dd032a1..0000000 --- a/content/docs/references/bridge/remote.ko.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Remote -description: 네이티브 호스트를 통해 설정된 원격 서버에 질의하는 bridge의 remote.* 명령어 그룹입니다. ---- - -`remote`는 네이티브 호스트를 통해 설정된 원격 서버와 통신하는 bridge 명령어 그룹입니다. 웹뷰 안의 웹 코드에서 이 그룹을 사용하면 원격이 제공하는 항목을 나열하고, 번들의 현재 정보를 조회하고, 다운로드를 실행할 수 있습니다. 웹 쪽에서 번들 바이트를 직접 다루지 않아도 됩니다. - -각 메서드는 대응하는 네이티브 핸들러로 라우팅되는 타입 지정 `invoke()` 호출입니다. 따라서 호출은 타입 검사를 거치며, `invoke()`를 직접 다룰 일은 거의 없습니다. 패키지 전반에 대한 내용은 [Bridge 개요](/docs/references/bridge)를 참고하세요. 이 명령어들이 도달하는 네이티브 쪽에 대한 내용은 [Node remote API](/docs/references/node/remote)를 참고하세요. - -## 메서드 - -| Method | Purpose | -| -------------------------------------- | ---------------------------------- | -| `listBundles()` | 원격이 제공하는 번들을 나열합니다. | -| `getInfo(bundleName)` | 번들의 현재 정보를 가져옵니다. | -| `download(bundleName)` | 현재 버전을 다운로드합니다. | -| `downloadVersion(bundleName, version)` | 특정 버전을 다운로드합니다. | - -```ts -import { remote } from '@wvb/bridge'; - -const bundles = await remote.listBundles(); -const info = await remote.getInfo('app'); - -await remote.download('app'); -await remote.downloadVersion('app', '1.2.0'); -``` - -## 반환값 - -`getInfo`, `download`, `downloadVersion`은 `RemoteBundleInfo`로 resolve됩니다. `download`와 `downloadVersion`은 오직 `RemoteBundleInfo`로만 resolve되며, 번들 바이트는 bridge를 통해 전달되지 않습니다. 네이티브 호스트가 다운로드한 번들을 자신의 소스 저장소에 기록하고, 웹 쪽은 메타데이터만 받습니다. - -`listBundles`는 원격이 현재 제공하는 번들 목록으로 resolve되며, 이는 원격의 `GET /bundles` 응답과 동일합니다. - - - 호스트에 원격이 설정되어 있지 않으면 이 호출들은 `code`가 `RemoteNotInitialized`인 `BridgeError`와 - 함께 실패합니다. 오류 처리에 대한 내용은 [Bridge 개요](/docs/references/bridge)를 참고하세요. - - -## 참고 자료 - - - - - - diff --git a/content/docs/references/bridge/remote.mdx b/content/docs/references/bridge/remote.mdx deleted file mode 100644 index bb20a07..0000000 --- a/content/docs/references/bridge/remote.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Remote -description: The bridge remote.* command group, which queries the configured remote server through the native host. ---- - -`remote` is the bridge command group that talks to the configured remote server through the native host. Use it from web code inside the webview to list what a remote offers, read a bundle's current info, and trigger downloads — without the web side handling any bundle bytes itself. - -Each method is a typed `invoke()` call that routes to the matching native handler, so calls are type-checked and you rarely touch `invoke()` directly. For the broader package, see the [Bridge overview](/docs/references/bridge); for the native side these commands reach into, see the [Node remote API](/docs/references/node/remote). - -## Methods - -| Method | Purpose | -| -------------------------------------- | ----------------------------------- | -| `listBundles()` | List bundles offered by the remote. | -| `getInfo(bundleName)` | Get current info for a bundle. | -| `download(bundleName)` | Download the current version. | -| `downloadVersion(bundleName, version)` | Download a specific version. | - -```ts -import { remote } from '@wvb/bridge'; - -const bundles = await remote.listBundles(); -const info = await remote.getInfo('app'); - -await remote.download('app'); -await remote.downloadVersion('app', '1.2.0'); -``` - -## Return values - -`getInfo`, `download`, and `downloadVersion` resolve to a `RemoteBundleInfo`. `download` and `downloadVersion` resolve to `RemoteBundleInfo` only — the bundle bytes are not transferred across the bridge. The native host writes the downloaded bundle into its source store, and the web side receives just the metadata. - -`listBundles` resolves to the list of bundles the remote currently offers, mirroring the remote `GET /bundles` response. - - - If no remote is configured on the host, these calls fail with a `BridgeError` whose `code` is - `RemoteNotInitialized`. See the [Bridge overview](/docs/references/bridge) for error handling. - - -## See also - - - - - - diff --git a/content/docs/references/bridge/source.ko.mdx b/content/docs/references/bridge/source.ko.mdx deleted file mode 100644 index 2bfd9e7..0000000 --- a/content/docs/references/bridge/source.ko.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Source -description: 네이티브 호스트에서 빌트인 번들과 원격 번들을 읽고 관리하는 bridge source.* 명령어 그룹입니다. ---- - -`source`는 네이티브 번들 소스에 매핑되는 bridge 명령어 그룹입니다. 네이티브 번들 소스는 호스트가 빌트인 번들과 원격 번들을 저장하는 공간입니다. 각 메서드는 타입이 지정된 `invoke()` 호출이므로, 웹 측에서 타입 검사를 거쳐 대응하는 네이티브 핸들러로 라우팅됩니다. 이 그룹으로 번들을 나열하고, 버전과 메타데이터를 불러오고, 디스크상의 경로를 확인하고, 다운로드한 원격 번들을 관리하세요. - -이 명령어들이 호출하는 네이티브 측은 [Node API source 레퍼런스](/docs/references/node/source)를 참고하세요. `invoke()`, `platform`, `remote` 및 `updater` 그룹, 오류, 테스트 등 나머지 bridge 영역은 [Bridge 개요](/docs/references/bridge)를 참고하세요. - -```ts -import { source } from '@wvb/bridge'; - -const bundles = await source.listBundles(); -const path = await source.resolveFilepath('app'); -``` - -## Methods - -| Method | Purpose | -| ----------------------------------------------- | ----------------------------------------------- | -| `listBundles()` | 사용 가능한 번들을 나열합니다. | -| `loadVersion(bundleName)` | 번들의 활성 버전을 불러옵니다. | -| `updateVersion(bundleName, version)` | 번들의 활성 원격 버전을 설정합니다. | -| `resolveFilepath(bundleName)` | 소스가 제공하는 디스크상의 경로를 확인합니다. | -| `getBuiltinBundleFilepath(bundleName, version)` | 빌트인 번들 파일의 경로입니다. | -| `getRemoteBundleFilepath(bundleName, version)` | 원격 번들 파일의 경로입니다. | -| `loadBuiltinMetadata(bundleName, version)` | 빌트인 번들의 메타데이터를 불러옵니다. | -| `loadRemoteMetadata(bundleName, version)` | 원격 번들의 메타데이터를 불러옵니다. | -| `unloadDescriptor(bundleName)` | 캐시된 번들 디스크립터를 제거합니다. | -| `removeRemoteBundle(bundleName, version)` | 다운로드한 원격 번들을 삭제합니다. | -| `remoteRetainedVersions(bundleName)` | 보관 중인 원격 버전(현재 및 이전)을 나열합니다. | -| `pruneRemoteBundles(bundleName)` | 더 이상 보관하지 않는 원격 번들을 정리합니다. | - - -bridge의 `ListBundleItem`은 `{ type, item }` 형태로 중첩되어 있어, `@wvb/node`의 평면적인 `ListBundleItem`과 다릅니다. 웹 측에서 작업할 때는 bridge 타입을 사용하세요. - - -## Managing remote bundles - -무선(OTA) 업데이트가 적용되면, 호스트는 현재 원격 버전과 이전 버전들을 함께 보관합니다. 보관 중인 항목을 확인한 다음, 더 이상 필요하지 않은 것을 정리하세요. - -```ts -import { source } from '@wvb/bridge'; - -async function tidyRemoteBundles(bundleName: string) { - const retained = await source.remoteRetainedVersions(bundleName); - console.log('retained versions:', retained); - - await source.pruneRemoteBundles(bundleName); -} -``` - -다운로드한 특정 버전을 삭제하려면 `removeRemoteBundle(bundleName, version)`을 호출하세요. - -## See also - - - - - diff --git a/content/docs/references/bridge/source.mdx b/content/docs/references/bridge/source.mdx deleted file mode 100644 index 071e581..0000000 --- a/content/docs/references/bridge/source.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Source -description: The bridge source.* command group for reading and managing builtin and remote bundles on the native host. ---- - -`source` is the bridge command group that maps to the native bundle source — the host's store of builtin and remote bundles. Each method is a typed `invoke()` call, so it is type-checked web-side and routes to the matching native handler. Use it to list bundles, load versions and metadata, resolve on-disk paths, and manage downloaded remote bundles. - -For the native side these commands call into, see the [Node API source reference](/docs/references/node/source). For the rest of the bridge surface — `invoke()`, `platform`, the `remote` and `updater` groups, errors, and testing — see the [Bridge overview](/docs/references/bridge). - -```ts -import { source } from '@wvb/bridge'; - -const bundles = await source.listBundles(); -const path = await source.resolveFilepath('app'); -``` - -## Methods - -| Method | Purpose | -| ----------------------------------------------- | ------------------------------------------------------ | -| `listBundles()` | List available bundles. | -| `loadVersion(bundleName)` | Load the active version for a bundle. | -| `updateVersion(bundleName, version)` | Set the active remote version for a bundle. | -| `resolveFilepath(bundleName)` | Resolve the on-disk path the source serves. | -| `getBuiltinBundleFilepath(bundleName, version)` | Path of a builtin bundle file. | -| `getRemoteBundleFilepath(bundleName, version)` | Path of a remote bundle file. | -| `loadBuiltinMetadata(bundleName, version)` | Load metadata for a builtin bundle. | -| `loadRemoteMetadata(bundleName, version)` | Load metadata for a remote bundle. | -| `unloadDescriptor(bundleName)` | Drop a cached bundle descriptor. | -| `removeRemoteBundle(bundleName, version)` | Remove a downloaded remote bundle. | -| `remoteRetainedVersions(bundleName)` | List retained remote versions (current plus previous). | -| `pruneRemoteBundles(bundleName)` | Prune remote bundles that are no longer retained. | - - -The bridge `ListBundleItem` is nested as `{ type, item }`, which differs from the flat `ListBundleItem` in `@wvb/node`. Use the bridge types when working web-side. - - -## Managing remote bundles - -After an over-the-air (OTA) update lands, the host retains the current remote version plus previous ones. Read what is retained, then prune what is no longer needed: - -```ts -import { source } from '@wvb/bridge'; - -async function tidyRemoteBundles(bundleName: string) { - const retained = await source.remoteRetainedVersions(bundleName); - console.log('retained versions:', retained); - - await source.pruneRemoteBundles(bundleName); -} -``` - -To drop a specific downloaded version, call `removeRemoteBundle(bundleName, version)`. - -## See also - - - - - diff --git a/content/docs/references/bridge/testing.ko.mdx b/content/docs/references/bridge/testing.ko.mdx deleted file mode 100644 index a8a857b..0000000 --- a/content/docs/references/bridge/testing.ko.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: 테스트 -description: '@wvb/bridge/testing 헬퍼로 네이티브 호스트 없이 단위 테스트에서 브리지를 구동합니다.' ---- - -`@wvb/bridge/testing` 하위 경로를 사용하면 실제 네이티브 호스트 없이 브리지를 호출하는 웹 코드를 실행할 수 있습니다. 코드가 호출하는 명령어에 핸들러를 등록하고, 감지된 플랫폼을 강제로 지정하며, 테스트 사이에 목(mock)을 정리하세요. 목 명령어 키는 `'source.loadVersion'`이나 `'updater.getUpdate'`처럼 점(dot)으로 구분되고 타입이 지정되어 있으므로, 핸들러 시그니처의 타입 검사가 유지됩니다. - -이 헬퍼가 대신하는 브리지 표면은 [Bridge 개요](/docs/references/bridge)를 참고하세요. - -## Import - -런타임 진입점과 별개인 `testing` 하위 경로에서 헬퍼를 가져오세요. - -```ts -import { mockInvoke, mockPlatform, mockBridge, clearInvokeMocks } from '@wvb/bridge/testing'; -``` - -## 헬퍼 - -| 헬퍼 | 시그니처 | 반환값 | -| ------------------ | ------------------------------ | ---------------- | -| `mockInvoke` | `mockInvoke(command, handler)` | `Disposable` | -| `mockPlatform` | `mockPlatform(type)` | `Disposable` | -| `mockBridge` | `mockBridge(options?)` | 체이닝 가능한 목 | -| `clearInvokeMocks` | `clearInvokeMocks()` | `void` | - -### mockInvoke - -`mockInvoke(command, handler)`는 단일 명령어에 핸들러를 등록하고 `Disposable`을 반환합니다. `command`는 명령어 그룹에 속한, 점으로 구분되고 타입이 지정된 키입니다. `handler`는 일치하는 호출이 resolve할 값을 만듭니다. 반환값을 dispose하면 해당 목만 제거됩니다. - -```ts -import { mockInvoke } from '@wvb/bridge/testing'; - -const mock = mockInvoke('source.loadVersion', () => '1.2.0'); - -// 나중에 이 단일 목만 제거합니다. -mock[Symbol.dispose](); -``` - -### mockPlatform - -`mockPlatform(type)`는 `platform.type`을 지정한 값으로 강제하고 `Disposable`을 반환합니다. 웹 코드의 플랫폼별 분기를 구동할 때 사용하세요. 모든 `platform` 속성은 실시간으로 계산되므로, 반환값을 dispose하면 정상적인 감지가 복원됩니다. - -```ts -import { mockPlatform } from '@wvb/bridge/testing'; - -const mock = mockPlatform('ios'); -// platform.type === 'ios', platform.isIos === true - -mock[Symbol.dispose](); -``` - -### mockBridge - -`mockBridge(options?)`는 플랫폼 목과 명령어 목을 결합한, 체이닝 가능한 목을 반환합니다. 옵션에 `platform`을 넘겨 감지될 호스트를 설정하고, `.mockInvoke(command, handler)`를 체이닝하여 명령어를 등록하며, `.clear()`를 호출하여 목이 등록한 모든 것을 제거하세요. - -```ts -import { mockBridge } from '@wvb/bridge/testing'; - -const bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, - })) - .mockInvoke('updater.download', () => undefined); - -// 테스트 대상 코드를 실행한 뒤: -bridge.clear(); -``` - -### clearInvokeMocks - -`clearInvokeMocks()`는 등록된 모든 명령어 목을 한 번에 제거합니다. 전역 teardown 훅에서 호출하여 어떤 목도 다음 테스트로 새어 나가지 않게 하세요. - -## 단위 테스트 예시 - -이 예시는 [Vitest](https://vitest.dev)를 사용하여 목으로 만든 브리지에 대해 업데이트 확인 함수를 테스트합니다. 목은 코드가 호출하는 `updater.getUpdate`와 `updater.download` 명령어를 제공하고, `.clear()`는 각 테스트 이후 상태를 초기화합니다. - -```ts title="update.test.ts" -import { afterEach, expect, test, vi } from 'vitest'; -import { updater } from '@wvb/bridge'; -import { mockBridge } from '@wvb/bridge/testing'; - -async function hasUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return false; - } - await updater.download(bundleName); - return true; -} - -let bridge: ReturnType; - -afterEach(() => { - bridge.clear(); -}); - -test('downloads when an update is available', async () => { - const download = vi.fn(() => undefined); - bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, - })) - .mockInvoke('updater.download', download); - - await expect(hasUpdate('app')).resolves.toBe(true); - expect(download).toHaveBeenCalledOnce(); -}); - -test('skips download when no update is available', async () => { - const download = vi.fn(() => undefined); - bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.1.0', - isAvailable: false, - })) - .mockInvoke('updater.download', download); - - await expect(hasUpdate('app')).resolves.toBe(false); - expect(download).not.toHaveBeenCalled(); -}); -``` - - - 테스트 대상 코드가 호출하는 모든 명령어에 핸들러를 등록하세요. 목으로 등록되지 않은 명령어는 - 핸들러가 없으므로, 브리지는 resolve하는 대신 `BridgeError`를 노출합니다. - - -## 함께 보기 - - - - - diff --git a/content/docs/references/bridge/testing.mdx b/content/docs/references/bridge/testing.mdx deleted file mode 100644 index 26e0e2c..0000000 --- a/content/docs/references/bridge/testing.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Testing -description: Drive the bridge in unit tests without a native host using the @wvb/bridge/testing helpers. ---- - -The `@wvb/bridge/testing` subpath lets you exercise web code that calls the bridge without a real native host. Register handlers for the commands your code invokes, force the detected platform, and tear the mocks down between tests. Mock command keys are dotted and typed, such as `'source.loadVersion'` or `'updater.getUpdate'`, so the handler signatures stay type-checked. - -For the bridge surface these helpers stand in for, see the [Bridge overview](/docs/references/bridge). - -## Import - -Import the helpers from the `testing` subpath, separate from the runtime entry point. - -```ts -import { mockInvoke, mockPlatform, mockBridge, clearInvokeMocks } from '@wvb/bridge/testing'; -``` - -## Helpers - -| Helper | Signature | Returns | -| ------------------ | ------------------------------ | -------------- | -| `mockInvoke` | `mockInvoke(command, handler)` | `Disposable` | -| `mockPlatform` | `mockPlatform(type)` | `Disposable` | -| `mockBridge` | `mockBridge(options?)` | chainable mock | -| `clearInvokeMocks` | `clearInvokeMocks()` | `void` | - -### mockInvoke - -`mockInvoke(command, handler)` registers a handler for a single command and returns a `Disposable`. The `command` is a dotted, typed key from a command group, and the `handler` produces the value the matching call resolves to. Dispose the result to remove just that mock. - -```ts -import { mockInvoke } from '@wvb/bridge/testing'; - -const mock = mockInvoke('source.loadVersion', () => '1.2.0'); - -// Later, remove this single mock. -mock[Symbol.dispose](); -``` - -### mockPlatform - -`mockPlatform(type)` forces `platform.type` to the given value and returns a `Disposable`. Use it to drive platform-specific branches in your web code. Because every `platform` property is computed live, disposing the result restores normal detection. - -```ts -import { mockPlatform } from '@wvb/bridge/testing'; - -const mock = mockPlatform('ios'); -// platform.type === 'ios', platform.isIos === true - -mock[Symbol.dispose](); -``` - -### mockBridge - -`mockBridge(options?)` returns a chainable mock that combines platform and command mocking. Pass `platform` in the options to set the detected host, chain `.mockInvoke(command, handler)` to register commands, and call `.clear()` to remove everything the mock registered. - -```ts -import { mockBridge } from '@wvb/bridge/testing'; - -const bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, - })) - .mockInvoke('updater.download', () => undefined); - -// Run code under test, then: -bridge.clear(); -``` - -### clearInvokeMocks - -`clearInvokeMocks()` removes every registered command mock at once. Call it in a global teardown hook so no mock leaks into the next test. - -## Unit test example - -This example uses [Vitest](https://vitest.dev) to test an update-check function against a mocked bridge. The mock supplies the `updater.getUpdate` and `updater.download` commands the code calls, and `.clear()` resets state after each test. - -```ts title="update.test.ts" -import { afterEach, expect, test, vi } from 'vitest'; -import { updater } from '@wvb/bridge'; -import { mockBridge } from '@wvb/bridge/testing'; - -async function hasUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return false; - } - await updater.download(bundleName); - return true; -} - -let bridge: ReturnType; - -afterEach(() => { - bridge.clear(); -}); - -test('downloads when an update is available', async () => { - const download = vi.fn(() => undefined); - bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.2.0', - isAvailable: true, - })) - .mockInvoke('updater.download', download); - - await expect(hasUpdate('app')).resolves.toBe(true); - expect(download).toHaveBeenCalledOnce(); -}); - -test('skips download when no update is available', async () => { - const download = vi.fn(() => undefined); - bridge = mockBridge({ platform: 'electron' }) - .mockInvoke('updater.getUpdate', () => ({ - name: 'app', - version: '1.1.0', - isAvailable: false, - })) - .mockInvoke('updater.download', download); - - await expect(hasUpdate('app')).resolves.toBe(false); - expect(download).not.toHaveBeenCalled(); -}); -``` - - - Register a handler for every command your code under test calls. An unmocked command has no - handler, so the bridge surfaces a `BridgeError` instead of resolving. - - -## See also - - - - - diff --git a/content/docs/references/bridge/updater.ko.mdx b/content/docs/references/bridge/updater.ko.mdx deleted file mode 100644 index b225488..0000000 --- a/content/docs/references/bridge/updater.ko.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Updater -description: 웹뷰 내부에서 무선(OTA) 업데이트 흐름을 구동하는 bridge updater.* 명령어 그룹. ---- - -`updater` 명령어 그룹은 웹뷰 내부에서 실행되는 웹 코드에서 무선(OTA) 업데이트 흐름을 구동합니다. 이 그룹으로 더 새로운 번들이 있는지 확인하고, 스테이징한 뒤, 네이티브 호스트에서 활성화하세요. 각 메서드는 타입이 지정된 `invoke()` 호출이며, 대응하는 네이티브 업데이터 핸들러로 라우팅됩니다. 따라서 `invoke()`를 직접 다룰 일은 거의 없습니다. - -이 명령어들 뒤에 있는 OTA 개념은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. bridge가 호출하는 네이티브 업데이터는 [Node 업데이터 API](/docs/references/node/updater)를 참고하세요. bridge 전체는 [Bridge 개요](/docs/references/bridge)를 참고하세요. - -```ts -import { updater } from '@wvb/bridge'; -``` - -## Methods - -| Method | Purpose | -| ------------------------------ | -------------------------------------- | -| `listRemotes()` | 원격 서버와 그 번들 정보를 나열합니다. | -| `getUpdate(bundleName)` | 업데이트가 있는지 확인합니다. | -| `download(bundleName)` | 설치할 업데이트를 스테이징합니다. | -| `install(bundleName, version)` | 스테이징된 버전을 활성화합니다. | - -## BundleUpdateInfo - -`getUpdate()`는 `BundleUpdateInfo`로 resolve됩니다. 이 값은 원격 번들과, 로컬에 설치된 버전과의 비교 결과를 설명합니다. - -```ts -type BundleUpdateInfo = { - name: string; - version: string; - localVersion?: string; - isAvailable: boolean; - etag?: string; - integrity?: string; - signature?: string; - lastModified?: string; -}; -``` - -업데이트를 스테이징하기 전에 `isAvailable`을 확인하세요. `version` 필드는 원격 서버가 제공하는 버전이고, `localVersion`은 호스트에 현재 설치된 버전입니다(있는 경우). - -## Update flow - -일반적인 OTA 확인은 더 새로운 번들을 스테이징하고 설치한 뒤, 웹뷰를 다시 로드하여 해당 번들을 제공합니다. - -```ts -import { updater } from '@wvb/bridge'; - -async function checkForUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return; - } - - await updater.download(bundleName); - await updater.install(bundleName, update.version); - - // 새로 설치된 번들을 웹뷰가 제공하도록 다시 로드합니다. - window.location.reload(); -} -``` - - - 네이티브 호스트에 업데이터가 설정되어 있지 않으면, 호출은 `code`가 - `BridgeErrorCode.UpdaterNotInitialized`인 `BridgeError`로 reject됩니다. 오류 처리는 [Bridge - 개요](/docs/references/bridge)를 참고하세요. - - -## See also - - - - - - diff --git a/content/docs/references/bridge/updater.mdx b/content/docs/references/bridge/updater.mdx deleted file mode 100644 index 59af018..0000000 --- a/content/docs/references/bridge/updater.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Updater -description: The bridge updater.* command group that drives the over-the-air update flow from inside the webview. ---- - -The `updater` command group drives the over-the-air (OTA) update flow from web code running inside the webview. Use it to check whether a newer bundle is available, stage it, and activate it on the native host. Each method is a typed `invoke()` call that routes to the matching native updater handler, so you rarely touch `invoke()` directly. - -For the OTA concepts behind these commands, see [Remote bundles](/docs/guide/remote-bundles). For the native updater the bridge calls into, see the [Node updater API](/docs/references/node/updater). For the bridge as a whole, see the [Bridge overview](/docs/references/bridge). - -```ts -import { updater } from '@wvb/bridge'; -``` - -## Methods - -| Method | Purpose | -| ------------------------------ | ------------------------------------- | -| `listRemotes()` | List remotes and their bundle info. | -| `getUpdate(bundleName)` | Check whether an update is available. | -| `download(bundleName)` | Stage an update for installation. | -| `install(bundleName, version)` | Activate a staged version. | - -## BundleUpdateInfo - -`getUpdate()` resolves to a `BundleUpdateInfo` describing the remote bundle and how it compares to the locally installed version: - -```ts -type BundleUpdateInfo = { - name: string; - version: string; - localVersion?: string; - isAvailable: boolean; - etag?: string; - integrity?: string; - signature?: string; - lastModified?: string; -}; -``` - -Check `isAvailable` before staging an update. The `version` field is the version the remote offers, while `localVersion` is the version currently installed on the host, if any. - -## Update flow - -A typical OTA check stages and installs a newer bundle, then reloads the webview to serve it: - -```ts -import { updater } from '@wvb/bridge'; - -async function checkForUpdate(bundleName: string) { - const update = await updater.getUpdate(bundleName); - if (!update.isAvailable) { - return; - } - - await updater.download(bundleName); - await updater.install(bundleName, update.version); - - // Reload so the webview serves the newly installed bundle. - window.location.reload(); -} -``` - - - If the native host has no updater configured, calls reject with a `BridgeError` whose `code` is - `BridgeErrorCode.UpdaterNotInitialized`. See the [Bridge overview](/docs/references/bridge) for - error handling. - - -## See also - - - - - - diff --git a/content/docs/guide/commands/builtin.ko.mdx b/content/docs/references/cli/builtin.ko.mdx similarity index 83% rename from content/docs/guide/commands/builtin.ko.mdx rename to content/docs/references/cli/builtin.ko.mdx index 400ea20..010dcd1 100644 --- a/content/docs/guide/commands/builtin.ko.mdx +++ b/content/docs/references/cli/builtin.ko.mdx @@ -3,11 +3,11 @@ title: wvb builtin description: 원격 또는 로컬 대상에서 빌트인 웹뷰 번들을 앱에 설치합니다. --- -`wvb builtin`은 앱이 빌트인 폴백으로 함께 배포할 수 있도록 번들을 출력 디렉터리에 설치합니다. 앱이 오프라인으로 시작하거나 무선(OTA)으로 더 새로운 번들을 가져오기 전에는 이 빌트인 번들을 제공합니다. 이 명령어는 [설정](/docs/config)의 `builtin.target`에 정의된 소스에서 번들을 가져오며, 기본값은 원격 대상입니다. +`wvb builtin`은 앱이 빌트인 폴백으로 함께 배포할 수 있도록 번들을 출력 디렉터리에 설치합니다. 앱이 오프라인으로 시작하거나 무선(OTA)으로 더 새로운 번들을 가져오기 전에는 이 빌트인 번들을 제공합니다. 이 명령어는 [설정](/docs/references/configuration)의 `builtin.target`에 정의된 소스에서 번들을 가져오며, 기본값은 원격 대상입니다. -이 명령어는 설치된 번들 집합을 설명하는 `manifest.json`과, 번들마다 `/_.wvb` 형태로 배치된 파일을 하나씩 기록합니다. 빌트인 소스와 원격 소스가 함께 동작하는 방식은 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +이 명령어는 설치된 번들 집합을 설명하는 `manifest.json`과, 번들마다 `/_.wvb` 형태로 배치된 파일을 하나씩 기록합니다. 빌트인 소스와 원격 소스가 함께 동작하는 방식은 [번들 소스](/docs/guide/core-concepts/bundle-source)를 참고하세요. -## Usage +## 사용법 ```sh # 설정된 대상에서 기본 디렉터리로 설치합니다 @@ -26,7 +26,7 @@ wvb builtin --android wvb builtin --no-write ``` -## Options +## 옵션 | Option | Aliases | Default | Description | | --------------- | ------- | ------------------------- | ------------------------------------------------------------------------------------------------- | @@ -46,34 +46,14 @@ wvb builtin --no-write 불리언 플래그는 `--flag`, `--flag=true|false`, 그리고 `--no-flag` 부정 형태를 받습니다. 따라서 `--no-write`와 `--no-clean`은 각각 `--write`와 `--clean`의 기본값을 끕니다. -## Mobile presets +## 모바일 프리셋 `--android`와 `--ios`는 일반 디렉터리 대신 플랫폼 프로젝트에 번들을 설치합니다. 각각 인자 없이 전달하면 대상을 자동 감지하고, `--android=` / `--ios=`로 전달하면 명시적인 모듈이나 프로젝트 디렉터리를 가리킵니다. `--ios` 프리셋은 `Project.swift`에 `folderReference`를 추가하며, 이는 Tuist 프로젝트에 적용됩니다. 한 번의 실행에서 `--android`와 `--ios`를 동시에 전달할 수 없습니다. -## Notes +## 참고 -- 대상 유형은 [설정](/docs/config)의 `builtin.target`에서 결정되며, `remote`와 `local`의 구별된 유니온입니다. 기본값은 원격 대상입니다. +- 대상 유형은 [설정](/docs/references/configuration)의 `builtin.target`에서 결정되며, `remote`와 `local`의 구별된 유니온입니다. 기본값은 원격 대상입니다. - `--endpoint`, `--channel`, `--concurrency`, `--progress`는 원격 대상에만 적용됩니다. - 실행에 성공하면 `manifest.json` 하나와, 설치된 번들마다 `/_.wvb` 파일 하나를 기록합니다. - -## See also - - - - - - diff --git a/content/docs/guide/commands/builtin.mdx b/content/docs/references/cli/builtin.mdx similarity index 85% rename from content/docs/guide/commands/builtin.mdx rename to content/docs/references/cli/builtin.mdx index 50d67eb..5ded2b1 100644 --- a/content/docs/guide/commands/builtin.mdx +++ b/content/docs/references/cli/builtin.mdx @@ -3,9 +3,9 @@ title: wvb builtin description: Install builtin webview bundles into your app from a remote or local target. --- -`wvb builtin` installs bundles into an output directory so your app can ship them as builtin fallbacks. When the app starts offline, or before it has fetched a newer bundle over the air, it serves these builtin bundles. The command pulls from the source defined by `builtin.target` in your [config](/docs/config), which defaults to a remote target. +`wvb builtin` installs bundles into an output directory so your app can ship them as builtin fallbacks. When the app starts offline, or before it has fetched a newer bundle over the air, it serves these builtin bundles. The command pulls from the source defined by `builtin.target` in your [config](/docs/references/configuration), which defaults to a remote target. -The command writes a `manifest.json` describing the installed set, plus one file per bundle laid out as `/_.wvb`. For how builtin and remote sources work together, see [Bundle sources](/docs/guide/bundle-sources). +The command writes a `manifest.json` describing the installed set, plus one file per bundle laid out as `/_.wvb`. For how builtin and remote sources work together, see [Bundle sources](/docs/guide/core-concepts/bundle-source). ## Usage @@ -54,26 +54,6 @@ Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation, ## Notes -- The target type comes from `builtin.target` in your [config](/docs/config), a discriminated union of `remote` and `local`. It defaults to a remote target. +- The target type comes from `builtin.target` in your [config](/docs/references/configuration), a discriminated union of `remote` and `local`. It defaults to a remote target. - `--endpoint`, `--channel`, `--concurrency`, and `--progress` apply to the remote target only. - A successful run writes a `manifest.json` and one `/_.wvb` file per installed bundle. - -## See also - - - - - - diff --git a/content/docs/references/cli/environment.ko.mdx b/content/docs/references/cli/environment.ko.mdx new file mode 100644 index 0000000..12175d2 --- /dev/null +++ b/content/docs/references/cli/environment.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '환경 변수' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/cli/environment.mdx b/content/docs/references/cli/environment.mdx new file mode 100644 index 0000000..c83fa5a --- /dev/null +++ b/content/docs/references/cli/environment.mdx @@ -0,0 +1,5 @@ +--- +title: 'Environment Variables' +--- + +This page is a work in progress. diff --git a/content/docs/guide/cli.ko.mdx b/content/docs/references/cli/index.ko.mdx similarity index 84% rename from content/docs/guide/cli.ko.mdx rename to content/docs/references/cli/index.ko.mdx index 762ac6a..27a7df6 100644 --- a/content/docs/guide/cli.ko.mdx +++ b/content/docs/references/cli/index.ko.mdx @@ -34,7 +34,7 @@ yarn wvb --help -대부분의 명령어는 작업 디렉터리에서 찾은 [`wvb.config`](/docs/config) 파일에서 기본값을 읽습니다. 따라서 일반적인 프로젝트에서는 인자 없이 `wvb pack`이나 `wvb upload`를 실행합니다. 각 명령어에는 전체 옵션 레퍼런스를 담은 전용 페이지가 있습니다. JavaScript에서 동일한 로직을 호출하려면 [프로그래밍 방식 API](/docs/guide/cli-programmatic)를 참고하세요. +대부분의 명령어는 작업 디렉터리에서 찾은 [`wvb.config`](/docs/references/configuration) 파일에서 기본값을 읽습니다. 따라서 일반적인 프로젝트에서는 인자 없이 `wvb pack`이나 `wvb upload`를 실행합니다. 각 명령어에는 전체 옵션 레퍼런스를 담은 전용 페이지가 있습니다. JavaScript에서 동일한 로직을 호출하려면 [프로그래밍 방식 API](/docs/references/cli/programmatic)를 참고하세요. ## 전역 플래그 @@ -73,54 +73,54 @@ wvb.config.json wvb.config.jsonc ``` -`wvb.config`와 `webview-bundle.config` 기본 이름은 모두 `.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, `.mts` 확장자를 지원합니다. `.json`과 `.jsonc` 확장자는 `wvb.config` 기본 이름에서만 지원합니다. 전체 스키마는 [설정](/docs/config)을 참고하세요. +`wvb.config`와 `webview-bundle.config` 기본 이름은 모두 `.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, `.mts` 확장자를 지원합니다. `.json`과 `.jsonc` 확장자는 `wvb.config` 기본 이름에서만 지원합니다. 전체 스키마는 [설정](/docs/references/configuration)을 참고하세요. ## 명령어 diff --git a/content/docs/guide/cli.mdx b/content/docs/references/cli/index.mdx similarity index 82% rename from content/docs/guide/cli.mdx rename to content/docs/references/cli/index.mdx index 083df92..57b755b 100644 --- a/content/docs/guide/cli.mdx +++ b/content/docs/references/cli/index.mdx @@ -34,7 +34,7 @@ yarn wvb --help -Most commands read their defaults from a [`wvb.config`](/docs/config) file discovered in the working directory, so a typical project runs `wvb pack` or `wvb upload` with no arguments at all. Each command has its own page with the full option reference. To call the same logic from JavaScript, see the [programmatic API](/docs/guide/cli-programmatic). +Most commands read their defaults from a [`wvb.config`](/docs/references/configuration) file discovered in the working directory, so a typical project runs `wvb pack` or `wvb upload` with no arguments at all. Each command has its own page with the full option reference. To call the same logic from JavaScript, see the [programmatic API](/docs/references/cli/programmatic). ## Global flags @@ -73,54 +73,54 @@ wvb.config.json wvb.config.jsonc ``` -Both the `wvb.config` and `webview-bundle.config` base names support the `.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, and `.mts` extensions. The `.json` and `.jsonc` extensions are supported for the `wvb.config` base name only. See [Configuration](/docs/config) for the full schema. +Both the `wvb.config` and `webview-bundle.config` base names support the `.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, and `.mts` extensions. The `.json` and `.jsonc` extensions are supported for the `wvb.config` base name only. See [Configuration](/docs/references/configuration) for the full schema. ## Commands diff --git a/content/docs/references/cli/installation.ko.mdx b/content/docs/references/cli/installation.ko.mdx new file mode 100644 index 0000000..b1d3193 --- /dev/null +++ b/content/docs/references/cli/installation.ko.mdx @@ -0,0 +1,99 @@ +--- +title: 설치 +description: '@wvb/cli 패키지를 설치하고 wvb 명령줄 도구를 실행합니다.' +--- + +명령줄 도구는 [`@wvb/cli`](/docs/references/api/cli) 패키지로 제공됩니다. 동일하게 동작하는 두 +바이너리 `wvb`와 `webview-bundle`을 노출하므로, 스크립트에서 읽기 좋은 쪽을 사용하세요. + +## 요구 사항 + +- **Node.js 18.14.1 이상.** Node 19는 19.7.0부터 지원하며, Node 20 이상은 제한 없이 동작합니다. +- 이 패키지는 ESM 전용입니다. 프로젝트 코드가 ESM이든 CommonJS든 관계없이 실행됩니다. + +## 설치 + +`@wvb/cli`를 개발 의존성으로 추가하세요. 이 도구는 앱의 런타임 의존성이 아니라 빌드 시점 +도구입니다. + + + + +```sh +npm install -D @wvb/cli +``` + + + + +```sh +pnpm add -D @wvb/cli +``` + + + + +```sh +yarn add -D @wvb/cli +``` + + + + +## 실행 + +바이너리를 패키지 매니저를 통해 실행하면 `node_modules`에서 해석됩니다. + + + + +```sh +npx wvb --help +``` + + + + +```sh +pnpm wvb --help +``` + + + + +```sh +yarn wvb --help +``` + + + + +반복 가능한 빌드를 위해 `package.json`의 scripts에 명령어를 추가하세요. + +```json title="package.json" +{ + "scripts": { + "bundle": "wvb pack", + "bundle:deploy": "wvb upload && wvb deploy" + } +} +``` + + + 전역 설치보다 프로젝트 로컬 설치를 권장합니다. CLI 버전이 다른 개발 의존성과 함께 고정되므로, 모든 + 머신과 CI 실행이 동일한 버전을 사용합니다. + + +## 확인 + +설치된 버전을 출력해 바이너리가 해석되는지 확인하세요. + +```sh +npx wvb --version +``` + +## 다음 단계 + +- [`wvb.config`](/docs/references/configuration) 파일을 만들어 명령어를 인자 없이 실행하세요. +- [CLI 개요](/docs/references/cli)에서 모든 명령어를 살펴보세요. +- [프로그래밍 방식 API](/docs/references/cli/programmatic)로 동일한 로직을 JavaScript에서 호출하세요. diff --git a/content/docs/references/cli/installation.mdx b/content/docs/references/cli/installation.mdx new file mode 100644 index 0000000..9730482 --- /dev/null +++ b/content/docs/references/cli/installation.mdx @@ -0,0 +1,100 @@ +--- +title: Installation +description: Install the @wvb/cli package and run the wvb command-line tool. +--- + +The command-line tool ships in the [`@wvb/cli`](/docs/references/api/cli) package. It exposes two +equivalent binaries, `wvb` and `webview-bundle` — use whichever reads better in your scripts. + +## Requirements + +- **Node.js 18.14.1 or later.** Node 19 is supported from 19.7.0; Node 20 and newer work without + restriction. +- The package is ESM-only. It runs from any project regardless of whether your own code is ESM or + CommonJS. + +## Install + +Add `@wvb/cli` as a dev dependency — it is a build-time tool, not a runtime dependency of your app. + + + + +```sh +npm install -D @wvb/cli +``` + + + + +```sh +pnpm add -D @wvb/cli +``` + + + + +```sh +yarn add -D @wvb/cli +``` + + + + +## Run + +Invoke the binary through your package manager so it resolves from `node_modules`: + + + + +```sh +npx wvb --help +``` + + + + +```sh +pnpm wvb --help +``` + + + + +```sh +yarn wvb --help +``` + + + + +Add commands to your `package.json` scripts for repeatable builds: + +```json title="package.json" +{ + "scripts": { + "bundle": "wvb pack", + "bundle:deploy": "wvb upload && wvb deploy" + } +} +``` + + + Prefer a project-local install over a global one. It pins the CLI version alongside your other dev + dependencies, so every machine and CI run uses the same version. + + +## Verify + +Print the installed version to confirm the binary resolves: + +```sh +npx wvb --version +``` + +## Next steps + +- Create a [`wvb.config`](/docs/references/configuration) file so commands run with no arguments. +- Browse every command on the [CLI overview](/docs/references/cli). +- Call the same logic from JavaScript with the [programmatic API](/docs/references/cli/programmatic). diff --git a/content/docs/references/cli/meta.json b/content/docs/references/cli/meta.json new file mode 100644 index 0000000..af98259 --- /dev/null +++ b/content/docs/references/cli/meta.json @@ -0,0 +1,18 @@ +{ + "title": "CLI", + "pages": [ + "index", + "installation", + "programmatic", + "environment", + "---Commands---", + "pack", + "unpack", + "builtin", + "serve", + "remote-upload", + "remote-deploy", + "remote-download", + "remote" + ] +} diff --git a/content/docs/references/cli/meta.ko.json b/content/docs/references/cli/meta.ko.json new file mode 100644 index 0000000..2a1293e --- /dev/null +++ b/content/docs/references/cli/meta.ko.json @@ -0,0 +1,18 @@ +{ + "title": "CLI", + "pages": [ + "index", + "installation", + "programmatic", + "environment", + "---명령어---", + "pack", + "unpack", + "builtin", + "serve", + "remote-upload", + "remote-deploy", + "remote-download", + "remote" + ] +} diff --git a/content/docs/guide/commands/pack.ko.mdx b/content/docs/references/cli/pack.ko.mdx similarity index 74% rename from content/docs/guide/commands/pack.ko.mdx rename to content/docs/references/cli/pack.ko.mdx index abf9d18..8e84d20 100644 --- a/content/docs/guide/commands/pack.ko.mdx +++ b/content/docs/references/cli/pack.ko.mdx @@ -5,9 +5,9 @@ description: 빌드된 웹 자산 디렉터리를 압축되고 무결성이 검 `wvb pack`은 빌드된 웹 자산(HTML, JS, CSS, 미디어)이 담긴 디렉터리를 단일 `.wvb` 아카이브로 묶습니다. 이 아카이브는 압축되고 체크섬이 계산됩니다. 앱은 이 아카이브를 배포하여 웹뷰에 제공하거나, 무선(OTA) 업데이트를 위해 원격 서버에 업로드합니다. -대부분의 프로젝트는 `wvb pack`을 인자 없이 실행합니다. 소스 디렉터리, 출력 경로를 비롯한 기본값은 작업 디렉터리에서 발견된 [`wvb.config`](/docs/config) 파일에서 가져옵니다. +대부분의 프로젝트는 `wvb pack`을 인자 없이 실행합니다. 소스 디렉터리, 출력 경로를 비롯한 기본값은 작업 디렉터리에서 발견된 [`wvb.config`](/docs/references/configuration) 파일에서 가져옵니다. -## Usage +## 사용법 ```sh wvb pack [SRC_DIR] @@ -39,7 +39,7 @@ wvb pack ./dist --header '*.html' 'cache-control' 'max-age=3600' wvb pack ./dist --no-write ``` -## Options +## 옵션 | Option | Aliases | Default | Description | | ------------- | ------------------ | ------------------------- | ------------------------------------------------------------------------------------ | @@ -50,7 +50,7 @@ wvb pack ./dist --no-write | `--no-write` | — | 기본적으로 씀 | 아카이브를 쓰지 않고 패킹을 실행합니다(드라이 런). | | `--overwrite` | — | `true` | 기존 출력 파일을 덮어씁니다. | -이 플래그들은 명령어마다 공통으로 쓰이는 `--config`(`-C`)와 `--cwd` 옵션도 받습니다. 설정 탐색과 전역 플래그의 동작 방식은 [CLI 개요](/docs/guide/cli)를 참고하세요. +이 플래그들은 명령어마다 공통으로 쓰이는 `--config`(`-C`)와 `--cwd` 옵션도 받습니다. 설정 탐색과 전역 플래그의 동작 방식은 [CLI 개요](/docs/references/cli)를 참고하세요. `pack`에는 `--outdir` 플래그가 없습니다. 출력 디렉터리는 `--outfile`로 결정됩니다. 기본값은 `.wvb/`으로 해석되며, 이때 이름은 가장 가까운 `package.json`에서 스코프를 제거한 값입니다. @@ -58,31 +58,6 @@ wvb pack ./dist --no-write 불리언 플래그는 `--flag`, `--flag=true|false`, 그리고 `--no-flag` 형태의 부정을 받습니다. 예를 들어 `--no-write`는 기본 동작인 `--write`를 끄고, `--no-overwrite`는 기존 출력 파일을 그대로 둡니다. -## Configuration - -[`wvb.config`](/docs/config) 파일에 `pack` 기본값을 설정하면 명령어를 인자 없이 깔끔하게 실행할 수 있습니다. 출력 경로에 해당하는 설정 필드는 `outFile`이며(단일 경로이고, `.wvb`가 자동으로 붙습니다), `overwrite`의 기본값은 `true`입니다. 명령줄에서 전달한 플래그는 항상 설정값보다 우선합니다. - -## See also - - - - - - - +## 설정 + +[`wvb.config`](/docs/references/configuration) 파일에 `pack` 기본값을 설정하면 명령어를 인자 없이 깔끔하게 실행할 수 있습니다. 출력 경로에 해당하는 설정 필드는 `outFile`이며(단일 경로이고, `.wvb`가 자동으로 붙습니다), `overwrite`의 기본값은 `true`입니다. 명령줄에서 전달한 플래그는 항상 설정값보다 우선합니다. diff --git a/content/docs/guide/commands/pack.mdx b/content/docs/references/cli/pack.mdx similarity index 73% rename from content/docs/guide/commands/pack.mdx rename to content/docs/references/cli/pack.mdx index 27a6e5c..2d88864 100644 --- a/content/docs/guide/commands/pack.mdx +++ b/content/docs/references/cli/pack.mdx @@ -5,7 +5,7 @@ description: Pack a directory of built web assets into a single compressed, inte `wvb pack` packs a directory of built web assets (HTML, JS, CSS, media) into a single `.wvb` archive. The archive is compressed and checksummed. It is the unit your app ships and serves to its webview, or uploads to a remote for over-the-air (OTA) updates. -Most projects run `wvb pack` with no arguments. The source directory, output path, and other defaults come from the [`wvb.config`](/docs/config) file discovered in the working directory. +Most projects run `wvb pack` with no arguments. The source directory, output path, and other defaults come from the [`wvb.config`](/docs/references/configuration) file discovered in the working directory. ## Usage @@ -50,7 +50,7 @@ wvb pack ./dist --no-write | `--no-write` | — | writes by default | Run the pack without writing the archive (dry run). | | `--overwrite` | — | `true` | Overwrite an existing output file. | -These flags also accept the common per-command `--config` (`-C`) and `--cwd` options. See the [CLI overview](/docs/guide/cli) for how config discovery and global flags work. +These flags also accept the common per-command `--config` (`-C`) and `--cwd` options. See the [CLI overview](/docs/references/cli) for how config discovery and global flags work. `pack` has no `--outdir` flag. The output directory is determined by `--outfile`; the default resolves to `.wvb/`, where the name comes from the nearest `package.json` with its scope stripped. @@ -60,29 +60,4 @@ Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation. ## Configuration -Set `pack` defaults in your [`wvb.config`](/docs/config) file so the command runs cleanly with no arguments. The config field for the output path is `outFile` (a single path; `.wvb` is appended automatically), and `overwrite` defaults to `true`. A flag passed on the command line always wins over the config value. - -## See also - - - - - - - +Set `pack` defaults in your [`wvb.config`](/docs/references/configuration) file so the command runs cleanly with no arguments. The config field for the output path is `outFile` (a single path; `.wvb` is appended automatically), and `overwrite` defaults to `true`. A flag passed on the command line always wins over the config value. diff --git a/content/docs/guide/cli-programmatic.ko.mdx b/content/docs/references/cli/programmatic.ko.mdx similarity index 93% rename from content/docs/guide/cli-programmatic.ko.mdx rename to content/docs/references/cli/programmatic.ko.mdx index ff5157d..cded1e6 100644 --- a/content/docs/guide/cli-programmatic.ko.mdx +++ b/content/docs/references/cli/programmatic.ko.mdx @@ -38,7 +38,7 @@ yarn add -D @wvb/cli 반환값(패킹된 `Bundle`, 실행 중인 서버 핸들)이 필요하거나 모든 작업을 하나의 Node 프로세스 안에서 유지하고 싶다면, `wvb` 바이너리를 실행하는 대신 API를 사용하세요. 명령줄 인터페이스는 [CLI - 레퍼런스](/docs/guide/cli)를 참고하세요. + 레퍼런스](/docs/references/cli)를 참고하세요. ## `/api` 익스포트 @@ -58,7 +58,7 @@ yarn add -D @wvb/cli `/api` 인터페이스는 의도적으로 CLI에 가깝게 설계되었으며, 완전한 클라이언트는 아닙니다. 여기에는 `deploy`나 `download` 함수가 없습니다. 이 명령어들은 CLI에만 존재하며, - [`@wvb/node`](/docs/references/node)의 `Remote` 클래스를 통해 원격과 통신합니다. + [`@wvb/node`](/docs/references/api/node)의 `Remote` 클래스를 통해 원격과 통신합니다. ### pack @@ -150,7 +150,8 @@ await remoteUpload({ 무결성은 SHA-2(기본값 `sha256`)를 사용합니다. 서명을 제공하면 무결성 문자열 바이트에 서명합니다. - 무결성과 서명이 어떻게 함께 동작하는지는 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. + 무결성과 서명이 어떻게 함께 동작하는지는 [원격 번들](/docs/guide/core-concepts/over-the-air)을 + 참고하세요. ### builtin @@ -203,7 +204,7 @@ await instance.shutdown(); ## 기본 엔트리의 설정 헬퍼 -기본 `@wvb/cli` 엔트리(`/api`가 아님)는 설정 도구를 다시 익스포트합니다. [`@wvb/config`](/docs/config)의 +기본 `@wvb/cli` 엔트리(`/api`가 아님)는 설정 도구를 다시 익스포트합니다. [`@wvb/config`](/docs/references/configuration)의 `defineConfig`, 그리고 `loadConfigFile`과 `resolveConfig`, `InlineConfig`와 `ResolvedConfig` 타입을 노출합니다. @@ -260,22 +261,6 @@ wvb.config.jsonc Deno에서는 설정 파일이 항상 ESM으로 취급됩니다. 로더가 Node에서 사용하는 package-type 및 확장자 - 추론을 건너뜁니다. Deno Desktop 지원은 실험적입니다. [Deno 가이드](/docs/guide/platforms/deno)를 + 추론을 건너뜁니다. Deno Desktop 지원은 실험적입니다. [Deno 가이드](/docs/guide/native/deno)를 참고하세요. - -## 관련 문서 - - - - - - diff --git a/content/docs/guide/cli-programmatic.mdx b/content/docs/references/cli/programmatic.mdx similarity index 92% rename from content/docs/guide/cli-programmatic.mdx rename to content/docs/references/cli/programmatic.mdx index 795a8a1..0fd22b1 100644 --- a/content/docs/guide/cli-programmatic.mdx +++ b/content/docs/references/cli/programmatic.mdx @@ -39,7 +39,7 @@ yarn add -D @wvb/cli Prefer the API over spawning the `wvb` binary when you need return values (the packed `Bundle`, a running server handle) or want to keep everything inside one Node process. For the command-line - surface, see the [CLI reference](/docs/guide/cli). + surface, see the [CLI reference](/docs/references/cli). ## The `/api` exports @@ -59,7 +59,7 @@ promise. Failures throw an `ApiError`. The `/api` surface is intentionally CLI-adjacent, not a full client. There are no `deploy` or `download` functions here — those commands live only on the CLI, which talks to a remote through - the `Remote` class from [`@wvb/node`](/docs/references/node). + the `Remote` class from [`@wvb/node`](/docs/references/api/node). ### pack @@ -151,8 +151,8 @@ integrity config to customize), `signature`, `logger`, and `cwd`. Integrity uses SHA-2 (`sha256` by default). The signature, when provided, signs the integrity - string bytes — see [Remote bundles](/docs/guide/remote-bundles) for how integrity and signatures - fit together. + string bytes — see [Remote bundles](/docs/guide/core-concepts/over-the-air) for how integrity and + signatures fit together. ### builtin @@ -208,7 +208,7 @@ and `colorEnabled`. It returns a `LocalRemoteInstance` with the raw `server` and ## Config helpers from the main entry The default `@wvb/cli` entry (not `/api`) re-exports the config tooling. It surfaces `defineConfig` -from [`@wvb/config`](/docs/config), plus `loadConfigFile` and `resolveConfig`, and the +from [`@wvb/config`](/docs/references/configuration), plus `loadConfigFile` and `resolveConfig`, and the `InlineConfig` and `ResolvedConfig` types. ```ts @@ -266,25 +266,5 @@ without a separate build step. Bare and `npm:` specifiers and Node builtins stay Under Deno, config files are always treated as ESM — the loader skips the package-type and extension sniffing it uses on Node. Deno Desktop support is experimental; see the [Deno - guide](/docs/guide/platforms/deno). + guide](/docs/guide/native/deno). - -## Related - - - - - - diff --git a/content/docs/guide/commands/deploy.ko.mdx b/content/docs/references/cli/remote-deploy.ko.mdx similarity index 61% rename from content/docs/guide/commands/deploy.ko.mdx rename to content/docs/references/cli/remote-deploy.ko.mdx index 11effe7..cbf0c95 100644 --- a/content/docs/guide/commands/deploy.ko.mdx +++ b/content/docs/references/cli/remote-deploy.ko.mdx @@ -5,9 +5,9 @@ description: 이미 업로드된 번들 버전을 클라이언트가 받을 현 `wvb deploy`는 이미 원격에 올라와 있는 번들 버전을 현재 버전으로 승격합니다. 그러면 번들을 다운로드하는 클라이언트가 그 버전을 받습니다. 번들을 업로드하면 원격에 저장되지만 현재 버전이 되지는 않습니다. 스위치를 켜는 단계가 바로 deploy입니다. -[`wvb upload`](/docs/guide/commands/upload)을 실행한 뒤에 사용하세요. 또는 `upload`에 `--deploy`를 넘겨 두 단계를 한 명령어로 합칠 수도 있습니다. +[`wvb upload`](/docs/references/cli/remote-upload)을 실행한 뒤에 사용하세요. 또는 `upload`에 `--deploy`를 넘겨 두 단계를 한 명령어로 합칠 수도 있습니다. -## Usage +## 사용법 ```sh wvb deploy app --version 1.2.0 @@ -17,7 +17,7 @@ wvb deploy # 설정 / package.json 기본값 번들 이름은 위치 인자 `BUNDLE`이며, 생략하면 설정이나 `package.json`에서 해석한 이름으로 대체됩니다. 버전은 `--version`(`-V`) 플래그로 지정합니다. 버전을 위한 위치 인자는 없습니다. -## Options +## 옵션 | Option | Aliases | Default | Description | | ----------- | ------- | ----------------------------- | --------------------------------- | @@ -27,33 +27,13 @@ wvb deploy # 설정 / package.json 기본값 | `--config` | `-C` | 설정 자동 탐색 | 설정 파일 경로. | | `--cwd` | — | `process.cwd()` | 경로 해석에 사용할 작업 디렉터리. | -전역 플래그 `--color`, `--log-level`, `--log-verbose`도 함께 적용됩니다. 자세한 내용은 [CLI 개요](/docs/guide/cli)를 참고하세요. +전역 플래그 `--color`, `--log-level`, `--log-verbose`도 함께 적용됩니다. 자세한 내용은 [CLI 개요](/docs/references/cli)를 참고하세요. -## Requirements +## 요구 사항 -`deploy`를 사용하려면 [설정](/docs/config/remote)에 `remote.deployer`가 필요합니다. deployer는 원격에서 어떤 버전이 현재 버전인지 기록하는 구성 요소입니다. 따라서 deployer가 없는 원격에는 배포할 수 없습니다. +`deploy`를 사용하려면 [설정](/docs/references/configuration/remote)에 `remote.deployer`가 필요합니다. deployer는 원격에서 어떤 버전이 현재 버전인지 기록하는 구성 요소입니다. 따라서 deployer가 없는 원격에는 배포할 수 없습니다. 버전은 위치 인자가 아니라 `--version`(`-V`) 플래그로 지정합니다. 생략하면 deploy는 설정이나 가장 가까운 `package.json`에서 해석한 버전을 사용합니다. - -## See also - - - - - - diff --git a/content/docs/guide/commands/deploy.mdx b/content/docs/references/cli/remote-deploy.mdx similarity index 66% rename from content/docs/guide/commands/deploy.mdx rename to content/docs/references/cli/remote-deploy.mdx index 86d6587..8e02c66 100644 --- a/content/docs/guide/commands/deploy.mdx +++ b/content/docs/references/cli/remote-deploy.mdx @@ -5,7 +5,7 @@ description: Mark an already-uploaded bundle version as the current one that cli `wvb deploy` promotes a bundle version that is already on the remote to be the current version. Clients that download the bundle then receive that version. Uploading a bundle stores it on the remote but does not make it current — deploy is the step that flips the switch. -Run it after [`wvb upload`](/docs/guide/commands/upload), or pass `--deploy` to `upload` to combine both steps in one command. +Run it after [`wvb upload`](/docs/references/cli/remote-upload), or pass `--deploy` to `upload` to combine both steps in one command. ## Usage @@ -27,33 +27,13 @@ The bundle name is the positional `BUNDLE` argument and falls back to the resolv | `--config` | `-C` | config auto-discovery | Path to the config file. | | `--cwd` | — | `process.cwd()` | Working directory for resolving paths. | -The global flags `--color`, `--log-level`, and `--log-verbose` also apply. See the [CLI overview](/docs/guide/cli) for details. +The global flags `--color`, `--log-level`, and `--log-verbose` also apply. See the [CLI overview](/docs/references/cli) for details. ## Requirements -`deploy` requires `remote.deployer` in your [config](/docs/config/remote). The deployer is the component that records which version is current on the remote, so you cannot deploy to a remote without one. +`deploy` requires `remote.deployer` in your [config](/docs/references/configuration/remote). The deployer is the component that records which version is current on the remote, so you cannot deploy to a remote without one. The version is the `--version` (`-V`) flag, not a positional argument. If you omit it, deploy uses the version resolved from your config or the nearest `package.json`. - -## See also - - - - - - diff --git a/content/docs/guide/commands/download.ko.mdx b/content/docs/references/cli/remote-download.ko.mdx similarity index 81% rename from content/docs/guide/commands/download.ko.mdx rename to content/docs/references/cli/remote-download.ko.mdx index 0467eab..f476ff5 100644 --- a/content/docs/guide/commands/download.ko.mdx +++ b/content/docs/references/cli/remote-download.ko.mdx @@ -5,7 +5,7 @@ description: 원격 서버에서 번들을 디스크로 다운로드하거나, `wvb download`는 원격 서버에서 번들을 가져오며, 기본적으로 `.wvb` 파일로 디스크에 저장합니다. 번들 이름과 선택적으로 버전을 전달하세요. 버전을 생략하면 현재 배포된 버전을 다운로드합니다. 이 명령어는 `wvb upload`와 `wvb deploy`가 이끄는 배포 워크플로의 읽기 측면에 해당하며, 원격 서버가 실제로 무엇을 제공하는지 확인할 때 유용합니다. -## Usage +## 사용법 ```sh wvb download app --endpoint https://updates.example.com @@ -15,9 +15,9 @@ wvb download app --no-write 첫 번째 예제는 현재 배포된 `app` 버전을 작업 디렉터리의 `app.wvb`로 다운로드합니다. 두 번째 예제는 특정 버전(`1.2.0`)을 명시한 경로로 다운로드하며, 기존 파일이 있으면 덮어씁니다. 세 번째 예제는 번들을 가져와 정보를 출력하되 디스크에는 아무것도 쓰지 않습니다. -번들 이름과 엔드포인트는 모두 [`wvb.config`](/docs/config/remote) 파일 값을 기본값으로 사용하므로, 설정이 갖춰진 프로젝트 안에서는 보통 플래그 없이 `wvb download`만 실행해 현재 버전을 가져올 수 있습니다. +번들 이름과 엔드포인트는 모두 [`wvb.config`](/docs/references/configuration/remote) 파일 값을 기본값으로 사용하므로, 설정이 갖춰진 프로젝트 안에서는 보통 플래그 없이 `wvb download`만 실행해 현재 버전을 가져올 수 있습니다. -## Options +## 옵션 | Option | Aliases | Default | Description | | ------------- | ------- | ------------------- | ------------------------------------------------------------------------------- | @@ -32,7 +32,7 @@ wvb download app --no-write | `--config` | `-C` | auto-discovery | 설정 파일 경로입니다. | | `--cwd` | — | `process.cwd()` | 경로를 확인할 작업 디렉터리입니다. | -불리언 플래그는 `--flag`, `--flag=true|false`, 그리고 `--no-flag` 부정 형태를 받습니다. 세 가지 전역 플래그(`--color`, `--log-level`, `--log-verbose`)도 여기에 적용됩니다. [CLI 개요](/docs/guide/cli)를 참고하세요. +불리언 플래그는 `--flag`, `--flag=true|false`, 그리고 `--no-flag` 부정 형태를 받습니다. 세 가지 전역 플래그(`--color`, `--log-level`, `--log-verbose`)도 여기에 적용됩니다. [CLI 개요](/docs/references/cli)를 참고하세요. 버전 위치 인자를 지정하면 `wvb download`는 해당 버전을 정확히 요청합니다. 지정하지 않으면 해당 @@ -43,25 +43,5 @@ wvb download app --no-write `--no-write`는 번들을 가져와 정보를 출력하되 파일을 저장하지 않습니다. 디스크를 건드리지 않고 원격 서버가 무엇을 제공하는지 확인할 때 사용하세요. 번들 본문을 전혀 다운로드하지 않고 메타데이터만 - 보려면 대신 [`wvb remote current`](/docs/guide/commands/remote)를 사용하세요. + 보려면 대신 [`wvb remote current`](/docs/references/cli/remote)를 사용하세요. - -## See also - - - - - - diff --git a/content/docs/guide/commands/download.mdx b/content/docs/references/cli/remote-download.mdx similarity index 82% rename from content/docs/guide/commands/download.mdx rename to content/docs/references/cli/remote-download.mdx index 2fd3812..b373396 100644 --- a/content/docs/guide/commands/download.mdx +++ b/content/docs/references/cli/remote-download.mdx @@ -15,7 +15,7 @@ wvb download app --no-write The first example downloads the current deployed version of `app` to `app.wvb` in the working directory. The second downloads a specific version (`1.2.0`) to an explicit path, overwriting any existing file. The third fetches the bundle and prints its information without writing anything to disk. -The bundle name and the endpoint both fall back to your [`wvb.config`](/docs/config/remote) file, so inside a configured project you can usually run `wvb download` with no flags to pull the current version. +The bundle name and the endpoint both fall back to your [`wvb.config`](/docs/references/configuration/remote) file, so inside a configured project you can usually run `wvb download` with no flags to pull the current version. ## Options @@ -32,7 +32,7 @@ The bundle name and the endpoint both fall back to your [`wvb.config`](/docs/con | `--config` | `-C` | auto-discovery | Path to the config file. | | `--cwd` | — | `process.cwd()` | Working directory for resolving paths. | -Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation. The three global flags (`--color`, `--log-level`, `--log-verbose`) apply here too; see the [CLI overview](/docs/guide/cli). +Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation. The three global flags (`--color`, `--log-level`, `--log-verbose`) apply here too; see the [CLI overview](/docs/references/cli). With a version positional, `wvb download` requests that exact version; without one, it downloads @@ -43,25 +43,5 @@ Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation. `--no-write` fetches the bundle and prints its information without saving a file. Use it to inspect what a remote serves without touching disk. To see metadata without downloading the bundle - body at all, use [`wvb remote current`](/docs/guide/commands/remote) instead. + body at all, use [`wvb remote current`](/docs/references/cli/remote) instead. - -## See also - - - - - - diff --git a/content/docs/guide/commands/upload.ko.mdx b/content/docs/references/cli/remote-upload.ko.mdx similarity index 83% rename from content/docs/guide/commands/upload.ko.mdx rename to content/docs/references/cli/remote-upload.ko.mdx index e136d9e..964ef8d 100644 --- a/content/docs/guide/commands/upload.ko.mdx +++ b/content/docs/references/cli/remote-upload.ko.mdx @@ -3,7 +3,7 @@ title: wvb upload description: 번들을 패킹하고 해시와 서명을 붙여 설정된 원격 서버에 업로드하며, 선택적으로 배포 단계까지 실행합니다. --- -`wvb upload`는 번들을 원격 서버에 게시합니다. 기본적으로 전체 파이프라인을 순서대로 실행합니다. 먼저 패킹하고, 무결성 해시를 계산하고, 서명한 뒤 업로드합니다. 따라서 명령어 하나로 빌드 산출물이 서명과 무결성 검증을 거친 번들이 되어 원격에 올라갑니다. 업로드 단계에는 [설정](/docs/config/remote)의 `remote.uploader`가 필요합니다. 마지막의 선택적 배포 단계에는 `remote.deployer`도 필요합니다. +`wvb upload`는 번들을 원격 서버에 게시합니다. 기본적으로 전체 파이프라인을 순서대로 실행합니다. 먼저 패킹하고, 무결성 해시를 계산하고, 서명한 뒤 업로드합니다. 따라서 명령어 하나로 빌드 산출물이 서명과 무결성 검증을 거친 번들이 되어 원격에 올라갑니다. 업로드 단계에는 [설정](/docs/references/configuration/remote)의 `remote.uploader`가 필요합니다. 마지막의 선택적 배포 단계에는 `remote.deployer`도 필요합니다. ```sh wvb upload # 설정 기본값을 사용합니다 @@ -49,27 +49,7 @@ wvb upload --no-pack --file ./build/app.wvb --force ## 요구 사항 -- 업로드 단계에는 `remote.uploader`가 설정되어 있어야 합니다. [원격, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. -- `--deploy`를 전달하면 `remote.deployer`도 추가로 필요합니다. 버전을 나중에 별도로 배포하려면 [`wvb deploy`](/docs/guide/commands/deploy)를 사용하세요. +- 업로드 단계에는 `remote.uploader`가 설정되어 있어야 합니다. [원격, 무결성 및 서명 설정](/docs/references/configuration/remote)을 참고하세요. +- `--deploy`를 전달하면 `remote.deployer`도 추가로 필요합니다. 버전을 나중에 별도로 배포하려면 [`wvb deploy`](/docs/references/cli/remote-deploy)를 사용하세요. 게시 모델과 로컬 테스트 과정은 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. - -## 함께 보기 - - - - - - diff --git a/content/docs/guide/commands/upload.mdx b/content/docs/references/cli/remote-upload.mdx similarity index 84% rename from content/docs/guide/commands/upload.mdx rename to content/docs/references/cli/remote-upload.mdx index 4e4ab0f..ba8d1ea 100644 --- a/content/docs/guide/commands/upload.mdx +++ b/content/docs/references/cli/remote-upload.mdx @@ -3,7 +3,7 @@ title: wvb upload description: Pack, hash, sign, and upload a bundle to the configured remote, with an optional deploy step. --- -`wvb upload` publishes a bundle to your remote server. By default it runs the full pipeline in order — pack, then integrity, then signature, then upload — so a single command turns your built assets into a signed, integrity-checked bundle on the remote. The upload step requires `remote.uploader` in your [config](/docs/config/remote); the optional deploy step at the end also requires `remote.deployer`. +`wvb upload` publishes a bundle to your remote server. By default it runs the full pipeline in order — pack, then integrity, then signature, then upload — so a single command turns your built assets into a signed, integrity-checked bundle on the remote. The upload step requires `remote.uploader` in your [config](/docs/references/configuration/remote); the optional deploy step at the end also requires `remote.deployer`. ```sh wvb upload # uses config defaults @@ -48,27 +48,7 @@ On success the command prints the bundle endpoint. ## Requirements -- `remote.uploader` must be configured for the upload step. See [Remote, integrity & signature config](/docs/config/remote). -- `remote.deployer` is additionally required when you pass `--deploy`. To deploy a version separately later, use [`wvb deploy`](/docs/guide/commands/deploy). +- `remote.uploader` must be configured for the upload step. See [Remote, integrity & signature config](/docs/references/configuration/remote). +- `remote.deployer` is additionally required when you pass `--deploy`. To deploy a version separately later, use [`wvb deploy`](/docs/references/cli/remote-deploy). For the publishing model and a local-testing walkthrough, see [Building a remote](/docs/guide/remote). - -## See also - - - - - - diff --git a/content/docs/guide/commands/remote.ko.mdx b/content/docs/references/cli/remote.ko.mdx similarity index 79% rename from content/docs/guide/commands/remote.ko.mdx rename to content/docs/references/cli/remote.ko.mdx index ec22842..cf18128 100644 --- a/content/docs/guide/commands/remote.ko.mdx +++ b/content/docs/references/cli/remote.ko.mdx @@ -11,7 +11,7 @@ description: 원격을 검사하고 테스트합니다. 현재 배포된 번들 - [`wvb remote list`](#wvb-remote-list) — 원격에서 사용 가능한 모든 번들을 나열합니다(별칭 `wvb remote ls`). - [`wvb remote local`](#wvb-remote-local) — 디렉터리를 기반으로 로컬 업데이트 서버를 시작합니다. -앞의 두 명령어는 기본적으로 [설정](/docs/config/remote)에서 `remote.endpoint`를 읽습니다. 따라서 원격을 한 번 설정해 두면 `--endpoint` 플래그를 생략할 수 있습니다. 배포 모델과 로컬 테스트 과정은 [원격 구축하기](/docs/guide/remote)를 참고하세요. +앞의 두 명령어는 기본적으로 [설정](/docs/references/configuration/remote)에서 `remote.endpoint`를 읽습니다. 따라서 원격을 한 번 설정해 두면 `--endpoint` 플래그를 생략할 수 있습니다. 배포 모델과 로컬 테스트 과정은 [원격 구축하기](/docs/guide/remote)를 참고하세요. ## wvb remote current [BUNDLE] @@ -50,7 +50,7 @@ wvb remote list | jq '.[].name' ## wvb remote local -디렉터리를 기반으로 로컬 업데이트 서버를 시작합니다. 프로덕션 원격과 동일한 HTTP 규약을 구현하므로, 호스팅된 [프로바이더](/docs/guide/providers/local)를 연결하기 전에 전체 업데이트 루프를 오프라인에서 테스트할 수 있습니다. 서버는 기본적으로 `~/.wvb/local`을 `4313` 포트로 제공합니다. +디렉터리를 기반으로 로컬 업데이트 서버를 시작합니다. 프로덕션 원격과 동일한 HTTP 규약을 구현하므로, 호스팅된 [프로바이더](/docs/guide/remote/providers/local)를 연결하기 전에 전체 업데이트 루프를 오프라인에서 테스트할 수 있습니다. 서버는 기본적으로 `~/.wvb/local`을 `4313` 포트로 제공합니다. ```sh wvb remote local # http://localhost:4313, ~/.wvb/local 제공 @@ -73,28 +73,3 @@ wvb remote local --allow-other-versions --hostname 0.0.0.0 로컬 서버는 기본적으로 각 번들에서 현재 버전으로 표시된 버전만 제공합니다. `--allow-other-versions`를 전달하면 특정 이전 버전도 함께 제공합니다. 이는 롤백과 버전 고정을 테스트할 때 유용합니다. - -## 참고 - - - - - - - diff --git a/content/docs/guide/commands/remote.mdx b/content/docs/references/cli/remote.mdx similarity index 82% rename from content/docs/guide/commands/remote.mdx rename to content/docs/references/cli/remote.mdx index 1f60f10..4b04330 100644 --- a/content/docs/guide/commands/remote.mdx +++ b/content/docs/references/cli/remote.mdx @@ -11,7 +11,7 @@ It groups three subcommands: - [`wvb remote list`](#wvb-remote-list) — list every bundle available on the remote (alias `wvb remote ls`). - [`wvb remote local`](#wvb-remote-local) — start a local update server backed by a directory. -The first two read `remote.endpoint` from your [config](/docs/config/remote) by default, so you can drop the `--endpoint` flag once a remote is configured. For the publishing model and a local-testing walkthrough, see [Building a remote](/docs/guide/remote). +The first two read `remote.endpoint` from your [config](/docs/references/configuration/remote) by default, so you can drop the `--endpoint` flag once a remote is configured. For the publishing model and a local-testing walkthrough, see [Building a remote](/docs/guide/remote). ## wvb remote current [BUNDLE] @@ -50,7 +50,7 @@ wvb remote list | jq '.[].name' ## wvb remote local -Start a local update server backed by a directory. It implements the same HTTP contract as a production remote, so you can test the full update loop offline before wiring in a hosted [provider](/docs/guide/providers/local). The server defaults to serving `~/.wvb/local` on port `4313`. +Start a local update server backed by a directory. It implements the same HTTP contract as a production remote, so you can test the full update loop offline before wiring in a hosted [provider](/docs/guide/remote/providers/local). The server defaults to serving `~/.wvb/local` on port `4313`. ```sh wvb remote local # http://localhost:4313, serving ~/.wvb/local @@ -73,28 +73,3 @@ wvb remote local --allow-other-versions --hostname 0.0.0.0 By default the local server only serves the version marked current for each bundle. Pass `--allow-other-versions` to also serve specific older versions, which is useful for testing rollbacks and version pinning. - -## See also - - - - - - - diff --git a/content/docs/guide/commands/serve.ko.mdx b/content/docs/references/cli/serve.ko.mdx similarity index 73% rename from content/docs/guide/commands/serve.ko.mdx rename to content/docs/references/cli/serve.ko.mdx index c360315..f1a2b77 100644 --- a/content/docs/guide/commands/serve.ko.mdx +++ b/content/docs/references/cli/serve.ko.mdx @@ -9,12 +9,12 @@ description: 단일 .wvb 번들의 파일을 HTTP로 제공하여 패킹한 번 `wvb serve`는 단일 번들의 내용을 미리 보여줍니다. HTTP 원격을 상대로 전체 무선(OTA) 업데이트 - 루프를 테스트하려면, [`wvb remote local`](/docs/guide/commands/remote)로 로컬 업데이트 서버를 + 루프를 테스트하려면, [`wvb remote local`](/docs/references/cli/remote)로 로컬 업데이트 서버를 실행하세요. 전체 과정을 처음부터 끝까지 따라가려면 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. -## Usage +## 사용법 ```sh wvb serve # 설정에서 해석한 번들을 제공 @@ -23,9 +23,9 @@ wvb serve ./build/app.wvb --port 8080 --hostname 0.0.0.0 wvb serve ./build/app.wvb --silent # 요청 로깅 비활성화 ``` -`FILE`을 생략하면 `wvb serve`는 [설정](/docs/config)의 `serve.file`을 사용하고, 그다음에는 해석된 패킹 출력 경로를 사용합니다. `wvb pack`을 실행한 일반적인 프로젝트라면 `wvb serve`만으로 미리 볼 수 있습니다. +`FILE`을 생략하면 `wvb serve`는 [설정](/docs/references/configuration)의 `serve.file`을 사용하고, 그다음에는 해석된 패킹 출력 경로를 사용합니다. `wvb pack`을 실행한 일반적인 프로젝트라면 `wvb serve`만으로 미리 볼 수 있습니다. -## Options +## 옵션 | Option | Aliases | Default | Description | | ------------ | ------- | --------------- | --------------------------------------------------------------------------------- | @@ -36,30 +36,10 @@ wvb serve ./build/app.wvb --silent # 요청 로깅 비활성화 | `--config` | `-C` | — | 설정 파일 경로입니다. | | `--cwd` | — | `process.cwd()` | 경로 해석에 사용할 작업 디렉터리입니다. | -불리언 플래그는 `--silent`, `--silent=true|false`, 그리고 부정형 `--no-silent`을 받습니다. 전역 플래그인 `--color`, `--log-level`, `--log-verbose`도 여기에 적용됩니다. [CLI 개요](/docs/guide/cli)를 참고하세요. +불리언 플래그는 `--silent`, `--silent=true|false`, 그리고 부정형 `--no-silent`을 받습니다. 전역 플래그인 `--color`, `--log-level`, `--log-verbose`도 여기에 적용됩니다. [CLI 개요](/docs/references/cli)를 참고하세요. -## Notes +## 참고 - 서버는 디렉터리 요청을 `index.html`로 해석합니다. 따라서 디렉터리에 매핑되는 클라이언트 측 라우트는 번들의 엔트리 문서를 로드합니다. - `--hostname 0.0.0.0`은 모든 인터페이스에 바인딩합니다. 네트워크상의 다른 기기에서 번들을 미리 볼 때 유용합니다. -- JavaScript에서 동일한 로직을 호출하려면 [프로그래매틱 API](/docs/guide/cli-programmatic)의 `serve` 함수를 사용하세요. - -## See also - - - - - - +- JavaScript에서 동일한 로직을 호출하려면 [프로그래매틱 API](/docs/references/cli/programmatic)의 `serve` 함수를 사용하세요. diff --git a/content/docs/guide/commands/serve.mdx b/content/docs/references/cli/serve.mdx similarity index 77% rename from content/docs/guide/commands/serve.mdx rename to content/docs/references/cli/serve.mdx index 689687e..de24618 100644 --- a/content/docs/guide/commands/serve.mdx +++ b/content/docs/references/cli/serve.mdx @@ -10,7 +10,7 @@ By default the server listens on `http://localhost:4312`. It handles `SIGINT` an `wvb serve` previews the contents of a single bundle. To test the full over-the-air (OTA) update loop against an HTTP remote, run a local update server with [`wvb remote - local`](/docs/guide/commands/remote) instead. See [Building a remote](/docs/guide/remote) for the + local`](/docs/references/cli/remote) instead. See [Building a remote](/docs/guide/remote) for the end-to-end walkthrough. @@ -23,7 +23,7 @@ wvb serve ./build/app.wvb --port 8080 --hostname 0.0.0.0 wvb serve ./build/app.wvb --silent # disable request logging ``` -If you omit `FILE`, `wvb serve` falls back to `serve.file` in your [config](/docs/config), and then to the resolved pack output path. A typical project that has run `wvb pack` can preview with a bare `wvb serve`. +If you omit `FILE`, `wvb serve` falls back to `serve.file` in your [config](/docs/references/configuration), and then to the resolved pack output path. A typical project that has run `wvb pack` can preview with a bare `wvb serve`. ## Options @@ -36,30 +36,10 @@ If you omit `FILE`, `wvb serve` falls back to `serve.file` in your [config](/doc | `--config` | `-C` | — | Path to the config file. | | `--cwd` | — | `process.cwd()` | Working directory for resolving paths. | -Boolean flags accept `--silent`, `--silent=true|false`, and the `--no-silent` negation. The global `--color`, `--log-level`, and `--log-verbose` flags apply here as well; see the [CLI overview](/docs/guide/cli). +Boolean flags accept `--silent`, `--silent=true|false`, and the `--no-silent` negation. The global `--color`, `--log-level`, and `--log-verbose` flags apply here as well; see the [CLI overview](/docs/references/cli). ## Notes - The server resolves directory requests to `index.html`, so client-side routes that map to a directory load the bundle's entry document. - `--hostname 0.0.0.0` binds all interfaces, which is useful for previewing the bundle from another device on your network. -- To call the same logic from JavaScript, use the `serve` function in the [programmatic API](/docs/guide/cli-programmatic). - -## See also - - - - - - +- To call the same logic from JavaScript, use the `serve` function in the [programmatic API](/docs/references/cli/programmatic). diff --git a/content/docs/guide/commands/extract.ko.mdx b/content/docs/references/cli/unpack.ko.mdx similarity index 82% rename from content/docs/guide/commands/extract.ko.mdx rename to content/docs/references/cli/unpack.ko.mdx index 427c6af..08307cf 100644 --- a/content/docs/guide/commands/extract.ko.mdx +++ b/content/docs/references/cli/unpack.ko.mdx @@ -5,7 +5,7 @@ description: .wvb 아카이브의 파일을 디스크로 다시 풀어 번들에 `wvb extract`는 `.wvb` 아카이브를 읽어 그 안의 파일을 디스크로 다시 씁니다. 번들이 무엇을 담고 있는지 확인하거나, 두 번들을 비교하거나, 번들에 들어간 자산을 복원할 때 사용하세요. -## Usage +## 사용법 추출할 번들 파일을 전달하세요. 이 명령어는 풀어낸 파일을 출력 디렉터리에 쓰며, 기본값은 `.wvb/`입니다. @@ -21,7 +21,7 @@ wvb extract ./build/app.wvb --outdir ./unpacked --clean wvb extract ./build/app.wvb --no-write ``` -## Options +## 옵션 | Option | Aliases | Default | Description | | ---------- | ------- | ------------------------------ | ----------------------------------------------------------------- | @@ -37,23 +37,3 @@ wvb extract ./build/app.wvb --no-write 전역 플래그인 `--color`, `--log-level`, `--log-verbose`는 다른 모든 명령어에서와 마찬가지로 여기서도 적용됩니다. 불리언 플래그는 `--flag`, `--flag=true|false`, 그리고 `--no-flag` 부정 형태를 받습니다. 따라서 `--no-write`는 기본 동작인 `--write`를 끕니다. - -## See also - - - - - - diff --git a/content/docs/guide/commands/extract.mdx b/content/docs/references/cli/unpack.mdx similarity index 82% rename from content/docs/guide/commands/extract.mdx rename to content/docs/references/cli/unpack.mdx index e89ac6d..0ed593c 100644 --- a/content/docs/guide/commands/extract.mdx +++ b/content/docs/references/cli/unpack.mdx @@ -37,23 +37,3 @@ wvb extract ./build/app.wvb --no-write The global `--color`, `--log-level`, and `--log-verbose` flags apply here as they do on every command. Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation, so `--no-write` turns off the default `--write` behavior. - -## See also - - - - - - diff --git a/content/docs/references/configuration/builtin.ko.mdx b/content/docs/references/configuration/builtin.ko.mdx new file mode 100644 index 0000000..48afd94 --- /dev/null +++ b/content/docs/references/configuration/builtin.ko.mdx @@ -0,0 +1,83 @@ +--- +title: builtin +description: 'wvb.config의 builtin 섹션 — 원격이나 로컬 워크스페이스에서 앱 안에 함께 배포할 번들을 설치합니다.' +--- + +`builtin`(`BuiltinConfig`)은 [`wvb builtin`](/docs/references/cli/builtin)의 기본값을 설정합니다. +`wvb builtin`은 앱 안에 함께 배포되는 번들을 설치합니다. 번들은 원격에서 다운로드하거나 로컬 +워크스페이스에서 수집합니다. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + builtin: { + outDir: '.wvb/builtin/bundles', + target: { type: 'remote' }, + include: ['app*'], + exclude: [/^internal-/], + clean: true, + }, +}); +``` + +| Field | Type | Default | +| --------- | -------------------------------------------------------------------- | ---------------------- | +| `outDir` | `string` | `.wvb/builtin/bundles` | +| `target` | `BuiltinTarget` | `{ type: 'remote' }` | +| `include` | `string \| RegExp \| Array \| ((info) => boolean)` | — | +| `exclude` | `string \| RegExp \| Array \| ((info) => boolean)` | — | +| `clean` | `boolean` | `true` | + +`include`와 `exclude`는 후보 번들을 필터링합니다. 각 필드는 글로브 문자열, 정규식, 둘 중 하나의 +배열, 또는 프레디킷 `(info: { name: string; version: string }) => boolean`(선택적으로 비동기)을 +받습니다. `clean`을 활성화하면 설치 전에 출력 디렉터리를 비웁니다. + +## target + +`target`은 `type`을 기준으로 하는 판별 유니온입니다. 서버에서 번들을 다운로드하려면 `remote`를, +저장소의 워크스페이스에서 번들을 수집하려면 `local`을 선택하세요. + +```ts +// remote 타겟 — 서버에서 다운로드 +target: { + type: 'remote', + endpoint: 'https://updates.example.com', + download: { + concurrency: 4, + // http: { /* @wvb/node의 HttpOptions */ }, + }, +} + +// local 타겟 — 워크스페이스에서 수집 +target: { + type: 'local', + workspaces: ['packages/*'], + bundleName: { from: 'package.json' }, + version: { from: 'package.json' }, + packBeforeInstall: true, +} +``` + +`remote` 타겟에서 `endpoint`와 `download`는 선택 사항입니다. 유일한 동시성 조절 값은 +`download.concurrency`입니다. `download.http`는 [`@wvb/node`](/docs/references/api/node)의 +`HttpOptions` 타입을 받습니다. + +| `remote` field | Type | Default | +| ---------------------- | ------------- | ------- | +| `endpoint` | `string` | — | +| `download.concurrency` | `number` | — | +| `download.http` | `HttpOptions` | — | + +`local` 타겟에서 `workspaces`는 필수입니다. 이는 설정 전체에서 유일하게 필수인 필드입니다. +`integrity`와 `signature` 옵션은 remote 섹션과 동일한 형태를 공유합니다. +[원격, 무결성 및 서명](/docs/references/configuration/remote)을 참고하세요. + +| `local` field | Type | Default | +| ------------------- | --------------------------------------------------- | -------- | +| `workspaces` | `string[] \| (() => string[] \| Promise)` | required | +| `bundleName` | `BundleNameResolver` | — | +| `version` | `VersionResolver` | — | +| `integrity` | `boolean \| IntegrityMakeConfig` | — | +| `signature` | `SignatureSignConfig` | — | +| `packBeforeInstall` | `boolean` | `true` | diff --git a/content/docs/references/configuration/builtin.mdx b/content/docs/references/configuration/builtin.mdx new file mode 100644 index 0000000..1b71cde --- /dev/null +++ b/content/docs/references/configuration/builtin.mdx @@ -0,0 +1,84 @@ +--- +title: builtin +description: The builtin section of wvb.config — install bundles that ship inside your app from a remote or local workspaces. +--- + +`builtin` (`BuiltinConfig`) sets the defaults for [`wvb builtin`](/docs/references/cli/builtin), +which installs bundles that ship inside your app — either downloaded from a remote or collected from +local workspaces. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + builtin: { + outDir: '.wvb/builtin/bundles', + target: { type: 'remote' }, + include: ['app*'], + exclude: [/^internal-/], + clean: true, + }, +}); +``` + +| Field | Type | Default | +| --------- | -------------------------------------------------------------------- | ---------------------- | +| `outDir` | `string` | `.wvb/builtin/bundles` | +| `target` | `BuiltinTarget` | `{ type: 'remote' }` | +| `include` | `string \| RegExp \| Array \| ((info) => boolean)` | — | +| `exclude` | `string \| RegExp \| Array \| ((info) => boolean)` | — | +| `clean` | `boolean` | `true` | + +`include` and `exclude` filter the candidate bundles. Each accepts a glob string, a regular +expression, an array of either, or a predicate +`(info: { name: string; version: string }) => boolean` (optionally async). With `clean` enabled, the +output directory is cleared before install. + +## target + +`target` is a discriminated union on `type`. Choose `remote` to download bundles from a server, or +`local` to collect them from workspaces in your repository. + +```ts +// Remote target — download from a server +target: { + type: 'remote', + endpoint: 'https://updates.example.com', + download: { + concurrency: 4, + // http: { /* HttpOptions from @wvb/node */ }, + }, +} + +// Local target — collect from workspaces +target: { + type: 'local', + workspaces: ['packages/*'], + bundleName: { from: 'package.json' }, + version: { from: 'package.json' }, + packBeforeInstall: true, +} +``` + +For the `remote` target, `endpoint` and `download` are optional. The only concurrency knob is +`download.concurrency`; `download.http` accepts the `HttpOptions` type from +[`@wvb/node`](/docs/references/api/node). + +| `remote` field | Type | Default | +| ---------------------- | ------------- | ------- | +| `endpoint` | `string` | — | +| `download.concurrency` | `number` | — | +| `download.http` | `HttpOptions` | — | + +For the `local` target, `workspaces` is required — it is the only required field anywhere in the +config. The `integrity` and `signature` options share the same shapes as the remote section; see +[Remote, integrity & signature](/docs/references/configuration/remote). + +| `local` field | Type | Default | +| ------------------- | --------------------------------------------------- | -------- | +| `workspaces` | `string[] \| (() => string[] \| Promise)` | required | +| `bundleName` | `BundleNameResolver` | — | +| `version` | `VersionResolver` | — | +| `integrity` | `boolean \| IntegrityMakeConfig` | — | +| `signature` | `SignatureSignConfig` | — | +| `packBeforeInstall` | `boolean` | `true` | diff --git a/content/docs/references/configuration/index.ko.mdx b/content/docs/references/configuration/index.ko.mdx new file mode 100644 index 0000000..900099b --- /dev/null +++ b/content/docs/references/configuration/index.ko.mdx @@ -0,0 +1,121 @@ +--- +title: 설정 +description: CLI와 프로그래밍 API가 pack, remote, serve, builtin 옵션을 읽어 오는 wvb.config 파일입니다. +--- + +단 하나의 `wvb.config` 파일이 모든 Webview Bundle 워크플로를 이끕니다. `wvb` CLI와 +프로그래밍 API(`@wvb/cli/api`)가 모두 이 파일을 불러옵니다. 따라서 설정 하나로 +패킹, 제공, 원격 배포, 빌트인 설치를 일관되게 유지합니다. `@wvb/config`의 +`defineConfig`로 작성하면 완전한 타입 체크와 에디터 자동 완성을 얻습니다. + +각 최상위 섹션에는 전용 페이지가 있습니다. + + + + + + + + +## 설정 파일 + +설정 파일을 프로젝트 루트에 두세요. CLI는 작업 디렉터리에서 다음 순서로 처음 일치하는 +파일을 자동으로 찾습니다. + +```text +wvb.config.js +wvb.config.cjs +wvb.config.mjs +wvb.config.ts +wvb.config.cts +wvb.config.mts +webview-bundle.config.js +webview-bundle.config.cjs +webview-bundle.config.mjs +webview-bundle.config.ts +webview-bundle.config.cts +webview-bundle.config.mts +wvb.config.json +wvb.config.jsonc +``` + +`wvb.config.*`와 `webview-bundle.config.*` 두 기본 이름 모두 `.js`, `.cjs`, `.mjs`, +`.ts`, `.cts`, `.mts` 확장자를 지원합니다. `.json`과 `.jsonc` 형식은 `wvb.config` +기본 이름에서만 인식됩니다. 자동 탐색에 의존하는 대신 특정 파일을 지정하려면 어떤 +명령어에든 `--config`(별칭 `-C`)를 전달하세요. + +```sh +wvb pack --config ./configs/wvb.prod.ts +``` + +### defineConfig + +설정을 `@wvb/config`의 `defineConfig`로 감싸세요. 이 함수는 런타임에서는 항등 함수이며, +타입을 붙이는 것이 유일한 역할입니다. 객체, 설정으로 이행되는 프로미스, 또는 설정을 +반환하는 동기 혹은 비동기 함수를 받습니다. 따라서 불러오는 시점에 값을 계산할 수 있습니다. + + + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + root: process.cwd(), + pack: { + srcDir: './dist', + }, + serve: { + port: 4312, + }, +}); +``` + + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig(async () => { + const endpoint = process.env.WVB_ENDPOINT; + return { + remote: { + endpoint, + }, + }; +}); +``` + + + +이 패키지를 개발 의존성으로 설치하세요: `npm install -D @wvb/config`. + +## 최상위 필드 + +설정 객체는 다섯 개의 선택 필드를 가집니다. 최상위에서는 그 외에 아무것도 읽지 않습니다. + +| Field | Type | Default | +| --------- | --------------------------------------------------------- | --------------- | +| `root` | `string` | `process.cwd()` | +| `pack` | [`PackConfig`](/docs/references/configuration/pack) | — | +| `remote` | [`RemoteConfig`](/docs/references/configuration/remote) | — | +| `serve` | [`ServeConfig`](/docs/references/configuration/serve) | — | +| `builtin` | [`BuiltinConfig`](/docs/references/configuration/builtin) | — | + +`root`는 상대 경로를 해석하는 데 사용하는 프로젝트 루트를 설정합니다. 절대 경로일 수도 +있고 설정 파일 기준 상대 경로일 수도 있습니다. 각 섹션 필드는 위에 링크된 전용 페이지에서 +설명합니다. diff --git a/content/docs/references/configuration/index.mdx b/content/docs/references/configuration/index.mdx new file mode 100644 index 0000000..0e8c648 --- /dev/null +++ b/content/docs/references/configuration/index.mdx @@ -0,0 +1,124 @@ +--- +title: Configuration +description: The wvb.config file that the CLI and the programmatic API read for pack, remote, serve, and builtin options. +--- + +A single `wvb.config` file drives every Webview Bundle workflow. Both the `wvb` CLI +and the programmatic API (`@wvb/cli/api`) load it, so one config keeps packing, +serving, remote publishing, and builtin installs consistent. Author it with +`defineConfig` from `@wvb/config` to get full type-checking and editor autocompletion. + +Each top-level section has its own page: + + + + + + + + +## The config file + +Place the config in your project root. The CLI auto-discovers the first matching +file in the working directory, in this order: + +```text +wvb.config.js +wvb.config.cjs +wvb.config.mjs +wvb.config.ts +wvb.config.cts +wvb.config.mts +webview-bundle.config.js +webview-bundle.config.cjs +webview-bundle.config.mjs +webview-bundle.config.ts +webview-bundle.config.cts +webview-bundle.config.mts +wvb.config.json +wvb.config.jsonc +``` + +Both base names `wvb.config.*` and `webview-bundle.config.*` are supported with the +`.js`, `.cjs`, `.mjs`, `.ts`, `.cts`, and `.mts` extensions. The `.json` and `.jsonc` +forms are only recognized for the `wvb.config` base name. Pass `--config` (alias +`-C`) to any command to point at a specific file instead of relying on discovery. + +```sh +wvb pack --config ./configs/wvb.prod.ts +``` + +### defineConfig + +Wrap your config with `defineConfig` from `@wvb/config`. It is an identity function +at runtime — its only job is to attach types. It accepts an object, a promise that +resolves to a config, or a synchronous or asynchronous function that returns one, so +you can compute values at load time. + + + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + root: process.cwd(), + pack: { + srcDir: './dist', + }, + serve: { + port: 4312, + }, +}); +``` + + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig(async () => { + const endpoint = process.env.WVB_ENDPOINT; + return { + remote: { + endpoint, + }, + }; +}); +``` + + + + + Install the package as a dev dependency: `npm install -D @wvb/config`. + + +## Top-level fields + +The config object has five optional fields. Nothing else is read at the top level. + +| Field | Type | Default | +| --------- | --------------------------------------------------------- | --------------- | +| `root` | `string` | `process.cwd()` | +| `pack` | [`PackConfig`](/docs/references/configuration/pack) | — | +| `remote` | [`RemoteConfig`](/docs/references/configuration/remote) | — | +| `serve` | [`ServeConfig`](/docs/references/configuration/serve) | — | +| `builtin` | [`BuiltinConfig`](/docs/references/configuration/builtin) | — | + +`root` sets the project root used to resolve relative paths. It may be absolute or +relative to the config file. Each section field is documented on its own page linked +above. diff --git a/content/docs/references/configuration/meta.json b/content/docs/references/configuration/meta.json new file mode 100644 index 0000000..8990ef8 --- /dev/null +++ b/content/docs/references/configuration/meta.json @@ -0,0 +1 @@ +{ "title": "Configuration", "pages": ["pack", "remote", "serve", "builtin"] } diff --git a/content/docs/references/configuration/meta.ko.json b/content/docs/references/configuration/meta.ko.json new file mode 100644 index 0000000..742ac8b --- /dev/null +++ b/content/docs/references/configuration/meta.ko.json @@ -0,0 +1 @@ +{ "title": "설정", "pages": ["pack", "remote", "serve", "builtin"] } diff --git a/content/docs/references/configuration/pack.ko.mdx b/content/docs/references/configuration/pack.ko.mdx new file mode 100644 index 0000000..e130d10 --- /dev/null +++ b/content/docs/references/configuration/pack.ko.mdx @@ -0,0 +1,60 @@ +--- +title: pack +description: 'wvb.config의 pack 섹션 — 소스 디렉터리, 출력 경로, ignore 규칙, 파일별 헤더입니다.' +--- + +`pack`(`PackConfig`)은 [`wvb pack`](/docs/references/cli/pack)의 기본값을 설정합니다. CLI +플래그는 호출할 때마다 이 값을 재정의합니다. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + pack: { + srcDir: './dist', + outFile: '.wvb/app', + overwrite: true, + ignore: ['*.map', /\.DS_Store$/], + headers: { + '*.html': { 'cache-control': 'max-age=0' }, + '*.js': { 'cache-control': 'max-age=31536000' }, + }, + }, +}); +``` + +| Field | Type | Default | +| ----------- | ------------------------------------------------------------------------------------ | ------------- | +| `srcDir` | `string` | `./dist` | +| `outFile` | `string` | `.wvb/` | +| `overwrite` | `boolean` | `true` | +| `ignore` | `Array \| ((file: string) => boolean \| Promise)` | — | +| `headers` | `Record \| Array<[glob, HeadersInit]> \| ((file) => HeadersInit)` | — | + +`outFile`은 단일 출력 경로입니다. 확장자를 생략하면 `.wvb` 확장자가 자동으로 붙습니다. 경로가 +절대 경로가 아니면 `root` 기준 상대 경로로 해석됩니다. 기본값 `.wvb/`은 `package.json`의 +name에서 스코프 접두사를 제거하여 ``을 도출합니다. + + + 이 필드는 완전한 경로인 `outFile`입니다. `pack`에는 `outFileName`이나 `outDir`가 없습니다. 하위 + 디렉터리를 포함한 전체 출력 경로를 `outFile`에 작성하세요. + + +`ignore`는 글로브와 정규식의 배열, 또는 불리언을 반환하는 프레디킷(선택적으로 비동기)을 +받습니다. `headers`는 일치하는 파일에 HTTP 헤더를 붙이며 세 가지 형태를 받습니다. + +```ts +// 1. 글로브를 키로 갖는 Record +headers: { + '*.html': { 'cache-control': 'max-age=3600' }, +} + +// 2. [glob, HeadersInit] 튜플의 배열 +headers: [ + ['*.html', { 'cache-control': 'max-age=3600' }], + ['*.png', [['cache-control', 'max-age=0']]], // HeadersInit는 [name, value] 쌍도 받습니다 +] + +// 3. 파일별로 헤더를 반환하는 함수 +headers: (file) => (file.endsWith('.html') ? { 'cache-control': 'max-age=0' } : undefined) +``` diff --git a/content/docs/references/configuration/pack.mdx b/content/docs/references/configuration/pack.mdx new file mode 100644 index 0000000..8b3e167 --- /dev/null +++ b/content/docs/references/configuration/pack.mdx @@ -0,0 +1,60 @@ +--- +title: pack +description: The pack section of wvb.config — source directory, output path, ignore rules, and per-file headers. +--- + +`pack` (`PackConfig`) sets the defaults for [`wvb pack`](/docs/references/cli/pack). CLI flags +override these per invocation. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + pack: { + srcDir: './dist', + outFile: '.wvb/app', + overwrite: true, + ignore: ['*.map', /\.DS_Store$/], + headers: { + '*.html': { 'cache-control': 'max-age=0' }, + '*.js': { 'cache-control': 'max-age=31536000' }, + }, + }, +}); +``` + +| Field | Type | Default | +| ----------- | ------------------------------------------------------------------------------------ | ------------- | +| `srcDir` | `string` | `./dist` | +| `outFile` | `string` | `.wvb/` | +| `overwrite` | `boolean` | `true` | +| `ignore` | `Array \| ((file: string) => boolean \| Promise)` | — | +| `headers` | `Record \| Array<[glob, HeadersInit]> \| ((file) => HeadersInit)` | — | + +`outFile` is a single output path. The `.wvb` extension is appended automatically when you omit it, +and the path resolves relative to `root` unless it is absolute. The default `.wvb/` derives +`` from your `package.json` name with any scope prefix stripped. + + + The field is `outFile`, a complete path. There is no `outFileName` or `outDir` on `pack` — write + the full output path, including any subdirectory, in `outFile`. + + +`ignore` accepts an array of globs and regular expressions, or a predicate that returns a boolean +(optionally async). `headers` attaches HTTP headers to matching files and accepts three shapes: + +```ts +// 1. Record keyed by glob +headers: { + '*.html': { 'cache-control': 'max-age=3600' }, +} + +// 2. Array of [glob, HeadersInit] tuples +headers: [ + ['*.html', { 'cache-control': 'max-age=3600' }], + ['*.png', [['cache-control', 'max-age=0']]], // HeadersInit also accepts [name, value] pairs +] + +// 3. Function returning headers per file +headers: (file) => (file.endsWith('.html') ? { 'cache-control': 'max-age=0' } : undefined) +``` diff --git a/content/docs/config/remote.ko.mdx b/content/docs/references/configuration/remote.ko.mdx similarity index 94% rename from content/docs/config/remote.ko.mdx rename to content/docs/references/configuration/remote.ko.mdx index 6415779..15ea411 100644 --- a/content/docs/config/remote.ko.mdx +++ b/content/docs/references/configuration/remote.ko.mdx @@ -1,5 +1,5 @@ --- -title: 원격, 무결성 및 서명 설정 +title: remote description: wvb.config의 remote 블록과 배포 측 무결성 및 서명 옵션을 설정합니다. --- @@ -9,9 +9,9 @@ description: wvb.config의 remote 블록과 배포 측 무결성 및 서명 옵 그 해시를 개인 키로 서명하여 클라이언트가 누가 번들을 배포했는지 검증할 수 있게 합니다. 이 페이지는 이 세 가지 옵션을 설명하고, 마지막에 복사해서 바로 쓸 수 있는 완전한 설정으로 마무리합니다. -나머지 설정 파일 내용(최상위 필드, `pack`, `serve`, `builtin`)은 [wvb.config 파일](/docs/config)을 +나머지 설정 파일 내용(최상위 필드, `pack`, `serve`, `builtin`)은 [wvb.config 파일](/docs/references/configuration)을 참고하세요. 런타임 측 동작, 즉 클라이언트가 이 번들을 무선(OTA)으로 다운로드하고 검증하고 활성화하는 -방법은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. +방법은 [원격 번들](/docs/guide/core-concepts/over-the-air)을 참고하세요. ## remote @@ -34,7 +34,7 @@ description: wvb.config의 remote 블록과 배포 측 무결성 및 서명 옵 (`wvb deploy --channel `)이지, 설정 객체의 속성이 아닙니다. -### Resolvers +### 리졸버 `bundleName`과 `version`은 리터럴 문자열, 리졸버 객체, 또는 함수를 받습니다. 함수 형태는 `{ packageJson, dir, file }`을 받아 문자열(또는 문자열의 `Promise`)을 반환합니다. @@ -57,7 +57,7 @@ remote: { `{ from: 'package.json' }`은 `name`/`version` 필드를 읽으며, 값이 없으면 오류를 던집니다. `{ from: 'git' }`(version 전용)은 현재 `HEAD` 커밋 해시를 사용합니다. -### Providers +### 프로바이더 `uploader`와 `deployer`는 `@wvb/config`에 내장되어 있지 않습니다. 프로바이더 패키지에서 가져옵니다. 각 프로바이더는 `remote`에 스프레드로 펼쳐 넣을 수 있는 `{ uploader, deployer }`를 반환하는 팩토리를 @@ -66,17 +66,17 @@ remote: { @@ -162,11 +162,11 @@ signature: async ({ message }) => myExternalSigner(message), 클라이언트는 대응하는 **공개** 키로 검증하며, 검증 측은 서명 측과 다른 키 포맷을 받습니다. SPKI, PKCS#1(RSA 전용), SEC1(ECDSA 전용), raw 32바이트(Ed25519 전용)를 받지만 JWK는 받지 않습니다. 검증 - 방법은 [원격 번들](/docs/guide/remote-bundles)을, 클라이언트 측 헬퍼는 [Node API - 레퍼런스](/docs/references/node)를 참고하세요. + 방법은 [원격 번들](/docs/guide/core-concepts/over-the-air)을, 클라이언트 측 헬퍼는 [Node API + 레퍼런스](/docs/references/api/node)를 참고하세요. -## Complete example +## 전체 예시 AWS 프로바이더를 통해 배포하고, SHA-384로 해시하고, Ed25519로 서명하는 완전한 `wvb.config.ts`입니다. 다른 프로바이더를 대상으로 하려면 `awsRemote`를 `localRemote`나 `cloudflareRemote`로 바꾸세요. @@ -204,5 +204,5 @@ export default defineConfig(() => { 배포하려면 먼저 `wvb upload`를 실행하여 `.wvb`를 푸시하고(`packBeforeUpload`가 `true`이면 먼저 패킹합니다), 그다음 `wvb deploy --version `를 실행하여 버전을 라이브로 표시합니다. 전체 명령어 집합은 -[CLI 레퍼런스](/docs/guide/cli)를, 원격을 처음부터 끝까지 구축하고 테스트하는 방법은 +[CLI 레퍼런스](/docs/references/cli)를, 원격을 처음부터 끝까지 구축하고 테스트하는 방법은 [원격 가이드](/docs/guide/remote)를 참고하세요. diff --git a/content/docs/config/remote.mdx b/content/docs/references/configuration/remote.mdx similarity index 94% rename from content/docs/config/remote.mdx rename to content/docs/references/configuration/remote.mdx index 9be3b96..df21832 100644 --- a/content/docs/config/remote.mdx +++ b/content/docs/references/configuration/remote.mdx @@ -1,5 +1,5 @@ --- -title: Remote, integrity & signature config +title: remote description: Configure the remote block of wvb.config, plus the publish-side integrity and signature options. --- @@ -10,8 +10,8 @@ signs that hash with your private key so clients can verify who published the bu documents those three options and ends with a complete, copy-pasteable config. For the rest of the config file (top-level fields, `pack`, `serve`, `builtin`), see -[The wvb.config file](/docs/config). For the runtime side — how a client downloads, verifies, and -activates these bundles over the air — see [Remote bundles](/docs/guide/remote-bundles). +[The wvb.config file](/docs/references/configuration). For the runtime side — how a client downloads, verifies, and +activates these bundles over the air — see [Remote bundles](/docs/guide/core-concepts/over-the-air). ## remote @@ -66,17 +66,17 @@ Each provider exports a factory that returns `{ uploader, deployer }` (AWS also @@ -163,8 +163,8 @@ signature: async ({ message }) => myExternalSigner(message), Clients verify with the matching **public** key, and the verify side accepts different key formats than the sign side — SPKI, PKCS#1 (RSA only), SEC1 (ECDSA only), and raw 32-byte (Ed25519 only), - but not JWK. See [Remote bundles](/docs/guide/remote-bundles) for verification and the [Node API - reference](/docs/references/node) for the client-side helpers. + but not JWK. See [Remote bundles](/docs/guide/core-concepts/over-the-air) for verification and the + [Node API reference](/docs/references/api/node) for the client-side helpers. ## Complete example @@ -205,5 +205,5 @@ export default defineConfig(() => { To publish, run `wvb upload` to push the `.wvb` (which packs first when `packBeforeUpload` is `true`), then `wvb deploy --version ` to mark a version live. See the -[CLI reference](/docs/guide/cli) for the full command set and the +[CLI reference](/docs/references/cli) for the full command set and the [remote guide](/docs/guide/remote) for building and testing a remote end to end. diff --git a/content/docs/references/configuration/serve.ko.mdx b/content/docs/references/configuration/serve.ko.mdx new file mode 100644 index 0000000..a2704aa --- /dev/null +++ b/content/docs/references/configuration/serve.ko.mdx @@ -0,0 +1,33 @@ +--- +title: serve +description: 'wvb.config의 serve 섹션 — 미리보기 서버의 로컬 파일, 포트, 로그 출력입니다.' +--- + +`serve`(`ServeConfig`)는 [`wvb serve`](/docs/references/cli/serve)의 기본값을 설정합니다. `wvb +serve`는 개발용으로 패킹된 번들을 제공하는 로컬 서버입니다. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + serve: { + file: './.wvb/app.wvb', + port: 4312, + silent: false, + }, +}); +``` + +| Field | Type | Default | +| -------- | --------- | ----------------------- | +| `file` | `string` | the `pack.outFile` path | +| `port` | `number` | `4312` | +| `silent` | `boolean` | — | + +`file`을 생략하면 해석된 `pack.outFile` 경로로 대체됩니다. 요청 로그 출력을 끄려면 `silent`를 +설정하세요. + + + `serve`에는 `hostname` 필드가 없습니다. 다른 호스트에 바인딩하려면 명령줄에서 `wvb serve`에 + `--hostname`(별칭 `-H`)을 전달하세요. + diff --git a/content/docs/references/configuration/serve.mdx b/content/docs/references/configuration/serve.mdx new file mode 100644 index 0000000..44bba09 --- /dev/null +++ b/content/docs/references/configuration/serve.mdx @@ -0,0 +1,33 @@ +--- +title: serve +description: The serve section of wvb.config — the local file, port, and log output for the preview server. +--- + +`serve` (`ServeConfig`) sets the defaults for [`wvb serve`](/docs/references/cli/serve), the local +server that serves a packed bundle for development. + +```ts title="wvb.config.ts" +import { defineConfig } from '@wvb/config'; + +export default defineConfig({ + serve: { + file: './.wvb/app.wvb', + port: 4312, + silent: false, + }, +}); +``` + +| Field | Type | Default | +| -------- | --------- | ----------------------- | +| `file` | `string` | the `pack.outFile` path | +| `port` | `number` | `4312` | +| `silent` | `boolean` | — | + +`file` falls back to the resolved `pack.outFile` path when omitted. Set `silent` to disable +request-log output. + + + `serve` has no `hostname` field. To bind a different host, pass `--hostname` (alias `-H`) to `wvb + serve` on the command line. + diff --git a/content/docs/references/deno/index.ko.mdx b/content/docs/references/deno/index.ko.mdx deleted file mode 100644 index df8003c..0000000 --- a/content/docs/references/deno/index.ko.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Deno API -description: '@wvb/node의 Deno FFI 대응 패키지인 @wvb/deno 레퍼런스입니다.' ---- - -실험적 - -Deno 연동은 실험적입니다. - -`@wvb/deno`는 Webview Bundle의 Deno 바인딩입니다. [`@wvb/node`](/docs/references/node)의 FFI 대응 패키지로, -동일한 프로토콜, 소스, 원격, 업데이터 기능을 제공합니다. Deno FFI로 로드한 네이티브 cdylib를 통해 -Deno 데스크톱 웹뷰에 이를 전달합니다. 이 페이지에서는 네이티브 라이브러리를 로드하는 방법을 다룹니다. -클래스, 옵션, 타입은 아래 카테고리 페이지에서 설명합니다. - -## API 표면 - -Deno 바인딩은 `@wvb/node`를 그대로 반영합니다. 각 클래스는 로드된 `lib`를 받고, -[Node API 레퍼런스](/docs/references/node)에서 설명한 것과 동일한 역할을 수행합니다. - - - - - - - - -## 네이티브 라이브러리 로드 - -Deno 바인딩은 Deno FFI로 Rust cdylib와 통신합니다. 클래스를 생성하기 전에 먼저 라이브러리를 로드하세요. -두 가지 방법이 있습니다. - -cdylib가 이미 디스크에 있고 이를 직접 가리키려면 `loadLib(libPath)`를 사용하세요. - -```ts title="main.ts" -import { loadLib } from 'jsr:@wvb/deno'; - -const lib = loadLib('./vendor/wvb/libwvb_deno.dylib'); -``` - -미리 빌드된 cdylib를 필요할 때 다운로드하려면 `loadLibViaPlug(options)`를 사용하세요. -[`@denosaurs/plug`](https://jsr.io/@denosaurs/plug)를 통해 일치하는 아티팩트를 가져오고, -게시된 SHA-256으로 검증한 뒤 로컬에 캐시합니다. - -```ts title="main.ts" -import { loadLibViaPlug } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); -``` - -로드 과정에서 해석되는 라이브러리 경로를 재정의하려면 `WVB_DENO_LIB` 환경 변수를 설정하세요. - -### cdylib 설치 - -런타임에 다운로드하는 대신 cdylib를 미리 벤더링할 수 있습니다. 설치 프로그램을 실행하세요. - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb -``` - -호스트가 아닌 특정 플랫폼 빌드를 가져오려면 `--target `을 전달하세요. - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb --target aarch64-unknown-linux-gnu -``` - -설치 프로그램은 GitHub Releases에서 자산을 다운로드하고 기본적으로 SHA-256을 검증합니다. 지원하는 -타깃은 다음과 같습니다. - -| Target triple | Platform | -| --------------------------- | --------------------- | -| `aarch64-apple-darwin` | macOS (Apple Silicon) | -| `x86_64-apple-darwin` | macOS (Intel) | -| `aarch64-unknown-linux-gnu` | Linux (arm64, glibc) | -| `x86_64-unknown-linux-gnu` | Linux (x64, glibc) | -| `x86_64-pc-windows-msvc` | Windows (x64) | - -## 인스턴스 해제 - -FFI 클래스는 네이티브 핸들을 소유합니다. 이들은 `Disposable`이므로, 사용을 마치면 `free()`를 호출하거나 -`using`으로 바인딩하여 스코프를 벗어날 때 `[Symbol.dispose]`를 통해 해제되도록 하세요. - -```ts -using protocol = new BundleProtocol(lib, source); -// 스코프가 끝나면 자동으로 해제됩니다 - -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - // ... -} finally { - remote.free(); -} -``` - -## 제한 사항 - -Deno 바인딩은 FFI 경계를 넘나들기 때문에, 일부 `@wvb/node` 기능은 현재 지원 범위 밖입니다. - -- **선언적 검증만 지원합니다.** `Updater`는 선언적 `signatureVerifier` - (`SignatureVerifierOptions`)를 지원합니다. `@wvb/node`의 커스텀 함수형 `integrityChecker`와 - `signatureVerifier` 콜백은 FFI에서는 지원되지 않습니다. -- **`HttpOptions.defaultHeaders`는 지원되지 않습니다.** Deno `Remote`에서는 사용할 수 없습니다. - -## Deno Desktop 연동 - -`@wvb/deno-desktop`은 `@wvb/deno`를 Deno의 데스크톱 런타임에 연결합니다. `webviewBundle` 팩토리는 -`BundleSource`(및 선택적 `Remote`/`Updater`)를 빌드하고 `Deno.serve` 호환 핸들러를 노출합니다. -한편 `registerBindings`는 [`@wvb/bridge`](/docs/guide/platform-integration)의 -`source.*`/`remote.*`/`updater.*` 명령어를 `Deno.BrowserWindow`에 연결합니다. - -전체 과정을 처음부터 끝까지 살펴보려면 [Deno Desktop 가이드](/docs/guide/platforms/deno)를 참고하세요. - - - - - diff --git a/content/docs/references/deno/index.mdx b/content/docs/references/deno/index.mdx deleted file mode 100644 index a33c08a..0000000 --- a/content/docs/references/deno/index.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Deno API -description: Reference for the @wvb/deno package, the Deno FFI peer of @wvb/node. ---- - -Experimental - -The Deno integration is experimental. - -`@wvb/deno` is the Deno binding for Webview Bundle. It is the FFI peer of [`@wvb/node`](/docs/references/node): -the same protocol, source, remote, and updater surface, served to a Deno desktop webview through a -native cdylib loaded over Deno FFI. Use this page to load the native library; the classes, options, and -types live on the category pages below. - -## API surface - -The Deno binding mirrors `@wvb/node`. Each class takes a loaded `lib` and serves the same role -described in the [Node API reference](/docs/references/node). - - - - - - - - -## Loading the native library - -The Deno binding talks to a Rust cdylib over Deno FFI. Load it before constructing any class. You have -two options. - -Use `loadLib(libPath)` when you already have the cdylib on disk and want to point at it directly: - -```ts title="main.ts" -import { loadLib } from 'jsr:@wvb/deno'; - -const lib = loadLib('./vendor/wvb/libwvb_deno.dylib'); -``` - -Use `loadLibViaPlug(options)` to download the prebuilt cdylib on demand. It fetches the matching -artifact through [`@denosaurs/plug`](https://jsr.io/@denosaurs/plug), verifies it against a published -SHA-256, and caches it locally: - -```ts title="main.ts" -import { loadLibViaPlug } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); -``` - -Set the `WVB_DENO_LIB` environment variable to override the library path that loading resolves to. - -### Installing the cdylib - -You can vendor the cdylib ahead of time instead of downloading it at runtime. Run the installer: - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb -``` - -Pass `--target ` to fetch a specific platform build instead of the host's: - -```sh -deno run -A jsr:@wvb/deno/install --out vendor/wvb --target aarch64-unknown-linux-gnu -``` - -The installer downloads the asset from GitHub Releases and verifies its SHA-256 by default. Supported -targets: - -| Target triple | Platform | -| --------------------------- | --------------------- | -| `aarch64-apple-darwin` | macOS (Apple silicon) | -| `x86_64-apple-darwin` | macOS (Intel) | -| `aarch64-unknown-linux-gnu` | Linux (arm64, glibc) | -| `x86_64-unknown-linux-gnu` | Linux (x64, glibc) | -| `x86_64-pc-windows-msvc` | Windows (x64) | - -## Disposing instances - -The FFI classes own native handles. They are `Disposable`: call `free()` when you are done, or bind -them with `using` so they release on scope exit through `[Symbol.dispose]`. - -```ts -using protocol = new BundleProtocol(lib, source); -// released automatically at end of scope - -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - // ... -} finally { - remote.free(); -} -``` - -## Limitations - -The Deno binding crosses an FFI boundary, so a few `@wvb/node` features are out of current scope: - -- **Declarative verification only.** `Updater` supports the declarative `signatureVerifier` - (`SignatureVerifierOptions`). The custom-function `integrityChecker` and `signatureVerifier` - callbacks of `@wvb/node` are not supported over FFI. -- **`HttpOptions.defaultHeaders` is not supported** on the Deno `Remote`. - -## Deno Desktop integration - -`@wvb/deno-desktop` ties `@wvb/deno` to Deno's desktop runtime. Its `webviewBundle` factory builds a -`BundleSource` (and an optional `Remote`/`Updater`) and exposes a `Deno.serve`-compatible handler, -while `registerBindings` wires the [`@wvb/bridge`](/docs/guide/platform-integration) -`source.*`/`remote.*`/`updater.*` commands into a `Deno.BrowserWindow`. - -For an end-to-end walkthrough, see the [Deno Desktop guide](/docs/guide/platforms/deno). - - - - - diff --git a/content/docs/references/deno/meta.json b/content/docs/references/deno/meta.json deleted file mode 100644 index 03d5bbf..0000000 --- a/content/docs/references/deno/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Deno API", - "pages": ["index", "source", "protocol", "remote", "updater"] -} diff --git a/content/docs/references/deno/meta.ko.json b/content/docs/references/deno/meta.ko.json deleted file mode 100644 index 03d5bbf..0000000 --- a/content/docs/references/deno/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Deno API", - "pages": ["index", "source", "protocol", "remote", "updater"] -} diff --git a/content/docs/references/deno/protocol.ko.mdx b/content/docs/references/deno/protocol.ko.mdx deleted file mode 100644 index 7010896..0000000 --- a/content/docs/references/deno/protocol.ko.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: 프로토콜 -description: 번들 파일을 제공하고 로컬 개발 URL을 웹뷰로 프록시하는 Deno 프로토콜 핸들러입니다. ---- - -Deno 바인딩은 두 가지 프로토콜 핸들러를 제공합니다. `BundleProtocol`은 커스텀 스킴을 통해 번들의 파일을 웹뷰에 제공합니다. `LocalProtocol`은 개발용으로 `app://host/...` URL을 로컬호스트 주소로 프록시합니다. 두 핸들러 모두 요청을 FFI 응답으로 해석하며, `toResponse(res)`로 표준 `Response`로 변환합니다. - -기본 개념은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. Deno가 제공하는 전체 API와 네이티브 라이브러리를 로드하는 방법은 [Deno API 개요](/docs/references/deno)를 참고하세요. - -## Handlers - -`BundleProtocol`은 로드된 `lib`과 [`BundleSource`](/docs/references/deno/source)를 받습니다. 들어오는 각 요청을 해석된 번들 안의 파일로 매핑합니다. - -`LocalProtocol`은 `app://host/...` 요청을 로컬호스트 주소로 프록시합니다. 개발 서버를 대상으로 작업할 때 유용합니다. - -각 핸들러는 HTTP 메서드, 요청 URL, 헤더 객체를 받아 FFI 응답으로 해석하는 `handle` 메서드를 제공합니다. 이 응답은 `toResponse(res)`로 웹 `Response`로 변환합니다. - -| Member | Description | -| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -| `handle(method, url, headers)` | 요청을 FFI 응답으로 해석합니다. `method`는 `HttpMethod`, `url`은 요청 URL 문자열, `headers`는 요청 헤더의 일반 객체입니다. | -| `toResponse(res)` | FFI 응답을 표준 `Response`로 변환합니다. 독립 함수로 내보내집니다. | -| `free()` | 네이티브 핸들을 해제합니다. `using` / `[Symbol.dispose]`로도 사용할 수 있습니다. | - -`HttpMethod`는 허용되는 요청 메서드의 유니온 타입입니다. 예를 들어 `'get'`과 `'head'`가 있습니다. - -## Serving a bundle - -`BundleSource`를 생성하고 `BundleProtocol`에 전달한 뒤, `Deno.serve` 콜백에서 `handle`을 호출하세요. 결과를 반환하기 전에 `toResponse`로 변환하세요. - -```ts title="serve.ts" -import { loadLibViaPlug, BundleProtocol, BundleSource, toResponse } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -using protocol = new BundleProtocol(lib, source); - -Deno.serve(async req => { - const res = await protocol.handle('get', req.url, Object.fromEntries(req.headers)); - return toResponse(res); -}); -``` - -## Disposing handlers - -프로토콜 클래스는 네이티브 핸들을 소유하므로 `Disposable`입니다. `using`으로 바인딩하면 스코프를 벗어날 때 해제되며, 작업이 끝나면 `free()`를 호출해 해제할 수도 있습니다. - -```ts -using protocol = new BundleProtocol(lib, source); -// 스코프 끝에서 자동으로 해제됨 -``` - - - `BundleProtocol`은 [`BundleSource`](/docs/references/deno/source)에서 읽어옵니다. - [Remote](/docs/references/deno/remote), [Updater](/docs/references/deno/updater)와 함께 사용하면 - 더 새로운 번들을 무선(OTA)으로 가져와 설치할 수 있습니다. - - - - - - diff --git a/content/docs/references/deno/protocol.mdx b/content/docs/references/deno/protocol.mdx deleted file mode 100644 index 1702bb6..0000000 --- a/content/docs/references/deno/protocol.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: Protocol -description: Deno protocol handlers that serve bundle files and proxy local development URLs to a webview. ---- - -The Deno binding ships two protocol handlers. `BundleProtocol` serves a bundle's files to the webview through a custom scheme. `LocalProtocol` proxies an `app://host/...` URL to a localhost address for development. Both resolve a request to an FFI response that you convert to a standard `Response` with `toResponse(res)`. - -For the underlying concepts, see [Protocol handling](/docs/guide/protocol-handling). For the full Deno surface and how to load the native library, see the [Deno API overview](/docs/references/deno). - -## Handlers - -`BundleProtocol` takes a loaded `lib` and a [`BundleSource`](/docs/references/deno/source). It maps each incoming request to a file inside the resolved bundle. - -`LocalProtocol` proxies an `app://host/...` request to a localhost address, which is useful while you develop against a dev server. - -Each handler exposes a `handle` method that accepts an HTTP method, a request URL, and a headers object, and resolves to an FFI response. Convert that response to a Web `Response` with `toResponse(res)`. - -| Member | Description | -| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `handle(method, url, headers)` | Resolve a request to an FFI response. `method` is an `HttpMethod`, `url` is the request URL string, `headers` is a plain object of request headers. | -| `toResponse(res)` | Convert an FFI response into a standard `Response`. Exported as a free function. | -| `free()` | Release the native handle. Also available through `using` / `[Symbol.dispose]`. | - -`HttpMethod` is the union of accepted request methods, for example `'get'` and `'head'`. - -## Serving a bundle - -Construct a `BundleSource`, pass it to `BundleProtocol`, and call `handle` from your `Deno.serve` callback. Convert the result with `toResponse` before returning it. - -```ts title="serve.ts" -import { loadLibViaPlug, BundleProtocol, BundleSource, toResponse } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -using protocol = new BundleProtocol(lib, source); - -Deno.serve(async req => { - const res = await protocol.handle('get', req.url, Object.fromEntries(req.headers)); - return toResponse(res); -}); -``` - -## Disposing handlers - -The protocol classes own native handles, so they are `Disposable`. Bind them with `using` to release on scope exit, or call `free()` when you are done. - -```ts -using protocol = new BundleProtocol(lib, source); -// released automatically at end of scope -``` - - - `BundleProtocol` reads from a [`BundleSource`](/docs/references/deno/source). Pair it with the - [Remote](/docs/references/deno/remote) and [Updater](/docs/references/deno/updater) to fetch and - install newer bundles over the air. - - - - - - diff --git a/content/docs/references/deno/remote.ko.mdx b/content/docs/references/deno/remote.ko.mdx deleted file mode 100644 index 3fc17ca..0000000 --- a/content/docs/references/deno/remote.ko.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Remote -description: Deno의 Remote 클래스는 Deno FFI를 통해 원격 번들 서버와 통신하여 번들을 조회하고 다운로드합니다. ---- - -`Remote`는 Deno 바인딩에서 원격 번들 서버에 접속하는 클라이언트입니다. 서버가 알리는 번들 목록을 -조회하고 각 번들의 원시 `.wvb` 바이트를 다운로드합니다. 이를 통해 [`Updater`](/docs/references/deno/updater)가 -새 버전을 무선(OTA)으로 설치합니다. `Remote`는 [`@wvb/node`](/docs/references/node/remote)의 `Remote`와 -FFI 상의 대응 구현이며, [`@wvb/deno`](/docs/references/deno)의 나머지 부분과 마찬가지로 네이티브 FFI -경계를 넘나듭니다. - -이 관계의 서버 측 — 원격 서버가 구현하는 HTTP 규약과 실행 방법 — 은 [원격 서버 만들기](/docs/guide/remote)를 -참고하세요. - -## Remote 생성하기 - -먼저 네이티브 라이브러리를 로드한 뒤, 로드한 `lib`과 서버 엔드포인트, 그리고 선택 항목인 `RemoteOptions`로 -`Remote`를 생성합니다. - -```ts title="remote.ts" -import { loadLibViaPlug, Remote } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); -using remote = new Remote(lib, 'https://bundles.example.com'); - -const list = await remote.listBundles(); -const { info, data } = await remote.download('app'); -// info: RemoteBundleInfo, data: Uint8Array (원시 .wvb 바이트) -``` - -엔드포인트는 원격 번들 서버의 기본 URL입니다. `RemoteOptions`는 `HttpOptions` 값을 포함한 선택적 요청 -설정을 담습니다. [아래 타입 레퍼런스](#options-and-types)를 참고하세요. - -## 메서드 - -| Method | Description | -| -------------------------------------- | --------------------------------------------------------------------------------- | -| `listBundles()` | 원격 서버가 알리는 번들 목록(이름과 버전 쌍)을 조회합니다. | -| `download(bundleName)` | `bundleName`의 현재 번들을 다운로드합니다. `{ info, data }`로 resolve됩니다. | -| `downloadVersion(bundleName, version)` | `bundleName`의 특정 `version`을 다운로드합니다. `{ info, data }`로 resolve됩니다. | -| `free()` | 네이티브 핸들을 해제합니다. `[Symbol.dispose]`를 통해서도 호출됩니다. | - -`download(bundleName)`과 `downloadVersion(bundleName, version)`은 모두 `{ info, data }`로 resolve됩니다. -여기서 `info`는 `RemoteBundleInfo`이고, `data`는 원시 번들 바이트를 담은 `Uint8Array`입니다. - -```ts title="download.ts" -const { info, data } = await remote.downloadVersion('app', '1.4.0'); -await Deno.writeFile('./bundles/remote/app.wvb', data); -``` - -## 옵션과 타입 - -`Remote`는 다음 관련 타입들과 함께 동작합니다. 이 타입들의 형태는 [`@wvb/node` Remote -레퍼런스](/docs/references/node/remote)와 동일하니, 필드 전체를 자세히 확인하려면 해당 문서를 참고하세요. - -| Type | Role | -| ------------------ | ---------------------------------------------------- | -| `RemoteOptions` | 생성자에 전달하는 선택적 설정입니다. | -| `HttpOptions` | `RemoteOptions`가 담는 HTTP 요청 설정입니다. | -| `RemoteBundleInfo` | 다운로드한 번들 바이트와 함께 반환되는 `info`입니다. | - - - `HttpOptions.defaultHeaders`는 Deno `Remote`에서 지원되지 않습니다. FFI 경계를 넘나들기 때문에 이 - `@wvb/node` 기능은 여기서 사용할 수 없습니다. - - -## Remote 정리하기 - -`Remote`는 네이티브 핸들을 소유하며 `Disposable`입니다. `using`으로 바인딩하면 스코프를 벗어날 때 자동으로 -해제되고, 사용을 마쳤을 때 `free()`를 직접 호출할 수도 있습니다. - -```ts title="dispose.ts" -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - const list = await remote.listBundles(); - // ... -} finally { - remote.free(); -} -``` - - - - - diff --git a/content/docs/references/deno/remote.mdx b/content/docs/references/deno/remote.mdx deleted file mode 100644 index 435cf1e..0000000 --- a/content/docs/references/deno/remote.mdx +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Remote -description: The Deno Remote class talks to a remote bundle server to list and download bundles over Deno FFI. ---- - -`Remote` is the Deno binding's client for a remote bundle server. It lists the bundles a server -advertises and downloads their raw `.wvb` bytes, so an [`Updater`](/docs/references/deno/updater) can -install newer versions over the air. It is the FFI peer of the -[`@wvb/node`](/docs/references/node/remote) `Remote`, and crosses a native FFI boundary like the rest of -[`@wvb/deno`](/docs/references/deno). - -For the server side of this relationship — the HTTP contract a remote implements and how to run one — -see [Building a remote](/docs/guide/remote). - -## Constructing a Remote - -Load the native library first, then construct `Remote` with the loaded `lib`, the server endpoint, and -optional `RemoteOptions`. - -```ts title="remote.ts" -import { loadLibViaPlug, Remote } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); -using remote = new Remote(lib, 'https://bundles.example.com'); - -const list = await remote.listBundles(); -const { info, data } = await remote.download('app'); -// info: RemoteBundleInfo, data: Uint8Array (raw .wvb bytes) -``` - -The endpoint is the base URL of the remote bundle server. `RemoteOptions` carries optional request -configuration, including an `HttpOptions` value. See [the type reference below](#options-and-types). - -## Methods - -| Method | Description | -| -------------------------------------- | ----------------------------------------------------------------------------- | -| `listBundles()` | Lists the bundles the remote advertises (name and version pairs). | -| `download(bundleName)` | Downloads the current bundle for `bundleName`; resolves to `{ info, data }`. | -| `downloadVersion(bundleName, version)` | Downloads a specific `version` of `bundleName`; resolves to `{ info, data }`. | -| `free()` | Releases the native handle. Also invoked via `[Symbol.dispose]`. | - -`download(bundleName)` and `downloadVersion(bundleName, version)` both resolve to `{ info, data }`, -where `info` is a `RemoteBundleInfo` and `data` is a `Uint8Array` of the raw bundle bytes. - -```ts title="download.ts" -const { info, data } = await remote.downloadVersion('app', '1.4.0'); -await Deno.writeFile('./bundles/remote/app.wvb', data); -``` - -## Options and types - -`Remote` works with these related types. The shapes mirror the [`@wvb/node` Remote -reference](/docs/references/node/remote); refer to it for full field details. - -| Type | Role | -| ------------------ | ------------------------------------------------------ | -| `RemoteOptions` | Optional configuration passed to the constructor. | -| `HttpOptions` | HTTP request configuration carried by `RemoteOptions`. | -| `RemoteBundleInfo` | The `info` returned alongside downloaded bundle bytes. | - - - `HttpOptions.defaultHeaders` is not supported on the Deno `Remote`. It crosses the FFI boundary, - so this `@wvb/node` feature is unavailable here. - - -## Disposing a Remote - -`Remote` owns a native handle and is `Disposable`. Bind it with `using` so it releases on scope exit, -or call `free()` explicitly when you are done. - -```ts title="dispose.ts" -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - const list = await remote.listBundles(); - // ... -} finally { - remote.free(); -} -``` - - - - - diff --git a/content/docs/references/deno/source.ko.mdx b/content/docs/references/deno/source.ko.mdx deleted file mode 100644 index a4e505b..0000000 --- a/content/docs/references/deno/source.ko.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: 소스 -description: builtin 디렉터리와 remote 디렉터리에서 번들을 해석하는 Deno의 BundleSource 클래스입니다. ---- - -`BundleSource`는 디스크에서 번들을 읽어 들이는 Deno 바인딩의 진입점입니다. builtin 디렉터리와 remote 디렉터리에서 번들을 해석하며, 이때 remote 디렉터리가 우선합니다. 또한 [`@wvb/node`](/docs/references/node)의 `BundleSource`와 동일한 데이터 API를 제공합니다. Deno 바인딩의 다른 클래스 대부분 — `BundleProtocol`, `Updater` — 은 `BundleSource`를 받아 이를 통해 데이터를 읽습니다. - -## 소스 생성하기 - -먼저 네이티브 라이브러리를 로드한 뒤, 로드된 `lib`와 옵션 객체를 생성자에 전달하세요. cdylib를 로드하는 두 가지 방법은 [Deno API 개요](/docs/references/deno)를 참고하세요. - -```ts title="source.ts" -import { loadLibViaPlug, BundleSource } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -``` - -생성자는 두 개의 인자를 받습니다. - -| 인자 | 타입 | 설명 | -| --------- | ----------------- | ------------------------------------------------------ | -| `lib` | 로드된 라이브러리 | `loadLib`/`loadLibViaPlug`가 반환한 cdylib 핸들입니다. | -| `options` | 객체 | 해석에 사용할 builtin 및 remote 디렉터리입니다. | - -옵션은 다음과 같습니다. - -| 옵션 | 타입 | 설명 | -| ------------ | -------- | ------------------------------------------------------------------------------------- | -| `builtinDir` | `string` | 앱에 기본 포함되어 배포되는 번들을 담는 디렉터리입니다. | -| `remoteDir` | `string` | 원격에서 다운로드한 번들을 담는 디렉터리입니다. `builtinDir`보다 우선하여 해석됩니다. | - -## 소스가 하는 일 - -`BundleSource`는 Node `BundleSource`와 동일한 데이터 API를 제공합니다. - -- 해석할 수 있는 번들 목록을 조회합니다. -- 특정 버전을 로드합니다. -- 번들 또는 그 디스크립터를 가져옵니다. -- 보관 중인 remote 버전을 정리합니다. - -이 공통 API의 정확한 메서드 이름, 시그니처, 반환 타입은 Deno 바인딩이 그대로 미러링하는 [Node API 레퍼런스](/docs/references/node)를 참고하세요. - -## 소스 사용하기 - -`BundleSource`는 프로토콜과 업데이터가 데이터를 읽어 들이는 대상입니다. 번들의 파일을 웹뷰에 제공하려면 `BundleProtocol`에 전달하고, 다운로드한 최신 버전을 remote 디렉터리에 설치하려면 `Updater`에 전달하세요. - -```ts title="serve.ts" -import { loadLibViaPlug, BundleProtocol, BundleSource, toResponse } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -using protocol = new BundleProtocol(lib, source); - -Deno.serve(async req => { - const res = await protocol.handle('get', req.url, Object.fromEntries(req.headers)); - return toResponse(res); -}); -``` - -해석된 파일이 웹뷰에 어떻게 전달되는지는 [프로토콜](/docs/references/deno/protocol)을 참고하세요. - -## Deno 관련 참고 사항 - -Deno 바인딩은 Deno FFI를 통해 Rust cdylib와 통신합니다. 따라서 `BundleSource`는 직접 import하지 않고 로드된 `lib`로부터 생성합니다. FFI 클래스는 네이티브 핸들을 소유하므로, 사용이 끝나면 반드시 해제하세요. `using`으로 바인딩하여 스코프를 벗어날 때 해제하거나, `free()`를 명시적으로 호출하면 됩니다. - - - - - diff --git a/content/docs/references/deno/source.mdx b/content/docs/references/deno/source.mdx deleted file mode 100644 index 5bfe546..0000000 --- a/content/docs/references/deno/source.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: Source -description: The Deno BundleSource class, which resolves bundles from a builtin directory and a remote directory. ---- - -`BundleSource` is the Deno binding's entry point for reading bundles off disk. It resolves a bundle from a builtin directory and a remote directory, with the remote directory taking priority, and exposes the same data API as [`@wvb/node`](/docs/references/node)'s `BundleSource`. Most other classes in the Deno binding — `BundleProtocol`, `Updater` — take a `BundleSource` and read through it. - -## Constructing a source - -Load the native library first, then pass the loaded `lib` and an options object to the constructor. See the [Deno API overview](/docs/references/deno) for the two ways to load the cdylib. - -```ts title="source.ts" -import { loadLibViaPlug, BundleSource } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -``` - -The constructor takes two arguments: - -| Argument | Type | Description | -| --------- | -------------- | --------------------------------------------------------- | -| `lib` | loaded library | The cdylib handle returned by `loadLib`/`loadLibViaPlug`. | -| `options` | object | The builtin and remote directories to resolve from. | - -Its options are: - -| Option | Type | Description | -| ------------ | -------- | --------------------------------------------------------------------------------------------- | -| `builtinDir` | `string` | Directory holding bundles the app ships with. | -| `remoteDir` | `string` | Directory holding bundles downloaded from a remote. Resolved with priority over `builtinDir`. | - -## What a source does - -A `BundleSource` exposes the same data API as the Node `BundleSource`: - -- List the bundles it can resolve. -- Load a specific version. -- Fetch a bundle or its descriptor. -- Prune retained remote versions. - -For the exact method names, signatures, and return types of this shared surface, see the [Node API reference](/docs/references/node), which the Deno binding mirrors. - -## Using a source - -`BundleSource` is what protocols and the updater read through. Pass it to a `BundleProtocol` to serve a bundle's files to the webview, or to an `Updater` to install newer downloaded versions into the remote directory. - -```ts title="serve.ts" -import { loadLibViaPlug, BundleProtocol, BundleSource, toResponse } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -using protocol = new BundleProtocol(lib, source); - -Deno.serve(async req => { - const res = await protocol.handle('get', req.url, Object.fromEntries(req.headers)); - return toResponse(res); -}); -``` - -For how the resolved files reach the webview, see [Protocols](/docs/references/deno/protocol). - -## Deno-specific notes - -The Deno binding talks to a Rust cdylib over Deno FFI, so a `BundleSource` is constructed from a loaded `lib` rather than imported directly. The FFI classes own native handles, so dispose of them when you are done — bind them with `using` to release on scope exit, or call `free()` explicitly. - - - - - diff --git a/content/docs/references/deno/updater.ko.mdx b/content/docs/references/deno/updater.ko.mdx deleted file mode 100644 index 04b17cf..0000000 --- a/content/docs/references/deno/updater.ko.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Updater -description: '@wvb/deno의 Updater로 Deno 데스크톱 앱에서 원격에 최신 번들이 있는지 확인하고 설치합니다.' ---- - -`Updater`는 [원격](/docs/references/deno/remote)에서 번들의 최신 버전을 확인하고 로컬 스토어에 -설치합니다. [`BundleSource`](/docs/references/deno/source)를 `Remote`에 연결하고, 무결성 및 서명 -정책을 적용하며, 다운로드한 번들을 기록합니다. 그러면 다음 실행 때 업데이트된 앱 코드가 제공됩니다. -이 방식으로 Deno 데스크톱 앱은 새로운 릴리스 없이 무선(OTA) 업데이트를 전달합니다. 전체 흐름은 -[원격 번들](/docs/guide/remote-bundles)을 참고하세요. 바인딩의 나머지 내용은 -[Deno API 개요](/docs/references/deno)를 참고하세요. - -## Constructor - -`Updater`는 로드된 `lib`, `BundleSource`, `Remote`, 그리고 선택적인 `UpdaterOptions`로 생성합니다. -소스와 원격은 같은 `lib`을 사용해야 합니다. - -```ts title="updater.ts" -import { loadLibViaPlug, BundleSource, Remote, Updater } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -using remote = new Remote(lib, 'https://bundles.example.com'); - -using updater = new Updater(lib, source, remote, { - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { format: 'raw', data: publicKeyBytes }, - }, -}); -``` - -| Parameter | Type | Description | -| --------- | ---------------- | ------------------------------------------------------------ | -| `lib` | loaded library | `loadLib`/`loadLibViaPlug`로 얻은 네이티브 라이브러리입니다. | -| `source` | `BundleSource` | 업데이트가 설치되는 로컬 스토어입니다. | -| `remote` | `Remote` | 업데이트를 가져오는 원격 서버입니다. | -| `options` | `UpdaterOptions` | 선택 사항. 무결성 정책과 서명 검증기입니다. | - -## Options - -`UpdaterOptions`는 가져온 번들을 설치하기 전에 어떻게 검증할지 설정합니다. - -| Option | Type | Description | -| ------------------- | -------------------------- | ------------------------------------------------------------------------------- | -| `integrityPolicy` | `IntegrityPolicy` | 무결성을 적용하는 방식입니다: `'strict'`, `'optional'`, `'none'`. | -| `signatureVerifier` | `SignatureVerifierOptions` | 선언적 서명 검증입니다. `algorithm`과 `key`(`format`과 키 `data`)로 지정합니다. | - -`IntegrityPolicy`는 `'strict' | 'optional' | 'none'` 유니온입니다. `SignatureVerifierOptions`는 -`SignatureAlgorithm`(예: `'ed25519'`)과 검증에 사용할 공개 키를 지정합니다. - - - Deno 바인딩의 검증은 선언적 방식만 지원합니다. [`@wvb/node`](/docs/references/node)의 사용자 정의 - 함수 콜백인 `integrityChecker`와 `signatureVerifier`는 FFI 경계를 넘지 못합니다. - - -## Methods - -업데이트는 세 단계로 실행합니다. 원격에 무엇이 있는지 확인하고, 다운로드한 다음, 설치합니다. - -```ts title="updater.ts" -const update = await updater.getUpdate('app'); -if (update.isAvailable) { - await updater.download('app'); - await updater.install('app', update.version); -} -``` - -| Method | Returns | Description | -| ------------------------------ | ------------------ | ---------------------------------------------------------------------- | -| `getUpdate(bundleName)` | `BundleUpdateInfo` | 원격에서 최신 버전을 확인합니다. `isAvailable`과 `version`을 읽으세요. | -| `download(bundleName)` | `Promise` | 원격에서 현재 번들을 로컬 스토어로 가져옵니다. | -| `install(bundleName, version)` | `Promise` | 다운로드한 버전을 설치하여 다음 실행 때 제공되게 합니다. | - -`BundleUpdateInfo`는 업데이트가 있는지(`isAvailable`)와 대상 버전(`version`)을 알려줍니다. - -## Disposing instances - -`Updater`, `Remote`, 그리고 다른 FFI 클래스는 네이티브 핸들을 소유합니다. 이들은 `Disposable`입니다. -사용을 마치면 `free()`를 호출하거나, `using`으로 바인딩하여 스코프를 벗어날 때 `[Symbol.dispose]`를 -통해 자동으로 해제되게 하세요. - -```ts -using updater = new Updater(lib, source, remote); -// 스코프가 끝나면 자동으로 해제됨 - -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - // ... -} finally { - remote.free(); -} -``` - - - - - diff --git a/content/docs/references/deno/updater.mdx b/content/docs/references/deno/updater.mdx deleted file mode 100644 index 1776cca..0000000 --- a/content/docs/references/deno/updater.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Updater -description: Check a remote for newer bundles and install them in a Deno desktop app with the @wvb/deno Updater. ---- - -`Updater` checks a [remote](/docs/references/deno/remote) for newer versions of a bundle and installs -them into the local store. It ties a [`BundleSource`](/docs/references/deno/source) to a `Remote`, -applies your integrity and signature policy, and writes the downloaded bundle so the next launch serves -the updated app code. This is how a Deno desktop app delivers updates over the air (OTA) without a new -release. For the end-to-end model, see [Remote bundles](/docs/guide/remote-bundles); for the rest of -the binding, see the [Deno API overview](/docs/references/deno). - -## Constructor - -Construct an `Updater` with a loaded `lib`, a `BundleSource`, a `Remote`, and optional -`UpdaterOptions`. The source and remote must use the same `lib`. - -```ts title="updater.ts" -import { loadLibViaPlug, BundleSource, Remote, Updater } from 'jsr:@wvb/deno'; - -const lib = await loadLibViaPlug(); - -const source = new BundleSource(lib, { - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -using remote = new Remote(lib, 'https://bundles.example.com'); - -using updater = new Updater(lib, source, remote, { - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { format: 'raw', data: publicKeyBytes }, - }, -}); -``` - -| Parameter | Type | Description | -| --------- | ---------------- | --------------------------------------------------- | -| `lib` | loaded library | The native library from `loadLib`/`loadLibViaPlug`. | -| `source` | `BundleSource` | Local store the update is installed into. | -| `remote` | `Remote` | Remote server the update is fetched from. | -| `options` | `UpdaterOptions` | Optional. Integrity policy and signature verifier. | - -## Options - -`UpdaterOptions` configures how a fetched bundle is verified before it is installed. - -| Option | Type | Description | -| ------------------- | -------------------------- | ------------------------------------------------------------------------------------------ | -| `integrityPolicy` | `IntegrityPolicy` | How integrity is enforced: `'strict'`, `'optional'`, or `'none'`. | -| `signatureVerifier` | `SignatureVerifierOptions` | Declarative signature verification: an `algorithm` and a `key` (`format` plus key `data`). | - -`IntegrityPolicy` is the union `'strict' | 'optional' | 'none'`. `SignatureVerifierOptions` names a -`SignatureAlgorithm` (for example `'ed25519'`) and the public key to verify against. - - - Verification on the Deno binding is declarative only. The custom-function `integrityChecker` and - `signatureVerifier` callbacks of [`@wvb/node`](/docs/references/node) do not cross the FFI - boundary. - - -## Methods - -Run an update in three steps: ask the remote what is available, download it, then install it. - -```ts title="updater.ts" -const update = await updater.getUpdate('app'); -if (update.isAvailable) { - await updater.download('app'); - await updater.install('app', update.version); -} -``` - -| Method | Returns | Description | -| ------------------------------ | ------------------ | ----------------------------------------------------------------------- | -| `getUpdate(bundleName)` | `BundleUpdateInfo` | Check the remote for a newer version. Read `isAvailable` and `version`. | -| `download(bundleName)` | `Promise` | Fetch the current bundle from the remote into the local store. | -| `install(bundleName, version)` | `Promise` | Install a downloaded version so it is served on the next launch. | - -`BundleUpdateInfo` reports whether an update `isAvailable` and the target `version`. - -## Disposing instances - -`Updater`, `Remote`, and the other FFI classes own native handles. They are `Disposable`: call `free()` -when you are done, or bind them with `using` so they release on scope exit through `[Symbol.dispose]`. - -```ts -using updater = new Updater(lib, source, remote); -// released automatically at end of scope - -const remote = new Remote(lib, 'https://bundles.example.com'); -try { - // ... -} finally { - remote.free(); -} -``` - - - - - diff --git a/content/docs/references/index.ko.mdx b/content/docs/references/index.ko.mdx index 87863bd..d11f98b 100644 --- a/content/docs/references/index.ko.mdx +++ b/content/docs/references/index.ko.mdx @@ -6,10 +6,10 @@ description: Rust 코어, Node 및 Deno 바인딩, 웹 측 브리지에 대한 A 대부분의 앱 개발자는 이 API를 직접 다룰 일이 없습니다. 앱에 Webview Bundle을 도입하려면 플랫폼 통합 패키지를 사용하고 플랫폼 가이드를 따르세요. 이 패키지들이 코어를 대신 감싸줍니다. 이 레퍼런스는 고급 사용과 임베딩을 위한 것입니다. 코어를 직접 호스팅하거나, Node 또는 Deno 바인딩을 다루거나, 웹뷰 내부에서 네이티브 호스트를 호출하는 경우에 해당합니다. - 앱을 만드시나요? [플랫폼 통합 가이드](/docs/guide/platform-integration)에서 시작해 플랫폼을 - 선택하세요. [Electron](/docs/guide/platforms/electron), [Tauri](/docs/guide/platforms/tauri), - [Android](/docs/guide/platforms/android), [iOS](/docs/guide/platforms/ios), [Deno - Desktop](/docs/guide/platforms/deno) 중에서 고르면 됩니다. + 앱을 만드시나요? [플랫폼 통합 가이드](/docs/guide/core-concepts/glue-to-native)에서 시작해 + 플랫폼을 선택하세요. [Electron](/docs/guide/native/electron), [Tauri](/docs/guide/native/tauri), + [Android](/docs/guide/native/android), [iOS](/docs/guide/native/ios), [Deno + Desktop](/docs/guide/native/deno) 중에서 고르면 됩니다. ## API 레퍼런스 @@ -22,24 +22,24 @@ description: Rust 코어, Node 및 Deno 바인딩, 웹 측 브리지에 대한 A /> ## 웹 측 브리지 -브리지(`@wvb/bridge`)는 웹뷰 내부에서 실행되며, 웹 앱이 네이티브 호스트를 호출하도록 해줍니다. 단일 `invoke()` 함수와 타입이 지정된 `source`, `remote`, `updater` 헬퍼를 제공합니다. 덕분에 동일한 코드가 Electron, Tauri, Android, iOS에서 그대로 동작하며, 플랫폼별 전송 계층은 추상화되어 있습니다. 전체 API는 [Bridge 레퍼런스](/docs/references/bridge)를 참고하세요. +브리지(`@wvb/bridge`)는 웹뷰 내부에서 실행되며, 웹 앱이 네이티브 호스트를 호출하도록 해줍니다. 단일 `invoke()` 함수와 타입이 지정된 `source`, `remote`, `updater` 헬퍼를 제공합니다. 덕분에 동일한 코드가 Electron, Tauri, Android, iOS에서 그대로 동작하며, 플랫폼별 전송 계층은 추상화되어 있습니다. 전체 API는 [Bridge 레퍼런스](/docs/references/api/bridge)를 참고하세요. ```ts import { invoke, updater } from '@wvb/bridge'; @@ -55,11 +55,11 @@ if (update.isAvailable) { const bundles = await invoke('sourceListBundles'); ``` -네이티브 측은 `@wvb/node`를 호스팅하며 이 호출들에 응답합니다. 브리지가 각 플랫폼에 어떻게 들어맞는지는 [플랫폼 통합 가이드](/docs/guide/platform-integration)를 참고하세요. +네이티브 측은 `@wvb/node`를 호스팅하며 이 호출들에 응답합니다. 브리지가 각 플랫폼에 어떻게 들어맞는지는 [플랫폼 통합 가이드](/docs/guide/core-concepts/glue-to-native)를 참고하세요. ## 그 밖의 플랫폼 Tauri와 모바일 통합은 각자의 API를 제공하며, 해당 가이드와 함께 문서화되어 있습니다. -- Tauri는 Rust 크레이트 `wvb-tauri`로 제공됩니다. [Tauri 가이드](/docs/guide/platforms/tauri)를 참고하세요. -- Android(Kotlin)와 iOS(Swift) 바인딩은 [Android 가이드](/docs/guide/platforms/android)와 [iOS 가이드](/docs/guide/platforms/ios)에서 다룹니다. +- Tauri는 Rust 크레이트 `wvb-tauri`로 제공됩니다. [Tauri 가이드](/docs/guide/native/tauri)를 참고하세요. +- Android(Kotlin)와 iOS(Swift) 바인딩은 [Android 가이드](/docs/guide/native/android)와 [iOS 가이드](/docs/guide/native/ios)에서 다룹니다. diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index 255634f..dc1d134 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -6,10 +6,11 @@ description: API references for the Rust core, the Node and Deno bindings, and t Most app developers never touch these APIs directly. To ship Webview Bundle in an app, reach for the platform integration packages and follow the platform guides — they wrap the core for you. These references are for advanced and embedding use: hosting the core yourself, driving the Node or Deno bindings, or calling the native host from inside the webview. - Building an app? Start with the [platform integration guide](/docs/guide/platform-integration) and - pick your platform: [Electron](/docs/guide/platforms/electron), - [Tauri](/docs/guide/platforms/tauri), [Android](/docs/guide/platforms/android), - [iOS](/docs/guide/platforms/ios), or [Deno Desktop](/docs/guide/platforms/deno). + Building an app? Start with the [platform integration + guide](/docs/guide/core-concepts/glue-to-native) and pick your platform: + [Electron](/docs/guide/native/electron), [Tauri](/docs/guide/native/tauri), + [Android](/docs/guide/native/android), [iOS](/docs/guide/native/ios), or [Deno + Desktop](/docs/guide/native/deno). ## API references @@ -22,24 +23,24 @@ Most app developers never touch these APIs directly. To ship Webview Bundle in a /> ## Web-side bridge -The bridge (`@wvb/bridge`) runs inside the webview and lets your web app call the native host. It exposes a single `invoke()` function plus typed `source`, `remote`, and `updater` helpers, so the same code works across Electron, Tauri, Android, and iOS — the per-platform transport is abstracted away. See the [Bridge reference](/docs/references/bridge) for the full API. +The bridge (`@wvb/bridge`) runs inside the webview and lets your web app call the native host. It exposes a single `invoke()` function plus typed `source`, `remote`, and `updater` helpers, so the same code works across Electron, Tauri, Android, and iOS — the per-platform transport is abstracted away. See the [Bridge reference](/docs/references/api/bridge) for the full API. ```ts import { invoke, updater } from '@wvb/bridge'; @@ -55,11 +56,11 @@ if (update.isAvailable) { const bundles = await invoke('sourceListBundles'); ``` -The native side hosts `@wvb/node` and answers these calls. For how the bridge fits into each platform, see the [platform integration guide](/docs/guide/platform-integration). +The native side hosts `@wvb/node` and answers these calls. For how the bridge fits into each platform, see the [platform integration guide](/docs/guide/core-concepts/glue-to-native). ## Other platforms The Tauri and mobile integrations expose their own APIs, documented alongside their guides: -- Tauri ships as the Rust crate `wvb-tauri` — see the [Tauri guide](/docs/guide/platforms/tauri). -- Android (Kotlin) and iOS (Swift) bindings are covered in the [Android guide](/docs/guide/platforms/android) and the [iOS guide](/docs/guide/platforms/ios). +- Tauri ships as the Rust crate `wvb-tauri` — see the [Tauri guide](/docs/guide/native/tauri). +- Android (Kotlin) and iOS (Swift) bindings are covered in the [Android guide](/docs/guide/native/android) and the [iOS guide](/docs/guide/native/ios). diff --git a/content/docs/references/meta.json b/content/docs/references/meta.json index 6fa009c..bdf1b15 100644 --- a/content/docs/references/meta.json +++ b/content/docs/references/meta.json @@ -1,5 +1,17 @@ { "root": true, "title": "References", - "pages": ["index", "[Rust (docs.rs)](https://docs.rs/wvb)", "node", "deno", "bridge"] + "pages": [ + "index", + "cli", + "configuration", + "---Rust---", + "[wvb (docs.rs)](https://docs.rs/wvb)", + "[wvb-tauri (docs.rs)](https://docs.rs/wvb-tauri)", + "---JavaScript---", + "...api", + "---Native---", + "[Android (javadoc.io)](https://javadoc.io/doc/dev.wvb/webview-bundle-android)", + "[iOS (Swift Package Index)](https://swiftpackageindex.com/webview-bundle/webview-bundle-ios)" + ] } diff --git a/content/docs/references/meta.ko.json b/content/docs/references/meta.ko.json index 7629f78..602d642 100644 --- a/content/docs/references/meta.ko.json +++ b/content/docs/references/meta.ko.json @@ -1,5 +1,17 @@ { "root": true, "title": "레퍼런스", - "pages": ["index", "[Rust (docs.rs)](https://docs.rs/wvb)", "node", "deno", "bridge"] + "pages": [ + "index", + "cli", + "configuration", + "---Rust---", + "[wvb (docs.rs)](https://docs.rs/wvb)", + "[wvb-tauri (docs.rs)](https://docs.rs/wvb-tauri)", + "---JavaScript---", + "...api", + "---네이티브---", + "[Android (javadoc.io)](https://javadoc.io/doc/dev.wvb/webview-bundle-android)", + "[iOS (Swift Package Index)](https://swiftpackageindex.com/webview-bundle/webview-bundle-ios)" + ] } diff --git a/content/docs/references/node/bundle.ko.mdx b/content/docs/references/node/bundle.ko.mdx deleted file mode 100644 index 9776006..0000000 --- a/content/docs/references/node/bundle.ko.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: Bundle -description: .wvb 아카이브를 읽고, 빌드하고, 검사하기 위한 @wvb/node 클래스. ---- - -`@wvb/node`는 메모리와 디스크에서 `.wvb` 아카이브를 다루는 소규모 클래스 집합을 제공합니다. `Bundle`과 `BundleBuilder`는 번들 전체를 읽고 씁니다. `BundleDescriptor`, `Header`, `Index`, `LoadedDescriptor`는 번들의 구조를 검사하고, 전체를 메모리에 올리지 않고도 개별 파일을 가져올 수 있게 합니다. - -소스, 프로토콜, 원격 클라이언트, 업데이터까지 패키지의 전체 표면은 [Node API 개요](/docs/references/node)를 참고하세요. 바이트가 디스크에 배치되는 방식은 [.wvb 번들 포맷](/docs/guide/bundle-format)을 참고하세요. - -## 번들 읽기와 파일 가져오기 - -`readBundle`을 호출해 디스크의 `.wvb` 파일을 파싱한 다음, `getData`로 개별 엔트리를 가져옵니다. `getData`는 `Buffer`를 반환하며, 경로가 번들에 없으면 `null`을 반환합니다. - -```ts -import { readBundle } from '@wvb/node'; - -const bundle = await readBundle('./dist/app.wvb'); - -const html = bundle.getData('index.html'); -if (html) { - console.log(html.toString('utf8')); -} -``` - -## 번들과 빌더 - -`Bundle`은 메모리에 보관된, 파싱이 끝난 아카이브입니다. `readBundle`(또는 `readBundleFromBuffer`)에서 얻거나 `BundleBuilder.build`를 호출해 얻습니다. `BundleBuilder`는 엔트리를 모아 `Bundle`을 만들며, 이렇게 만든 번들을 디스크에 쓸 수 있습니다. - -| 클래스 | 생성자 | 주요 메서드 | -| --------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Bundle` | `readBundle` / `BundleBuilder.build`로 생성됨 | `descriptor(): BundleDescriptor`; `getData(path: string): Buffer \| null`; `getDataChecksum(path: string): number \| null` | -| `BundleBuilder` | `new BundleBuilder(version?: Version)` | `get version: Version`; `entryPaths(): Array`; `insertEntry(path, data: Buffer, contentType?, headers?: Record): boolean`; `removeEntry(path): boolean`; `containsEntry(path): boolean`; `build(options?: BuildOptions): Bundle` | - - - `getDataChecksum`은 파일 바이트의 내부 xxHash-32 체크섬을 반환하며, 이는 아카이브 내부의 손상을 - 감지하는 데 쓰입니다. 이는 번들의 무결성 해시와는 다릅니다. 무결성 해시는 SHA-2 (`sha256`, - `sha384`, `sha512`)를 사용해 다운로드된 내용을 검증합니다. - - -## 디스크립터, 헤더, 인덱스 - -디스크립터는 모든 파일을 메모리에 올리지 않고 번들의 구조를 노출합니다. `bundle.descriptor()`를 호출해 `BundleDescriptor`를 얻은 다음, 그 `Header`와 `Index`를 읽거나 필요할 때 개별 파일을 가져옵니다. - -| 클래스 | 주요 메서드 | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `BundleDescriptor` | `header(): Header`; `index(): Index`; `getData(filepath, path): Buffer \| null`; `getDataChecksum(filepath, path): number \| null`; `asyncGetData(...)`; `asyncGetDataChecksum(...)` | -| `Header` | `version(): Version`; `indexEndOffset(): bigint`; `indexSize(): number` | -| `Index` | `entries(): Record`; `getEntry(path): IndexEntry \| null`; `containsPath(path): boolean` | -| `LoadedDescriptor` | `descriptor(): BundleDescriptor`; `getData(path): Promise` (지연 디스크 읽기); `getDataChecksum(path): Promise` | - -`Header`는 번들의 포맷 `version`과 인덱스가 파일 내 어디에 위치하는지를 설명합니다. `Index`는 각 파일 경로를 `IndexEntry`에 매핑합니다. - -```ts -type IndexEntry = { - offset: number; - len: number; // 압축된 길이 - isEmpty: boolean; - contentType: string; - contentLength: number; // 압축 해제된 길이 - headers: Record; -}; -``` - -`LoadedDescriptor`는 디스크의 파일에 대한 참조 카운트 핸들을 보유하며, 이 핸들은 가비지 컬렉션 시 해제됩니다. 따라서 `getData`와 `getDataChecksum`은 아카이브 전체를 버퍼링하지 않고 지연 방식으로 읽습니다. - - - `Version` 열거형에는 `'v1'`이라는 값 하나만 있습니다. `.wvb` 포맷 버전은 `@wvb/node` 패키지 버전과 - 독립적입니다. - - -## 관련 항목 - - - - - diff --git a/content/docs/references/node/bundle.mdx b/content/docs/references/node/bundle.mdx deleted file mode 100644 index 2cde41b..0000000 --- a/content/docs/references/node/bundle.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -title: Bundle -description: The @wvb/node classes for reading, building, and inspecting .wvb archives. ---- - -`@wvb/node` exposes a small set of classes for working with a `.wvb` archive in memory and on disk. `Bundle` and `BundleBuilder` read and write whole bundles, while `BundleDescriptor`, `Header`, `Index`, and `LoadedDescriptor` let you inspect a bundle's structure and pull individual files without loading everything into memory. - -For the full package surface — sources, protocols, the remote client, and the updater — see the [Node API overview](/docs/references/node). For how the bytes are laid out on disk, see [The .wvb bundle format](/docs/guide/bundle-format). - -## Read a bundle and get a file - -Call `readBundle` to parse a `.wvb` file from disk, then `getData` to pull a single entry. `getData` returns a `Buffer`, or `null` when the path is not in the bundle. - -```ts -import { readBundle } from '@wvb/node'; - -const bundle = await readBundle('./dist/app.wvb'); - -const html = bundle.getData('index.html'); -if (html) { - console.log(html.toString('utf8')); -} -``` - -## Bundle and builder - -`Bundle` is a parsed archive held in memory. You get one from `readBundle` (or `readBundleFromBuffer`) or by calling `BundleBuilder.build`. `BundleBuilder` collects entries and produces a `Bundle` you can then write out. - -| Class | Constructor | Key methods | -| --------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Bundle` | Produced by `readBundle` / `BundleBuilder.build` | `descriptor(): BundleDescriptor`; `getData(path: string): Buffer \| null`; `getDataChecksum(path: string): number \| null` | -| `BundleBuilder` | `new BundleBuilder(version?: Version)` | `get version: Version`; `entryPaths(): Array`; `insertEntry(path, data: Buffer, contentType?, headers?: Record): boolean`; `removeEntry(path): boolean`; `containsEntry(path): boolean`; `build(options?: BuildOptions): Bundle` | - - - `getDataChecksum` returns the internal xxHash-32 checksum of a file's bytes, used to detect - corruption inside the archive. It is distinct from a bundle's integrity hash, which uses SHA-2 - (`sha256`, `sha384`, or `sha512`) to verify what was downloaded. - - -## Descriptor, header, and index - -A descriptor exposes a bundle's structure without loading every file into memory. Call `bundle.descriptor()` to get a `BundleDescriptor`, then read its `Header` and `Index`, or pull individual files on demand. - -| Class | Key methods | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `BundleDescriptor` | `header(): Header`; `index(): Index`; `getData(filepath, path): Buffer \| null`; `getDataChecksum(filepath, path): number \| null`; `asyncGetData(...)`; `asyncGetDataChecksum(...)` | -| `Header` | `version(): Version`; `indexEndOffset(): bigint`; `indexSize(): number` | -| `Index` | `entries(): Record`; `getEntry(path): IndexEntry \| null`; `containsPath(path): boolean` | -| `LoadedDescriptor` | `descriptor(): BundleDescriptor`; `getData(path): Promise` (lazy disk read); `getDataChecksum(path): Promise` | - -`Header` describes the bundle's format `version` and where the index sits in the file. `Index` maps each file path to an `IndexEntry`: - -```ts -type IndexEntry = { - offset: number; - len: number; // compressed length - isEmpty: boolean; - contentType: string; - contentLength: number; // uncompressed length - headers: Record; -}; -``` - -`LoadedDescriptor` holds a reference-counted handle to the file on disk that is released on garbage collection, so its `getData` and `getDataChecksum` read lazily rather than buffering the whole archive. - - - The `Version` enum has a single value, `'v1'`. The `.wvb` format version is independent of the - `@wvb/node` package version. - - -## Related - - - - - diff --git a/content/docs/references/node/index.ko.mdx b/content/docs/references/node/index.ko.mdx deleted file mode 100644 index 500ef60..0000000 --- a/content/docs/references/node/index.ko.mdx +++ /dev/null @@ -1,117 +0,0 @@ ---- -title: Node API -description: Node.js에서 Webview Bundle을 읽고, 제공하고, 업데이트하는 N-API 네이티브 애드온인 @wvb/node의 레퍼런스입니다. ---- - -`@wvb/node` 0.1.0은 Webview Bundle의 Node.js 바인딩입니다. N-API 네이티브 애드온(`wvb-node` Rust 크레이트)으로 제공되며, 플랫폼별 사전 빌드 바이너리를 포함합니다. 따라서 Rust 툴체인 없이 설치하고 실행할 수 있습니다. 이 패키지는 번들 리더와 라이터, `BundleSource`, 프로토콜 핸들러, `Remote` 클라이언트, `Updater`를 노출합니다. 모두 `@wvb/electron`과 CLI의 원격 작업을 뒷받침하는 것과 동일한 구성 요소입니다. - -웹뷰 안에서 실행되는 웹 앱은 `@wvb/node`를 직접 호출하지 않습니다. 대신 [`@wvb/bridge`](/docs/references/bridge)를 사용합니다. `@wvb/bridge`의 `source`, `remote`, `updater` 명령어가 여기서 문서화하는 클래스에 대응합니다. 전체 흐름은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. 실험적인 Deno 대응 구현은 [Deno API](/docs/references/deno)를 참고하세요. - - -애드온은 `@wvb/node-` 선택적 의존성 중 하나에서 사전 빌드 바이너리를 해석합니다(macOS, Linux, Windows, Android에 걸친 12개의 NAPI 타깃). 지원되는 Node 엔진은 `>= 12.22.0 < 13`, `>= 14.17.0 < 15`, `>= 15.12.0 < 16`, `>= 16.0.0`입니다. - - -## Install - - - - -```sh -npm install @wvb/node -``` - - - - -```sh -pnpm add @wvb/node -``` - - - - -```sh -yarn add @wvb/node -``` - - - - -## Top-level functions - -이 함수들은 `.wvb` 아카이브를 읽고 씁니다. - -| Function | Signature | Returns | -| ----------------------- | ---------------------------------------------------------------- | ---------------------------- | -| `readBundle` | `readBundle(filepath: string): Promise` | 디스크에서 읽어 파싱한 번들. | -| `readBundleFromBuffer` | `readBundleFromBuffer(buffer: Buffer): Bundle` | 메모리 버퍼에서 파싱한 번들. | -| `writeBundle` | `writeBundle(bundle: Bundle, filepath: string): Promise` | 기록한 바이트 수. | -| `writeBundleIntoBuffer` | `writeBundleIntoBuffer(bundle: Bundle): Buffer` | 직렬화한 `.wvb` 바이트. | - -## API reference - -클래스, 열거형, 옵션 타입은 주제별 페이지로 나누어 정리했습니다. - - - - - - - - - - -## Example - -디스크에서 `.wvb` 아카이브를 읽고 그 안에서 파일 하나를 꺼냅니다. - -```ts -import { readBundle } from '@wvb/node'; - -const bundle = await readBundle('./dist/app.wvb'); - -const html = bundle.getData('index.html'); -if (html) { - console.log(html.toString('utf8')); -} -``` - -## Related - - - - - - diff --git a/content/docs/references/node/index.mdx b/content/docs/references/node/index.mdx deleted file mode 100644 index caf6594..0000000 --- a/content/docs/references/node/index.mdx +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Node API -description: Reference for @wvb/node — the N-API native addon that reads, serves, and updates Webview Bundles from Node.js. ---- - -`@wvb/node` 0.1.0 is the Node.js binding for Webview Bundle. It ships as an N-API native addon (the `wvb-node` Rust crate) with prebuilt platform binaries, so you install and run it without a Rust toolchain. The package exposes the bundle reader and writer, a `BundleSource`, the protocol handlers, a `Remote` client, and an `Updater` — the same building blocks that back `@wvb/electron` and the CLI's remote operations. - -The web app running inside the webview never calls `@wvb/node` directly. It uses [`@wvb/bridge`](/docs/references/bridge), whose `source`, `remote`, and `updater` commands map onto the classes documented here. See [Remote bundles](/docs/guide/remote-bundles) for the end-to-end flow and [Deno API](/docs/references/deno) for the experimental Deno peer. - - -The addon resolves a prebuilt binary from one of the `@wvb/node-` optional dependencies (12 NAPI targets across macOS, Linux, Windows, and Android). Supported Node engines are `>= 12.22.0 < 13`, `>= 14.17.0 < 15`, `>= 15.12.0 < 16`, and `>= 16.0.0`. - - -## Install - - - - -```sh -npm install @wvb/node -``` - - - - -```sh -pnpm add @wvb/node -``` - - - - -```sh -yarn add @wvb/node -``` - - - - -## Top-level functions - -These functions read and write `.wvb` archives. - -| Function | Signature | Returns | -| ----------------------- | ---------------------------------------------------------------- | --------------------------------------- | -| `readBundle` | `readBundle(filepath: string): Promise` | Parsed bundle read from disk. | -| `readBundleFromBuffer` | `readBundleFromBuffer(buffer: Buffer): Bundle` | Parsed bundle from an in-memory buffer. | -| `writeBundle` | `writeBundle(bundle: Bundle, filepath: string): Promise` | Number of bytes written. | -| `writeBundleIntoBuffer` | `writeBundleIntoBuffer(bundle: Bundle): Buffer` | Serialized `.wvb` bytes. | - -## API reference - -The classes, enums, and option types are split across focused pages. - - - - - - - - - - -## Example - -Read a `.wvb` archive from disk and pull a file out of it. - -```ts -import { readBundle } from '@wvb/node'; - -const bundle = await readBundle('./dist/app.wvb'); - -const html = bundle.getData('index.html'); -if (html) { - console.log(html.toString('utf8')); -} -``` - -## Related - - - - - - diff --git a/content/docs/references/node/meta.json b/content/docs/references/node/meta.json deleted file mode 100644 index d5ece34..0000000 --- a/content/docs/references/node/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Node API", - "pages": ["index", "bundle", "source", "protocol", "remote", "updater", "types"] -} diff --git a/content/docs/references/node/meta.ko.json b/content/docs/references/node/meta.ko.json deleted file mode 100644 index d5ece34..0000000 --- a/content/docs/references/node/meta.ko.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Node API", - "pages": ["index", "bundle", "source", "protocol", "remote", "updater", "types"] -} diff --git a/content/docs/references/node/protocol.ko.mdx b/content/docs/references/node/protocol.ko.mdx deleted file mode 100644 index 0a19300..0000000 --- a/content/docs/references/node/protocol.ko.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: 프로토콜 -description: '@wvb/node 프로토콜 핸들러 레퍼런스 — BundleProtocol은 번들 자산을 제공하고, LocalProtocol은 localhost로 프록시합니다.' ---- - -`@wvb/node`는 웹뷰의 요청을 HTTP 방식의 응답으로 바꾸는 두 가지 프로토콜 핸들러를 제공합니다. `BundleProtocol`은 `BundleSource`에서 자산을 제공하고, `LocalProtocol`은 개발 중에 요청을 localhost 서버로 프록시합니다. 두 핸들러 모두 HTTP 메서드, URI, 선택적 헤더를 받아 `HttpResponse`를 반환합니다. 이 스킴들이 웹뷰 플랫폼에 어떻게 들어맞는지는 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. - -두 핸들러는 동일한 호출 형태를 공유합니다. - -```ts -handle( - method: HttpMethod, - uri: string, - headers?: Record, -): Promise -``` - -`HttpMethod`와 `HttpResponse`는 [타입](/docs/references/node/types)에 문서화되어 있습니다. `HttpMethod`는 소문자 문자열 유니온(`'get'`, `'head'` 등)입니다. `HttpResponse`는 `{ status, headers, body }`이며, 여기서 `body`는 `Buffer`입니다. - -## BundleProtocol - -`new BundleProtocol(source: BundleSource)`는 `scheme://bundle_name/path` 요청을 [`BundleSource`](/docs/references/node/source)에서 제공합니다. 번들 내부에서 요청한 파일을 찾아 그 바이트를 반환합니다. - -| Member | Signature | -| ----------- | ---------------------------------------------------------------------------------------------- | -| Constructor | `new BundleProtocol(source: BundleSource)` | -| `handle` | `(method: HttpMethod, uri: string, headers?: Record) => Promise` | - -`BundleProtocol`은 `GET`과 `HEAD` 요청에 응답합니다. `GET`은 파일 내용을 `body`에 담아 반환하고, `HEAD`는 본문 없이 응답 메타데이터만 반환합니다. 요청에 `Range` 헤더가 있으면 핸들러는 상태 `206`으로 부분 응답을 제공합니다. - -## LocalProtocol - -`new LocalProtocol(hosts: Record)`는 `app://host/...` 요청을 localhost URL로 프록시합니다. 각 가상 호스트를 전달할 로컬 오리진에 매핑하세요. 이 방식은 웹뷰가 패킹된 `.wvb` 번들 대신 개발 서버를 가리키는 개발 환경에서 유용합니다. - -| Member | Signature | -| ----------- | ---------------------------------------------------------------------------------------------- | -| Constructor | `new LocalProtocol(hosts: Record)` | -| `handle` | `(method: HttpMethod, uri: string, headers?: Record) => Promise` | - -`LocalProtocol`은 응답을 캐시하며, 업스트림 리소스가 변경되지 않았으면 `304`를 반환합니다. - - - 프로덕션에서 패킹된 번들을 제공할 때는 `BundleProtocol`을 사용하고, 개발 중에 개발 서버로 프록시할 - 때는 `LocalProtocol`을 사용하세요. 두 스킴 중 하나를 플랫폼에 연결하는 방법은 [프로토콜 - 처리](/docs/guide/protocol-handling)를 참고하세요. - - -## 프로토콜로 요청 처리하기 - -`BundleSource`를 만들고 `BundleProtocol`로 감싼 뒤, 웹뷰의 요청에 응답하세요. - -```ts -import { BundleSource, BundleProtocol } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -const protocol = new BundleProtocol(source); - -const res = await protocol.handle('get', 'app://my-app/index.html'); -console.log(res.status); // 200 -console.log(res.headers['content-type']); -res.body; // Buffer -``` - -## 관련 문서 - - - - - - diff --git a/content/docs/references/node/protocol.mdx b/content/docs/references/node/protocol.mdx deleted file mode 100644 index 36ca5a6..0000000 --- a/content/docs/references/node/protocol.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: Protocol -description: Reference for the @wvb/node protocol handlers — BundleProtocol serves bundle assets, LocalProtocol proxies to localhost. ---- - -`@wvb/node` ships two protocol handlers that turn a request from the webview into an HTTP-style response. `BundleProtocol` serves assets out of a `BundleSource`, and `LocalProtocol` proxies requests to a localhost server during development. Both take an HTTP method, a URI, and optional headers, and resolve to an `HttpResponse`. See [Protocol handling](/docs/guide/protocol-handling) for how these schemes fit into a webview platform. - -Both handlers share the same call shape: - -```ts -handle( - method: HttpMethod, - uri: string, - headers?: Record, -): Promise -``` - -`HttpMethod` and `HttpResponse` are documented in [Types](/docs/references/node/types). `HttpMethod` is a lowercase string union (`'get'`, `'head'`, and so on); `HttpResponse` is `{ status, headers, body }`, where `body` is a `Buffer`. - -## BundleProtocol - -`new BundleProtocol(source: BundleSource)` serves `scheme://bundle_name/path` requests from a [`BundleSource`](/docs/references/node/source). It resolves the requested file inside the bundle and returns its bytes. - -| Member | Signature | -| ----------- | ---------------------------------------------------------------------------------------------- | -| Constructor | `new BundleProtocol(source: BundleSource)` | -| `handle` | `(method: HttpMethod, uri: string, headers?: Record) => Promise` | - -`BundleProtocol` answers `GET` and `HEAD` requests. A `GET` returns the file contents in `body`; a `HEAD` returns the response metadata without the body. When the request carries a `Range` header, the handler serves a partial response with status `206`. - -## LocalProtocol - -`new LocalProtocol(hosts: Record)` proxies `app://host/...` requests to a localhost URL. Map each virtual host to the local origin it should forward to. This is useful in development, where the webview points at a dev server instead of a packed `.wvb` bundle. - -| Member | Signature | -| ----------- | ---------------------------------------------------------------------------------------------- | -| Constructor | `new LocalProtocol(hosts: Record)` | -| `handle` | `(method: HttpMethod, uri: string, headers?: Record) => Promise` | - -`LocalProtocol` caches responses and returns `304` when the upstream resource is unchanged. - - - Use `BundleProtocol` to serve packed bundles in production and `LocalProtocol` to proxy a dev - server during development. See [Protocol handling](/docs/guide/protocol-handling) to wire either - scheme into your platform. - - -## Serve requests through a protocol - -Build a `BundleSource`, wrap it in a `BundleProtocol`, and answer requests from the webview. - -```ts -import { BundleSource, BundleProtocol } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -const protocol = new BundleProtocol(source); - -const res = await protocol.handle('get', 'app://my-app/index.html'); -console.log(res.status); // 200 -console.log(res.headers['content-type']); -res.body; // Buffer -``` - -## Related - - - - - - diff --git a/content/docs/references/node/remote.ko.mdx b/content/docs/references/node/remote.ko.mdx deleted file mode 100644 index 13bf3ad..0000000 --- a/content/docs/references/node/remote.ko.mdx +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: Remote -description: 원격에서 번들을 나열하고, 살펴보고, 다운로드하는 @wvb/node HTTP 클라이언트입니다. ---- - -`Remote`는 [번들 원격](/docs/guide/remote)을 위한 `@wvb/node` HTTP 클라이언트입니다. [Remote HTTP Spec](/docs/guide/spec)을 구현한 모든 서버와 통신합니다. 따라서 Node.js에서 사용 가능한 번들을 나열하고, 번들의 메타데이터를 읽고, 번들 바이트를 다운로드할 수 있습니다. [Updater](/docs/references/node/updater)는 `Remote` 위에서 업데이트를 준비하고 활성화합니다. 원격에 더 낮은 수준으로 접근해야 할 때는 `Remote`를 직접 사용하세요. - -## Constructor - -```ts -new Remote(endpoint: string, options?: RemoteOptions) -``` - -원격의 기본 `endpoint`를 전달하세요(예: `https://bundles.example.com`). 클라이언트는 HTTP 규약에 따라 해당 엔드포인트로 요청을 보냅니다. - -```ts -import { Remote } from '@wvb/node'; - -const remote = new Remote('https://bundles.example.com'); -``` - -## Methods - -| Method | Signature | -| ----------------- | --------------------------------------------------------------------- | -| `listBundles` | `(channel?: string): Promise` | -| `getInfo` | `(bundleName: string, channel?: string): Promise` | -| `download` | `(bundleName, channel?): Promise<[RemoteBundleInfo, Bundle, Buffer]>` | -| `downloadVersion` | `(bundleName, version): Promise<[RemoteBundleInfo, Bundle, Buffer]>` | - -`listBundles`는 원격이 제공하는 번들을 나열하며, `channel`로 필터링할 수도 있습니다. `getInfo`는 단일 번들의 현재 메타데이터를 읽습니다. `download`는 번들의 현재 버전을 가져오고, `downloadVersion`은 특정 버전을 가져옵니다. 두 다운로드 메서드 모두 번들 정보, 파싱된 [`Bundle`](/docs/references/node/bundle), 그리고 원본 `.wvb` 바이트를 담은 `Buffer`로 이루어진 튜플을 반환합니다. - -```ts -const [info, bundle, bytes] = await remote.download('my-app', 'stable'); - -console.log(info.name, info.version); -console.log(bundle.getData('index.html')?.toString('utf8')); -console.log(bytes.byteLength); -``` - -## RemoteOptions - -`RemoteOptions`는 HTTP 전송과 선택적인 다운로드 진행률 콜백을 설정합니다. - -```ts -type RemoteOptions = { - http?: HttpOptions; - onDownload?: (data: RemoteOnDownloadData) => void; -}; - -type RemoteOnDownloadData = { - downloadedBytes: number; - totalBytes?: number; - endpoint: string; -}; -``` - -`onDownload`는 바이트가 도착할 때마다 실행됩니다. `totalBytes`는 원격이 콘텐츠 길이를 알려줄 때 설정됩니다. - -```ts -const remote = new Remote('https://bundles.example.com', { - onDownload: ({ downloadedBytes, totalBytes }) => { - if (totalBytes) { - console.log(`${Math.round((downloadedBytes / totalBytes) * 100)}%`); - } - }, -}); -``` - -## HttpOptions - -`HttpOptions`는 내부 HTTP 클라이언트를 조정합니다. 모든 필드는 선택 사항입니다. - -| Field | Type | Description | -| -------------------- | ------------------------ | ------------------------------------------ | -| `defaultHeaders` | `Record` | 모든 요청에 함께 보내는 헤더입니다. | -| `userAgent` | `string` | `User-Agent` 헤더 값입니다. | -| `timeout` | `number` | 전체 요청 타임아웃(밀리초 단위)입니다. | -| `readTimeout` | `number` | 읽기 타임아웃(밀리초 단위)입니다. | -| `connectTimeout` | `number` | 연결 타임아웃(밀리초 단위)입니다. | -| `poolIdleTimeout` | `number` | 풀링된 연결의 유휴 타임아웃입니다. | -| `poolMaxIdlePerHost` | `number` | 호스트당 유지하는 최대 유휴 연결 수입니다. | -| `referer` | `boolean` | `Referer` 헤더를 보낼지 여부입니다. | -| `tcpNodelay` | `boolean` | 소켓에서 `TCP_NODELAY`를 활성화합니다. | -| `hickoryDns` | `boolean` | Hickory DNS 리졸버를 사용합니다. | - - - 기본 요청 타임아웃은 120초입니다. `HttpOptions.timeout`(밀리초)으로 재정의하세요. - - -```ts -const remote = new Remote('https://bundles.example.com', { - http: { - timeout: 30_000, - userAgent: 'my-app/1.0', - }, -}); -``` - -## Result shapes - -`getInfo`, `download`, `downloadVersion`은 `RemoteBundleInfo`를 반환합니다. `listBundles`는 `ListRemoteBundleInfo` 배열을 반환합니다. - -```ts -type ListRemoteBundleInfo = { - name: string; - version: string; -}; - -type RemoteBundleInfo = { - name: string; - version: string; - etag?: string; - integrity?: string; // ":", 예: "sha256:n4bQ…" - signature?: string; // base64 - lastModified?: string; -}; -``` - -`integrity`와 `signature` 필드는 원격이 `Webview-Bundle-Integrity`와 `Webview-Bundle-Signature` 응답 헤더로 알리는 값을 담습니다. `404`(배포되지 않음)나 `403`(금지됨) 같은 실패 응답을 클라이언트가 어떻게 드러내는지는 [Errors](/docs/guide/spec/errors)를 참고하세요. - -## Related - - - - - - diff --git a/content/docs/references/node/remote.mdx b/content/docs/references/node/remote.mdx deleted file mode 100644 index f065f14..0000000 --- a/content/docs/references/node/remote.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Remote -description: The @wvb/node HTTP client that lists, inspects, and downloads bundles from a remote. ---- - -`Remote` is the `@wvb/node` HTTP client for a [bundle remote](/docs/guide/remote). It talks to any server that implements the [Remote HTTP Spec](/docs/guide/spec), so you can list available bundles, read a bundle's metadata, and download bundle bytes from Node.js. The [Updater](/docs/references/node/updater) builds on `Remote` to stage and activate updates; use `Remote` directly when you need lower-level access to the remote. - -## Constructor - -```ts -new Remote(endpoint: string, options?: RemoteOptions) -``` - -Pass the remote's base `endpoint` (for example `https://bundles.example.com`). The client issues requests against that endpoint following the HTTP contract. - -```ts -import { Remote } from '@wvb/node'; - -const remote = new Remote('https://bundles.example.com'); -``` - -## Methods - -| Method | Signature | -| ----------------- | --------------------------------------------------------------------- | -| `listBundles` | `(channel?: string): Promise` | -| `getInfo` | `(bundleName: string, channel?: string): Promise` | -| `download` | `(bundleName, channel?): Promise<[RemoteBundleInfo, Bundle, Buffer]>` | -| `downloadVersion` | `(bundleName, version): Promise<[RemoteBundleInfo, Bundle, Buffer]>` | - -`listBundles` lists the bundles the remote serves, optionally filtered by `channel`. `getInfo` reads a single bundle's current metadata. `download` fetches the current version of a bundle, while `downloadVersion` fetches a specific version. Both download methods resolve to a tuple of the bundle info, the parsed [`Bundle`](/docs/references/node/bundle), and the raw `.wvb` bytes as a `Buffer`. - -```ts -const [info, bundle, bytes] = await remote.download('my-app', 'stable'); - -console.log(info.name, info.version); -console.log(bundle.getData('index.html')?.toString('utf8')); -console.log(bytes.byteLength); -``` - -## RemoteOptions - -`RemoteOptions` configures the HTTP transport and an optional download-progress callback. - -```ts -type RemoteOptions = { - http?: HttpOptions; - onDownload?: (data: RemoteOnDownloadData) => void; -}; - -type RemoteOnDownloadData = { - downloadedBytes: number; - totalBytes?: number; - endpoint: string; -}; -``` - -`onDownload` fires as bytes arrive. `totalBytes` is set when the remote reports a content length. - -```ts -const remote = new Remote('https://bundles.example.com', { - onDownload: ({ downloadedBytes, totalBytes }) => { - if (totalBytes) { - console.log(`${Math.round((downloadedBytes / totalBytes) * 100)}%`); - } - }, -}); -``` - -## HttpOptions - -`HttpOptions` tunes the underlying HTTP client. Every field is optional. - -| Field | Type | Description | -| -------------------- | ------------------------ | ----------------------------------------- | -| `defaultHeaders` | `Record` | Headers sent with every request. | -| `userAgent` | `string` | `User-Agent` header value. | -| `timeout` | `number` | Overall request timeout, in milliseconds. | -| `readTimeout` | `number` | Read timeout, in milliseconds. | -| `connectTimeout` | `number` | Connection timeout, in milliseconds. | -| `poolIdleTimeout` | `number` | Idle timeout for pooled connections. | -| `poolMaxIdlePerHost` | `number` | Maximum idle connections kept per host. | -| `referer` | `boolean` | Whether to send a `Referer` header. | -| `tcpNodelay` | `boolean` | Enable `TCP_NODELAY` on sockets. | -| `hickoryDns` | `boolean` | Use the Hickory DNS resolver. | - - - The default request timeout is 120 seconds. Override it through `HttpOptions.timeout` - (milliseconds). - - -```ts -const remote = new Remote('https://bundles.example.com', { - http: { - timeout: 30_000, - userAgent: 'my-app/1.0', - }, -}); -``` - -## Result shapes - -`getInfo`, `download`, and `downloadVersion` return a `RemoteBundleInfo`. `listBundles` returns an array of `ListRemoteBundleInfo`. - -```ts -type ListRemoteBundleInfo = { - name: string; - version: string; -}; - -type RemoteBundleInfo = { - name: string; - version: string; - etag?: string; - integrity?: string; // ":", e.g. "sha256:n4bQ…" - signature?: string; // base64 - lastModified?: string; -}; -``` - -The `integrity` and `signature` fields carry the values the remote advertises through the `Webview-Bundle-Integrity` and `Webview-Bundle-Signature` response headers. See [Errors](/docs/guide/spec/errors) for how the client surfaces non-success responses such as `404` (not deployed) and `403` (forbidden). - -## Related - - - - - - diff --git a/content/docs/references/node/source.ko.mdx b/content/docs/references/node/source.ko.mdx deleted file mode 100644 index f86573c..0000000 --- a/content/docs/references/node/source.ko.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Source -description: BundleSource는 builtin 디렉터리와 remote 디렉터리에 걸쳐 번들을 해석하며, remote 사본이 우선합니다. ---- - -`BundleSource`는 주어진 번들 이름에 대해 어떤 `.wvb` 파일을 제공할지 결정하는 `@wvb/node`의 클래스입니다. 이 클래스는 두 디렉터리에 걸쳐 번들을 관리합니다. 하나는 앱과 함께 배포된 번들을 담는 builtin 디렉터리이고, 다른 하나는 무선(OTA)으로 다운로드한 번들을 담는 remote 디렉터리입니다. 두 디렉터리가 같은 이름의 번들을 모두 가지고 있으면 remote 사본이 우선합니다. 따라서 OTA 업데이트가 앱과 함께 배포된 버전을 자연스럽게 대체합니다. - -`BundleSource`는 [`BundleProtocol`](/docs/references/node/protocol)이 번들을 제공하는 기반이자 [`Updater`](/docs/references/node/updater)가 다운로드를 준비해 두는 기반입니다. 매니페스트, 채널, 디렉터리 구조가 어떻게 맞물리는지는 [Bundle sources](/docs/guide/bundle-sources)를 참고하세요. 패키지 전체 구성은 [Node API 개요](/docs/references/node)를 참고하세요. - -## Constructor - -```ts -new BundleSource(config: BundleSourceConfig) -``` - -`config`는 [`BundleSourceConfig`](/docs/references/node/types)입니다. 이 설정은 소스가 두 디렉터리를 가리키게 하며, 선택적으로 명시적인 매니페스트 파일 경로도 지정합니다: - -```ts -import { BundleSource } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -``` - - - 번들 이름이 두 디렉터리에서 모두 해석되면 `BundleSource`는 remote 버전을 반환합니다. 이 덕분에 - 다운로드한 업데이트가 네이티브 앱스토어 릴리스 없이도 builtin 번들을 대체합니다. - - -## Methods - -| Method | Signature | -| -------------------------- | ---------------------------------------------------------------- | -| `listBundles` | `(): Promise` | -| `loadVersion` | `(bundleName): Promise` | -| `updateRemoteVersion` | `(bundleName, version): Promise` | -| `resolveFilepath` | `(bundleName): Promise` | -| `getBuiltinBundleFilepath` | `(bundleName, version): string` | -| `getRemoteBundleFilepath` | `(bundleName, version): string` | -| `fetchBundle` | `(bundleName): Promise` | -| `fetchBuiltinBundle` | `(name, version): Promise` | -| `fetchRemoteBundle` | `(name, version): Promise` | -| `fetchDescriptor` | `(bundleName): Promise` | -| `loadBuiltinMetadata` | `(name, version): Promise` | -| `loadRemoteMetadata` | `(name, version): Promise` | -| `writeRemoteBundle` | `(name, version, bundle, metadata): Promise` | -| `loadDescriptor` | `(bundleName): Promise` (single-flight cached) | -| `unloadDescriptor` | `(bundleName): boolean` | -| `removeRemoteBundle` | `(name, version): Promise` | -| `remoteRetainedVersions` | `(name): Promise` (current + previous) | -| `pruneRemoteBundles` | `(name): Promise` | - -`listBundles`는 두 디렉터리에 걸친 모든 번들을 [`ListBundleItem`](/docs/references/node/types)으로 보고합니다. 각 항목은 `type`(`builtin` 또는 `remote`), 이름, 버전, 현재 버전 여부, 매니페스트 메타데이터를 담습니다. `loadVersion`은 이름에 대한 유효 버전을 해석하고, 그 버전이 어느 디렉터리에서 왔는지 알려줍니다. `resolveFilepath`는 remote 우선 규칙에 따라 선택되는 경로를 반환합니다. 반면 `getBuiltinBundleFilepath`와 `getRemoteBundleFilepath`는 특정 버전에 대해 디렉터리별 경로를 계산합니다. - -`fetch*` 메서드는 파싱된 [`Bundle`](/docs/references/node/bundle)을 읽습니다. `fetchBundle`은 우선 규칙을 따르고, `fetchBuiltinBundle`과 `fetchRemoteBundle`은 특정 디렉터리 하나만 대상으로 합니다. `fetchDescriptor`는 모든 파일을 메모리에 로드하지 않고 [`BundleDescriptor`](/docs/references/node/bundle)를 반환합니다. - -`loadDescriptor`는 single-flight 방식으로 `LoadedDescriptor`를 캐시합니다. 따라서 여러 호출자가 동시에 호출해도 하나의 로드를 공유합니다. `unloadDescriptor`는 캐시된 핸들을 해제합니다. remote 디렉터리에 기록하는 경로는 `writeRemoteBundle`입니다. 이 메서드는 다운로드한 번들과 그 [`BundleManifestMetadata`](/docs/references/node/types)를 저장합니다. `remoteRetainedVersions`는 소스가 이름에 대해 보관하는 버전(현재 버전과 이전 버전)을 나열합니다. `removeRemoteBundle`은 특정 버전을 삭제하고, `pruneRemoteBundles`는 더 이상 보관되지 않는 버전을 제거합니다. - - - 상위 수준의 OTA 흐름은 대개 [`Updater`](/docs/references/node/updater)를 통해 이 메서드들을 - 호출합니다. `Updater`는 remote 버전을 준비하고 활성화해 줍니다. 해석, 메타데이터, 정리를 세밀하게 - 제어해야 할 때는 `BundleSource`를 직접 사용하세요. - - -## Related - - - - - - - diff --git a/content/docs/references/node/source.mdx b/content/docs/references/node/source.mdx deleted file mode 100644 index 6074734..0000000 --- a/content/docs/references/node/source.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: Source -description: BundleSource resolves a bundle across the builtin and remote directories, with the remote copy taking priority. ---- - -`BundleSource` is the class in `@wvb/node` that decides which `.wvb` file to serve for a given bundle name. It manages bundles across two directories: a builtin directory for the bundles shipped with the app, and a remote directory for bundles downloaded over the air (OTA). When both directories hold a bundle for the same name, the remote copy wins, so an OTA update transparently supersedes the version that shipped in the app. - -A `BundleSource` is the foundation that [`BundleProtocol`](/docs/references/node/protocol) serves from and that [`Updater`](/docs/references/node/updater) stages downloads into. See [Bundle sources](/docs/guide/bundle-sources) for how the manifest, channels, and directory layout fit together, and the [Node API overview](/docs/references/node) for the full package surface. - -## Constructor - -```ts -new BundleSource(config: BundleSourceConfig) -``` - -`config` is a [`BundleSourceConfig`](/docs/references/node/types). It points the source at the two directories and, optionally, at explicit manifest file paths: - -```ts -import { BundleSource } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); -``` - - - When a bundle name resolves in both directories, `BundleSource` returns the remote version. This - is what lets a downloaded update take over from the builtin bundle without a native app-store - release. - - -## Methods - -| Method | Signature | -| -------------------------- | ---------------------------------------------------------------- | -| `listBundles` | `(): Promise` | -| `loadVersion` | `(bundleName): Promise` | -| `updateRemoteVersion` | `(bundleName, version): Promise` | -| `resolveFilepath` | `(bundleName): Promise` | -| `getBuiltinBundleFilepath` | `(bundleName, version): string` | -| `getRemoteBundleFilepath` | `(bundleName, version): string` | -| `fetchBundle` | `(bundleName): Promise` | -| `fetchBuiltinBundle` | `(name, version): Promise` | -| `fetchRemoteBundle` | `(name, version): Promise` | -| `fetchDescriptor` | `(bundleName): Promise` | -| `loadBuiltinMetadata` | `(name, version): Promise` | -| `loadRemoteMetadata` | `(name, version): Promise` | -| `writeRemoteBundle` | `(name, version, bundle, metadata): Promise` | -| `loadDescriptor` | `(bundleName): Promise` (single-flight cached) | -| `unloadDescriptor` | `(bundleName): boolean` | -| `removeRemoteBundle` | `(name, version): Promise` | -| `remoteRetainedVersions` | `(name): Promise` (current + previous) | -| `pruneRemoteBundles` | `(name): Promise` | - -`listBundles` reports every bundle across both directories as a [`ListBundleItem`](/docs/references/node/types), each carrying its `type` (`builtin` or `remote`), name, version, whether it is current, and its manifest metadata. `loadVersion` resolves the effective version for a name and tells you which directory it came from. `resolveFilepath` returns the path that wins under the remote-priority rule, while `getBuiltinBundleFilepath` and `getRemoteBundleFilepath` compute a directory-specific path for an exact version. - -The `fetch*` methods read a parsed [`Bundle`](/docs/references/node/bundle): `fetchBundle` honors the priority rule, while `fetchBuiltinBundle` and `fetchRemoteBundle` target one directory explicitly. `fetchDescriptor` returns a [`BundleDescriptor`](/docs/references/node/bundle) without loading every file into memory. - -`loadDescriptor` caches a `LoadedDescriptor` with single-flight semantics, so concurrent callers share one load; `unloadDescriptor` drops that cached handle. The remote-directory write path is `writeRemoteBundle`, which persists a downloaded bundle and its [`BundleManifestMetadata`](/docs/references/node/types). `remoteRetainedVersions` lists the versions the source keeps for a name (current plus previous), `removeRemoteBundle` deletes a specific version, and `pruneRemoteBundles` removes versions that are no longer retained. - - - Higher-level OTA flows usually call these methods through - [`Updater`](/docs/references/node/updater), which stages and activates remote versions for you. - Reach for `BundleSource` directly when you need fine-grained control over resolution, metadata, or - cleanup. - - -## Related - - - - - - - diff --git a/content/docs/references/node/types.ko.mdx b/content/docs/references/node/types.ko.mdx deleted file mode 100644 index e92b7dc..0000000 --- a/content/docs/references/node/types.ko.mdx +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: 타입 -description: '@wvb/node API 전반에서 공유되는 열거형, 유니온, 옵션 인터페이스, 결과 타입입니다.' ---- - -[Node API](/docs/references/node)의 클래스와 함수가 반환하고 받아들이는 보조 타입들입니다. reader, [BundleSource](/docs/references/node/source), [프로토콜 핸들러](/docs/references/node/protocol), [Remote](/docs/references/node/remote), [Updater](/docs/references/node/updater) 페이지가 여기서 정의한 형태들을 참조합니다. - -## 열거형과 유니온 - -고정된 값 집합에 이름을 붙이는 문자열 리터럴 유니온입니다. - -```ts -type Version = 'v1'; - -type HttpMethod = - | 'get' - | 'head' - | 'options' - | 'post' - | 'put' - | 'patch' - | 'delete' - | 'trace' - | 'connect'; - -type BundleSourceKind = 'builtin' | 'remote'; -``` - -`Version`은 번들 포맷 버전입니다. 현재는 `v1`만 존재합니다. - -### 무결성 - -```ts -// SHA-2 계열이며, sha384를 권장합니다. -type IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'; - -// optional이 기본값입니다. -type IntegrityPolicy = 'strict' | 'optional' | 'none'; -``` - -무결성은 SHA-2를 사용하며, `":"` 형태로 직렬화됩니다(예: `sha256:n4bQ…`). `strict`는 값이 존재하고 일치해야 합니다. `optional`은 값이 존재할 때만 검증합니다. `none`은 검사를 건너뜁니다. [Remote, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. - -### 서명 - -```ts -type SignatureAlgorithm = - | 'ecdsaSecp256R1' - | 'ecdsaSecp384R1' - | 'ed25519' - | 'rsaPkcs1V15' - | 'rsaPss'; - -type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; -``` - -서명은 무결성 문자열의 바이트에 서명하여 누가 번들을 게시했는지 증명합니다. 따라서 서명 검증에는 무결성 값이 존재해야 합니다. - - - 키 포맷 제약: `pkcs1Der`와 `pkcs1Pem`은 RSA에만, `sec1`은 ECDSA에만, `raw`는 Ed25519에만(32바이트 - 키) 적용됩니다. - - -## 옵션 인터페이스 - -생성자와 메서드에 전달하는 형태입니다. - -```ts -type BundleSourceConfig = { - builtinDir: string; - remoteDir: string; - builtinManifestFilepath?: string; - remoteManifestFilepath?: string; -}; - -type UpdaterOptions = { - channel?: string; - integrityPolicy?: IntegrityPolicy; - integrityChecker?: (data: Uint8Array, integrity: string) => Promise; - signatureVerifier?: - | SignatureVerifierOptions - | ((data: Uint8Array, signature: string) => Promise); -}; - -type SignatureVerifierOptions = { - algorithm: SignatureAlgorithm; - key: { format: VerifyingKeyFormat; data: string | Uint8Array }; -}; -``` - -`UpdaterOptions`는 선언적 `signatureVerifier`(알고리즘과 키)와 커스텀 콜백을 모두 받습니다. `integrityChecker`도 마찬가지로, 기본 SHA-2 검사를 직접 만든 함수로 대체할 수 있습니다. - -## 결과 타입 - -reader, [BundleSource](/docs/references/node/source), [Updater](/docs/references/node/updater)가 반환하는 형태입니다. - -```ts -type BundleManifestMetadata = { - etag?: string; - integrity?: string; // ":", 예: "sha256:n4bQ…" - signature?: string; // base64 - lastModified?: string; -}; - -// BundleSource.loadVersion() -type BundleSourceVersion = { - type: BundleSourceKind; // 'builtin' | 'remote' - version: string; -}; - -// BundleSource.listBundles() -type ListBundleItem = { - type: BundleSourceKind; - name: string; - version: string; - current: boolean; - metadata: BundleManifestMetadata; -}; - -// Updater.getUpdate() -type BundleUpdateInfo = { - name: string; - version: string; // 배포된 버전 - localVersion?: string; // 설치된 버전이 있다면 그 버전 - isAvailable: boolean; // version !== localVersion일 때 true - etag?: string; - integrity?: string; - signature?: string; - lastModified?: string; -}; - -// BundleBuilder.build() — seed는 테스트용입니다 -type BuildOptions = { - header?: { checksumSeed?: number }; - index?: { checksumSeed?: number }; - dataChecksumSeed?: number; -}; - -// 프로토콜 핸들러의 handle()이 반환합니다 -type HttpResponse = { - status: number; - headers: Record; - body: Buffer; -}; -``` - -## 관련 문서 - - - - - - - diff --git a/content/docs/references/node/types.mdx b/content/docs/references/node/types.mdx deleted file mode 100644 index 8739489..0000000 --- a/content/docs/references/node/types.mdx +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: Types -description: Enums, unions, option interfaces, and result types shared across the @wvb/node API. ---- - -These are the supporting types that the [Node API](/docs/references/node) classes and functions return and accept. The reader, [BundleSource](/docs/references/node/source), [protocol handlers](/docs/references/node/protocol), [Remote](/docs/references/node/remote), and [Updater](/docs/references/node/updater) pages reference the shapes defined here. - -## Enums and unions - -String-literal unions that name a fixed set of values. - -```ts -type Version = 'v1'; - -type HttpMethod = - | 'get' - | 'head' - | 'options' - | 'post' - | 'put' - | 'patch' - | 'delete' - | 'trace' - | 'connect'; - -type BundleSourceKind = 'builtin' | 'remote'; -``` - -`Version` is the bundle format version; only `v1` exists today. - -### Integrity - -```ts -// SHA-2 family; sha384 is recommended. -type IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'; - -// optional is the default. -type IntegrityPolicy = 'strict' | 'optional' | 'none'; -``` - -Integrity uses SHA-2, serialized as `":"` (for example `sha256:n4bQ…`). `strict` requires a value to be present and to match, `optional` verifies a value only when present, and `none` skips the check. See [Remote, integrity & signature config](/docs/config/remote). - -### Signature - -```ts -type SignatureAlgorithm = - | 'ecdsaSecp256R1' - | 'ecdsaSecp384R1' - | 'ed25519' - | 'rsaPkcs1V15' - | 'rsaPss'; - -type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; -``` - -A signature proves who published a bundle by signing the integrity string's bytes, so signature verification requires an integrity value to be present. - - - Key-format constraints: `pkcs1Der` and `pkcs1Pem` apply to RSA only, `sec1` to ECDSA only, and - `raw` to Ed25519 only (a 32-byte key). - - -## Option interfaces - -Shapes passed into constructors and methods. - -```ts -type BundleSourceConfig = { - builtinDir: string; - remoteDir: string; - builtinManifestFilepath?: string; - remoteManifestFilepath?: string; -}; - -type UpdaterOptions = { - channel?: string; - integrityPolicy?: IntegrityPolicy; - integrityChecker?: (data: Uint8Array, integrity: string) => Promise; - signatureVerifier?: - | SignatureVerifierOptions - | ((data: Uint8Array, signature: string) => Promise); -}; - -type SignatureVerifierOptions = { - algorithm: SignatureAlgorithm; - key: { format: VerifyingKeyFormat; data: string | Uint8Array }; -}; -``` - -`UpdaterOptions` accepts both a declarative `signatureVerifier` (algorithm plus key) and a custom callback. The same applies to `integrityChecker`, which can replace the built-in SHA-2 check with your own function. - -## Result types - -Shapes returned by the reader, [BundleSource](/docs/references/node/source), and [Updater](/docs/references/node/updater). - -```ts -type BundleManifestMetadata = { - etag?: string; - integrity?: string; // ":", e.g. "sha256:n4bQ…" - signature?: string; // base64 - lastModified?: string; -}; - -// BundleSource.loadVersion() -type BundleSourceVersion = { - type: BundleSourceKind; // 'builtin' | 'remote' - version: string; -}; - -// BundleSource.listBundles() -type ListBundleItem = { - type: BundleSourceKind; - name: string; - version: string; - current: boolean; - metadata: BundleManifestMetadata; -}; - -// Updater.getUpdate() -type BundleUpdateInfo = { - name: string; - version: string; // the deployed version - localVersion?: string; // the installed version, if any - isAvailable: boolean; // true when version !== localVersion - etag?: string; - integrity?: string; - signature?: string; - lastModified?: string; -}; - -// BundleBuilder.build() — seeds are for tests -type BuildOptions = { - header?: { checksumSeed?: number }; - index?: { checksumSeed?: number }; - dataChecksumSeed?: number; -}; - -// Returned by protocol handlers' handle() -type HttpResponse = { - status: number; - headers: Record; - body: Buffer; -}; -``` - -## Related - - - - - - - diff --git a/content/docs/references/node/updater.ko.mdx b/content/docs/references/node/updater.ko.mdx deleted file mode 100644 index dc72e26..0000000 --- a/content/docs/references/node/updater.ko.mdx +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: Updater -description: '@wvb/node의 Updater 클래스 — 원격 서버에서 최신 번들을 확인하고, 무결성과 서명 검증을 거쳐 스테이징한 뒤 활성화합니다.' ---- - -`Updater`는 번들의 무선(OTA) 업데이트를 처리합니다. [`BundleSource`](/docs/references/node/source)와 [`Remote`](/docs/references/node/remote)를 짝지어 동작합니다. 원격 서버에서 더 새로운 버전을 확인하고, 소스의 원격 디렉터리로 다운로드한 뒤 활성화합니다. 확인, 다운로드, 활성화 단계는 각각 별도의 메서드입니다. 따라서 백그라운드에서 업데이트를 스테이징해 두고 나중에 전환할 수 있습니다. 예를 들어 다음 앱 재시작 시점입니다. 전체 흐름은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. - -```ts -import { BundleSource, Remote, Updater } from '@wvb/node'; - -const updater = new Updater(source, remote, options); -``` - -## Constructor - -`new Updater(source: BundleSource, remote: Remote, options?: UpdaterOptions)` - -| Argument | Type | Description | -| --------- | ---------------------------------------------- | ----------------------------------------------------------- | -| `source` | [`BundleSource`](/docs/references/node/source) | 스테이징된 번들과 활성 번들이 디스크에 저장되는 위치입니다. | -| `remote` | [`Remote`](/docs/references/node/remote) | 원격 서버용 HTTP 클라이언트입니다. | -| `options` | `UpdaterOptions` | 채널과 검증 설정입니다(선택). | - -다운로드와 설치는 번들 단위로 직렬화됩니다. 따라서 같은 번들 이름에 대한 동시 호출은 한 번에 하나씩 실행됩니다. - -## Methods - -| Method | Signature | Behavior | -| ------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `listRemotes` | `(): Promise` | 원격 서버에서 사용 가능한 번들을 나열합니다(설정된 `channel`을 따릅니다). | -| `getUpdate` | `(bundleName): Promise` | 원격 서버의 현재 버전을 로컬 버전과 비교합니다. 이것이 확인 단계입니다. | -| `download` | `(bundleName, version?): Promise` | 버전을 원격 디렉터리에 스테이징합니다. 설정된 경우 무결성과 서명을 검증합니다. 활성화하지는 않습니다. | -| `install` | `(bundleName, version): Promise` | 스테이징된 버전을 활성화합니다. 다시 검증하고, 현재 버전을 교체하고, 캐시된 디스크립터를 버리고, 오래된 버전을 정리합니다. | - -`getUpdate`가 확인 단계입니다. `check`라는 이름의 메서드는 없습니다. `download`와 `install`을 분리해 두면 업데이트 단계를 재시작할 수 있습니다. 네트워크를 사용할 수 있을 때 바이트를 스테이징한 뒤, 안전한 시점에 활성화하세요. - -`download`에서 `version` 인자를 생략하면, 업데이터는 설정된 채널의 현재 원격 버전을 가져옵니다. 특정 버전을 스테이징하려면 그 `version`을 직접 전달하세요. - - - `getUpdate`는 `BundleUpdateInfo`를 반환합니다. 원격 서버에 배포된 `version`이 설치된 - `localVersion`과 다르면 `isAvailable`이 `true`입니다. 이 값으로 다운로드와 설치 호출을 제어하세요. - - -## UpdaterOptions - -```ts -type UpdaterOptions = { - channel?: string; - integrityPolicy?: IntegrityPolicy; - integrityChecker?: (data: Uint8Array, integrity: string) => Promise; - signatureVerifier?: - | SignatureVerifierOptions - | ((data: Uint8Array, signature: string) => Promise); -}; - -type SignatureVerifierOptions = { - algorithm: SignatureAlgorithm; - key: { format: VerifyingKeyFormat; data: string | Uint8Array }; -}; -``` - -| Field | Type | Description | -| ------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------ | -| `channel` | `string` | 나열 및 확인 호출 시 원격 서버로 전달되는 릴리스 채널입니다. | -| `integrityPolicy` | `IntegrityPolicy` | `'strict'`, `'optional'`(기본값), 또는 `'none'`입니다. | -| `integrityChecker` | `(data, integrity) => Promise` | 내장 SHA-2 비교를 대체하는 커스텀 무결성 검사입니다. | -| `signatureVerifier` | `SignatureVerifierOptions` or `(data, signature) => Promise` | 선언적인 알고리즘·키 검증기 또는 커스텀 콜백입니다. | - -### Integrity policy - -`integrityPolicy`는 스테이징된 번들의 무결성 해시를 `download` 중에 어떻게 검사하고 `install` 중에 어떻게 다시 검사할지 결정합니다. - -| Policy | Behavior | -| ------------ | ------------------------------------------------------------------------------------ | -| `'strict'` | 무결성 값이 반드시 있어야 하고 일치해야 합니다. 그렇지 않으면 업데이트가 실패합니다. | -| `'optional'` | 원격 서버가 무결성 값을 제공하는 경우 검증합니다(기본값). | -| `'none'` | 무결성 검사를 완전히 건너뜁니다. | - -무결성은 SHA-2(`sha256`, `sha384`, `sha512`)를 사용하며, `":"` 형식으로 직렬화됩니다. 예를 들면 `sha256:n4bQ…`입니다. 자체 로직을 연결하려면, 내장 검사에 의존하는 대신 `integrityChecker` 콜백을 제공하세요. - -### Signature verification - -신뢰하는 키로 업데이트가 서명되도록 요구하려면 `signatureVerifier`를 설정하세요. 알고리즘과 공개 키로 선언적으로 제공하거나, 커스텀 콜백을 제공하세요. - -```ts -type SignatureAlgorithm = - | 'ecdsaSecp256R1' - | 'ecdsaSecp384R1' - | 'ed25519' - | 'rsaPkcs1V15' - | 'rsaPss'; - -type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; -``` - -키 형식 제약: `pkcs1Der`와 `pkcs1Pem`은 RSA에만, `sec1`은 ECDSA에만, `raw`는 Ed25519(32바이트 키)에만 적용됩니다. - - - 서명은 원본 번들 바이트가 아니라 무결성 문자열의 바이트를 대상으로 합니다. 따라서 서명 검증에는 - 무결성 값이 반드시 있어야 합니다. `signatureVerifier`는 무결성 값을 생성하는 무결성 정책과 함께 - 사용하세요. - - -전체 타입 목록은 [열거형, 옵션, 결과 타입](/docs/references/node/types)을, 배포 측 설정은 [Remote, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. - -## Update with integrity and signature checks - -엄격한 무결성 정책과 선언적 Ed25519 서명 검증기로 `Updater`를 구성한 뒤, 확인·다운로드·설치 단계를 순서대로 실행하세요. - -```ts -import { BundleSource, Remote, Updater } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -const remote = new Remote('https://bundles.example.com'); - -const updater = new Updater(source, remote, { - channel: 'stable', - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { - format: 'spkiPem', - data: process.env.WVB_PUBLIC_KEY!, - }, - }, -}); - -const update = await updater.getUpdate('my-app'); -if (update.isAvailable) { - await updater.download('my-app', update.version); - await updater.install('my-app', update.version); -} -``` - -## Related - - - - - - - diff --git a/content/docs/references/node/updater.mdx b/content/docs/references/node/updater.mdx deleted file mode 100644 index 575c05f..0000000 --- a/content/docs/references/node/updater.mdx +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: Updater -description: The @wvb/node Updater class — check a remote for newer bundles, stage them with integrity and signature verification, then activate them. ---- - -`Updater` drives over-the-air (OTA) updates for a bundle. It pairs a [`BundleSource`](/docs/references/node/source) with a [`Remote`](/docs/references/node/remote): it checks the remote for a newer version, downloads it into the source's remote directory, and activates it. The check, download, and activate steps are separate methods, so you can stage an update in the background and switch to it later — for example on the next app restart. See [Remote bundles](/docs/guide/remote-bundles) for the end-to-end flow. - -```ts -import { BundleSource, Remote, Updater } from '@wvb/node'; - -const updater = new Updater(source, remote, options); -``` - -## Constructor - -`new Updater(source: BundleSource, remote: Remote, options?: UpdaterOptions)` - -| Argument | Type | Description | -| --------- | ---------------------------------------------- | --------------------------------------------- | -| `source` | [`BundleSource`](/docs/references/node/source) | Where staged and active bundles live on disk. | -| `remote` | [`Remote`](/docs/references/node/remote) | The HTTP client for the remote. | -| `options` | `UpdaterOptions` | Channel and verification settings (optional). | - -Downloads and installs are serialized per bundle, so concurrent calls for the same bundle name run one at a time. - -## Methods - -| Method | Signature | Behavior | -| ------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `listRemotes` | `(): Promise` | Lists the bundles available on the remote (honoring the configured `channel`). | -| `getUpdate` | `(bundleName): Promise` | Checks the remote's current version against the local one. This is the check step. | -| `download` | `(bundleName, version?): Promise` | Stages a version into the remote directory, verifying integrity and signature if configured. Does not activate it. | -| `install` | `(bundleName, version): Promise` | Activates a staged version: re-verifies it, swaps the current version, drops the cached descriptor, and prunes old ones. | - -`getUpdate` is the check step — there is no method named `check`. Keeping `download` and `install` distinct makes update phases restartable: stage the bytes whenever the network is available, then activate at a safe moment. - -When you omit the `version` argument to `download`, the updater fetches the remote's current version for the configured channel. Pass a specific `version` to stage that exact version instead. - - - `getUpdate` returns a `BundleUpdateInfo` whose `isAvailable` is `true` when the remote's deployed - `version` differs from the installed `localVersion`. Use it to gate the download and install - calls. - - -## UpdaterOptions - -```ts -type UpdaterOptions = { - channel?: string; - integrityPolicy?: IntegrityPolicy; - integrityChecker?: (data: Uint8Array, integrity: string) => Promise; - signatureVerifier?: - | SignatureVerifierOptions - | ((data: Uint8Array, signature: string) => Promise); -}; - -type SignatureVerifierOptions = { - algorithm: SignatureAlgorithm; - key: { format: VerifyingKeyFormat; data: string | Uint8Array }; -}; -``` - -| Field | Type | Description | -| ------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- | -| `channel` | `string` | Release channel forwarded to the remote on list and check calls. | -| `integrityPolicy` | `IntegrityPolicy` | `'strict'`, `'optional'` (default), or `'none'`. | -| `integrityChecker` | `(data, integrity) => Promise` | Custom integrity check that replaces the built-in SHA-2 comparison. | -| `signatureVerifier` | `SignatureVerifierOptions` or `(data, signature) => Promise` | Declarative algorithm-and-key verifier, or a custom callback. | - -### Integrity policy - -The `integrityPolicy` decides how a staged bundle's integrity hash is checked during `download` and re-checked during `install`. - -| Policy | Behavior | -| ------------ | ----------------------------------------------------------------------- | -| `'strict'` | An integrity value must be present and must match, or the update fails. | -| `'optional'` | Verify the integrity value if the remote provides one (default). | -| `'none'` | Skip integrity checking entirely. | - -Integrity uses SHA-2 (`sha256`, `sha384`, or `sha512`), serialized as `":"`, for example `sha256:n4bQ…`. To plug in your own logic, supply an `integrityChecker` callback instead of relying on the built-in check. - -### Signature verification - -Set `signatureVerifier` to require that an update is signed by a key you trust. Provide it declaratively with an algorithm and a public key, or supply a custom callback. - -```ts -type SignatureAlgorithm = - | 'ecdsaSecp256R1' - | 'ecdsaSecp384R1' - | 'ed25519' - | 'rsaPkcs1V15' - | 'rsaPss'; - -type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec1' | 'raw'; -``` - -Key-format constraints: `pkcs1Der` and `pkcs1Pem` apply to RSA only, `sec1` to ECDSA only, and `raw` to Ed25519 only (a 32-byte key). - - - A signature covers the integrity string's bytes, not the raw bundle bytes. Signature verification - therefore requires an integrity value to be present — pair `signatureVerifier` with an integrity - policy that produces one. - - -See [enums, options, and result types](/docs/references/node/types) for the full type listing and [Remote, integrity & signature config](/docs/config/remote) for the publish side. - -## Update with integrity and signature checks - -Wire an `Updater` with a strict integrity policy and a declarative Ed25519 signature verifier, then run the check, download, and install steps in order. - -```ts -import { BundleSource, Remote, Updater } from '@wvb/node'; - -const source = new BundleSource({ - builtinDir: './bundles/builtin', - remoteDir: './bundles/remote', -}); - -const remote = new Remote('https://bundles.example.com'); - -const updater = new Updater(source, remote, { - channel: 'stable', - integrityPolicy: 'strict', - signatureVerifier: { - algorithm: 'ed25519', - key: { - format: 'spkiPem', - data: process.env.WVB_PUBLIC_KEY!, - }, - }, -}); - -const update = await updater.getUpdate('my-app'); -if (update.isAvailable) { - await updater.download('my-app', update.version); - await updater.install('my-app', update.version); -} -``` - -## Related - - - - - - - diff --git a/package.json b/package.json index f40287e..396ddab 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "lint:fix": "oxlint --type-aware --fix", "format": "oxfmt", "gen:cf-types": "wrangler types", - "genW:fumadocs": "fumadocs-mdx" + "gen:fumadocs": "fumadocs-mdx", + "gen:references": "node scripts/generate-references.ts" }, "dependencies": { "@base-ui/react": "1.6.0", @@ -49,6 +50,7 @@ "oxlint": "1.72.0", "oxlint-tsgolint": "0.24.0", "tailwindcss": "4.3.2", + "typedoc": "0.28.19", "typescript": "6.0.3", "vite": "8.1.3", "wrangler": "4.107.0" diff --git a/public/diagrams/wvb-data-layout.svg b/public/diagrams/wvb-data-layout.svg new file mode 100644 index 0000000..246aa83 --- /dev/null +++ b/public/diagrams/wvb-data-layout.svg @@ -0,0 +1,37 @@ + + The data section stores each entry's compressed bytes back to back; the index records the offset and length of each. + + + + + + + 0 + 768 + 1420 + + + + + + + /index.js + /index.html + … + Checksum + diff --git a/public/diagrams/wvb-index-lookup.svg b/public/diagrams/wvb-index-lookup.svg new file mode 100644 index 0000000..4cedf4e --- /dev/null +++ b/public/diagrams/wvb-index-lookup.svg @@ -0,0 +1,49 @@ + + A request path is looked up in the index to get an offset and length, then the matching bytes are read from the data section. + + + + + + + + + + Request + GET /index.js + + + + look up + + + + Index + "/index.js" + → offset, len + + + + read + + + + Data + bytes[offset, len) + lz4-decompress + diff --git a/public/diagrams/wvb-structure.svg b/public/diagrams/wvb-structure.svg new file mode 100644 index 0000000..0368947 --- /dev/null +++ b/public/diagrams/wvb-structure.svg @@ -0,0 +1,64 @@ + + The .wvb file layout: a Header, Index, and Data section, each ending with an xxHash-32 checksum. + + + + Header (17 B) + Index + Data + + + + + + + + + + + Magic + Version + Idx Size + Checksum + 8 B + 1 B + 4 B + 4 B + + + + + + + Index + Checksum + index size + 4 B + + + + + + + Data + Checksum + variable + 4 B + + diff --git a/scripts/generate-references.ts b/scripts/generate-references.ts new file mode 100644 index 0000000..4448420 --- /dev/null +++ b/scripts/generate-references.ts @@ -0,0 +1,136 @@ +// Generates the JavaScript API reference pages (content/docs/references/api/*) +// from the webview-bundle monorepo's TypeScript sources + JSDoc, using TypeDoc. +// +// Usage: +// node scripts/generate-references.ts [--repo ] +// +// The repo path defaults to $WVB_REPO or ../webview-bundle. The monorepo must +// have its dependencies installed (yarn install) so imports resolve. +// +// Layout: one folder per package (content/docs/references/api//), with one +// MDX page per exported symbol plus an index. Each symbol page splits the API +// into readable sections — Signature, Parameters, Returns, Examples — and uses +// Fumadocs' for property and parameter tables. Output is committed +// to git; CI regenerates it on release (see .github/workflows/references.yaml). +// +// The generator is split by responsibility under scripts/references/: +// discovery — find the @wvb packages (npm + Deno) to document +// typedoc — convert one package's sources to a TypeDoc project +// reflection — read type/signature strings and JSDoc tags off reflections +// mdx — escape text and render comment parts to MDX / plain text +// render — per-kind page-body renderers + +// pages — group exports into pages, write index/meta files +// i18n — sidebar label translations (extensible per locale) +import { existsSync } from 'node:fs'; +import { mkdir, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; +import { ReflectionKind } from 'typedoc'; +import { discoverPackages } from './references/discovery.ts'; +import { LOCALES, metaFileName } from './references/i18n.ts'; +import { + apiMeta, + exportPage, + groupExports, + indexPage, + isRemote, + packageMeta, + remotesMeta, +} from './references/pages.ts'; +import { convertPackage } from './references/typedoc.ts'; +import type { PackageTarget, RenderCtx } from './references/types.ts'; + +const WEBSITE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const OUT_DIR = path.join(WEBSITE_ROOT, 'content/docs/references/api'); +// Remote packages render as a collapsible "Remotes" group under this subfolder. +const REMOTES_DIR = path.join(OUT_DIR, 'remotes'); + +function repoPathFromArgs(): string { + const idx = process.argv.indexOf('--repo'); + const fromArgs = idx !== -1 ? process.argv[idx + 1] : undefined; + const raw = fromArgs ?? process.env.WVB_REPO ?? '../webview-bundle'; + if (raw == null || raw === '') throw new Error('Missing --repo '); + return path.resolve(WEBSITE_ROOT, raw); +} + +async function generatePackage(pkg: PackageTarget, parentDir: string): Promise { + const project = await convertPackage(pkg); + const children: any[] = (project.children ?? []).filter( + (c: any) => c.kind !== ReflectionKind.Module && c.kind !== ReflectionKind.Reference + ); + const groups = groupExports(children); + const nameToSlug = new Map(groups.map(g => [g.name, g.slug])); + + // Absolute page URL for this package, so generated cross-links and cards route + // client-side (relative `./` hrefs are not localized nor matched by the router). + const urlBase = `/docs/references/api${isRemote(pkg) ? '/remotes' : ''}/${pkg.slug}`; + const ctx: RenderCtx = { + link: label => { + const slug = nameToSlug.get(label); + return slug == null ? null : `${urlBase}/${slug}`; + }, + sourceUrl: reflection => reflection?.sources?.[0]?.url ?? null, + emittedSources: new Set(), + }; + + const pkgDir = path.join(parentDir, pkg.slug); + await mkdir(pkgDir, { recursive: true }); + await writeFile(path.join(pkgDir, 'index.mdx'), indexPage(pkg, groups, urlBase)); + for (const group of groups) { + await writeFile(path.join(pkgDir, `${group.slug}.mdx`), exportPage(group, ctx)); + } + for (const locale of LOCALES) { + await writeFile(path.join(pkgDir, metaFileName(locale)), packageMeta(pkg, groups, locale)); + } + return groups.map(g => g.slug); +} + +async function main(): Promise { + const repo = repoPathFromArgs(); + if (!existsSync(path.join(repo, 'packages'))) { + throw new Error(`Not a webview-bundle checkout: ${repo}`); + } + const packages = await discoverPackages(repo); + console.log(`Generating API references for ${packages.length} packages from ${repo}`); + + await rm(OUT_DIR, { recursive: true, force: true }); + await mkdir(OUT_DIR, { recursive: true }); + + const failed: string[] = []; + const ok: PackageTarget[] = []; + for (const pkg of packages) { + try { + const parentDir = isRemote(pkg) ? REMOTES_DIR : OUT_DIR; + const slugs = await generatePackage(pkg, parentDir); + ok.push(pkg); + console.log(` ok ${pkg.name} (${slugs.length} exports)`); + } catch (error) { + failed.push(pkg.name); + console.error( + ` FAIL ${pkg.name}: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + + // The `api` folder is flattened into the sidebar (`...api`), so it has no + // index page. Core packages sit directly under it; remotes get a collapsible + // `api/remotes/` subfolder with its own localized "Remotes" title. + const core = ok.filter(p => !isRemote(p)); + const remotes = ok.filter(isRemote); + for (const locale of LOCALES) { + await writeFile(path.join(OUT_DIR, metaFileName(locale)), apiMeta(core, remotes.length > 0)); + } + if (remotes.length > 0) { + await mkdir(REMOTES_DIR, { recursive: true }); + for (const locale of LOCALES) { + await writeFile(path.join(REMOTES_DIR, metaFileName(locale)), remotesMeta(remotes, locale)); + } + } + + if (failed.length > 0) { + throw new Error(`Failed packages: ${failed.join(', ')}`); + } +} + +await main(); diff --git a/scripts/references/discovery.ts b/scripts/references/discovery.ts new file mode 100644 index 0000000..4b06368 --- /dev/null +++ b/scripts/references/discovery.ts @@ -0,0 +1,122 @@ +// Discovers the public @wvb packages to document from a webview-bundle checkout: +// npm packages (package.json) under packages/** and packages/remote/**, plus the +// Deno packages (deno.json). +import { existsSync } from 'node:fs'; +import { readdir, readFile } from 'node:fs/promises'; +import path from 'node:path'; +import type { PackageTarget } from './types.ts'; + +interface PackageManifest { + name?: string; + private?: boolean; + description?: string; + types?: string; +} + +// Subset of deno.json we read for JSR-published packages (@wvb/deno*). +interface DenoManifest { + name?: string; + description?: string; + exports?: string | Record; +} + +const PACKAGE_ROOTS = ['packages', 'packages/remote']; + +// Stable sidebar order: core packages first, then the remotes alphabetically. +const ORDER_HINT = [ + 'node', + 'bridge', + 'cli', + 'config', + 'electron', + 'electron-builder', + 'electron-forge', + 'deno', + 'deno-desktop', +]; + +async function readManifest(file: string): Promise { + return JSON.parse(await readFile(file, 'utf8')) as PackageManifest; +} + +// Resolve the main entry (`.`) from a deno.json `exports` field. +function denoMainExport(manifest: DenoManifest): string | null { + const exports = manifest.exports; + if (exports == null) return null; + if (typeof exports === 'string') return exports; + return exports['.'] ?? null; +} + +// Build a PackageTarget from a directory that ships a package.json (npm) or a +// deno.json (JSR). Returns null when the directory is not a public @wvb package. +async function targetFromDir(repo: string, dir: string): Promise { + const pkgFile = path.join(dir, 'package.json'); + if (existsSync(pkgFile)) { + const pkg = await readManifest(pkgFile); + if (pkg.private === true || pkg.name == null || !pkg.name.startsWith('@wvb/')) return null; + // Prefer TypeScript source (full JSDoc, no build needed); fall back to the + // committed .d.ts entry (e.g. @wvb/node's napi-generated index.d.ts). + const srcEntry = path.join(dir, 'src/index.ts'); + const typesEntry = pkg.types != null ? path.join(dir, pkg.types) : null; + const entry = existsSync(srcEntry) ? srcEntry : typesEntry; + if (entry == null || !existsSync(entry)) { + console.warn(`[skip] ${pkg.name}: no src/index.ts or types entry`); + return null; + } + return { + name: pkg.name, + slug: pkg.name.slice('@wvb/'.length), + description: pkg.description ?? '', + dir, + entry, + tsconfig: existsSync(path.join(dir, 'tsconfig.json')) + ? path.join(dir, 'tsconfig.json') + : path.join(repo, 'tsconfig.json'), + }; + } + + // Deno package: no package.json, but a deno.json with JSR name + exports. + // TypeDoc can still read the TypeScript source (with a synthetic tsconfig — + // see convertPackage), so these are documented here rather than on jsr.io. + const denoFile = path.join(dir, 'deno.json'); + if (existsSync(denoFile)) { + const manifest = JSON.parse(await readFile(denoFile, 'utf8')) as DenoManifest; + if (manifest.name == null || !manifest.name.startsWith('@wvb/')) return null; + const mainExport = denoMainExport(manifest); + const entry = mainExport == null ? null : path.join(dir, mainExport); + if (entry == null || !existsSync(entry)) { + console.warn(`[skip] ${manifest.name}: deno.json has no resolvable main export`); + return null; + } + return { + name: manifest.name, + slug: manifest.name.slice('@wvb/'.length), + description: manifest.description ?? '', + dir, + entry, + tsconfig: null, // convertPackage writes a synthetic one + }; + } + return null; +} + +// Discover public packages: packages/*/(package.json|deno.json) + +// packages/remote/*/package.json. +export async function discoverPackages(repo: string): Promise { + const found: PackageTarget[] = []; + for (const root of PACKAGE_ROOTS) { + const base = path.join(repo, root); + if (!existsSync(base)) continue; + for (const dirent of await readdir(base, { withFileTypes: true })) { + if (!dirent.isDirectory()) continue; + const target = await targetFromDir(repo, path.join(base, dirent.name)); + if (target != null) found.push(target); + } + } + return found.sort((a, b) => { + const ai = ORDER_HINT.indexOf(a.slug); + const bi = ORDER_HINT.indexOf(b.slug); + if (ai !== -1 || bi !== -1) return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi); + return a.slug.localeCompare(b.slug); + }); +} diff --git a/scripts/references/i18n.ts b/scripts/references/i18n.ts new file mode 100644 index 0000000..8c263b7 --- /dev/null +++ b/scripts/references/i18n.ts @@ -0,0 +1,62 @@ +// Localization for the generated reference docs. +// +// The generated API pages themselves are English (code and type signatures are +// language-neutral); what gets translated is the *sidebar structure* — the +// `---separator---` group labels in each `meta.json` / `meta..json`. +// +// To add a language: add its code to `Locale` + `LOCALES`, then fill in the +// column for it in `LABELS`. Nothing else in the generator needs to change. +// Keep this list in sync with the site's own i18n (`src/lib/i18n.ts`). +import { ReflectionKind } from 'typedoc'; + +export type Locale = 'en' | 'ko'; + +// Default locale → `meta.json`; others → `meta..json` (Fumadocs convention). +export const DEFAULT_LOCALE: Locale = 'en'; +export const LOCALES: Locale[] = ['en', 'ko']; + +// Every translatable label the generator emits into docs. +type LabelKey = + | 'classes' + | 'functions' + | 'interfaces' + | 'enumerations' + | 'typeAliases' + | 'variables' + | 'remotes'; + +const LABELS: Record> = { + classes: { en: 'Classes', ko: '클래스' }, + functions: { en: 'Functions', ko: '함수' }, + interfaces: { en: 'Interfaces', ko: '인터페이스' }, + enumerations: { en: 'Enumerations', ko: '열거형' }, + typeAliases: { en: 'Type Aliases', ko: '타입' }, + variables: { en: 'Variables', ko: '변수' }, + remotes: { en: 'Remotes', ko: '리모트' }, +}; + +export function t(key: LabelKey, locale: Locale): string { + return LABELS[key][locale]; +} + +// Export kinds in the order they appear in a package index and sidebar, each +// tied to its translatable label. +export const KIND_ORDER: { kind: number; label: LabelKey }[] = [ + { kind: ReflectionKind.Class, label: 'classes' }, + { kind: ReflectionKind.Function, label: 'functions' }, + { kind: ReflectionKind.Interface, label: 'interfaces' }, + { kind: ReflectionKind.Enum, label: 'enumerations' }, + { kind: ReflectionKind.TypeAlias, label: 'typeAliases' }, + { kind: ReflectionKind.Variable, label: 'variables' }, +]; + +// Sort rank for a reflection kind (unknown kinds sort last). +export function kindRank(kind: number): number { + const i = KIND_ORDER.findIndex(g => g.kind === kind); + return i === -1 ? KIND_ORDER.length : i; +} + +// Fumadocs meta filename for a locale. +export function metaFileName(locale: Locale): string { + return locale === DEFAULT_LOCALE ? 'meta.json' : `meta.${locale}.json`; +} diff --git a/scripts/references/mdx.ts b/scripts/references/mdx.ts new file mode 100644 index 0000000..0c57c1d --- /dev/null +++ b/scripts/references/mdx.ts @@ -0,0 +1,119 @@ +// MDX text helpers: escaping for MDX/JSX/YAML contexts, frontmatter assembly, +// and rendering TypeDoc comment "display parts" into MDX prose or plain text. +import type { LinkResolver } from './types.ts'; + +// Escape the characters MDX treats as JSX/expression syntax, but leave inline +// code spans (`...`) untouched so type-y text like `Promise` renders as-is. +// Entities render identically, so this is visually lossless. +export function escapeMdxText(input: string): string { + return input + .split(/(`[^`]*`)/g) + .map((part, i) => { + if (i % 2 === 1) return part; // inside a `...` span + return part + .replace(/[{}]/g, m => `\\${m}`) + .replace(//g, '>'); + }) + .join(''); +} + +// Single-quoted YAML scalar for frontmatter. +export function yamlQuote(value: string): string { + return `'${value.replace(/'/g, "''")}'`; +} + +// Escape a plain string for use inside a double-quoted JSX/HTML attribute. +export function htmlAttr(value: string): string { + return value + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>') + .replace(/[{}]/g, ''); +} + +// A short, plain-text description for frontmatter/cards: first sentence, no +// markdown, no brackets. A summary that is only a bullet list has no single +// sentence to lift, so fall back rather than quoting one list item. +export function frontmatterDescription(text: string, fallback: string): string { + const firstLine = text.trimStart().split(/\r?\n/)[0] ?? ''; + if (/^[-*]\s/.test(firstLine)) return fallback; + const flat = text + .replace(/`+/g, '') + .replace(/\[|\]/g, '') // drop stray markdown reference brackets + .replace(/^[\s>*-]+/, '') + .replace(/\s+/g, ' ') + .trim(); + if (flat === '') return fallback; + const firstSentence = flat.split(/(?<=[.!?])\s/)[0] ?? flat; + const clipped = firstSentence.length > 160 ? `${firstSentence.slice(0, 157)}…` : firstSentence; + return clipped; +} + +export function frontmatter(title: string, description: string): string { + return [ + '---', + `title: ${yamlQuote(title)}`, + `description: ${yamlQuote(description)}`, + '---', + '', + ].join('\n'); +} + +// Resolve Markdown shortcut references — `[`Name`]` / `[Name]` with no +// destination — the same way `{@link Name}` is handled: link to Name's page +// when it is an export, else drop the brackets and keep inline code. Real +// links `[text](url)` and reference links `[text][id]` are left untouched. +export function resolveShortcutRefs(text: string, link: LinkResolver): string { + return text.replace(/\[`?([A-Za-z_$][\w.$]*)`?\](?!\(|\[|:)/g, (_m, name) => { + const href = link(name); + return href == null ? `\`${name}\`` : `[\`${name}\`](${href})`; + }); +} + +// Render a TypeDoc comment `summary` (array of display parts) to MDX prose, +// turning `{@link X}` into a link when X is an export on the same package. +export function renderParts(parts: any[] | undefined, link: LinkResolver): string { + if (parts == null) return ''; + const joined = parts + .map(part => { + if (part.kind === 'inline-tag' && (part.tag === '@link' || part.tag === '@linkcode')) { + const label = String(part.text ?? '').trim(); + const href = link(label); + return href == null ? `\`${label}\`` : `[\`${label}\`](${href})`; + } + if (part.kind === 'code') return part.text; // already fenced/backticked + return escapeMdxText(String(part.text ?? '')); + }) + .join(''); + // Resolve shortcut refs on the joined string — TypeDoc splits `[`X`]` into a + // `[` text part, a `X` code part, and a `]` text part, so this only matches + // once the parts are back together. + return resolveShortcutRefs(joined, link).trim(); +} + +// Plain text of a comment summary (no MDX escaping) — for TypeTable field +// values, which render as React text nodes, and for frontmatter. +export function plainParts(parts: any[] | undefined): string { + if (parts == null) return ''; + return parts + .map(part => (part.kind === 'inline-tag' ? String(part.text ?? '') : String(part.text ?? ''))) + .join('') + .trim(); +} + +// Clean single-line text for string fields. TypeTable renders +// description/returns/default/parameter text as raw React children (no Markdown +// pass), so strip inline code backticks and reference brackets, and flatten +// bullet lists and newlines into one readable line. +export function tableText(parts: any[] | undefined): string { + return plainParts(parts) + .replace(/\[`?([A-Za-z_$][\w.$]*)`?\](?!\(|\[|:)/g, '$1') // shortcut refs → bare name + .replace(/`+/g, '') + .replace(/\r?\n[ \t]*[-*]\s+/g, '; ') // bullet items → "; " + .replace(/^[ \t]*[-*]\s+/, '') // leading bullet + .replace(/\r?\n+/g, ' ') + .replace(/[ \t]+/g, ' ') + .trim(); +} diff --git a/scripts/references/pages.ts b/scripts/references/pages.ts new file mode 100644 index 0000000..ca1126d --- /dev/null +++ b/scripts/references/pages.ts @@ -0,0 +1,150 @@ +// Assembles per-export pages, per-package index pages, the top-level API index, +// and the Fumadocs `meta.json` sidebar files. Groups a package's exports into +// one page per name, ordered and slugged deterministically. +import { ReflectionKind } from 'typedoc'; +import { DEFAULT_LOCALE, KIND_ORDER, kindRank, type Locale, t } from './i18n.ts'; +import { frontmatter, frontmatterDescription, htmlAttr, plainParts } from './mdx.ts'; +import { commentOf } from './reflection.ts'; +import { renderReflection } from './render.ts'; +import type { ExportGroup, PackageTarget, RenderCtx } from './types.ts'; + +export const AUTOGEN_NOTE = + '{/* This page is auto-generated from TypeScript + JSDoc by scripts/generate-references.ts — do not edit by hand. */}'; + +// --------------------------------------------------------------------------- +// Grouping top-level exports into pages (one page per exported name) +// --------------------------------------------------------------------------- + +function slugify(name: string): string { + return name + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +export function groupExports(children: any[]): ExportGroup[] { + const byName = new Map(); + for (const child of children) { + const list = byName.get(child.name) ?? []; + list.push(child); + byName.set(child.name, list); + } + const groups: ExportGroup[] = [...byName.entries()].map(([name, reflections]) => { + reflections.sort((a, b) => kindRank(a.kind) - kindRank(b.kind)); + const primary = reflections[0]; + const summarySource = + reflections.find(r => plainParts(commentOf(r)?.summary) !== '') ?? primary; + return { + name, + slug: '', + reflections, + primaryKind: primary.kind, + summary: plainParts(commentOf(summarySource)?.summary), + }; + }); + // Sort into final order first, then assign slugs — so a type/value name + // collision (e.g. `WebviewBundle` class vs `webviewBundle` fn) resolves to a + // deterministic, kind-tagged slug regardless of export source order. + groups.sort((a, b) => { + const r = kindRank(a.primaryKind) - kindRank(b.primaryKind); + return r !== 0 ? r : a.name.localeCompare(b.name); + }); + const usedSlugs = new Set(); + for (const g of groups) { + let slug = slugify(g.name); + const kindTag = slugify(ReflectionKind[g.primaryKind] ?? 'export'); + if (slug === '' || slug === 'index') slug = `${slug}-${kindTag}`; + if (usedSlugs.has(slug)) slug = `${slug}-${kindTag}`; + let unique = slug; + let n = 2; + while (usedSlugs.has(unique)) unique = `${slug}-${n++}`; + usedSlugs.add(unique); + g.slug = unique; + } + return groups; +} + +// --------------------------------------------------------------------------- +// Page writers +// --------------------------------------------------------------------------- + +export function exportPage(group: ExportGroup, ctx: RenderCtx): string { + ctx.emittedSources.clear(); + const description = frontmatterDescription(group.summary, `${group.name} — API reference.`); + // In the enum pattern (a `const` plus a `type` of the same name), show the + // value's members first and the derived type alias last. Distinct section + // headings (## Properties, ## Type) already tell the two parts apart, so a + // horizontal rule is enough of a divider. + const ordered = [...group.reflections].sort( + (a, b) => + (a.kind === ReflectionKind.TypeAlias ? 1 : 0) - (b.kind === ReflectionKind.TypeAlias ? 1 : 0) + ); + const body = ordered.map(r => renderReflection(r, ctx).trim()).join('\n\n---\n\n'); + return [frontmatter(group.name, description), AUTOGEN_NOTE, '', body.trim(), ''].join('\n'); +} + +// `urlBase` is the package's absolute page URL (e.g. /docs/references/api/node); +// cards link to `${urlBase}/${export}` so client-side routing can resolve them +// (relative `./` hrefs are not localized/routed). +export function indexPage(pkg: PackageTarget, groups: ExportGroup[], urlBase: string): string { + const description = pkg.description === '' ? `API reference for ${pkg.name}.` : pkg.description; + let body = ''; + for (const kg of KIND_ORDER) { + const inKind = groups.filter(g => g.primaryKind === kg.kind); + if (inKind.length === 0) continue; + body += `## ${t(kg.label, DEFAULT_LOCALE)}\n\n\n`; + for (const g of inKind) { + const cardDesc = htmlAttr(frontmatterDescription(g.summary, '')); + body += ` \n`; + } + body += `\n\n`; + } + return [frontmatter(pkg.name, description), AUTOGEN_NOTE, '', body.trim(), ''].join('\n'); +} + +// Sidebar meta for one package folder, grouped by kind with separators. +// `index` is intentionally NOT listed: Fumadocs auto-detects `index.mdx` as the +// folder's own landing page (the clickable folder header), so listing it would +// duplicate it as a redundant child item of the same name. +export function packageMeta(pkg: PackageTarget, groups: ExportGroup[], locale: Locale): string { + const pages: string[] = []; + for (const kg of KIND_ORDER) { + const inKind = groups.filter(g => g.primaryKind === kg.kind); + if (inKind.length === 0) continue; + pages.push(`---${t(kg.label, locale)}---`); + for (const g of inKind) pages.push(g.slug); + } + return `${JSON.stringify({ title: pkg.name, pages }, null, 2)}\n`; +} + +// --------------------------------------------------------------------------- +// Top-level API section +// +// The `api` folder is flattened into the References sidebar via `...api` in +// content/docs/references/meta.json, so its own title is never shown and it +// needs no index page: core packages appear directly under "JavaScript", and +// the remotes live in an `api/remotes/` subfolder that renders as a collapsible +// "Remotes" group. +// --------------------------------------------------------------------------- + +// Remote packages (`@wvb/remote-*`) are grouped under `api/remotes/`; everything +// else is a top-level core package. +export function isRemote(pkg: PackageTarget): boolean { + return pkg.slug.startsWith('remote-'); +} + +// The `api` folder meta: core packages, then the `remotes` subfolder. Locale +// independent — the entries are path refs, and the folder title is not rendered +// (the folder is extracted into its parent). +export function apiMeta(core: PackageTarget[], hasRemotes: boolean): string { + const pages = core.map(p => p.slug); + if (hasRemotes) pages.push('remotes'); + return `${JSON.stringify({ title: 'API', pages }, null, 2)}\n`; +} + +// The `api/remotes` folder meta: a collapsible group titled "Remotes" per locale. +export function remotesMeta(remotes: PackageTarget[], locale: Locale): string { + const pages = remotes.map(p => p.slug); + return `${JSON.stringify({ title: t('remotes', locale), pages }, null, 2)}\n`; +} diff --git a/scripts/references/reflection.ts b/scripts/references/reflection.ts new file mode 100644 index 0000000..2524e52 --- /dev/null +++ b/scripts/references/reflection.ts @@ -0,0 +1,139 @@ +// Accessors over the TypeDoc in-memory reflection model: type/signature +// stringification and JSDoc-tag extraction. No MDX concerns here — these return +// plain strings, arrays, and booleans. +import { ReflectionKind } from 'typedoc'; + +// --------------------------------------------------------------------------- +// Type / signature stringification (reuses TypeDoc's Type#toString) +// --------------------------------------------------------------------------- + +export function typeStr(type: any): string { + const s = type?.toString?.(); + return s == null || s === '' ? 'unknown' : s; +} + +// Collapse top-level object-type literals in a very long type string to `{ … }`. +// Pathological types — e.g. a Deno FFI `DynamicLibrary<{ …40 symbols… }>` — would +// otherwise render as a multi-line wall (and reflow unstably under formatting). +// Full detail stays available via the "View source" link. +export function collapseLongType(s: string, max = 200): string { + if (s.length <= max) return s; + let out = ''; + let depth = 0; + for (const c of s) { + if (c === '{') { + if (depth === 0) out += '{ … }'; + depth++; + } else if (c === '}') { + if (depth > 0) depth--; + } else if (depth === 0) { + out += c; + } + } + return out.length < s.length ? out : s; +} + +// Type string for signatures and return values; collapses only pathological +// giant object literals. Type-alias definitions use full `typeStr` instead. +export function displayType(type: any): string { + return collapseLongType(typeStr(type)); +} + +export function typeParamsStr(sig: any): string { + const tps = sig?.typeParameters; + if (tps == null || tps.length === 0) return ''; + const inner = tps + .map((tp: any) => { + let out = tp.name; + if (tp.type != null) out += ` extends ${typeStr(tp.type)}`; + if (tp.default != null) out += ` = ${typeStr(tp.default)}`; + return out; + }) + .join(', '); + return `<${inner}>`; +} + +export function paramStr(param: any): string { + const rest = param.flags?.isRest === true ? '...' : ''; + const hasDefault = param.defaultValue != null; + // A default already implies optionality, so don't also print `?`. + const optional = hasDefault ? false : param.flags?.isOptional === true; + let out = `${rest}${param.name}${optional ? '?' : ''}: ${typeStr(param.type)}`; + if (hasDefault) out += ` = ${param.defaultValue}`; + return out; +} + +export function paramListStr(sig: any): string { + return (sig.parameters ?? []).map(paramStr).join(', '); +} + +// A method/property whose type is a function: `type.declaration.signatures`. +export function callSignaturesOf(reflection: any): any[] | null { + if (Array.isArray(reflection?.signatures) && reflection.signatures.length > 0) { + return reflection.signatures; + } + const decl = reflection?.type?.declaration; + if (decl?.signatures != null && decl.signatures.length > 0) return decl.signatures; + return null; +} + +// Split an interface/class body into data properties and function members. +export function partitionMembers(children: any[]): { props: any[]; methods: any[] } { + const props: any[] = []; + const methods: any[] = []; + for (const child of children) { + if (child.kind === ReflectionKind.Constructor) continue; + if (callSignaturesOf(child) != null) methods.push(child); + else props.push(child); + } + return { props, methods }; +} + +// --------------------------------------------------------------------------- +// Comment / JSDoc tag extraction +// --------------------------------------------------------------------------- + +// The comment that carries the docs for a reflection: functions/methods keep +// theirs on the call signature; everything else on the declaration. +export function commentOf(reflection: any): any { + if (reflection?.comment != null) return reflection.comment; + const sig = reflection?.signatures?.[0]; + return sig?.comment ?? null; +} + +export function blockTags(comment: any, ...tags: string[]): any[] { + const all = comment?.blockTags ?? []; + return all.filter((t: any) => tags.includes(t.tag)); +} + +export function isDeprecated(comment: any): boolean { + return blockTags(comment, '@deprecated').length > 0; +} + +// The `@default` / `@defaultValue` value, unwrapped from any code fence TypeDoc +// stores it in and stripped of backticks (renders as plain text in TypeTable). +export function defaultTag(comment: any): string | undefined { + const tag = blockTags(comment, '@default', '@defaultValue')[0]; + if (tag == null) return undefined; + const raw = (tag.content ?? []) + .map((c: any) => c.text) + .join('') + .trim() + .replace(/^```[a-z]*\n?/i, '') + .replace(/\n?```$/, '') + .replace(/`+/g, '') + .trim(); + return raw === '' ? undefined : raw; +} + +// `@example` blocks, each already containing (or wrapped into) a code fence. +export function exampleBlocks(comment: any): string[] { + return blockTags(comment, '@example').map(tag => { + const body: string = (tag.content ?? []) + .map((c: any) => c.text) + .join('') + .trim(); + if (body.includes('```')) return body; + return `\`\`\`ts\n${body}\n\`\`\``; + }); +} diff --git a/scripts/references/render.ts b/scripts/references/render.ts new file mode 100644 index 0000000..25ddc3b --- /dev/null +++ b/scripts/references/render.ts @@ -0,0 +1,354 @@ +// Per-kind page-body renderers: turn a TypeDoc reflection into the MDX sections +// (Signature / Parameters / Returns / Properties / Methods / Examples) and the +// Fumadocs blocks. +import { ReflectionKind } from 'typedoc'; +import { plainParts, renderParts, tableText } from './mdx.ts'; +import { + blockTags, + callSignaturesOf, + commentOf, + defaultTag, + displayType, + exampleBlocks, + isDeprecated, + paramListStr, + partitionMembers, + typeParamsStr, + typeStr, +} from './reflection.ts'; +import type { RenderCtx, TypeRow } from './types.ts'; + +// --------------------------------------------------------------------------- +// emission +// --------------------------------------------------------------------------- + +// JSON is valid JS, so `` embeds cleanly in MDX. +function typeTable(rows: Record): string { + const clean: Record = {}; + for (const [name, row] of Object.entries(rows)) { + const entry: TypeRow = { type: row.type }; + if (row.description != null && row.description !== '') entry.description = row.description; + if (row.required != null) entry.required = row.required; + if (row.default != null && row.default !== '') entry.default = row.default; + if (row.deprecated === true) entry.deprecated = true; + if (row.parameters != null && row.parameters.length > 0) entry.parameters = row.parameters; + if (row.returns != null && row.returns !== '') entry.returns = row.returns; + clean[name] = entry; + } + return ``; +} + +// A row for a function-typed member (interface method, class method, API field). +// `member` carries the declaration flags (optionality, deprecation); `sig` is +// its call signature. +function methodRow(member: any, sig: any): TypeRow { + const comment = sig.comment ?? commentOf(member) ?? {}; + const params = (sig.parameters ?? []).map((p: any) => { + const desc = tableText(p.comment?.summary); + const typeText = typeStr(p.type); + const name = `${p.name}${p.flags?.isOptional === true ? '?' : ''}`; + return { name, description: desc === '' ? typeText : `${typeText} — ${desc}` }; + }); + const returnsTag = tableText(blockTags(comment, '@returns', '@return')[0]?.content); + const retType = displayType(sig.type); + return { + type: `(${paramListStr(sig)}) => ${retType}`, + description: tableText(comment.summary), + required: member.flags?.isOptional !== true, + deprecated: isDeprecated(commentOf(member) ?? sig.comment), + parameters: params, + returns: returnsTag === '' ? retType : `${retType} — ${returnsTag}`, + }; +} + +// --------------------------------------------------------------------------- +// Shared sections +// --------------------------------------------------------------------------- + +function sourceLine(reflection: any, ctx: RenderCtx): string { + const url = ctx.sourceUrl(reflection); + if (url == null || ctx.emittedSources.has(url)) return ''; + ctx.emittedSources.add(url); + // A plain link on its own line — the formatter leaves standalone links intact. + return `[View source ↗](${url})\n\n`; +} + +function deprecatedCallout(comment: any, ctx: RenderCtx): string { + const tags = blockTags(comment, '@deprecated'); + if (tags.length === 0) return ''; + const note = renderParts(tags[0].content, ctx.link); + return `Deprecated${note === '' ? '' : ` — ${note}`}\n\n`; +} + +// Lead prose shared by every page: summary + @remarks + @deprecated. +function leadBody(comment: any, ctx: RenderCtx): string { + let out = ''; + out += deprecatedCallout(comment, ctx); + const summary = renderParts(comment?.summary, ctx.link); + if (summary !== '') out += `${summary}\n\n`; + for (const remark of blockTags(comment, '@remarks')) { + const text = renderParts(remark.content, ctx.link); + if (text !== '') out += `${text}\n\n`; + } + return out; +} + +function examplesSection(comment: any): string { + const blocks = exampleBlocks(comment); + if (blocks.length === 0) return ''; + return `## Examples\n\n${blocks.join('\n\n')}\n\n`; +} + +// An `## Examples` section gathering the type-level examples plus each member's +// examples under a `### member` subheading — the compact for methods +// has nowhere to show @example, so they would otherwise be lost. +function memberExamplesSection(reflection: any): string { + const children = reflection.children ?? reflection.type?.declaration?.children ?? []; + const top = exampleBlocks(commentOf(reflection)); + const perMember: { name: string; blocks: string[] }[] = []; + for (const m of children) { + const comments = new Set(); + if (m.comment != null) comments.add(m.comment); + for (const sig of callSignaturesOf(m) ?? []) if (sig.comment != null) comments.add(sig.comment); + const blocks: string[] = []; + for (const c of comments) blocks.push(...exampleBlocks(c)); + if (blocks.length > 0) { + perMember.push({ + name: m.kind === ReflectionKind.Constructor ? 'constructor' : m.name, + blocks, + }); + } + } + if (top.length === 0 && perMember.length === 0) return ''; + let out = '## Examples\n\n'; + if (top.length > 0) out += `${top.join('\n\n')}\n\n`; + for (const g of perMember) out += `### ${g.name}\n\n${g.blocks.join('\n\n')}\n\n`; + return out; +} + +// Render an interface/class/type's index signatures (`[key: K]: V`) as a code +// block — TypeDoc keeps these off `children`, so they are otherwise dropped. +function indexSignatureSection(sigs: any[] | undefined): string { + if (sigs == null || sigs.length === 0) return ''; + const lines = sigs.map((s: any) => { + const p = s.parameters?.[0]; + return `[${p?.name ?? 'key'}: ${typeStr(p?.type)}]: ${typeStr(s.type)};`; + }); + return `## Index Signature\n\n\`\`\`ts\n${lines.join('\n')}\n\`\`\`\n\n`; +} + +// Parameters + Returns for a single call signature. +function signatureDetail(sig: any, ctx: RenderCtx): string { + let out = ''; + const params = sig.parameters ?? []; + if (params.length > 0) { + const rows: Record = {}; + for (const p of params) { + rows[p.name] = { + type: typeStr(p.type), + description: tableText(p.comment?.summary), + required: p.flags?.isOptional !== true && p.defaultValue == null, + default: p.defaultValue ?? undefined, + }; + } + out += `## Parameters\n\n${typeTable(rows)}\n\n`; + } + const retType = displayType(sig.type); + const returnsDesc = renderParts( + blockTags(sig.comment, '@returns', '@return')[0]?.content, + ctx.link + ); + out += `## Returns\n\n\`${retType}\`${returnsDesc === '' ? '' : ` — ${returnsDesc}`}\n\n`; + return out; +} + +function propertiesSection(props: any[], heading = 'Properties'): string { + if (props.length === 0) return ''; + const rows: Record = {}; + for (const p of props) { + const comment = commentOf(p) ?? {}; + const gs = p.getSignature; // accessor + const type = gs != null ? typeStr(gs.type) : typeStr(p.type); + const flags: string[] = []; + if (p.flags?.isStatic === true) flags.push('static'); + if (p.flags?.isReadonly === true) flags.push('readonly'); + const label = flags.length > 0 ? `${flags.join(' ')} ${type}` : type; + rows[p.name] = { + type: label, + description: tableText((gs?.comment ?? comment).summary), + required: p.flags?.isOptional !== true, + default: defaultTag(comment), + deprecated: isDeprecated(comment), + }; + } + return `## ${heading}\n\n${typeTable(rows)}\n\n`; +} + +function methodsSection(methods: any[], heading = 'Methods'): string { + if (methods.length === 0) return ''; + const rows: Record = {}; + for (const m of methods) { + const sig = (callSignaturesOf(m) ?? [])[0]; + if (sig == null) continue; + rows[m.name] = methodRow(m, sig); + } + return `## ${heading}\n\n${typeTable(rows)}\n\n`; +} + +// --------------------------------------------------------------------------- +// Per-kind renderers +// --------------------------------------------------------------------------- + +function renderFunction(reflection: any, ctx: RenderCtx): string { + let out = ''; + const sigs = reflection.signatures ?? []; + out += leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + sigs.forEach((sig: any, i: number) => { + if (sigs.length > 1) out += `## Overload ${i + 1}\n\n`; + out += `\`\`\`ts\nfunction ${reflection.name}${typeParamsStr(sig)}(${paramListStr(sig)}): ${displayType(sig.type)};\n\`\`\`\n\n`; + // leadBody already printed signatures[0]'s summary; only add per-overload + // summaries for the later overloads (i > 0) to avoid duplicating it. + if (sigs.length > 1 && i > 0) { + const s = renderParts(sig.comment?.summary, ctx.link); + if (s !== '') out += `${s}\n\n`; + } + out += signatureDetail(sig, ctx); + out += examplesSection(sig.comment); + }); + return out; +} + +function renderInterface(reflection: any, ctx: RenderCtx): string { + let out = leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + const extended = (reflection.extendedTypes ?? []).map((t: any) => `\`${typeStr(t)}\``); + if (extended.length > 0) out += `**Extends:** ${extended.join(', ')}\n\n`; + const { props, methods } = partitionMembers(reflection.children ?? []); + out += propertiesSection(props); + out += methodsSection(methods); + out += indexSignatureSection(reflection.indexSignatures); + out += memberExamplesSection(reflection); + return out; +} + +function renderClass(reflection: any, ctx: RenderCtx): string { + let out = leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + const extended = (reflection.extendedTypes ?? []).map((t: any) => `\`${typeStr(t)}\``); + if (extended.length > 0) out += `**Extends:** ${extended.join(', ')}\n\n`; + + const children = reflection.children ?? []; + const ctor = children.find((c: any) => c.kind === ReflectionKind.Constructor); + if (ctor != null) { + const sig = ctor.signatures?.[0]; + if (sig != null) { + out += `## Constructor\n\n\`\`\`ts\nnew ${reflection.name}${typeParamsStr(sig)}(${paramListStr(sig)});\n\`\`\`\n\n`; + const params = sig.parameters ?? []; + if (params.length > 0) { + const rows: Record = {}; + for (const p of params) { + rows[p.name] = { + type: typeStr(p.type), + description: tableText(p.comment?.summary), + required: p.flags?.isOptional !== true && p.defaultValue == null, + default: p.defaultValue ?? undefined, + }; + } + out += `${typeTable(rows)}\n\n`; + } + } + } + + const { props, methods } = partitionMembers(children); + out += propertiesSection(props); + out += methodsSection(methods); + out += indexSignatureSection(reflection.indexSignatures); + out += memberExamplesSection(reflection); + return out; +} + +function renderEnum(reflection: any, ctx: RenderCtx): string { + let out = leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + const rows: Record = {}; + for (const member of reflection.children ?? []) { + rows[member.name] = { + type: typeStr(member.type), + description: plainParts(commentOf(member)?.summary), + required: true, + }; + } + out += `## Members\n\n${typeTable(rows)}\n\n`; + return out; +} + +function renderTypeAlias(reflection: any, ctx: RenderCtx): string { + let out = leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + // Object-like type literal → show its members as a table too. + const decl = reflection.type?.declaration; + out += `## Type\n\n\`\`\`ts\ntype ${reflection.name}${typeParamsStr({ typeParameters: reflection.typeParameters })} = ${typeStr(reflection.type)};\n\`\`\`\n\n`; + if (decl?.children != null && decl.children.length > 0) { + const { props, methods } = partitionMembers(decl.children); + out += propertiesSection(props); + out += methodsSection(methods); + } + out += indexSignatureSection(decl?.indexSignatures); + out += examplesSection(commentOf(reflection)); + return out; +} + +function renderVariable(reflection: any, ctx: RenderCtx): string { + let out = leadBody(commentOf(reflection), ctx); + out += sourceLine(reflection, ctx); + const decl = reflection.type?.declaration; + if (decl?.signatures != null && decl.signatures.length > 0) { + // Function-typed const (arrow function): render like a function. + for (const sig of decl.signatures) { + out += `\`\`\`ts\nconst ${reflection.name}: ${typeParamsStr(sig)}(${paramListStr(sig)}) => ${displayType(sig.type)};\n\`\`\`\n\n`; + out += signatureDetail(sig, ctx); + out += examplesSection(sig.comment); + } + return out; + } + if (decl?.children != null && decl.children.length > 0) { + // Inline object literal (e.g. `platform`, an enum-like const): tabulate it. + const { props, methods } = partitionMembers(decl.children); + out += propertiesSection(props); + out += methodsSection(methods); + out += indexSignatureSection(decl.indexSignatures); + out += memberExamplesSection(reflection); + return out; + } + // Typed by a named export (e.g. `const remote: RemoteApi`, or a `typeof fn` + // alias): link across to that export's page. + const type = typeStr(reflection.type); + const targetName = reflection.type?.name ?? reflection.type?.queryType?.name; + const href = targetName != null ? ctx.link(targetName) : null; + out += + href == null + ? `**Type:** \`${type}\`\n\n` + : `**Type:** [\`${type}\`](${href})\n\nSee [\`${type}\`](${href}) for the full member list.\n\n`; + out += examplesSection(commentOf(reflection)); + return out; +} + +export function renderReflection(reflection: any, ctx: RenderCtx): string { + switch (reflection.kind) { + case ReflectionKind.Function: + return renderFunction(reflection, ctx); + case ReflectionKind.Interface: + return renderInterface(reflection, ctx); + case ReflectionKind.Class: + return renderClass(reflection, ctx); + case ReflectionKind.Enum: + return renderEnum(reflection, ctx); + case ReflectionKind.TypeAlias: + return renderTypeAlias(reflection, ctx); + case ReflectionKind.Variable: + return renderVariable(reflection, ctx); + default: + return leadBody(commentOf(reflection), ctx) + sourceLine(reflection, ctx); + } +} diff --git a/scripts/references/typedoc.ts b/scripts/references/typedoc.ts new file mode 100644 index 0000000..135f62e --- /dev/null +++ b/scripts/references/typedoc.ts @@ -0,0 +1,57 @@ +// TypeDoc conversion for one package, including a synthetic tsconfig for Deno +// packages that ship none. +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { Application } from 'typedoc'; +import type { PackageTarget } from './types.ts'; + +// Write a synthetic tsconfig for a package that ships none (Deno packages use +// deno.json). Deno sources use explicit `.ts` import extensions and bundler +// resolution; `@std/*` JSR imports don't resolve under Node but degrade to +// `any` under skipErrorChecking, which is fine for doc extraction. Returns the +// tsconfig path and a cleanup callback for its temp dir. +async function syntheticTsconfig( + pkg: PackageTarget +): Promise<{ file: string; cleanup: () => Promise }> { + const tmp = await mkdtemp(path.join(tmpdir(), `wvb-tsconfig-${pkg.slug}-`)); + const file = path.join(tmp, 'tsconfig.json'); + await writeFile( + file, + JSON.stringify({ + compilerOptions: { + allowImportingTsExtensions: true, + noEmit: true, + moduleResolution: 'bundler', + module: 'esnext', + target: 'esnext', + skipLibCheck: true, + strict: false, + types: [], + }, + include: [path.join(pkg.dir, '**/*.ts')], + }) + ); + return { file, cleanup: () => rm(tmp, { recursive: true, force: true }) }; +} + +export async function convertPackage(pkg: PackageTarget): Promise { + const synthetic = pkg.tsconfig == null ? await syntheticTsconfig(pkg) : null; + try { + const app = await Application.bootstrapWithPlugins({ + entryPoints: [pkg.entry], + tsconfig: pkg.tsconfig ?? synthetic!.file, + readme: 'none', + excludePrivate: true, + excludeInternal: true, + excludeExternals: true, + skipErrorChecking: true, + logLevel: 'Error', + }); + const project = await app.convert(); + if (project == null) throw new Error(`TypeDoc failed to convert ${pkg.name}`); + return project; + } finally { + await synthetic?.cleanup(); + } +} diff --git a/scripts/references/types.ts b/scripts/references/types.ts new file mode 100644 index 0000000..aba44f2 --- /dev/null +++ b/scripts/references/types.ts @@ -0,0 +1,39 @@ +// Shared types for the reference generator. TypeDoc reflections are loosely +// typed here (`any`) — the generator reads a small, stable slice of the model. + +export type LinkResolver = (label: string) => string | null; + +export interface PackageTarget { + name: string; + slug: string; + description: string; + dir: string; + entry: string; + // A tsconfig on disk, or null when the package ships none (Deno packages use + // deno.json) — convertPackage then writes a synthetic one. + tsconfig: string | null; +} + +export interface ExportGroup { + name: string; + slug: string; + reflections: any[]; // same name, e.g. a `const` + its `type` (enum pattern) + primaryKind: number; // best kind for sidebar grouping + index card + summary: string; // plain-text lead for index cards + frontmatter +} + +export interface TypeRow { + type: string; + description?: string; + required?: boolean; + default?: string; + deprecated?: boolean; + parameters?: { name: string; description: string }[]; + returns?: string; +} + +export interface RenderCtx { + link: LinkResolver; // resolve an export name to a relative page href + sourceUrl: (reflection: any) => string | null; + emittedSources: Set; // dedupe "View source" links within one page +} diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 0000000..673b79b --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "allowImportingTsExtensions": true, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": ["node"], + "lib": ["ESNext"] + }, + "include": ["**/*.ts"], + "exclude": [] +} diff --git a/src/layouts/MobileNav.tsx b/src/layouts/MobileNav.tsx index 7153240..27320f0 100644 --- a/src/layouts/MobileNav.tsx +++ b/src/layouts/MobileNav.tsx @@ -44,7 +44,7 @@ export function MobileNav({ className }: { className?: string }) { render={{section.label}} className={cn( 'rounded-md px-3 py-2.5 text-zinc-700 transition-colors hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-900', - pathname.startsWith(section.href) && 'text-brand dark:text-brand' + pathname.startsWith(section.match ?? section.href) && 'text-brand dark:text-brand' )} /> ))} diff --git a/src/layouts/NotFound.tsx b/src/layouts/NotFound.tsx new file mode 100644 index 0000000..8ab4dcb --- /dev/null +++ b/src/layouts/NotFound.tsx @@ -0,0 +1,53 @@ +import { useRouterState } from '@tanstack/react-router'; +import { localeFromPathname, localizeHref, LocaleProvider, useLocale } from '../lib/locale'; +import { useUiStrings } from '../lib/ui-strings'; +import { SiteHeader } from './SiteHeader'; + +// Global 404 page. Rendered by the router's `defaultNotFoundComponent`, so it +// catches both unmatched URLs and `notFound()` thrown from docs loaders. The +// locale is inferred from the path (`/ko/...` → Korean) so the message and the +// nav links match the section the reader came from. +export function NotFound() { + const pathname = useRouterState({ select: state => state.location.pathname }); + const locale = localeFromPathname(pathname); + + return ( + +
+ +
+ +
+
+
+ ); +} + +function NotFoundBody() { + const t = useUiStrings(); + const locale = useLocale(); + + return ( + <> +

404

+

+ {t.notFound.title} +

+

{t.notFound.message}

+ + + ); +} diff --git a/src/layouts/SiteHeader.tsx b/src/layouts/SiteHeader.tsx index e858b74..0b1b9e5 100644 --- a/src/layouts/SiteHeader.tsx +++ b/src/layouts/SiteHeader.tsx @@ -39,7 +39,7 @@ export function SiteHeader({ className }: { className?: string }) { {section.label} diff --git a/src/layouts/changelog/Changelog.tsx b/src/layouts/changelog/Changelog.tsx new file mode 100644 index 0000000..59169b5 --- /dev/null +++ b/src/layouts/changelog/Changelog.tsx @@ -0,0 +1,370 @@ +import { type ComponentProps, useEffect, useMemo, useRef, useState } from 'react'; +import { + CHANGELOG_REPO, + filterReleases, + type Release, + type ReleaseChange, + releasePackages, +} from '../../lib/changelog'; +import { cn } from '../../lib/cn'; +import type { Locale } from '../../lib/i18n'; +import { localizeHref, useLocale } from '../../lib/locale'; +import { useUiStrings } from '../../lib/ui-strings'; + +function ChevronDownIcon(props: ComponentProps<'svg'>) { + return ( + + ); +} + +function PackageIcon(props: ComponentProps<'svg'>) { + return ( + + ); +} + +function SearchIcon(props: ComponentProps<'svg'>) { + return ( + + ); +} + +function ArrowUpRightIcon(props: ComponentProps<'svg'>) { + return ( + + ); +} + +// Package filter dropdown. Manual popover (matching LanguageDropdown) so it +// shares the header controls' look and needs no extra dependency. +function PackageFilter({ + packages, + value, + onChange, +}: { + packages: { key: string; name: string }[]; + value: string; + onChange: (key: string) => void; +}) { + const [open, setOpen] = useState(false); + const rootRef = useRef(null); + const t = useUiStrings(); + const options = [{ key: 'all', name: t.changelog.allPackages }, ...packages]; + const current = options.find(option => option.key === value) ?? options[0]!; + + // Dismiss on outside pointerdown or Escape. A fixed full-screen overlay can't + // be used here: the sticky toolbar's `backdrop-blur` establishes a containing + // block for fixed descendants, so the overlay would be confined to the toolbar + // and neither catch outside clicks nor let clicks through to sibling controls. + useEffect(() => { + if (!open) return; + function onPointerDown(event: PointerEvent) { + if (rootRef.current != null && !rootRef.current.contains(event.target as Node)) { + setOpen(false); + } + } + function onKeyDown(event: KeyboardEvent) { + if (event.key === 'Escape') setOpen(false); + } + document.addEventListener('pointerdown', onPointerDown); + document.addEventListener('keydown', onKeyDown); + return () => { + document.removeEventListener('pointerdown', onPointerDown); + document.removeEventListener('keydown', onKeyDown); + }; + }, [open]); + + return ( +
+ + + {open && ( +
    + {options.map(option => { + const active = option.key === value; + return ( +
  • + +
  • + ); + })} +
+ )} +
+ ); +} + +// A single changelog bullet: a subtle conventional-commit prefix, the message, +// and the PR number linked to its pull request. +function ChangeText({ change }: { change: ReleaseChange }) { + let text = change.text; + if (change.prNumber != null) { + text = text.replace(/\s*\(#\d+\)\s*$/, ''); + } + + let prefix: string | undefined; + const conventional = text.match(/^(\w+(?:\([^)]*\))?!?:)\s+([\s\S]+)$/); + let rest = text; + if (conventional != null) { + prefix = conventional[1]; + rest = conventional[2]!; + } + + return ( + <> + {prefix != null && ( + {prefix} + )} + {rest} + {change.prNumber != null && ( + <> + {' '} +
+ #{change.prNumber} + + + )} + + ); +} + +function formatDate(date: string, locale: Locale): string { + if (date === '') return ''; + return new Intl.DateTimeFormat(locale === 'ko' ? 'ko-KR' : 'en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }).format(new Date(date)); +} + +function ReleaseItem({ release, isLatest }: { release: Release; isLatest: boolean }) { + const locale = useLocale(); + const t = useUiStrings(); + + return ( +
+
+
+ {release.packageUrl != null ? ( + + {release.packageName} + + ) : ( + + {release.packageName} + + )} + + v{release.version} + + {isLatest && ( + + {t.changelog.latest} + + )} +
+
+ {release.date !== '' && ( + + )} + + GitHub + + +
+
+ + {release.changes.length > 0 && ( +
    + {release.changes.map((change, index) => ( +
  • + + – + + + + +
  • + ))} +
+ )} +
+ ); +} + +export function Changelog({ releases, error }: { releases: Release[]; error: boolean }) { + const t = useUiStrings(); + const locale = useLocale(); + const [selected, setSelected] = useState('all'); + const [query, setQuery] = useState(''); + + const packages = useMemo(() => releasePackages(releases), [releases]); + const filtered = useMemo( + () => filterReleases(releases, selected, query), + [releases, selected, query] + ); + + // The newest release for each package (computed over the full set) is marked + // "latest" — stays meaningful while searching/filtering. + const latestTags = useMemo(() => { + const seen = new Set(); + const tags = new Set(); + for (const release of releases) { + if (!seen.has(release.packageKey)) { + seen.add(release.packageKey); + tags.add(release.tag); + } + } + return tags; + }, [releases]); + + return ( +
+
+

+ {t.changelog.title} +

+

{t.changelog.subtitle}

+
+ + {releases.length === 0 ? ( +

+ {error ? t.changelog.empty : t.changelog.noReleases} +

+ ) : ( + <> +
+ +
+ + setQuery(event.target.value)} + placeholder={t.changelog.searchPlaceholder} + aria-label={t.changelog.searchLabel} + className="h-9 w-full rounded-md border border-zinc-300 bg-transparent ps-8 pe-3 text-sm text-fd-foreground placeholder:text-zinc-400 focus:border-brand focus:outline-none dark:border-zinc-800" + /> +
+
+ + {filtered.length === 0 ? ( +

+ {t.changelog.noResults} +

+ ) : ( +
+ {filtered.map(release => ( + + ))} +
+ )} + + )} + + +
+ ); +} diff --git a/src/layouts/changelog/ChangelogRoute.tsx b/src/layouts/changelog/ChangelogRoute.tsx new file mode 100644 index 0000000..c35ab29 --- /dev/null +++ b/src/layouts/changelog/ChangelogRoute.tsx @@ -0,0 +1,32 @@ +import type { Release } from '../../lib/changelog'; +import type { Locale } from '../../lib/i18n'; +import { LocaleProvider } from '../../lib/locale'; +import { SiteHeader } from '../SiteHeader'; +import { Changelog } from './Changelog'; + +// Standalone shell for the changelog route. It reuses the shared SiteHeader +// (so the section tabs, global search, language, and theme controls all work +// and the "Changelog" tab reads as active) but deliberately skips the fumadocs +// DocsLayout: the changelog is served from GitHub releases, not the MDX source, +// so it carries no page tree and is absent from the global search index. Its own +// scoped search + package filter live in the toolbar. +export function ChangelogShell({ + locale, + releases, + error, +}: { + locale: Locale; + releases: Release[]; + error: boolean; +}) { + return ( + +
+ +
+ +
+
+
+ ); +} diff --git a/src/layouts/docs/sidebar-badges.tsx b/src/layouts/docs/sidebar-badges.tsx index 1902993..9b66996 100644 --- a/src/layouts/docs/sidebar-badges.tsx +++ b/src/layouts/docs/sidebar-badges.tsx @@ -2,7 +2,7 @@ import type * as PageTree from 'fumadocs-core/page-tree'; import type { ReactNode } from 'react'; // Sidebar entries (by URL) that should carry a small "Experimental" badge. -const EXPERIMENTAL_URLS = new Set(['/docs/guide/platforms/deno', '/docs/references/deno']); +const EXPERIMENTAL_URLS = new Set(['/docs/guide/native/deno', '/docs/references/api/deno']); function ExperimentalBadge() { return ( diff --git a/src/layouts/home/components/ArchitectureDiagram.tsx b/src/layouts/home/components/ArchitectureDiagram.tsx index 8a3a6a0..8dda4f5 100644 --- a/src/layouts/home/components/ArchitectureDiagram.tsx +++ b/src/layouts/home/components/ArchitectureDiagram.tsx @@ -1,75 +1,151 @@ import { SOURCE_FILES, WEBVIEW_HOSTS } from '../data'; -import { ChevronRightIcon } from './icons'; import { Logo } from './Logo'; +// A tiny closed-padlock, marking the bundle's ed25519 signature. +function LockIcon(props: React.ComponentProps<'svg'>) { + return ( + + ); +} + function ColumnLabel({ children }: { children: React.ReactNode }) { return ( -
+
{children}
); } -export function ArchitectureDiagram() { +// A flow connector between two stages. Horizontal on `md+` (with a label above) +// and vertical when the diagram stacks on mobile. Three brand pulses travel along +// it on a stagger, so data visibly moves source → bundle → host. +function FlowConnector({ label }: { label: string }) { return ( -
-
- - Architecture + <> + {/* Desktop: horizontal */} +
+ + {label} +
+ {[0, 1, 2].map(i => ( + + ))} +
+
+ {/* Mobile: vertical */} +
+
+ {[0, 1].map(i => ( + + ))} +
+
+ + ); +} + +export function ArchitectureDiagram() { + return ( +
+ {/* Faint dot grid for depth. */} +