Среды исполнения и сквозной сценарий
Что меняется между native, WASM и браузером, а что остаётся одинаковым; полный путь «открыть байты → изменить → вернуть XLSX».
Что меняется между средами, а что остаётся одинаковым
Заголовок раздела «Что меняется между средами, а что остаётся одинаковым»| Среда | Структурный API | События пользователя | Изменение документа |
|---|---|---|---|
| Browser/WASM | Через session facade поверх WASM dispatch. | Click, dblclick, contextmenu, keyboard, selection. | Command → WASM Rust. |
| Desktop/native | Тот же facade; transport вызывает native session. | Те же semantic events Web Shell. | Command → native Rust process. |
| Mobile WebView | Тот же facade с меньшим viewport. | Tap, double-tap, long-press преобразуются в те же события. | Command → WASM; нужен mobile lifecycle. |
| Server/agent | Headless session: queries и commands. | Нет click; есть command/session/change events. | Command → native worker/service host. |
Смысловой контракт одинаков
Заголовок раздела «Смысловой контракт одинаков»Selection, ContextTarget, Operation, Revision, Result и SavedArtifact не должны зависеть от транспорта. На мобильном long-press становится contextMenuOpening. На сервере человеческого клика нет, но агент может запросить тот же CellDetails или выполнить ту же operation с теми же правами и отказами.
Особые ограничения
Заголовок раздела «Особые ограничения»-
Web: нельзя отдавать неограниченную структуру всей большой книги; запросы bounded.
-
Mobile: сессия должна переживать suspend через checkpoint/reopen, а не ссылку на WebView memory.
-
Server: требуется tenant isolation, quotas, cancellation и audit; внутренний service-host ещё требует productization.
-
Agent: разрешены только allowlisted queries/commands; credentials и raw M/VBA bodies не попадают в event payload.
8 · РАБОЧИЙ NATIVE-ПРИМЕР
Открыть байты → прочитать → изменить → сохранить
Заголовок раздела «Открыть байты → прочитать → изменить → сохранить»Пример ниже использует тот же протокол, что и canary в репозитории. Индексы row/col нулевые. viewport ограничивает публикуемую область; sourceBytesBase64 позволяет host-приложению не давать Rust доступ к своей файловой системе.
Часть 1: открыть документ и получить структуру
import { spawn } from "node:child_process";import { readFile } from "node:fs/promises";import readline from "node:readline";
const cli = spawn("./target/release/compute_contract_cli", ["--session"], { stdio: ["pipe", "pipe", "inherit"] });const lines = readline.createInterface({ input: cli.stdout });const iterator = lines[Symbol.asyncIterator]();const next = async () => JSON.parse((await iterator.next()).value);const ask = async request => { cli.stdin.write(JSON.stringify(request) + "\n"); return next();};
const hello = await next();if (hello.kind !== "compute_contract_cli_session_ready") throw new Error("F1 core handshake failed");
const bytes = await readFile("./demo.xlsx");const viewport = { top: 0, left: 0, rows: 40, cols: 12 };const opened = await ask({ operation: "open_workbook_subset_from_bytes", sourceName: "demo.xlsx", sourceBytesBase64: bytes.toString("base64"), viewport, requestedBackend: "native-desktop"});if (!opened.ok) throw new Error(opened.code);
const { subsetId, activeSheet, objectInventory = [] } = opened.payload;console.log(activeSheet, objectInventory);9 · NATIVE: ЧТЕНИЕ И ЗАПИСЬ
Адресная операция, revision guard и возврат XLSX-байтов
Заголовок раздела «Адресная операция, revision guard и возврат XLSX-байтов»После открытия subsetId идентифицирует живую Rust-сессию книги. Все последующие запросы несут этот идентификатор. Изменение передаёт expectedRevision; при устаревшей ревизии ядро отказывает до мутации, а не частично применяет данные.
Часть 2: адресное чтение, атомарная правка и сохранение
const cell = await ask({ operation: "read_cell_details", subsetId, row: 0, col: 0, viewport, requestedBackend: "native-desktop"});
const edited = await ask({ operation: "apply_cell_edits", subsetId, edits: [{ inputKind: "text", sheetKey: activeSheet.path, row: 1, col: 1, input: "Доставлено" }], expectedRevision: 0, viewport, requestedBackend: "native-desktop"});if (!edited.ok) throw new Error(`${edited.code}: ${edited.message ?? ""}`);
const saved = await ask({ operation: "save_workbook_subset_to_bytes", subsetId, preserveCachedValues: true, requestedBackend: "native-desktop"});const output = Buffer.from(saved.payload.bytesBase64, "base64");10 · WASM И WEB-СОБЫТИЯ
Тот же Rust-контракт в браузере; адаптер событий поверх Canvas
Заголовок раздела «Тот же Rust-контракт в браузере; адаптер событий поверх Canvas»Реальный вызов Rust/WASM
Заголовок раздела «Реальный вызов Rust/WASM»WASM session: JavaScript владеет транспортом, Rust — семантикой
import init, { WasmComputeSession } from "./f1_bridge_wasm.js";await init();
const core = new WasmComputeSession(Date.now(), 0x362cafe);const dispatch = request => JSON.parse(core.dispatch(JSON.stringify(request)));
const opened = dispatch({ operation: "open_workbook_subset_from_bytes", sourceName: file.name, sourceBytesBase64: await fileToBase64(file), viewport: { top: 0, left: 0, rows: 40, cols: 12 }, requestedBackend: "wasm"});Адаптация текущего Web runtime
Заголовок раздела «Адаптация текущего Web runtime»Сегодня shell уже испускает внутреннее событие F1:surface-object-selected и имеет собственные обработчики click, dblclick и contextmenu. Для пилота их следует завернуть в один facade. Прямую подписку ниже допустимо использовать только во внутренней сборке, не как долгосрочный API внешнее приложение.
Внутренний bridge событий, который надо стабилизировать как SDK
// INTERNAL / UNSTABLE: адаптер внутри поставки F1.surface.addEventListener("F1:surface-object-selected", event => { hostEvents.emit("objectSelected", { target: normalizeObjectTarget(event.detail), revision: currentRevision() });});
surface.addEventListener("dblclick", event => { const target = semanticHitTest(event.clientX, event.clientY); hostEvents.emit("doubleClicked", { target });});
surface.addEventListener("contextmenu", event => { event.preventDefault(); const target = semanticHitTest(event.clientX, event.clientY); openComposedMenu(target, hostMenuProviders);});| Граница ответственности внешнее приложение подписывается на objectSelected/doubleClicked/contextMenuOpening и получает ContextTarget. Только код F1 знает внутреннее имя события, DOM Canvas и hit-test реализацию. |
|---|
11 · ЧТО МОЖНО ОТВЕТИТЬ внешнее приложение