@dalgo/dalgo2typesense 0.0.0-bootstrap.0 → 0.2.1

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 CHANGED
@@ -1,5 +1,258 @@
1
- # @dalgo/dalgo2typesense — bootstrap placeholder
1
+ # @dalgo/dalgo2typesense
2
2
 
3
- This prerelease only establishes the package identity for CI trusted publishing.
4
- It contains no adapter implementation, runtime entry point, scripts or dependencies.
5
- Use the later normal release published by dal-go/dalgo-http-adapters instead.
3
+ A bounded, read-only native [Typesense](https://typesense.org) adapter for the
4
+ modern [`@dalgo/core`](https://github.com/dal-go/dalgo-js) TypeScript contracts.
5
+
6
+ It reads documents already stored in one existing Typesense collection. It does
7
+ not create collections, write documents, manage keys, expose an admin endpoint,
8
+ or require a DALgo envelope. The Typesense document `id` is always a string and
9
+ is the DALgo record key, so `"001"` and `"1"` remain different records.
10
+
11
+ This package is an independent MIT-licensed HTTP client. It does not link,
12
+ embed, copy or redistribute the Typesense server or SDK. The real-service test
13
+ fixture uses the GPL-3.0 Typesense server only as a disposable test dependency.
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { collection } from "@dalgo/core";
19
+ import { TypesenseDatabase } from "@dalgo/dalgo2typesense";
20
+
21
+ // One per-physical-request callback. A pure `documents:search` credential can
22
+ // search and read exact ids; a separately scoped read-only credential with
23
+ // `collections:get`/`list` is used by a second adapter instance when schema
24
+ // metadata is genuinely needed. There is no separate metadata option, no
25
+ // fallback and no admin upgrade.
26
+ const db = new TypesenseDatabase({
27
+ baseUrl: "https://typesense.example.com:8108",
28
+ collection: "books",
29
+ headers: () => ({ "X-TYPESENSE-API-KEY": searchKey }),
30
+ });
31
+
32
+ const books = collection<{ title: string; year: number }>("books");
33
+ const page = await db.queryWithSignal(
34
+ {
35
+ ...books.query().where("year", ">=", 1965).limit(20).build(),
36
+ projection: ["title", "year"],
37
+ },
38
+ signal,
39
+ );
40
+ ```
41
+
42
+ `queryWithSignal` is the additive shared cancellable surface. The caller signal
43
+ and one adapter deadline cover header acquisition, fetch, body reading and every
44
+ verification/page step. Verification yields a real macrotask on a bounded,
45
+ operation-wide work budget, so a caller timer can abort a large buffered
46
+ verification instead of waiting behind a microtask chain. A caller abort,
47
+ adapter deadline or `close()` rejects with the canonical `QueryCancelledError`;
48
+ the producer never copies `AbortSignal.reason`, a transport error or a response
49
+ body into the message.
50
+
51
+ ## Typed native search
52
+
53
+ Native search is a separate, typed, bounded surface. It keeps Typesense typo,
54
+ prefix and drop-token semantics, and it never accepts a raw URL, arbitrary
55
+ option bag, management endpoint, multi-search, join or RPC call.
56
+
57
+ ```ts
58
+ const result = await db.searchWithSignal({
59
+ q: "Neuromancr",
60
+ queryBy: ["title", "author"],
61
+ filterBy: "genre:=`cyberpunk`",
62
+ sortBy: [{ field: "year", direction: "desc" }],
63
+ facetBy: ["year"],
64
+ maxFacetValues: 10,
65
+ limit: 20,
66
+ }, signal);
67
+ ```
68
+
69
+ Facet values, counts and statistics are the server's real native values (an
70
+ optional server `highlighted` string is retained). A cutoff, short page or
71
+ `found` mismatch is reported as `partial`/`truncated`; it is never turned into a
72
+ clean end-of-data claim.
73
+
74
+ ### Native full-text score
75
+
76
+ An ordinary native full-text query (`q != "*"`) returns the server's uint64
77
+ `text_match` wrapper as an **exact decimal string** in
78
+ `TypesenseNativeSearchHit.textMatch`. Typesense serialises a ranking such as
79
+ `578730123373578361`, which a JavaScript `number` would round. The raw response
80
+ scanner therefore admits one unsafe integer only at the exact
81
+ `hits[].text_match` protocol path (validated as an unsigned uint64), preserves
82
+ its decimal lexeme, and cross-checks the server's
83
+ `text_match_info.score` counterpart. Every application document value, facet
84
+ identity or other JSON number still has to round-trip losslessly and is refused
85
+ before codec, record or cursor exposure. There is no generic unsafe-number
86
+ exemption and no rounding.
87
+
88
+ Every read path — structured, native, point and batch — sets
89
+ `enable_curations=false` and `filter_curated_hits=true`. A curated or pinned
90
+ document cannot bypass `filter_by`, and the request types offer no override.
91
+
92
+ ## Structured query subset
93
+
94
+ The adapter accepts the core `StructuredQuery` shape plus two approved,
95
+ provider-local additions:
96
+
97
+ ```ts
98
+ interface TypesenseQuery<T> extends StructuredQuery<T> {
99
+ where?: TugQLCondition; // ANDed with the flat filters array
100
+ projection?: readonly string[];
101
+ }
102
+ ```
103
+
104
+ Admitted:
105
+
106
+ - one unqualified top-level collection, `q=*`;
107
+ - positive scalar `==`;
108
+ - `in` over a homogeneous non-empty scalar list;
109
+ - `<`, `<=`, `>`, `>=` only when the real collection schema declares the field
110
+ as scalar `int32`, `int64` or `float`;
111
+ - bounded `AND`/`OR` groups in `where` plus flat filters;
112
+ - explicit safe sort fields (`sort_by`, at most three, no invented `id` sort);
113
+ - an exact projection of unique top-level fields.
114
+
115
+ Refused before I/O:
116
+
117
+ - joins, nested/collection-group sources, grouping, aggregation, subqueries,
118
+ computed expressions, wildcard projections and qualified fields;
119
+ - null tests and null comparisons;
120
+ - **all negative structured predicates**: `!=`, `not-in`, and any negated
121
+ group. Native negative string folding is not a safe candidate superset: it can
122
+ omit a row the exact predicate would keep, and the verifier cannot repair a
123
+ false negative;
124
+ - `array-contains` and `array-contains-any`, because the pinned TypeScript core
125
+ has no established exact array-membership contract for these operators;
126
+ - empty/control/backtick-containing filter strings and unsafe numbers.
127
+
128
+ The native filter is only a **candidate producer**. The exact verifier evaluates
129
+ the same admitted tree with the pinned TypeScript core semantics: `in` compares
130
+ the entire left operand with each item by strict equality, a missing/null
131
+ comparison is UNKNOWN, and `AND`/`OR` short-circuit with three-valued truth.
132
+ Every candidate in the complete bounded prefix is verified before any row or
133
+ cursor is exposed. Any mismatch is a typed
134
+ `TypesenseSemanticError` (also a core `UnsupportedError`), never silent local
135
+ filtering, a returned prefix, a retry or a broader scan.
136
+
137
+ Filtered reads fetch the whole candidate window from offset zero through
138
+ `offset + limit`, including a requested filtered offset, verify it, and only then
139
+ apply the offset locally. This is why a later-page mismatch cannot leave an
140
+ apparently successful earlier prefix.
141
+
142
+ An unfiltered read whose DALgo limit is larger than the configured
143
+ `maxRowsPerPage` (and the absolute 250-row Typesense page cap) is served by
144
+ bounded physical paging. If the required physical page/window work would exceed
145
+ `maxQueryPages`/`maxQueryOffset`, the query is refused before I/O rather than
146
+ silently truncated or sent as one oversized page.
147
+
148
+ ### Numeric candidates and documented differences
149
+
150
+ Typesense indexes `float` values in quantised buckets. A strict native `>` or
151
+ `<` can omit a raw value that the exact predicate would keep. Structured strict
152
+ ranges therefore use the inclusive `>=`/`<=` candidate with the **same wire
153
+ literal**, and the verifier still applies the original strict operator. A
154
+ boundary candidate that fails is a typed refusal, not a silent drop.
155
+
156
+ TypeScript compares only finite JavaScript numbers losslessly. Integers must be
157
+ safe integers; the adapter refuses non-roundtripping JSON before `JSON.parse`,
158
+ never coerces strings/BigInts into numbers, and never turns an unsafe int64 into
159
+ a `number`. Unlike the Go adapter, this is not a full int64/uint64 subset: values
160
+ such as `9007199254740993` are refused rather than represented. Arrays, objects,
161
+ booleans and null are not stringified.
162
+
163
+ The Go implementation also matches scalar `IN` against array elements; the
164
+ TypeScript core does **not**. This adapter follows TypeScript: an array-valued
165
+ left operand does not satisfy a scalar `IN`. If another `OR` branch makes the
166
+ whole condition true, the row is admitted; otherwise the row is refused. There
167
+ is no claim of Go overlap parity.
168
+
169
+ ## Point and batch reads
170
+
171
+ `get` and `getMany` use one bounded exact-id search, never a management
172
+ document endpoint and never a metadata request:
173
+
174
+ ```
175
+ filter_by=id:=[`001`,`1`]
176
+ ```
177
+
178
+ Requested key order and duplicates are preserved; an absent id is an honest
179
+ `{ key, exists: false }`. Ids containing a backtick are refused before I/O
180
+ because a Typesense filter literal cannot escape one. A cutoff, inconsistent
181
+ `found` count or budget failure is a refusal, not a false "not found".
182
+
183
+ ## Pagination and cursors
184
+
185
+ A non-empty page that may have more rows carries one opaque, adapter-issued,
186
+ single-use handle in `nextCursor.values`. The handle is bound to the endpoint,
187
+ collection, exact compiled query, projection, sort, page size, filtered/unfiltered
188
+ mode and credential scope (or the explicit non-secret `identityScope`).
189
+
190
+ - A foreign, malformed, already-used or changed-query handle is refused.
191
+ - Identity binds the **exact admitted predicate AST**, including original
192
+ operators and literal classes, not only the rendered native candidate filter.
193
+ Typesense widens `>`/`<` to `>=`/`<=`, so `>n` and `>=n` can render the same
194
+ native filter; their handles are still different and swapping them is refused.
195
+ - The handle is leased only after every remaining planning bound passes. It is
196
+ committed only after a successful page, including codec decoding. A canceled
197
+ or failed attempt releases the lease and can retry the same handle.
198
+ - A page that has more rows but whose next position would exceed a configured
199
+ offset/window/page bound is refused explicitly with
200
+ `TypesenseContinuationBoundError`; an unusable handle is never returned.
201
+ - The registry, token bytes and conserved handles are bounded.
202
+ - Filtered continuation re-verifies the entire prefix from zero on every page.
203
+
204
+ Pagination is **offset based**. It is not a snapshot: concurrent inserts,
205
+ updates, deletes or a changed sort value can move rows between pages. Typesense
206
+ does not provide a stable cursor here, and the adapter does not invent an `id`
207
+ sort or claim snapshot isolation. Use explicit sort fields where order matters.
208
+
209
+ ## Metadata
210
+
211
+ `schema()`, `listCollections()`, `testConnection()` and `describe()` make
212
+ read-only metadata requests through the same `headers` callback as every other
213
+ request. A pure `documents:search` adapter instance cannot read metadata and is
214
+ denied explicitly; a second instance with a scoped read-only credential that
215
+ carries `collections:get`/`list` can. There is no separate metadata option and no
216
+ admin fallback.
217
+
218
+ They report the real collection schema, keeping native array types such as
219
+ `string[]` in `field.type`. Typesense 30.2 always emits
220
+ `default_sorting_field`; a collection created without one reports the empty
221
+ string, which the adapter accepts as an honest sentinel rather than a malformed
222
+ schema (it is not a sorting guarantee). The implicit `id` is reported exactly
223
+ once as a non-nullable string, including when the server marks an explicitly
224
+ declared `id` as optional. A 401/403/404 is an explicit `unavailable` reason with
225
+ its status, never an empty list and never an admin fallback.
226
+
227
+ A pure `documents:search` key can search and read exact ids but cannot read
228
+ metadata. Numeric-range structured queries need readable schema to prove the
229
+ field type; without it they are refused rather than guessing.
230
+
231
+ ## Bounds and lifecycle
232
+
233
+ Every operation has one deadline, one page budget, one per-response byte bound
234
+ and one cumulative response-byte bound. Rows have a full pre-projection JSON
235
+ size bound, requests have an encoded byte bound, filters/projections/sorts/
236
+ facets have explicit maxima, and `getMany` has key and retained-byte bounds.
237
+ Exhausting a budget stops further HTTP and fails the operation; it never returns
238
+ a partial success. Removing adapter ownership is explicit: every listener the
239
+ operation registered on the container or the shared root is removed on success,
240
+ error, cancellation and `close()` alike, so completed operations are not
241
+ retained and an in-flight operation still observes `close()`.
242
+
243
+ `close()`/`dispose()` is idempotent. It cancels owned operations, retires issued
244
+ cursors and rejects new work. A caller-supplied `fetch` is never mutated; the
245
+ default is `globalThis.fetch.bind(globalThis)`, including in a real browser.
246
+
247
+ Transport requires HTTPS. Plain HTTP is accepted only for an explicit loopback
248
+ host (`localhost`, `127.0.0.1`, `[::1]`) with
249
+ `allowInsecureLoopbackHttp: true`. Redirects are refused so a credential cannot
250
+ be forwarded to another origin. Header values are validated and never logged.
251
+
252
+ ## Scope
253
+
254
+ This package is an adapter/SDK slice. It does not claim DataTug web UI, CLI or
255
+ hosted Typesense support, npm publication, or any application integration.
256
+ Direct browser execution needs a CORS-enabled deployment and an appropriately
257
+ scoped credential; short-lived scoped search keys are the intended browser
258
+ posture. A trusted Node/server runtime can keep the key server-side.
@@ -0,0 +1,185 @@
1
+ import type { AdapterReadCapabilities, NumericCapabilityBound, QueryCapabilityBounds } from "@dalgo/adapter-runtime";
2
+ /** Typesense-specific safety bounds in addition to the shared capability shape. */
3
+ export interface TypesenseQueryBounds extends QueryCapabilityBounds {
4
+ readonly maxGetManyKeys: NumericCapabilityBound;
5
+ readonly maxGetManyBytes: NumericCapabilityBound;
6
+ readonly maxRowsPerPage: NumericCapabilityBound;
7
+ readonly maxQueryPages: NumericCapabilityBound;
8
+ readonly maxQueryOffset: NumericCapabilityBound;
9
+ readonly maxTotalBytes: NumericCapabilityBound;
10
+ readonly maxRowBytes: NumericCapabilityBound;
11
+ readonly maxFilterBytes: NumericCapabilityBound;
12
+ readonly maxColumns: NumericCapabilityBound;
13
+ readonly maxSortFields: NumericCapabilityBound;
14
+ readonly maxFacetFields: NumericCapabilityBound;
15
+ readonly maxFacetValues: NumericCapabilityBound;
16
+ readonly maxCursorBytes: NumericCapabilityBound;
17
+ readonly maxIssuedCursors: NumericCapabilityBound;
18
+ }
19
+ export interface TypesenseCapabilityDescriptor extends Omit<AdapterReadCapabilities, "bounds"> {
20
+ readonly bounds: TypesenseQueryBounds;
21
+ }
22
+ /**
23
+ * Honest capabilities of the read-only native Typesense adapter.
24
+ *
25
+ * Structured filters admit only positive `==`, `in` and numeric ranges. Native
26
+ * negative filters remain available only through the separately typed native
27
+ * search, because the exact verifier cannot repair a native negative candidate
28
+ * that omits an eligible row. `native` means a typed, bounded Typesense search
29
+ * (q/query_by/filter_by/sort_by/facet_by), never a raw URL or admin escape.
30
+ */
31
+ export declare const TYPESENSE_READ_CAPABILITIES: {
32
+ readonly adapter: "@dalgo/dalgo2typesense";
33
+ readonly storage: "native-rows";
34
+ readonly read: {
35
+ readonly get: true;
36
+ readonly getMany: true;
37
+ readonly maxGetManyKeys: {
38
+ readonly default: 100;
39
+ readonly maximum: 250;
40
+ };
41
+ readonly maxParallelReads: {
42
+ readonly default: 1;
43
+ readonly maximum: 1;
44
+ };
45
+ };
46
+ readonly query: {
47
+ readonly projection: {
48
+ readonly status: "native";
49
+ readonly detail: "explicit unique top-level fields via include_fields; the native id and predicate helper fields are fetched but omitted unless explicitly requested";
50
+ };
51
+ readonly filter: {
52
+ readonly status: "native";
53
+ readonly detail: "positive scalar ==, numeric <, <=, >, >= on schema-declared scalar numeric fields, and homogeneous scalar in lists; flat filters AND where, with bounded AND/OR groups; every native candidate is exactly verified before exposure and negative operators are refused before I/O";
54
+ };
55
+ readonly sort: {
56
+ readonly status: "native";
57
+ readonly detail: "explicit simple fields and asc/desc by sort_by, up to the Typesense limit; no id sort is invented and offset pagination is not mutation-stable; an unfiltered limit above maxRowsPerPage is served by bounded physical paging or refused before I/O";
58
+ };
59
+ readonly pagination: {
60
+ readonly status: "native";
61
+ readonly detail: "adapter-issued opaque single-use cursors bound to the endpoint, collection, exact admitted predicate AST (original operator and literal class, not only the widened native filter), page size, credential scope and configuration; a lease is taken only after all planning bounds pass and committed only after a successful page; each filtered page verifies the whole candidate prefix from zero; offset continuation is documented as non-snapshot-stable";
62
+ };
63
+ readonly joins: {
64
+ readonly status: "unsupported";
65
+ };
66
+ readonly aggregation: {
67
+ readonly status: "unsupported";
68
+ };
69
+ readonly grouping: {
70
+ readonly status: "unsupported";
71
+ };
72
+ readonly subquery: {
73
+ readonly status: "unsupported";
74
+ };
75
+ readonly fulltext: {
76
+ readonly status: "native";
77
+ readonly detail: "only through the typed native search path, which keeps Typesense typo/prefix/drop-token semantics; the uint64 text_match ranking is exposed as an exact decimal string, never a rounded JavaScript number";
78
+ };
79
+ readonly timeseries: {
80
+ readonly status: "unsupported";
81
+ };
82
+ readonly native: {
83
+ readonly status: "native";
84
+ readonly detail: "typed q/query_by/filter_by/sort_by/facet_by/limit/offset search with bounded faceting; no arbitrary URL, options bag, management endpoint, multi-search, join or RPC surface";
85
+ };
86
+ readonly discovery: {
87
+ readonly status: "native";
88
+ readonly detail: "read-only collection schema via GET /collections/{name} with the same per-request header callback; a pure documents:search credential is denied explicitly and there is no separate metadata option or admin fallback; the official empty default_sorting_field sentinel is accepted";
89
+ };
90
+ };
91
+ readonly bounds: {
92
+ readonly maxQueryLimit: {
93
+ readonly default: 100;
94
+ readonly maximum: 10000;
95
+ };
96
+ readonly maxRequestBytes: {
97
+ readonly default: 1048576;
98
+ readonly maximum: 16777216;
99
+ };
100
+ readonly maxResponseBytes: {
101
+ readonly default: 1048576;
102
+ readonly maximum: 16777216;
103
+ };
104
+ readonly maxGetManyKeys: {
105
+ readonly default: 100;
106
+ readonly maximum: 250;
107
+ };
108
+ readonly maxGetManyBytes: {
109
+ readonly default: 1048576;
110
+ readonly maximum: 16777216;
111
+ };
112
+ readonly maxRowsPerPage: {
113
+ readonly default: 100;
114
+ readonly maximum: 250;
115
+ };
116
+ readonly maxQueryPages: {
117
+ readonly default: 20;
118
+ readonly maximum: 100;
119
+ };
120
+ readonly maxQueryOffset: {
121
+ readonly default: 10000;
122
+ readonly maximum: 1000000;
123
+ };
124
+ readonly maxTotalBytes: {
125
+ readonly default: 16777216;
126
+ readonly maximum: 67108864;
127
+ };
128
+ readonly maxRowBytes: {
129
+ readonly default: 1048576;
130
+ readonly maximum: 67108864;
131
+ };
132
+ readonly maxFilterBytes: {
133
+ readonly default: 8192;
134
+ readonly maximum: 65536;
135
+ };
136
+ readonly maxColumns: {
137
+ readonly default: 100;
138
+ readonly maximum: 250;
139
+ };
140
+ readonly maxSortFields: {
141
+ readonly default: 3;
142
+ readonly maximum: 3;
143
+ };
144
+ readonly maxFacetFields: {
145
+ readonly default: 5;
146
+ readonly maximum: 10;
147
+ };
148
+ readonly maxFacetValues: {
149
+ readonly default: 100;
150
+ readonly maximum: 250;
151
+ };
152
+ readonly maxCursorBytes: {
153
+ readonly default: 8192;
154
+ readonly maximum: 65536;
155
+ };
156
+ readonly maxIssuedCursors: {
157
+ readonly default: 1000;
158
+ readonly maximum: 10000;
159
+ };
160
+ readonly filterOperators: readonly ["==", "<", "<=", ">", ">=", "in"];
161
+ readonly orderDirections: readonly ["asc", "desc"];
162
+ };
163
+ };
164
+ export type TypesenseReadCapabilities = typeof TYPESENSE_READ_CAPABILITIES;
165
+ /** Actual configured bounds are reported by the instance `capabilities` getter. */
166
+ export interface TypesenseConfiguredBounds {
167
+ readonly maxGetManyKeys: number;
168
+ readonly maxGetManyBytes: number;
169
+ readonly maxParallelReads: number;
170
+ readonly maxQueryLimit: number;
171
+ readonly maxRowsPerPage: number;
172
+ readonly maxQueryPages: number;
173
+ readonly maxQueryOffset: number;
174
+ readonly maxRequestBytes: number;
175
+ readonly maxResponseBytes: number;
176
+ readonly maxTotalBytes: number;
177
+ readonly maxRowBytes: number;
178
+ readonly maxFilterBytes: number;
179
+ readonly maxColumns: number;
180
+ readonly maxSortFields: number;
181
+ readonly maxFacetFields: number;
182
+ readonly maxFacetValues: number;
183
+ readonly maxCursorBytes: number;
184
+ readonly maxIssuedCursors: number;
185
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Honest capabilities of the read-only native Typesense adapter.
3
+ *
4
+ * Structured filters admit only positive `==`, `in` and numeric ranges. Native
5
+ * negative filters remain available only through the separately typed native
6
+ * search, because the exact verifier cannot repair a native negative candidate
7
+ * that omits an eligible row. `native` means a typed, bounded Typesense search
8
+ * (q/query_by/filter_by/sort_by/facet_by), never a raw URL or admin escape.
9
+ */
10
+ export const TYPESENSE_READ_CAPABILITIES = {
11
+ adapter: "@dalgo/dalgo2typesense",
12
+ storage: "native-rows",
13
+ read: {
14
+ get: true,
15
+ getMany: true,
16
+ maxGetManyKeys: { default: 100, maximum: 250 },
17
+ maxParallelReads: { default: 1, maximum: 1 },
18
+ },
19
+ query: {
20
+ projection: {
21
+ status: "native",
22
+ detail: "explicit unique top-level fields via include_fields; the native id and predicate helper fields are fetched but omitted unless explicitly requested",
23
+ },
24
+ filter: {
25
+ status: "native",
26
+ detail: "positive scalar ==, numeric <, <=, >, >= on schema-declared scalar numeric fields, and homogeneous scalar in lists; flat filters AND where, with bounded AND/OR groups; every native candidate is exactly verified before exposure and negative operators are refused before I/O",
27
+ },
28
+ sort: {
29
+ status: "native",
30
+ detail: "explicit simple fields and asc/desc by sort_by, up to the Typesense limit; no id sort is invented and offset pagination is not mutation-stable; an unfiltered limit above maxRowsPerPage is served by bounded physical paging or refused before I/O",
31
+ },
32
+ pagination: {
33
+ status: "native",
34
+ detail: "adapter-issued opaque single-use cursors bound to the endpoint, collection, exact admitted predicate AST (original operator and literal class, not only the widened native filter), page size, credential scope and configuration; a lease is taken only after all planning bounds pass and committed only after a successful page; each filtered page verifies the whole candidate prefix from zero; offset continuation is documented as non-snapshot-stable",
35
+ },
36
+ joins: { status: "unsupported" },
37
+ aggregation: { status: "unsupported" },
38
+ grouping: { status: "unsupported" },
39
+ subquery: { status: "unsupported" },
40
+ fulltext: {
41
+ status: "native",
42
+ detail: "only through the typed native search path, which keeps Typesense typo/prefix/drop-token semantics; the uint64 text_match ranking is exposed as an exact decimal string, never a rounded JavaScript number",
43
+ },
44
+ timeseries: { status: "unsupported" },
45
+ native: {
46
+ status: "native",
47
+ detail: "typed q/query_by/filter_by/sort_by/facet_by/limit/offset search with bounded faceting; no arbitrary URL, options bag, management endpoint, multi-search, join or RPC surface",
48
+ },
49
+ discovery: {
50
+ status: "native",
51
+ detail: "read-only collection schema via GET /collections/{name} with the same per-request header callback; a pure documents:search credential is denied explicitly and there is no separate metadata option or admin fallback; the official empty default_sorting_field sentinel is accepted",
52
+ },
53
+ },
54
+ bounds: {
55
+ maxQueryLimit: { default: 100, maximum: 10_000 },
56
+ maxRequestBytes: { default: 1_048_576, maximum: 16_777_216 },
57
+ maxResponseBytes: { default: 1_048_576, maximum: 16_777_216 },
58
+ maxGetManyKeys: { default: 100, maximum: 250 },
59
+ maxGetManyBytes: { default: 1_048_576, maximum: 16_777_216 },
60
+ maxRowsPerPage: { default: 100, maximum: 250 },
61
+ maxQueryPages: { default: 20, maximum: 100 },
62
+ maxQueryOffset: { default: 10_000, maximum: 1_000_000 },
63
+ maxTotalBytes: { default: 16_777_216, maximum: 67_108_864 },
64
+ maxRowBytes: { default: 1_048_576, maximum: 67_108_864 },
65
+ maxFilterBytes: { default: 8_192, maximum: 65_536 },
66
+ maxColumns: { default: 100, maximum: 250 },
67
+ maxSortFields: { default: 3, maximum: 3 },
68
+ maxFacetFields: { default: 5, maximum: 10 },
69
+ maxFacetValues: { default: 100, maximum: 250 },
70
+ maxCursorBytes: { default: 8_192, maximum: 65_536 },
71
+ maxIssuedCursors: { default: 1_000, maximum: 10_000 },
72
+ filterOperators: ["==", "<", "<=", ">", ">=", "in"],
73
+ orderDirections: ["asc", "desc"],
74
+ },
75
+ };
76
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { type CancellableQueryExecutor } from "@dalgo/adapter-runtime";
2
+ import { Key, type Codec, type Database, type QueryPage, type ReadwriteTransaction, type RecordSnapshot, type StructuredQuery } from "@dalgo/core";
3
+ import { type TypesenseCapabilityDescriptor, type TypesenseQueryBounds } from "./capabilities.js";
4
+ import { type TypesenseCollectionDiscovery, type TypesenseSchemaDiscovery } from "./metadata.js";
5
+ import { type TypesenseNativeSearchRequest, type TypesenseNativeSearchResult } from "./search.js";
6
+ import { type TypesenseFetch, type TypesenseHeaderProvider } from "./transport.js";
7
+ export interface TypesenseDatabaseOptions {
8
+ /** Typesense origin, for example `https://typesense.example.com:8108`. */
9
+ readonly baseUrl: string;
10
+ /** Allows plain HTTP only for `localhost`, `127.0.0.1` or `[::1]`. */
11
+ readonly allowInsecureLoopbackHttp?: boolean;
12
+ /** The one existing collection this database exposes. */
13
+ readonly collection: string;
14
+ /** Re-evaluated for every physical request; must include X-TYPESENSE-API-KEY. */
15
+ readonly headers?: TypesenseHeaderProvider;
16
+ /** Stable non-secret identity label bound into cursors instead of header digests. */
17
+ readonly identityScope?: string;
18
+ readonly fetch?: TypesenseFetch;
19
+ readonly timeoutMs?: number;
20
+ readonly maxRequestBytes?: number;
21
+ readonly maxResponseBytes?: number;
22
+ readonly maxTotalBytes?: number;
23
+ readonly maxRowBytes?: number;
24
+ readonly maxGetManyKeys?: number;
25
+ readonly maxGetManyBytes?: number;
26
+ readonly maxQueryLimit?: number;
27
+ readonly maxRowsPerPage?: number;
28
+ readonly maxQueryPages?: number;
29
+ readonly maxQueryOffset?: number;
30
+ readonly maxFilterBytes?: number;
31
+ readonly maxColumns?: number;
32
+ readonly maxSortFields?: number;
33
+ readonly maxFacetFields?: number;
34
+ readonly maxFacetValues?: number;
35
+ readonly maxCursorBytes?: number;
36
+ readonly maxIssuedCursors?: number;
37
+ }
38
+ export interface TypesenseConnectionTest {
39
+ readonly ok: boolean;
40
+ readonly collection: string;
41
+ readonly status: "connected" | "unauthorized" | "forbidden" | "not-found" | "unreachable" | "metadata-malformed";
42
+ readonly httpStatus?: number;
43
+ }
44
+ export interface TypesenseDescribe {
45
+ readonly adapter: "@dalgo/dalgo2typesense";
46
+ readonly storage: "native-rows";
47
+ readonly readOnly: true;
48
+ readonly endpoint: string;
49
+ readonly collection: string;
50
+ readonly schema: TypesenseSchemaDiscovery;
51
+ readonly limits: TypesenseQueryBounds;
52
+ readonly capabilities: TypesenseCapabilityDescriptor;
53
+ }
54
+ /**
55
+ * Read-only bounded adapter over one existing Typesense collection.
56
+ *
57
+ * Documents are read natively: the string Typesense `id` is the DALgo key and
58
+ * no DALgo envelope is required or written. Structured queries use a positive
59
+ * native candidate filter plus an exact verifier over the complete bounded
60
+ * candidate prefix. Every read disables curations and filters curated hits.
61
+ */
62
+ export declare class TypesenseDatabase implements Database, CancellableQueryExecutor {
63
+ #private;
64
+ readonly readOnly: true;
65
+ constructor(options: TypesenseDatabaseOptions);
66
+ get collection(): string;
67
+ get endpoint(): string;
68
+ get limits(): TypesenseQueryBounds;
69
+ get capabilities(): TypesenseCapabilityDescriptor;
70
+ get<T>(key: Key, codec?: Codec<T>): Promise<RecordSnapshot<T>>;
71
+ getMany<T>(keys: readonly Key[], codec?: Codec<T>): Promise<readonly RecordSnapshot<T>[]>;
72
+ getManyWithSignal<T>(keys: readonly Key[], signal: AbortSignal, codec?: Codec<T>): Promise<readonly RecordSnapshot<T>[]>;
73
+ query<T>(query: StructuredQuery<T>): Promise<QueryPage<T>>;
74
+ queryWithSignal<T>(query: StructuredQuery<T>, signal: AbortSignal): Promise<QueryPage<T>>;
75
+ search(request: TypesenseNativeSearchRequest): Promise<TypesenseNativeSearchResult>;
76
+ searchWithSignal(request: TypesenseNativeSearchRequest, signal: AbortSignal): Promise<TypesenseNativeSearchResult>;
77
+ listCollections(signal?: AbortSignal): Promise<TypesenseCollectionDiscovery>;
78
+ schema(signal?: AbortSignal): Promise<TypesenseSchemaDiscovery>;
79
+ testConnection(signal?: AbortSignal): Promise<TypesenseConnectionTest>;
80
+ describe(signal?: AbortSignal): Promise<TypesenseDescribe>;
81
+ /** Cancels every owned operation. Idempotent. */
82
+ close(): void;
83
+ /** Alias kept for callers that use a disposal vocabulary. */
84
+ dispose(): void;
85
+ runReadwriteTransaction<Result>(callback: (transaction: ReadwriteTransaction) => Promise<Result>): Promise<Result>;
86
+ }