@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 +257 -4
- package/dist/capabilities.d.ts +185 -0
- package/dist/capabilities.js +76 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/database.d.ts +86 -0
- package/dist/database.js +1050 -0
- package/dist/database.js.map +1 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +63 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +32 -0
- package/dist/json.js +222 -0
- package/dist/json.js.map +1 -0
- package/dist/metadata.d.ts +54 -0
- package/dist/metadata.js +159 -0
- package/dist/metadata.js.map +1 -0
- package/dist/search.d.ts +82 -0
- package/dist/search.js +238 -0
- package/dist/search.js.map +1 -0
- package/dist/structured.d.ts +79 -0
- package/dist/structured.js +399 -0
- package/dist/structured.js.map +1 -0
- package/dist/transport.d.ts +51 -0
- package/dist/transport.js +317 -0
- package/dist/transport.js.map +1 -0
- package/package.json +50 -7
- package/LICENSE +0 -22
package/README.md
CHANGED
|
@@ -1,5 +1,258 @@
|
|
|
1
|
-
# @dalgo/dalgo2typesense
|
|
1
|
+
# @dalgo/dalgo2typesense
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
+
}
|