From fb9702d3adcca23beab0e331ab23b26681bb808f Mon Sep 17 00:00:00 2001 From: Seokju Na Date: Sat, 4 Jul 2026 22:41:00 +0900 Subject: [PATCH 1/9] wip --- .../guide/core-concepts/bundle-source.ko.mdx | 89 ++++++++++ .../guide/core-concepts/bundle-source.mdx | 3 + ...municate-between-native-and-webview.ko.mdx | 15 ++ ...communicate-between-native-and-webview.mdx | 3 + .../guide/core-concepts/glue-to-native.ko.mdx | 3 + .../guide/core-concepts/glue-to-native.mdx | 32 ++++ .../guide/core-concepts/over-the-air.ko.mdx | 81 +++++++++ .../docs/guide/core-concepts/over-the-air.mdx | 3 + .../core-concepts/protocol-handling.ko.mdx | 93 +++++++++++ .../guide/core-concepts/protocol-handling.mdx | 3 + .../guide/core-concepts/webview-bundle.ko.mdx | 66 ++++++++ .../guide/core-concepts/webview-bundle.mdx | 66 ++++++++ .../docs/guide/getting-started/example.ko.mdx | 29 ++++ .../docs/guide/getting-started/example.mdx | 5 + .../docs/guide/getting-started/index.ko.mdx | 35 ++++ content/docs/guide/getting-started/index.mdx | 35 ++++ .../platform-and-framework-support.ko.mdx | 18 ++ .../platform-and-framework-support.mdx | 4 + .../getting-started/prerequisites.ko.mdx | 40 +++++ .../guide/getting-started/prerequisites.mdx | 3 + content/docs/guide/meta.json | 50 +++--- content/docs/guide/meta.ko.json | 44 ++--- content/docs/guide/platform-support.ko.mdx | 155 ------------------ content/docs/guide/platform-support.mdx | 155 ------------------ 24 files changed, 677 insertions(+), 353 deletions(-) create mode 100644 content/docs/guide/core-concepts/bundle-source.ko.mdx create mode 100644 content/docs/guide/core-concepts/bundle-source.mdx create mode 100644 content/docs/guide/core-concepts/communicate-between-native-and-webview.ko.mdx create mode 100644 content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx create mode 100644 content/docs/guide/core-concepts/glue-to-native.ko.mdx create mode 100644 content/docs/guide/core-concepts/glue-to-native.mdx create mode 100644 content/docs/guide/core-concepts/over-the-air.ko.mdx create mode 100644 content/docs/guide/core-concepts/over-the-air.mdx create mode 100644 content/docs/guide/core-concepts/protocol-handling.ko.mdx create mode 100644 content/docs/guide/core-concepts/protocol-handling.mdx create mode 100644 content/docs/guide/core-concepts/webview-bundle.ko.mdx create mode 100644 content/docs/guide/core-concepts/webview-bundle.mdx create mode 100644 content/docs/guide/getting-started/example.ko.mdx create mode 100644 content/docs/guide/getting-started/example.mdx create mode 100644 content/docs/guide/getting-started/index.ko.mdx create mode 100644 content/docs/guide/getting-started/index.mdx create mode 100644 content/docs/guide/getting-started/platform-and-framework-support.ko.mdx create mode 100644 content/docs/guide/getting-started/platform-and-framework-support.mdx create mode 100644 content/docs/guide/getting-started/prerequisites.ko.mdx create mode 100644 content/docs/guide/getting-started/prerequisites.mdx delete mode 100644 content/docs/guide/platform-support.ko.mdx delete mode 100644 content/docs/guide/platform-support.mdx 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..510e7e4 --- /dev/null +++ b/content/docs/guide/core-concepts/bundle-source.ko.mdx @@ -0,0 +1,89 @@ +--- +title: 번들 소스 +--- + +**소스**는 번들이 저장되는 기기 내의 위치입니다. 두 가지 유형의 소스가 있습니다. 하나는 앱 패키지 안에 포함되어 배포되는 읽기 전용 빌트인(`builtin`) 소스이고, 다른 하나는 리모트로부터 원격으로 업데이트하는 리모트(`remote`) 소스입니다. + +## 빌트인 vs 리모트 + +웹뷰 번들은 빌트인(`builtin`)과 리모트(`remote`) 번들 소스를 나눕니다. + +- **`builtin`** — 앱 패키지 _안에_ 포함되어 배포되는 번들입니다. 읽기 전용이며, 앱을 처음 실행할 때 아직 아무것도 다운로드되지 않은 상태에서 폴백으로 사용됩니다. +- **`remote`** — 리모트 서버에서 다운로드한 번들입니다. 쓰기 가능하며, 앱스토어 릴리스 없이 원격(OTA)으로 업데이트됩니다. + +번들이 두 소스에 모두 존재하면 **리모트가 우선합니다**. 소스는 리모트 매니페스트의 현재 버전을 먼저 확인하여 버전을 결정하며, 리모트에 없으면 빌트인으로 폴백합니다. + +--- + +이러한 분리 덕분에 업데이트가 안전해집니다. 폴백할 수 있는 배포된 빌트인 버전이 항상 존재하며, 다운로드된 리모트 버전은 검증과 설치를 마치면 자연스럽게 빌트인 버전을 대체합니다. 다운로드 및 검증 흐름은 [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..51485cd --- /dev/null +++ b/content/docs/guide/core-concepts/bundle-source.mdx @@ -0,0 +1,3 @@ +--- +title: Bundle source +--- 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..6b93f31 --- /dev/null +++ b/content/docs/guide/core-concepts/communicate-between-native-and-webview.ko.mdx @@ -0,0 +1,15 @@ +--- +title: 네이티브와 웹뷰 간 통신 +--- + +(웹뷰에서 네이티브 기능을 사용하기 위한 어떻게 통신할 수 있는지에 대해 설명) + +## 브릿지 호출하기 + +(`@wvb/bridge` 통합 브릿지 패키지를 사용하여 웹뷰에서 사용 가능) + +(브릿지 호출시 네이티브 단에 등록된 핸들러가 브릿지 호출을 처리해줌. 각 플랫폼/프레임워크 별 가이드) + +(Electron: ipc / Tauri : ipc / Android : JavaScriptInterface / iOS : WKScriptMessageHandler / Deno Desktop : Bindings(CEF)) + +(브릿지 API를 사용하는 자세한 방법은 /docs/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..96a7527 --- /dev/null +++ b/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx @@ -0,0 +1,3 @@ +--- +title: Communicate between native and webview +--- 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..df61481 --- /dev/null +++ b/content/docs/guide/core-concepts/glue-to-native.ko.mdx @@ -0,0 +1,3 @@ +--- +title: 네이티브 글루(Glue) +--- 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..3987253 --- /dev/null +++ b/content/docs/guide/core-concepts/glue-to-native.mdx @@ -0,0 +1,32 @@ +--- +title: Glue to native +--- + +(특정 네이티브 플랫폼/프레임워크에 종속되지 않게 설계되었다는 설명. 익숙한 웹 애플리케이션 코드를 한번만 작성하면 어떻게 모든 플랫폼/프레임워크에 연동할 수 있는 이점이 있는지 설명) + +## Rust로 작성된 코어 + +코어 크레이트(`wvb`)는 Rust로 작성되어 멀티 플랫폼 환경에서 동작하게 설계하였습니다. + +코어 크레이트에서 제공되는 기능은 다음과 같습니다. + +(기능 설명) + +## 멀티 플랫폼/환경 지원 + +여러 플랫폼 및 환경을 지원하기 위해 `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) 확인해주세요. + +## 네이티브에 붙이기 + +(코어 크레이트를 어떻게 네이티브 웹뷰 코드에 붙이는지, 안드로이드 WebView의 인터셉트 코드를 예시로 간략하게 설명) 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..b59fb2a --- /dev/null +++ b/content/docs/guide/core-concepts/over-the-air.ko.mdx @@ -0,0 +1,81 @@ +--- +title: 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** : +- **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= +``` + +(무결성 해시를 클라이언트에서 검증하는 방법. 검증 정책 설명) + +### 서명 + +(서명의 이유. 지원 알고리즘. 클라이언트에서 서명 검증하는 방법) + +## 리모트 + +(리모트 서버에 대한 설명) + +### 제공자 + +(기본 제공되는 AWS, Cloudflare 리모트 서버에 대한 설명. 직접 만드는 경우에는 리모트 문서를 읽으라고 가이드) + +### 채널 + +(채널에 대한 설명. Rust코드로 클라이언트에서 채널 지정하는 방법) 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..e1b603a --- /dev/null +++ b/content/docs/guide/core-concepts/over-the-air.mdx @@ -0,0 +1,3 @@ +--- +title: Over-the-air +--- 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..3bc53e3 --- /dev/null +++ b/content/docs/guide/core-concepts/protocol-handling.mdx @@ -0,0 +1,3 @@ +--- +title: Protocol handling +--- 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/getting-started/example.ko.mdx b/content/docs/guide/getting-started/example.ko.mdx new file mode 100644 index 0000000..c010293 --- /dev/null +++ b/content/docs/guide/getting-started/example.ko.mdx @@ -0,0 +1,29 @@ +--- +title: 예제와 함께 시작하기 +--- + +`create-wvb` 를 사용하여 선호하는 도구로 예제 프로젝트를 시작해 보세요. + + + + +```sh +npx create-wvb@latest +``` + + + +```sh +pnpx create-wvb@latest +``` + + +```sh +yarn dlx create-wvb@latest +``` + + + +## 예제 목록 + +사용가능한 예제 목록은 다음 테이블에서 확인해주세요. diff --git a/content/docs/guide/getting-started/example.mdx b/content/docs/guide/getting-started/example.mdx new file mode 100644 index 0000000..7a9abe1 --- /dev/null +++ b/content/docs/guide/getting-started/example.mdx @@ -0,0 +1,5 @@ +--- +title: 'Start with an example' +--- + +This page is a work in progress. 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..3b16ce5 --- /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)(실험적)을 지원하며, 웹뷰와 통신할 수 있는 [브릿지](...) 또한 제공됩니다. + +## 기능 + +### 네이티븡에 웹을 탑재 + +웹뷰 번들을 이용하면 익숙한 프론트엔드 코드를 네이티브에 탑재하여 오프라인에서 동작하는 애플리케이션을 만들 수 있습니다. 이를 지원하기 위해 웹 애플리케이션을 웹뷰 번들(`.wvb`) 포맷으로 패킹하여, 네이티브 앱 번들에 포함시킵니다. + +네이티브에서는 웹뷰의 요청을 인터셉트하여 네트워크 요청을 웹뷰 번들 파일 내의 리소스를 읽어 응답합니다. 따라서, 네트워크 요청이 인터넷을 통할 필요없이 로컬데이터를 읽어 응답하므로 오프라인에서도 웹 애플리케이션이 동작할 수 있습니다. + +### OTA(Over-the-air) + +네이티브 앱 번들에 포함된 번들을 최신 상태로 업데이트하기 위해 웹뷰 번들 시스템은 OTA 기술을 제공합니다. [리모트](...) 제공자를 만들어 배포할 번들을 업로드할 수 있고, 프론트엔드 혹은 네이티브에서 원하는 타이밍에 신규 번들을 다운받아 최신 상태를 유지할 수 있습니다. + +웹뷰 번들을 원격으로 업데이트하는 과정은 네이티브 앱 배포 혹은 앱 재시작없이 수행되기에 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..8208e77 --- /dev/null +++ b/content/docs/guide/getting-started/index.mdx @@ -0,0 +1,35 @@ +--- +title: Introduction +--- + +--- + +## Overview + +Webview Bundle is an offline-first delivery 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](...) 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](...) 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..6d5ea35 --- /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 | 실험적 | + +더 많은 플랫폼/프레임워크 지원이 필요하다면 [깃헙 논의](...)에 요청사항을 적어주시면 감사하겠습니다. 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..43314df --- /dev/null +++ b/content/docs/guide/getting-started/platform-and-framework-support.mdx @@ -0,0 +1,4 @@ +--- +title: Platform/Framework Support +description: Which platform/framework(s) run Webview Bundle and the package to install for each. +--- 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..10c0cb8 --- /dev/null +++ b/content/docs/guide/getting-started/prerequisites.ko.mdx @@ -0,0 +1,40 @@ +--- +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](...)을 사용한다면 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..a5b8246 --- /dev/null +++ b/content/docs/guide/getting-started/prerequisites.mdx @@ -0,0 +1,3 @@ +--- +title: 'Prerequisites' +--- diff --git a/content/docs/guide/meta.json b/content/docs/guide/meta.json index 913b498..96f5659 100644 --- a/content/docs/guide/meta.json +++ b/content/docs/guide/meta.json @@ -2,29 +2,33 @@ "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/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..1d722f5 100644 --- a/content/docs/guide/meta.ko.json +++ b/content/docs/guide/meta.ko.json @@ -2,28 +2,32 @@ "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/bridges", "---원격---", "remote", + "remote-using-cli", "providers", "spec" ] 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 - - - - - - - From 79a7d86fffbe4132cbc804a53a9fb663ec71a48d Mon Sep 17 00:00:00 2001 From: Seokju Na Date: Sun, 5 Jul 2026 01:25:56 +0900 Subject: [PATCH 2/9] wip --- content/docs/changelog/index.ko.mdx | 41 -- content/docs/changelog/index.mdx | 43 -- content/docs/changelog/meta.json | 5 - content/docs/changelog/meta.ko.json | 5 - content/docs/config/meta.json | 5 - content/docs/config/meta.ko.json | 5 - content/docs/guide/bundle-format.ko.mdx | 165 -------- content/docs/guide/bundle-format.mdx | 189 --------- content/docs/guide/bundle-sources.ko.mdx | 141 ------- content/docs/guide/bundle-sources.mdx | 178 --------- content/docs/guide/commands/meta.json | 4 - content/docs/guide/commands/meta.ko.json | 4 - .../guide/core-concepts/bundle-source.ko.mdx | 2 +- .../guide/core-concepts/bundle-source.mdx | 87 ++++ ...municate-between-native-and-webview.ko.mdx | 13 +- ...communicate-between-native-and-webview.mdx | 15 + .../guide/core-concepts/glue-to-native.ko.mdx | 50 ++- .../guide/core-concepts/glue-to-native.mdx | 49 ++- .../guide/core-concepts/over-the-air.ko.mdx | 48 ++- .../docs/guide/core-concepts/over-the-air.mdx | 106 +++++ .../guide/core-concepts/protocol-handling.mdx | 92 ++++- .../frontend/bridges/checking-sources.ko.mdx | 5 + .../frontend/bridges/checking-sources.mdx | 5 + .../docs/guide/frontend/bridges/index.ko.mdx | 5 + content/docs/guide/frontend/bridges/index.mdx | 5 + content/docs/guide/frontend/bridges/meta.json | 4 + .../docs/guide/frontend/bridges/meta.ko.json | 1 + .../guide/frontend/bridges/testing.ko.mdx | 5 + .../docs/guide/frontend/bridges/testing.mdx | 5 + .../bridges/update-remote-bundles.ko.mdx | 5 + .../bridges/update-remote-bundles.mdx | 5 + content/docs/guide/frontend/deploy.ko.mdx | 3 + content/docs/guide/frontend/deploy.mdx | 3 + .../docs/guide/frontend/development.ko.mdx | 5 + content/docs/guide/frontend/development.mdx | 5 + content/docs/guide/frontend/packing.ko.mdx | 5 + content/docs/guide/frontend/packing.mdx | 5 + content/docs/guide/frontend/upload.ko.mdx | 5 + content/docs/guide/frontend/upload.mdx | 5 + .../docs/guide/getting-started/example.ko.mdx | 5 + .../docs/guide/getting-started/example.mdx | 33 +- .../docs/guide/getting-started/index.ko.mdx | 10 +- content/docs/guide/getting-started/index.mdx | 6 +- .../platform-and-framework-support.ko.mdx | 2 +- .../platform-and-framework-support.mdx | 18 +- .../getting-started/prerequisites.ko.mdx | 4 +- .../guide/getting-started/prerequisites.mdx | 37 +- content/docs/guide/index.ko.mdx | 204 ---------- content/docs/guide/index.mdx | 205 ---------- content/docs/guide/meta.json | 1 + content/docs/guide/meta.ko.json | 9 +- .../docs/guide/native/android/builtin.ko.mdx | 5 + content/docs/guide/native/android/builtin.mdx | 5 + .../android/index.ko.mdx} | 9 +- .../android.mdx => native/android/index.mdx} | 9 +- .../native/android/local-development.ko.mdx | 5 + .../native/android/local-development.mdx | 5 + content/docs/guide/native/android/meta.json | 1 + .../docs/guide/native/android/meta.ko.json | 1 + .../docs/guide/native/deno/building.ko.mdx | 5 + content/docs/guide/native/deno/building.mdx | 5 + content/docs/guide/native/deno/builtin.ko.mdx | 5 + content/docs/guide/native/deno/builtin.mdx | 5 + .../deno.ko.mdx => native/deno/index.ko.mdx} | 18 +- .../deno.mdx => native/deno/index.mdx} | 18 +- .../native/deno/local-development.ko.mdx | 5 + .../guide/native/deno/local-development.mdx | 5 + content/docs/guide/native/deno/meta.json | 1 + content/docs/guide/native/deno/meta.ko.json | 1 + .../electron/builder.ko.mdx | 10 +- .../electron/builder.mdx | 10 +- .../docs/guide/native/electron/builtin.ko.mdx | 5 + .../docs/guide/native/electron/builtin.mdx | 5 + .../electron/forge.ko.mdx | 14 +- .../{platforms => native}/electron/forge.mdx | 14 +- .../electron/index.ko.mdx | 15 +- .../{platforms => native}/electron/index.mdx | 14 +- .../native/electron/local-development.ko.mdx | 5 + .../native/electron/local-development.mdx | 5 + content/docs/guide/native/electron/meta.json | 4 + .../docs/guide/native/electron/meta.ko.json | 4 + content/docs/guide/native/ios/builtin.ko.mdx | 5 + content/docs/guide/native/ios/builtin.mdx | 5 + .../ios.ko.mdx => native/ios/index.ko.mdx} | 8 +- .../ios.mdx => native/ios/index.mdx} | 8 +- .../guide/native/ios/local-development.ko.mdx | 5 + .../guide/native/ios/local-development.mdx | 5 + content/docs/guide/native/ios/meta.json | 1 + content/docs/guide/native/ios/meta.ko.json | 1 + .../docs/guide/native/tauri/builtin.ko.mdx | 5 + content/docs/guide/native/tauri/builtin.mdx | 5 + .../tauri/index.ko.mdx} | 12 +- .../tauri.mdx => native/tauri/index.mdx} | 12 +- .../native/tauri/local-development.ko.mdx | 5 + .../guide/native/tauri/local-development.mdx | 5 + content/docs/guide/native/tauri/meta.json | 1 + content/docs/guide/native/tauri/meta.ko.json | 1 + content/docs/guide/native/tauri/mobile.ko.mdx | 5 + content/docs/guide/native/tauri/mobile.mdx | 5 + .../docs/guide/platform-integration.ko.mdx | 296 -------------- content/docs/guide/platform-integration.mdx | 296 -------------- .../docs/guide/platforms/electron/meta.json | 4 - .../guide/platforms/electron/meta.ko.json | 4 - content/docs/guide/protocol-handling.ko.mdx | 243 ------------ content/docs/guide/protocol-handling.mdx | 243 ------------ content/docs/guide/providers/aws.ko.mdx | 210 ---------- content/docs/guide/providers/aws.mdx | 209 ---------- .../docs/guide/providers/cloudflare.ko.mdx | 231 ----------- content/docs/guide/providers/cloudflare.mdx | 231 ----------- content/docs/guide/providers/local.ko.mdx | 242 ------------ content/docs/guide/providers/local.mdx | 242 ------------ content/docs/guide/providers/meta.json | 4 - content/docs/guide/providers/meta.ko.json | 4 - content/docs/guide/remote-bundles.ko.mdx | 259 ------------ content/docs/guide/remote-bundles.mdx | 260 ------------ content/docs/guide/remote.ko.mdx | 235 ----------- content/docs/guide/remote.mdx | 235 ----------- .../http-spec/endpoints}/get-current.ko.mdx | 2 +- .../http-spec/endpoints}/get-current.mdx | 2 +- .../http-spec/endpoints}/get-version.ko.mdx | 0 .../http-spec/endpoints}/get-version.mdx | 0 .../http-spec/endpoints}/head-current.ko.mdx | 0 .../http-spec/endpoints}/head-current.mdx | 0 .../http-spec/endpoints}/list-bundles.ko.mdx | 2 +- .../http-spec/endpoints}/list-bundles.mdx | 2 +- .../remote/http-spec/endpoints/meta.json | 4 + .../remote/http-spec/endpoints/meta.ko.json | 4 + .../docs/guide/remote/http-spec/errors.ko.mdx | 3 + .../docs/guide/remote/http-spec/errors.mdx | 3 + .../docs/guide/remote/http-spec/index.ko.mdx | 3 + content/docs/guide/remote/http-spec/index.mdx | 3 + content/docs/guide/remote/http-spec/meta.json | 4 + .../docs/guide/remote/http-spec/meta.ko.json | 4 + content/docs/guide/remote/index.ko.mdx | 3 + content/docs/guide/remote/index.mdx | 3 + .../docs/guide/remote/providers/aws.ko.mdx | 3 + content/docs/guide/remote/providers/aws.mdx | 3 + .../guide/remote/providers/cloudflare.ko.mdx | 3 + .../guide/remote/providers/cloudflare.mdx | 3 + .../docs/guide/remote/providers/local.ko.mdx | 3 + content/docs/guide/remote/providers/local.mdx | 3 + content/docs/guide/remote/providers/meta.json | 4 + .../docs/guide/remote/providers/meta.ko.json | 4 + content/docs/guide/remote/using-cli.ko.mdx | 3 + content/docs/guide/remote/using-cli.mdx | 3 + content/docs/guide/spec/errors.ko.mdx | 93 ----- content/docs/guide/spec/errors.mdx | 93 ----- content/docs/guide/spec/index.ko.mdx | 76 ---- content/docs/guide/spec/index.mdx | 76 ---- content/docs/guide/spec/meta.json | 4 - content/docs/guide/spec/meta.ko.json | 4 - content/docs/guide/why-webview-bundle.ko.mdx | 189 --------- content/docs/guide/why-webview-bundle.mdx | 190 --------- content/docs/meta.json | 2 +- content/docs/meta.ko.json | 2 +- content/docs/references/android.ko.mdx | 5 + content/docs/references/android.mdx | 5 + content/docs/references/bridge/meta.json | 4 - content/docs/references/bridge/meta.ko.json | 4 - .../cli}/builtin.ko.mdx | 8 +- .../commands => references/cli}/builtin.mdx | 8 +- .../docs/references/cli/environment.ko.mdx | 5 + content/docs/references/cli/environment.mdx | 5 + .../cli/index.ko.mdx} | 22 +- .../cli.mdx => references/cli/index.mdx} | 22 +- .../docs/references/cli/installation.ko.mdx | 5 + content/docs/references/cli/installation.mdx | 5 + content/docs/references/cli/meta.json | 18 + content/docs/references/cli/meta.ko.json | 18 + .../commands => references/cli}/pack.ko.mdx | 14 +- .../commands => references/cli}/pack.mdx | 14 +- .../cli/programmatic.ko.mdx} | 20 +- .../cli/programmatic.mdx} | 18 +- .../cli/remote-deploy.ko.mdx} | 12 +- .../cli/remote-deploy.mdx} | 12 +- .../cli/remote-download.ko.mdx} | 12 +- .../cli/remote-download.mdx} | 12 +- .../cli/remote-upload.ko.mdx} | 12 +- .../cli/remote-upload.mdx} | 12 +- .../commands => references/cli}/remote.ko.mdx | 8 +- .../commands => references/cli}/remote.mdx | 8 +- .../commands => references/cli}/serve.ko.mdx | 14 +- .../commands => references/cli}/serve.mdx | 14 +- .../cli/unpack.ko.mdx} | 6 +- .../extract.mdx => references/cli/unpack.mdx} | 6 +- .../configuration}/index.ko.mdx | 18 +- .../configuration}/index.mdx | 18 +- .../docs/references/configuration/meta.json | 1 + .../references/configuration/meta.ko.json | 1 + .../configuration}/remote.ko.mdx | 10 +- .../configuration}/remote.mdx | 10 +- content/docs/references/deno/meta.json | 4 - content/docs/references/deno/meta.ko.json | 4 - content/docs/references/index.ko.mdx | 22 +- content/docs/references/index.mdx | 22 +- content/docs/references/ios.ko.mdx | 5 + content/docs/references/ios.mdx | 5 + .../{ => javascript}/bridge/errors.ko.mdx | 4 +- .../{ => javascript}/bridge/errors.mdx | 4 +- .../{ => javascript}/bridge/index.ko.mdx | 18 +- .../{ => javascript}/bridge/index.mdx | 18 +- .../references/javascript/bridge/meta.json | 1 + .../references/javascript/bridge/meta.ko.json | 1 + .../{ => javascript}/bridge/remote.ko.mdx | 9 +- .../{ => javascript}/bridge/remote.mdx | 9 +- .../{ => javascript}/bridge/source.ko.mdx | 6 +- .../{ => javascript}/bridge/source.mdx | 6 +- .../{ => javascript}/bridge/testing.ko.mdx | 6 +- .../{ => javascript}/bridge/testing.mdx | 6 +- .../{ => javascript}/bridge/updater.ko.mdx | 10 +- .../{ => javascript}/bridge/updater.mdx | 12 +- .../references/javascript/deno-desktop.ko.mdx | 5 + .../references/javascript/deno-desktop.mdx | 5 + .../{ => javascript}/deno/index.ko.mdx | 20 +- .../{ => javascript}/deno/index.mdx | 20 +- .../docs/references/javascript/deno/meta.json | 1 + .../references/javascript/deno/meta.ko.json | 1 + .../{ => javascript}/deno/protocol.ko.mdx | 13 +- .../{ => javascript}/deno/protocol.mdx | 13 +- .../{ => javascript}/deno/remote.ko.mdx | 10 +- .../{ => javascript}/deno/remote.mdx | 10 +- .../{ => javascript}/deno/source.ko.mdx | 12 +- .../{ => javascript}/deno/source.mdx | 12 +- .../{ => javascript}/deno/updater.ko.mdx | 16 +- .../{ => javascript}/deno/updater.mdx | 16 +- .../javascript/electron-builder.ko.mdx | 5 + .../javascript/electron-builder.mdx | 5 + .../javascript/electron-forge.ko.mdx | 5 + .../references/javascript/electron-forge.mdx | 5 + .../references/javascript/electron.ko.mdx | 5 + .../docs/references/javascript/electron.mdx | 5 + .../{ => javascript}/node/bundle.ko.mdx | 4 +- .../{ => javascript}/node/bundle.mdx | 4 +- .../{ => javascript}/node/index.ko.mdx | 24 +- .../{ => javascript}/node/index.mdx | 20 +- .../{ => javascript}/node/meta.json | 2 +- .../{ => javascript}/node/meta.ko.json | 2 +- .../{ => javascript}/node/protocol.ko.mdx | 8 +- .../{ => javascript}/node/protocol.mdx | 8 +- .../{ => javascript}/node/remote.ko.mdx | 6 +- .../{ => javascript}/node/remote.mdx | 6 +- .../{ => javascript}/node/source.ko.mdx | 22 +- .../{ => javascript}/node/source.mdx | 22 +- .../{ => javascript}/node/types.ko.mdx | 14 +- .../{ => javascript}/node/types.mdx | 14 +- .../{ => javascript}/node/updater.ko.mdx | 22 +- .../{ => javascript}/node/updater.mdx | 22 +- .../references/javascript/remotes/meta.json | 13 + .../javascript/remotes/meta.ko.json | 13 + .../remotes/remote-aws-provider-pulumi.ko.mdx | 5 + .../remotes/remote-aws-provider-pulumi.mdx | 5 + .../remotes/remote-aws-provider.ko.mdx | 5 + .../remotes/remote-aws-provider.mdx | 5 + .../javascript/remotes/remote-aws.ko.mdx | 5 + .../javascript/remotes/remote-aws.mdx | 5 + .../remotes/remote-cloudflare-provider.ko.mdx | 5 + .../remotes/remote-cloudflare-provider.mdx | 5 + .../remotes/remote-cloudflare-pulumi.ko.mdx | 5 + .../remotes/remote-cloudflare-pulumi.mdx | 5 + .../remotes/remote-cloudflare.ko.mdx | 5 + .../javascript/remotes/remote-cloudflare.mdx | 5 + .../remotes/remote-local-provider.ko.mdx | 5 + .../remotes/remote-local-provider.mdx | 5 + .../javascript/remotes/remote-local.ko.mdx | 5 + .../javascript/remotes/remote-local.mdx | 5 + content/docs/references/meta.json | 21 +- content/docs/references/meta.ko.json | 21 +- new-doc/guide/getting-started/introduction.md | 41 ++ .../guide/getting-started/platform-support.md | 13 + .../guide/getting-started/prerequisties.md | 31 ++ .../getting-started/start-with-and-example.md | 27 ++ package.json | 2 +- public/diagrams/wvb-data-layout.svg | 37 ++ public/diagrams/wvb-index-lookup.svg | 49 +++ public/diagrams/wvb-structure.svg | 64 +++ src/layouts/MobileNav.tsx | 2 +- src/layouts/NotFound.tsx | 53 +++ src/layouts/SiteHeader.tsx | 2 +- src/layouts/changelog/Changelog.tsx | 370 ++++++++++++++++++ src/layouts/changelog/ChangelogRoute.tsx | 32 ++ src/layouts/docs/sidebar-badges.tsx | 2 +- .../home/components/ArchitectureDiagram.tsx | 97 +++-- src/layouts/home/components/CallToAction.tsx | 28 +- src/layouts/home/components/Features.tsx | 126 ++++-- src/layouts/home/components/Hero.tsx | 2 +- src/layouts/home/components/Platforms.tsx | 48 ++- src/layouts/home/components/Showcase.tsx | 5 +- src/layouts/home/components/logos.tsx | 53 +++ src/layouts/home/data.ts | 67 +--- src/lib/changelog-data.ts | 110 ++++++ src/lib/changelog.ts | 166 ++++++++ src/lib/locale.tsx | 38 +- src/lib/ui-strings.ts | 166 +++++--- src/routeTree.gen.ts | 42 ++ src/router.tsx | 2 + src/routes/docs/$.tsx | 7 +- src/routes/docs/changelog.tsx | 14 + src/routes/ko/docs/$.tsx | 7 +- src/routes/ko/docs/changelog.tsx | 14 + src/styles.css | 61 +++ 300 files changed, 3157 insertions(+), 6131 deletions(-) delete mode 100644 content/docs/changelog/index.ko.mdx delete mode 100644 content/docs/changelog/index.mdx delete mode 100644 content/docs/changelog/meta.json delete mode 100644 content/docs/changelog/meta.ko.json delete mode 100644 content/docs/config/meta.json delete mode 100644 content/docs/config/meta.ko.json delete mode 100644 content/docs/guide/bundle-format.ko.mdx delete mode 100644 content/docs/guide/bundle-format.mdx delete mode 100644 content/docs/guide/bundle-sources.ko.mdx delete mode 100644 content/docs/guide/bundle-sources.mdx delete mode 100644 content/docs/guide/commands/meta.json delete mode 100644 content/docs/guide/commands/meta.ko.json create mode 100644 content/docs/guide/frontend/bridges/checking-sources.ko.mdx create mode 100644 content/docs/guide/frontend/bridges/checking-sources.mdx create mode 100644 content/docs/guide/frontend/bridges/index.ko.mdx create mode 100644 content/docs/guide/frontend/bridges/index.mdx create mode 100644 content/docs/guide/frontend/bridges/meta.json create mode 100644 content/docs/guide/frontend/bridges/meta.ko.json create mode 100644 content/docs/guide/frontend/bridges/testing.ko.mdx create mode 100644 content/docs/guide/frontend/bridges/testing.mdx create mode 100644 content/docs/guide/frontend/bridges/update-remote-bundles.ko.mdx create mode 100644 content/docs/guide/frontend/bridges/update-remote-bundles.mdx create mode 100644 content/docs/guide/frontend/deploy.ko.mdx create mode 100644 content/docs/guide/frontend/deploy.mdx create mode 100644 content/docs/guide/frontend/development.ko.mdx create mode 100644 content/docs/guide/frontend/development.mdx create mode 100644 content/docs/guide/frontend/packing.ko.mdx create mode 100644 content/docs/guide/frontend/packing.mdx create mode 100644 content/docs/guide/frontend/upload.ko.mdx create mode 100644 content/docs/guide/frontend/upload.mdx delete mode 100644 content/docs/guide/index.ko.mdx delete mode 100644 content/docs/guide/index.mdx create mode 100644 content/docs/guide/native/android/builtin.ko.mdx create mode 100644 content/docs/guide/native/android/builtin.mdx rename content/docs/guide/{platforms/android.ko.mdx => native/android/index.ko.mdx} (98%) rename content/docs/guide/{platforms/android.mdx => native/android/index.mdx} (97%) create mode 100644 content/docs/guide/native/android/local-development.ko.mdx create mode 100644 content/docs/guide/native/android/local-development.mdx create mode 100644 content/docs/guide/native/android/meta.json create mode 100644 content/docs/guide/native/android/meta.ko.json create mode 100644 content/docs/guide/native/deno/building.ko.mdx create mode 100644 content/docs/guide/native/deno/building.mdx create mode 100644 content/docs/guide/native/deno/builtin.ko.mdx create mode 100644 content/docs/guide/native/deno/builtin.mdx rename content/docs/guide/{platforms/deno.ko.mdx => native/deno/index.ko.mdx} (90%) rename content/docs/guide/{platforms/deno.mdx => native/deno/index.mdx} (89%) create mode 100644 content/docs/guide/native/deno/local-development.ko.mdx create mode 100644 content/docs/guide/native/deno/local-development.mdx create mode 100644 content/docs/guide/native/deno/meta.json create mode 100644 content/docs/guide/native/deno/meta.ko.json rename content/docs/guide/{platforms => native}/electron/builder.ko.mdx (93%) rename content/docs/guide/{platforms => native}/electron/builder.mdx (91%) create mode 100644 content/docs/guide/native/electron/builtin.ko.mdx create mode 100644 content/docs/guide/native/electron/builtin.mdx rename content/docs/guide/{platforms => native}/electron/forge.ko.mdx (88%) rename content/docs/guide/{platforms => native}/electron/forge.mdx (90%) rename content/docs/guide/{platforms => native}/electron/index.ko.mdx (96%) rename content/docs/guide/{platforms => native}/electron/index.mdx (95%) create mode 100644 content/docs/guide/native/electron/local-development.ko.mdx create mode 100644 content/docs/guide/native/electron/local-development.mdx create mode 100644 content/docs/guide/native/electron/meta.json create mode 100644 content/docs/guide/native/electron/meta.ko.json create mode 100644 content/docs/guide/native/ios/builtin.ko.mdx create mode 100644 content/docs/guide/native/ios/builtin.mdx rename content/docs/guide/{platforms/ios.ko.mdx => native/ios/index.ko.mdx} (98%) rename content/docs/guide/{platforms/ios.mdx => native/ios/index.mdx} (98%) create mode 100644 content/docs/guide/native/ios/local-development.ko.mdx create mode 100644 content/docs/guide/native/ios/local-development.mdx create mode 100644 content/docs/guide/native/ios/meta.json create mode 100644 content/docs/guide/native/ios/meta.ko.json create mode 100644 content/docs/guide/native/tauri/builtin.ko.mdx create mode 100644 content/docs/guide/native/tauri/builtin.mdx rename content/docs/guide/{platforms/tauri.ko.mdx => native/tauri/index.ko.mdx} (96%) rename content/docs/guide/{platforms/tauri.mdx => native/tauri/index.mdx} (96%) create mode 100644 content/docs/guide/native/tauri/local-development.ko.mdx create mode 100644 content/docs/guide/native/tauri/local-development.mdx create mode 100644 content/docs/guide/native/tauri/meta.json create mode 100644 content/docs/guide/native/tauri/meta.ko.json create mode 100644 content/docs/guide/native/tauri/mobile.ko.mdx create mode 100644 content/docs/guide/native/tauri/mobile.mdx delete mode 100644 content/docs/guide/platform-integration.ko.mdx delete mode 100644 content/docs/guide/platform-integration.mdx delete mode 100644 content/docs/guide/platforms/electron/meta.json delete mode 100644 content/docs/guide/platforms/electron/meta.ko.json delete mode 100644 content/docs/guide/protocol-handling.ko.mdx delete mode 100644 content/docs/guide/protocol-handling.mdx delete mode 100644 content/docs/guide/providers/aws.ko.mdx delete mode 100644 content/docs/guide/providers/aws.mdx delete mode 100644 content/docs/guide/providers/cloudflare.ko.mdx delete mode 100644 content/docs/guide/providers/cloudflare.mdx delete mode 100644 content/docs/guide/providers/local.ko.mdx delete mode 100644 content/docs/guide/providers/local.mdx delete mode 100644 content/docs/guide/providers/meta.json delete mode 100644 content/docs/guide/providers/meta.ko.json delete mode 100644 content/docs/guide/remote-bundles.ko.mdx delete mode 100644 content/docs/guide/remote-bundles.mdx delete mode 100644 content/docs/guide/remote.ko.mdx delete mode 100644 content/docs/guide/remote.mdx rename content/docs/guide/{spec => remote/http-spec/endpoints}/get-current.ko.mdx (95%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/get-current.mdx (96%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/get-version.ko.mdx (100%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/get-version.mdx (100%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/head-current.ko.mdx (100%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/head-current.mdx (100%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/list-bundles.ko.mdx (98%) rename content/docs/guide/{spec => remote/http-spec/endpoints}/list-bundles.mdx (98%) create mode 100644 content/docs/guide/remote/http-spec/endpoints/meta.json create mode 100644 content/docs/guide/remote/http-spec/endpoints/meta.ko.json create mode 100644 content/docs/guide/remote/http-spec/errors.ko.mdx create mode 100644 content/docs/guide/remote/http-spec/errors.mdx create mode 100644 content/docs/guide/remote/http-spec/index.ko.mdx create mode 100644 content/docs/guide/remote/http-spec/index.mdx create mode 100644 content/docs/guide/remote/http-spec/meta.json create mode 100644 content/docs/guide/remote/http-spec/meta.ko.json create mode 100644 content/docs/guide/remote/index.ko.mdx create mode 100644 content/docs/guide/remote/index.mdx create mode 100644 content/docs/guide/remote/providers/aws.ko.mdx create mode 100644 content/docs/guide/remote/providers/aws.mdx create mode 100644 content/docs/guide/remote/providers/cloudflare.ko.mdx create mode 100644 content/docs/guide/remote/providers/cloudflare.mdx create mode 100644 content/docs/guide/remote/providers/local.ko.mdx create mode 100644 content/docs/guide/remote/providers/local.mdx create mode 100644 content/docs/guide/remote/providers/meta.json create mode 100644 content/docs/guide/remote/providers/meta.ko.json create mode 100644 content/docs/guide/remote/using-cli.ko.mdx create mode 100644 content/docs/guide/remote/using-cli.mdx delete mode 100644 content/docs/guide/spec/errors.ko.mdx delete mode 100644 content/docs/guide/spec/errors.mdx delete mode 100644 content/docs/guide/spec/index.ko.mdx delete mode 100644 content/docs/guide/spec/index.mdx delete mode 100644 content/docs/guide/spec/meta.json delete mode 100644 content/docs/guide/spec/meta.ko.json delete mode 100644 content/docs/guide/why-webview-bundle.ko.mdx delete mode 100644 content/docs/guide/why-webview-bundle.mdx create mode 100644 content/docs/references/android.ko.mdx create mode 100644 content/docs/references/android.mdx delete mode 100644 content/docs/references/bridge/meta.json delete mode 100644 content/docs/references/bridge/meta.ko.json rename content/docs/{guide/commands => references/cli}/builtin.ko.mdx (92%) rename content/docs/{guide/commands => references/cli}/builtin.mdx (93%) create mode 100644 content/docs/references/cli/environment.ko.mdx create mode 100644 content/docs/references/cli/environment.mdx rename content/docs/{guide/cli.ko.mdx => references/cli/index.ko.mdx} (84%) rename content/docs/{guide/cli.mdx => references/cli/index.mdx} (82%) create mode 100644 content/docs/references/cli/installation.ko.mdx create mode 100644 content/docs/references/cli/installation.mdx create mode 100644 content/docs/references/cli/meta.json create mode 100644 content/docs/references/cli/meta.ko.json rename content/docs/{guide/commands => references/cli}/pack.ko.mdx (85%) rename content/docs/{guide/commands => references/cli}/pack.mdx (83%) rename content/docs/{guide/cli-programmatic.ko.mdx => references/cli/programmatic.ko.mdx} (94%) rename content/docs/{guide/cli-programmatic.mdx => references/cli/programmatic.mdx} (94%) rename content/docs/{guide/commands/deploy.ko.mdx => references/cli/remote-deploy.ko.mdx} (78%) rename content/docs/{guide/commands/deploy.mdx => references/cli/remote-deploy.mdx} (80%) rename content/docs/{guide/commands/download.ko.mdx => references/cli/remote-download.ko.mdx} (90%) rename content/docs/{guide/commands/download.mdx => references/cli/remote-download.mdx} (91%) rename content/docs/{guide/commands/upload.ko.mdx => references/cli/remote-upload.ko.mdx} (91%) rename content/docs/{guide/commands/upload.mdx => references/cli/remote-upload.mdx} (92%) rename content/docs/{guide/commands => references/cli}/remote.ko.mdx (92%) rename content/docs/{guide/commands => references/cli}/remote.mdx (92%) rename content/docs/{guide/commands => references/cli}/serve.ko.mdx (84%) rename content/docs/{guide/commands => references/cli}/serve.mdx (87%) rename content/docs/{guide/commands/extract.ko.mdx => references/cli/unpack.ko.mdx} (96%) rename content/docs/{guide/commands/extract.mdx => references/cli/unpack.mdx} (95%) rename content/docs/{config => references/configuration}/index.ko.mdx (95%) rename content/docs/{config => references/configuration}/index.mdx (96%) create mode 100644 content/docs/references/configuration/meta.json create mode 100644 content/docs/references/configuration/meta.ko.json rename content/docs/{config => references/configuration}/remote.ko.mdx (96%) rename content/docs/{config => references/configuration}/remote.mdx (96%) delete mode 100644 content/docs/references/deno/meta.json delete mode 100644 content/docs/references/deno/meta.ko.json create mode 100644 content/docs/references/ios.ko.mdx create mode 100644 content/docs/references/ios.mdx rename content/docs/references/{ => javascript}/bridge/errors.ko.mdx (96%) rename content/docs/references/{ => javascript}/bridge/errors.mdx (96%) rename content/docs/references/{ => javascript}/bridge/index.ko.mdx (90%) rename content/docs/references/{ => javascript}/bridge/index.mdx (90%) create mode 100644 content/docs/references/javascript/bridge/meta.json create mode 100644 content/docs/references/javascript/bridge/meta.ko.json rename content/docs/references/{ => javascript}/bridge/remote.ko.mdx (87%) rename content/docs/references/{ => javascript}/bridge/remote.mdx (88%) rename content/docs/references/{ => javascript}/bridge/source.ko.mdx (91%) rename content/docs/references/{ => javascript}/bridge/source.mdx (90%) rename content/docs/references/{ => javascript}/bridge/testing.ko.mdx (97%) rename content/docs/references/{ => javascript}/bridge/testing.mdx (97%) rename content/docs/references/{ => javascript}/bridge/updater.ko.mdx (86%) rename content/docs/references/{ => javascript}/bridge/updater.mdx (83%) create mode 100644 content/docs/references/javascript/deno-desktop.ko.mdx create mode 100644 content/docs/references/javascript/deno-desktop.mdx rename content/docs/references/{ => javascript}/deno/index.ko.mdx (88%) rename content/docs/references/{ => javascript}/deno/index.mdx (88%) create mode 100644 content/docs/references/javascript/deno/meta.json create mode 100644 content/docs/references/javascript/deno/meta.ko.json rename content/docs/references/{ => javascript}/deno/protocol.ko.mdx (86%) rename content/docs/references/{ => javascript}/deno/protocol.mdx (90%) rename content/docs/references/{ => javascript}/deno/remote.ko.mdx (89%) rename content/docs/references/{ => javascript}/deno/remote.mdx (91%) rename content/docs/references/{ => javascript}/deno/source.ko.mdx (88%) rename content/docs/references/{ => javascript}/deno/source.mdx (87%) rename content/docs/references/{ => javascript}/deno/updater.ko.mdx (87%) rename content/docs/references/{ => javascript}/deno/updater.mdx (91%) create mode 100644 content/docs/references/javascript/electron-builder.ko.mdx create mode 100644 content/docs/references/javascript/electron-builder.mdx create mode 100644 content/docs/references/javascript/electron-forge.ko.mdx create mode 100644 content/docs/references/javascript/electron-forge.mdx create mode 100644 content/docs/references/javascript/electron.ko.mdx create mode 100644 content/docs/references/javascript/electron.mdx rename content/docs/references/{ => javascript}/node/bundle.ko.mdx (96%) rename content/docs/references/{ => javascript}/node/bundle.mdx (96%) rename content/docs/references/{ => javascript}/node/index.ko.mdx (81%) rename content/docs/references/{ => javascript}/node/index.mdx (82%) rename content/docs/references/{ => javascript}/node/meta.json (78%) rename content/docs/references/{ => javascript}/node/meta.ko.json (78%) rename content/docs/references/{ => javascript}/node/protocol.ko.mdx (87%) rename content/docs/references/{ => javascript}/node/protocol.mdx (90%) rename content/docs/references/{ => javascript}/node/remote.ko.mdx (92%) rename content/docs/references/{ => javascript}/node/remote.mdx (94%) rename content/docs/references/{ => javascript}/node/source.ko.mdx (64%) rename content/docs/references/{ => javascript}/node/source.mdx (67%) rename content/docs/references/{ => javascript}/node/types.ko.mdx (82%) rename content/docs/references/{ => javascript}/node/types.mdx (84%) rename content/docs/references/{ => javascript}/node/updater.ko.mdx (82%) rename content/docs/references/{ => javascript}/node/updater.mdx (82%) create mode 100644 content/docs/references/javascript/remotes/meta.json create mode 100644 content/docs/references/javascript/remotes/meta.ko.json create mode 100644 content/docs/references/javascript/remotes/remote-aws-provider-pulumi.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-aws-provider-pulumi.mdx create mode 100644 content/docs/references/javascript/remotes/remote-aws-provider.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-aws-provider.mdx create mode 100644 content/docs/references/javascript/remotes/remote-aws.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-aws.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare-provider.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare-provider.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare-pulumi.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare-pulumi.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-cloudflare.mdx create mode 100644 content/docs/references/javascript/remotes/remote-local-provider.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-local-provider.mdx create mode 100644 content/docs/references/javascript/remotes/remote-local.ko.mdx create mode 100644 content/docs/references/javascript/remotes/remote-local.mdx create mode 100644 new-doc/guide/getting-started/introduction.md create mode 100644 new-doc/guide/getting-started/platform-support.md create mode 100644 new-doc/guide/getting-started/prerequisties.md create mode 100644 new-doc/guide/getting-started/start-with-and-example.md create mode 100644 public/diagrams/wvb-data-layout.svg create mode 100644 public/diagrams/wvb-index-lookup.svg create mode 100644 public/diagrams/wvb-structure.svg create mode 100644 src/layouts/NotFound.tsx create mode 100644 src/layouts/changelog/Changelog.tsx create mode 100644 src/layouts/changelog/ChangelogRoute.tsx create mode 100644 src/layouts/home/components/logos.tsx create mode 100644 src/lib/changelog-data.ts create mode 100644 src/lib/changelog.ts create mode 100644 src/routes/docs/changelog.tsx create mode 100644 src/routes/ko/docs/changelog.tsx 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/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 index 510e7e4..275ba20 100644 --- a/content/docs/guide/core-concepts/bundle-source.ko.mdx +++ b/content/docs/guide/core-concepts/bundle-source.ko.mdx @@ -9,7 +9,7 @@ title: 번들 소스 웹뷰 번들은 빌트인(`builtin`)과 리모트(`remote`) 번들 소스를 나눕니다. - **`builtin`** — 앱 패키지 _안에_ 포함되어 배포되는 번들입니다. 읽기 전용이며, 앱을 처음 실행할 때 아직 아무것도 다운로드되지 않은 상태에서 폴백으로 사용됩니다. -- **`remote`** — 리모트 서버에서 다운로드한 번들입니다. 쓰기 가능하며, 앱스토어 릴리스 없이 원격(OTA)으로 업데이트됩니다. +- **`remote`** — 리모트 서버에서 다운로드한 번들입니다. 쓰기 가능하며, 앱스토어 릴리스 없이 원격으로 업데이트됩니다. 번들이 두 소스에 모두 존재하면 **리모트가 우선합니다**. 소스는 리모트 매니페스트의 현재 버전을 먼저 확인하여 버전을 결정하며, 리모트에 없으면 빌트인으로 폴백합니다. diff --git a/content/docs/guide/core-concepts/bundle-source.mdx b/content/docs/guide/core-concepts/bundle-source.mdx index 51485cd..8960c35 100644 --- a/content/docs/guide/core-concepts/bundle-source.mdx +++ b/content/docs/guide/core-concepts/bundle-source.mdx @@ -1,3 +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 index 6b93f31..d994696 100644 --- 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 @@ -2,14 +2,17 @@ title: 네이티브와 웹뷰 간 통신 --- -(웹뷰에서 네이티브 기능을 사용하기 위한 어떻게 통신할 수 있는지에 대해 설명) +웹뷰의 웹 코드에서 네이티브 기능을 사용하려면 네이티브와 통신해야 합니다. 웹뷰 번들은 이를 위해 브릿지를 제공합니다. ## 브릿지 호출하기 -(`@wvb/bridge` 통합 브릿지 패키지를 사용하여 웹뷰에서 사용 가능) +웹뷰에서는 통합 브릿지 패키지 `@wvb/bridge`의 `invoke(name, params)`로 네이티브 기능을 호출합니다. -(브릿지 호출시 네이티브 단에 등록된 핸들러가 브릿지 호출을 처리해줌. 각 플랫폼/프레임워크 별 가이드) +호출은 네이티브 단에 등록된 핸들러가 처리하고 결과를 돌려줍니다. 플랫폼/프레임워크별 전달 방식은 다음과 같습니다. -(Electron: ipc / Tauri : ipc / Android : JavaScriptInterface / iOS : WKScriptMessageHandler / Deno Desktop : Bindings(CEF)) +- **Electron** — Electron IPC +- **Tauri** — Tauri 커맨드 +- **Android** — WebView 메시지 리스너(`WebViewCompat.addWebMessageListener`) +- **iOS** — `WKScriptMessageHandler` -(브릿지 API를 사용하는 자세한 방법은 /docs/frontend/bridges 로 가이드) +브릿지 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 index 96a7527..641e69b 100644 --- a/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx +++ b/content/docs/guide/core-concepts/communicate-between-native-and-webview.mdx @@ -1,3 +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 index df61481..56f3888 100644 --- a/content/docs/guide/core-concepts/glue-to-native.ko.mdx +++ b/content/docs/guide/core-concepts/glue-to-native.ko.mdx @@ -1,3 +1,51 @@ --- -title: 네이티브 글루(Glue) +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 index 3987253..14261cc 100644 --- a/content/docs/guide/core-concepts/glue-to-native.mdx +++ b/content/docs/guide/core-concepts/glue-to-native.mdx @@ -2,31 +2,50 @@ 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. -## Rust로 작성된 코어 +## A core written in Rust -코어 크레이트(`wvb`)는 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 -여러 플랫폼 및 환경을 지원하기 위해 `wvb` 크레이트는 바인딩되어 제공됩니다. 바인딩은 코어 크레이트인 `wvb`의 API를 그대로 노출하여 서로 다른 플랫폼/환경에서도 동일한 API를 사용할 수 있습니다. +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** -- [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)를 참고해주세요. + - 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** -- [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) 라이브러리에 포함돼 퍼블리싱 됩니다. + - 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** -- [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API를 이용해 동적으로 라이브러리를 불러와 사용합니다. + - Uses the [Deno FFI](https://docs.deno.com/runtime/fundamentals/ffi/) API to load the library dynamically. -자세한 API 레퍼런스는 [여기서](/docs/references) 확인해주세요. +See the full API reference [here](/docs/references). -## 네이티브에 붙이기 +## Glue to native -(코어 크레이트를 어떻게 네이티브 웹뷰 코드에 붙이는지, 안드로이드 WebView의 인터셉트 코드를 예시로 간략하게 설명) +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 index b59fb2a..5ace0eb 100644 --- a/content/docs/guide/core-concepts/over-the-air.ko.mdx +++ b/content/docs/guide/core-concepts/over-the-air.ko.mdx @@ -1,8 +1,8 @@ --- -title: Over-the-air +title: Over-The-Air --- -신규 웹뷰 번들을 원격에서 다운로드할 수 있습니다. 네이티브 앱 배포없이도 업데이트된 앱 코드를 웹뷰 번들 리모트 서버를 통해 배포할 수 있습니다. +신규 웹뷰 번들을 원격(Over-The-Air)에서 다운로드할 수 있습니다. 네이티브 앱 배포없이도 업데이트된 앱 코드를 웹뷰 번들 리모트 서버를 통해 배포할 수 있습니다. ## 업데이트 라이프사이클 @@ -28,9 +28,9 @@ wvb deploy --version 1.2.0 # 1.2.0을 현재 버전으로 배포 클라이언트에서 리모트 번들을 다운받아 리모트 [번들 소스](/docs/guide/core-concepts/bundle-source)에 설치하는 과정을 제어할 수 있습니다. 사용할 수 있는 API는 다음과 같습니다. -- **updater.getUpdate** : -- **updater.download** : -- **updater.install** : +- **updater.getUpdate** : 리모트의 현재 버전 정보를 조회(HEAD 요청)해 설치된 버전과 비교합니다. 다운로드는 하지 않으며, 업데이트 가능 여부와 버전 정보를 반환합니다. +- **updater.download** : 새 버전의 번들을 내려받아 검증한 뒤 디스크에 준비합니다. 이 단계에서는 아직 활성화되지 않습니다. +- **updater.install** : 내려받아 둔 버전을 현재 버전으로 활성화합니다. 설치한 뒤부터 해당 버전이 제공됩니다. 다운로드와 설치 과정이 분리되어 있어서, 앱의 비즈니스 요구사항에 맞춰 원하는 타이밍에 신규 번들을 반영할 수 있습니다. @@ -62,20 +62,48 @@ if update.is_available { sha256:n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg= ``` -(무결성 해시를 클라이언트에서 검증하는 방법. 검증 정책 설명) +클라이언트는 다운로드한 번들 데이터로 해시를 다시 계산해 리모트가 알려 준 무결성 해시와 일치하는지 확인합니다. 검증은 설치 전에 수행하며, 일치하지 않으면 설치가 실패합니다. 검증 정책은 세 가지입니다. + +- `strict` : 무결성 해시가 반드시 있어야 하며, 없거나 일치하지 않으면 실패합니다. +- `optional`(기본값) : 해시가 있으면 검증하고, 없으면 건너뜁니다. +- `none` : 무결성을 검증하지 않습니다. ### 서명 -(서명의 이유. 지원 알고리즘. 클라이언트에서 서명 검증하는 방법) +서명은 번들이 개인 키 소유자가 배포한 것임을 증명합니다. 무결성 해시는 데이터가 특정 해시와 일치하는지만 보장하지만, 서명은 배포자의 진위까지 확인합니다. 서명은 무결성 문자열에 대해 생성되므로, 서명을 사용하려면 무결성을 함께 활성화해야 합니다. + +지원하는 알고리즘은 다음과 같습니다. + +- ECDSA (P-256, P-384) +- Ed25519 +- RSA (PKCS#1 v1.5, PSS) + +서명 검증은 선택 사항입니다. 클라이언트에 공개 키로 서명 검증기를 설정하면, 다운로드한 번들의 서명을 무결성 문자열과 대조해 확인하고, 검증에 실패하면 설치를 중단합니다. ## 리모트 -(리모트 서버에 대한 설명) +리모트는 클라이언트가 번들과 메타데이터를 내려받는 HTTP 서버입니다. 엔드포인트 URL로 구성하며, 번들 목록 조회, 현재 버전 메타데이터 조회(HEAD), 현재 버전 및 특정 버전 다운로드를 제공합니다. 버전·무결성·서명 값은 응답 헤더로 전달됩니다. ### 제공자 -(기본 제공되는 AWS, Cloudflare 리모트 서버에 대한 설명. 직접 만드는 경우에는 리모트 문서를 읽으라고 가이드) +번들 서버를 직접 운영하지 않아도 되도록 기본 제공자를 제공합니다. + +- **AWS** — `@wvb/remote-aws` +- **Cloudflare** — `@wvb/remote-cloudflare` + +리모트 HTTP 규약을 구현하면 직접 제공자를 만들 수도 있습니다. 자세한 내용은 [리모트](/docs/guide/remote) 문서를 참고하세요. ### 채널 -(채널에 대한 설명. Rust코드로 클라이언트에서 채널 지정하는 방법) +채널은 배포를 구분하는 이름표입니다(예: `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 index e1b603a..7a1b4e0 100644 --- a/content/docs/guide/core-concepts/over-the-air.mdx +++ b/content/docs/guide/core-concepts/over-the-air.mdx @@ -1,3 +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.mdx b/content/docs/guide/core-concepts/protocol-handling.mdx index 3bc53e3..f7022ab 100644 --- a/content/docs/guide/core-concepts/protocol-handling.mdx +++ b/content/docs/guide/core-concepts/protocol-handling.mdx @@ -1,3 +1,93 @@ --- -title: Protocol handling +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/frontend/bridges/checking-sources.ko.mdx b/content/docs/guide/frontend/bridges/checking-sources.ko.mdx new file mode 100644 index 0000000..a2a1c2c --- /dev/null +++ b/content/docs/guide/frontend/bridges/checking-sources.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '소스 확인하기' +--- + +이 문서는 작성 중입니다. 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..9e394d1 --- /dev/null +++ b/content/docs/guide/frontend/bridges/checking-sources.mdx @@ -0,0 +1,5 @@ +--- +title: 'Checking the sources' +--- + +This page is a work in progress. 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..c533a12 --- /dev/null +++ b/content/docs/guide/frontend/bridges/index.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '개요' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/frontend/bridges/index.mdx b/content/docs/guide/frontend/bridges/index.mdx new file mode 100644 index 0000000..e8e5951 --- /dev/null +++ b/content/docs/guide/frontend/bridges/index.mdx @@ -0,0 +1,5 @@ +--- +title: 'Overview' +--- + +This page is a work in progress. 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..eb1c584 --- /dev/null +++ b/content/docs/guide/frontend/bridges/testing.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '테스트' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/frontend/bridges/testing.mdx b/content/docs/guide/frontend/bridges/testing.mdx new file mode 100644 index 0000000..7d09703 --- /dev/null +++ b/content/docs/guide/frontend/bridges/testing.mdx @@ -0,0 +1,5 @@ +--- +title: 'Testing' +--- + +This page is a work in progress. 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..3504cea --- /dev/null +++ b/content/docs/guide/frontend/bridges/update-remote-bundles.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '리모트 번들 업데이트' +--- + +이 문서는 작성 중입니다. 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..837a9b6 --- /dev/null +++ b/content/docs/guide/frontend/bridges/update-remote-bundles.mdx @@ -0,0 +1,5 @@ +--- +title: 'Update remote bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/frontend/deploy.ko.mdx b/content/docs/guide/frontend/deploy.ko.mdx new file mode 100644 index 0000000..aca4ca3 --- /dev/null +++ b/content/docs/guide/frontend/deploy.ko.mdx @@ -0,0 +1,3 @@ +--- +title: 배포하기 +--- diff --git a/content/docs/guide/frontend/deploy.mdx b/content/docs/guide/frontend/deploy.mdx new file mode 100644 index 0000000..b5414d2 --- /dev/null +++ b/content/docs/guide/frontend/deploy.mdx @@ -0,0 +1,3 @@ +--- +title: Deploy +--- diff --git a/content/docs/guide/frontend/development.ko.mdx b/content/docs/guide/frontend/development.ko.mdx new file mode 100644 index 0000000..2eac542 --- /dev/null +++ b/content/docs/guide/frontend/development.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '개발' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/frontend/development.mdx b/content/docs/guide/frontend/development.mdx new file mode 100644 index 0000000..8ec74d7 --- /dev/null +++ b/content/docs/guide/frontend/development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Development' +--- + +This page is a work in progress. diff --git a/content/docs/guide/frontend/packing.ko.mdx b/content/docs/guide/frontend/packing.ko.mdx new file mode 100644 index 0000000..02d1d8c --- /dev/null +++ b/content/docs/guide/frontend/packing.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '패킹' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/frontend/packing.mdx b/content/docs/guide/frontend/packing.mdx new file mode 100644 index 0000000..49dcea6 --- /dev/null +++ b/content/docs/guide/frontend/packing.mdx @@ -0,0 +1,5 @@ +--- +title: 'Packing' +--- + +This page is a work in progress. diff --git a/content/docs/guide/frontend/upload.ko.mdx b/content/docs/guide/frontend/upload.ko.mdx new file mode 100644 index 0000000..30e111b --- /dev/null +++ b/content/docs/guide/frontend/upload.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '리모트에 업로드' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/frontend/upload.mdx b/content/docs/guide/frontend/upload.mdx new file mode 100644 index 0000000..4409ea7 --- /dev/null +++ b/content/docs/guide/frontend/upload.mdx @@ -0,0 +1,5 @@ +--- +title: 'Upload to remote' +--- + +This page is a work in progress. diff --git a/content/docs/guide/getting-started/example.ko.mdx b/content/docs/guide/getting-started/example.ko.mdx index c010293..45cd197 100644 --- a/content/docs/guide/getting-started/example.ko.mdx +++ b/content/docs/guide/getting-started/example.ko.mdx @@ -27,3 +27,8 @@ 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 index 7a9abe1..cdfec1f 100644 --- a/content/docs/guide/getting-started/example.mdx +++ b/content/docs/guide/getting-started/example.mdx @@ -1,5 +1,34 @@ --- -title: 'Start with an example' +title: Start with an example --- -This page is a work in progress. +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 index 3b16ce5..466a321 100644 --- a/content/docs/guide/getting-started/index.ko.mdx +++ b/content/docs/guide/getting-started/index.ko.mdx @@ -8,21 +8,21 @@ title: 소개 웹뷰 번들은 웹뷰 기반 프레임워크 및 플랫폼을 위한 "오프라인 우선(Offline First)" 방식의 웹 애플리케이션 배포 시스템입니다. -웹 애플리케이션 코드를 네트워크를 통해 불러와 웹뷰에 런더하는 대신, 웹뷰 번들 파일(`.wvb`)로 패킹하여 로컬에서 리소스를 불러와 웹뷰에 렌더합니다. 또한, 리모트를 구성하여 원격으로 웹뷰 번들을 다운받아 최신상태의 애플리케이션 코드를 네이티브 배포 없이 효과적으로 업데이트할 수 있습니다. +웹 애플리케이션 코드를 네트워크를 통해 불러와 웹뷰에 렌더하는 대신, 웹뷰 번들 파일(`.wvb`)로 패킹하여 로컬에서 리소스를 불러와 웹뷰에 렌더합니다. 또한, 리모트를 구성하여 원격으로 웹뷰 번들을 다운받아 최신상태의 애플리케이션 코드를 네이티브 배포 없이 효과적으로 업데이트할 수 있습니다. -웹뷰 번들은 웹뷰가 제공되는 [Electron](https://electronjs.org), [Tauri](https://tauri.app), Android, iOS, 그리고 [Deno Desktop](https://deno.com/blog/v2.9#deno-desktop)(실험적)을 지원하며, 웹뷰와 통신할 수 있는 [브릿지](...) 또한 제공됩니다. +웹뷰 번들은 웹뷰가 제공되는 [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`) 포맷으로 패킹하여, 네이티브 앱 번들에 포함시킵니다. 네이티브에서는 웹뷰의 요청을 인터셉트하여 네트워크 요청을 웹뷰 번들 파일 내의 리소스를 읽어 응답합니다. 따라서, 네트워크 요청이 인터넷을 통할 필요없이 로컬데이터를 읽어 응답하므로 오프라인에서도 웹 애플리케이션이 동작할 수 있습니다. -### OTA(Over-the-air) +### 원격 -네이티브 앱 번들에 포함된 번들을 최신 상태로 업데이트하기 위해 웹뷰 번들 시스템은 OTA 기술을 제공합니다. [리모트](...) 제공자를 만들어 배포할 번들을 업로드할 수 있고, 프론트엔드 혹은 네이티브에서 원하는 타이밍에 신규 번들을 다운받아 최신 상태를 유지할 수 있습니다. +네이티브 앱 번들에 포함된 번들을 최신 상태로 업데이트하기 위해 웹뷰 번들 시스템은 원격 기술을 제공합니다. [리모트](/docs/guide/core-concepts/over-the-air) 제공자를 만들어 배포할 번들을 업로드할 수 있고, 프론트엔드 혹은 네이티브에서 원하는 타이밍에 신규 번들을 다운받아 최신 상태를 유지할 수 있습니다. 웹뷰 번들을 원격으로 업데이트하는 과정은 네이티브 앱 배포 혹은 앱 재시작없이 수행되기에 UX를 해치지 않고 최신 상태를 유지할 수 있습니다. 예를들어, Electron은 업데이트 하려면 수십MB를 다운받고 앱을 재시작해야 업데이트가 반영되는데, 웹뷰 번들을 이용하면 이 과정을 생략할 수 있습니다. diff --git a/content/docs/guide/getting-started/index.mdx b/content/docs/guide/getting-started/index.mdx index 8208e77..1f45b7b 100644 --- a/content/docs/guide/getting-started/index.mdx +++ b/content/docs/guide/getting-started/index.mdx @@ -6,11 +6,11 @@ title: Introduction ## Overview -Webview Bundle is an offline-first delivery system for web applications on webview-based frameworks and platforms. +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](...) for communicating with the webview. +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 @@ -22,7 +22,7 @@ On the native side, the host intercepts the webview's requests and answers them ### 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](...) provider, upload the bundles you want to ship, and download new bundles from the frontend or the native side whenever you choose. +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. 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 index 6d5ea35..b5e71a9 100644 --- a/content/docs/guide/getting-started/platform-and-framework-support.ko.mdx +++ b/content/docs/guide/getting-started/platform-and-framework-support.ko.mdx @@ -15,4 +15,4 @@ description: Webview Bundle을 실행하는 플랫폼/프레임워크에 대해 | 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 index 43314df..acdcfbf 100644 --- a/content/docs/guide/getting-started/platform-and-framework-support.mdx +++ b/content/docs/guide/getting-started/platform-and-framework-support.mdx @@ -1,4 +1,18 @@ --- -title: Platform/Framework Support -description: Which platform/framework(s) run Webview Bundle and the package to install for each. +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 index 10c0cb8..cd93292 100644 --- a/content/docs/guide/getting-started/prerequisites.ko.mdx +++ b/content/docs/guide/getting-started/prerequisites.ko.mdx @@ -2,8 +2,6 @@ title: 필요 사항 --- -# 필요 사항 - 프로젝트를 시작하려면 먼저 몇 가지 종속성을 설치해야 합니다. ## Node.js @@ -33,7 +31,7 @@ iOS 앱 개발을 위해서 [Xcode](https://developer.apple.com/xcode/) 설치 ## Deno -[Deno Desktop](...)을 사용한다면 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 index a5b8246..dcf4948 100644 --- a/content/docs/guide/getting-started/prerequisites.mdx +++ b/content/docs/guide/getting-started/prerequisites.mdx @@ -1,3 +1,38 @@ --- -title: 'Prerequisites' +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 96f5659..8c15bc1 100644 --- a/content/docs/guide/meta.json +++ b/content/docs/guide/meta.json @@ -24,6 +24,7 @@ "frontend/development", "frontend/packing", "frontend/upload", + "frontend/deploy", "frontend/bridges", "---Remote---", "remote/index", diff --git a/content/docs/guide/meta.ko.json b/content/docs/guide/meta.ko.json index 1d722f5..0035fc2 100644 --- a/content/docs/guide/meta.ko.json +++ b/content/docs/guide/meta.ko.json @@ -24,11 +24,12 @@ "frontend/development", "frontend/packing", "frontend/upload", + "frontend/deploy", "frontend/bridges", "---원격---", - "remote", - "remote-using-cli", - "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..beb43ea --- /dev/null +++ b/content/docs/guide/native/android/builtin.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '내장 번들' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/android/builtin.mdx b/content/docs/guide/native/android/builtin.mdx new file mode 100644 index 0000000..dec9414 --- /dev/null +++ b/content/docs/guide/native/android/builtin.mdx @@ -0,0 +1,5 @@ +--- +title: 'Builtin bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/platforms/android.ko.mdx b/content/docs/guide/native/android/index.ko.mdx similarity index 98% rename from content/docs/guide/platforms/android.ko.mdx rename to content/docs/guide/native/android/index.ko.mdx index c65a787..ff94215 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-support)을 + 참고하세요. ## 빠른 시작 @@ -314,14 +315,14 @@ 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/over-the-air)과 [원격 구축하기](/docs/guide/remote) 가이드를 참고하세요. ## 다음 단계 - See [Platform support](/docs/guide/platform-support) for the status of every platform. + See [Platform support](/docs/guide/getting-started/platform-support) for the status of every + platform. ## Quick start @@ -314,14 +315,14 @@ 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). +For the remote HTTP contract, integrity, and signatures across platforms, see [Remote bundles](/docs/guide/over-the-air) and the guide to [building a remote](/docs/guide/remote). ## Next steps 이 문서는 작성 중입니다. 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..e03f1bc --- /dev/null +++ b/content/docs/guide/native/android/local-development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Local development' +--- + +This page is a work in progress. 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..e178252 --- /dev/null +++ b/content/docs/guide/native/deno/building.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '빌드' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/deno/building.mdx b/content/docs/guide/native/deno/building.mdx new file mode 100644 index 0000000..fb758b2 --- /dev/null +++ b/content/docs/guide/native/deno/building.mdx @@ -0,0 +1,5 @@ +--- +title: 'Building' +--- + +This page is a work in progress. 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..beb43ea --- /dev/null +++ b/content/docs/guide/native/deno/builtin.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '내장 번들' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/deno/builtin.mdx b/content/docs/guide/native/deno/builtin.mdx new file mode 100644 index 0000000..dec9414 --- /dev/null +++ b/content/docs/guide/native/deno/builtin.mdx @@ -0,0 +1,5 @@ +--- +title: 'Builtin bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/platforms/deno.ko.mdx b/content/docs/guide/native/deno/index.ko.mdx similarity index 90% rename from content/docs/guide/platforms/deno.ko.mdx rename to content/docs/guide/native/deno/index.ko.mdx index de7ceb3..49f2897 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 아카이브를 제공하고 업데이트합니다. --- @@ -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/javascript/node)의 FFI 짝으로, 공유 Rust 코어를 구동합니다. ## 구성 방식 -창은 `Deno.serve`가 제공하는 단일 로컬 origin을 가리킵니다. 따라서 이 통합은 **단일 origin**이며, 정확히 하나의 프로토콜만 허용합니다. 코어가 각 플랫폼에 도달하는 방식은 [플랫폼 통합](/docs/guide/platform-integration)을, 프로토콜 모델은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. +창은 `Deno.serve`가 제공하는 단일 로컬 origin을 가리킵니다. 따라서 이 통합은 **단일 origin**이며, 정확히 하나의 프로토콜만 허용합니다. 코어가 각 플랫폼에 도달하는 방식은 [플랫폼 통합](/docs/guide/glue-to-native)을, 프로토콜 모델은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. ## @wvb/deno-desktop @@ -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/javascript/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/javascript/deno)를 참고하세요. ## 제한 사항 @@ -185,24 +185,24 @@ const wvb = await webviewBundle({ }); ``` -무결성과 서명이 플랫폼 전반에서 동작하는 방식은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. +무결성과 서명이 플랫폼 전반에서 동작하는 방식은 [원격 번들](/docs/guide/over-the-air)을 참고하세요. ## 다음 단계 diff --git a/content/docs/guide/platforms/deno.mdx b/content/docs/guide/native/deno/index.mdx similarity index 89% rename from content/docs/guide/platforms/deno.mdx rename to content/docs/guide/native/deno/index.mdx index 5bb4d53..069e429 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. --- @@ -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/javascript/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/glue-to-native) for how the core reaches each platform and [Protocol handling](/docs/guide/protocol-handling) for the protocol model. ## @wvb/deno-desktop @@ -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/javascript/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/javascript/deno). ## Limitations @@ -185,24 +185,24 @@ const wvb = await webviewBundle({ }); ``` -For how integrity and signatures work across platforms, see [Remote bundles](/docs/guide/remote-bundles). +For how integrity and signatures work across platforms, see [Remote bundles](/docs/guide/over-the-air). ## Next steps 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..8f63aaf --- /dev/null +++ b/content/docs/guide/native/deno/local-development.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '로컬 개발' +--- + +이 문서는 작성 중입니다. 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..e03f1bc --- /dev/null +++ b/content/docs/guide/native/deno/local-development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Local development' +--- + +This page is a work in progress. 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 93% rename from content/docs/guide/platforms/electron/builder.ko.mdx rename to content/docs/guide/native/electron/builder.ko.mdx index ba2d6ad..8eaa1ae 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에서 제외하기 @@ -88,17 +88,17 @@ export default withWebviewBundle( 이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/electron/builtin.mdx b/content/docs/guide/native/electron/builtin.mdx new file mode 100644 index 0000000..dec9414 --- /dev/null +++ b/content/docs/guide/native/electron/builtin.mdx @@ -0,0 +1,5 @@ +--- +title: 'Builtin bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/platforms/electron/forge.ko.mdx b/content/docs/guide/native/electron/forge.ko.mdx similarity index 88% rename from content/docs/guide/platforms/electron/forge.ko.mdx rename to content/docs/guide/native/electron/forge.ko.mdx index 6dc3f87..63472ec 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를 유지해 네이티브 모듈을 번들에 포함하기 @@ -86,22 +86,22 @@ const config: ForgeConfig = { diff --git a/content/docs/guide/platforms/electron/forge.mdx b/content/docs/guide/native/electron/forge.mdx similarity index 90% rename from content/docs/guide/platforms/electron/forge.mdx rename to content/docs/guide/native/electron/forge.mdx index 3cecc07..9c342dd 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 @@ -86,22 +86,22 @@ const config: ForgeConfig = { diff --git a/content/docs/guide/platforms/electron/index.ko.mdx b/content/docs/guide/native/electron/index.ko.mdx similarity index 96% rename from content/docs/guide/platforms/electron/index.ko.mdx rename to content/docs/guide/native/electron/index.ko.mdx index eeb8770..dc32f96 100644 --- a/content/docs/guide/platforms/electron/index.ko.mdx +++ b/content/docs/guide/native/electron/index.ko.mdx @@ -1,5 +1,5 @@ --- -title: Electron +title: 설정 description: 커스텀 프로토콜을 통해 Electron UI를 .wvb 번들에서 제공하고, 개발 서버 프록시와 무선(OTA) 업데이트를 활용하세요. --- @@ -137,7 +137,8 @@ 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/javascript/node)에 + 있습니다. `wvb(...)`가 반환하는 인스턴스에서 메인 프로세스의 동일한 인터페이스에 접근할 수 있습니다. @@ -178,7 +179,7 @@ updater: { }, ``` -`SignatureAlgorithm`은 `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss` 중 하나입니다. 무결성과 서명에 대한 자세한 내용은 [원격 번들](/docs/guide/remote-bundles)을, 서버를 세우는 방법(테스트용 로컬 서버 포함)은 [원격 서버 구축](/docs/guide/remote)을 참고하세요. +`SignatureAlgorithm`은 `ecdsaSecp256R1`, `ecdsaSecp384R1`, `ed25519`, `rsaPkcs1V15`, `rsaPss` 중 하나입니다. 무결성과 서명에 대한 자세한 내용은 [원격 번들](/docs/guide/over-the-air)을, 서버를 세우는 방법(테스트용 로컬 서버 포함)은 [원격 서버 구축](/docs/guide/remote)을 참고하세요. ## Where bundles live @@ -209,12 +210,12 @@ npx wvb pack ./dist --outfile bundles/app/app_1.0.0.wvb @@ -231,7 +232,7 @@ npx wvb pack ./dist --outfile bundles/app/app_1.0.0.wvb diff --git a/content/docs/guide/platforms/electron/index.mdx b/content/docs/guide/native/electron/index.mdx similarity index 95% rename from content/docs/guide/platforms/electron/index.mdx rename to content/docs/guide/native/electron/index.mdx index 4c60aef..daf3ce8 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. --- @@ -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/javascript/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/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 @@ -209,12 +209,12 @@ Packaging an Electron app that uses `@wvb/electron` needs two things to land in @@ -231,7 +231,7 @@ Packaging an Electron app that uses `@wvb/electron` needs two things to land in 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..8f63aaf --- /dev/null +++ b/content/docs/guide/native/electron/local-development.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '로컬 개발' +--- + +이 문서는 작성 중입니다. 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..e03f1bc --- /dev/null +++ b/content/docs/guide/native/electron/local-development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Local development' +--- + +This page is a work in progress. 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..beb43ea --- /dev/null +++ b/content/docs/guide/native/ios/builtin.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '내장 번들' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/ios/builtin.mdx b/content/docs/guide/native/ios/builtin.mdx new file mode 100644 index 0000000..dec9414 --- /dev/null +++ b/content/docs/guide/native/ios/builtin.mdx @@ -0,0 +1,5 @@ +--- +title: 'Builtin bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/platforms/ios.ko.mdx b/content/docs/guide/native/ios/index.ko.mdx similarity index 98% rename from content/docs/guide/platforms/ios.ko.mdx rename to content/docs/guide/native/ios/index.ko.mdx index 24c63f1..080565e 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 아카이브를 제공하고 업데이트합니다. --- @@ -18,7 +18,7 @@ description: webview-bundle-ios Swift 패키지로 WKWebView에서 Webview Bundl 이 패키지는 `WebViewBundleLibrary`라는 UniFFI 생성 모듈을 통해 Rust 코어에 바인딩되며, 둘 다 `WebViewBundle` 모듈로 노출됩니다. 공유 코어가 각 플랫폼에 어떻게 도달하는지는 -[플랫폼 통합](/docs/guide/platform-integration)을 참고하세요. +[플랫폼 통합](/docs/guide/glue-to-native)을 참고하세요. 패키지를 의존성으로 추가하세요(네이티브 FFI는 아래 설치 스크립트가 연결합니다). @@ -227,7 +227,7 @@ await webView.evaluateJavaScript("location.reload()") 서명 실패는 FFI 오류 `Error.Signature(message:)`로 나타납니다. -원격 HTTP 규약, 무결성, 서명 세부 사항은 [원격 번들](/docs/guide/remote-bundles)과 +원격 HTTP 규약, 무결성, 서명 세부 사항은 [원격 번들](/docs/guide/over-the-air)과 [원격 구축하기](/docs/guide/remote)를 참고하세요. ## Next steps @@ -235,7 +235,7 @@ await webView.evaluateJavaScript("location.reload()") 이 문서는 작성 중입니다. 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..e03f1bc --- /dev/null +++ b/content/docs/guide/native/ios/local-development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Local development' +--- + +This page is a work in progress. 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..beb43ea --- /dev/null +++ b/content/docs/guide/native/tauri/builtin.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '내장 번들' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/tauri/builtin.mdx b/content/docs/guide/native/tauri/builtin.mdx new file mode 100644 index 0000000..dec9414 --- /dev/null +++ b/content/docs/guide/native/tauri/builtin.mdx @@ -0,0 +1,5 @@ +--- +title: 'Builtin bundles' +--- + +This page is a work in progress. diff --git a/content/docs/guide/platforms/tauri.ko.mdx b/content/docs/guide/native/tauri/index.ko.mdx similarity index 96% rename from content/docs/guide/platforms/tauri.ko.mdx rename to content/docs/guide/native/tauri/index.ko.mdx index 81b4b58..e25abdb 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) 업데이트를 사용할 수 있습니다. --- @@ -158,7 +158,7 @@ resources에 디렉터리를 나열하여 빌드와 함께 배포하세요. } ``` -전체 패킹 워크플로와 플래그는 [CLI 레퍼런스](/docs/guide/cli)와 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. +전체 패킹 워크플로와 플래그는 [CLI 레퍼런스](/docs/references/cli)와 [번들 소스](/docs/guide/bundle-sources)를 참고하세요. ## 프런트엔드에서 업데이트 제어 @@ -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/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-support)을 참고하세요. ## 문제 해결 @@ -346,12 +346,12 @@ tauri::Builder::default() /> 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-support) for current status. ## Troubleshooting @@ -346,12 +346,12 @@ The plugin then extracts each bundle from the APK on first request and caches it /> 이 문서는 작성 중입니다. 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..e03f1bc --- /dev/null +++ b/content/docs/guide/native/tauri/local-development.mdx @@ -0,0 +1,5 @@ +--- +title: 'Local development' +--- + +This page is a work in progress. 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..a97b366 --- /dev/null +++ b/content/docs/guide/native/tauri/mobile.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '모바일' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/guide/native/tauri/mobile.mdx b/content/docs/guide/native/tauri/mobile.mdx new file mode 100644 index 0000000..5eba06a --- /dev/null +++ b/content/docs/guide/native/tauri/mobile.mdx @@ -0,0 +1,5 @@ +--- +title: 'For mobile' +--- + +This page is a work in progress. 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/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 95% 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..d6904f7 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/javascript/node/updater)는 검사 결과 새 버전이 있다고 확인되면 이 호출로 새 버전을 가져옵니다. ```http GET /bundles/{name} diff --git a/content/docs/guide/spec/get-current.mdx b/content/docs/guide/remote/http-spec/endpoints/get-current.mdx similarity index 96% rename from content/docs/guide/spec/get-current.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-current.mdx index e182f6f..bae8a64 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/javascript/node/updater) makes to fetch a new version after a check reports one is available. ```http GET /bundles/{name} 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 100% rename from content/docs/guide/spec/get-version.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-version.ko.mdx diff --git a/content/docs/guide/spec/get-version.mdx b/content/docs/guide/remote/http-spec/endpoints/get-version.mdx similarity index 100% rename from content/docs/guide/spec/get-version.mdx rename to content/docs/guide/remote/http-spec/endpoints/get-version.mdx 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 100% rename from content/docs/guide/spec/head-current.ko.mdx rename to content/docs/guide/remote/http-spec/endpoints/head-current.ko.mdx diff --git a/content/docs/guide/spec/head-current.mdx b/content/docs/guide/remote/http-spec/endpoints/head-current.mdx similarity index 100% rename from content/docs/guide/spec/head-current.mdx rename to content/docs/guide/remote/http-spec/endpoints/head-current.mdx 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 98% 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..4ca156d 100644 --- a/content/docs/guide/spec/list-bundles.ko.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/list-bundles.ko.mdx @@ -91,7 +91,7 @@ curl -s 'http://localhost:4313/bundles?channel=beta' /> diff --git a/content/docs/guide/spec/list-bundles.mdx b/content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx similarity index 98% rename from content/docs/guide/spec/list-bundles.mdx rename to content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx index c3fc6fc..6c7e242 100644 --- a/content/docs/guide/spec/list-bundles.mdx +++ b/content/docs/guide/remote/http-spec/endpoints/list-bundles.mdx @@ -91,7 +91,7 @@ curl -s 'http://localhost:4313/bundles?channel=beta' /> 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..e18cd42 --- /dev/null +++ b/content/docs/guide/remote/http-spec/endpoints/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Endpoints", + "pages": [] +} 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..fc964e4 --- /dev/null +++ b/content/docs/guide/remote/http-spec/endpoints/meta.ko.json @@ -0,0 +1,4 @@ +{ + "title": "엔드포인트", + "pages": [] +} 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..9d765ae --- /dev/null +++ b/content/docs/guide/remote/http-spec/errors.ko.mdx @@ -0,0 +1,3 @@ +--- +title: 오류 +--- 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..71f1725 --- /dev/null +++ b/content/docs/guide/remote/http-spec/errors.mdx @@ -0,0 +1,3 @@ +--- +title: Errors +--- 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..d28d21c --- /dev/null +++ b/content/docs/guide/remote/http-spec/index.ko.mdx @@ -0,0 +1,3 @@ +--- +title: HTTP 규약 +--- 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..118bfcf --- /dev/null +++ b/content/docs/guide/remote/http-spec/index.mdx @@ -0,0 +1,3 @@ +--- +title: HTTP spec +--- 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..9704c60 --- /dev/null +++ b/content/docs/guide/remote/index.ko.mdx @@ -0,0 +1,3 @@ +--- +title: 개요 +--- diff --git a/content/docs/guide/remote/index.mdx b/content/docs/guide/remote/index.mdx new file mode 100644 index 0000000..7137deb --- /dev/null +++ b/content/docs/guide/remote/index.mdx @@ -0,0 +1,3 @@ +--- +title: Overview +--- 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..d048a47 --- /dev/null +++ b/content/docs/guide/remote/providers/aws.ko.mdx @@ -0,0 +1,3 @@ +--- +title: AWS +--- diff --git a/content/docs/guide/remote/providers/aws.mdx b/content/docs/guide/remote/providers/aws.mdx new file mode 100644 index 0000000..d048a47 --- /dev/null +++ b/content/docs/guide/remote/providers/aws.mdx @@ -0,0 +1,3 @@ +--- +title: AWS +--- 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..0c96470 --- /dev/null +++ b/content/docs/guide/remote/providers/cloudflare.ko.mdx @@ -0,0 +1,3 @@ +--- +title: Cloudflare +--- diff --git a/content/docs/guide/remote/providers/cloudflare.mdx b/content/docs/guide/remote/providers/cloudflare.mdx new file mode 100644 index 0000000..0c96470 --- /dev/null +++ b/content/docs/guide/remote/providers/cloudflare.mdx @@ -0,0 +1,3 @@ +--- +title: Cloudflare +--- 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..3d1b112 --- /dev/null +++ b/content/docs/guide/remote/providers/local.ko.mdx @@ -0,0 +1,3 @@ +--- +title: Local +--- diff --git a/content/docs/guide/remote/providers/local.mdx b/content/docs/guide/remote/providers/local.mdx new file mode 100644 index 0000000..3d1b112 --- /dev/null +++ b/content/docs/guide/remote/providers/local.mdx @@ -0,0 +1,3 @@ +--- +title: Local +--- 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..fbfd4e2 --- /dev/null +++ b/content/docs/guide/remote/using-cli.ko.mdx @@ -0,0 +1,3 @@ +--- +title: CLI 사용하기 +--- diff --git a/content/docs/guide/remote/using-cli.mdx b/content/docs/guide/remote/using-cli.mdx new file mode 100644 index 0000000..89d97f9 --- /dev/null +++ b/content/docs/guide/remote/using-cli.mdx @@ -0,0 +1,3 @@ +--- +title: Using CLI +--- 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/android.ko.mdx b/content/docs/references/android.ko.mdx new file mode 100644 index 0000000..5ff92d2 --- /dev/null +++ b/content/docs/references/android.ko.mdx @@ -0,0 +1,5 @@ +--- +title: 'Android' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/android.mdx b/content/docs/references/android.mdx new file mode 100644 index 0000000..8029be3 --- /dev/null +++ b/content/docs/references/android.mdx @@ -0,0 +1,5 @@ +--- +title: 'Android' +--- + +This page is a work in progress. 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/guide/commands/builtin.ko.mdx b/content/docs/references/cli/builtin.ko.mdx similarity index 92% rename from content/docs/guide/commands/builtin.ko.mdx rename to content/docs/references/cli/builtin.ko.mdx index 400ea20..0901111 100644 --- a/content/docs/guide/commands/builtin.ko.mdx +++ b/content/docs/references/cli/builtin.ko.mdx @@ -3,7 +3,7 @@ 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)를 참고하세요. @@ -54,7 +54,7 @@ wvb builtin --no-write ## Notes -- 대상 유형은 [설정](/docs/config)의 `builtin.target`에서 결정되며, `remote`와 `local`의 구별된 유니온입니다. 기본값은 원격 대상입니다. +- 대상 유형은 [설정](/docs/references/configuration)의 `builtin.target`에서 결정되며, `remote`와 `local`의 구별된 유니온입니다. 기본값은 원격 대상입니다. - `--endpoint`, `--channel`, `--concurrency`, `--progress`는 원격 대상에만 적용됩니다. - 실행에 성공하면 `manifest.json` 하나와, 설치된 번들마다 `/_.wvb` 파일 하나를 기록합니다. @@ -63,7 +63,7 @@ wvb builtin --no-write diff --git a/content/docs/guide/commands/builtin.mdx b/content/docs/references/cli/builtin.mdx similarity index 93% rename from content/docs/guide/commands/builtin.mdx rename to content/docs/references/cli/builtin.mdx index 50d67eb..1006012 100644 --- a/content/docs/guide/commands/builtin.mdx +++ b/content/docs/references/cli/builtin.mdx @@ -3,7 +3,7 @@ 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). @@ -54,7 +54,7 @@ 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. @@ -63,7 +63,7 @@ Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation, 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..8179a1d --- /dev/null +++ b/content/docs/references/cli/installation.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '설치' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/cli/installation.mdx b/content/docs/references/cli/installation.mdx new file mode 100644 index 0000000..572b5ce --- /dev/null +++ b/content/docs/references/cli/installation.mdx @@ -0,0 +1,5 @@ +--- +title: 'Installation' +--- + +This page is a work in progress. 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 85% rename from content/docs/guide/commands/pack.ko.mdx rename to content/docs/references/cli/pack.ko.mdx index abf9d18..57bff39 100644 --- a/content/docs/guide/commands/pack.ko.mdx +++ b/content/docs/references/cli/pack.ko.mdx @@ -5,7 +5,7 @@ description: 빌드된 웹 자산 디렉터리를 압축되고 무결성이 검 `wvb pack`은 빌드된 웹 자산(HTML, JS, CSS, 미디어)이 담긴 디렉터리를 단일 `.wvb` 아카이브로 묶습니다. 이 아카이브는 압축되고 체크섬이 계산됩니다. 앱은 이 아카이브를 배포하여 웹뷰에 제공하거나, 무선(OTA) 업데이트를 위해 원격 서버에 업로드합니다. -대부분의 프로젝트는 `wvb pack`을 인자 없이 실행합니다. 소스 디렉터리, 출력 경로를 비롯한 기본값은 작업 디렉터리에서 발견된 [`wvb.config`](/docs/config) 파일에서 가져옵니다. +대부분의 프로젝트는 `wvb pack`을 인자 없이 실행합니다. 소스 디렉터리, 출력 경로를 비롯한 기본값은 작업 디렉터리에서 발견된 [`wvb.config`](/docs/references/configuration) 파일에서 가져옵니다. ## Usage @@ -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`에서 스코프를 제거한 값입니다. @@ -60,29 +60,29 @@ wvb pack ./dist --no-write ## Configuration -[`wvb.config`](/docs/config) 파일에 `pack` 기본값을 설정하면 명령어를 인자 없이 깔끔하게 실행할 수 있습니다. 출력 경로에 해당하는 설정 필드는 `outFile`이며(단일 경로이고, `.wvb`가 자동으로 붙습니다), `overwrite`의 기본값은 `true`입니다. 명령줄에서 전달한 플래그는 항상 설정값보다 우선합니다. +[`wvb.config`](/docs/references/configuration) 파일에 `pack` 기본값을 설정하면 명령어를 인자 없이 깔끔하게 실행할 수 있습니다. 출력 경로에 해당하는 설정 필드는 `outFile`이며(단일 경로이고, `.wvb`가 자동으로 붙습니다), `overwrite`의 기본값은 `true`입니다. 명령줄에서 전달한 플래그는 항상 설정값보다 우선합니다. ## See also diff --git a/content/docs/guide/commands/pack.mdx b/content/docs/references/cli/pack.mdx similarity index 83% rename from content/docs/guide/commands/pack.mdx rename to content/docs/references/cli/pack.mdx index 27a6e5c..5e9a646 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,29 @@ 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. +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. ## See also diff --git a/content/docs/guide/cli-programmatic.ko.mdx b/content/docs/references/cli/programmatic.ko.mdx similarity index 94% rename from content/docs/guide/cli-programmatic.ko.mdx rename to content/docs/references/cli/programmatic.ko.mdx index ff5157d..bf7d471 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/javascript/node)의 `Remote` 클래스를 통해 원격과 통신합니다. ### pack @@ -150,7 +150,7 @@ await remoteUpload({ 무결성은 SHA-2(기본값 `sha256`)를 사용합니다. 서명을 제공하면 무결성 문자열 바이트에 서명합니다. - 무결성과 서명이 어떻게 함께 동작하는지는 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. + 무결성과 서명이 어떻게 함께 동작하는지는 [원격 번들](/docs/guide/over-the-air)을 참고하세요. ### builtin @@ -203,7 +203,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 +260,26 @@ 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 94% rename from content/docs/guide/cli-programmatic.mdx rename to content/docs/references/cli/programmatic.mdx index 795a8a1..264d595 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/javascript/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/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,7 +266,7 @@ 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 @@ -274,17 +274,17 @@ without a separate build step. Bare and `npm:` specifiers and Node builtins stay diff --git a/content/docs/guide/commands/deploy.ko.mdx b/content/docs/references/cli/remote-deploy.ko.mdx similarity index 78% rename from content/docs/guide/commands/deploy.ko.mdx rename to content/docs/references/cli/remote-deploy.ko.mdx index 11effe7..682f0f9 100644 --- a/content/docs/guide/commands/deploy.ko.mdx +++ b/content/docs/references/cli/remote-deploy.ko.mdx @@ -5,7 +5,7 @@ description: 이미 업로드된 번들 버전을 클라이언트가 받을 현 `wvb deploy`는 이미 원격에 올라와 있는 번들 버전을 현재 버전으로 승격합니다. 그러면 번들을 다운로드하는 클라이언트가 그 버전을 받습니다. 번들을 업로드하면 원격에 저장되지만 현재 버전이 되지는 않습니다. 스위치를 켜는 단계가 바로 deploy입니다. -[`wvb upload`](/docs/guide/commands/upload)을 실행한 뒤에 사용하세요. 또는 `upload`에 `--deploy`를 넘겨 두 단계를 한 명령어로 합칠 수도 있습니다. +[`wvb upload`](/docs/references/cli/remote-upload)을 실행한 뒤에 사용하세요. 또는 `upload`에 `--deploy`를 넘겨 두 단계를 한 명령어로 합칠 수도 있습니다. ## Usage @@ -27,11 +27,11 @@ 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는 설정이나 가장 @@ -43,17 +43,17 @@ wvb deploy # 설정 / package.json 기본값 diff --git a/content/docs/guide/commands/deploy.mdx b/content/docs/references/cli/remote-deploy.mdx similarity index 80% rename from content/docs/guide/commands/deploy.mdx rename to content/docs/references/cli/remote-deploy.mdx index 86d6587..ce6f1e0 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,11 +27,11 @@ 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 @@ -43,17 +43,17 @@ The global flags `--color`, `--log-level`, and `--log-verbose` also apply. See t diff --git a/content/docs/guide/commands/download.ko.mdx b/content/docs/references/cli/remote-download.ko.mdx similarity index 90% rename from content/docs/guide/commands/download.ko.mdx rename to content/docs/references/cli/remote-download.ko.mdx index 0467eab..87ac821 100644 --- a/content/docs/guide/commands/download.ko.mdx +++ b/content/docs/references/cli/remote-download.ko.mdx @@ -15,7 +15,7 @@ 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 @@ -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,7 +43,7 @@ wvb download app --no-write `--no-write`는 번들을 가져와 정보를 출력하되 파일을 저장하지 않습니다. 디스크를 건드리지 않고 원격 서버가 무엇을 제공하는지 확인할 때 사용하세요. 번들 본문을 전혀 다운로드하지 않고 메타데이터만 - 보려면 대신 [`wvb remote current`](/docs/guide/commands/remote)를 사용하세요. + 보려면 대신 [`wvb remote current`](/docs/references/cli/remote)를 사용하세요. ## See also @@ -51,17 +51,17 @@ wvb download app --no-write diff --git a/content/docs/guide/commands/download.mdx b/content/docs/references/cli/remote-download.mdx similarity index 91% rename from content/docs/guide/commands/download.mdx rename to content/docs/references/cli/remote-download.mdx index 2fd3812..ba192b8 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,7 +43,7 @@ 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 @@ -51,17 +51,17 @@ Boolean flags accept `--flag`, `--flag=true|false`, and a `--no-flag` negation. diff --git a/content/docs/guide/commands/upload.ko.mdx b/content/docs/references/cli/remote-upload.ko.mdx similarity index 91% rename from content/docs/guide/commands/upload.ko.mdx rename to content/docs/references/cli/remote-upload.ko.mdx index e136d9e..8331db8 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,8 +49,8 @@ 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)를 참고하세요. @@ -59,17 +59,17 @@ wvb upload --no-pack --file ./build/app.wvb --force diff --git a/content/docs/guide/commands/upload.mdx b/content/docs/references/cli/remote-upload.mdx similarity index 92% rename from content/docs/guide/commands/upload.mdx rename to content/docs/references/cli/remote-upload.mdx index 4e4ab0f..acb516b 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,8 +48,8 @@ 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). @@ -58,17 +58,17 @@ For the publishing model and a local-testing walkthrough, see [Building a remote diff --git a/content/docs/guide/commands/remote.ko.mdx b/content/docs/references/cli/remote.ko.mdx similarity index 92% rename from content/docs/guide/commands/remote.ko.mdx rename to content/docs/references/cli/remote.ko.mdx index ec22842..9d78e7c 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] @@ -79,17 +79,17 @@ wvb remote local --allow-other-versions --hostname 0.0.0.0 `wvb serve`는 단일 번들의 내용을 미리 보여줍니다. HTTP 원격을 상대로 전체 무선(OTA) 업데이트 - 루프를 테스트하려면, [`wvb remote local`](/docs/guide/commands/remote)로 로컬 업데이트 서버를 + 루프를 테스트하려면, [`wvb remote local`](/docs/references/cli/remote)로 로컬 업데이트 서버를 실행하세요. 전체 과정을 처음부터 끝까지 따라가려면 [원격 서버 구축하기](/docs/guide/remote)를 참고하세요. @@ -23,7 +23,7 @@ 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 @@ -36,30 +36,30 @@ 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` 함수를 사용하세요. +- JavaScript에서 동일한 로직을 호출하려면 [프로그래매틱 API](/docs/references/cli/programmatic)의 `serve` 함수를 사용하세요. ## See also diff --git a/content/docs/guide/commands/serve.mdx b/content/docs/references/cli/serve.mdx similarity index 87% rename from content/docs/guide/commands/serve.mdx rename to content/docs/references/cli/serve.mdx index 689687e..753dd8c 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,30 @@ 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). +- To call the same logic from JavaScript, use the `serve` function in the [programmatic API](/docs/references/cli/programmatic). ## See also diff --git a/content/docs/guide/commands/extract.ko.mdx b/content/docs/references/cli/unpack.ko.mdx similarity index 96% rename from content/docs/guide/commands/extract.ko.mdx rename to content/docs/references/cli/unpack.ko.mdx index 427c6af..8ed1fd3 100644 --- a/content/docs/guide/commands/extract.ko.mdx +++ b/content/docs/references/cli/unpack.ko.mdx @@ -43,17 +43,17 @@ wvb extract ./build/app.wvb --no-write diff --git a/content/docs/guide/commands/extract.mdx b/content/docs/references/cli/unpack.mdx similarity index 95% rename from content/docs/guide/commands/extract.mdx rename to content/docs/references/cli/unpack.mdx index e89ac6d..c0af6f5 100644 --- a/content/docs/guide/commands/extract.mdx +++ b/content/docs/references/cli/unpack.mdx @@ -43,17 +43,17 @@ The global `--color`, `--log-level`, and `--log-verbose` flags apply here as the diff --git a/content/docs/config/index.ko.mdx b/content/docs/references/configuration/index.ko.mdx similarity index 95% rename from content/docs/config/index.ko.mdx rename to content/docs/references/configuration/index.ko.mdx index de045d9..837feab 100644 --- a/content/docs/config/index.ko.mdx +++ b/content/docs/references/configuration/index.ko.mdx @@ -14,17 +14,17 @@ description: CLI와 프로그래밍 API가 pack, remote, serve, builtin 옵션 @@ -114,7 +114,7 @@ export default defineConfig(async () => { `root`는 상대 경로를 해석하는 데 사용하는 프로젝트 루트를 설정합니다. 절대 경로일 수도 있고 설정 파일 기준 상대 경로일 수도 있습니다. `remote` 필드는 -[원격, 무결성 및 서명](/docs/config/remote) 페이지에서 설명합니다. +[원격, 무결성 및 서명](/docs/references/configuration/remote) 페이지에서 설명합니다. ## pack @@ -264,7 +264,7 @@ target: { ``` `remote` 타겟에서 `endpoint`와 `download`는 선택 사항입니다. 유일한 동시성 조절 값은 -`download.concurrency`입니다. `download.http`는 [`@wvb/node`](/docs/references/node)의 +`download.concurrency`입니다. `download.http`는 [`@wvb/node`](/docs/references/javascript/node)의 `HttpOptions` 타입을 받습니다. | `remote` field | Type | Default | @@ -275,7 +275,7 @@ target: { `local` 타겟에서 `workspaces`는 필수입니다. 이는 설정 전체에서 유일하게 필수인 필드입니다. `integrity`와 `signature` 옵션은 remote 섹션과 동일한 형태를 공유합니다. -[원격, 무결성 및 서명](/docs/config/remote)을 참고하세요. +[원격, 무결성 및 서명](/docs/references/configuration/remote)을 참고하세요. | `local` field | Type | Default | | ------------------- | --------------------------------------------------- | -------- | @@ -291,17 +291,17 @@ target: { diff --git a/content/docs/config/index.mdx b/content/docs/references/configuration/index.mdx similarity index 96% rename from content/docs/config/index.mdx rename to content/docs/references/configuration/index.mdx index 2985ebc..1bce679 100644 --- a/content/docs/config/index.mdx +++ b/content/docs/references/configuration/index.mdx @@ -15,17 +15,17 @@ dedicated page. @@ -118,7 +118,7 @@ The config object has five optional fields. Nothing else is read at the top leve `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. +[Remote, integrity & signature](/docs/references/configuration/remote) page. ## pack @@ -272,7 +272,7 @@ target: { 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). +[`@wvb/node`](/docs/references/javascript/node). | `remote` field | Type | Default | | ---------------------- | ------------- | ------- | @@ -282,7 +282,7 @@ knob is `download.concurrency`; `download.http` accepts the `HttpOptions` type f 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). +as the remote section; see [Remote, integrity & signature](/docs/references/configuration/remote). | `local` field | Type | Default | | ------------------- | --------------------------------------------------- | -------- | @@ -298,17 +298,17 @@ as the remote section; see [Remote, integrity & signature](/docs/config/remote). diff --git a/content/docs/references/configuration/meta.json b/content/docs/references/configuration/meta.json new file mode 100644 index 0000000..57050e9 --- /dev/null +++ b/content/docs/references/configuration/meta.json @@ -0,0 +1 @@ +{ "title": "Configuration", "pages": ["index", "remote"] } diff --git a/content/docs/references/configuration/meta.ko.json b/content/docs/references/configuration/meta.ko.json new file mode 100644 index 0000000..fff2be7 --- /dev/null +++ b/content/docs/references/configuration/meta.ko.json @@ -0,0 +1 @@ +{ "title": "설정", "pages": ["index", "remote"] } diff --git a/content/docs/config/remote.ko.mdx b/content/docs/references/configuration/remote.ko.mdx similarity index 96% rename from content/docs/config/remote.ko.mdx rename to content/docs/references/configuration/remote.ko.mdx index 6415779..360808b 100644 --- a/content/docs/config/remote.ko.mdx +++ b/content/docs/references/configuration/remote.ko.mdx @@ -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/over-the-air)을 참고하세요. ## remote @@ -162,8 +162,8 @@ 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/over-the-air)을, 클라이언트 측 헬퍼는 [Node API + 레퍼런스](/docs/references/javascript/node)를 참고하세요. ## Complete example @@ -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 96% rename from content/docs/config/remote.mdx rename to content/docs/references/configuration/remote.mdx index 9be3b96..56baa67 100644 --- a/content/docs/config/remote.mdx +++ b/content/docs/references/configuration/remote.mdx @@ -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/over-the-air). ## remote @@ -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/over-the-air) for verification and the [Node API + reference](/docs/references/javascript/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/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/index.ko.mdx b/content/docs/references/index.ko.mdx index 87863bd..0b68d19 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/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/javascript/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/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..f02c7b3 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -6,10 +6,10 @@ 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/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 +22,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/javascript/bridge) for the full API. ```ts import { invoke, updater } from '@wvb/bridge'; @@ -55,11 +55,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/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/ios.ko.mdx b/content/docs/references/ios.ko.mdx new file mode 100644 index 0000000..a0a23a7 --- /dev/null +++ b/content/docs/references/ios.ko.mdx @@ -0,0 +1,5 @@ +--- +title: 'iOS' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/ios.mdx b/content/docs/references/ios.mdx new file mode 100644 index 0000000..65562f1 --- /dev/null +++ b/content/docs/references/ios.mdx @@ -0,0 +1,5 @@ +--- +title: 'iOS' +--- + +This page is a work in progress. diff --git a/content/docs/references/bridge/errors.ko.mdx b/content/docs/references/javascript/bridge/errors.ko.mdx similarity index 96% rename from content/docs/references/bridge/errors.ko.mdx rename to content/docs/references/javascript/bridge/errors.ko.mdx index 4cbe533..69d908d 100644 --- a/content/docs/references/bridge/errors.ko.mdx +++ b/content/docs/references/javascript/bridge/errors.ko.mdx @@ -50,12 +50,12 @@ try { diff --git a/content/docs/references/bridge/errors.mdx b/content/docs/references/javascript/bridge/errors.mdx similarity index 96% rename from content/docs/references/bridge/errors.mdx rename to content/docs/references/javascript/bridge/errors.mdx index 5cc93d0..a7239e4 100644 --- a/content/docs/references/bridge/errors.mdx +++ b/content/docs/references/javascript/bridge/errors.mdx @@ -50,12 +50,12 @@ try { diff --git a/content/docs/references/bridge/index.ko.mdx b/content/docs/references/javascript/bridge/index.ko.mdx similarity index 90% rename from content/docs/references/bridge/index.ko.mdx rename to content/docs/references/javascript/bridge/index.ko.mdx index 57a31c9..213b811 100644 --- a/content/docs/references/bridge/index.ko.mdx +++ b/content/docs/references/javascript/bridge/index.ko.mdx @@ -5,7 +5,7 @@ description: 웹뷰 안의 JavaScript가 네이티브 호스트를 호출하게 `@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)를 참고하세요. +이 패키지는 순수 TypeScript이며, 타입이 지정된 명령어 그룹(`source`, `remote`, `updater`)을 제공합니다. 따라서 `invoke()`를 직접 호출하는 경우는 드뭅니다. 네이티브 호스트가 이 명령어들을 노출하는 방법은 [플랫폼 연동](/docs/guide/glue-to-native)을 참고하세요. 대응하는 네이티브 API는 [Node API 레퍼런스](/docs/references/javascript/node)를 참고하세요. ## Install @@ -96,27 +96,27 @@ console.log(platform.type); // 'electron' | 'tauri' | 'android' | 'ios' @@ -142,19 +142,19 @@ async function checkForUpdate(bundleName: string) { } ``` -업데이터의 전체 기능은 [업데이터 레퍼런스](/docs/references/bridge/updater)를 참고하세요. +업데이터의 전체 기능은 [업데이터 레퍼런스](/docs/references/javascript/bridge/updater)를 참고하세요. ## See also diff --git a/content/docs/references/bridge/index.mdx b/content/docs/references/javascript/bridge/index.mdx similarity index 90% rename from content/docs/references/bridge/index.mdx rename to content/docs/references/javascript/bridge/index.mdx index 9bc40ef..3f78526 100644 --- a/content/docs/references/bridge/index.mdx +++ b/content/docs/references/javascript/bridge/index.mdx @@ -5,7 +5,7 @@ description: The web-side @wvb/bridge library that lets JavaScript inside the we `@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). +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/glue-to-native); for the matching native API, see the [Node API reference](/docs/references/javascript/node). ## Install @@ -96,27 +96,27 @@ The bridge exposes three typed command groups. Each method is a typed `invoke()` @@ -142,19 +142,19 @@ async function checkForUpdate(bundleName: string) { } ``` -For the full updater surface, see the [updater reference](/docs/references/bridge/updater). +For the full updater surface, see the [updater reference](/docs/references/javascript/bridge/updater). ## See also diff --git a/content/docs/references/javascript/bridge/meta.json b/content/docs/references/javascript/bridge/meta.json new file mode 100644 index 0000000..14c1484 --- /dev/null +++ b/content/docs/references/javascript/bridge/meta.json @@ -0,0 +1 @@ +{ "title": "@wvb/bridge", "pages": ["index", "source", "remote", "updater", "errors", "testing"] } diff --git a/content/docs/references/javascript/bridge/meta.ko.json b/content/docs/references/javascript/bridge/meta.ko.json new file mode 100644 index 0000000..14c1484 --- /dev/null +++ b/content/docs/references/javascript/bridge/meta.ko.json @@ -0,0 +1 @@ +{ "title": "@wvb/bridge", "pages": ["index", "source", "remote", "updater", "errors", "testing"] } diff --git a/content/docs/references/bridge/remote.ko.mdx b/content/docs/references/javascript/bridge/remote.ko.mdx similarity index 87% rename from content/docs/references/bridge/remote.ko.mdx rename to content/docs/references/javascript/bridge/remote.ko.mdx index dd032a1..22d2a09 100644 --- a/content/docs/references/bridge/remote.ko.mdx +++ b/content/docs/references/javascript/bridge/remote.ko.mdx @@ -5,7 +5,7 @@ description: 네이티브 호스트를 통해 설정된 원격 서버에 질의 `remote`는 네이티브 호스트를 통해 설정된 원격 서버와 통신하는 bridge 명령어 그룹입니다. 웹뷰 안의 웹 코드에서 이 그룹을 사용하면 원격이 제공하는 항목을 나열하고, 번들의 현재 정보를 조회하고, 다운로드를 실행할 수 있습니다. 웹 쪽에서 번들 바이트를 직접 다루지 않아도 됩니다. -각 메서드는 대응하는 네이티브 핸들러로 라우팅되는 타입 지정 `invoke()` 호출입니다. 따라서 호출은 타입 검사를 거치며, `invoke()`를 직접 다룰 일은 거의 없습니다. 패키지 전반에 대한 내용은 [Bridge 개요](/docs/references/bridge)를 참고하세요. 이 명령어들이 도달하는 네이티브 쪽에 대한 내용은 [Node remote API](/docs/references/node/remote)를 참고하세요. +각 메서드는 대응하는 네이티브 핸들러로 라우팅되는 타입 지정 `invoke()` 호출입니다. 따라서 호출은 타입 검사를 거치며, `invoke()`를 직접 다룰 일은 거의 없습니다. 패키지 전반에 대한 내용은 [Bridge 개요](/docs/references/javascript/bridge)를 참고하세요. 이 명령어들이 도달하는 네이티브 쪽에 대한 내용은 [Node remote API](/docs/references/javascript/node/remote)를 참고하세요. ## 메서드 @@ -34,7 +34,8 @@ await remote.downloadVersion('app', '1.2.0'); 호스트에 원격이 설정되어 있지 않으면 이 호출들은 `code`가 `RemoteNotInitialized`인 `BridgeError`와 - 함께 실패합니다. 오류 처리에 대한 내용은 [Bridge 개요](/docs/references/bridge)를 참고하세요. + 함께 실패합니다. 오류 처리에 대한 내용은 [Bridge 개요](/docs/references/javascript/bridge)를 + 참고하세요. ## 참고 자료 @@ -42,7 +43,7 @@ await remote.downloadVersion('app', '1.2.0'); diff --git a/content/docs/references/bridge/remote.mdx b/content/docs/references/javascript/bridge/remote.mdx similarity index 88% rename from content/docs/references/bridge/remote.mdx rename to content/docs/references/javascript/bridge/remote.mdx index bb20a07..df2f150 100644 --- a/content/docs/references/bridge/remote.mdx +++ b/content/docs/references/javascript/bridge/remote.mdx @@ -5,7 +5,7 @@ description: The bridge remote.* command group, which queries the configured rem `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). +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/javascript/bridge); for the native side these commands reach into, see the [Node remote API](/docs/references/javascript/node/remote). ## Methods @@ -34,7 +34,8 @@ await remote.downloadVersion('app', '1.2.0'); 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. + `RemoteNotInitialized`. See the [Bridge overview](/docs/references/javascript/bridge) for error + handling. ## See also @@ -42,7 +43,7 @@ await remote.downloadVersion('app', '1.2.0'); diff --git a/content/docs/references/bridge/source.ko.mdx b/content/docs/references/javascript/bridge/source.ko.mdx similarity index 91% rename from content/docs/references/bridge/source.ko.mdx rename to content/docs/references/javascript/bridge/source.ko.mdx index 2bfd9e7..841b032 100644 --- a/content/docs/references/bridge/source.ko.mdx +++ b/content/docs/references/javascript/bridge/source.ko.mdx @@ -5,7 +5,7 @@ description: 네이티브 호스트에서 빌트인 번들과 원격 번들을 `source`는 네이티브 번들 소스에 매핑되는 bridge 명령어 그룹입니다. 네이티브 번들 소스는 호스트가 빌트인 번들과 원격 번들을 저장하는 공간입니다. 각 메서드는 타입이 지정된 `invoke()` 호출이므로, 웹 측에서 타입 검사를 거쳐 대응하는 네이티브 핸들러로 라우팅됩니다. 이 그룹으로 번들을 나열하고, 버전과 메타데이터를 불러오고, 디스크상의 경로를 확인하고, 다운로드한 원격 번들을 관리하세요. -이 명령어들이 호출하는 네이티브 측은 [Node API source 레퍼런스](/docs/references/node/source)를 참고하세요. `invoke()`, `platform`, `remote` 및 `updater` 그룹, 오류, 테스트 등 나머지 bridge 영역은 [Bridge 개요](/docs/references/bridge)를 참고하세요. +이 명령어들이 호출하는 네이티브 측은 [Node API source 레퍼런스](/docs/references/javascript/node/source)를 참고하세요. `invoke()`, `platform`, `remote` 및 `updater` 그룹, 오류, 테스트 등 나머지 bridge 영역은 [Bridge 개요](/docs/references/javascript/bridge)를 참고하세요. ```ts import { source } from '@wvb/bridge'; @@ -57,12 +57,12 @@ async function tidyRemoteBundles(bundleName: string) { diff --git a/content/docs/references/bridge/source.mdx b/content/docs/references/javascript/bridge/source.mdx similarity index 90% rename from content/docs/references/bridge/source.mdx rename to content/docs/references/javascript/bridge/source.mdx index 071e581..94e421c 100644 --- a/content/docs/references/bridge/source.mdx +++ b/content/docs/references/javascript/bridge/source.mdx @@ -5,7 +5,7 @@ description: The bridge source.* command group for reading and managing builtin `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). +For the native side these commands call into, see the [Node API source reference](/docs/references/javascript/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/javascript/bridge). ```ts import { source } from '@wvb/bridge'; @@ -57,12 +57,12 @@ To drop a specific downloaded version, call `removeRemoteBundle(bundleName, vers diff --git a/content/docs/references/bridge/testing.ko.mdx b/content/docs/references/javascript/bridge/testing.ko.mdx similarity index 97% rename from content/docs/references/bridge/testing.ko.mdx rename to content/docs/references/javascript/bridge/testing.ko.mdx index a8a857b..d1dd872 100644 --- a/content/docs/references/bridge/testing.ko.mdx +++ b/content/docs/references/javascript/bridge/testing.ko.mdx @@ -5,7 +5,7 @@ description: '@wvb/bridge/testing 헬퍼로 네이티브 호스트 없이 단위 `@wvb/bridge/testing` 하위 경로를 사용하면 실제 네이티브 호스트 없이 브리지를 호출하는 웹 코드를 실행할 수 있습니다. 코드가 호출하는 명령어에 핸들러를 등록하고, 감지된 플랫폼을 강제로 지정하며, 테스트 사이에 목(mock)을 정리하세요. 목 명령어 키는 `'source.loadVersion'`이나 `'updater.getUpdate'`처럼 점(dot)으로 구분되고 타입이 지정되어 있으므로, 핸들러 시그니처의 타입 검사가 유지됩니다. -이 헬퍼가 대신하는 브리지 표면은 [Bridge 개요](/docs/references/bridge)를 참고하세요. +이 헬퍼가 대신하는 브리지 표면은 [Bridge 개요](/docs/references/javascript/bridge)를 참고하세요. ## Import @@ -136,12 +136,12 @@ test('skips download when no update is available', async () => { diff --git a/content/docs/references/bridge/testing.mdx b/content/docs/references/javascript/bridge/testing.mdx similarity index 97% rename from content/docs/references/bridge/testing.mdx rename to content/docs/references/javascript/bridge/testing.mdx index 26e0e2c..498dea4 100644 --- a/content/docs/references/bridge/testing.mdx +++ b/content/docs/references/javascript/bridge/testing.mdx @@ -5,7 +5,7 @@ description: Drive the bridge in unit tests without a native host using the @wvb 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). +For the bridge surface these helpers stand in for, see the [Bridge overview](/docs/references/javascript/bridge). ## Import @@ -136,12 +136,12 @@ test('skips download when no update is available', async () => { diff --git a/content/docs/references/bridge/updater.ko.mdx b/content/docs/references/javascript/bridge/updater.ko.mdx similarity index 86% rename from content/docs/references/bridge/updater.ko.mdx rename to content/docs/references/javascript/bridge/updater.ko.mdx index b225488..be2873a 100644 --- a/content/docs/references/bridge/updater.ko.mdx +++ b/content/docs/references/javascript/bridge/updater.ko.mdx @@ -5,7 +5,7 @@ description: 웹뷰 내부에서 무선(OTA) 업데이트 흐름을 구동하는 `updater` 명령어 그룹은 웹뷰 내부에서 실행되는 웹 코드에서 무선(OTA) 업데이트 흐름을 구동합니다. 이 그룹으로 더 새로운 번들이 있는지 확인하고, 스테이징한 뒤, 네이티브 호스트에서 활성화하세요. 각 메서드는 타입이 지정된 `invoke()` 호출이며, 대응하는 네이티브 업데이터 핸들러로 라우팅됩니다. 따라서 `invoke()`를 직접 다룰 일은 거의 없습니다. -이 명령어들 뒤에 있는 OTA 개념은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. bridge가 호출하는 네이티브 업데이터는 [Node 업데이터 API](/docs/references/node/updater)를 참고하세요. bridge 전체는 [Bridge 개요](/docs/references/bridge)를 참고하세요. +이 명령어들 뒤에 있는 OTA 개념은 [원격 번들](/docs/guide/over-the-air)을 참고하세요. bridge가 호출하는 네이티브 업데이터는 [Node 업데이터 API](/docs/references/javascript/node/updater)를 참고하세요. bridge 전체는 [Bridge 개요](/docs/references/javascript/bridge)를 참고하세요. ```ts import { updater } from '@wvb/bridge'; @@ -63,7 +63,7 @@ async function checkForUpdate(bundleName: string) { 네이티브 호스트에 업데이터가 설정되어 있지 않으면, 호출은 `code`가 `BridgeErrorCode.UpdaterNotInitialized`인 `BridgeError`로 reject됩니다. 오류 처리는 [Bridge - 개요](/docs/references/bridge)를 참고하세요. + 개요](/docs/references/javascript/bridge)를 참고하세요. ## See also @@ -71,17 +71,17 @@ async function checkForUpdate(bundleName: string) { diff --git a/content/docs/references/bridge/updater.mdx b/content/docs/references/javascript/bridge/updater.mdx similarity index 83% rename from content/docs/references/bridge/updater.mdx rename to content/docs/references/javascript/bridge/updater.mdx index 59af018..7f10114 100644 --- a/content/docs/references/bridge/updater.mdx +++ b/content/docs/references/javascript/bridge/updater.mdx @@ -5,7 +5,7 @@ description: The bridge updater.* command group that drives the over-the-air upd 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). +For the OTA concepts behind these commands, see [Remote bundles](/docs/guide/over-the-air). For the native updater the bridge calls into, see the [Node updater API](/docs/references/javascript/node/updater). For the bridge as a whole, see the [Bridge overview](/docs/references/javascript/bridge). ```ts import { updater } from '@wvb/bridge'; @@ -62,8 +62,8 @@ async function checkForUpdate(bundleName: string) { 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. + `BridgeErrorCode.UpdaterNotInitialized`. See the [Bridge + overview](/docs/references/javascript/bridge) for error handling. ## See also @@ -71,17 +71,17 @@ async function checkForUpdate(bundleName: string) { diff --git a/content/docs/references/javascript/deno-desktop.ko.mdx b/content/docs/references/javascript/deno-desktop.ko.mdx new file mode 100644 index 0000000..24c0ea3 --- /dev/null +++ b/content/docs/references/javascript/deno-desktop.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/deno-desktop' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/deno-desktop.mdx b/content/docs/references/javascript/deno-desktop.mdx new file mode 100644 index 0000000..49d8298 --- /dev/null +++ b/content/docs/references/javascript/deno-desktop.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/deno-desktop' +--- + +This page is a work in progress. diff --git a/content/docs/references/deno/index.ko.mdx b/content/docs/references/javascript/deno/index.ko.mdx similarity index 88% rename from content/docs/references/deno/index.ko.mdx rename to content/docs/references/javascript/deno/index.ko.mdx index df8003c..b59622b 100644 --- a/content/docs/references/deno/index.ko.mdx +++ b/content/docs/references/javascript/deno/index.ko.mdx @@ -7,7 +7,7 @@ description: '@wvb/node의 Deno FFI 대응 패키지인 @wvb/deno 레퍼런스 Deno 연동은 실험적입니다. -`@wvb/deno`는 Webview Bundle의 Deno 바인딩입니다. [`@wvb/node`](/docs/references/node)의 FFI 대응 패키지로, +`@wvb/deno`는 Webview Bundle의 Deno 바인딩입니다. [`@wvb/node`](/docs/references/javascript/node)의 FFI 대응 패키지로, 동일한 프로토콜, 소스, 원격, 업데이터 기능을 제공합니다. Deno FFI로 로드한 네이티브 cdylib를 통해 Deno 데스크톱 웹뷰에 이를 전달합니다. 이 페이지에서는 네이티브 라이브러리를 로드하는 방법을 다룹니다. 클래스, 옵션, 타입은 아래 카테고리 페이지에서 설명합니다. @@ -15,27 +15,27 @@ Deno 데스크톱 웹뷰에 이를 전달합니다. 이 페이지에서는 네 ## API 표면 Deno 바인딩은 `@wvb/node`를 그대로 반영합니다. 각 클래스는 로드된 `lib`를 받고, -[Node API 레퍼런스](/docs/references/node)에서 설명한 것과 동일한 역할을 수행합니다. +[Node API 레퍼런스](/docs/references/javascript/node)에서 설명한 것과 동일한 역할을 수행합니다. @@ -120,20 +120,20 @@ Deno 바인딩은 FFI 경계를 넘나들기 때문에, 일부 `@wvb/node` 기 `@wvb/deno-desktop`은 `@wvb/deno`를 Deno의 데스크톱 런타임에 연결합니다. `webviewBundle` 팩토리는 `BundleSource`(및 선택적 `Remote`/`Updater`)를 빌드하고 `Deno.serve` 호환 핸들러를 노출합니다. -한편 `registerBindings`는 [`@wvb/bridge`](/docs/guide/platform-integration)의 +한편 `registerBindings`는 [`@wvb/bridge`](/docs/guide/glue-to-native)의 `source.*`/`remote.*`/`updater.*` 명령어를 `Deno.BrowserWindow`에 연결합니다. -전체 과정을 처음부터 끝까지 살펴보려면 [Deno Desktop 가이드](/docs/guide/platforms/deno)를 참고하세요. +전체 과정을 처음부터 끝까지 살펴보려면 [Deno Desktop 가이드](/docs/guide/native/deno)를 참고하세요. diff --git a/content/docs/references/deno/index.mdx b/content/docs/references/javascript/deno/index.mdx similarity index 88% rename from content/docs/references/deno/index.mdx rename to content/docs/references/javascript/deno/index.mdx index a33c08a..c8314d7 100644 --- a/content/docs/references/deno/index.mdx +++ b/content/docs/references/javascript/deno/index.mdx @@ -7,7 +7,7 @@ description: Reference for the @wvb/deno package, the Deno FFI peer of @wvb/node 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): +`@wvb/deno` is the Deno binding for Webview Bundle. It is the FFI peer of [`@wvb/node`](/docs/references/javascript/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. @@ -15,27 +15,27 @@ 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). +described in the [Node API reference](/docs/references/javascript/node). @@ -120,20 +120,20 @@ The Deno binding crosses an FFI boundary, so a few `@wvb/node` features are out `@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) +while `registerBindings` wires the [`@wvb/bridge`](/docs/guide/glue-to-native) `source.*`/`remote.*`/`updater.*` commands into a `Deno.BrowserWindow`. -For an end-to-end walkthrough, see the [Deno Desktop guide](/docs/guide/platforms/deno). +For an end-to-end walkthrough, see the [Deno Desktop guide](/docs/guide/native/deno). diff --git a/content/docs/references/javascript/deno/meta.json b/content/docs/references/javascript/deno/meta.json new file mode 100644 index 0000000..b23ae19 --- /dev/null +++ b/content/docs/references/javascript/deno/meta.json @@ -0,0 +1 @@ +{ "title": "@wvb/deno", "pages": ["index", "source", "protocol", "remote", "updater"] } diff --git a/content/docs/references/javascript/deno/meta.ko.json b/content/docs/references/javascript/deno/meta.ko.json new file mode 100644 index 0000000..b23ae19 --- /dev/null +++ b/content/docs/references/javascript/deno/meta.ko.json @@ -0,0 +1 @@ +{ "title": "@wvb/deno", "pages": ["index", "source", "protocol", "remote", "updater"] } diff --git a/content/docs/references/deno/protocol.ko.mdx b/content/docs/references/javascript/deno/protocol.ko.mdx similarity index 86% rename from content/docs/references/deno/protocol.ko.mdx rename to content/docs/references/javascript/deno/protocol.ko.mdx index 7010896..ba6521e 100644 --- a/content/docs/references/deno/protocol.ko.mdx +++ b/content/docs/references/javascript/deno/protocol.ko.mdx @@ -5,11 +5,11 @@ description: 번들 파일을 제공하고 로컬 개발 URL을 웹뷰로 프록 Deno 바인딩은 두 가지 프로토콜 핸들러를 제공합니다. `BundleProtocol`은 커스텀 스킴을 통해 번들의 파일을 웹뷰에 제공합니다. `LocalProtocol`은 개발용으로 `app://host/...` URL을 로컬호스트 주소로 프록시합니다. 두 핸들러 모두 요청을 FFI 응답으로 해석하며, `toResponse(res)`로 표준 `Response`로 변환합니다. -기본 개념은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. Deno가 제공하는 전체 API와 네이티브 라이브러리를 로드하는 방법은 [Deno API 개요](/docs/references/deno)를 참고하세요. +기본 개념은 [프로토콜 처리](/docs/guide/protocol-handling)를 참고하세요. Deno가 제공하는 전체 API와 네이티브 라이브러리를 로드하는 방법은 [Deno API 개요](/docs/references/javascript/deno)를 참고하세요. ## Handlers -`BundleProtocol`은 로드된 `lib`과 [`BundleSource`](/docs/references/deno/source)를 받습니다. 들어오는 각 요청을 해석된 번들 안의 파일로 매핑합니다. +`BundleProtocol`은 로드된 `lib`과 [`BundleSource`](/docs/references/javascript/deno/source)를 받습니다. 들어오는 각 요청을 해석된 번들 안의 파일로 매핑합니다. `LocalProtocol`은 `app://host/...` 요청을 로컬호스트 주소로 프록시합니다. 개발 서버를 대상으로 작업할 때 유용합니다. @@ -55,15 +55,16 @@ using protocol = new BundleProtocol(lib, source); ``` - `BundleProtocol`은 [`BundleSource`](/docs/references/deno/source)에서 읽어옵니다. - [Remote](/docs/references/deno/remote), [Updater](/docs/references/deno/updater)와 함께 사용하면 - 더 새로운 번들을 무선(OTA)으로 가져와 설치할 수 있습니다. + `BundleProtocol`은 [`BundleSource`](/docs/references/javascript/deno/source)에서 읽어옵니다. + [Remote](/docs/references/javascript/deno/remote), + [Updater](/docs/references/javascript/deno/updater)와 함께 사용하면 더 새로운 번들을 무선(OTA)으로 + 가져와 설치할 수 있습니다. - `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. + `BundleProtocol` reads from a [`BundleSource`](/docs/references/javascript/deno/source). Pair it + with the [Remote](/docs/references/javascript/deno/remote) and + [Updater](/docs/references/javascript/deno/updater) to fetch and install newer bundles over the + air. { }); ``` -해석된 파일이 웹뷰에 어떻게 전달되는지는 [프로토콜](/docs/references/deno/protocol)을 참고하세요. +해석된 파일이 웹뷰에 어떻게 전달되는지는 [프로토콜](/docs/references/javascript/deno/protocol)을 참고하세요. ## Deno 관련 참고 사항 @@ -76,12 +76,12 @@ Deno 바인딩은 Deno FFI를 통해 Rust cdylib와 통신합니다. 따라서 ` diff --git a/content/docs/references/deno/source.mdx b/content/docs/references/javascript/deno/source.mdx similarity index 87% rename from content/docs/references/deno/source.mdx rename to content/docs/references/javascript/deno/source.mdx index 5bfe546..4f16579 100644 --- a/content/docs/references/deno/source.mdx +++ b/content/docs/references/javascript/deno/source.mdx @@ -3,11 +3,11 @@ 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. +`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/javascript/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. +Load the native library first, then pass the loaded `lib` and an options object to the constructor. See the [Deno API overview](/docs/references/javascript/deno) for the two ways to load the cdylib. ```ts title="source.ts" import { loadLibViaPlug, BundleSource } from 'jsr:@wvb/deno'; @@ -43,7 +43,7 @@ A `BundleSource` exposes the same data API as the Node `BundleSource`: - 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. +For the exact method names, signatures, and return types of this shared surface, see the [Node API reference](/docs/references/javascript/node), which the Deno binding mirrors. ## Using a source @@ -67,7 +67,7 @@ Deno.serve(async req => { }); ``` -For how the resolved files reach the webview, see [Protocols](/docs/references/deno/protocol). +For how the resolved files reach the webview, see [Protocols](/docs/references/javascript/deno/protocol). ## Deno-specific notes @@ -76,12 +76,12 @@ The Deno binding talks to a Rust cdylib over Deno FFI, so a `BundleSource` is co diff --git a/content/docs/references/deno/updater.ko.mdx b/content/docs/references/javascript/deno/updater.ko.mdx similarity index 87% rename from content/docs/references/deno/updater.ko.mdx rename to content/docs/references/javascript/deno/updater.ko.mdx index 04b17cf..715cd24 100644 --- a/content/docs/references/deno/updater.ko.mdx +++ b/content/docs/references/javascript/deno/updater.ko.mdx @@ -3,12 +3,12 @@ title: Updater description: '@wvb/deno의 Updater로 Deno 데스크톱 앱에서 원격에 최신 번들이 있는지 확인하고 설치합니다.' --- -`Updater`는 [원격](/docs/references/deno/remote)에서 번들의 최신 버전을 확인하고 로컬 스토어에 -설치합니다. [`BundleSource`](/docs/references/deno/source)를 `Remote`에 연결하고, 무결성 및 서명 +`Updater`는 [원격](/docs/references/javascript/deno/remote)에서 번들의 최신 버전을 확인하고 로컬 스토어에 +설치합니다. [`BundleSource`](/docs/references/javascript/deno/source)를 `Remote`에 연결하고, 무결성 및 서명 정책을 적용하며, 다운로드한 번들을 기록합니다. 그러면 다음 실행 때 업데이트된 앱 코드가 제공됩니다. 이 방식으로 Deno 데스크톱 앱은 새로운 릴리스 없이 무선(OTA) 업데이트를 전달합니다. 전체 흐름은 -[원격 번들](/docs/guide/remote-bundles)을 참고하세요. 바인딩의 나머지 내용은 -[Deno API 개요](/docs/references/deno)를 참고하세요. +[원격 번들](/docs/guide/over-the-air)을 참고하세요. 바인딩의 나머지 내용은 +[Deno API 개요](/docs/references/javascript/deno)를 참고하세요. ## Constructor @@ -55,8 +55,8 @@ using updater = new Updater(lib, source, remote, { `SignatureAlgorithm`(예: `'ed25519'`)과 검증에 사용할 공개 키를 지정합니다. - Deno 바인딩의 검증은 선언적 방식만 지원합니다. [`@wvb/node`](/docs/references/node)의 사용자 정의 - 함수 콜백인 `integrityChecker`와 `signatureVerifier`는 FFI 경계를 넘지 못합니다. + Deno 바인딩의 검증은 선언적 방식만 지원합니다. [`@wvb/node`](/docs/references/javascript/node)의 + 사용자 정의 함수 콜백인 `integrityChecker`와 `signatureVerifier`는 FFI 경계를 넘지 못합니다. ## Methods @@ -100,12 +100,12 @@ try { diff --git a/content/docs/references/deno/updater.mdx b/content/docs/references/javascript/deno/updater.mdx similarity index 91% rename from content/docs/references/deno/updater.mdx rename to content/docs/references/javascript/deno/updater.mdx index 1776cca..06ca437 100644 --- a/content/docs/references/deno/updater.mdx +++ b/content/docs/references/javascript/deno/updater.mdx @@ -3,12 +3,12 @@ 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`, +`Updater` checks a [remote](/docs/references/javascript/deno/remote) for newer versions of a bundle and installs +them into the local store. It ties a [`BundleSource`](/docs/references/javascript/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). +release. For the end-to-end model, see [Remote bundles](/docs/guide/over-the-air); for the rest of +the binding, see the [Deno API overview](/docs/references/javascript/deno). ## Constructor @@ -56,8 +56,8 @@ using updater = new Updater(lib, source, remote, { 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. + `signatureVerifier` callbacks of [`@wvb/node`](/docs/references/javascript/node) do not cross the + FFI boundary. ## Methods @@ -100,12 +100,12 @@ try { diff --git a/content/docs/references/javascript/electron-builder.ko.mdx b/content/docs/references/javascript/electron-builder.ko.mdx new file mode 100644 index 0000000..b49cc55 --- /dev/null +++ b/content/docs/references/javascript/electron-builder.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron-builder' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/electron-builder.mdx b/content/docs/references/javascript/electron-builder.mdx new file mode 100644 index 0000000..e24423f --- /dev/null +++ b/content/docs/references/javascript/electron-builder.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron-builder' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/electron-forge.ko.mdx b/content/docs/references/javascript/electron-forge.ko.mdx new file mode 100644 index 0000000..ee3a5de --- /dev/null +++ b/content/docs/references/javascript/electron-forge.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron-forge' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/electron-forge.mdx b/content/docs/references/javascript/electron-forge.mdx new file mode 100644 index 0000000..12b90eb --- /dev/null +++ b/content/docs/references/javascript/electron-forge.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron-forge' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/electron.ko.mdx b/content/docs/references/javascript/electron.ko.mdx new file mode 100644 index 0000000..908a255 --- /dev/null +++ b/content/docs/references/javascript/electron.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/electron.mdx b/content/docs/references/javascript/electron.mdx new file mode 100644 index 0000000..b2128f9 --- /dev/null +++ b/content/docs/references/javascript/electron.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/electron' +--- + +This page is a work in progress. diff --git a/content/docs/references/node/bundle.ko.mdx b/content/docs/references/javascript/node/bundle.ko.mdx similarity index 96% rename from content/docs/references/node/bundle.ko.mdx rename to content/docs/references/javascript/node/bundle.ko.mdx index 9776006..006ab7d 100644 --- a/content/docs/references/node/bundle.ko.mdx +++ b/content/docs/references/javascript/node/bundle.ko.mdx @@ -5,7 +5,7 @@ description: .wvb 아카이브를 읽고, 빌드하고, 검사하기 위한 @wvb `@wvb/node`는 메모리와 디스크에서 `.wvb` 아카이브를 다루는 소규모 클래스 집합을 제공합니다. `Bundle`과 `BundleBuilder`는 번들 전체를 읽고 씁니다. `BundleDescriptor`, `Header`, `Index`, `LoadedDescriptor`는 번들의 구조를 검사하고, 전체를 메모리에 올리지 않고도 개별 파일을 가져올 수 있게 합니다. -소스, 프로토콜, 원격 클라이언트, 업데이터까지 패키지의 전체 표면은 [Node API 개요](/docs/references/node)를 참고하세요. 바이트가 디스크에 배치되는 방식은 [.wvb 번들 포맷](/docs/guide/bundle-format)을 참고하세요. +소스, 프로토콜, 원격 클라이언트, 업데이터까지 패키지의 전체 표면은 [Node API 개요](/docs/references/javascript/node)를 참고하세요. 바이트가 디스크에 배치되는 방식은 [.wvb 번들 포맷](/docs/guide/bundle-format)을 참고하세요. ## 번들 읽기와 파일 가져오기 @@ -73,7 +73,7 @@ type IndexEntry = { 애드온은 `@wvb/node-` 선택적 의존성 중 하나에서 사전 빌드 바이너리를 해석합니다(macOS, Linux, Windows, Android에 걸친 12개의 NAPI 타깃). 지원되는 Node 엔진은 `>= 12.22.0 < 13`, `>= 14.17.0 < 15`, `>= 15.12.0 < 16`, `>= 16.0.0`입니다. @@ -55,30 +55,34 @@ yarn add @wvb/node - + ## Example @@ -101,17 +105,17 @@ if (html) { diff --git a/content/docs/references/node/index.mdx b/content/docs/references/javascript/node/index.mdx similarity index 82% rename from content/docs/references/node/index.mdx rename to content/docs/references/javascript/node/index.mdx index caf6594..3312f7e 100644 --- a/content/docs/references/node/index.mdx +++ b/content/docs/references/javascript/node/index.mdx @@ -5,7 +5,7 @@ description: Reference for @wvb/node — the N-API native addon that reads, serv `@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 web app running inside the webview never calls `@wvb/node` directly. It uses [`@wvb/bridge`](/docs/references/javascript/bridge), whose `source`, `remote`, and `updater` commands map onto the classes documented here. See [Remote bundles](/docs/guide/over-the-air) for the end-to-end flow and [Deno API](/docs/references/javascript/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`. @@ -55,32 +55,32 @@ The classes, enums, and option types are split across focused pages. @@ -105,17 +105,17 @@ if (html) { diff --git a/content/docs/references/node/meta.json b/content/docs/references/javascript/node/meta.json similarity index 78% rename from content/docs/references/node/meta.json rename to content/docs/references/javascript/node/meta.json index d5ece34..f4bc544 100644 --- a/content/docs/references/node/meta.json +++ b/content/docs/references/javascript/node/meta.json @@ -1,4 +1,4 @@ { - "title": "Node API", + "title": "@wvb/node", "pages": ["index", "bundle", "source", "protocol", "remote", "updater", "types"] } diff --git a/content/docs/references/node/meta.ko.json b/content/docs/references/javascript/node/meta.ko.json similarity index 78% rename from content/docs/references/node/meta.ko.json rename to content/docs/references/javascript/node/meta.ko.json index d5ece34..f4bc544 100644 --- a/content/docs/references/node/meta.ko.json +++ b/content/docs/references/javascript/node/meta.ko.json @@ -1,4 +1,4 @@ { - "title": "Node API", + "title": "@wvb/node", "pages": ["index", "bundle", "source", "protocol", "remote", "updater", "types"] } diff --git a/content/docs/references/node/protocol.ko.mdx b/content/docs/references/javascript/node/protocol.ko.mdx similarity index 87% rename from content/docs/references/node/protocol.ko.mdx rename to content/docs/references/javascript/node/protocol.ko.mdx index 0a19300..c145582 100644 --- a/content/docs/references/node/protocol.ko.mdx +++ b/content/docs/references/javascript/node/protocol.ko.mdx @@ -15,11 +15,11 @@ handle( ): Promise ``` -`HttpMethod`와 `HttpResponse`는 [타입](/docs/references/node/types)에 문서화되어 있습니다. `HttpMethod`는 소문자 문자열 유니온(`'get'`, `'head'` 등)입니다. `HttpResponse`는 `{ status, headers, body }`이며, 여기서 `body`는 `Buffer`입니다. +`HttpMethod`와 `HttpResponse`는 [타입](/docs/references/javascript/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)에서 제공합니다. 번들 내부에서 요청한 파일을 찾아 그 바이트를 반환합니다. +`new BundleProtocol(source: BundleSource)`는 `scheme://bundle_name/path` 요청을 [`BundleSource`](/docs/references/javascript/node/source)에서 제공합니다. 번들 내부에서 요청한 파일을 찾아 그 바이트를 반환합니다. | Member | Signature | | ----------- | ---------------------------------------------------------------------------------------------- | @@ -75,12 +75,12 @@ res.body; // Buffer /> diff --git a/content/docs/references/node/protocol.mdx b/content/docs/references/javascript/node/protocol.mdx similarity index 90% rename from content/docs/references/node/protocol.mdx rename to content/docs/references/javascript/node/protocol.mdx index 36ca5a6..61fdd83 100644 --- a/content/docs/references/node/protocol.mdx +++ b/content/docs/references/javascript/node/protocol.mdx @@ -15,11 +15,11 @@ handle( ): 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`. +`HttpMethod` and `HttpResponse` are documented in [Types](/docs/references/javascript/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. +`new BundleProtocol(source: BundleSource)` serves `scheme://bundle_name/path` requests from a [`BundleSource`](/docs/references/javascript/node/source). It resolves the requested file inside the bundle and returns its bytes. | Member | Signature | | ----------- | ---------------------------------------------------------------------------------------------- | @@ -75,12 +75,12 @@ res.body; // Buffer /> diff --git a/content/docs/references/node/remote.ko.mdx b/content/docs/references/javascript/node/remote.ko.mdx similarity index 92% rename from content/docs/references/node/remote.ko.mdx rename to content/docs/references/javascript/node/remote.ko.mdx index 13bf3ad..b4a6969 100644 --- a/content/docs/references/node/remote.ko.mdx +++ b/content/docs/references/javascript/node/remote.ko.mdx @@ -3,7 +3,7 @@ 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`를 직접 사용하세요. +`Remote`는 [번들 원격](/docs/guide/remote)을 위한 `@wvb/node` HTTP 클라이언트입니다. [Remote HTTP Spec](/docs/guide/spec)을 구현한 모든 서버와 통신합니다. 따라서 Node.js에서 사용 가능한 번들을 나열하고, 번들의 메타데이터를 읽고, 번들 바이트를 다운로드할 수 있습니다. [Updater](/docs/references/javascript/node/updater)는 `Remote` 위에서 업데이트를 준비하고 활성화합니다. 원격에 더 낮은 수준으로 접근해야 할 때는 `Remote`를 직접 사용하세요. ## Constructor @@ -28,7 +28,7 @@ const remote = new Remote('https://bundles.example.com'); | `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`로 이루어진 튜플을 반환합니다. +`listBundles`는 원격이 제공하는 번들을 나열하며, `channel`로 필터링할 수도 있습니다. `getInfo`는 단일 번들의 현재 메타데이터를 읽습니다. `download`는 번들의 현재 버전을 가져오고, `downloadVersion`은 특정 버전을 가져옵니다. 두 다운로드 메서드 모두 번들 정보, 파싱된 [`Bundle`](/docs/references/javascript/node/bundle), 그리고 원본 `.wvb` 바이트를 담은 `Buffer`로 이루어진 튜플을 반환합니다. ```ts const [info, bundle, bytes] = await remote.download('my-app', 'stable'); @@ -134,7 +134,7 @@ type RemoteBundleInfo = { /> diff --git a/content/docs/references/node/remote.mdx b/content/docs/references/javascript/node/remote.mdx similarity index 94% rename from content/docs/references/node/remote.mdx rename to content/docs/references/javascript/node/remote.mdx index f065f14..ef4cdb2 100644 --- a/content/docs/references/node/remote.mdx +++ b/content/docs/references/javascript/node/remote.mdx @@ -3,7 +3,7 @@ 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. +`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/javascript/node/updater) builds on `Remote` to stage and activate updates; use `Remote` directly when you need lower-level access to the remote. ## Constructor @@ -28,7 +28,7 @@ const remote = new Remote('https://bundles.example.com'); | `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`. +`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/javascript/node/bundle), and the raw `.wvb` bytes as a `Buffer`. ```ts const [info, bundle, bytes] = await remote.download('my-app', 'stable'); @@ -135,7 +135,7 @@ The `integrity` and `signature` fields carry the values the remote advertises th /> diff --git a/content/docs/references/node/source.ko.mdx b/content/docs/references/javascript/node/source.ko.mdx similarity index 64% rename from content/docs/references/node/source.ko.mdx rename to content/docs/references/javascript/node/source.ko.mdx index f86573c..11a90cb 100644 --- a/content/docs/references/node/source.ko.mdx +++ b/content/docs/references/javascript/node/source.ko.mdx @@ -5,7 +5,7 @@ description: BundleSource는 builtin 디렉터리와 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)를 참고하세요. +`BundleSource`는 [`BundleProtocol`](/docs/references/javascript/node/protocol)이 번들을 제공하는 기반이자 [`Updater`](/docs/references/javascript/node/updater)가 다운로드를 준비해 두는 기반입니다. 매니페스트, 채널, 디렉터리 구조가 어떻게 맞물리는지는 [Bundle sources](/docs/guide/bundle-sources)를 참고하세요. 패키지 전체 구성은 [Node API 개요](/docs/references/javascript/node)를 참고하세요. ## Constructor @@ -13,7 +13,7 @@ description: BundleSource는 builtin 디렉터리와 remote 디렉터리에 걸 new BundleSource(config: BundleSourceConfig) ``` -`config`는 [`BundleSourceConfig`](/docs/references/node/types)입니다. 이 설정은 소스가 두 디렉터리를 가리키게 하며, 선택적으로 명시적인 매니페스트 파일 경로도 지정합니다: +`config`는 [`BundleSourceConfig`](/docs/references/javascript/node/types)입니다. 이 설정은 소스가 두 디렉터리를 가리키게 하며, 선택적으로 명시적인 매니페스트 파일 경로도 지정합니다: ```ts import { BundleSource } from '@wvb/node'; @@ -52,16 +52,16 @@ const source = new BundleSource({ | `remoteRetainedVersions` | `(name): Promise` (current + previous) | | `pruneRemoteBundles` | `(name): Promise` | -`listBundles`는 두 디렉터리에 걸친 모든 번들을 [`ListBundleItem`](/docs/references/node/types)으로 보고합니다. 각 항목은 `type`(`builtin` 또는 `remote`), 이름, 버전, 현재 버전 여부, 매니페스트 메타데이터를 담습니다. `loadVersion`은 이름에 대한 유효 버전을 해석하고, 그 버전이 어느 디렉터리에서 왔는지 알려줍니다. `resolveFilepath`는 remote 우선 규칙에 따라 선택되는 경로를 반환합니다. 반면 `getBuiltinBundleFilepath`와 `getRemoteBundleFilepath`는 특정 버전에 대해 디렉터리별 경로를 계산합니다. +`listBundles`는 두 디렉터리에 걸친 모든 번들을 [`ListBundleItem`](/docs/references/javascript/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)를 반환합니다. +`fetch*` 메서드는 파싱된 [`Bundle`](/docs/references/javascript/node/bundle)을 읽습니다. `fetchBundle`은 우선 규칙을 따르고, `fetchBuiltinBundle`과 `fetchRemoteBundle`은 특정 디렉터리 하나만 대상으로 합니다. `fetchDescriptor`는 모든 파일을 메모리에 로드하지 않고 [`BundleDescriptor`](/docs/references/javascript/node/bundle)를 반환합니다. -`loadDescriptor`는 single-flight 방식으로 `LoadedDescriptor`를 캐시합니다. 따라서 여러 호출자가 동시에 호출해도 하나의 로드를 공유합니다. `unloadDescriptor`는 캐시된 핸들을 해제합니다. remote 디렉터리에 기록하는 경로는 `writeRemoteBundle`입니다. 이 메서드는 다운로드한 번들과 그 [`BundleManifestMetadata`](/docs/references/node/types)를 저장합니다. `remoteRetainedVersions`는 소스가 이름에 대해 보관하는 버전(현재 버전과 이전 버전)을 나열합니다. `removeRemoteBundle`은 특정 버전을 삭제하고, `pruneRemoteBundles`는 더 이상 보관되지 않는 버전을 제거합니다. +`loadDescriptor`는 single-flight 방식으로 `LoadedDescriptor`를 캐시합니다. 따라서 여러 호출자가 동시에 호출해도 하나의 로드를 공유합니다. `unloadDescriptor`는 캐시된 핸들을 해제합니다. remote 디렉터리에 기록하는 경로는 `writeRemoteBundle`입니다. 이 메서드는 다운로드한 번들과 그 [`BundleManifestMetadata`](/docs/references/javascript/node/types)를 저장합니다. `remoteRetainedVersions`는 소스가 이름에 대해 보관하는 버전(현재 버전과 이전 버전)을 나열합니다. `removeRemoteBundle`은 특정 버전을 삭제하고, `pruneRemoteBundles`는 더 이상 보관되지 않는 버전을 제거합니다. - 상위 수준의 OTA 흐름은 대개 [`Updater`](/docs/references/node/updater)를 통해 이 메서드들을 - 호출합니다. `Updater`는 remote 버전을 준비하고 활성화해 줍니다. 해석, 메타데이터, 정리를 세밀하게 - 제어해야 할 때는 `BundleSource`를 직접 사용하세요. + 상위 수준의 OTA 흐름은 대개 [`Updater`](/docs/references/javascript/node/updater)를 통해 이 + 메서드들을 호출합니다. `Updater`는 remote 버전을 준비하고 활성화해 줍니다. 해석, 메타데이터, + 정리를 세밀하게 제어해야 할 때는 `BundleSource`를 직접 사용하세요. ## Related @@ -74,17 +74,17 @@ const source = new BundleSource({ /> diff --git a/content/docs/references/node/source.mdx b/content/docs/references/javascript/node/source.mdx similarity index 67% rename from content/docs/references/node/source.mdx rename to content/docs/references/javascript/node/source.mdx index 6074734..9ecab28 100644 --- a/content/docs/references/node/source.mdx +++ b/content/docs/references/javascript/node/source.mdx @@ -5,7 +5,7 @@ description: BundleSource resolves a bundle across the builtin and remote direct `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. +A `BundleSource` is the foundation that [`BundleProtocol`](/docs/references/javascript/node/protocol) serves from and that [`Updater`](/docs/references/javascript/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/javascript/node) for the full package surface. ## Constructor @@ -13,7 +13,7 @@ A `BundleSource` is the foundation that [`BundleProtocol`](/docs/references/node 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: +`config` is a [`BundleSourceConfig`](/docs/references/javascript/node/types). It points the source at the two directories and, optionally, at explicit manifest file paths: ```ts import { BundleSource } from '@wvb/node'; @@ -53,17 +53,17 @@ const source = new BundleSource({ | `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. +`listBundles` reports every bundle across both directories as a [`ListBundleItem`](/docs/references/javascript/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. +The `fetch*` methods read a parsed [`Bundle`](/docs/references/javascript/node/bundle): `fetchBundle` honors the priority rule, while `fetchBuiltinBundle` and `fetchRemoteBundle` target one directory explicitly. `fetchDescriptor` returns a [`BundleDescriptor`](/docs/references/javascript/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. +`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/javascript/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. + [`Updater`](/docs/references/javascript/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 @@ -76,17 +76,17 @@ The `fetch*` methods read a parsed [`Bundle`](/docs/references/node/bundle): `fe /> diff --git a/content/docs/references/node/types.ko.mdx b/content/docs/references/javascript/node/types.ko.mdx similarity index 82% rename from content/docs/references/node/types.ko.mdx rename to content/docs/references/javascript/node/types.ko.mdx index e92b7dc..7fd3873 100644 --- a/content/docs/references/node/types.ko.mdx +++ b/content/docs/references/javascript/node/types.ko.mdx @@ -3,7 +3,7 @@ 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) 페이지가 여기서 정의한 형태들을 참조합니다. +[Node API](/docs/references/javascript/node)의 클래스와 함수가 반환하고 받아들이는 보조 타입들입니다. reader, [BundleSource](/docs/references/javascript/node/source), [프로토콜 핸들러](/docs/references/javascript/node/protocol), [Remote](/docs/references/javascript/node/remote), [Updater](/docs/references/javascript/node/updater) 페이지가 여기서 정의한 형태들을 참조합니다. ## 열거형과 유니온 @@ -38,7 +38,7 @@ type IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'; type IntegrityPolicy = 'strict' | 'optional' | 'none'; ``` -무결성은 SHA-2를 사용하며, `":"` 형태로 직렬화됩니다(예: `sha256:n4bQ…`). `strict`는 값이 존재하고 일치해야 합니다. `optional`은 값이 존재할 때만 검증합니다. `none`은 검사를 건너뜁니다. [Remote, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. +무결성은 SHA-2를 사용하며, `":"` 형태로 직렬화됩니다(예: `sha256:n4bQ…`). `strict`는 값이 존재하고 일치해야 합니다. `optional`은 값이 존재할 때만 검증합니다. `none`은 검사를 건너뜁니다. [Remote, 무결성 및 서명 설정](/docs/references/configuration/remote)을 참고하세요. ### 서명 @@ -91,7 +91,7 @@ type SignatureVerifierOptions = { ## 결과 타입 -reader, [BundleSource](/docs/references/node/source), [Updater](/docs/references/node/updater)가 반환하는 형태입니다. +reader, [BundleSource](/docs/references/javascript/node/source), [Updater](/docs/references/javascript/node/updater)가 반환하는 형태입니다. ```ts type BundleManifestMetadata = { @@ -148,22 +148,22 @@ type HttpResponse = { diff --git a/content/docs/references/node/types.mdx b/content/docs/references/javascript/node/types.mdx similarity index 84% rename from content/docs/references/node/types.mdx rename to content/docs/references/javascript/node/types.mdx index 8739489..7a68611 100644 --- a/content/docs/references/node/types.mdx +++ b/content/docs/references/javascript/node/types.mdx @@ -3,7 +3,7 @@ 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. +These are the supporting types that the [Node API](/docs/references/javascript/node) classes and functions return and accept. The reader, [BundleSource](/docs/references/javascript/node/source), [protocol handlers](/docs/references/javascript/node/protocol), [Remote](/docs/references/javascript/node/remote), and [Updater](/docs/references/javascript/node/updater) pages reference the shapes defined here. ## Enums and unions @@ -38,7 +38,7 @@ type IntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512'; 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). +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/references/configuration/remote). ### Signature @@ -91,7 +91,7 @@ type SignatureVerifierOptions = { ## Result types -Shapes returned by the reader, [BundleSource](/docs/references/node/source), and [Updater](/docs/references/node/updater). +Shapes returned by the reader, [BundleSource](/docs/references/javascript/node/source), and [Updater](/docs/references/javascript/node/updater). ```ts type BundleManifestMetadata = { @@ -148,22 +148,22 @@ type HttpResponse = { diff --git a/content/docs/references/node/updater.ko.mdx b/content/docs/references/javascript/node/updater.ko.mdx similarity index 82% rename from content/docs/references/node/updater.ko.mdx rename to content/docs/references/javascript/node/updater.ko.mdx index dc72e26..335a513 100644 --- a/content/docs/references/node/updater.ko.mdx +++ b/content/docs/references/javascript/node/updater.ko.mdx @@ -3,7 +3,7 @@ title: Updater description: '@wvb/node의 Updater 클래스 — 원격 서버에서 최신 번들을 확인하고, 무결성과 서명 검증을 거쳐 스테이징한 뒤 활성화합니다.' --- -`Updater`는 번들의 무선(OTA) 업데이트를 처리합니다. [`BundleSource`](/docs/references/node/source)와 [`Remote`](/docs/references/node/remote)를 짝지어 동작합니다. 원격 서버에서 더 새로운 버전을 확인하고, 소스의 원격 디렉터리로 다운로드한 뒤 활성화합니다. 확인, 다운로드, 활성화 단계는 각각 별도의 메서드입니다. 따라서 백그라운드에서 업데이트를 스테이징해 두고 나중에 전환할 수 있습니다. 예를 들어 다음 앱 재시작 시점입니다. 전체 흐름은 [원격 번들](/docs/guide/remote-bundles)을 참고하세요. +`Updater`는 번들의 무선(OTA) 업데이트를 처리합니다. [`BundleSource`](/docs/references/javascript/node/source)와 [`Remote`](/docs/references/javascript/node/remote)를 짝지어 동작합니다. 원격 서버에서 더 새로운 버전을 확인하고, 소스의 원격 디렉터리로 다운로드한 뒤 활성화합니다. 확인, 다운로드, 활성화 단계는 각각 별도의 메서드입니다. 따라서 백그라운드에서 업데이트를 스테이징해 두고 나중에 전환할 수 있습니다. 예를 들어 다음 앱 재시작 시점입니다. 전체 흐름은 [원격 번들](/docs/guide/over-the-air)을 참고하세요. ```ts import { BundleSource, Remote, Updater } from '@wvb/node'; @@ -15,11 +15,11 @@ const updater = new Updater(source, remote, options); `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` | 채널과 검증 설정입니다(선택). | +| Argument | Type | Description | +| --------- | --------------------------------------------------------- | ----------------------------------------------------------- | +| `source` | [`BundleSource`](/docs/references/javascript/node/source) | 스테이징된 번들과 활성 번들이 디스크에 저장되는 위치입니다. | +| `remote` | [`Remote`](/docs/references/javascript/node/remote) | 원격 서버용 HTTP 클라이언트입니다. | +| `options` | `UpdaterOptions` | 채널과 검증 설정입니다(선택). | 다운로드와 설치는 번들 단위로 직렬화됩니다. 따라서 같은 번들 이름에 대한 동시 호출은 한 번에 하나씩 실행됩니다. @@ -101,7 +101,7 @@ type VerifyingKeyFormat = 'spkiDer' | 'spkiPem' | 'pkcs1Der' | 'pkcs1Pem' | 'sec 사용하세요. -전체 타입 목록은 [열거형, 옵션, 결과 타입](/docs/references/node/types)을, 배포 측 설정은 [Remote, 무결성 및 서명 설정](/docs/config/remote)을 참고하세요. +전체 타입 목록은 [열거형, 옵션, 결과 타입](/docs/references/javascript/node/types)을, 배포 측 설정은 [Remote, 무결성 및 서명 설정](/docs/references/configuration/remote)을 참고하세요. ## Update with integrity and signature checks @@ -141,22 +141,22 @@ if (update.isAvailable) { diff --git a/content/docs/references/node/updater.mdx b/content/docs/references/javascript/node/updater.mdx similarity index 82% rename from content/docs/references/node/updater.mdx rename to content/docs/references/javascript/node/updater.mdx index 575c05f..5c8936e 100644 --- a/content/docs/references/node/updater.mdx +++ b/content/docs/references/javascript/node/updater.mdx @@ -3,7 +3,7 @@ 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. +`Updater` drives over-the-air (OTA) updates for a bundle. It pairs a [`BundleSource`](/docs/references/javascript/node/source) with a [`Remote`](/docs/references/javascript/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/over-the-air) for the end-to-end flow. ```ts import { BundleSource, Remote, Updater } from '@wvb/node'; @@ -15,11 +15,11 @@ const updater = new Updater(source, remote, options); `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). | +| Argument | Type | Description | +| --------- | --------------------------------------------------------- | --------------------------------------------- | +| `source` | [`BundleSource`](/docs/references/javascript/node/source) | Where staged and active bundles live on disk. | +| `remote` | [`Remote`](/docs/references/javascript/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. @@ -102,7 +102,7 @@ Key-format constraints: `pkcs1Der` and `pkcs1Pem` apply to RSA only, `sec1` to E 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. +See [enums, options, and result types](/docs/references/javascript/node/types) for the full type listing and [Remote, integrity & signature config](/docs/references/configuration/remote) for the publish side. ## Update with integrity and signature checks @@ -142,22 +142,22 @@ if (update.isAvailable) { diff --git a/content/docs/references/javascript/remotes/meta.json b/content/docs/references/javascript/remotes/meta.json new file mode 100644 index 0000000..39ce07b --- /dev/null +++ b/content/docs/references/javascript/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-pulumi", + "remote-local", + "remote-local-provider" + ] +} diff --git a/content/docs/references/javascript/remotes/meta.ko.json b/content/docs/references/javascript/remotes/meta.ko.json new file mode 100644 index 0000000..34f0901 --- /dev/null +++ b/content/docs/references/javascript/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-pulumi", + "remote-local", + "remote-local-provider" + ] +} diff --git a/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.ko.mdx b/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.ko.mdx new file mode 100644 index 0000000..6439d76 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws-provider-pulumi' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.mdx b/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.mdx new file mode 100644 index 0000000..d1c9076 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws-provider-pulumi.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws-provider-pulumi' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-aws-provider.ko.mdx b/content/docs/references/javascript/remotes/remote-aws-provider.ko.mdx new file mode 100644 index 0000000..9fffa70 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws-provider.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws-provider' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-aws-provider.mdx b/content/docs/references/javascript/remotes/remote-aws-provider.mdx new file mode 100644 index 0000000..58cede8 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws-provider.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws-provider' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-aws.ko.mdx b/content/docs/references/javascript/remotes/remote-aws.ko.mdx new file mode 100644 index 0000000..0fe4d32 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-aws.mdx b/content/docs/references/javascript/remotes/remote-aws.mdx new file mode 100644 index 0000000..bc55a05 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-aws.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-aws' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare-provider.ko.mdx b/content/docs/references/javascript/remotes/remote-cloudflare-provider.ko.mdx new file mode 100644 index 0000000..bfd1aa7 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare-provider.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare-provider' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare-provider.mdx b/content/docs/references/javascript/remotes/remote-cloudflare-provider.mdx new file mode 100644 index 0000000..78a6ed0 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare-provider.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare-provider' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.ko.mdx b/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.ko.mdx new file mode 100644 index 0000000..00ed869 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare-pulumi' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.mdx b/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.mdx new file mode 100644 index 0000000..3791c26 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare-pulumi.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare-pulumi' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare.ko.mdx b/content/docs/references/javascript/remotes/remote-cloudflare.ko.mdx new file mode 100644 index 0000000..ded7400 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-cloudflare.mdx b/content/docs/references/javascript/remotes/remote-cloudflare.mdx new file mode 100644 index 0000000..4d873ec --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-cloudflare.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-cloudflare' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-local-provider.ko.mdx b/content/docs/references/javascript/remotes/remote-local-provider.ko.mdx new file mode 100644 index 0000000..a51d076 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-local-provider.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-local-provider' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-local-provider.mdx b/content/docs/references/javascript/remotes/remote-local-provider.mdx new file mode 100644 index 0000000..672dfb2 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-local-provider.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-local-provider' +--- + +This page is a work in progress. diff --git a/content/docs/references/javascript/remotes/remote-local.ko.mdx b/content/docs/references/javascript/remotes/remote-local.ko.mdx new file mode 100644 index 0000000..8b4b6b1 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-local.ko.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-local' +--- + +이 문서는 작성 중입니다. diff --git a/content/docs/references/javascript/remotes/remote-local.mdx b/content/docs/references/javascript/remotes/remote-local.mdx new file mode 100644 index 0000000..6f29fc6 --- /dev/null +++ b/content/docs/references/javascript/remotes/remote-local.mdx @@ -0,0 +1,5 @@ +--- +title: '@wvb/remote-local' +--- + +This page is a work in progress. diff --git a/content/docs/references/meta.json b/content/docs/references/meta.json index 6fa009c..191178b 100644 --- a/content/docs/references/meta.json +++ b/content/docs/references/meta.json @@ -1,5 +1,24 @@ { "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---", + "javascript/bridge", + "javascript/node", + "javascript/electron", + "javascript/electron-builder", + "javascript/electron-forge", + "javascript/deno", + "javascript/deno-desktop", + "javascript/remotes", + "---Native---", + "android", + "ios" + ] } diff --git a/content/docs/references/meta.ko.json b/content/docs/references/meta.ko.json index 7629f78..e0cb179 100644 --- a/content/docs/references/meta.ko.json +++ b/content/docs/references/meta.ko.json @@ -1,5 +1,24 @@ { "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---", + "javascript/bridge", + "javascript/node", + "javascript/electron", + "javascript/electron-builder", + "javascript/electron-forge", + "javascript/deno", + "javascript/deno-desktop", + "javascript/remotes", + "---네이티브---", + "android", + "ios" + ] } diff --git a/new-doc/guide/getting-started/introduction.md b/new-doc/guide/getting-started/introduction.md new file mode 100644 index 0000000..2a265d3 --- /dev/null +++ b/new-doc/guide/getting-started/introduction.md @@ -0,0 +1,41 @@ +# 웹뷰 번들이란? + +--- + +웹뷰 번들은 웹뷰 기반 프레임워크 및 플랫폼을 위한 "오프라인 우선(Offline First)" 방식의 웹 애플리케이션 배포 시스템입니다. + +웹 애플리케이션 코드를 네트워크를 통해 불러와 웹뷰에 런더하는 대신, 웹뷰 번들 파일(`.wvb`)로 패킹하여 로컬에서 리소스를 불러와 웹뷰에 렌더합니다. 또한, 리모트를 구성하여 원격으로 웹뷰 번들을 다운받아 최신상태의 애플리케이션 코드를 네이티브 배포 없이 효과적으로 업데이트할 수 있습니다. + +웹뷰 번들은 웹뷰가 제공되는 [Electron](https://electronjs.org), [Tauri](https://tauri.app), Android, iOS, 그리고 [Deno Desktop](https://deno.com/blog/v2.9#deno-desktop)(실험적)을 지원하며, 웹뷰와 통신할 수 있는 [브릿지](...) 또한 제공됩니다. + +## 기능 + +### 네이티브에 웹을 탑재 + +웹뷰 번들을 이용하면 익숙한 프론트엔드 코드를 네이티브에 탑재하여 오프라인에서 동작하는 애플리케이션을 만들 수 있습니다. 이를 지원하기 위해 웹 애플리케이션을 웹뷰 번들(`.wvb`) 포맷으로 패킹하여, 네이티브 앱 번들에 포함시킵니다. + +네이티브에서는 웹뷰의 요청을 인터셉트하여 네트워크 요청을 웹뷰 번들 파일 내의 리소스를 읽어 응답합니다. 따라서, 네트워크 요청이 인터넷을 통할 필요없이 로컬데이터를 읽어 응답하므로 오프라인에서도 웹 애플리케이션이 동작할 수 있습니다. + +### OTA(Over-the-air) + +네이티브 앱 번들에 포함된 번들을 최신 상태로 업데이트하기 위해 웹뷰 번들 시스템은 OTA 기술을 제공합니다. [리모트](...) 제공자를 만들어 배포할 번들을 업로드할 수 있고, 프론트엔드 혹은 네이티브에서 원하는 타이밍에 신규 번들을 다운받아 최신 상태를 유지할 수 있습니다. + +웹뷰 번들을 원격으로 업데이트하는 과정은 네이티브 앱 배포 혹은 앱 재시작없이 수행되기에 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를 이용해 동적 라이브러리를 링킹하여 사용합니다. + +## 리소스 + +이 [가이드](https://wvb.dev/guide)를 포함해 + +- [webview-bundle](https://github.com/webview-bundle/webview-bundle) : 웹뷰 번들 패키지 모노리포 +- [webview-bundle-android](https://github.com/webview-bundle/webview-bundle-android) : 웹뷰 번들 Android 라이브러리 +- [webview-bundle-ios](https://github.com/webview-bundle/webview-bundle-ios) : 웹뷰 번들 iOS 라이브러리 +- playground +- AI 연동 diff --git a/new-doc/guide/getting-started/platform-support.md b/new-doc/guide/getting-started/platform-support.md new file mode 100644 index 0000000..630c5a2 --- /dev/null +++ b/new-doc/guide/getting-started/platform-support.md @@ -0,0 +1,13 @@ +# 플랫폼 지원 + +웹뷰 번들은 여러 네이티브 플랫폼을 지원합니다. + +## Electron + +## Tauri + +## Android + +## iOS + +## Deno Desktop diff --git a/new-doc/guide/getting-started/prerequisties.md b/new-doc/guide/getting-started/prerequisties.md new file mode 100644 index 0000000..7d87285 --- /dev/null +++ b/new-doc/guide/getting-started/prerequisties.md @@ -0,0 +1,31 @@ +# 필요 사항 + +프로젝트를 시작하려면 먼저 몇 가지 종속성을 설치해야 합니다. + +## Node.js + +웹뷰 번들 커맨드라인 사용을 위해 [Node.js](https://nodejs.org/ko)를 설치해주세요. + +- 최소 요구 버전 : 18 +- 권장 버전 : LTS + +## Android + +안드로이드 앱 개발을 위해서 Android SDK + +- JDK 버전 +- Android SDK 지원 버전 +- 동적 라이브러리 사용 중 + +## iOS + +- Swift 버전 +- macOS, iOS 지원 버전 + +## Deno + +[Deno Desktop](...)을 사용한다면 Deno 설치가 필요합니다. + +--- + +플랫폼 별로 필요한 종속성은 각 프레임워크의 가이드를 참고해주세요. diff --git a/new-doc/guide/getting-started/start-with-and-example.md b/new-doc/guide/getting-started/start-with-and-example.md new file mode 100644 index 0000000..796f890 --- /dev/null +++ b/new-doc/guide/getting-started/start-with-and-example.md @@ -0,0 +1,27 @@ +# 예제와 함께 시작하기 + +`create-wvb` 를 사용하여 선호하는 도구로 예제 프로젝트를 시작해 보세요. + + + + +```sh +npx create-wvb@latest +``` + + + +```sh +pnpx create-wvb@latest +``` + + +```sh +yarn dlx create-wvb@latest +``` + + + +## 예제 목록 + +사용가능한 예제 목록은 다음 테이블에서 확인해주세요. diff --git a/package.json b/package.json index f40287e..1c060d3 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "lint:fix": "oxlint --type-aware --fix", "format": "oxfmt", "gen:cf-types": "wrangler types", - "genW:fumadocs": "fumadocs-mdx" + "gen:fumadocs": "fumadocs-mdx" }, "dependencies": { "@base-ui/react": "1.6.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/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..6ea5d0c 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/javascript/deno']); function ExperimentalBadge() { return ( diff --git a/src/layouts/home/components/ArchitectureDiagram.tsx b/src/layouts/home/components/ArchitectureDiagram.tsx index 8a3a6a0..9b5807e 100644 --- a/src/layouts/home/components/ArchitectureDiagram.tsx +++ b/src/layouts/home/components/ArchitectureDiagram.tsx @@ -1,63 +1,110 @@ import { SOURCE_FILES, WEBVIEW_HOSTS } from '../data'; -import { ChevronRightIcon } from './icons'; import { Logo } from './Logo'; 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. */} +