mpb-localkit 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/utils.ts","../../src/cli/commands/dev.ts","../../src/cli/generator/templates/index.ts","../../src/cli/generator/templates/auth.ts","../../src/cli/generator/templates/sync.ts","../../src/cli/generator/templates/ws.ts","../../src/cli/generator/templates/errors.ts","../../src/cli/generator/templates/health.ts","../../src/cli/generator/templates/r2-storage.ts","../../src/cli/generator/templates/kv-storage.ts","../../src/cli/generator/templates/wrangler.ts","../../src/cli/generator/index.ts","../../src/cli/targets/cloudflare.ts","../../src/cli/targets/node.ts","../../src/cli/commands/build.ts","../../src/cli/commands/deploy.ts","../../src/cli/commands/eject.ts","../../src/cli/index.ts"],"names":["program","join","mkdirSync","writeFileSync","resolve","existsSync","targets","require"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\n/** Color helpers for terminal output (no dependencies) */\nconst colors = {\n green: (s: string) => `\\x1b[32m${s}\\x1b[0m`,\n yellow: (s: string) => `\\x1b[33m${s}\\x1b[0m`,\n red: (s: string) => `\\x1b[31m${s}\\x1b[0m`,\n cyan: (s: string) => `\\x1b[36m${s}\\x1b[0m`,\n bold: (s: string) => `\\x1b[1m${s}\\x1b[0m`,\n dim: (s: string) => `\\x1b[2m${s}\\x1b[0m`,\n}\n\nexport const log = {\n info: (msg: string) => console.log(`${colors.cyan('ℹ')} ${msg}`),\n success: (msg: string) => console.log(`${colors.green('✓')} ${msg}`),\n warn: (msg: string) => console.warn(`${colors.yellow('⚠')} ${msg}`),\n error: (msg: string) => console.error(`${colors.red('✗')} ${msg}`),\n bold: (msg: string) => console.log(colors.bold(msg)),\n dim: (msg: string) => console.log(colors.dim(msg)),\n}\n\nconst CONFIG_CANDIDATES = [\n 'offlinekit.config.ts',\n 'offlinekit.config.js',\n 'offlinekit.schema.ts',\n 'offlinekit.schema.js',\n]\n\n/** Finds the OfflineKit config/schema file in the current working directory */\nexport function findSchemaFile(cwd = process.cwd()): string | null {\n for (const candidate of CONFIG_CANDIDATES) {\n const p = resolve(cwd, candidate)\n if (existsSync(p)) return p\n }\n return null\n}\n\n/** Dynamically imports and returns the default export from the schema file */\nexport async function loadSchema(filePath: string): Promise<unknown> {\n const mod = await import(filePath) as { default?: unknown }\n return mod.default ?? mod\n}\n","import type { Command } from 'commander'\nimport { log } from '../utils.js'\n\nexport function registerDev(program: Command): void {\n program\n .command('dev')\n .description('Start OfflineKit in local-only mode (no cloud sync)')\n .action(() => {\n process.env['OFFLINEKIT_MODE'] = 'local'\n log.bold('OfflineKit Dev Mode')\n log.info('Running in local-only mode (no cloud sync)')\n log.dim('All data is stored locally. Sync is disabled.')\n })\n}\n","/** Main Hono app entry point template */\nexport function workerIndexTemplate(_appName: string): string {\n return `\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport { auth } from './routes/auth.js'\nimport { sync } from './routes/sync.js'\nimport { ws, WsSessions } from './routes/ws.js'\nimport { errors } from './routes/errors.js'\nimport { health } from './routes/health.js'\n\nexport interface Env {\n STORAGE: R2Bucket\n KV: KVNamespace\n JWT_SECRET: string\n WS_SESSIONS: DurableObjectNamespace\n}\n\nexport { WsSessions }\n\nconst app = new Hono<{ Bindings: Env }>()\n\napp.use('/*', cors({\n origin: '*',\n allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n}))\n\napp.route('/auth', auth)\napp.route('/sync', sync)\napp.route('/ws', ws)\napp.route('/errors', errors)\napp.route('/health', health)\n\nexport default app\n`.trimStart()\n}\n","/** Auth routes: POST /auth/signup, /auth/signin, /auth/signout */\nexport function authTemplate(): string {\n return `\nimport { Hono } from 'hono'\nimport { sign, verify } from 'hono/jwt'\nimport type { Env } from './index.js'\nimport { AUTH_KEY, SESSION_KEY, type StoredUser } from '../storage/kv.js'\n\nexport const auth = new Hono<{ Bindings: Env }>()\n\n/** Constant-time string comparison to prevent timing side-channels */\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n const encoder = new TextEncoder()\n const bufA = encoder.encode(a)\n const bufB = encoder.encode(b)\n let result = 0\n for (let i = 0; i < bufA.length; i++) result |= bufA[i] ^ bufB[i]\n return result === 0\n}\n\n/** Extract userId from Bearer token — throws on invalid/expired */\nexport async function authMiddleware(c: any, next: () => Promise<void>) {\n const header = c.req.header('Authorization') ?? ''\n const token = header.startsWith('Bearer ') ? header.slice(7) : null\n if (!token) return c.json({ error: 'Unauthorized' }, 401)\n try {\n const payload = await verify(token, c.env.JWT_SECRET) as { sub: string }\n c.set('userId', payload.sub)\n } catch {\n return c.json({ error: 'Invalid token' }, 401)\n }\n return next()\n}\n\nauth.post('/signup', async (c) => {\n const { email, passwordHash } = await c.req.json<{ email: string; passwordHash: string }>()\n if (!email || !passwordHash) return c.json({ error: 'email and passwordHash required' }, 400)\n\n const existing = await c.env.KV.get(AUTH_KEY(email))\n if (existing) return c.json({ error: 'Email already registered' }, 409)\n\n const userId = crypto.randomUUID()\n const user: StoredUser = { userId, email, passwordHash }\n await c.env.KV.put(AUTH_KEY(email), JSON.stringify(user))\n\n const token = await sign({ sub: userId, email, exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30 }, c.env.JWT_SECRET)\n return c.json({ user: { id: userId, email }, token }, 201)\n})\n\nauth.post('/signin', async (c) => {\n const { email, passwordHash } = await c.req.json<{ email: string; passwordHash: string }>()\n if (!email || !passwordHash) return c.json({ error: 'email and passwordHash required' }, 400)\n\n const raw = await c.env.KV.get(AUTH_KEY(email))\n if (!raw) return c.json({ error: 'Invalid credentials' }, 401)\n\n const user = JSON.parse(raw) as StoredUser\n if (!timingSafeEqual(user.passwordHash, passwordHash)) return c.json({ error: 'Invalid credentials' }, 401)\n\n const token = await sign({ sub: user.userId, email, exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30 }, c.env.JWT_SECRET)\n return c.json({ user: { id: user.userId, email }, token })\n})\n\nauth.post('/signout', async (c) => {\n const header = c.req.header('Authorization') ?? ''\n const token = header.startsWith('Bearer ') ? header.slice(7) : null\n if (token) await c.env.KV.delete(SESSION_KEY(token))\n return c.json({ ok: true })\n})\n`.trimStart()\n}\n","/** Sync routes: POST /sync/push, GET /sync/pull?since= */\nexport function syncTemplate(): string {\n return `\nimport { Hono } from 'hono'\nimport type { Env } from './index.js'\nimport { authMiddleware } from './auth.js'\nimport { docKey, collectionPrefix } from '../storage/r2.js'\n\ninterface SyncDoc {\n _id: string\n _collection: string\n _updatedAt: number\n _deleted: boolean\n [key: string]: unknown\n}\n\nexport const sync = new Hono<{ Bindings: Env }>()\n\nsync.use('/*', authMiddleware)\n\nsync.post('/push', async (c) => {\n const userId = c.get('userId') as string\n const { changes } = await c.req.json<{ changes: SyncDoc[] }>()\n\n await Promise.all(\n changes.map(async (doc) => {\n const key = docKey(userId, doc._collection, doc._id)\n const existing = await c.env.STORAGE.get(key, 'json') as SyncDoc | null\n\n // LWW — only overwrite if incoming is newer\n if (!existing || doc._updatedAt >= existing._updatedAt) {\n await c.env.STORAGE.put(key, JSON.stringify(doc), {\n customMetadata: { updatedAt: String(doc._updatedAt) },\n })\n }\n }),\n )\n\n return c.json({ ok: true, received: changes.length })\n})\n\nsync.get('/pull', async (c) => {\n const userId = c.get('userId') as string\n const since = parseInt(c.req.query('since') ?? '0', 10)\n\n // Scan all collections for this user — list all objects under {userId}/\n const prefix = \\`\\${userId}/\\`\n const listed = await c.env.STORAGE.list({ prefix })\n\n const changes = await Promise.all(\n listed.objects\n .filter((obj) => {\n const ts = parseInt(obj.customMetadata?.updatedAt ?? '0', 10)\n return ts > since\n })\n .map(async (obj) => {\n const blob = await c.env.STORAGE.get(obj.key, 'json')\n return blob as SyncDoc\n }),\n )\n\n return c.json({ changes: changes.filter(Boolean) })\n})\n`.trimStart()\n}\n","/** WebSocket Durable Object handler for real-time sync */\nexport function wsTemplate(): string {\n return `import { Hono } from 'hono'\nimport { verify } from 'hono/jwt'\nimport type { Env } from './index.js'\nimport { authMiddleware } from './auth.js'\nimport { docKey } from '../storage/r2.js'\n\nexport const ws = new Hono<{ Bindings: Env }>()\n\n// Proxy WS upgrade requests to the user-scoped Durable Object\nws.get('/', authMiddleware, async (c) => {\n const userId = c.get('userId') as string\n const id = c.env.WS_SESSIONS.idFromName(userId)\n return c.env.WS_SESSIONS.get(id).fetch(c.req.raw)\n})\n\n// ---- Durable Object: one instance per userId, owns all WebSocket sessions ----\n\ninterface SyncDoc {\n _id: string\n _collection: string\n _updatedAt: number\n _deleted: boolean\n [key: string]: unknown\n}\n\ninterface WsMsg {\n type: string\n id?: string\n payload?: unknown\n since?: number\n token?: string\n}\n\nexport class WsSessions {\n private readonly sessions = new Map<WebSocket, { userId: string | null }>()\n\n constructor(\n private readonly state: DurableObjectState,\n private readonly env: Env,\n ) {}\n\n async fetch(request: Request): Promise<Response> {\n if (request.headers.get('Upgrade') !== 'websocket') {\n return new Response('Expected WebSocket upgrade', { status: 426 })\n }\n\n const pair = new WebSocketPair()\n const [client, server] = Object.values(pair) as [WebSocket, WebSocket]\n server.accept()\n\n const session = { userId: null as string | null }\n this.sessions.set(server, session)\n\n server.addEventListener('message', async (event) => {\n let msg: WsMsg\n try { msg = JSON.parse(event.data as string) as WsMsg } catch { return }\n try {\n await this.handle(server, session, msg)\n } catch (err) {\n server.send(JSON.stringify({ type: 'error', id: msg.id, payload: { message: String(err) } }))\n }\n })\n\n server.addEventListener('close', () => { this.sessions.delete(server) })\n\n return new Response(null, { status: 101, webSocket: client })\n }\n\n private async handle(ws: WebSocket, session: { userId: string | null }, msg: WsMsg): Promise<void> {\n if (msg.type === 'auth') {\n try {\n const payload = await verify(msg.token ?? '', this.env.JWT_SECRET) as { sub?: string }\n if (!payload.sub) throw new Error('Missing sub')\n session.userId = payload.sub\n ws.send(JSON.stringify({ type: 'auth_ack', id: msg.id }))\n } catch {\n ws.send(JSON.stringify({ type: 'error', id: msg.id, payload: { message: 'Unauthorized' } }))\n ws.close(1008, 'Unauthorized')\n }\n return\n }\n\n if (!session.userId) {\n ws.send(JSON.stringify({ type: 'error', id: msg.id, payload: { message: 'Not authenticated' } }))\n return\n }\n\n if (msg.type === 'push') {\n const { changes } = msg.payload as { changes: SyncDoc[] }\n await Promise.all(changes.map(async (doc) => {\n const key = docKey(session.userId!, doc._collection, doc._id)\n const existing = await this.env.STORAGE.get(key, 'json') as SyncDoc | null\n if (!existing || doc._updatedAt >= existing._updatedAt) {\n await this.env.STORAGE.put(key, JSON.stringify(doc), {\n customMetadata: { updatedAt: String(doc._updatedAt) },\n })\n }\n }))\n ws.send(JSON.stringify({ type: 'push_ack', id: msg.id, payload: { ok: true } }))\n this.broadcast(session.userId, ws, { type: 'remote_changes', changes })\n return\n }\n\n if (msg.type === 'pull') {\n const since = typeof msg.since === 'number' ? msg.since : 0\n const listed = await this.env.STORAGE.list({ prefix: \\`\\${session.userId}/\\` })\n const changes = (await Promise.all(\n listed.objects\n .filter((o) => parseInt(o.customMetadata?.updatedAt ?? '0', 10) > since)\n .map((o) => this.env.STORAGE.get(o.key, 'json') as Promise<SyncDoc>),\n )).filter(Boolean)\n ws.send(JSON.stringify({ type: 'pull_response', id: msg.id, payload: { changes } }))\n return\n }\n\n ws.send(JSON.stringify({ type: 'error', id: msg.id, payload: { message: \\`Unknown type: \\${msg.type}\\` } }))\n }\n\n private broadcast(userId: string, sender: WebSocket, msg: object): void {\n const data = JSON.stringify(msg)\n for (const [ws, s] of this.sessions) {\n if (ws !== sender && s.userId === userId) {\n try { ws.send(data) } catch { /* stale connection */ }\n }\n }\n }\n}\n\n// JWT verification handled by hono/jwt verify() — no hand-rolled crypto needed\n`.trimStart()\n}\n","/** GET /errors — list error blobs for authenticated user */\nexport function errorsTemplate(): string {\n return `\nimport { Hono } from 'hono'\nimport type { Env } from './index.js'\nimport { authMiddleware } from './auth.js'\n\nexport const errors = new Hono<{ Bindings: Env }>()\n\nerrors.use('/*', authMiddleware)\n\nerrors.get('/', async (c) => {\n const userId = c.get('userId') as string\n const prefix = \\`\\${userId}/_errors/\\`\n\n const listed = await c.env.STORAGE.list({ prefix })\n const results = await Promise.all(\n listed.objects.map(async (obj) => {\n const blob = await c.env.STORAGE.get(obj.key)\n if (!blob) return null\n return blob.json()\n }),\n )\n\n return c.json({ errors: results.filter(Boolean) })\n})\n`.trimStart()\n}\n","/** GET /health — liveness check */\nexport function healthTemplate(): string {\n return `\nimport { Hono } from 'hono'\n\nexport const health = new Hono()\n\nhealth.get('/', (c) => {\n return c.json({ status: 'ok', timestamp: Date.now() })\n})\n`.trimStart()\n}\n","/** R2 document key helpers — {userId}/{collection}/{docId}.json */\nexport function r2StorageTemplate(): string {\n return `\nexport const docKey = (userId: string, collection: string, docId: string) =>\n \\`\\${userId}/\\${collection}/\\${docId}.json\\`\n\nexport const collectionPrefix = (userId: string, collection: string) =>\n \\`\\${userId}/\\${collection}/\\`\n`.trimStart()\n}\n","/** KV storage helpers — sessions, auth credentials, metadata */\nexport function kvStorageTemplate(): string {\n return `\n/** Key: auth:{email} Value: { userId, email, passwordHash } */\nexport const AUTH_KEY = (email: string) => \\`auth:\\${email}\\`\n\n/** Key: session:{token} Value: { userId, email, exp } */\nexport const SESSION_KEY = (token: string) => \\`session:\\${token}\\`\n\n/** Key: meta:{userId} Value: arbitrary metadata object */\nexport const META_KEY = (userId: string) => \\`meta:\\${userId}\\`\n\nexport interface StoredUser {\n userId: string\n email: string\n passwordHash: string\n}\n\nexport interface StoredSession {\n userId: string\n email: string\n exp: number\n}\n`.trimStart()\n}\n","/** Generate wrangler.toml for a Cloudflare Worker with R2 + KV bindings */\nexport function wranglerTemplate(appName: string): string {\n const name = appName.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n return `name = \"${name}-worker\"\nmain = \"src/index.ts\"\ncompatibility_date = \"2024-01-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n[[r2_buckets]]\nbinding = \"STORAGE\"\nbucket_name = \"${name}-storage\"\n\n[[kv_namespaces]]\nbinding = \"KV\"\nid = \"REPLACE_WITH_KV_NAMESPACE_ID\"\n\n[[durable_objects.bindings]]\nname = \"WS_SESSIONS\"\nclass_name = \"WsSessions\"\n\n[[migrations]]\ntag = \"v1\"\nnew_classes = [\"WsSessions\"]\n\n# Security: Set secrets via Cloudflare dashboard or CLI:\n# wrangler secret put JWT_SECRET\n`\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { workerIndexTemplate } from './templates/index.js'\nimport { authTemplate } from './templates/auth.js'\nimport { syncTemplate } from './templates/sync.js'\nimport { wsTemplate } from './templates/ws.js'\nimport { errorsTemplate } from './templates/errors.js'\nimport { healthTemplate } from './templates/health.js'\nimport { r2StorageTemplate } from './templates/r2-storage.js'\nimport { kvStorageTemplate } from './templates/kv-storage.js'\nimport { wranglerTemplate } from './templates/wrangler.js'\n\ninterface GeneratorOptions {\n appName: string\n outDir: string\n}\n\n/**\n * Generates a complete Hono Cloudflare Worker from the app schema.\n * Writes all source files to outDir.\n */\nexport function generateWorker(options: GeneratorOptions): void {\n const { appName, outDir } = options\n\n // Create directory tree\n const dirs = [\n outDir,\n join(outDir, 'src'),\n join(outDir, 'src', 'routes'),\n join(outDir, 'src', 'storage'),\n ]\n for (const dir of dirs) mkdirSync(dir, { recursive: true })\n\n const files: [string, string][] = [\n [join(outDir, 'wrangler.toml'), wranglerTemplate(appName)],\n [join(outDir, 'src', 'index.ts'), workerIndexTemplate(appName)],\n [join(outDir, 'src', 'routes', 'auth.ts'), authTemplate()],\n [join(outDir, 'src', 'routes', 'sync.ts'), syncTemplate()],\n [join(outDir, 'src', 'routes', 'ws.ts'), wsTemplate()],\n [join(outDir, 'src', 'routes', 'errors.ts'), errorsTemplate()],\n [join(outDir, 'src', 'routes', 'health.ts'), healthTemplate()],\n [join(outDir, 'src', 'storage', 'r2.ts'), r2StorageTemplate()],\n [join(outDir, 'src', 'storage', 'kv.ts'), kvStorageTemplate()],\n [join(outDir, 'package.json'), workerPackageJson(appName)],\n [join(outDir, 'tsconfig.json'), workerTsConfig()],\n ]\n\n for (const [path, content] of files) {\n writeFileSync(path, content, 'utf8')\n }\n\n console.warn(\n '\\n⚠ Remember to replace placeholder values in wrangler.toml before deploying:\\n' +\n ' - JWT_SECRET = \"REPLACE_WITH_SECRET\" → set a strong random secret\\n' +\n ' - KV namespace id → set your actual KV namespace ID\\n'\n )\n}\n\nfunction workerPackageJson(appName: string): string {\n const name = appName.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n return JSON.stringify(\n {\n name: `${name}-worker`,\n version: '1.0.0',\n type: 'module',\n scripts: {\n dev: 'wrangler dev',\n deploy: 'wrangler deploy',\n 'type-check': 'tsc --noEmit',\n },\n dependencies: {\n hono: '^4.6.0',\n },\n devDependencies: {\n '@cloudflare/workers-types': '^4.0.0',\n typescript: '^5.6.0',\n wrangler: '^3.0.0',\n },\n },\n null,\n 2,\n )\n}\n\nfunction workerTsConfig(): string {\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['@cloudflare/workers-types'],\n strict: true,\n skipLibCheck: true,\n },\n include: ['src/**/*'],\n },\n null,\n 2,\n )\n}\n","import { generateWorker } from '../generator/index.js'\nimport type { DeployTarget } from './types.js'\n\nexport const cloudflareTarget: DeployTarget = {\n name: 'cloudflare',\n generate({ appName, outDir }: { appName: string; outDir: string }): void {\n generateWorker({ appName, outDir })\n },\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { authTemplate } from '../generator/templates/auth.js'\nimport { syncTemplate } from '../generator/templates/sync.js'\nimport { wsTemplate } from '../generator/templates/ws.js'\nimport { errorsTemplate } from '../generator/templates/errors.js'\nimport { healthTemplate } from '../generator/templates/health.js'\nimport { r2StorageTemplate } from '../generator/templates/r2-storage.js'\nimport { kvStorageTemplate } from '../generator/templates/kv-storage.js'\nimport type { DeployTarget } from './types.js'\n\nexport const nodeTarget: DeployTarget = {\n name: 'node',\n generate({ appName, outDir }: { appName: string; outDir: string }): void {\n const dirs = [\n outDir,\n join(outDir, 'src'),\n join(outDir, 'src', 'routes'),\n join(outDir, 'src', 'storage'),\n ]\n for (const dir of dirs) mkdirSync(dir, { recursive: true })\n\n const serverEntry = nodeServerEntry(appName)\n\n const files: [string, string][] = [\n [join(outDir, 'src', 'index.ts'), serverEntry],\n [join(outDir, 'src', 'routes', 'auth.ts'), authTemplate()],\n [join(outDir, 'src', 'routes', 'sync.ts'), syncTemplate()],\n [join(outDir, 'src', 'routes', 'ws.ts'), wsTemplate()],\n [join(outDir, 'src', 'routes', 'errors.ts'), errorsTemplate()],\n [join(outDir, 'src', 'routes', 'health.ts'), healthTemplate()],\n [join(outDir, 'src', 'storage', 'r2.ts'), r2StorageTemplate()],\n [join(outDir, 'src', 'storage', 'kv.ts'), kvStorageTemplate()],\n [join(outDir, 'package.json'), nodePackageJson(appName)],\n [join(outDir, 'tsconfig.json'), nodeTsConfig()],\n ]\n\n for (const [path, content] of files) {\n writeFileSync(path, content, 'utf8')\n }\n },\n}\n\nfunction nodeServerEntry(appName: string): string {\n return `import { Hono } from 'hono'\nimport { serve } from '@hono/node-server'\nimport { createNodeWebSocket } from '@hono/node-ws'\nimport { verify } from 'hono/jwt'\nimport { auth } from './routes/auth.js'\nimport { sync } from './routes/sync.js'\nimport { health } from './routes/health.js'\n\nconst app = new Hono()\nconst { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })\n\n// In-memory session store for WebSocket connections (mirrors DO sessions map)\nconst sessions = new Map<WebSocket, { userId: string }>()\n\napp.route('/auth', auth)\napp.route('/sync', sync)\napp.route('/health', health)\n\napp.get('/ws', upgradeWebSocket(() => ({\n onOpen(_event, ws) {\n // session added on auth message\n },\n async onMessage(event, ws) {\n let msg: { type: string; [k: string]: unknown }\n try {\n msg = JSON.parse(event.data.toString())\n } catch {\n ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON' }))\n return\n }\n\n const session = sessions.get(ws.raw as WebSocket)\n\n if (msg.type === 'auth') {\n try {\n const jwtSecret = process.env.JWT_SECRET\n if (!jwtSecret) throw new Error('JWT_SECRET not configured')\n const payload = await verify(msg.token as string, jwtSecret) as { sub: string }\n if (!payload.sub) throw new Error('Missing sub claim')\n sessions.set(ws.raw as WebSocket, { userId: payload.sub })\n ws.send(JSON.stringify({ type: 'auth', ok: true }))\n } catch {\n ws.send(JSON.stringify({ type: 'error', message: 'Invalid token' }))\n ws.close()\n }\n } else if (!session) {\n ws.send(JSON.stringify({ type: 'error', message: 'Not authenticated' }))\n } else if (msg.type === 'push') {\n ws.send(JSON.stringify({ type: 'push', ok: true }))\n } else if (msg.type === 'pull') {\n ws.send(JSON.stringify({ type: 'pull', changes: [] }))\n }\n },\n onClose(_event, ws) {\n sessions.delete(ws.raw as WebSocket)\n },\n})))\n\nconst port = parseInt(process.env.PORT ?? '3000', 10)\nconsole.log(\\`${appName} server running on http://localhost:\\${port}\\`)\n\nconst server = serve({ fetch: app.fetch, port })\ninjectWebSocket(server)\n`\n}\n\nfunction nodePackageJson(appName: string): string {\n const name = appName.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n return JSON.stringify(\n {\n name: `${name}-server`,\n version: '1.0.0',\n type: 'module',\n scripts: {\n dev: 'tsx watch src/index.ts',\n start: 'node dist/index.js',\n build: 'tsc',\n },\n dependencies: {\n hono: '^4.6.0',\n '@hono/node-server': '^1.13.0',\n '@hono/node-ws': '^1.0.0',\n },\n devDependencies: {\n typescript: '^5.6.0',\n tsx: '^4.0.0',\n '@types/node': '^22.0.0',\n },\n },\n null,\n 2,\n )\n}\n\nfunction nodeTsConfig(): string {\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node'],\n strict: true,\n skipLibCheck: true,\n outDir: 'dist',\n },\n include: ['src/**/*'],\n },\n null,\n 2,\n )\n}\n","import type { Command } from 'commander'\nimport { resolve } from 'node:path'\nimport { cloudflareTarget } from '../targets/cloudflare.js'\nimport { nodeTarget } from '../targets/node.js'\nimport { findSchemaFile, loadSchema, log } from '../utils.js'\n\nconst targets = { cloudflare: cloudflareTarget, node: nodeTarget }\n\nexport function registerBuild(program: Command): void {\n program\n .command('build')\n .description('Build the OfflineKit backend bundle')\n .option('-o, --out <dir>', 'Output directory', '.offlinekit/worker')\n .option('-n, --name <name>', 'App name for the worker', 'offlinekit-app')\n .option('-t, --target <target>', 'Deploy target: cloudflare or node', 'cloudflare')\n .action(async (opts: { out: string; name: string; target: string }) => {\n log.bold('OfflineKit Build')\n\n const target = targets[opts.target as keyof typeof targets]\n if (!target) {\n log.error(`Unknown target: ${opts.target}. Valid targets: ${Object.keys(targets).join(', ')}`)\n process.exit(1)\n }\n\n const schemaFile = findSchemaFile()\n if (!schemaFile) {\n log.error('No schema file found. Create offlinekit.config.ts in your project root.')\n process.exit(1)\n }\n\n log.info(`Found schema: ${schemaFile}`)\n\n try {\n await loadSchema(schemaFile)\n log.success('Schema loaded successfully')\n } catch (err) {\n log.error(`Schema load failed: ${err instanceof Error ? err.message : String(err)}`)\n process.exit(1)\n }\n\n const outDir = resolve(process.cwd(), opts.out)\n log.info(`Generating ${target.name} backend to: ${outDir}`)\n\n try {\n target.generate({ appName: opts.name, outDir })\n log.success(`Backend generated at ${outDir}`)\n if (opts.target === 'node') {\n log.dim(`Run: cd ${opts.out} && npm install && npm run dev`)\n } else {\n log.dim(`Run: cd ${opts.out} && npm install && wrangler deploy`)\n }\n } catch (err) {\n log.error(`Build failed: ${err instanceof Error ? err.message : String(err)}`)\n process.exit(1)\n }\n })\n}\n","import type { Command } from 'commander'\nimport { execSync, spawnSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { log } from '../utils.js'\n\nfunction checkWrangler(): string | null {\n try {\n const result = execSync('wrangler --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })\n return result.trim()\n } catch {\n return null\n }\n}\n\nexport function registerDeploy(program: Command): void {\n program\n .command('deploy')\n .description('Deploy the OfflineKit Worker to Cloudflare via Wrangler')\n .option('-d, --dir <dir>', 'Worker output directory', '.offlinekit/worker')\n .option('-e, --env <env>', 'Wrangler environment (e.g. production, staging)')\n .option('-t, --target <target>', 'Deploy target (default: cloudflare)', 'cloudflare')\n .option('--dry-run', 'Print the wrangler command without running it')\n .action((opts: { dir: string; env?: string; target: string; dryRun?: boolean }) => {\n log.bold('OfflineKit Deploy')\n\n // 0. Check if project has been ejected\n const serverDir = resolve(process.cwd(), 'server')\n if (existsSync(serverDir)) {\n log.info('Warning: This project has been ejected (./server/ exists).')\n log.dim('You can deploy the ejected code directly with `cd server && wrangler deploy`.')\n }\n\n // 0b. Validate target\n if (opts.target !== 'cloudflare') {\n log.error(`Deploy target \"${opts.target}\" is not supported yet. Only \"cloudflare\" is deployable.`)\n log.dim('For Node.js targets, run `offlinekit build --target node` then deploy the output manually.')\n process.exit(1)\n }\n\n // 1. Check wrangler\n const version = checkWrangler()\n if (!version) {\n log.error('wrangler is not installed or not in PATH.')\n log.dim('Install it with: npm install -g wrangler')\n process.exit(1)\n }\n log.success(`wrangler ${version}`)\n\n // 2. Check output directory exists\n const outDir = resolve(process.cwd(), opts.dir)\n if (!existsSync(outDir)) {\n log.error(`Worker directory not found: ${outDir}`)\n log.dim('Run `offlinekit build` first to generate the worker.')\n process.exit(1)\n }\n\n // 3. Check wrangler.toml is present\n const tomlPath = resolve(outDir, 'wrangler.toml')\n if (!existsSync(tomlPath)) {\n log.error(`wrangler.toml not found in ${outDir}`)\n log.dim('Run `offlinekit build` to regenerate the worker.')\n process.exit(1)\n }\n\n // 4. Build wrangler deploy command\n const args = ['deploy']\n if (opts.env) args.push('--env', opts.env)\n\n log.info(`Deploying from: ${outDir}`)\n if (opts.env) log.info(`Environment: ${opts.env}`)\n\n if (opts.dryRun) {\n log.dim(`[dry-run] wrangler ${args.join(' ')}`)\n log.dim(`[dry-run] cwd: ${outDir}`)\n return\n }\n\n // 5. Run wrangler deploy\n const result = spawnSync('wrangler', args, {\n cwd: outDir,\n stdio: 'inherit',\n encoding: 'utf8',\n })\n\n if (result.status !== 0) {\n log.error(`wrangler deploy failed (exit ${result.status ?? 'unknown'})`)\n process.exit(result.status ?? 1)\n }\n\n log.success('Deployed successfully!')\n })\n}\n","import type { Command } from 'commander'\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { cloudflareTarget } from '../targets/cloudflare.js'\nimport { nodeTarget } from '../targets/node.js'\nimport type { DeployTarget } from '../targets/types.js'\nimport { log } from '../utils.js'\n\nconst targets: Record<string, DeployTarget> = {\n cloudflare: cloudflareTarget,\n node: nodeTarget,\n}\n\nexport function registerEject(program: Command): void {\n program\n .command('eject')\n .description('Eject generated Worker code into ./server/ for manual customization')\n .option('-o, --out <dir>', 'Output directory for ejected code', 'server')\n .option('-n, --name <name>', 'App name', 'offlinekit-app')\n .option('-t, --target <target>', 'Deploy target (cloudflare, node)', 'cloudflare')\n .action((opts: { out: string; name: string; target: string }) => {\n log.bold('OfflineKit Eject')\n\n const target = targets[opts.target]\n if (!target) {\n log.error(`Unknown target: \"${opts.target}\"`)\n log.dim(`Available targets: ${Object.keys(targets).join(', ')}`)\n process.exit(1)\n }\n\n const outDir = resolve(process.cwd(), opts.out)\n\n if (existsSync(outDir)) {\n log.error(`Directory already exists: ${outDir}`)\n log.dim('Remove it or choose a different output directory with --out.')\n process.exit(1)\n }\n\n log.info(`Ejecting ${opts.target} code to: ${outDir}`)\n\n try {\n target.generate({ appName: opts.name, outDir })\n log.success(`Ejected to ./${opts.out}/`)\n\n if (opts.target === 'node') {\n log.dim('You can now customize and deploy your Node.js server.')\n log.dim('')\n log.dim('Next steps:')\n log.dim(` cd ${opts.out}`)\n log.dim(' npm install')\n log.dim(' npm run dev')\n log.dim('')\n log.dim('To run in production: npm start')\n } else {\n log.dim('You can now customize and deploy with `wrangler deploy`.')\n log.dim('')\n log.dim('Next steps:')\n log.dim(` cd ${opts.out}`)\n log.dim(' npm install')\n log.dim(' wrangler deploy')\n log.dim('')\n log.dim('Note: Running `offlinekit deploy` will warn that code has been ejected.')\n }\n } catch (err) {\n log.error(`Eject failed: ${err instanceof Error ? err.message : String(err)}`)\n process.exit(1)\n }\n })\n}\n","#!/usr/bin/env node\nimport { Command } from 'commander'\nimport { createRequire } from 'node:module'\nimport { registerDev } from './commands/dev.js'\nimport { registerBuild } from './commands/build.js'\nimport { registerDeploy } from './commands/deploy.js'\nimport { registerEject } from './commands/eject.js'\n\nconst require = createRequire(import.meta.url)\nconst pkg = require('../../package.json') as { version: string; description: string }\n\nconst program = new Command()\n\nprogram\n .name('offlinekit')\n .description(pkg.description)\n .version(pkg.version, '-v, --version', 'Print the current version')\n\nregisterDev(program)\nregisterBuild(program)\nregisterDeploy(program)\nregisterEject(program)\n\nprogram.parse(process.argv)\n"]}
@@ -0,0 +1,404 @@
1
+ import { ZodRawShape, ZodObject } from 'zod';
2
+ export { z } from 'zod';
3
+ import { e as CollectionOptions, b as CollectionDescriptor, f as StorageAdapter, D as Doc, F as Filter, g as Change, d as SyncEngine, c as SyncConfig, h as SyncTransport, P as PushPayload, i as PullPayload, A as AuthAdapter, a as Credentials, U as User, j as SocialAuthAdapter, k as SocialProvider, O as ObservableCollection } from '../collection-CBG-_MiM.js';
4
+ export { C as Collection, l as OfflineSession, Q as QueryOptions, m as SyncResult, S as SyncStatus, T as TransportType, n as WhereClause, W as WithMeta } from '../collection-CBG-_MiM.js';
5
+ import { E as ErrorConfig, a as ErrorEntry, b as ErrorType, c as ErrorContext, S as SyncAPI, d as ErrorTrackingConfig, A as AuthConfig } from '../types-BlqxG_-6.js';
6
+ export { e as AppConfig, f as ErrorSnapshot } from '../types-BlqxG_-6.js';
7
+ import { A as AuthStore, S as SyncStore } from '../events-hGI5qde5.js';
8
+ export { c as collectionQueryOptions, l as localkitKeys, s as subscribeToCollection } from '../query-BgIfgMSp.js';
9
+
10
+ /**
11
+ * Wraps a Zod schema to create a typed collection descriptor.
12
+ * TypeScript infers the document type from the schema automatically.
13
+ *
14
+ * @example
15
+ * const brews = collection(z.object({ style: z.string(), og: z.number() }))
16
+ * // brews._inferredType is { style: string; og: number }
17
+ */
18
+ declare function collection<TShape extends ZodRawShape>(schema: ZodObject<TShape>, options?: CollectionOptions): CollectionDescriptor<TShape>;
19
+
20
+ declare function generateId(): string;
21
+
22
+ /** A raw CryptoKey or a password+salt pair for PBKDF2 / AES-KW derivation. */
23
+ type KeySpec = CryptoKey | {
24
+ password: string;
25
+ salt: string;
26
+ };
27
+ /** Multi-version keychain configuration for key rotation support. */
28
+ interface KeychainConfig {
29
+ keys: Record<number, KeySpec>;
30
+ activeVersion: number;
31
+ }
32
+ /** Key-wrapping options (Mode 3). */
33
+ interface WrapConfig {
34
+ /** Base64-encoded wrapped data key from a previous session. */
35
+ wrapped?: string;
36
+ /** Called once with the base64-encoded wrapped data key on first use. */
37
+ onKeyWrapped?: (wrappedKey: string) => void;
38
+ }
39
+ /**
40
+ * Encryption configuration.
41
+ *
42
+ * - Mode 1 `{ key: { password, salt } }` — PBKDF2-SHA256 key derivation
43
+ * - Mode 2 `{ key: CryptoKey }` — raw key used directly
44
+ * - Mode 3 `{ key: { password, salt }, wrap: { ... } }` — AES-KW key wrapping
45
+ * - Keychain mode `{ keychain: { keys, activeVersion } }` — multi-version key rotation
46
+ */
47
+ interface EncryptionConfig {
48
+ key?: KeySpec;
49
+ keychain?: KeychainConfig;
50
+ wrap?: WrapConfig;
51
+ /** PBKDF2 iteration count (default: 600 000). */
52
+ iterations?: number;
53
+ }
54
+ /**
55
+ * Wraps any StorageAdapter with transparent AES-GCM 256-bit encryption.
56
+ *
57
+ * @throws If `inner` is already an EncryptedStorageAdapter (double-encryption guard).
58
+ * @throws If key verification fails (wrong password / corrupted wrapped key).
59
+ */
60
+ declare function encrypted(inner: StorageAdapter, config: EncryptionConfig): Promise<EncryptedStorageAdapter>;
61
+ declare class EncryptedStorageAdapter implements StorageAdapter {
62
+ private readonly inner;
63
+ private readonly keychain;
64
+ private readonly activeVersion;
65
+ constructor(inner: StorageAdapter, keychain: Map<number, CryptoKey>, activeVersion: number);
66
+ private isEncryptedDoc;
67
+ private encryptDoc;
68
+ private decryptDoc;
69
+ get(collection: string, id: string): Promise<Doc | null>;
70
+ getRaw(collection: string, id: string): Promise<Doc | null>;
71
+ getMany(collection: string, filter?: Filter): Promise<Doc[]>;
72
+ put(collection: string, id: string, doc: Doc): Promise<void>;
73
+ delete(collection: string, id: string): Promise<void>;
74
+ /** E2E: encrypted blobs travel to the server as-is — no decryption here. */
75
+ getChangesSince(timestamp: number): Promise<Change[]>;
76
+ /** Returns raw encrypted storage representation. Do not use in production data flows. */
77
+ getEncrypted(collection: string, id: string): Promise<Doc | null>;
78
+ }
79
+
80
+ interface ReEncryptResult {
81
+ total: number;
82
+ reEncrypted: number;
83
+ skipped: number;
84
+ errors: string[];
85
+ }
86
+ /**
87
+ * Re-encrypts all documents whose `_keyVersion` differs from `activeVersion`.
88
+ *
89
+ * The adapter must have a keychain containing both the old key(s) and the new
90
+ * active key so it can decrypt with the old key and re-encrypt with the active one.
91
+ *
92
+ * This operation is resumable: documents already at `activeVersion` are skipped,
93
+ * so a second run after an interruption picks up where it left off.
94
+ */
95
+ declare function reEncryptAll(adapter: EncryptedStorageAdapter, innerStorage: StorageAdapter, activeVersion: number): Promise<ReEncryptResult>;
96
+ /**
97
+ * Re-wraps a data key under a new password without re-encrypting any documents
98
+ * (Mode 3 — AES-KW).
99
+ *
100
+ * Unwraps the stored wrapped key using a key derived from `oldPassword`, then
101
+ * re-wraps it using a key derived from `newPassword`. The salt is unchanged,
102
+ * so documents are unaffected.
103
+ *
104
+ * @returns New base64-encoded wrapped key to persist in place of the old one.
105
+ */
106
+ declare function rewrapKey(oldPassword: string, newPassword: string, wrappedKey: string, salt: string, iterations?: number): Promise<string>;
107
+
108
+ interface MigrationResult {
109
+ total: number;
110
+ encrypted: number;
111
+ skipped: number;
112
+ errors: string[];
113
+ }
114
+ /**
115
+ * Migrates all plaintext documents from `storage` into `encryptedStorage`.
116
+ *
117
+ * Idempotent: documents that already have `_encrypted` and `_iv` fields are skipped.
118
+ * Handles mixed state: only plaintext documents are encrypted.
119
+ */
120
+ declare function migrateToEncrypted(storage: StorageAdapter, encryptedStorage: EncryptedStorageAdapter): Promise<MigrationResult>;
121
+
122
+ /**
123
+ * Last-Write-Wins conflict resolver.
124
+ * Compares _updatedAt timestamps; highest wins.
125
+ * Sufficient for single-user, multi-device scenarios.
126
+ */
127
+ declare function resolveConflict(local: Doc, remote: Doc): Doc;
128
+
129
+ /** Syncs when the user returns to the tab (visibilitychange → visible) */
130
+ declare function setupFocusTrigger(engine: SyncEngine, config: SyncConfig): () => void;
131
+ /** Syncs when the network comes back online */
132
+ declare function setupReconnectTrigger(engine: SyncEngine, config: SyncConfig): () => void;
133
+ /** Periodic sync at the configured interval while online */
134
+ declare function setupIntervalTrigger(engine: SyncEngine, config: SyncConfig): () => void;
135
+
136
+ declare class HttpTransport implements SyncTransport {
137
+ private readonly endpoint;
138
+ private token;
139
+ constructor(endpoint: string);
140
+ setToken(token: string | null): void;
141
+ push(payload: PushPayload): Promise<number>;
142
+ pull(payload: PullPayload): Promise<Doc[]>;
143
+ destroy(): void;
144
+ private fetchWithRetry;
145
+ private buildHeaders;
146
+ }
147
+
148
+ interface WebSocketTransportConfig {
149
+ url: string;
150
+ token?: string;
151
+ reconnect?: boolean;
152
+ maxReconnectAttempts?: number;
153
+ requestTimeout?: number;
154
+ onRemoteChanges?: (changes: Doc[]) => void;
155
+ onConnected?: () => void;
156
+ }
157
+ declare class WebSocketTransport implements SyncTransport {
158
+ private readonly config;
159
+ private ws;
160
+ private readonly pending;
161
+ private reconnectAttempt;
162
+ private reconnectTimer;
163
+ private destroyed;
164
+ private token;
165
+ constructor(config: WebSocketTransportConfig);
166
+ setToken(token: string | null): void;
167
+ push(payload: PushPayload): Promise<number>;
168
+ pull(payload: PullPayload): Promise<Doc[]>;
169
+ destroy(): void;
170
+ private connect;
171
+ private scheduleReconnect;
172
+ private send;
173
+ private generateId;
174
+ }
175
+
176
+ interface AutoTransportConfig {
177
+ wsUrl: string;
178
+ httpEndpoint: string;
179
+ token?: string;
180
+ onRemoteChanges?: (changes: Doc[]) => void;
181
+ }
182
+ declare class AutoTransport implements SyncTransport {
183
+ private readonly ws;
184
+ private readonly http;
185
+ private useWs;
186
+ constructor(config: AutoTransportConfig);
187
+ setToken(token: string | null): void;
188
+ push(payload: PushPayload): Promise<number>;
189
+ pull(payload: PullPayload): Promise<Doc[]>;
190
+ destroy(): void;
191
+ }
192
+
193
+ interface EmailPasswordAuthConfig {
194
+ endpoint: string;
195
+ }
196
+ /**
197
+ * Email + password auth adapter.
198
+ * - Hashes password client-side (PBKDF2) before sending
199
+ * - Stores JWT + user in localStorage for offline access
200
+ * - Exposes getToken() for the sync engine to attach to requests
201
+ */
202
+ declare class EmailPasswordAuth implements AuthAdapter {
203
+ private readonly config;
204
+ private session;
205
+ constructor(config: EmailPasswordAuthConfig);
206
+ signUp(credentials: Credentials): Promise<User>;
207
+ signIn(credentials: Credentials): Promise<User>;
208
+ signOut(): Promise<void>;
209
+ currentUser(): User | null;
210
+ getToken(): string | null;
211
+ }
212
+
213
+ /**
214
+ * Hashes a password client-side using PBKDF2 + Web Crypto API.
215
+ * Returns a `salt_hex:hash_hex` string. The server receives only the hash — never the plaintext.
216
+ *
217
+ * A cryptographically random 16-byte salt is generated per call so that the same
218
+ * password always produces a unique output. Use verifyPassword() to check passwords.
219
+ */
220
+ declare function hashPassword(password: string, _email?: string): Promise<string>;
221
+
222
+ /**
223
+ * Better Auth response envelope — all client methods return { data, error }.
224
+ * @see https://www.better-auth.com/docs/basic-usage
225
+ */
226
+ interface BetterAuthResponse<T> {
227
+ data: T | null;
228
+ error: {
229
+ message: string;
230
+ status?: number;
231
+ } | null;
232
+ }
233
+ /** Core user fields returned by Better Auth */
234
+ interface BetterAuthUser {
235
+ id: string;
236
+ email: string;
237
+ name: string;
238
+ image?: string | null;
239
+ emailVerified?: boolean;
240
+ createdAt?: Date;
241
+ updatedAt?: Date;
242
+ [key: string]: unknown;
243
+ }
244
+ /**
245
+ * Minimal interface matching Better Auth's createAuthClient() return type.
246
+ * Requires the bearer plugin on the server for token-based auth.
247
+ *
248
+ * @see https://www.better-auth.com/docs/plugins/bearer
249
+ */
250
+ interface BetterAuthClient {
251
+ signUp: {
252
+ email: (opts: {
253
+ email: string;
254
+ password: string;
255
+ name: string;
256
+ }) => Promise<BetterAuthResponse<{
257
+ user: BetterAuthUser;
258
+ token: string | null;
259
+ }>>;
260
+ };
261
+ signIn: {
262
+ email: (opts: {
263
+ email: string;
264
+ password: string;
265
+ }) => Promise<BetterAuthResponse<{
266
+ user: BetterAuthUser;
267
+ token: string;
268
+ }>>;
269
+ social: (opts: {
270
+ provider: string;
271
+ }) => Promise<BetterAuthResponse<{
272
+ url?: string;
273
+ redirect?: boolean;
274
+ user?: BetterAuthUser;
275
+ token?: string;
276
+ }>>;
277
+ };
278
+ signOut: () => Promise<BetterAuthResponse<null>>;
279
+ getSession: () => Promise<BetterAuthResponse<{
280
+ session: {
281
+ id: string;
282
+ token: string;
283
+ userId: string;
284
+ expiresAt: Date;
285
+ };
286
+ user: BetterAuthUser;
287
+ }>>;
288
+ }
289
+ interface BetterAuthAdapterConfig {
290
+ client: BetterAuthClient;
291
+ trustedOrigins?: string[];
292
+ }
293
+ declare class BetterAuthAdapter implements SocialAuthAdapter {
294
+ private readonly config;
295
+ private session;
296
+ constructor(config: BetterAuthAdapterConfig);
297
+ /**
298
+ * Revalidate the stored session against the server.
299
+ * Call on app startup to verify a cached bearer token is still valid.
300
+ * On network failure the cached session is kept for offline use.
301
+ */
302
+ revalidateSession(): Promise<User | null>;
303
+ signUp(credentials: Credentials): Promise<User>;
304
+ signIn(credentials: Credentials): Promise<User>;
305
+ signInWithSocial(provider: SocialProvider): Promise<User>;
306
+ signOut(): Promise<void>;
307
+ currentUser(): User | null;
308
+ getToken(): string | null;
309
+ }
310
+
311
+ declare class OfflineSessionAdapter implements AuthAdapter {
312
+ private readonly inner;
313
+ private readonly cacheDuration;
314
+ constructor(inner: AuthAdapter, cacheDuration?: number);
315
+ signUp(credentials: Credentials): Promise<User>;
316
+ signIn(credentials: Credentials): Promise<User>;
317
+ signOut(): Promise<void>;
318
+ currentUser(): User | null;
319
+ getToken(): string | null;
320
+ }
321
+
322
+ /**
323
+ * Low-level error tracker that persists errors to a StorageAdapter.
324
+ * Supports snapshot capture, webhook delivery with retry, and automatic pruning.
325
+ */
326
+ declare class ErrorTracker {
327
+ private readonly storage;
328
+ private readonly config;
329
+ constructor(storage: StorageAdapter, config: ErrorConfig);
330
+ /**
331
+ * Retrieve all stored error entries.
332
+ *
333
+ * @returns An array of all persisted {@link ErrorEntry} records.
334
+ */
335
+ getAll(): Promise<ErrorEntry[]>;
336
+ /**
337
+ * Clear all stored error entries (soft-deletes them from storage).
338
+ */
339
+ clear(): Promise<void>;
340
+ /**
341
+ * Capture an error and persist it to storage. Triggers the `onError` callback
342
+ * and webhook (if configured), then prunes old entries if over the limit.
343
+ *
344
+ * @param error - The Error instance to capture.
345
+ * @param type - The error category (e.g. `'unhandled_exception'`, `'sync_error'`).
346
+ * @param context - Optional additional context merged with auto-detected fields.
347
+ */
348
+ capture(error: Error, type: ErrorType, context?: Partial<ErrorContext>): Promise<void>;
349
+ private sendWebhook;
350
+ private reportWebhookFailure;
351
+ private captureSnapshot;
352
+ private pruneIfNeeded;
353
+ }
354
+
355
+ /** High-level API for error tracking wired into createApp. */
356
+ declare class ErrorManager {
357
+ private readonly tracker;
358
+ constructor(storage: StorageAdapter, config?: Partial<ErrorConfig>);
359
+ /**
360
+ * Capture an error and persist it to storage. Triggers the `onError` callback
361
+ * and webhook (if configured), then prunes old entries if over the limit.
362
+ *
363
+ * @param error - The Error instance to capture.
364
+ * @param type - The error category (defaults to `'unhandled_exception'`).
365
+ * @param context - Optional additional context (online status, user agent, etc.).
366
+ */
367
+ capture(error: Error, type?: ErrorType, context?: Partial<ErrorContext>): Promise<void>;
368
+ /**
369
+ * Retrieve all stored error entries.
370
+ *
371
+ * @returns An array of all persisted {@link ErrorEntry} records.
372
+ */
373
+ getAll(): Promise<ErrorEntry[]>;
374
+ /**
375
+ * Clear all stored error entries (soft-deletes them from storage).
376
+ */
377
+ clear(): Promise<void>;
378
+ /**
379
+ * Expose the underlying tracker for handler wiring (e.g. global error handlers).
380
+ *
381
+ * @returns The {@link ErrorTracker} instance.
382
+ */
383
+ getTracker(): ErrorTracker;
384
+ }
385
+
386
+ type ObservableInferredCollections<C extends Record<string, CollectionDescriptor<ZodRawShape>>> = {
387
+ [K in keyof C]: ObservableCollection<C[K]['_inferredType']>;
388
+ };
389
+ type App<C extends Record<string, CollectionDescriptor<ZodRawShape>>> = ObservableInferredCollections<C> & {
390
+ auth: AuthStore;
391
+ errors: ErrorManager;
392
+ syncStore: SyncStore;
393
+ sync?: SyncAPI;
394
+ };
395
+ interface CreateAppConfig<C extends Record<string, CollectionDescriptor<ZodRawShape>>> {
396
+ collections: C;
397
+ storage?: StorageAdapter;
398
+ sync?: Partial<SyncConfig>;
399
+ errorTracking?: ErrorTrackingConfig;
400
+ auth?: AuthConfig;
401
+ }
402
+ declare function createApp<C extends Record<string, CollectionDescriptor<ZodRawShape>>>(config: CreateAppConfig<C>): App<C>;
403
+
404
+ export { type App, AuthAdapter, AuthConfig, AutoTransport, type AutoTransportConfig, BetterAuthAdapter, type BetterAuthAdapterConfig, type BetterAuthClient, Change, CollectionDescriptor, type CreateAppConfig, Credentials, Doc, EmailPasswordAuth, type EmailPasswordAuthConfig, EncryptedStorageAdapter, type EncryptionConfig, ErrorContext, ErrorEntry, ErrorTrackingConfig, ErrorType, Filter, HttpTransport, type KeySpec, type KeychainConfig, type MigrationResult, OfflineSessionAdapter, PullPayload, PushPayload, type ReEncryptResult, SocialAuthAdapter, SocialProvider, StorageAdapter, SyncConfig, SyncEngine, SyncTransport, User, WebSocketTransport, type WebSocketTransportConfig, type WrapConfig, collection, createApp, encrypted, generateId, hashPassword, migrateToEncrypted, reEncryptAll, resolveConflict, rewrapKey, setupFocusTrigger, setupIntervalTrigger, setupReconnectTrigger };