window.engarde
Когда HTML-страница открывается через desktop-клиент Engarde в overlay-зоне (например .engarde / .rus), клиент внедряет в <head> мост:
<script src="/__engarde/bridge.js"></script>Скрипт выставляет window.engarde до запуска модулей страницы. Вне Engarde объекта нет — всегда делайте feature-detect.
if (!window.engarde) {
// обычный браузер — нет локального SQLite / моста
}
await window.engarde.ready;flowchart LR Page["JS overlay-страницы"] -->|вызов| Bridge["window.engarde"] Bridge -->|WebSocket| WS["/__engarde/ws"] WS --> Client["Engarde desktop"] Client --> SQL["SQLite на origin"]
Доступность
| Контекст | window.engarde |
|---|---|
| Overlay-сайт через Engarde SOCKS / DNS | Есть, __engardeNative === true |
| Тот же URL в Chrome / Safari без Engarde | Нет |
Vite npm run dev без клиента |
Нет |
Перед вызовами дождитесь ready — промис резолвится, когда открыт WebSocket к /__engarde/ws.
Поверхность API (v0.1)
type EngardeBridge = {
version: string; // например "0.1"
ready: Promise<void>;
__engardeNative: true;
sql: {
query(sql: string, args?: unknown[]): Promise<SqlQueryResult>;
exec(sql: string, args?: unknown[]): Promise<SqlExecResult>;
};
doc: { get(): Promise<unknown>; set(): Promise<unknown> }; // reserved
rpc: { connect(): Promise<unknown>; call(): Promise<unknown> }; // reserved
};
type SqlQueryResult = {
columns: string[];
rows: unknown[][]; // строка = массив по columns
};
type SqlExecResult = {
changes: number;
lastInsertRowid: number | null;
};Уже поддерживается
На native-стороне реализованы только sql.query и sql.exec.
Зарезервировано (пока не реализовано)
doc.*— sync документа / CRDT (ответnot_implemented)rpc.*— peer RPC (ответnot_implemented)
Вызовы отклоняются с ошибкой, у которой code === "not_implemented".
SQL API
Хранилище на origin overlay-сайта (hostname): файл SQLite в data-директории клиента (overlay_site_db/…). Разные сайты таблицы не делят.
sql.exec(sql, args?)
Оператор без result set (DDL / INSERT / UPDATE / DELETE).
await window.engarde.ready;
await window.engarde.sql.exec(`
CREATE TABLE IF NOT EXISTS kv (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
`);
await window.engarde.sql.exec(
`INSERT INTO kv (key, value) VALUES (?1, ?2)
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
['counter', '3'],
);Возвращает { changes, lastInsertRowid }.
sql.query(sql, args?)
Чтение (SELECT). Возвращает { columns, rows }, где каждая строка — массив значений ячеек.
const { columns, rows } = await window.engarde.sql.query(
'SELECT value FROM kv WHERE key = ?1',
['counter'],
);
const value = rows[0]?.[0]; // первая колонка первой строкиПараметры
Нумерованные плейсхолдеры SQLite (?1, ?2, …). Значения — JSON-сериализуемые: строки, числа, boolean, null.
Лимиты (на стороне клиента)
| Лимит | Значение |
|---|---|
| Текст SQL | 64 KiB |
| Аргументы | 64 |
| Размер строкового аргумента | 256 KiB каждый |
| Строк в результате | 10 000 |
| Размер ответа | ~1 MiB |
| Файл БД | 64 MiB |
| Таймаут statement | 5 с |
| Rate limit | ~30 req/s на host (burst 60) |
| Одновременных WS | 8 на host |
Ошибки
Неудачный вызов отклоняет Promise с Error и опциональным error.code:
| Code | Смысл |
|---|---|
not_implemented |
doc / rpc (или неизвестный метод) |
invalid_params |
Плохой SQL / args |
forbidden |
Rate limit / слишком много сокетов |
timeout |
Statement дольше 5 с |
quota_exceeded |
Слишком много строк / большой результат |
sql_error |
Ошибка SQLite / worker |
unauthorized |
Мост отклонил host |
Формат по проводу (для отладки): JSON по WebSocket { id, ns, method, params } → { id, ok, result } или { id, ok: false, error: { code, message } }.
Минимальный пример
async function loadCounter() {
const api = window.engarde;
if (!api) return { mode: 'memory', value: 0 };
await api.ready;
await api.sql.exec(
'CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL)',
);
const { rows } = await api.sql.query(
'SELECT value FROM kv WHERE key = ?1',
['counter'],
);
return { mode: 'saved', value: Number(rows[0]?.[0] ?? 0) || 0 };
}Публикация сайта
- Собрать статику (
dist/). - Опубликовать через
engarde-operator site run dist(пишетengarde.json, сидит Collection). - Посетители открывают overlay-hostname через Engarde; HTML получает мост автоматически.
См. также пример в монорепо: examples/overlay-demo.