@volter/twin-turbopuffer 0.1.36 → 1.0.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.
- package/README.md +25 -29
- package/dist/generated/pack-facts.json +57 -0
- package/dist/spec/SOURCE.md +59 -0
- package/dist/spec/doc-examples.json +11120 -0
- package/dist/spec/openapi.yaml.gz +0 -0
- package/dist/spec/patches.json +308 -0
- package/dist/spec/sources.json +53 -0
- package/dist/src/cli.js +8 -18
- package/dist/src/{turbopuffer-filter.d.ts → engine/filter.d.ts} +1 -1
- package/dist/src/{turbopuffer-filter.js → engine/filter.js} +1 -1
- package/dist/src/{turbopuffer-store.d.ts → engine/namespace.d.ts} +3 -16
- package/dist/src/{turbopuffer-store.js → engine/namespace.js} +15 -71
- package/dist/src/{turbopuffer-text.js → engine/text.js} +1 -1
- package/dist/src/fetch.d.ts +2 -0
- package/dist/src/fetch.js +19 -0
- package/dist/src/generated/surface.gen.json +1 -1
- package/dist/src/generated/ui.gen.json +1 -1
- package/dist/src/index.d.ts +5 -11
- package/dist/src/index.js +8 -65
- package/dist/src/manifest.js +67 -1
- package/dist/src/screens/dashboard.d.ts +4 -11
- package/dist/src/screens/dashboard.js +25 -71
- package/dist/src/semantics/around.d.ts +2 -0
- package/dist/src/semantics/around.js +30 -0
- package/dist/src/semantics/doors.d.ts +9 -0
- package/dist/src/semantics/doors.js +31 -0
- package/dist/src/semantics/index.d.ts +2 -0
- package/dist/src/semantics/index.js +6 -0
- package/dist/src/semantics/namespaces.d.ts +8 -5
- package/dist/src/semantics/namespaces.js +97 -16
- package/dist/src/semantics/shared.d.ts +24 -0
- package/dist/src/semantics/shared.js +57 -0
- package/dist/src/server.d.ts +7 -0
- package/dist/src/server.js +7 -0
- package/generated/pack-facts.json +57 -0
- package/package.json +9 -16
- package/spec/SOURCE.md +59 -0
- package/spec/doc-examples.json +11120 -0
- package/spec/openapi.yaml.gz +0 -0
- package/spec/patches.json +308 -0
- package/spec/sources.json +53 -0
- package/src/cli.ts +8 -18
- package/src/{turbopuffer-filter.ts → engine/filter.ts} +1 -1
- package/src/{turbopuffer-store.ts → engine/namespace.ts} +17 -67
- package/src/{turbopuffer-text.ts → engine/text.ts} +1 -1
- package/src/fetch.ts +20 -0
- package/src/generated/surface.gen.json +1 -1
- package/src/generated/ui.gen.json +1 -1
- package/src/index.ts +8 -90
- package/src/manifest.ts +76 -9
- package/src/screens/dashboard.tsx +28 -73
- package/src/semantics/around.ts +28 -0
- package/src/semantics/doors.ts +33 -0
- package/src/semantics/index.ts +10 -0
- package/src/semantics/namespaces.ts +104 -29
- package/src/semantics/shared.ts +63 -0
- package/src/server.ts +8 -0
- package/dist/src/key-gate.d.ts +0 -3
- package/dist/src/key-gate.js +0 -39
- package/dist/src/turbopuffer-capabilities.d.ts +0 -6
- package/dist/src/turbopuffer-capabilities.js +0 -442
- package/dist/src/turbopuffer-conformance.d.ts +0 -8
- package/dist/src/turbopuffer-conformance.js +0 -102
- package/dist/src/turbopuffer-connector.d.ts +0 -34
- package/dist/src/turbopuffer-connector.js +0 -152
- package/dist/src/turbopuffer-server.d.ts +0 -23
- package/dist/src/turbopuffer-server.js +0 -72
- package/dist/src/turbopuffer-twin.d.ts +0 -25
- package/dist/src/turbopuffer-twin.js +0 -406
- package/src/key-gate.ts +0 -39
- package/src/turbopuffer-capabilities.ts +0 -455
- package/src/turbopuffer-conformance.ts +0 -101
- package/src/turbopuffer-connector.ts +0 -153
- package/src/turbopuffer-server.ts +0 -81
- package/src/turbopuffer-twin.ts +0 -429
- /package/dist/src/{turbopuffer-stem.d.ts → engine/stem.d.ts} +0 -0
- /package/dist/src/{turbopuffer-stem.js → engine/stem.js} +0 -0
- /package/dist/src/{turbopuffer-text.d.ts → engine/text.d.ts} +0 -0
- /package/src/{turbopuffer-stem.ts → engine/stem.ts} +0 -0
package/README.md
CHANGED
|
@@ -33,17 +33,13 @@ Grounded in the installed `@turbopuffer/turbopuffer@2.8.0` (`src/` paths below a
|
|
|
33
33
|
| Region | `region` option or `TURBOPUFFER_REGION`; required when the URL has `{region}` | `src/client.ts:255`, `274-286` |
|
|
34
34
|
| Base URL override | `TURBOPUFFER_BASE_URL` (or `baseURL`); `{region}` is substituted into it | `src/client.ts:253`, `281` |
|
|
35
35
|
| Auth | `Authorization: Bearer <apiKey>` (`TURBOPUFFER_API_KEY`) | `src/client.ts:254`, `383` |
|
|
36
|
-
| Request headers | `Accept: application/json`, `Accept-Encoding: identity`, `User-Agent`, `X-Stainless
|
|
36
|
+
| Request headers | `Accept: application/json`, `Accept-Encoding: identity`, `User-Agent`, `X-Stainless-*` | `src/client.ts:858-864` |
|
|
37
37
|
| Request body | JSON; gzip only with `compression: true` (default false) | `src/client.ts:299`, `812` |
|
|
38
38
|
| Node transport | undici `Agent.request`, selected by the `#fetch` import map for the `node` condition | `package.json` `imports`, `src/internal/custom/fetch-node.ts` |
|
|
39
|
-
| List namespaces | `GET /v1/namespaces?prefix&page_size&cursor` → `{ namespaces: [{id}], next_cursor }` | `src/client.ts:368`, `src/core/pagination.ts:123` |
|
|
40
39
|
| Write | `POST /v2/namespaces/{ns}` | `src/resources/namespaces.ts:174` |
|
|
41
40
|
| deleteAll | `DELETE /v2/namespaces/{ns}` → `{ status: 'OK' }` | `namespaces.ts:46`, `1100` |
|
|
42
41
|
| Query | `POST /v2/namespaces/{ns}/query` | `namespaces.ts:104` |
|
|
43
42
|
| multiQuery | `POST /v2/namespaces/{ns}/query?stainless_overload=multiQuery` with `{ queries }` | `namespaces.ts:90` |
|
|
44
|
-
| Metadata | `GET /v2/namespaces/{ns}/metadata` | `namespaces.ts:79` |
|
|
45
|
-
| Schema | `GET` / `POST /v1/namespaces/{ns}/schema` | `namespaces.ts:126`, `163` |
|
|
46
|
-
| Cache warm | `GET /v1/namespaces/{ns}/hint_cache_warm` | `namespaces.ts:68` |
|
|
47
43
|
| Write response | `{ status: 'OK', message, rows_affected, rows_upserted?, rows_patched?, rows_deleted?, *_ids?, billing }` | `namespaces.ts:1231-1294` |
|
|
48
44
|
| Query response | `{ rows?, aggregations?, billing, performance }`; a row is `{ id, $dist?, ...attributes }` | `namespaces.ts:850-868`, `1159-1175` |
|
|
49
45
|
| FTS defaults | tokenizer `word_v4`, `k1` 1.2, `b` 0.75, case-insensitive, stopwords kept (`remove_stopwords` false since January 2026), no stemming, `max_token_length` 39 | `namespaces.ts:474-526` |
|
|
@@ -57,7 +53,7 @@ that use the fetch build (Bun, edge).
|
|
|
57
53
|
|
|
58
54
|
## Coverage
|
|
59
55
|
|
|
60
|
-
Modelled
|
|
56
|
+
Modelled:
|
|
61
57
|
|
|
62
58
|
- **write**: `upsert_rows`, `upsert_columns`, `patch_rows`, `patch_columns`, `deletes`,
|
|
63
59
|
`delete_by_filter`, `patch_by_filter`, `schema`, `distance_metric`, `return_affected_ids`,
|
|
@@ -74,12 +70,13 @@ Modelled (each has a `done` capability with a verify in `src/turbopuffer-capabil
|
|
|
74
70
|
`[vector, ANN|kNN, q]` with exact `cosine_distance` / `euclidean_squared`; `top_k` / `limit`
|
|
75
71
|
(default 10); `include_attributes` / `exclude_attributes`; `aggregate_by` Count, Count(attr),
|
|
76
72
|
Sum(attr); `consistency` (the twin is always strongly consistent); `multiQuery`.
|
|
77
|
-
- **namespaces**:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- **
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
- **namespaces**: deleteAll. A query on a namespace that does not exist answers 404. Listing, metadata, the schema
|
|
74
|
+
endpoints and the cache-warm hint are called by no application here ([journeys/demand.json](./journeys/demand.json))
|
|
75
|
+
and answer the gap.
|
|
76
|
+
- **auth**: a missing or empty Bearer key is 401; a key the dashboard (or the World's credential door, which issues the
|
|
77
|
+
application's `TURBOPUFFER_API_KEY`) never made, or one it expired, is 401.
|
|
78
|
+
- **no state system** (the gap): a World's writes are not performed against turbopuffer, and a real account's
|
|
79
|
+
namespaces are not observed.
|
|
83
80
|
|
|
84
81
|
Semantics a caller depends on: an attribute a document does not carry reads as null, so `NotIn`,
|
|
85
82
|
`NotEq` and `NotContainsAny` match it (Dub omits `groupId`/`country` for exactly this). A
|
|
@@ -103,16 +100,13 @@ Also the twin's own choices, not grounded in the SDK: namespace names `[A-Za-z0-
|
|
|
103
100
|
`page_size` at most 1000; `deletes` counts every requested id in `rows_deleted`; an undeclared
|
|
104
101
|
integer attribute is inferred as `int`; an attribute a row lacks projects as `null` when named in
|
|
105
102
|
`include_attributes`; a token filter whose every token is dropped (stopwords) matches nothing; id
|
|
106
|
-
comparisons order numbers before strings.
|
|
107
|
-
full-text config the twin does not model (the default `word_v4`, stemming, another language) is
|
|
108
|
-
stored, but BM25 and token filters on it are refused with a 400 naming the gap.
|
|
103
|
+
comparisons order numbers before strings.
|
|
109
104
|
|
|
110
105
|
### Planned (todo)
|
|
111
106
|
|
|
112
107
|
Refused with a 400 or 404 naming the gap, never answered with a fabricated success: conditional
|
|
113
108
|
writes (`upsert_condition` / `patch_condition` / `delete_condition`), `copy_from_namespace`,
|
|
114
|
-
`branch_from_namespace
|
|
115
|
-
synchronously, which the SDK accepts), write backpressure, metadata PATCH (pinning), explain_query,
|
|
109
|
+
`branch_from_namespace` and their asynchronous operations, `encryption`, `sharding`, write backpressure, metadata PATCH (pinning), explain_query,
|
|
116
110
|
recall, `group_by`, `compute_attributes`, `aggregate_by` together with `rank_by`, `limit.per`,
|
|
117
111
|
`rerank_by`, rank expressions beyond BM25/Sum/Max/Product, highlights, Regex / Fuzzy /
|
|
118
112
|
ContainsTokenSequence filters, tokenizers other than `word_v2` and `pre_tokenized_array` (including
|
|
@@ -120,10 +114,9 @@ the default `word_v4`, so `full_text_search: true` is refused), stemming, non-En
|
|
|
120
114
|
byte-equal BM25 scores, base64 vectors, named / multi / sparse vectors, server-side embedding, the
|
|
121
115
|
v1 namespace API older SDKs call, and the vendor's literal error strings.
|
|
122
116
|
|
|
123
|
-
The
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
through `TURBOPUFFER_BASE_URL`. The census `baseline` stays 0 until the catalog gate measures it.
|
|
117
|
+
The evidence recorded when the pack was built was the real `@turbopuffer/turbopuffer@2.8.0` client, constructed as
|
|
118
|
+
Dub constructs it, performing Dub's write → multiQuery → count → delete → deleteAll sequence against a served twin
|
|
119
|
+
through `TURBOPUFFER_BASE_URL`.
|
|
127
120
|
|
|
128
121
|
### No UI mirror
|
|
129
122
|
|
|
@@ -135,11 +128,14 @@ happen there.
|
|
|
135
128
|
|
|
136
129
|
| File | Role |
|
|
137
130
|
|---|---|
|
|
138
|
-
| `src/
|
|
139
|
-
| `src/
|
|
140
|
-
| `src/
|
|
141
|
-
| `src/
|
|
142
|
-
| `src/
|
|
143
|
-
| `src/
|
|
144
|
-
| `src/
|
|
145
|
-
| `src/
|
|
131
|
+
| `src/fetch.ts` | the region segment, the API key gate, the derived dispatch |
|
|
132
|
+
| `src/server.ts` | the discovery door, the dashboard, the server |
|
|
133
|
+
| `src/semantics/namespaces.ts` | the handlers, one per operation |
|
|
134
|
+
| `src/semantics/shared.ts` | the namespaces from the tree, the error body, a write landed |
|
|
135
|
+
| `src/engine/namespace.ts` | namespaces and documents; the pure write and query |
|
|
136
|
+
| `src/engine/filter.ts` | the filter language |
|
|
137
|
+
| `src/engine/text.ts`, `src/engine/stem.ts` | tokenizer, English stemming and BM25 |
|
|
138
|
+
| `src/screens/api-keys.tsx` | the dashboard's sign-in and API keys |
|
|
139
|
+
|
|
140
|
+
A World's writes are not performed against turbopuffer and its namespaces are not observed from it (no state system:
|
|
141
|
+
the gap).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/pack-facts.ts — current pack descriptors and explicit world-runtime server backings; no prior-artifact entries are retained.",
|
|
3
|
+
"packs": {
|
|
4
|
+
"turbopuffer": {
|
|
5
|
+
"adoption": {
|
|
6
|
+
"sdks": [
|
|
7
|
+
"@turbopuffer/turbopuffer"
|
|
8
|
+
],
|
|
9
|
+
"pypi": [
|
|
10
|
+
"turbopuffer"
|
|
11
|
+
],
|
|
12
|
+
"scopes": [
|
|
13
|
+
"@turbopuffer/"
|
|
14
|
+
],
|
|
15
|
+
"envStems": [
|
|
16
|
+
"TURBOPUFFER"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"hosts": [
|
|
20
|
+
{
|
|
21
|
+
"hostPattern": "^(?:aws|gcp|azure)-[a-z0-9-]+\\.turbopuffer\\.com$"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"host": "turbopuffer.com",
|
|
25
|
+
"pathPattern": "^/(login|dashboard|_twin/users)(/|$)"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"archetype": "crud",
|
|
29
|
+
"protocol": {
|
|
30
|
+
"declared": "3",
|
|
31
|
+
"major": 3,
|
|
32
|
+
"standing": "current"
|
|
33
|
+
},
|
|
34
|
+
"transport": "rest",
|
|
35
|
+
"resources": [
|
|
36
|
+
"namespace",
|
|
37
|
+
"document"
|
|
38
|
+
],
|
|
39
|
+
"endpointEnv": {
|
|
40
|
+
"name": "TURBOPUFFER_TWIN_URL",
|
|
41
|
+
"templates": {
|
|
42
|
+
"TURBOPUFFER_BASE_URL": "${url}/{region}"
|
|
43
|
+
},
|
|
44
|
+
"note": "grounded in @turbopuffer/turbopuffer@2.8.0 src/client.ts:253-288 (TURBOPUFFER_BASE_URL, {region} placeholder substitution) and internal/custom/fetch-node.ts (undici Agent.request under node, outside the injector); the injector also claims the regional *.turbopuffer.com hosts for fetch-based runtimes."
|
|
45
|
+
},
|
|
46
|
+
"credentialDoor": {
|
|
47
|
+
"path": "/_twin/app-credentials",
|
|
48
|
+
"body": {},
|
|
49
|
+
"fill": {
|
|
50
|
+
"TURBOPUFFER_API_KEY": "api_key"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"serveExport": "createTurbopufferTwinServer"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"undeclared": []
|
|
57
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# turbopuffer spec — provenance
|
|
2
|
+
|
|
3
|
+
- **File:** `openapi.yaml.gz`, gzip of the OpenAPI document turbopuffer's official TypeScript client was generated
|
|
4
|
+
from, the vendor's bytes unchanged inside (OpenAPI 3.1.0, `info.version` `0.0.1`).
|
|
5
|
+
- **Upstream:** the client's `.stats.yml` at tag `v2.8.0`
|
|
6
|
+
(https://github.com/turbopuffer/turbopuffer-typescript/blob/v2.8.0/.stats.yml; 2.8.0 is the version dubinc/dub pins)
|
|
7
|
+
names `openapi_spec_url`
|
|
8
|
+
https://storage.googleapis.com/stainless-sdk-openapi-specs/turbopuffer-benesch/turbopuffer-e18ebc6e8f59aa35045717e326162e1e56ac3f344771f4149e17e1104f7b1c06.yml,
|
|
9
|
+
`openapi_spec_hash` `3db106e23d2536c8a73917ebb6047b19` (the MD5 of these bytes) and 14 configured endpoints. Fetched
|
|
10
|
+
2026-09-27.
|
|
11
|
+
- **Also published:** turbopuffer's API overview names a public specification, https://github.com/turbopuffer/turbopuffer-openapi
|
|
12
|
+
(https://turbopuffer.com/docs/auth, "Specification"). Its `openapi.yml` at commit `155e59adbecaa92b00a9c66edfad419cf0fe5c17`
|
|
13
|
+
(2026-08-04), whose `.stats.yml` names the same spec hash, parses to the same document as this file (it differs
|
|
14
|
+
only in YAML formatting). That repository's head has moved on (16 endpoints, adding the asynchronous copy's start and
|
|
15
|
+
poll); the file vendored here is the one the pinned client speaks.
|
|
16
|
+
- **SHA-256 of the uncompressed document:** `245e887f04ab7c111d8ab8b0812a631a0f04f4a7804f2f04c6601d90d090a27b`.
|
|
17
|
+
- **Operation ids:** the document names none, so each operation's id is the reader's `method_path` slug
|
|
18
|
+
(`post_v2_namespaces_namespace` is the write, `post_v2_namespaces_namespace_query` the query).
|
|
19
|
+
- **Corrections:** `patches.json` (RFC 6902, each with its reason and evidence), applied before the IR. The document
|
|
20
|
+
writes three pseudo-paths, `/v2/namespaces/{namespace}?stainless_overload=branchFrom`, `...?stainless_overload=copyFrom`
|
|
21
|
+
and `/v2/namespaces/{namespace}/query?stainless_overload=multiQuery`: its generator's way of giving one path and
|
|
22
|
+
method a second client method. The client sends that query key (`src/resources/namespaces.ts` at v2.8.0), but
|
|
23
|
+
turbopuffer's documentation names no such parameter: it branches and copies with `branch_from_namespace` and
|
|
24
|
+
`copy_from_namespace` in the body of `POST /v2/namespaces/:namespace` (https://turbopuffer.com/docs/write,
|
|
25
|
+
https://turbopuffer.com/docs/branching) and posts a multi-query's `queries` to the plain query path
|
|
26
|
+
(https://turbopuffer.com/docs/query). The patches fold each pseudo-path into that operation (the write body already
|
|
27
|
+
has both fields; the copy's `dest_encryption` moves into `CopyFromNamespaceConfig`; the query takes either body and
|
|
28
|
+
answers `QueryResult` or `MultiQueryResult`), leaving 11 operations. The twin routes by path and ignores the key.
|
|
29
|
+
**Inference, not observation:** that turbopuffer's own server ignores `stainless_overload` is read from the
|
|
30
|
+
documentation, which never mentions it; no request to the real service was made.
|
|
31
|
+
- **Read by:** `bun scripts/derive-pack.ts turbopuffer`, which writes `../src/generated/`.
|
|
32
|
+
- **Pages cited:** the documentation's curl examples are rendered in the HTML pages' script payload, not their text;
|
|
33
|
+
the `.md` form of a page (https://turbopuffer.com/docs/query.md, listed in https://turbopuffer.com/llms.txt) is cited
|
|
34
|
+
where a quote comes from one.
|
|
35
|
+
- **The pages' examples** (`doc-examples.json`, 99): every example turbopuffer's documentation publishes for an
|
|
36
|
+
operation of this spec, from the API reference (Overview, Write, Query, Namespace metadata, Warm cache, List namespaces,
|
|
37
|
+
Delete namespace, Recall) and the guides (Quickstart, Full-Text Search, Branching, Embedding, Permissions, Ingestion,
|
|
38
|
+
Regions, Testing, Export, Backups, Pinning), read from each page's `.md` form (https://turbopuffer.com/llms.txt lists
|
|
39
|
+
them). An example is one request: a page's multi-language block shows one request in each client language, so it
|
|
40
|
+
counts once, by its curl form where it has one and otherwise by the request the page's client code sends (the
|
|
41
|
+
Embedding page's TypeScript, the Backups, Export, Testing, Pagination and custom-tokenization Python). The Chunking
|
|
42
|
+
page's example calls no turbopuffer operation, and the Quickstart's Connect block only sets shell variables; neither is
|
|
43
|
+
counted. The examples of Pinning (`PATCH /v1/namespaces/{namespace}/metadata`) and Recall are counted and not
|
|
44
|
+
replayed: the twin does not serve those operations.
|
|
45
|
+
- **Where the pages and this spec disagree** (each an `exampleDiffers` in `../journeys/vendor-examples.json`): the
|
|
46
|
+
metadata page's example answer names `approx_size_bytes`, `approx_num_rows` and `encryption: {"sse": true}`, where the
|
|
47
|
+
same page's Response section and this spec's `NamespaceMetadata` name `approx_logical_bytes`, `approx_row_count` and
|
|
48
|
+
`encryption.mode`; the delete page's example answers `"status": "ok"`, where this spec's delete answer is the constant
|
|
49
|
+
`"OK"`. The twin answers the spec. `remove_stopwords` defaults to `true` in this spec's description and to `false` on
|
|
50
|
+
the Write page ("Defaults to false (i.e. keep common words)") and in the January 2026 changelog; the twin answers the
|
|
51
|
+
page. The spec names an embedding's computed attribute `$embed_<attribute>`, the Write and Embedding pages
|
|
52
|
+
`embed_<attribute>`; the twin answers the pages.
|
|
53
|
+
- **APIs the pages document and this spec omits, outside the count until modelled:** the asynchronous form of a copy
|
|
54
|
+
(`Prefer: respond-async`, answered 202 with a `Location` to poll, https://turbopuffer.com/docs/auth "Asynchronous
|
|
55
|
+
requests"), and the export read `GET /v1/namespaces/{namespace}` and `HEAD /v1/namespaces/{namespace}`, which the
|
|
56
|
+
document's own header says it leaves out (deprecated in favour of paging queries and the schema).
|
|
57
|
+
- **Outside any API:** API keys are made and expired on turbopuffer's dashboard ("You can create and expire tokens in
|
|
58
|
+
the dashboard", https://turbopuffer.com/docs/auth); its audit log names the two acts (`api-key-created`,
|
|
59
|
+
`api-key-marked-as-expired`, https://turbopuffer.com/docs/audit-logs).
|