@volter/twin-turbopuffer 0.1.37 → 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.
Files changed (79) hide show
  1. package/README.md +25 -29
  2. package/dist/generated/pack-facts.json +57 -0
  3. package/dist/spec/SOURCE.md +59 -0
  4. package/dist/spec/doc-examples.json +11120 -0
  5. package/dist/spec/openapi.yaml.gz +0 -0
  6. package/dist/spec/patches.json +308 -0
  7. package/dist/spec/sources.json +53 -0
  8. package/dist/src/cli.js +8 -18
  9. package/dist/src/{turbopuffer-filter.d.ts → engine/filter.d.ts} +1 -1
  10. package/dist/src/{turbopuffer-filter.js → engine/filter.js} +1 -1
  11. package/dist/src/{turbopuffer-store.d.ts → engine/namespace.d.ts} +3 -16
  12. package/dist/src/{turbopuffer-store.js → engine/namespace.js} +15 -71
  13. package/dist/src/{turbopuffer-text.js → engine/text.js} +1 -1
  14. package/dist/src/fetch.d.ts +2 -0
  15. package/dist/src/fetch.js +19 -0
  16. package/dist/src/generated/surface.gen.json +1 -1
  17. package/dist/src/generated/ui.gen.json +1 -1
  18. package/dist/src/index.d.ts +5 -11
  19. package/dist/src/index.js +8 -65
  20. package/dist/src/manifest.js +67 -1
  21. package/dist/src/screens/dashboard.d.ts +4 -11
  22. package/dist/src/screens/dashboard.js +25 -71
  23. package/dist/src/semantics/around.d.ts +2 -0
  24. package/dist/src/semantics/around.js +30 -0
  25. package/dist/src/semantics/doors.d.ts +9 -0
  26. package/dist/src/semantics/doors.js +31 -0
  27. package/dist/src/semantics/index.d.ts +2 -0
  28. package/dist/src/semantics/index.js +6 -0
  29. package/dist/src/semantics/namespaces.d.ts +8 -5
  30. package/dist/src/semantics/namespaces.js +97 -16
  31. package/dist/src/semantics/shared.d.ts +24 -0
  32. package/dist/src/semantics/shared.js +57 -0
  33. package/dist/src/server.d.ts +7 -0
  34. package/dist/src/server.js +7 -0
  35. package/generated/pack-facts.json +57 -0
  36. package/package.json +9 -16
  37. package/spec/SOURCE.md +59 -0
  38. package/spec/doc-examples.json +11120 -0
  39. package/spec/openapi.yaml.gz +0 -0
  40. package/spec/patches.json +308 -0
  41. package/spec/sources.json +53 -0
  42. package/src/cli.ts +8 -18
  43. package/src/{turbopuffer-filter.ts → engine/filter.ts} +1 -1
  44. package/src/{turbopuffer-store.ts → engine/namespace.ts} +17 -67
  45. package/src/{turbopuffer-text.ts → engine/text.ts} +1 -1
  46. package/src/fetch.ts +20 -0
  47. package/src/generated/surface.gen.json +1 -1
  48. package/src/generated/ui.gen.json +1 -1
  49. package/src/index.ts +8 -90
  50. package/src/manifest.ts +76 -9
  51. package/src/screens/dashboard.tsx +28 -73
  52. package/src/semantics/around.ts +28 -0
  53. package/src/semantics/doors.ts +33 -0
  54. package/src/semantics/index.ts +10 -0
  55. package/src/semantics/namespaces.ts +104 -29
  56. package/src/semantics/shared.ts +63 -0
  57. package/src/server.ts +8 -0
  58. package/dist/src/key-gate.d.ts +0 -3
  59. package/dist/src/key-gate.js +0 -39
  60. package/dist/src/turbopuffer-capabilities.d.ts +0 -6
  61. package/dist/src/turbopuffer-capabilities.js +0 -442
  62. package/dist/src/turbopuffer-conformance.d.ts +0 -8
  63. package/dist/src/turbopuffer-conformance.js +0 -102
  64. package/dist/src/turbopuffer-connector.d.ts +0 -34
  65. package/dist/src/turbopuffer-connector.js +0 -152
  66. package/dist/src/turbopuffer-server.d.ts +0 -23
  67. package/dist/src/turbopuffer-server.js +0 -72
  68. package/dist/src/turbopuffer-twin.d.ts +0 -25
  69. package/dist/src/turbopuffer-twin.js +0 -406
  70. package/src/key-gate.ts +0 -39
  71. package/src/turbopuffer-capabilities.ts +0 -455
  72. package/src/turbopuffer-conformance.ts +0 -101
  73. package/src/turbopuffer-connector.ts +0 -153
  74. package/src/turbopuffer-server.ts +0 -81
  75. package/src/turbopuffer-twin.ts +0 -429
  76. /package/dist/src/{turbopuffer-stem.d.ts → engine/stem.d.ts} +0 -0
  77. /package/dist/src/{turbopuffer-stem.js → engine/stem.js} +0 -0
  78. /package/dist/src/{turbopuffer-text.d.ts → engine/text.d.ts} +0 -0
  79. /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-*`, `Prefer: respond-async` | `src/client.ts:858-864`, `src/internal/custom/respond-async.ts` |
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 (each has a `done` capability with a verify in `src/turbopuffer-capabilities.ts`):
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**: list (prefix, cursor paging), metadata, schema read and update, cache-warm hint,
78
- deleteAll. A query, metadata or schema read on a namespace that does not exist answers 404.
79
- - **auth**: a missing or empty Bearer key is 401; `--token` makes the twin demand one exact key.
80
- - **real state system**: `performTurbopufferAction` replays a recorded write / deleteAll / schema
81
- update against the vendor; `syncTurbopufferFromRemote` observes namespaces, schema and documents
82
- (paged by id) from a real account.
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. A namespace refreshed from a real account whose
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`, `encryption`, `sharding`, `respond-async` polling (the twin answers
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 capability verifies were written but not run in the build that added this pack; the evidence
124
- recorded there was the real `@turbopuffer/turbopuffer@2.8.0` client, constructed as Dub constructs
125
- it, performing Dub's write → multiQuery → count → delete → deleteAll sequence against a served twin
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/turbopuffer-twin.ts` | routing, auth, the error envelope, kernel reads and writes |
139
- | `src/turbopuffer-store.ts` | namespaces and documents as tree subjects; pure write and query |
140
- | `src/turbopuffer-filter.ts` | the filter language |
141
- | `src/turbopuffer-text.ts` | tokenizer and BM25 |
142
- | `src/turbopuffer-server.ts` | the fetch adapter and server |
143
- | `src/turbopuffer-connector.ts` | perform and refresh adapters |
144
- | `src/turbopuffer-capabilities.ts` | the capability manifest |
145
- | `src/turbopuffer-conformance.ts` | endpoint probes and router census |
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).