@noy-db/in-rest 0.4.0-pre.4 → 0.4.0-pre.5
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/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -182,26 +182,26 @@ function buildRouter(store, user, sessions, basePath) {
|
|
|
182
182
|
return async function route(req) {
|
|
183
183
|
const path = stripBase(req.pathname);
|
|
184
184
|
const method = req.method.toUpperCase();
|
|
185
|
-
if (method === "POST" && path === "/sessions/unlock/
|
|
185
|
+
if (method === "POST" && path === "/sessions/unlock/secret") {
|
|
186
186
|
let body;
|
|
187
187
|
try {
|
|
188
188
|
body = await req.json();
|
|
189
189
|
} catch {
|
|
190
190
|
return json(400, { error: "invalid_json" });
|
|
191
191
|
}
|
|
192
|
-
const
|
|
193
|
-
if (typeof
|
|
194
|
-
return json(400, { error: "
|
|
192
|
+
const secret = body?.secret;
|
|
193
|
+
if (typeof secret !== "string" || !secret) {
|
|
194
|
+
return json(400, { error: "secret_required" });
|
|
195
195
|
}
|
|
196
196
|
try {
|
|
197
|
-
const db2 = await createNoydb({ store, user, secret
|
|
197
|
+
const db2 = await createNoydb({ store, user, secret });
|
|
198
198
|
const token2 = sessions.create(db2);
|
|
199
199
|
return json(200, { token: token2 });
|
|
200
200
|
} catch (err) {
|
|
201
201
|
if (err instanceof ValidationError) {
|
|
202
|
-
return json(400, { error: "
|
|
202
|
+
return json(400, { error: "weak_secret", message: err.message });
|
|
203
203
|
}
|
|
204
|
-
return json(401, { error: "
|
|
204
|
+
return json(401, { error: "invalid_secret" });
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
if (method === "GET" && path === "/sessions/current") {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sessions.ts","../src/router.ts","../src/query-params.ts","../src/index.ts"],"sourcesContent":["import type { Noydb } from '@noy-db/hub'\n\ninterface Session {\n db: Noydb\n expiresAt: number\n}\n\n/**\n * Options for `SessionStore`.\n *\n * `sweepIntervalMs` controls the periodic background sweep that drops\n * expired sessions from the map. Without it, expired\n * tokens only clear when a matching `get()` / `peek()` call happens —\n * a long-running process with many short-lived sessions accumulates\n * dead entries unbounded. Default is 5 minutes; pass `0` to disable\n * the sweep (appropriate for tests or very short-lived CLI processes).\n */\nexport interface SessionStoreOptions {\n sweepIntervalMs?: number\n}\n\nconst DEFAULT_SWEEP_INTERVAL_MS = 5 * 60 * 1000\n\nexport class SessionStore {\n private readonly sessions = new Map<string, Session>()\n private readonly ttlMs: number\n private readonly sweepTimer: ReturnType<typeof setInterval> | null\n\n constructor(ttlSeconds: number, options: SessionStoreOptions = {}) {\n this.ttlMs = ttlSeconds * 1000\n const sweepMs = options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS\n if (sweepMs > 0) {\n // Node's setInterval returns a Timeout whose `.unref()` prevents\n // the timer from keeping the event loop alive. That matters in\n // CLI / serverless contexts where the Node process should exit\n // cleanly once the request cycle ends.\n const timer = setInterval(() => this.sweep(), sweepMs)\n if (typeof (timer as { unref?: () => void }).unref === 'function') {\n (timer as { unref: () => void }).unref()\n }\n this.sweepTimer = timer\n } else {\n this.sweepTimer = null\n }\n }\n\n create(db: Noydb): string {\n const token = crypto.randomUUID()\n this.sessions.set(token, { db, expiresAt: Date.now() + this.ttlMs })\n return token\n }\n\n /**\n * Look up a session and refresh its sliding-window TTL. Call from\n * auth-guarded routes that are treated as \"activity\".\n */\n get(token: string): Noydb | null {\n const session = this.sessions.get(token)\n if (!session) return null\n if (Date.now() > session.expiresAt) {\n this.sessions.delete(token)\n return null\n }\n session.expiresAt = Date.now() + this.ttlMs\n return session.db\n }\n\n delete(token: string): void {\n this.sessions.delete(token)\n }\n\n /**\n * Non-refreshing existence check. Used by polling endpoints like\n * `GET /sessions/current` that should not extend the session merely\n * by being queried.\n */\n peek(token: string): boolean {\n const session = this.sessions.get(token)\n if (!session) return false\n if (Date.now() > session.expiresAt) {\n this.sessions.delete(token)\n return false\n }\n return true\n }\n\n /**\n * Drop every session whose TTL has elapsed. Called automatically by\n * the internal interval timer; exposed so tests can deterministically\n * trigger the sweep without waiting. Returns the count of entries\n * removed — useful for logging / metrics hooks.\n */\n sweep(): number {\n const now = Date.now()\n let removed = 0\n for (const [token, session] of this.sessions) {\n if (now > session.expiresAt) {\n this.sessions.delete(token)\n removed++\n }\n }\n return removed\n }\n\n /**\n * Current number of tracked sessions (expired or not). Primarily for\n * tests that verify the sweep is working; operators wanting live\n * metrics should wrap the store or emit their own counters.\n */\n size(): number {\n return this.sessions.size\n }\n\n /**\n * Stop the background sweep timer and drop every session. Call this\n * on shutdown to allow the event loop to exit cleanly — `unref()`\n * handles most cases, but explicit cleanup is cheaper than relying\n * on finalization.\n */\n close(): void {\n if (this.sweepTimer !== null) clearInterval(this.sweepTimer)\n this.sessions.clear()\n }\n}\n","import { createNoydb, PermissionDeniedError, NotFoundError, ConflictError, ValidationError } from '@noy-db/hub'\nimport type { NoydbStore } from '@noy-db/hub'\nimport type { RestRequest, RestResponse } from './index.js'\nimport type { SessionStore } from './sessions.js'\nimport { parseQueryParams } from './query-params.js'\n\nfunction json(status: number, body: unknown): RestResponse {\n return {\n status,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }\n}\n\nfunction extractToken(req: RestRequest): string | null {\n // HTTP headers are case-insensitive; check both common casings.\n const auth = req.headers['authorization'] ?? req.headers['Authorization']\n if (!auth?.startsWith('Bearer ')) return null\n return auth.slice(7)\n}\n\nexport function buildRouter(store: NoydbStore, user: string, sessions: SessionStore, basePath: string) {\n function stripBase(pathname: string): string {\n // Segment-aware prefix match: basePath '/api' matches '/api' or '/api/...'\n // but NOT '/apifoo' or '/api-other/...'.\n if (!basePath) return pathname\n if (pathname === basePath) return '/'\n if (pathname.startsWith(basePath + '/')) return pathname.slice(basePath.length)\n return pathname\n }\n\n return async function route(req: RestRequest): Promise<RestResponse> {\n const path = stripBase(req.pathname)\n const method = req.method.toUpperCase()\n\n // ── Session routes (no auth required) ─────────────────────────\n\n if (method === 'POST' && path === '/sessions/unlock/passphrase') {\n let body: unknown\n try { body = await req.json() } catch { return json(400, { error: 'invalid_json' }) }\n const passphrase = (body as Record<string, unknown>)?.passphrase\n if (typeof passphrase !== 'string' || !passphrase) {\n return json(400, { error: 'passphrase_required' })\n }\n try {\n const db = await createNoydb({ store, user, secret: passphrase })\n const token = sessions.create(db)\n return json(200, { token })\n } catch (err) {\n if (err instanceof ValidationError) {\n return json(400, { error: 'weak_passphrase', message: err.message })\n }\n return json(401, { error: 'invalid_passphrase' })\n }\n }\n\n if (method === 'GET' && path === '/sessions/current') {\n const token = extractToken(req)\n const active = token !== null && sessions.peek(token)\n return json(200, { active })\n }\n\n if (method === 'DELETE' && path === '/sessions/current') {\n const token = extractToken(req)\n if (!token || !sessions.peek(token)) return json(401, { error: 'unauthorized' })\n sessions.delete(token)\n return { status: 204, headers: {}, body: null }\n }\n\n // ── Auth guard ────────────────────────────────────────────────\n\n const token = extractToken(req)\n const db = token ? sessions.get(token) : null\n if (!db) return json(401, { error: 'unauthorized' })\n\n // ── Vault routes ──────────────────────────────────────────────\n\n if (method === 'GET' && path === '/vaults') {\n try {\n if (typeof store.listVaults === 'function') {\n const vaults = await db.listAccessibleVaults()\n return json(200, vaults.map(v => v.id))\n }\n return json(200, [])\n } catch {\n return json(200, [])\n }\n }\n\n // Match /vaults/:vault/collections/:collection/:id\n const recordMatch = path.match(/^\\/vaults\\/([^/]+)\\/collections\\/([^/]+)\\/([^/]+)$/)\n if (recordMatch) {\n const vaultName = recordMatch[1]\n const collName = recordMatch[2]\n const id = recordMatch[3]\n if (vaultName === undefined || collName === undefined || id === undefined) {\n return json(500, { error: 'internal_error' })\n }\n try {\n const vault = await db.openVault(vaultName)\n const coll = vault.collection<Record<string, unknown>>(collName)\n\n if (method === 'GET') {\n const record = await coll.get(id)\n if (!record) return json(404, { error: 'not_found' })\n return json(200, record)\n }\n\n if (method === 'POST') {\n let body: unknown\n try { body = await req.json() } catch { return json(400, { error: 'invalid_json' }) }\n await coll.put(id, body as Record<string, unknown>)\n return json(200, { ok: true })\n }\n\n if (method === 'DELETE') {\n await coll.delete(id)\n return json(200, { ok: true })\n }\n\n return {\n status: 405,\n headers: { 'content-type': 'application/json', allow: 'GET, POST, DELETE' },\n body: JSON.stringify({ error: 'method_not_allowed' }),\n }\n } catch (err) {\n if (err instanceof PermissionDeniedError) return json(403, { error: 'forbidden' })\n if (err instanceof NotFoundError) return json(404, { error: 'not_found' })\n if (err instanceof ConflictError) return json(409, { error: 'conflict' })\n return json(500, { error: 'internal_error' })\n }\n }\n\n // Match /vaults/:vault/collections/:collection (list)\n const collMatch = path.match(/^\\/vaults\\/([^/]+)\\/collections\\/([^/]+)$/)\n if (collMatch) {\n if (method !== 'GET') {\n return {\n status: 405,\n headers: { 'content-type': 'application/json', allow: 'GET' },\n body: JSON.stringify({ error: 'method_not_allowed' }),\n }\n }\n const vaultName = collMatch[1]\n const collName = collMatch[2]\n if (vaultName === undefined || collName === undefined) {\n return json(500, { error: 'internal_error' })\n }\n const params = parseQueryParams(req.searchParams)\n if (params.error) return json(400, params.error)\n try {\n const vault = await db.openVault(vaultName)\n const coll = vault.collection<Record<string, unknown>>(collName)\n let results = params.apply(coll.query()).toArray()\n if (params.limit !== null) results = results.slice(0, params.limit)\n return json(200, results)\n } catch (err) {\n if (err instanceof PermissionDeniedError) return json(403, { error: 'forbidden' })\n return json(500, { error: 'internal_error' })\n }\n }\n\n return json(404, { error: 'not_found' })\n }\n}\n","import type { Query } from '@noy-db/hub'\n\ntype QueryOp = '==' | '!=' | '>' | '>=' | '<' | '<='\n\nconst OP_MAP: Record<string, QueryOp> = {\n eq: '==',\n neq: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n}\n\nexport interface ParsedQueryParams {\n error?: { error: string; op?: string }\n apply<T>(q: Query<T>): Query<T>\n limit: number | null\n}\n\nexport function parseQueryParams(searchParams: URLSearchParams): ParsedQueryParams {\n const wheres = searchParams.getAll('where')\n const orderByParam = searchParams.get('orderBy')\n const limitParam = searchParams.get('limit')\n\n const whereClauses: Array<{ field: string; op: QueryOp; value: unknown }> = []\n\n for (const clause of wheres) {\n const parts = clause.split(':')\n if (parts.length < 3) {\n return {\n error: { error: 'invalid_where', op: clause },\n apply: (q) => q,\n limit: null,\n }\n }\n const field = parts[0] as string\n const opStr = parts[1] as string\n const value = parts.slice(2).join(':')\n const op = OP_MAP[opStr]\n if (!op) {\n return {\n error: { error: 'invalid_op', op: opStr },\n apply: (q) => q,\n limit: null,\n }\n }\n whereClauses.push({ field, op, value: coerce(value) })\n }\n\n let orderBy: { field: string; dir: 'asc' | 'desc' } | null = null\n if (orderByParam) {\n const obParts = orderByParam.split(':')\n const obField = obParts[0] as string\n const obDir = obParts[1]\n orderBy = { field: obField, dir: obDir === 'desc' ? 'desc' : 'asc' }\n }\n\n const limit = limitParam ? parseInt(limitParam, 10) : null\n\n return {\n apply<T>(q: Query<T>): Query<T> {\n let result = q\n for (const { field, op, value } of whereClauses) {\n result = result.where(field, op, value as T[keyof T & string])\n }\n if (orderBy) {\n result = result.orderBy(orderBy.field, orderBy.dir)\n }\n return result\n },\n limit,\n }\n}\n\nfunction coerce(raw: string): unknown {\n if (raw === 'true') return true\n if (raw === 'false') return false\n const n = Number(raw)\n if (!isNaN(n) && raw.trim() !== '') return n\n return raw\n}\n","/**\n * **@noy-db/in-rest** — Framework-neutral REST API integration for noy-db.\n *\n * @example\n * ```ts\n * import { createRestHandler } from '@noy-db/in-rest'\n * import { honoAdapter } from '@noy-db/in-rest/hono'\n *\n * const handler = createRestHandler({ store, user: 'api' })\n * app.route('/api/noydb', honoAdapter(handler))\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore } from '@noy-db/hub'\nimport { SessionStore } from './sessions.js'\nimport { buildRouter } from './router.js'\n\nexport interface RestRequest {\n readonly method: string\n readonly pathname: string\n readonly searchParams: URLSearchParams\n readonly headers: Record<string, string>\n json(): Promise<unknown>\n}\n\nexport interface RestResponse {\n readonly status: number\n readonly headers: Record<string, string>\n readonly body: string | Uint8Array | null\n}\n\nexport interface NoydbRestHandler {\n handle(req: RestRequest): Promise<RestResponse>\n}\n\nexport interface RestHandlerOptions {\n readonly store: NoydbStore\n readonly user: string\n readonly ttlSeconds?: number\n readonly basePath?: string\n}\n\nexport function createRestHandler(options: RestHandlerOptions): NoydbRestHandler {\n const sessions = new SessionStore(options.ttlSeconds ?? 900)\n const route = buildRouter(options.store, options.user, sessions, options.basePath ?? '')\n return { handle: route }\n}\n"],"mappings":"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","names":["db","token"]}
|
|
1
|
+
{"version":3,"sources":["../src/sessions.ts","../src/router.ts","../src/query-params.ts","../src/index.ts"],"sourcesContent":["import type { Noydb } from '@noy-db/hub'\n\ninterface Session {\n db: Noydb\n expiresAt: number\n}\n\n/**\n * Options for `SessionStore`.\n *\n * `sweepIntervalMs` controls the periodic background sweep that drops\n * expired sessions from the map. Without it, expired\n * tokens only clear when a matching `get()` / `peek()` call happens —\n * a long-running process with many short-lived sessions accumulates\n * dead entries unbounded. Default is 5 minutes; pass `0` to disable\n * the sweep (appropriate for tests or very short-lived CLI processes).\n */\nexport interface SessionStoreOptions {\n sweepIntervalMs?: number\n}\n\nconst DEFAULT_SWEEP_INTERVAL_MS = 5 * 60 * 1000\n\nexport class SessionStore {\n private readonly sessions = new Map<string, Session>()\n private readonly ttlMs: number\n private readonly sweepTimer: ReturnType<typeof setInterval> | null\n\n constructor(ttlSeconds: number, options: SessionStoreOptions = {}) {\n this.ttlMs = ttlSeconds * 1000\n const sweepMs = options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS\n if (sweepMs > 0) {\n // Node's setInterval returns a Timeout whose `.unref()` prevents\n // the timer from keeping the event loop alive. That matters in\n // CLI / serverless contexts where the Node process should exit\n // cleanly once the request cycle ends.\n const timer = setInterval(() => this.sweep(), sweepMs)\n if (typeof (timer as { unref?: () => void }).unref === 'function') {\n (timer as { unref: () => void }).unref()\n }\n this.sweepTimer = timer\n } else {\n this.sweepTimer = null\n }\n }\n\n create(db: Noydb): string {\n const token = crypto.randomUUID()\n this.sessions.set(token, { db, expiresAt: Date.now() + this.ttlMs })\n return token\n }\n\n /**\n * Look up a session and refresh its sliding-window TTL. Call from\n * auth-guarded routes that are treated as \"activity\".\n */\n get(token: string): Noydb | null {\n const session = this.sessions.get(token)\n if (!session) return null\n if (Date.now() > session.expiresAt) {\n this.sessions.delete(token)\n return null\n }\n session.expiresAt = Date.now() + this.ttlMs\n return session.db\n }\n\n delete(token: string): void {\n this.sessions.delete(token)\n }\n\n /**\n * Non-refreshing existence check. Used by polling endpoints like\n * `GET /sessions/current` that should not extend the session merely\n * by being queried.\n */\n peek(token: string): boolean {\n const session = this.sessions.get(token)\n if (!session) return false\n if (Date.now() > session.expiresAt) {\n this.sessions.delete(token)\n return false\n }\n return true\n }\n\n /**\n * Drop every session whose TTL has elapsed. Called automatically by\n * the internal interval timer; exposed so tests can deterministically\n * trigger the sweep without waiting. Returns the count of entries\n * removed — useful for logging / metrics hooks.\n */\n sweep(): number {\n const now = Date.now()\n let removed = 0\n for (const [token, session] of this.sessions) {\n if (now > session.expiresAt) {\n this.sessions.delete(token)\n removed++\n }\n }\n return removed\n }\n\n /**\n * Current number of tracked sessions (expired or not). Primarily for\n * tests that verify the sweep is working; operators wanting live\n * metrics should wrap the store or emit their own counters.\n */\n size(): number {\n return this.sessions.size\n }\n\n /**\n * Stop the background sweep timer and drop every session. Call this\n * on shutdown to allow the event loop to exit cleanly — `unref()`\n * handles most cases, but explicit cleanup is cheaper than relying\n * on finalization.\n */\n close(): void {\n if (this.sweepTimer !== null) clearInterval(this.sweepTimer)\n this.sessions.clear()\n }\n}\n","import { createNoydb, PermissionDeniedError, NotFoundError, ConflictError, ValidationError } from '@noy-db/hub'\nimport type { NoydbStore } from '@noy-db/hub'\nimport type { RestRequest, RestResponse } from './index.js'\nimport type { SessionStore } from './sessions.js'\nimport { parseQueryParams } from './query-params.js'\n\nfunction json(status: number, body: unknown): RestResponse {\n return {\n status,\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }\n}\n\nfunction extractToken(req: RestRequest): string | null {\n // HTTP headers are case-insensitive; check both common casings.\n const auth = req.headers['authorization'] ?? req.headers['Authorization']\n if (!auth?.startsWith('Bearer ')) return null\n return auth.slice(7)\n}\n\nexport function buildRouter(store: NoydbStore, user: string, sessions: SessionStore, basePath: string) {\n function stripBase(pathname: string): string {\n // Segment-aware prefix match: basePath '/api' matches '/api' or '/api/...'\n // but NOT '/apifoo' or '/api-other/...'.\n if (!basePath) return pathname\n if (pathname === basePath) return '/'\n if (pathname.startsWith(basePath + '/')) return pathname.slice(basePath.length)\n return pathname\n }\n\n return async function route(req: RestRequest): Promise<RestResponse> {\n const path = stripBase(req.pathname)\n const method = req.method.toUpperCase()\n\n // ── Session routes (no auth required) ─────────────────────────\n\n if (method === 'POST' && path === '/sessions/unlock/secret') {\n let body: unknown\n try { body = await req.json() } catch { return json(400, { error: 'invalid_json' }) }\n const secret = (body as Record<string, unknown>)?.secret\n if (typeof secret !== 'string' || !secret) {\n return json(400, { error: 'secret_required' })\n }\n try {\n const db = await createNoydb({ store, user, secret: secret })\n const token = sessions.create(db)\n return json(200, { token })\n } catch (err) {\n if (err instanceof ValidationError) {\n return json(400, { error: 'weak_secret', message: err.message })\n }\n return json(401, { error: 'invalid_secret' })\n }\n }\n\n if (method === 'GET' && path === '/sessions/current') {\n const token = extractToken(req)\n const active = token !== null && sessions.peek(token)\n return json(200, { active })\n }\n\n if (method === 'DELETE' && path === '/sessions/current') {\n const token = extractToken(req)\n if (!token || !sessions.peek(token)) return json(401, { error: 'unauthorized' })\n sessions.delete(token)\n return { status: 204, headers: {}, body: null }\n }\n\n // ── Auth guard ────────────────────────────────────────────────\n\n const token = extractToken(req)\n const db = token ? sessions.get(token) : null\n if (!db) return json(401, { error: 'unauthorized' })\n\n // ── Vault routes ──────────────────────────────────────────────\n\n if (method === 'GET' && path === '/vaults') {\n try {\n if (typeof store.listVaults === 'function') {\n const vaults = await db.listAccessibleVaults()\n return json(200, vaults.map(v => v.id))\n }\n return json(200, [])\n } catch {\n return json(200, [])\n }\n }\n\n // Match /vaults/:vault/collections/:collection/:id\n const recordMatch = path.match(/^\\/vaults\\/([^/]+)\\/collections\\/([^/]+)\\/([^/]+)$/)\n if (recordMatch) {\n const vaultName = recordMatch[1]\n const collName = recordMatch[2]\n const id = recordMatch[3]\n if (vaultName === undefined || collName === undefined || id === undefined) {\n return json(500, { error: 'internal_error' })\n }\n try {\n const vault = await db.openVault(vaultName)\n const coll = vault.collection<Record<string, unknown>>(collName)\n\n if (method === 'GET') {\n const record = await coll.get(id)\n if (!record) return json(404, { error: 'not_found' })\n return json(200, record)\n }\n\n if (method === 'POST') {\n let body: unknown\n try { body = await req.json() } catch { return json(400, { error: 'invalid_json' }) }\n await coll.put(id, body as Record<string, unknown>)\n return json(200, { ok: true })\n }\n\n if (method === 'DELETE') {\n await coll.delete(id)\n return json(200, { ok: true })\n }\n\n return {\n status: 405,\n headers: { 'content-type': 'application/json', allow: 'GET, POST, DELETE' },\n body: JSON.stringify({ error: 'method_not_allowed' }),\n }\n } catch (err) {\n if (err instanceof PermissionDeniedError) return json(403, { error: 'forbidden' })\n if (err instanceof NotFoundError) return json(404, { error: 'not_found' })\n if (err instanceof ConflictError) return json(409, { error: 'conflict' })\n return json(500, { error: 'internal_error' })\n }\n }\n\n // Match /vaults/:vault/collections/:collection (list)\n const collMatch = path.match(/^\\/vaults\\/([^/]+)\\/collections\\/([^/]+)$/)\n if (collMatch) {\n if (method !== 'GET') {\n return {\n status: 405,\n headers: { 'content-type': 'application/json', allow: 'GET' },\n body: JSON.stringify({ error: 'method_not_allowed' }),\n }\n }\n const vaultName = collMatch[1]\n const collName = collMatch[2]\n if (vaultName === undefined || collName === undefined) {\n return json(500, { error: 'internal_error' })\n }\n const params = parseQueryParams(req.searchParams)\n if (params.error) return json(400, params.error)\n try {\n const vault = await db.openVault(vaultName)\n const coll = vault.collection<Record<string, unknown>>(collName)\n let results = params.apply(coll.query()).toArray()\n if (params.limit !== null) results = results.slice(0, params.limit)\n return json(200, results)\n } catch (err) {\n if (err instanceof PermissionDeniedError) return json(403, { error: 'forbidden' })\n return json(500, { error: 'internal_error' })\n }\n }\n\n return json(404, { error: 'not_found' })\n }\n}\n","import type { Query } from '@noy-db/hub'\n\ntype QueryOp = '==' | '!=' | '>' | '>=' | '<' | '<='\n\nconst OP_MAP: Record<string, QueryOp> = {\n eq: '==',\n neq: '!=',\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<=',\n}\n\nexport interface ParsedQueryParams {\n error?: { error: string; op?: string }\n apply<T>(q: Query<T>): Query<T>\n limit: number | null\n}\n\nexport function parseQueryParams(searchParams: URLSearchParams): ParsedQueryParams {\n const wheres = searchParams.getAll('where')\n const orderByParam = searchParams.get('orderBy')\n const limitParam = searchParams.get('limit')\n\n const whereClauses: Array<{ field: string; op: QueryOp; value: unknown }> = []\n\n for (const clause of wheres) {\n const parts = clause.split(':')\n if (parts.length < 3) {\n return {\n error: { error: 'invalid_where', op: clause },\n apply: (q) => q,\n limit: null,\n }\n }\n const field = parts[0] as string\n const opStr = parts[1] as string\n const value = parts.slice(2).join(':')\n const op = OP_MAP[opStr]\n if (!op) {\n return {\n error: { error: 'invalid_op', op: opStr },\n apply: (q) => q,\n limit: null,\n }\n }\n whereClauses.push({ field, op, value: coerce(value) })\n }\n\n let orderBy: { field: string; dir: 'asc' | 'desc' } | null = null\n if (orderByParam) {\n const obParts = orderByParam.split(':')\n const obField = obParts[0] as string\n const obDir = obParts[1]\n orderBy = { field: obField, dir: obDir === 'desc' ? 'desc' : 'asc' }\n }\n\n const limit = limitParam ? parseInt(limitParam, 10) : null\n\n return {\n apply<T>(q: Query<T>): Query<T> {\n let result = q\n for (const { field, op, value } of whereClauses) {\n result = result.where(field, op, value as T[keyof T & string])\n }\n if (orderBy) {\n result = result.orderBy(orderBy.field, orderBy.dir)\n }\n return result\n },\n limit,\n }\n}\n\nfunction coerce(raw: string): unknown {\n if (raw === 'true') return true\n if (raw === 'false') return false\n const n = Number(raw)\n if (!isNaN(n) && raw.trim() !== '') return n\n return raw\n}\n","/**\n * **@noy-db/in-rest** — Framework-neutral REST API integration for noy-db.\n *\n * @example\n * ```ts\n * import { createRestHandler } from '@noy-db/in-rest'\n * import { honoAdapter } from '@noy-db/in-rest/hono'\n *\n * const handler = createRestHandler({ store, user: 'api' })\n * app.route('/api/noydb', honoAdapter(handler))\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NoydbStore } from '@noy-db/hub'\nimport { SessionStore } from './sessions.js'\nimport { buildRouter } from './router.js'\n\nexport interface RestRequest {\n readonly method: string\n readonly pathname: string\n readonly searchParams: URLSearchParams\n readonly headers: Record<string, string>\n json(): Promise<unknown>\n}\n\nexport interface RestResponse {\n readonly status: number\n readonly headers: Record<string, string>\n readonly body: string | Uint8Array | null\n}\n\nexport interface NoydbRestHandler {\n handle(req: RestRequest): Promise<RestResponse>\n}\n\nexport interface RestHandlerOptions {\n readonly store: NoydbStore\n readonly user: string\n readonly ttlSeconds?: number\n readonly basePath?: string\n}\n\nexport function createRestHandler(options: RestHandlerOptions): NoydbRestHandler {\n const sessions = new SessionStore(options.ttlSeconds ?? 900)\n const route = buildRouter(options.store, options.user, sessions, options.basePath ?? '')\n return { handle: route }\n}\n"],"mappings":"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","names":["db","token"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noy-db/in-rest",
|
|
3
|
-
"version": "0.4.0-pre.
|
|
3
|
+
"version": "0.4.0-pre.5",
|
|
4
4
|
"description": "Framework-neutral REST API integration for noy-db — createRestHandler with Hono, Express, Fastify, and Nitro subpath adapters.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vLannaAi <vicio@lanna.ai>",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=22.0.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@noy-db/hub": "0.4.0-pre.
|
|
51
|
+
"@noy-db/hub": "0.4.0-pre.5"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
|
54
54
|
"hono": {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"fastify": "^5.0.0",
|
|
71
71
|
"h3": "^1.13.0",
|
|
72
72
|
"hono": "^4.0.0",
|
|
73
|
-
"@noy-db/hub": "0.4.0-pre.
|
|
73
|
+
"@noy-db/hub": "0.4.0-pre.5"
|
|
74
74
|
},
|
|
75
75
|
"keywords": [
|
|
76
76
|
"noy-db",
|