Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/record-adapter-do-sqlite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
'@haverstack/record-adapter-do-sqlite': patch
'@haverstack/record-adapter-sqlite': patch
---

Add `@haverstack/record-adapter-do-sqlite` — a `StackRecordAdapter` over Cloudflare
Durable Objects' SQLite storage, for Workers deployments with no Node runtime
available. Reuses `SharedSqlRecordLogic`, the FTS5 schema and strategy, the query
builder, cursor codec, and row mappers from `@haverstack/sqlite-shared` — the same
shared layer `record-adapter-sqlite` is built on, now via its `./record` subpath
(the token-store and file-lock pieces stay Node-only and unreachable from this
adapter's bundle). No lock file: a Durable Object id maps to exactly one running
instance, so the platform itself is the single-writer guarantee. No persist/flush
step: every write through `ctx.storage.sql` is durable by the time the call returns.

`@haverstack/sqlite-shared`'s `SqlExecutor` gained a `transaction<T>(fn: () => T): T`
primitive, replacing the raw `BEGIN`/`COMMIT`/`ROLLBACK` statements `record-logic.ts`
used to issue directly. Durable Object SQLite storage rejects those statements
outright and does not roll back a write on a later exception the way an open SQL
transaction would (verified against the real Workers runtime) — its real primitive
is `ctx.storage.transactionSync(fn)`, a callback boundary that three independent
string-based `exec()` calls can't reach. `record-adapter-sqlite`'s executor
implements `transaction()` as literal `BEGIN`/`COMMIT`/`ROLLBACK` around `fn()`,
behavior-identical to what the inline code did before — its full test suite passes
unchanged.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ dist/
*.js.map
*.d.ts
*.d.ts.map
# ...except hand-authored ambient declaration files, e.g. a Workers
# package's triple-slash reference to @cloudflare/vitest-pool-workers/types
# for `cloudflare:test` typings (wrangler's own generated
# worker-configuration.d.ts stays ignored — it's regenerated by a
# pretest/pretypecheck script, not committed).
!**/tests/support/*.d.ts

# Test databases
*.db
Expand Down
40 changes: 23 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,16 @@ The delegation itself — "this app acts for Bob" — is asserted by you when th

This is a monorepo. Packages are published to npm under the `@haverstack` scope.

| Package | Description |
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [`@haverstack/core`](./packages/core) | Stack class, types, schema, validation, ID generation |
| [`@haverstack/adapter-local`](./packages/adapter-local) | Local adapter (native SQLite + disk) — single-app/embedded or server use |
| [`@haverstack/record-adapter-sqlite`](./packages/record-adapter-sqlite) | Node native SQLite (`node:sqlite`) `StackRecordAdapter` — used by `adapter-local` |
| [`@haverstack/blob-adapter-disk`](./packages/blob-adapter-disk) | Disk filesystem `StackBlobAdapter` |
| [`@haverstack/blob-adapter-s3`](./packages/blob-adapter-s3) | S3 (and S3-compatible, e.g. Cloudflare R2) `StackBlobAdapter` |
| [`@haverstack/adapter-api`](./packages/adapter-api) | HTTP adapter for remote stack servers |
| [`@haverstack/commons`](./packages/commons) | Canonical Schema Commons type definitions (`note`, `task`, `contact`, ...) |
| Package | Description |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [`@haverstack/core`](./packages/core) | Stack class, types, schema, validation, ID generation |
| [`@haverstack/adapter-local`](./packages/adapter-local) | Local adapter (native SQLite + disk) — single-app/embedded or server use |
| [`@haverstack/record-adapter-sqlite`](./packages/record-adapter-sqlite) | Node native SQLite (`node:sqlite`) `StackRecordAdapter` — used by `adapter-local` |
| [`@haverstack/record-adapter-do-sqlite`](./packages/record-adapter-do-sqlite) | Cloudflare Durable Objects (SQLite storage) `StackRecordAdapter` — Workers |
| [`@haverstack/blob-adapter-disk`](./packages/blob-adapter-disk) | Disk filesystem `StackBlobAdapter` |
| [`@haverstack/blob-adapter-s3`](./packages/blob-adapter-s3) | S3 (and S3-compatible, e.g. Cloudflare R2) `StackBlobAdapter` |
| [`@haverstack/adapter-api`](./packages/adapter-api) | HTTP adapter for remote stack servers |
| [`@haverstack/commons`](./packages/commons) | Canonical Schema Commons type definitions (`note`, `task`, `contact`, ...) |

Planned:

Expand Down Expand Up @@ -244,14 +245,15 @@ The adapter interface is split into `StackRecordAdapter` (structured records) an
- **`record-adapter-*`** — `StackRecordAdapter` only
- **`blob-adapter-*`** — `StackBlobAdapter` only

| Package | Type | Use case |
| ----------------------- | ------ | ------------------------------------------------------------------------------- |
| `adapter-local` | full | Single-app/embedded or server use — native SQLite records + disk blobs |
| `record-adapter-sqlite` | record | Node native SQLite (`node:sqlite`) records, FTS5, WAL — used by `adapter-local` |
| `blob-adapter-disk` | blob | Content-addressed blobs on the local filesystem |
| `blob-adapter-s3` | blob | Content-addressed blobs on S3 or an S3-compatible store (e.g. Cloudflare R2) |
| `adapter-api` | full | Hosted/shared stacks via HTTP |
| `adapter-json` | full | Portable JSON files _(planned)_ |
| Package | Type | Use case |
| -------------------------- | ------ | ------------------------------------------------------------------------------- |
| `adapter-local` | full | Single-app/embedded or server use — native SQLite records + disk blobs |
| `record-adapter-sqlite` | record | Node native SQLite (`node:sqlite`) records, FTS5, WAL — used by `adapter-local` |
| `record-adapter-do-sqlite` | record | Cloudflare Durable Objects (SQLite storage) records, FTS5 |
| `blob-adapter-disk` | blob | Content-addressed blobs on the local filesystem |
| `blob-adapter-s3` | blob | Content-addressed blobs on S3 or an S3-compatible store (e.g. Cloudflare R2) |
| `adapter-api` | full | Hosted/shared stacks via HTTP |
| `adapter-json` | full | Portable JSON files _(planned)_ |

Use `combineAdapters({ record, blob })` from `@haverstack/core/adapter` to compose a record adapter with a different blob backend — for example, `NativeSQLiteRecordAdapter` with `S3BlobAdapter`. `adapter-local` wraps this pattern for the common case.

Expand Down Expand Up @@ -314,6 +316,10 @@ packages/
index.ts # NativeSQLiteRecordAdapter (StackRecordAdapter), node:sqlite
token-store.ts # NativeTokenStore (StackTokenStore), separate file from records
tests/
record-adapter-do-sqlite/ # @haverstack/record-adapter-do-sqlite
src/
index.ts # DoSQLiteRecordAdapter (StackRecordAdapter), Cloudflare Durable Objects
tests/
blob-adapter-disk/ # @haverstack/blob-adapter-disk
src/
index.ts # DiskBlobAdapter (StackBlobAdapter)
Expand Down
Loading
Loading