@meistrari/pages
v0.1.0
Published
SQL, KV, objects and queues for Tela Pages backends, with a zero-config local mode
Maintainers
Keywords
Readme
@meistrari/pages
SQL, KV, objetos com estado e filas para backends de Tela Pages. O mesmo arquivo roda na sua máquina com bun run server.ts (recursos locais em ./.pages, sem gateway nem celld) e no Pages (recursos governados pelo gateway e executados no celld).
bun add @meistrari/pagesimport { kv, object, pages, queue, sql } from '@meistrari/pages'
export default pages({
async fetch(request, { path, viewer }) {
if (path === '/api/tasks') {
await sql('main').query('CREATE TABLE IF NOT EXISTS tasks (id TEXT PRIMARY KEY, title TEXT)')
await sql('main').query('INSERT OR IGNORE INTO tasks VALUES (?, ?)', ['t1', 'Revisar contrato'])
const { rows } = await sql('main').query('SELECT * FROM tasks')
await kv('settings').put('theme', 'dark', 3600)
const visits = await object('counters', 'visits').increment()
const job = await queue('jobs').send({ taskId: 't1', by: viewer?.email })
return Response.json({ rows, visits, job })
}
return new Response('not found', { status: 404 })
},
queues: {
// Entrega da fila. Lançar erro faz o Pages tentar de novo (4 tentativas) e depois marcar como failed.
jobs: async (job) => {
await object('counters', 'jobs').increment()
},
},
})Como funciona
pages() embrulha o handler. A cada requisição ele lê a credencial de cinco minutos que o Pages injeta, abre um contexto por invocação (AsyncLocalStorage) e é dele que sql, kv, object e queue tiram o cliente. Chamar essas funções fora de uma requisição lança um erro claro. Requisições concorrentes nunca compartilham credencial.
path já vem sem o prefixo /s/<site> das Pages hospedadas, então as rotas leem igual local e em produção. viewer traz email, nome e foto do usuário logado (null localmente e em entregas de fila). mode é 'pages' ou 'local'.
Entregas de fila chegam pela rota cadastrada na política da Page e são roteadas para queues[nome] antes do seu fetch; o gateway bloqueia viewers nessa rota. A entrega é pelo menos uma vez: deduplique por job.id antes de produzir efeitos que não podem repetir.
Modo local
Fora do runtime do Pages, o pacote usa SQLite do Bun em ./.pages (adicione ao .gitignore): um arquivo por banco SQL, mais state.sqlite com KV, objetos e trabalhos. Limites e erros são os mesmos do serviço (50 comandos por lote, 1.000 linhas, TTL de 60s a 1 ano, 128 KB por mensagem, 403 para fila sem handler, 409 em compareAndSet com versão antiga). A fila entrega para o seu handler no mesmo processo, com 4 tentativas e 1s entre elas, e trabalhos pendentes são retomados quando o servidor reinicia.
bun run --hot server.ts # sobe em http://localhost:3000Publicar no Pages
bun build server.ts --target=node --format=esm --outfile=server.mjs
zip server.zip server.mjs
pages-gateway deploy minha-page ./dist --mode spa-api --server ./server.zipOs recursos de uma Page precisam estar habilitados pelo operador do Pages (política em PAGES_GATEWAY_RESOURCES_CONFIG: bancos, namespaces, objetos, filas e rota de entrega). Sem política, qualquer uso de recurso falha com "Resources are not enabled for this Page". Detalhes do serviço, limites e contrato HTTP em services/page-resources/README.md.
API
| Função | Métodos |
| --- | --- |
| sql(db) | query(sql, params?), batch(statements) (lote = transação) |
| kv(namespace) | get(key), put(key, value, ttlSeconds?), delete(key) |
| object(namespace, id) | get(), set(value), delete(), increment(n?), compareAndSet(version, value) |
| queue(name) | send(payload), status(id) |
Erros do serviço são PageResourcesError com status HTTP (404 chave ausente, 403 recurso fora da política, 409 versão mudou, 413 limite excedido).
