Changes
16 changed files (+299/-2)
-
-
-
@@ -19,11 +19,16 @@ }] }, "yaml": {}, "markup": { "useTabs": true }, "plugins": [ "https://plugins.dprint.dev/markdown-0.17.8.wasm", // "Biome is a fast formatter for JavaScript, TypeScript, JSX, TSX, JSON, CSS and GraphQL..." "https://plugins.dprint.dev/biome-0.7.1.wasm", "https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm" "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm", // "HTML, Vue, Svelte, Astro, Angular, Jinja, Twig, Nunjucks, and Vento files via markup_fmt." "https://plugins.dprint.dev/g-plane/markup_fmt-v0.18.0.wasm" ] }
-
-
-
@@ -4,7 +4,8 @@ "private": true,"scripts": { "lint": "buf lint", "build": "buf generate", "clean": "rm -rf es" "clean": "rm -rf es", "prepare": "bun run build" }, "type": "module", "exports": {
-
-
packages/pwa/.gitignore (new)
-
@@ -0,0 +1,8 @@# このディレクトリ特有の無視設定。 # # SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> # SPDX-License-Identifier: AGPL-3.0-only # What: ビルドされた PWA のページとアセット。 # Why: 編集するものではないため。 /dist
-
-
-
@@ -0,0 +1,18 @@{ "name": "@yamori/pwa", "private": true, "type": "module", "scripts": { "dev": "vite", "build": "vite build", "clean": "rm -rf dist" }, "dependencies": { "@bufbuild/protobuf": "^2.2.2", "@yamori/proto": "workspace:*" }, "devDependencies": { "typescript": "^5.7.2", "vite": "^6.0.2" } }
-
-
-
@@ -0,0 +1,2 @@SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
-
@@ -0,0 +1,13 @@<!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta http-equiv="content-security-policy" content="default-src 'none'; img-src 'self' data:; script-src 'self'; connect-src 'self'; style-src 'self' 'unsafe-inline'; worker-src 'self'" /> <script type="module" src="./main.ts"></script> </head> <body></body> </html>
-
-
-
@@ -0,0 +1,2 @@SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
packages/pwa/src/main.ts (new)
-
@@ -0,0 +1,74 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { fromBinary } from "@bufbuild/protobuf"; import { KeyValueStorageBasedWorkspaceService } from "@yamori/proto/yamori/workspace/v1/key_value_storage_based_workspace_service_pb.js"; import { isValidMessage, requestMessage } from "./worker/message.ts"; const button = document.createElement("button"); button.textContent = "Send Message"; button.disabled = true; document.body.appendChild(button); const pre = document.createElement("pre"); const output = document.createElement("output"); pre.appendChild(output); document.body.appendChild(pre); const worker = new Worker(new URL("./worker/main.ts", import.meta.url), { type: "module", }); worker.addEventListener( "message", (ev) => { if (ev.data === "ready") { button.disabled = false; button.addEventListener("click", (ev) => { ev.preventDefault(); const req = requestMessage(KeyValueStorageBasedWorkspaceService.method.list, {}); worker.postMessage(req, { transfer: [req.data.buffer], }); }); worker.addEventListener("message", (ev) => { if (!isValidMessage(ev.data)) { console.warn("Invalid message sent from worker thread."); return; } switch (ev.data.service) { case KeyValueStorageBasedWorkspaceService.typeName: { switch (ev.data.method) { case KeyValueStorageBasedWorkspaceService.method.list.name: { const resp = fromBinary( KeyValueStorageBasedWorkspaceService.method.list.output, ev.data.data, ); output.textContent += "\n" + JSON.stringify(resp, null, 2); return; } default: { console.warn( `Unknown method name "${ev.data.method}" at ${ev.data.service}`, ); return; } } } default: { console.warn(`Unknown service name "${ev.data.service}"`); return; } } }); } }, { once: true }, );
-
-
-
@@ -0,0 +1,56 @@// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { fromBinary } from "@bufbuild/protobuf"; import { KeyValueStorageBasedWorkspaceService } from "@yamori/proto/yamori/workspace/v1/key_value_storage_based_workspace_service_pb.js"; import { isValidMessage, responseMessage } from "./message.ts"; addEventListener("message", (ev) => { if (!isValidMessage(ev.data)) { console.warn("Invalid message sent from main thread."); return; } switch (ev.data.service) { case KeyValueStorageBasedWorkspaceService.typeName: { switch (ev.data.method) { case KeyValueStorageBasedWorkspaceService.method.list.name: { fromBinary( KeyValueStorageBasedWorkspaceService.method.list.input, ev.data.data, ); const resp = responseMessage(KeyValueStorageBasedWorkspaceService.method.list, { result: { case: "ok", value: { workspaces: [ { id: { value: "ws-foo", }, displayName: "Foo", }, ], }, }, }); self.postMessage(resp, { transfer: [resp.data.buffer] }); return; } default: { console.warn(`Unknown method name "${ev.data.method}" at ${ev.data.service}`); return; } } } default: { console.warn(`Unknown service name "${ev.data.service}"`); return; } } }); self.postMessage("ready");
-
-
-
@@ -0,0 +1,64 @@// ワーカーとメインスレッド間でやりとりするメッセージフォーマット。 // 及びそのヘルパ関数。 // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { create, toBinary, type DescMethod, type MessageInitShape, } from "@bufbuild/protobuf"; export interface Message { service: string; method: string; data: Uint8Array; } export function isValidMessage(x: unknown): x is Message { if (typeof x !== "object" || !x) { return false; } if (!("service" in x && typeof x.service === "string" && x.service)) { return false; } if (!("method" in x && typeof x.method === "string" && x.method)) { return false; } if (!("data" in x && x.data instanceof Uint8Array)) { return false; } return true; } export function requestMessage<Method extends DescMethod>( method: Method, data: MessageInitShape<Method["input"]>, ): Message { const binary = toBinary(method.input, create(method.input, data)); return { service: method.parent.typeName, method: method.name, data: binary, }; } export function responseMessage<Method extends DescMethod>( method: Method, data: MessageInitShape<Method["output"]>, ): Message { const binary = toBinary(method.output, create(method.output, data)); return { service: method.parent.typeName, method: method.name, data: binary, }; }
-
-
-
@@ -0,0 +1,7 @@{ "extends": "../../tsconfig.json", "compilerOptions": { "lib": ["ES2020", "WebWorker"] }, "includes": ["./**/*.ts"] }
-
-
-
@@ -0,0 +1,7 @@このディレクトリ配下限定の TypeScript のコンパイラ設定ファイル。 Worker はメインスレッドの DOM 環境と異なるので設定を分ける必要がある。 コメントではなく別ファイルなのは ../../tsconfig.json.license を参照。 SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
-
@@ -0,0 +1,16 @@{ "compilerOptions": { "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "strict": true, "allowImportingTsExtensions": true, "module": "ES2020", "moduleResolution": "Bundler", "noEmit": true, "lib": ["ES2020", "DOM"] }, "include": ["*.ts", "src/**/*.ts"] }
-
-
-
@@ -0,0 +1,9 @@TypeScript のコンパイラ設定ファイル。 実際は JSON ではなく JSONC のためコメントを含められるが、 .jsonc にすると TypeScript 関連のツールがデフォルトで読まなくなる。逆に .json のまま コメントを含めるとフォーマッタといった TypeScript 以外の JSON を扱うツール でエラーになる。そのため拡張子と仕様に準拠している。 SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> SPDX-License-Identifier: AGPL-3.0-only
-
-
-
@@ -0,0 +1,15 @@// Web 向けのバンドラー、 Vite の設定ファイル。 // <https://vite.dev/> // // SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: AGPL-3.0-only import { defineConfig } from "vite"; export default defineConfig({ root: new URL("./src", import.meta.url).pathname, build: { emptyOutDir: true, outDir: "../dist", }, });
-