UNPKG

bknd

Version:

Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.

46 lines (37 loc) 12 kB
import{genericSqlite as $}from"bknd";function b(t){let r=t.binding;return $("d1-sqlite",r,e=>{let o=(s,i)=>r.prepare(s).bind(...i||[]),n=s=>{if(s.error)throw new Error(s.error);let i=s.meta.changes>0?e.parseBigInt(s.meta.changes):void 0;return{insertId:s.meta.last_row_id?e.parseBigInt(s.meta.last_row_id):void 0,numAffectedRows:i,rows:s.results,meta:s.meta}};return{db:r,batch:async s=>(await r.batch(s.map(({sql:a,parameters:d})=>o(a,d)))).map(n),query:e.buildQueryFn({all:async(s,i)=>{let a=o(s,i);return n(await a.all()).rows},run:async(s,i)=>{let a=o(s,i);return n(await a.run())}}),close:()=>{}}},{supports:{batching:!0,softscans:!1},excludeTables:["_cf_KV","_cf_METADATA"]})}import{Hono as J}from"hono";import{serveStatic as G}from"hono/cloudflare-workers";import{$console as Q}from"bknd/utils";import{createRuntimeApp as L}from"bknd/adapter";import{Connection as M}from"bknd";import{sqlite as V}from"bknd/adapter/sqlite";import{makeConfig as z}from"bknd/adapter";import{registries as N,isDebug as P,guessMimeType as S}from"bknd";import{inspect as F}from"node:util";function w(t,r){let e=[];for(let o in t)try{(t[o].constructor.name===r||String(t[o])===`[object ${r}]`||F(t[o]).includes(r))&&e.push({key:o,value:t[o]})}catch{}return e}function x(t,r){let e=w(t,r);if(e.length===0)throw new Error(`No ${r} found in bindings`);return e[0]}import{s as E}from"bknd/utils";import{StorageAdapter as I}from"bknd";function q(t=[]){return E.object({binding:t.length>0?E.string({enum:t}):E.string().optional()},{title:"R2",description:"Cloudflare R2 storage"})}function _(t,r=N){let e=w(t,"R2Bucket");r.media.register("r2",class extends v{constructor(n){let s=e.find(i=>i.key===n.binding);if(!s)throw new Error(`No R2Bucket found with key ${n.binding}`);super(s?.value);this.config=n}getSchema(){return q(e.map(n=>n.key))}toJSON(){return{...super.toJSON(),config:this.config}}})}var v=class extends I{constructor(e){super();this.bucket=e}keyPrefix="";getName(){return"r2"}getSchema(){return q()}async putObject(e,o){try{return(await this.bucket.put(this.getKey(e),o))?.etag}catch{return}}async listObjects(e=""){return(await this.bucket.list({limit:50,prefix:this.getKey(e)})).objects.map(n=>({key:n.key.replace(this.getKey(""),""),size:n.size,last_modified:n.uploaded}))}async headObject(e){return await this.bucket.head(this.getKey(e))}async objectExists(e){return await this.headObject(e)!==null}async getObject(e,o){let n,s=this.getKey(e),i=new Headers({"Accept-Ranges":"bytes","Content-Type":S(s)}),a=o.has("range");if(a){let d=P()?{}:{range:o,onlyIf:o};if(n=await this.bucket.get(s,d),!n)return new Response(null,{status:404});if(n.range){let f="offset"in n.range?n.range.offset:0,l="end"in n.range?n.range.end:n.size-1;i.set("Content-Range",`bytes ${f}-${l}/${n.size}`),i.set("Connection","keep-alive"),i.set("Vary","Accept-Encoding")}}else if(n=await this.bucket.get(s),n===null)return new Response(null,{status:404});return this.writeHttpMetadata(i,n),i.set("etag",n.httpEtag),i.set("Content-Length",String(n.size)),i.set("Last-Modified",n.uploaded.toUTCString()),new Response(n.body,{status:a?206:200,headers:i})}writeHttpMetadata(e,o){let n=o.httpMetadata;(!n||Object.keys(n).length===0)&&(n={contentType:S(o.key)});for(let[s,i]of Object.entries(n)){let a=s.replace(/([A-Z])/g,"-$1").toLowerCase();e.set(a,i)}}async getObjectMeta(e){let o=await this.headObject(e);if(!o)throw new Error("Object not found");return{type:String(o.httpMetadata?.contentType??S(e)),size:o.size}}async deleteObject(e){await this.bucket.delete(this.getKey(e))}getObjectUrl(e){throw new Error("Method getObjectUrl not implemented.")}getKey(e){return this.keyPrefix.length>0?`${this.keyPrefix}/${e}`.replace(/^\/\//,"/"):e}toJSON(e){return{type:this.getName(),config:{}}}};import{App as K}from"bknd";import{$console as j}from"bknd/utils";import{setCookie as U}from"hono/cookie";var D={exec_async_event_id:"cf_register_waituntil",cache_endpoint:"/__bknd/cache",do_endpoint:"/__bknd/do",d1_session:{cookie:"cf_d1_session",header:"x-cf-d1-session"}};function H(t,r){if(!t)return null;for(let e of t.split("; ")){let[o,n]=e.split("=");if(o===r&&n)return decodeURIComponent(n)}return null}function W(t){let r=D.d1_session.header,e=D.d1_session.cookie,o=t.d1?.transport;return{get:n=>{if(!(!n||!t.d1?.session)){if(!o||o==="cookie"){let s=n.headers.get("Cookie");if(s){let i=H(s,e);if(i)return i}}if((!o||o==="header")&&n.headers.has(r))return n.headers.get(r)}},set:(n,s)=>{if(!s||!t.d1?.session)return;let i=s.getBookmark();i&&((!o||o==="header")&&n.header(r,i),(!o||o==="cookie")&&U(n,e,i,{httpOnly:!0,secure:!0,sameSite:"Lax",maxAge:60*5}))}}}var A=!1;async function B(t,r){!A&&t.registerMedia!==!1&&(typeof t.registerMedia=="function"?t.registerMedia(r?.env):_(r?.env),A=!0);let e=await z(t,r?.env);if(M.isConnection(e.connection)){if(t.d1?.session)throw new Error("D1 session don't work when D1 is directly given as connection. Define it in bindings instead.")}else if(e.connection)e.connection=V(e.connection);else if(r?.env){let o=await t.bindings?.(r?.env),n=W(t),s=n.get(r.request),i,a;if(o?.db)j.debug("Using database from bindings"),a=o.db;else{let d=x(r.env,"D1Database");d&&(j.debug(`Using database from env "${d.key}"`),a=d.value)}if(a)if(t.d1?.session){if(i=a.withSession(s??t.d1?.first),!i)throw new Error("Couldn't create session");e.connection=b({binding:i}),e.options={...e.options,manager:{...e.options?.manager,onServerInit:d=>{d.use(async(f,l)=>{n.set(f,i),await l()})}}}}else e.connection=b({binding:a})}if(!M.isConnection(e.connection))throw new Error("Couldn't find database connection");return e}function O(t,r){t.emgr.onEvent(K.Events.AppBeforeResponse,async e=>{r.waitUntil(e.params.app.emgr.executeAsyncs())},{mode:"sync",id:D.exec_async_event_id})}async function T(t={},r={}){let e=await B(t,r);return await L({...e,onBuilt:async o=>{r.ctx&&O(o,r?.ctx),await e.onBuilt?.(o)}},r?.env)}var Z=T,R;function X(t={},r){return{async fetch(e,o,n){let s=new URL(e.url);if(t.manifest&&t.static==="assets")Q.warn("manifest is not useful with static 'assets'");else if(!t.manifest&&t.static==="kv")throw new Error("manifest is required with static 'kv'");if(t.manifest&&t.static==="kv"){let a=s.pathname.slice(1),d=JSON.parse(t.manifest);if(a&&a in d){let f=new J;return f.all("*",async(l,h)=>{let g=await G({path:`./${a}`,manifest:t.manifest})(l,h);return g instanceof Response?(g.headers.set("Cache-Control","public, max-age=31536000"),g):l.notFound()}),f.fetch(e,o)}}let{warm:i}=r?.(o)??{};return(!R||i!==!0)&&(R=await T(t,{request:e,env:o,ctx:n})),R.fetch(e,o,n)}}}import{genericSqlite as Y}from"bknd";function ee(t){let r="sql"in t?t.sql:t.storage.sql;return Y("do-sqlite",r,e=>{let o=async(s,i)=>r.exec(s,...i||[]),n=s=>({insertId:void 0,numAffectedRows:s.rowsWritten>0?e.parseBigInt(s.rowsWritten):void 0,rows:s.toArray()||[],meta:{rowsWritten:s.rowsWritten,rowsRead:s.rowsRead,databaseSize:r.databaseSize}});return{db:r,batch:async s=>Promise.all(s.map(async i=>n(await o(i.sql,i.parameters)))),query:e.buildQueryFn({all:async(s,i)=>{let a=o(s,i);return n(await a).rows},run:async(s,i)=>{let a=o(s,i);return n(await a)}}),close:()=>{}}},{supports:{batching:!0,softscans:!1},excludeTables:["_cf_KV","_cf_METADATA"]})}import{registries as He}from"bknd";import{writeFile as te}from"node:fs/promises";import{resolve as ne}from"node:path";function se({verbose:t=!1,configFile:r="bknd.config.ts"}={}){let e=!1,o="";return{name:"dev-fs-plugin",enforce:"pre",configResolved(n){e=n.command==="serve",o=n.root},configureServer(n){if(!e){t&&console.debug("[dev-fs-plugin] Not in dev mode, skipping");return}let s=new Map,i=process.stdout.write;process.stdout.write=function(a,d,f){let l=a.toString();if(l.includes("[dev-fs-plugin]")||l.includes("[dev-fs-polyfill]"))return i.apply(process.stdout,arguments);let h=!1;if(l.includes("{{DEV_FS_START}}")){let g=[...l.matchAll(/{{DEV_FS_START}} ([a-z0-9]+) (\d+) (.+)/g)];for(let p of g){let m=p[1],c=Number.parseInt(p[2]),u=p[3];s.set(m,{totalChunks:c,filename:u,chunks:new Array(c),receivedChunks:0}),t&&console.debug(`[dev-fs-plugin] Started request ${m} for ${u} (${c} chunks)`)}h=!0}if(l.includes("{{DEV_FS_CHUNK}}")){let g=[...l.matchAll(/{{DEV_FS_CHUNK}} ([a-z0-9]+) (\d+) ([A-Za-z0-9+/=]+)/g)];for(let p of g){let m=p[1],c=Number.parseInt(p[2]),u=p[3],y=s.get(m);y&&(y.chunks[c]=u,y.receivedChunks++,t&&console.debug(`[dev-fs-plugin] Received chunk ${c}/${y.totalChunks-1} for ${y.filename} (length: ${u.length})`),u.length<1e3&&c<y.totalChunks-1&&t&&console.warn(`[dev-fs-plugin] WARNING: Chunk ${c} seems truncated (length: ${u.length})`))}h=!0}if(l.includes("{{DEV_FS_END}}")){let g=[...l.matchAll(/{{DEV_FS_END}} ([a-z0-9]+)/g)];for(let p of g){let m=p[1],c=s.get(m);if(c&&c.receivedChunks===c.totalChunks)try{let u=c.chunks.join("");t&&console.debug(`[dev-fs-plugin] Reconstructed ${c.filename} - base64 length: ${u.length}`);let y=atob(u),C=JSON.parse(y);if(C.type==="DEV_FS_WRITE_REQUEST")return t&&console.debug(`[dev-fs-plugin] Processing write request for ${C.filename}`),(async()=>{try{let k=ne(o,C.filename);t&&console.debug(`[dev-fs-plugin] Writing to: ${k}`),await te(k,C.data),t&&console.debug("[dev-fs-plugin] File written successfully!")}catch(k){console.error("[dev-fs-plugin] Error writing file:",k)}})(),s.delete(m),!0}catch(u){console.error("[dev-fs-plugin] Error processing chunked request:",String(u)),s.delete(m)}else c&&t&&console.debug(`[dev-fs-plugin] Request ${m} incomplete: ${c.receivedChunks}/${c.totalChunks} chunks`)}h=!0}return h?f?f():!0:i.apply(process.stdout,arguments)},n.httpServer?.on("close",()=>{process.stdout.write=i})},transform(n,s,i){if(e){if(s.includes(r))return t&&console.debug("[dev-fs-plugin] Transforming",r),` // Dev-fs polyfill injected by vite-plugin-dev-fs if (typeof globalThis !== 'undefined') { globalThis.__devFsPolyfill = { writeFile: async (filename, data) => { ${t?"console.debug('[dev-fs-polyfill] Intercepting write request for', filename);":""} // Use console logging as a communication channel // The main process will watch for this specific log pattern const writeRequest = { type: 'DEV_FS_WRITE_REQUEST', filename: filename, data: data, timestamp: Date.now() }; // Output as a specially formatted console message with end delimiter // Base64 encode the JSON to avoid any control character issues const jsonString = JSON.stringify(writeRequest); const encodedJson = btoa(jsonString); // Split into reasonable chunks that balance performance vs reliability const chunkSize = 2000; // 2KB chunks - safe for most environments const chunks = []; for (let i = 0; i < encodedJson.length; i += chunkSize) { chunks.push(encodedJson.slice(i, i + chunkSize)); } const requestId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5); // Send start marker (use stdout.write to avoid console display) process.stdout.write('{{DEV_FS_START}} ' + requestId + ' ' + chunks.length + ' ' + filename + '\\n'); // Send each chunk chunks.forEach((chunk, index) => { process.stdout.write('{{DEV_FS_CHUNK}} ' + requestId + ' ' + index + ' ' + chunk + '\\n'); }); // Send end marker process.stdout.write('{{DEV_FS_END}} ' + requestId + '\\n'); return Promise.resolve(); } }; }`+n;t&&console.debug("[dev-fs-plugin] Not transforming",s)}}}}async function oe(t,r){try{if(typeof globalThis<"u"&&globalThis.__devFsPolyfill)return globalThis.__devFsPolyfill.writeFile(t,r);let{writeFile:e}=await import("node:fs/promises");return e(t,r)}catch(e){console.error("[dev-fs-write] Error writing file:",e)}}function Fe(t){return b(t)}export{v as StorageR2Adapter,D as constants,T as createApp,Fe as d1,b as d1Sqlite,se as devFsVitePlugin,oe as devFsWrite,ee as doSqlite,x as getBinding,w as getBindings,Z as getFresh,B as makeConfig,_ as registerMedia,He as registries,X as serve};