@dreamlake/dreamdb

中文文档
0.5.10发布于 2026/11/13MIT OR Apache-2.0TypeScriptESM + CJS零依赖近 30 天下载 4038

DreamDB SDK — isomorphic (browser + Node) read and write, compiled from the Rust core

原始使用文档
文档目录9 节
  1. Reading
  2. Writing
  3. Anchors are nanoseconds, and you should pass a bigint
  4. Server-side authoring
  5. Entry points
  6. Implementing a Backend
  7. Two things that will bite you
  8. Conformance
  9. Licence

@dreamlake/dreamdb

This 0.5.10 build includes read-only SqlSession.open(refName, backend), asynchronous query and synchronous explain. See the SQL adapter guide. The browser SQL artifact loads on first open; Node includes it directly. This addition is not a claim that previously published npm versions support SQL.

Package scope: npm 0.5.10 updates the shared core. The one JavaScript API addition since 0.5.8 is Space.readEmbeddingColumn, first published in 0.5.9: a bounded, exact read of a whole Embedding column that refuses rather than returning a partial column. The rest is shared-core write and read cost reductions and fixes. Paged array indexes from 0.5.8, SQL from 0.5.6, and per-publication SDK/build diagnostics and the deferred compaction commit fix from 0.5.5 remain. Existing entity keys, geometry, structured arrays and query-local rerank controls remain. Rust operator APIs are not automatically JS exports. See the release notes, feature guide and SDK reference.

The DreamDB SDK — read and write, in the browser and on the server, compiled to WebAssembly from the same Rust core the CLI and Python SDK use.

There is no separate JavaScript implementation of the protocol, and that is the point. A hand-written port has to reproduce BLAKE3, canonical CBOR, spatial-key encoding, bucket headers and index layout bit for bit, forever. The previous TypeScript port did not, and its tests were all green anyway, because they were its own reader reading its own writer.

npm install @dreamlake/dreamdb

Reading

import { Space } from '@dreamlake/dreamdb'

const space = await Space.fromUri('https://bucket.s3.amazonaws.com/refs/my-dataset', null)
const hits = await space.queryVector('visual', queryVec, 24, 8)

Passing null for the backend uses direct fetch against the URI's base — enough for a public bucket. For anything else, supply a Backend.

Writing

import { Writer, PresignedBackend } from '@dreamlake/dreamdb'

const backend = new PresignedBackend({
  readBase: 'https://bucket.s3.amazonaws.com',
  mintPut: async (paths) => (await postJson('/api/dreamdb/sign', { paths })).urls,
  commitRef: async (path, opts, bytes) =>
    await postJson('/api/dreamdb/ref', {
      path,
      bytesBase64: base64(bytes),
      ifMatch: opts.ifMatch,
      ifNoneMatchStar: opts.ifNoneMatchStar,
    }),
})

const w = await Writer.open('https://bucket.s3.amazonaws.com/refs/my-dataset', backend)
await w.appendMany([
  {
    anchor: 1735689600000000000n,          // nanoseconds — pass a bigint
    visual: { kind: 'embedding', algorithm: 'dreamdb.lsh-cosine', vector: vec },
    caption: { kind: 'categorical', value: 'a red car' },
  },
])
const manifest = await w.commit()

Credentials never enter the browser: your server mints short-lived presigned PUT URLs, and performs the ref's compare-and-swap itself.

Anchors are nanoseconds, and you should pass a bigint

A number is accepted only while it is below Number.MAX_SAFE_INTEGER; above that the SDK throws rather than round it. This is not pedantry. The previous TypeScript SDK wrote anchors in microseconds specifically to stay under 2^53, and every record it produced reads as 1970 in any conformant reader.

Server-side authoring

Authoring adds dataset creation, layers, merge, compaction and history. It is Node only — the browser build exports a stub whose methods throw with an explanation.

import { Authoring } from '@dreamlake/dreamdb'   // resolves to the Node build

const a = await Authoring.create(
  'my-dataset',
  [
    { name: 'visual', kind: 'embedding', dim: 768, algorithm: 'dreamdb.lsh-cosine' },
    { name: 'caption', kind: 'scalar', valueType: 'categorical', required: false },
  ],
  'https://bucket.s3.amazonaws.com',
  backend,
)
await a.writer().appendMany(samples)
await a.compact(null, 1, 0)

The split is a size boundary with a mechanism behind it. create and the embedding-layer builders construct a SpatialDispatcher, which is an enum — so one reachable construction makes every index family's build code reachable (IVF, IMI, LSH, Vamana, AdaIVF, plus codebook training). Measured: 484 KB gzipped for the browser build versus 607 KB with authoring included.

Embedding fields declaring dreamdb.ivf-cosine or dreamdb.imi-cosine cannot be created here at all: those index families need training data that does not exist at create time. Create with the default dreamdb.lsh-cosine and attach a trained index as a layer, or build it with the CLI.

Entry points

ImportResolves toNotes
@dreamlake/dreamdb in a bundler--target bundlerNeeds vite-plugin-wasm under Vite
@dreamlake/dreamdb in Node--target nodejsESM and CJS both work; includes Authoring
@dreamlake/dreamdb/web--target webNo bundler plugin needed; await ready() first
<script type="module">
  import ready, { Space } from 'https://esm.sh/@dreamlake/dreamdb/web'
  await ready()                 // fetches and instantiates the wasm
</script>

Implementing a Backend

Only get is required; a get-only backend is the read-only v1 shape and still works for reading. Implement getStream to keep full-object reads bounded when DreamDB must scan a historical oversized inline Track. Write entry points check for put up front and refuse with a message naming what is missing.

interface Backend {
  // `range` is half-open: start is included, end is excluded.
  get(path: string, range?: { start: number; end: number })
    : Promise<Uint8Array | { bytes: Uint8Array; etag?: string; totalLength?: number }>
  getStream?(path: string)
    : Promise<ReadableStream<Uint8Array> | {
        stream: ReadableStream<Uint8Array>
        etag?: string
        totalLength?: number
      }>
  head?(path: string): Promise<{ exists?: boolean; etag?: string; size?: number }>
  put?(path: string, bytes: Uint8Array,
       opts?: { ifMatch?: string; ifNoneMatchStar?: boolean })
    : Promise<{ status: 'created' | 'exists' | 'casFailed'; etag?: string }>
  delete?(path: string): Promise<void>
  list?(prefix: string): Promise<string[]>
}

getStream chunks must themselves be bounded; returning one object-sized Uint8Array is functionally valid but does not provide the bounded-memory profile. The SDK hashes every chunk and exposes no decoded result until the complete response matches its content address. Existing backends without getStream continue through get and are intentionally classified as the unbounded compatibility profile.

Two things that will bite you

Your bucket's CORS rule must include ExposeHeaders: ["ETag"].

A ref advances by compare-and-swap. The SDK sends If-Match: <etag> when it knows the ref's current version and If-None-Match: * when it does not. Without ExposeHeaders, the browser can read the response body but not the ETag header, so the SDK never learns the current version, falls back to create-only, and the second commit to any ref fails while the first succeeded. That asymmetry is what makes it hard to recognise.

{
  "AllowedOrigins": ["https://your.app"],
  "AllowedMethods": ["GET", "HEAD", "PUT"],
  "AllowedHeaders": ["*"],
  "ExposeHeaders": ["ETag", "Content-Range", "Content-Length"]
}

Any HTTP layer you put in front of the bucket must honour Range.

DreamDB reads exact vectors by byte offset. A server that ignores Range and returns 200 with the whole object makes every ranged read return offset 0, and search then returns plausible-looking results with cosine scores of 0.0000 — indistinguishable from a genuinely corrupt index. (python3 -m http.server does exactly this.)

Conformance

dreamdb-conformance/vectors/ holds 85 language-agnostic JSON vectors. This package runs the 48 that map to pure functions on its surface — canonical CBOR, BLAKE3 multihash, spatial keys, time anchors and buckets, address round-trips, modality parsing, HTTP range translation — and reports the other 37 by name and reason rather than omitting them silently.

node test/conformance.mjs

The first run of that suite found a real bug in this package's HTTP range translation, which is roughly the point.

Licence

MIT OR Apache-2.0

Publisher identity

Writer/Authoring publications record @dreamlake/dreamdb and the actual npm version in the existing Manifest writer tag. Use writer.setApplication(name, revision) (or authoring.writer().setApplication) for optional application metadata on later publications, and Space.history() to inspect it. Application components are 1–128 UTF-8 bytes without control characters. Existing payloads are not attributed to their latest publisher; provenance is diagnostic, not an execution attestation or compatibility check.

版本与发布记录

@dreamlake/dreamdb 共发布 19 个版本,首次发布于 2026/05/25;过去 12 个月发布了 19 个版本。

版本发布日期距今
0.5.10latest2026/11/13今天
0.5.92026/11/103 天前
0.5.82026/10/131 个月前
0.5.72026/09/181 个月前
0.5.62026/09/171 个月前
0.5.52026/09/151 个月前

关于 @dreamlake/dreamdb 的常见问题

@dreamlake/dreamdb 是什么?

DreamDB SDK — isomorphic (browser + Node) read and write, compiled from the Rust core

如何安装 @dreamlake/dreamdb?

在项目目录运行 npm install @dreamlake/dreamdb,或使用 pnpm add / yarn add / bun add。

@dreamlake/dreamdb 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dreamlake/dreamdb 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@dreamlake/dreamdb 可以商用吗?

@dreamlake/dreamdb 使用 MIT OR Apache-2.0 许可。可在 MIT、Apache-2.0 中任选其一遵守。选择 MIT 时,宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dreamlake/dreamdb 的下载量有多少?

根据 npm 官方下载统计,@dreamlake/dreamdb 在 2026/10/13 – 2026/11/11 这 30 天内被下载 4,038 次,日均约 135 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dreamlake/dreamdb 还在维护吗?

最新版本 0.5.10 发布于 2026/11/13(今天);至今共发布 19 个版本,其中过去 12 个月发布了 19 个;首次发布于 2026/05/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。