{"_id":"@airdraft/db-adapter","_rev":"3-0df979bf060d63d8edb95846ec39f2a7","name":"@airdraft/db-adapter","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@airdraft/db-adapter","version":"0.1.0","license":"MIT","_id":"@airdraft/db-adapter@0.1.0","maintainers":[{"name":"miracleio","email":"miracleficient@gmail.com"}],"dist":{"shasum":"dcd3504b5b9a6bc9c1055971f5b6330a6433479a","tarball":"https://registry.npmjs.org/@airdraft/db-adapter/-/db-adapter-0.1.0.tgz","fileCount":18,"integrity":"sha512-Vge7DbMTJa1t4tEd1G5JhoaZQ2i9f4ZGTJWe3E4035L3AHyVtJM/0lj+1u9D67xNhrp0PCqFTicrhI6X1ZzZzw==","signatures":[{"sig":"MEUCIQC1ZY4kZ0DQhxEPQ4lJWET4EhtM/443lmL9DeDQyj+iuAIgUgVB4V6fuofquBQ9FYhK1KEp6Dj/Var/flUD4OlyvEQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39185},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing/contract.d.ts","import":"./dist/testing/contract.js"}},"gitHead":"487fa21cb4354fe9f328783fc337269784e1262c","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"miracleio","email":"miracleficient@gmail.com"},"_npmVersion":"11.9.0","description":"Airdraft base database adapter — abstract class, shared types, and contract test suite","directories":{},"_nodeVersion":"24.14.0","dependencies":{"@airdraft/core":"workspace:*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.4.0","@types/node":"^20.0.0"},"peerDependenciesMeta":{},"_npmOperationalInternal":{"tmp":"tmp/db-adapter_0.1.0_1780294549321_0.8404684432219278","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@airdraft/db-adapter","version":"0.1.1","license":"MIT","_id":"@airdraft/db-adapter@0.1.1","maintainers":[{"name":"miracleio","email":"miracleficient@gmail.com"}],"dist":{"shasum":"b04b1bc6a606992cda95c095f85481690bcb6969","tarball":"https://registry.npmjs.org/@airdraft/db-adapter/-/db-adapter-0.1.1.tgz","fileCount":18,"integrity":"sha512-gs6z4j3n064+PfWZClF1RXPHhO7vB5xQyUE7Ey6TejOtEXS/SI18w5wio2XkMLL3Rbav2PHZS7T9kwsRat3LPw==","signatures":[{"sig":"MEYCIQDBSnvdTtGej06dEpHi/2fOJU6ZFAniscV4sMRobRJlDwIhAO5Xr5z6mNqXMMgPLb/iAJcQRlw1RZxLnQI1ya6ORugb","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":39175},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./testing":{"types":"./dist/testing/contract.d.ts","import":"./dist/testing/contract.js"}},"gitHead":"633bdabb60ea9a7fd1cf9b12434309cfa8b881cf","scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"miracleio","email":"miracleficient@gmail.com"},"_npmVersion":"11.9.0","description":"Airdraft base database adapter — abstract class, shared types, and contract test suite","directories":{},"_nodeVersion":"24.14.0","dependencies":{"@airdraft/core":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","typescript":"^5.4.0","@types/node":"^20.0.0"},"peerDependenciesMeta":{},"_npmOperationalInternal":{"tmp":"tmp/db-adapter_0.1.1_1780295734402_0.6899914003127254","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@airdraft/db-adapter","version":"0.1.2","description":"Airdraft base database adapter — abstract class, shared types, and contract test suite","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./testing":{"import":"./dist/testing/contract.js","types":"./dist/testing/contract.d.ts"}},"scripts":{"build":"tsc","dev":"tsc --watch","clean":"rm -rf dist","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"publishConfig":{"access":"public"},"license":"MIT","dependencies":{"@airdraft/core":"*"},"devDependencies":{"@types/node":"^20.0.0","typescript":"^5.4.0","vitest":"^2.0.0"},"peerDependenciesMeta":{},"gitHead":"9fdb3e0fbca1e58bb84aca8dd92155d0057ca4b1","_id":"@airdraft/db-adapter@0.1.2","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-fpd2i8k1/+meaQETqcJwyfgVbFk4F336P8rDtxN/vatZ+gZxAwAOsaeglc+cbNQzkhKcQM0eFsyRmfGkLN7e2A==","shasum":"75e5c4fdc4cd9700800e21ca40dafa2966b4f4d7","tarball":"https://registry.npmjs.org/@airdraft/db-adapter/-/db-adapter-0.1.2.tgz","fileCount":18,"unpackedSize":39205,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCGj3VuViXv1iW1lXj04ZV/PfaStgvZt3ZMQ2uX0roxZAIhAMeVwuS8LtDLPpjzl8slkMqd/rfnBDUcmp3I0GcVcWaW"}]},"_npmUser":{"name":"miracleio","email":"miracleficient@gmail.com"},"directories":{},"maintainers":[{"name":"miracleio","email":"miracleficient@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/db-adapter_0.1.2_1782411662158_0.8163749875334916"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-01T06:15:49.113Z","modified":"2026-06-25T18:21:02.418Z","0.1.0":"2026-06-01T06:15:49.484Z","0.1.1":"2026-06-01T06:35:34.543Z","0.1.2":"2026-06-25T18:21:02.298Z"},"license":"MIT","description":"Airdraft base database adapter — abstract class, shared types, and contract test suite","maintainers":[{"name":"miracleio","email":"miracleficient@gmail.com"}],"readme":"# @airdraft/db-adapter\n\nBase package for Airdraft database adapters. Provides the abstract `BaseDatabaseAdapter` class, shared TypeScript types, and a portable contract test suite that all driver packages run against.\n\nYou never install this package directly in a user project. It is a peer dependency of each driver package (`@airdraft/db-adapter-sqlite`, `@airdraft/db-adapter-postgres`, `@airdraft/db-adapter-mongodb`).\n\n---\n\n## Architecture\n\nAirdraft stores content as typed documents (entries). Each entry lives at a path `<collection>/<slug>`, carries an opaque `sha` (SHA-256 of the serialized content), and is tied to a `projectId` for multi-tenancy.\n\n`BaseDatabaseAdapter` implements the full `StorageAdapter` interface expected by the CMS engine, so the engine, plugins, and client SDKs see no difference between a file adapter and a database adapter.\n\nThe engine detects a database adapter via `instanceof BaseDatabaseAdapter` and routes bulk list operations through the single-query `queryEntries()` method rather than the N+1 `list()` + `read()` loop used for file adapters.\n\n---\n\n## Abstract methods (implement in your driver)\n\n| Method | Description |\n|---|---|\n| `migrate(): Promise<void>` | Creates `airdraft_entries` and (when `history: true`) `airdraft_entry_history`. Idempotent — safe to call on every deploy. |\n| `read(path): Promise<FileResult \\| null>` | Reads a single entry by path. |\n| `write(path, content, options): Promise<void>` | Creates or updates an entry. Must enforce SHA-based optimistic concurrency and throw `ConflictError` on mismatch. |\n| `delete(path, options): Promise<void>` | Deletes an entry. |\n| `queryEntries(collection, options): Promise<QueryEntriesResult>` | Bulk query: filters, sorts, and paginates in a single DB call. Called by the engine fast-path. |\n| `atomicUpdate(path, ops, sha): Promise<void>` | Applies `AtomicOp` field operations (increment / set / push / pull) atomically. |\n| `rollback(path, sha): Promise<void>` | Restore a previous revision from the history table. Only when `history: true`. |\n| `close(): Promise<void>` | Release connection pool / file handles. |\n\n---\n\n## Constructor options (`DbAdapterOptions`)\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `projectId` | `string` | `'default'` | Isolates all reads/writes within a shared DB instance. Set to the Airdraft project slug for multi-tenant deployments. |\n| `history` | `boolean` | `false` | When `true`, mirrors every write to `airdraft_entry_history`. Enables `rollback()`. |\n| `cacheTtlMs` | `number` | `0` | Per-entry read cache TTL in milliseconds. `0` disables the cache. |\n| `cacheMaxSize` | `number` | `500` | Maximum entries in the LRU read cache. |\n\n---\n\n## Shared types\n\n```ts\nimport type { DbAdapterOptions, QueryEntriesResult, AtomicOp } from '@airdraft/db-adapter'\n```\n\n### `QueryEntriesResult`\n\n```ts\ninterface QueryEntriesResult {\n  entries: Array<{ path: string; content: string; sha: string }>\n  total: number  // total matching rows before pagination — used for meta.total\n}\n```\n\n### `AtomicOp`\n\n```ts\ntype AtomicOp =\n  | { op: 'increment'; path: string; by: number }\n  | { op: 'set';       path: string; value: unknown }\n  | { op: 'push';      path: string; value: unknown }\n  | { op: 'pull';      path: string; value: unknown }\n```\n\n`path` uses dot-notation — e.g. `'stats.viewCount'` for a nested field.\n\n---\n\n## Contract test suite\n\nThe `@airdraft/db-adapter/testing` export provides a shared Vitest test suite that all driver packages run against to verify full contract compliance.\n\n```ts\n// src/__tests__/contract.test.ts (inside a driver package)\nimport { describe } from 'vitest'\nimport { runStorageAdapterContract } from '@airdraft/db-adapter/testing'\nimport { SQLiteAdapter } from '../SQLiteAdapter.js'\n\ndescribe('SQLiteAdapter contract', () => {\n  runStorageAdapterContract(() => new SQLiteAdapter({ filename: ':memory:', history: true }))\n})\n```\n\nThe suite covers: `migrate`, `write`/`read`, `ConflictError` on SHA mismatch, `delete`, `list`, `queryEntries` (with filters, sort, pagination), `atomicUpdate`, history writes, and `rollback`.\n\n---\n\n## Writing a custom adapter\n\nExtend `BaseDatabaseAdapter` and implement all abstract methods:\n\n```ts\nimport { BaseDatabaseAdapter } from '@airdraft/db-adapter'\nimport type { FileResult, WriteOptions, DeleteOptions, ListOptions } from '@airdraft/core'\nimport type { QueryEntriesResult, AtomicOp } from '@airdraft/db-adapter'\n\nexport class MyAdapter extends BaseDatabaseAdapter {\n  async migrate() { /* CREATE TABLE IF NOT EXISTS … */ }\n  async read(path: string): Promise<FileResult | null> { /* … */ }\n  async write(path: string, content: string, options: WriteOptions): Promise<void> { /* … */ }\n  async delete(path: string, options: DeleteOptions): Promise<void> { /* … */ }\n  async queryEntries(collection: string, options: ListOptions): Promise<QueryEntriesResult> { /* … */ }\n  async atomicUpdate(path: string, ops: AtomicOp[], sha: string): Promise<void> { /* … */ }\n  async rollback(path: string, sha: string): Promise<void> { /* … */ }\n  async close(): Promise<void> { /* … */ }\n}\n```\n\nRun the shared contract suite in your tests to confirm your implementation is spec-compliant.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}