const db =newDocStore(newMemoryStorageAdapter());// o persistente: new DocStore('./data')// o Cloudflare KV: new DocStore(new CloudflareKVAdapter(env.MY_KV))const users = db.collection('users');
users.createIndex('email',{unique:true});
users.insert({name:'Alice',email:'alice@test.com',age:30});
users.insert({name:'Bob',email:'bob@test.com',age:25});
users.find({age:{$gte:18}}).sort({age:-1}).limit(10).toArray();
db.flush();// persiste a disco/KV
users.createIndex('email',{unique:true});
users.createIndex('category');// Las queries sobre campos indexados usan el indice automaticamente
users.findOne({email:'alice@test.com'});// O(1) en vez de O(n)
Sorted Index (rangos + ORDER BY)
users.createIndex('age',{type:'sorted'});// Range queries usan binary search
users.find({age:{$gte:18,$lte:65}}).toArray();
const adapter =await EncryptedAdapter.create(newFileStorageAdapter('./data'),'my-password');const db =newDocStore(adapter);// Todo se encripta con AES-256-GCM automaticamente
db.flush();await adapter.persist();// escribe encriptado a disco// Para leer: preload primeroconst adapter2 =await EncryptedAdapter.create(innerAdapter,'my-password');await adapter2.preload(['users.docs.json','users.meta.json']);const db2 =newDocStore(adapter2);
Field-level (campos individuales)
const fc =await FieldCrypto.create('my-password');
users.insert({name:'Alice',// queryable, indexablecity:'Madrid',// queryable, indexablessn:await fc.encrypt('123-45-6789'),// encriptadocreditCard:await fc.encrypt('4111-...'),// encriptado});// Leer campo encriptadoconst doc = users.findOne({name:'Alice'});const ssn =await fc.decrypt(doc.ssn);// '123-45-6789'// Verificar si esta encriptado
fc.isEncrypted(doc.ssn);// true
fc.isEncrypted(doc.name);// false
const user =await auth.register('alice@test.com','password123',{name:'Alice'});const{ token, user }=await auth.login('alice@test.com','password123');
Verificar token
const payload =await auth.verify(token);// { sub: 'user-id', email: 'alice@test.com', roles: ['user'], exp: ... }// null si invalido o expirado
RBAC
auth.assignRole(userId,'admin');
auth.removeRole(userId,'admin');
auth.hasRole(userId,'admin');// Verificar token + rol en una llamadaconst payload =await auth.authorize(token,'admin');// payload si autorizado, null si no
Gestion de usuarios
auth.getUser(userId);
auth.getUserByEmail('alice@test.com');
auth.listUsers({roles:{$contains:'admin'}},{sort:{createdAt:-1},limit:10});
auth.disableUser(userId);// no puede hacer login
auth.enableUser(userId);
auth.deleteUser(userId);// elimina user + sessions
Passwords y sesiones
await auth.changePassword(userId,'old-pass','new-pass');await auth.resetPassword(userId,'new-pass');// admin/recovery
auth.logout(token);// invalida sesion
auth.logoutAll(userId);// invalida todas las sesiones
auth.cleanExpiredSessions();// limpieza
Storage Adapters
// Node.js (disco)newDocStore('./data');newDocStore(newFileStorageAdapter('./data'));// Memoria (tests, browser)newDocStore(newMemoryStorageAdapter());// Cloudflare Workers KVconst adapter =newCloudflareKVAdapter(env.MY_KV,'prefix/');await adapter.preloadAll();// carga todos los archivos bajo el prefixnewDocStore(adapter);// despues: db.flush(); await adapter.persist();// O preload selectivo (si sabes que archivos necesitas):// await adapter.preload(['users.docs.json', 'users.meta.json']);// Listar keys disponibles:// const keys = await adapter.listKeys(); // ['users.docs.json', 'users.meta.json', ...]// Encriptado (wraps any adapter)const adapter =await EncryptedAdapter.create(innerAdapter,'password');newDocStore(adapter);// Custom (implementar 3 metodos):classMyAdapter{readJson(filename){/* → object | null */}writeJson(filename, data){/* object → void */}delete(filename){/* void */}}
contacts.getSchema();// { name, columns: [...] }
contacts.addColumn({name:'Score',type:'number',default:0});
contacts.renameColumn('Score','Rating');// renombra en todos los docs
contacts.removeColumn('Rating');