@dreamlake/dreamdb 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ export const memory: WebAssembly.Memory;
4
+ export const __wbg_s3backend_free: (a: number, b: number) => void;
5
+ export const __wbg_space_free: (a: number, b: number) => void;
6
+ export const __wbg_writer_free: (a: number, b: number) => void;
7
+ export const addressRoundTrip: (a: number, b: number) => [number, number, number, number];
8
+ export const addressVariant: (a: number, b: number) => [number, number, number, number];
9
+ export const bytesToBase32: (a: number, b: number) => [number, number];
10
+ export const decodeCbor: (a: number, b: number) => [number, number, number];
11
+ export const encodeCbor: (a: any) => [number, number, number, number];
12
+ export const modalityParse: (a: number, b: number) => [number, number, number];
13
+ export const multihashBase32: (a: number, b: number) => [number, number];
14
+ export const multihashHex: (a: number, b: number) => [number, number];
15
+ export const rangeHeader: (a: bigint, b: bigint) => [number, number, number, number];
16
+ export const s3backend_get: (a: number, b: number, c: number, d: any) => any;
17
+ export const s3backend_list: (a: number, b: number, c: number) => any;
18
+ export const s3backend_new: (a: number, b: number) => number;
19
+ export const space_connectorBase: (a: number) => [number, number];
20
+ export const space_fromUri: (a: number, b: number, c: any) => any;
21
+ export const space_history: (a: number, b: number) => any;
22
+ export const space_manifestHash: (a: number) => [number, number];
23
+ export const space_queryHybrid: (a: number, b: number, c: number, d: any) => any;
24
+ export const space_queryScalar: (a: number, b: number, c: number, d: number, e: number, f: any) => any;
25
+ export const space_queryText: (a: number, b: number, c: number, d: number, e: number, f: number) => any;
26
+ export const space_queryVector: (a: number, b: number, c: number, d: number, e: number, f: any) => any;
27
+ export const space_readScalarColumn: (a: number, b: any, c: any) => any;
28
+ export const space_resolveObjectIndex: (a: number, b: any) => any;
29
+ export const space_timelineId: (a: number) => [number, number, number, number];
30
+ export const space_trackFor: (a: number, b: number, c: number) => any;
31
+ export const space_tracks: (a: number) => any;
32
+ export const spatialKeyRoundTrip: (a: number, b: number) => [number, number, number, number];
33
+ export const timeAnchorFromHex: (a: number, b: number) => [bigint, number, number];
34
+ export const timeAnchorHex: (a: bigint) => [number, number];
35
+ export const timeBucket: (a: bigint, b: number, c: number) => [bigint, number, number];
36
+ export const version: () => [number, number];
37
+ export const writer_appendMany: (a: number, b: any) => any;
38
+ export const writer_commit: (a: number) => any;
39
+ export const writer_deleteRecords: (a: number, b: number, c: number, d: number, e: number) => any;
40
+ export const writer_manifestHash: (a: number) => [number, number, number, number];
41
+ export const writer_open: (a: number, b: number, c: any) => any;
42
+ export const writer_refName: (a: number) => [number, number];
43
+ export const writer_snapshot: (a: number, b: number, c: number) => any;
44
+ export const zeroSpatialKey: () => [number, number];
45
+ export const __wasm_init: () => void;
46
+ export const wasm_bindgen_f7c54996eb9137d9___convert__closures_____invoke___wasm_bindgen_f7c54996eb9137d9___JsValue__core_7d5f0a2ba6a62c33___result__Result_____wasm_bindgen_f7c54996eb9137d9___JsError___true_: (a: number, b: number, c: any) => [number, number];
47
+ export const wasm_bindgen_f7c54996eb9137d9___convert__closures_____invoke___js_sys_ac40961855821e8e___Function_fn_wasm_bindgen_f7c54996eb9137d9___JsValue_____wasm_bindgen_f7c54996eb9137d9___sys__Undefined___js_sys_ac40961855821e8e___Function_fn_wasm_bindgen_f7c54996eb9137d9___JsValue_____wasm_bindgen_f7c54996eb9137d9___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
48
+ export const __wbindgen_malloc: (a: number, b: number) => number;
49
+ export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
50
+ export const __wbindgen_exn_store: (a: number) => void;
51
+ export const __externref_table_alloc: () => number;
52
+ export const __wbindgen_externrefs: WebAssembly.Table;
53
+ export const __wbindgen_destroy_closure: (a: number, b: number) => void;
54
+ export const __externref_table_dealloc: (a: number) => void;
55
+ export const __wbindgen_free: (a: number, b: number, c: number) => void;
56
+ export const __wbindgen_start: () => void;
@@ -0,0 +1,193 @@
1
+ // Browser-side additions to the wasm bindings.
2
+ //
3
+ // These live in JS rather than Rust on purpose: they are pure IO plus a
4
+ // user-supplied callback, and the crate itself says so (`connector.rs` keeps
5
+ // URL minting on the JS side). Nothing here touches the protocol, so nothing
6
+ // here can drift from it.
7
+ //
8
+ // Loaded only by the `browser` and `web` entry points. Node does not want a
9
+ // presigned-URL backend — on a server you hand the SDK real credentials via an
10
+ // S3 client, and minting a URL for yourself just adds a round trip.
11
+
12
+ /** Zero spatial-key path segment for non-spatial (fragment) object addresses. */
13
+ export const ZERO_SPATIAL_KEY = '0000000000000000'
14
+
15
+ /**
16
+ * Stand-in for the authoring surface, which the browser build does not carry.
17
+ *
18
+ * The type declarations describe `Authoring` on both ends so that a codebase
19
+ * shared between server and client type-checks once. Rather than let the
20
+ * browser fail with `undefined is not a constructor`, every entry point is a
21
+ * function that says what is missing and where to run it.
22
+ */
23
+ class AuthoringUnavailable {
24
+ static #nope(what) {
25
+ throw new Error(
26
+ `Authoring.${what} is not available in the browser build of ` +
27
+ `@dreamlake/dreamdb. Dataset creation, layers, merge and compaction ` +
28
+ `pull in the index builders (IVF/IMI/LSH/Vamana + codebook training), ` +
29
+ `which cost ~123 KB gzipped and need training data that does not ` +
30
+ `belong in a tab. Run it under Node — the "node" export condition ` +
31
+ `resolves to the full build — or use the CLI.`,
32
+ )
33
+ }
34
+ static async create() { AuthoringUnavailable.#nope('create') }
35
+ static async open() { AuthoringUnavailable.#nope('open') }
36
+ }
37
+
38
+ export { AuthoringUnavailable as Authoring }
39
+
40
+ /**
41
+ * A Backend (contract v2) that gets bytes over plain `fetch` and writes through
42
+ * short-lived presigned URLs minted by *your* server.
43
+ *
44
+ * Credentials never reach the browser. That was the explicit constraint, and it
45
+ * is also the only version of browser writes that is safe to ship: a bucket
46
+ * key in client JS is a bucket key in everyone's DevTools.
47
+ *
48
+ * ## What you must provide
49
+ *
50
+ * ```js
51
+ * new PresignedBackend({
52
+ * readBase: 'https://bucket.s3.amazonaws.com', // public/CDN read path
53
+ * mintPut: async (paths, opts) => {
54
+ * // paths: string[] → return { [path]: { url, headers? } }
55
+ * // opts.ifMatch: caller wants a conditional PUT (ref updates only)
56
+ * const r = await fetch('/api/dreamdb/sign', {
57
+ * method: 'POST',
58
+ * headers: { 'content-type': 'application/json' },
59
+ * body: JSON.stringify({ paths, ifMatch: opts?.ifMatch }),
60
+ * })
61
+ * if (!r.ok) throw new Error(`sign failed: ${r.status}`)
62
+ * return (await r.json()).urls
63
+ * },
64
+ * })
65
+ * ```
66
+ *
67
+ * `mintPut` takes an **array** of paths. One commit writes buckets, tracks, a
68
+ * manifest and a ref; signing them one at a time turns a commit into N server
69
+ * round trips. The backend batches where it can, and signs alone only when a
70
+ * write needs its own conditional header.
71
+ *
72
+ * ## Two things that will bite you if the bucket is misconfigured
73
+ *
74
+ * 1. **`ExposeHeaders: ["ETag"]` in the bucket's CORS rule is mandatory.**
75
+ * Without it the browser can see the response but not its ETag header, so
76
+ * the SDK has no prior version to compare against, falls back to a
77
+ * create-only PUT, and the *second* commit to any ref fails while the first
78
+ * succeeded. That asymmetry is what makes it hard to spot.
79
+ * 2. **Whether `If-Match` survives presigning is provider-specific.** SigV4
80
+ * signs whatever headers you tell it to, so a presigned conditional PUT is
81
+ * constructible — but it is not something the docs promise, and GCS uses
82
+ * `x-goog-if-generation-match` instead. Hence `refStrategy` below.
83
+ */
84
+ export class PresignedBackend {
85
+ /**
86
+ * @param {object} o
87
+ * @param {string} o.readBase Base URL for reads.
88
+ * @param {(paths: string[], opts?: {ifMatch?: string}) => Promise<Record<string, {url: string, headers?: Record<string,string>}>>} o.mintPut
89
+ * @param {(path: string, opts: {ifMatch?: string, ifNoneMatchStar?: boolean}, bytes: Uint8Array) => Promise<{status: string, etag?: string}>} [o.commitRef]
90
+ * Server-side ref commit. Used when `refStrategy` is `'proxy'`.
91
+ * @param {'proxy'|'presign'} [o.refStrategy='proxy']
92
+ * How the 33-byte ref object is written. `'proxy'` posts it to your
93
+ * server, which performs the conditional PUT with real credentials —
94
+ * works identically on S3, R2, MinIO and GCS. `'presign'` signs a
95
+ * conditional PUT and sends it from the browser; fewer moving parts,
96
+ * but it depends on provider behaviour this SDK cannot promise.
97
+ * @param {(path: string) => string} [o.readUrl] Override read URL building.
98
+ */
99
+ constructor({ readBase, mintPut, commitRef, refStrategy = 'proxy', readUrl }) {
100
+ if (!readBase) throw new Error('PresignedBackend: readBase is required')
101
+ if (typeof mintPut !== 'function') {
102
+ throw new Error('PresignedBackend: mintPut(paths, opts) is required')
103
+ }
104
+ if (refStrategy === 'proxy' && typeof commitRef !== 'function') {
105
+ throw new Error(
106
+ "PresignedBackend: refStrategy 'proxy' (the default) needs commitRef(). " +
107
+ "Pass refStrategy: 'presign' to sign conditional ref PUTs instead — " +
108
+ 'but read the note on provider support first.',
109
+ )
110
+ }
111
+ this.readBase = readBase.replace(/\/+$/, '')
112
+ this.mintPut = mintPut
113
+ this.commitRef = commitRef
114
+ this.refStrategy = refStrategy
115
+ this._readUrl = readUrl
116
+ }
117
+
118
+ #url(path) {
119
+ return this._readUrl ? this._readUrl(path) : `${this.readBase}/${path}`
120
+ }
121
+
122
+ /** v2 get: returns `{bytes, etag}`. Range is inclusive of `end`. */
123
+ async get(path, range) {
124
+ const headers = {}
125
+ if (range) headers.range = `bytes=${range.start}-${range.end}`
126
+ const r = await fetch(this.#url(path), { headers })
127
+ if (!r.ok) throw new Error(`GET ${path}: ${r.status} ${r.statusText}`)
128
+ return {
129
+ bytes: new Uint8Array(await r.arrayBuffer()),
130
+ etag: normalizeEtag(r.headers.get('etag')),
131
+ }
132
+ }
133
+
134
+ async head(path) {
135
+ const r = await fetch(this.#url(path), { method: 'HEAD' })
136
+ if (r.status === 404) return { exists: false }
137
+ if (!r.ok) throw new Error(`HEAD ${path}: ${r.status}`)
138
+ const len = r.headers.get('content-length')
139
+ return {
140
+ exists: true,
141
+ etag: normalizeEtag(r.headers.get('etag')),
142
+ size: len == null ? undefined : Number(len),
143
+ }
144
+ }
145
+
146
+ async put(path, bytes, opts = {}) {
147
+ // Ref updates are the only conditional writes, and they are the ones whose
148
+ // semantics differ per provider — so they get their own path.
149
+ const isRef = path.startsWith('refs/')
150
+ if (isRef && this.refStrategy === 'proxy') {
151
+ return await this.commitRef(path, opts, bytes)
152
+ }
153
+
154
+ const minted = await this.mintPut([path], opts.ifMatch ? { ifMatch: opts.ifMatch } : undefined)
155
+ const spec = minted?.[path]
156
+ if (!spec?.url) throw new Error(`mintPut returned no URL for ${path}`)
157
+
158
+ const headers = { ...(spec.headers || {}) }
159
+ if (opts.ifMatch) headers['if-match'] = opts.ifMatch
160
+ else if (opts.ifNoneMatchStar) headers['if-none-match'] = '*'
161
+
162
+ const r = await fetch(spec.url, { method: 'PUT', body: bytes, headers })
163
+
164
+ // 412 is genuinely ambiguous and the SDK cannot disambiguate it from the
165
+ // status alone: for `If-None-Match: *` on a content-addressed object it
166
+ // means "already there", which is success; for `If-Match` on a ref it means
167
+ // someone else moved it, which is a conflict the caller must resolve.
168
+ if (r.status === 412 || r.status === 409) {
169
+ return opts.ifNoneMatchStar
170
+ ? { status: 'exists' }
171
+ : { status: 'casFailed', etag: normalizeEtag(r.headers.get('etag')) }
172
+ }
173
+ if (!r.ok) throw new Error(`PUT ${path}: ${r.status} ${r.statusText}`)
174
+ return { status: 'created', etag: normalizeEtag(r.headers.get('etag')) }
175
+ }
176
+
177
+ // No `delete`. It would need a signed DELETE URL, and the sign endpoint only
178
+ // ever signs PUT — handing out object deletion to a browser is not something
179
+ // this write channel should make easy. Nothing in the browser write surface
180
+ // needs it either: `deleteRecords` writes tombstones (new objects), and
181
+ // dropping a ref is an `Authoring` operation, which is Node-only.
182
+ }
183
+
184
+ /**
185
+ * S3 quotes ETags and sometimes suffixes them (`W/"…"`, `"…-3"` for multipart).
186
+ * The conditional-write comparison is byte-exact, so a stray quote turns every
187
+ * CAS into a mismatch — which reads as "someone else is writing" forever.
188
+ */
189
+ function normalizeEtag(v) {
190
+ if (!v) return undefined
191
+ const t = v.trim().replace(/^W\//, '').replace(/^"|"$/g, '')
192
+ return t || undefined
193
+ }
package/browser.mjs ADDED
@@ -0,0 +1,4 @@
1
+ // Bundler entry (Vite, webpack, Next, Rollup). The wasm is imported as a
2
+ // module and instantiated by the bundler; there is nothing to await.
3
+ export * from './browser/dreamdb.js'
4
+ export { ZERO_SPATIAL_KEY, PresignedBackend, Authoring } from './browser-extras.mjs'