mpb-localkit 1.3.7 → 1.4.2
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.
- package/dist/cli/index.js +303 -92
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.js +129 -3
- package/dist/core/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +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 LocalKit in local-only mode (no cloud sync)')\n .action(() => {\n process.env['OFFLINEKIT_MODE'] = 'local'\n log.bold('LocalKit 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 LocalKit backend bundle')\n .option('-o, --out <dir>', 'Output directory', '.offlinekit/worker')\n .option('-n, --name <name>', 'App name for the worker', 'localkit-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('LocalKit 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 LocalKit 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('LocalKit 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 `npx mpb-localkit 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 `npx mpb-localkit 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 `npx mpb-localkit 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', 'localkit-app')\n .option('-t, --target <target>', 'Deploy target (cloudflare, node)', 'cloudflare')\n .action((opts: { out: string; name: string; target: string }) => {\n log.bold('LocalKit 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 `npx mpb-localkit 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('mpb-localkit')\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"]}
|
|
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 LocalKit in local-only mode (no cloud sync)')\n .action(() => {\n process.env['OFFLINEKIT_MODE'] = 'local'\n log.bold('LocalKit 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, 'HS256') 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, 'HS256')\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, 'HS256')\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, 'HS256') 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, kvNamespaceId?: 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 = \"${kvNamespaceId ?? 'REPLACE_WITH_KV_NAMESPACE_ID'}\"\n\n[[durable_objects.bindings]]\nname = \"WS_SESSIONS\"\nclass_name = \"WsSessions\"\n\n[[migrations]]\ntag = \"v1\"\nnew_sqlite_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 kvNamespaceId?: 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, kvNamespaceId } = 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, kvNamespaceId)],\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 const kvWarning = kvNamespaceId\n ? ''\n : ' - KV namespace id → set your actual KV namespace ID\\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 kvWarning\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, kvNamespaceId }: { appName: string; outDir: string; kvNamespaceId?: string }): void {\n generateWorker({ appName, outDir, kvNamespaceId })\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 { 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 LocalKit backend bundle')\n .option('-o, --out <dir>', 'Output directory', '.offlinekit/worker')\n .option('-n, --name <name>', 'App name for the worker', 'localkit-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('LocalKit 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 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, readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { randomBytes } from 'node:crypto'\nimport { resolve, dirname } from 'node:path'\nimport { cloudflareTarget } from '../targets/cloudflare.js'\nimport { log } from '../utils.js'\n\n// --- Types ---\n\nexport interface DeployState {\n kvNamespaceId: string\n r2BucketName: string\n workerName: string\n workerUrl?: string\n jwtSecretSet: boolean\n}\n\ninterface WranglerResult {\n stdout: string\n stderr: string\n status: number | null\n}\n\n// --- Helpers ---\n\nexport function runWrangler(\n args: string[],\n options?: { cwd?: string; input?: string },\n): WranglerResult {\n const result = spawnSync('wrangler', args, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n ...options,\n })\n return {\n stdout: result.stdout ?? '',\n stderr: result.stderr ?? '',\n status: result.status,\n }\n}\n\nfunction checkWrangler(): string | null {\n try {\n const result = execSync('wrangler --version', {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n return result.trim()\n } catch {\n return null\n }\n}\n\nexport function checkAuth(): boolean {\n const result = runWrangler(['whoami'])\n return result.status === 0\n}\n\nexport function createR2Bucket(name: string): void {\n log.info(`Creating R2 bucket: ${name}`)\n const result = runWrangler(['r2', 'bucket', 'create', name])\n if (result.status !== 0) {\n if (result.stderr.includes('already exists')) {\n log.info(`R2 bucket \"${name}\" already exists, continuing`)\n return\n }\n if (result.stderr.includes('R2') || result.stderr.includes('not allowed') || result.stderr.includes('not enabled')) {\n throw new Error(\n `Failed to create R2 bucket: ${result.stderr}\\n` +\n 'Hint: Enable R2 in your Cloudflare dashboard at https://dash.cloudflare.com → R2 Object Storage before deploying.',\n )\n }\n throw new Error(`Failed to create R2 bucket: ${result.stderr}`)\n }\n log.success(`R2 bucket \"${name}\" created`)\n}\n\nexport function kvNamespaceExists(id: string): boolean {\n const result = runWrangler(['kv', 'namespace', 'list', '--json'])\n if (result.status !== 0) return false\n try {\n const namespaces = JSON.parse(result.stdout) as Array<{ id: string }>\n return namespaces.some((ns) => ns.id === id)\n } catch {\n return false\n }\n}\n\nexport function createKvNamespace(name: string): string {\n log.info(`Creating KV namespace: ${name}`)\n const result = runWrangler(['kv', 'namespace', 'create', name])\n\n if (result.status === 0) {\n // Primary: parse ID from create stdout\n const match = result.stdout.match(/id\\s*=\\s*\"([a-f0-9]+)\"/)\n if (match) {\n log.success(`KV namespace \"${name}\" created with ID: ${match[1]}`)\n return match[1]\n }\n // Try JSON parsing if --json was supported\n try {\n const parsed = JSON.parse(result.stdout)\n if (parsed.id) return parsed.id\n } catch {\n // not JSON, continue\n }\n }\n\n // Fallback: namespace already exists, list and find by title\n if (result.stderr.includes('already exists') || result.status !== 0) {\n log.info(`KV namespace \"${name}\" may already exist, looking up ID...`)\n const listResult = runWrangler(['kv', 'namespace', 'list', '--json'])\n if (listResult.status === 0) {\n try {\n const namespaces = JSON.parse(listResult.stdout) as Array<{\n id: string\n title: string\n }>\n const found = namespaces.find((ns) => ns.title === name)\n if (found) {\n log.success(`Found existing KV namespace \"${name}\" with ID: ${found.id}`)\n return found.id\n }\n } catch {\n log.warn('Failed to parse KV namespace list JSON, falling back to regex')\n }\n // Last resort: regex on raw list output\n const idMatch = listResult.stdout.match(/id\\s*=\\s*\"([a-f0-9]+)\"/)\n if (idMatch) return idMatch[1]\n }\n }\n\n throw new Error(\n `Failed to create or find KV namespace \"${name}\". ` +\n `stdout: ${result.stdout}, stderr: ${result.stderr}`,\n )\n}\n\nexport function setSecret(name: string, value: string, cwd: string): void {\n log.info(`Setting secret: ${name}`)\n const result = runWrangler(['secret', 'put', name], { cwd, input: value })\n if (result.status !== 0) {\n throw new Error(`Failed to set secret ${name}: ${result.stderr}`)\n }\n log.success(`Secret \"${name}\" set`)\n}\n\nexport function readDeployState(outDir: string): DeployState | null {\n const statePath = resolve(outDir, '..', 'deploy-state.json')\n if (!existsSync(statePath)) return null\n try {\n return JSON.parse(readFileSync(statePath, 'utf8')) as DeployState\n } catch {\n return null\n }\n}\n\nexport function writeDeployState(outDir: string, state: DeployState): void {\n const statePath = resolve(outDir, '..', 'deploy-state.json')\n mkdirSync(dirname(statePath), { recursive: true })\n writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8')\n}\n\n// --- Composable Functions ---\n\nexport function provisionCloudflare(\n appName: string,\n _outDir: string,\n dryRun: boolean,\n): { kvNamespaceId: string; r2BucketName: string; workerName: string } {\n const name = appName.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n const r2BucketName = `${name}-storage`\n const kvTitle = `${name}-kv`\n const workerName = `${name}-worker`\n\n // Check auth\n if (dryRun) {\n log.dim('[dry-run] Would check wrangler auth (wrangler whoami)')\n log.dim(`[dry-run] Would create R2 bucket: ${r2BucketName}`)\n log.dim(`[dry-run] Would create KV namespace: ${kvTitle}`)\n log.dim('[dry-run] Would set JWT_SECRET')\n return { kvNamespaceId: 'DRY_RUN_PLACEHOLDER', r2BucketName, workerName }\n }\n\n if (!checkAuth()) {\n log.error('Not authenticated with Cloudflare.')\n log.dim('Run `wrangler login` to authenticate, then try again.')\n process.exit(1)\n }\n log.success('Authenticated with Cloudflare')\n\n // Create R2 bucket\n createR2Bucket(r2BucketName)\n\n // Create KV namespace\n const kvNamespaceId = createKvNamespace(kvTitle)\n\n return { kvNamespaceId, r2BucketName, workerName }\n}\n\nexport function generateAndDeploy(\n appName: string,\n outDir: string,\n kvNamespaceId: string,\n env: string | undefined,\n dryRun: boolean,\n): string | undefined {\n if (dryRun) {\n log.dim(`[dry-run] Would generate worker code to: ${outDir}`)\n log.dim(`[dry-run] Would run: npm install (in ${outDir})`)\n log.dim(`[dry-run] Would run: wrangler deploy${env ? ` --env ${env}` : ''}`)\n return undefined\n }\n\n // Generate worker code with real KV namespace ID\n log.info('Generating worker code...')\n cloudflareTarget.generate({ appName, outDir, kvNamespaceId })\n log.success('Worker code generated')\n\n // npm install\n log.info('Installing dependencies...')\n const installResult = spawnSync('npm', ['install'], {\n cwd: outDir,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n if (installResult.status !== 0) {\n throw new Error(`npm install failed: ${installResult.stderr}`)\n }\n log.success('Dependencies installed')\n\n // wrangler deploy\n log.info('Deploying to Cloudflare...')\n const deployArgs = ['deploy']\n if (env) deployArgs.push('--env', env)\n\n const deployResult = spawnSync('wrangler', deployArgs, {\n cwd: outDir,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n if (deployResult.status !== 0) {\n throw new Error(\n `wrangler deploy failed (exit ${deployResult.status}): ${deployResult.stderr}`,\n )\n }\n\n // Parse worker URL\n const urlMatch = deployResult.stdout.match(/https:\\/\\/[^\\s]+\\.workers\\.dev/)\n const workerUrl = urlMatch ? urlMatch[0] : undefined\n if (workerUrl) {\n log.success(`Deployed to: ${workerUrl}`)\n } else {\n log.success('Deployed successfully!')\n }\n\n return workerUrl\n}\n\n// --- Command Registration ---\n\nexport function registerDeploy(program: Command): void {\n program\n .command('deploy')\n .description('Deploy the LocalKit Worker to Cloudflare via Wrangler')\n .option('-d, --dir <dir>', 'Worker output directory', '.offlinekit/worker')\n .option('-n, --name <name>', 'App name for the worker', 'localkit-app')\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', 'Show what would happen without executing')\n .action(\n (opts: {\n dir: string\n name: string\n env?: string\n target: string\n dryRun?: boolean\n }) => {\n log.bold('LocalKit Deploy')\n\n const dryRun = opts.dryRun ?? false\n\n // Check if project has been ejected\n const serverDir = resolve(process.cwd(), 'server')\n if (existsSync(serverDir)) {\n log.info(\n 'Warning: This project has been ejected (./server/ exists).',\n )\n log.dim(\n 'You can deploy the ejected code directly with `cd server && wrangler deploy`.',\n )\n }\n\n // Validate target\n if (opts.target !== 'cloudflare') {\n log.error(\n `Deploy target \"${opts.target}\" is not supported yet. Only \"cloudflare\" is deployable.`,\n )\n log.dim(\n 'For Node.js targets, run `npx mpb-localkit build --target node` then deploy the output manually.',\n )\n process.exit(1)\n }\n\n // 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 const outDir = resolve(process.cwd(), opts.dir)\n const appName = opts.name\n\n // Read existing deploy state\n const existingState = readDeployState(outDir)\n\n let kvNamespaceId: string\n let r2BucketName: string\n let workerName: string\n if (existingState?.kvNamespaceId && existingState?.r2BucketName) {\n // Subsequent deploy -- validate resources still exist\n log.info('Found existing deploy state, validating resources...')\n r2BucketName = existingState.r2BucketName\n workerName = existingState.workerName\n\n if (dryRun) {\n log.dim('[dry-run] Would validate KV namespace exists')\n kvNamespaceId = existingState.kvNamespaceId\n } else if (kvNamespaceExists(existingState.kvNamespaceId)) {\n log.success('KV namespace validated')\n kvNamespaceId = existingState.kvNamespaceId\n } else {\n log.warn('KV namespace no longer exists, re-creating...')\n const name = appName.toLowerCase().replace(/[^a-z0-9-]/g, '-')\n kvNamespaceId = createKvNamespace(`${name}-kv`)\n writeDeployState(outDir, {\n ...existingState,\n kvNamespaceId,\n })\n log.success('Deploy state updated with new KV namespace ID')\n }\n } else {\n // First deploy -- provision resources\n const provisioned = provisionCloudflare(appName, outDir, dryRun)\n kvNamespaceId = provisioned.kvNamespaceId\n r2BucketName = provisioned.r2BucketName\n workerName = provisioned.workerName\n\n // Save provisioned state (jwtSecretSet: false until secret is actually set)\n if (!dryRun) {\n writeDeployState(outDir, {\n kvNamespaceId,\n r2BucketName,\n workerName,\n jwtSecretSet: false,\n })\n log.success('Deploy state saved to .offlinekit/deploy-state.json')\n }\n }\n\n // Pre-flight: ensure we never deploy with a placeholder KV namespace ID\n if (kvNamespaceId === 'REPLACE_WITH_KV_NAMESPACE_ID' || !kvNamespaceId) {\n log.error(\n 'KV namespace ID is missing or still a placeholder. ' +\n 'Run `mpb-localkit deploy` (not `wrangler deploy` directly) to auto-provision resources, ' +\n 'or create one manually: wrangler kv namespace create KV',\n )\n process.exit(1)\n }\n\n const alreadyHasSecret = existingState?.jwtSecretSet ?? false\n\n // Generate code and deploy (this creates wrangler.toml in outDir)\n let workerUrl: string | undefined\n try {\n workerUrl = generateAndDeploy(appName, outDir, kvNamespaceId, opts.env, dryRun)\n } catch (err) {\n log.error(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`)\n process.exit(1)\n }\n\n if (dryRun) {\n if (!alreadyHasSecret) {\n log.dim('[dry-run] Would set JWT_SECRET (first deploy only)')\n }\n log.bold('Dry run complete. No changes were made.')\n return\n }\n\n // Set JWT_SECRET now that wrangler.toml exists in outDir\n let jwtSecretSet = alreadyHasSecret\n if (!jwtSecretSet) {\n try {\n setSecret('JWT_SECRET', randomBytes(32).toString('hex'), outDir)\n log.success('JWT_SECRET set')\n jwtSecretSet = true\n } catch (err) {\n log.warn(`Could not set JWT_SECRET: ${err instanceof Error ? err.message : String(err)}`)\n log.dim('Set it manually: wrangler secret put JWT_SECRET')\n }\n }\n\n // Persist final state with workerUrl\n writeDeployState(outDir, { kvNamespaceId, r2BucketName, workerName, workerUrl, jwtSecretSet })\n\n // Optionally update .env\n if (workerUrl) {\n const envPath = resolve(process.cwd(), '.env')\n if (existsSync(envPath)) {\n let envContent = readFileSync(envPath, 'utf8')\n if (envContent.includes('VITE_SYNC_URL=')) {\n envContent = envContent.replace(/VITE_SYNC_URL=.*/, `VITE_SYNC_URL=${workerUrl}`)\n } else {\n envContent += `\\nVITE_SYNC_URL=${workerUrl}\\n`\n }\n writeFileSync(envPath, envContent, 'utf8')\n log.info(`Updated .env with VITE_SYNC_URL=${workerUrl}`)\n }\n }\n },\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', 'localkit-app')\n .option('-t, --target <target>', 'Deploy target (cloudflare, node)', 'cloudflare')\n .action((opts: { out: string; name: string; target: string }) => {\n log.bold('LocalKit 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 `npx mpb-localkit 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('mpb-localkit')\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"]}
|
package/dist/core/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { z } from 'zod';
|
|
2
|
+
import { openDB } from 'idb';
|
|
2
3
|
import { v7 } from 'uuid';
|
|
3
4
|
|
|
4
5
|
// src/core/schema/index.ts
|
|
@@ -12,6 +13,101 @@ function collection(schema, options) {
|
|
|
12
13
|
migrate: options?.migrate
|
|
13
14
|
};
|
|
14
15
|
}
|
|
16
|
+
var CHANGES_INDEX = "_updatedAt";
|
|
17
|
+
var IndexedDBAdapter = class {
|
|
18
|
+
dbName;
|
|
19
|
+
db = null;
|
|
20
|
+
_ensureLock = Promise.resolve();
|
|
21
|
+
constructor(appName) {
|
|
22
|
+
this.dbName = `offlinekit-${appName}`;
|
|
23
|
+
}
|
|
24
|
+
async getDb() {
|
|
25
|
+
if (this.db) return this.db;
|
|
26
|
+
this.db = await openDB(this.dbName, 1, {
|
|
27
|
+
upgrade(_db) {
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return this.db;
|
|
31
|
+
}
|
|
32
|
+
async ensureStore(collection2) {
|
|
33
|
+
const prev = this._ensureLock;
|
|
34
|
+
let release;
|
|
35
|
+
this._ensureLock = new Promise((r) => {
|
|
36
|
+
release = r;
|
|
37
|
+
});
|
|
38
|
+
await prev;
|
|
39
|
+
try {
|
|
40
|
+
let db = await this.getDb();
|
|
41
|
+
if (db.objectStoreNames.contains(collection2)) return db;
|
|
42
|
+
const version = db.version + 1;
|
|
43
|
+
db.close();
|
|
44
|
+
this.db = null;
|
|
45
|
+
db = await openDB(this.dbName, version, {
|
|
46
|
+
upgrade(db2) {
|
|
47
|
+
if (!db2.objectStoreNames.contains(collection2)) {
|
|
48
|
+
const store = db2.createObjectStore(collection2, { keyPath: "_id" });
|
|
49
|
+
store.createIndex(CHANGES_INDEX, "_updatedAt");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.db = db;
|
|
54
|
+
return db;
|
|
55
|
+
} finally {
|
|
56
|
+
release();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async get(collection2, id) {
|
|
60
|
+
const db = await this.ensureStore(collection2);
|
|
61
|
+
const doc = await db.get(collection2, id);
|
|
62
|
+
if (!doc || doc._deleted) return null;
|
|
63
|
+
return doc;
|
|
64
|
+
}
|
|
65
|
+
async getMany(collection2, filter) {
|
|
66
|
+
const db = await this.ensureStore(collection2);
|
|
67
|
+
const all = await db.getAll(collection2);
|
|
68
|
+
return all.filter((doc) => {
|
|
69
|
+
if (doc._deleted) return false;
|
|
70
|
+
if (!filter) return true;
|
|
71
|
+
return Object.entries(filter).every(([k, v]) => Reflect.get(doc, k) === v);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async put(collection2, id, doc) {
|
|
75
|
+
const db = await this.ensureStore(collection2);
|
|
76
|
+
await db.put(collection2, { ...doc, _id: id, _collection: collection2 });
|
|
77
|
+
}
|
|
78
|
+
async delete(collection2, id) {
|
|
79
|
+
const db = await this.ensureStore(collection2);
|
|
80
|
+
const existing = await db.get(collection2, id);
|
|
81
|
+
if (!existing) return;
|
|
82
|
+
await db.put(collection2, {
|
|
83
|
+
...existing,
|
|
84
|
+
_deleted: true,
|
|
85
|
+
_updatedAt: Date.now()
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async getChangesSince(timestamp) {
|
|
89
|
+
const db = await this.getDb();
|
|
90
|
+
const results = [];
|
|
91
|
+
for (const storeName of Array.from(db.objectStoreNames)) {
|
|
92
|
+
if (!db.objectStoreNames.contains(storeName)) continue;
|
|
93
|
+
const tx = db.transaction(storeName, "readonly");
|
|
94
|
+
const store = tx.objectStore(storeName);
|
|
95
|
+
const index = store.index(CHANGES_INDEX);
|
|
96
|
+
const range = IDBKeyRange.lowerBound(timestamp, true);
|
|
97
|
+
const docs = await index.getAll(range);
|
|
98
|
+
for (const doc of docs) {
|
|
99
|
+
results.push({
|
|
100
|
+
collection: storeName,
|
|
101
|
+
id: doc._id,
|
|
102
|
+
doc,
|
|
103
|
+
updatedAt: doc._updatedAt,
|
|
104
|
+
deleted: doc._deleted
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
15
111
|
|
|
16
112
|
// src/core/storage/memory.ts
|
|
17
113
|
var MemoryAdapter = class {
|
|
@@ -573,6 +669,7 @@ var AutoTransport = class {
|
|
|
573
669
|
url: config.wsUrl,
|
|
574
670
|
token: config.token,
|
|
575
671
|
reconnect: true,
|
|
672
|
+
maxReconnectAttempts: 5,
|
|
576
673
|
onRemoteChanges: config.onRemoteChanges,
|
|
577
674
|
onConnected: () => {
|
|
578
675
|
this.useWs = true;
|
|
@@ -762,12 +859,11 @@ var SyncEngine = class {
|
|
|
762
859
|
resolveTransport(config) {
|
|
763
860
|
const t = config.transport;
|
|
764
861
|
if (!t || t === "http") return new HttpTransport(config.endpoint);
|
|
862
|
+
const wsUrl = config.endpoint.replace(/^http/, "ws").replace(/\/sync\/?$/, "/ws");
|
|
765
863
|
if (t === "websocket") {
|
|
766
|
-
const wsUrl = config.endpoint.replace(/^http/, "ws");
|
|
767
864
|
return new WebSocketTransport({ url: wsUrl });
|
|
768
865
|
}
|
|
769
866
|
if (t === "auto") {
|
|
770
|
-
const wsUrl = config.endpoint.replace(/^http/, "ws");
|
|
771
867
|
return new AutoTransport({ wsUrl, httpEndpoint: config.endpoint });
|
|
772
868
|
}
|
|
773
869
|
return t;
|
|
@@ -1680,8 +1776,38 @@ function resolveAuth(config) {
|
|
|
1680
1776
|
}
|
|
1681
1777
|
return adapter;
|
|
1682
1778
|
}
|
|
1779
|
+
var LazyStorageAdapter = class {
|
|
1780
|
+
_inner = null;
|
|
1781
|
+
get inner() {
|
|
1782
|
+
if (!this._inner) {
|
|
1783
|
+
this._inner = typeof indexedDB !== "undefined" ? new IndexedDBAdapter("localkit") : new MemoryAdapter();
|
|
1784
|
+
}
|
|
1785
|
+
return this._inner;
|
|
1786
|
+
}
|
|
1787
|
+
get(collection2, id) {
|
|
1788
|
+
return this.inner.get(collection2, id);
|
|
1789
|
+
}
|
|
1790
|
+
getRaw(collection2, id) {
|
|
1791
|
+
return this.inner.getRaw?.(collection2, id) ?? this.inner.get(collection2, id);
|
|
1792
|
+
}
|
|
1793
|
+
getMany(collection2, filter) {
|
|
1794
|
+
return this.inner.getMany(collection2, filter);
|
|
1795
|
+
}
|
|
1796
|
+
put(collection2, id, doc) {
|
|
1797
|
+
return this.inner.put(collection2, id, doc);
|
|
1798
|
+
}
|
|
1799
|
+
delete(collection2, id) {
|
|
1800
|
+
return this.inner.delete(collection2, id);
|
|
1801
|
+
}
|
|
1802
|
+
getChangesSince(since) {
|
|
1803
|
+
return this.inner.getChangesSince(since);
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
function defaultStorage() {
|
|
1807
|
+
return new LazyStorageAdapter();
|
|
1808
|
+
}
|
|
1683
1809
|
function createApp(config) {
|
|
1684
|
-
const storage = config.storage ??
|
|
1810
|
+
const storage = config.storage ?? defaultStorage();
|
|
1685
1811
|
const RESERVED_KEYS = ["auth", "errors", "syncStore", "sync"];
|
|
1686
1812
|
for (const name of Object.keys(config.collections)) {
|
|
1687
1813
|
if (RESERVED_KEYS.includes(name)) {
|