@xcitedbs/client 0.2.11 → 0.2.13
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 +22 -0
- package/dist/client.d.ts +31 -6
- package/dist/client.js +79 -11
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +37 -2
- package/llms-full.txt +60 -20
- package/llms.txt +21 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,19 @@ const docs = await client.queryByIdentifier('/test1', 'FirstMatch');
|
|
|
34
34
|
await client.put('app.settings', { theme: 'dark' });
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
### Full-text search (temporal)
|
|
38
|
+
|
|
39
|
+
`client.search()` accepts `TextSearchQuery` with optional **`at_date`** (point-in-time), **`date_from`** / **`date_to`** (range overlap), and **`mode`: `'fts'`** for pure keyword search. Omit temporal fields for the default “current” posting view. Hits may include **`valid_from`** / **`valid_to`** (7-character internal date keys returned by the server).
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
await client.search({
|
|
43
|
+
query: 'installation guide',
|
|
44
|
+
mode: 'fts',
|
|
45
|
+
at_date: '2024-06-01',
|
|
46
|
+
limit: 20,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
37
50
|
### WebSocket
|
|
38
51
|
|
|
39
52
|
```typescript
|
|
@@ -45,6 +58,15 @@ client.subscribe(
|
|
|
45
58
|
|
|
46
59
|
With JWT in browsers, tokens are passed as `access_token` query parameter on the WebSocket URL. API keys can use `api_key` query parameter.
|
|
47
60
|
|
|
61
|
+
## Test sessions (ephemeral and overlay)
|
|
62
|
+
|
|
63
|
+
Call **`POST /api/v1/test/sessions`** with your normal API key or Bearer (same project context as usual). Use **`XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`** to get a client that sends **`X-Test-Session`** on requests.
|
|
64
|
+
|
|
65
|
+
- **Default:** isolated empty LMDB under the server’s `_test/<uuid>/` (writes never touch production).
|
|
66
|
+
- **Overlay:** pass **`overlay: true`** in **`createTestSession`** options (or **`POST`** body **`{"overlay":true}`**). The server layers a writable LMDB on top of the **current project’s on-disk data opened read-only** so you can debug against real data; changes still live only under `_test/<uuid>/`. Use project-scoped credentials or platform Bearer + **`X-Project-Id`** as when calling production APIs.
|
|
67
|
+
|
|
68
|
+
Tear down with **`destroyTestSession()`** (or **`DELETE /api/v1/test/sessions/current`** with the session header). See **`llms.txt`** / **`llms-full.txt`** in this package for full behavior, limits, and auth notes.
|
|
69
|
+
|
|
48
70
|
## Build
|
|
49
71
|
|
|
50
72
|
```bash
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, PublishResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, XCiteQuery, UserIsolationConfig } from './types';
|
|
1
|
+
import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, PublishResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, XCiteQuery, UserIsolationConfig } from './types';
|
|
2
2
|
import { WebSocketSubscription } from './websocket';
|
|
3
3
|
export declare class XCiteDBClient {
|
|
4
4
|
private baseUrl;
|
|
@@ -18,10 +18,13 @@ export declare class XCiteDBClient {
|
|
|
18
18
|
private testRequireAuth?;
|
|
19
19
|
private userIsolation?;
|
|
20
20
|
private cachedAppUserId?;
|
|
21
|
+
private readonly requestTimeoutMs?;
|
|
21
22
|
constructor(options: XCiteDBClientOptions);
|
|
22
23
|
/**
|
|
23
|
-
* Create an ephemeral
|
|
24
|
+
* Create an ephemeral test database: calls `POST /api/v1/test/sessions` with your API key or Bearer,
|
|
24
25
|
* then returns a client that sends `X-Test-Session` (auth-free by default).
|
|
26
|
+
* With `opts.overlay === true`, the server stores overlay mode: reads merge the empty `_test/...` LMDB
|
|
27
|
+
* over the current project's production data (read-only base); writes stay under `_test/...` only.
|
|
25
28
|
*/
|
|
26
29
|
static createTestSession(opts: CreateTestSessionOptions): Promise<XCiteDBClient>;
|
|
27
30
|
/**
|
|
@@ -433,12 +436,18 @@ export declare class XCiteDBClient {
|
|
|
433
436
|
queryLog(query: XCiteQuery, fromDate: string, toDate: string): Promise<LogEntry[]>;
|
|
434
437
|
addMeta(identifier: string, value: unknown, path?: string, opts?: {
|
|
435
438
|
mode?: 'set' | 'append';
|
|
439
|
+
overwrite?: boolean;
|
|
436
440
|
}): Promise<boolean>;
|
|
437
441
|
addMetaByQuery(query: XCiteQuery, value: unknown, path?: string, firstMatch?: boolean, opts?: {
|
|
438
442
|
mode?: 'set' | 'append';
|
|
443
|
+
overwrite?: boolean;
|
|
444
|
+
}): Promise<boolean>;
|
|
445
|
+
appendMeta(identifier: string, value: unknown, path?: string, opts?: {
|
|
446
|
+
overwrite?: boolean;
|
|
447
|
+
}): Promise<boolean>;
|
|
448
|
+
appendMetaByQuery(query: XCiteQuery, value: unknown, path?: string, firstMatch?: boolean, opts?: {
|
|
449
|
+
overwrite?: boolean;
|
|
439
450
|
}): Promise<boolean>;
|
|
440
|
-
appendMeta(identifier: string, value: unknown, path?: string): Promise<boolean>;
|
|
441
|
-
appendMetaByQuery(query: XCiteQuery, value: unknown, path?: string, firstMatch?: boolean): Promise<boolean>;
|
|
442
451
|
queryMeta<T = MetaValue>(identifier: string, path?: string): Promise<T>;
|
|
443
452
|
queryMetaByQuery<T = MetaValue>(query: XCiteQuery, path?: string): Promise<T>;
|
|
444
453
|
clearMeta(query: XCiteQuery): Promise<boolean>;
|
|
@@ -484,6 +493,18 @@ export declare class XCiteDBClient {
|
|
|
484
493
|
getProjectSearchSettings(): Promise<ProjectSearchSettings>;
|
|
485
494
|
/** Update project search settings (`PUT /api/v1/project/settings/search`). Returns the same shape as GET. */
|
|
486
495
|
updateProjectSearchSettings(patch: ProjectSearchSettingsUpdate): Promise<ProjectSearchSettings>;
|
|
496
|
+
/** Per-project `document.conf` override (`GET /api/v1/project/settings/doc-conf`). */
|
|
497
|
+
getProjectDocConf(): Promise<ProjectDocConfResponse>;
|
|
498
|
+
/** Save or clear project `document.conf` (`PUT /api/v1/project/settings/doc-conf`). */
|
|
499
|
+
updateProjectDocConf(body: {
|
|
500
|
+
doc_conf_text: string;
|
|
501
|
+
} | {
|
|
502
|
+
clear: true;
|
|
503
|
+
}): Promise<ProjectDocConfResponse>;
|
|
504
|
+
/** Remove project override; server uses platform default (`DELETE /api/v1/project/settings/doc-conf`). */
|
|
505
|
+
deleteProjectDocConf(): Promise<ProjectDocConfResponse>;
|
|
506
|
+
/** Embedded platform default `document.conf` text (`GET /api/v1/platform/default-doc-conf`). */
|
|
507
|
+
getPlatformDefaultDocConf(): Promise<PlatformDefaultDocConfResponse>;
|
|
487
508
|
/** Blocking full DB scan (admin; no calls to embedding API). Prefer {@link postVectorIndexEstimateSession} for UI. */
|
|
488
509
|
getVectorIndexEstimate(): Promise<VectorIndexEstimate>;
|
|
489
510
|
/** Start background estimate (202); cancel prior session for this tenant. */
|
|
@@ -502,12 +523,16 @@ export declare class XCiteDBClient {
|
|
|
502
523
|
* The final event has `done: true` and may include `sources`.
|
|
503
524
|
*/
|
|
504
525
|
ragQueryStream(options: Omit<RagQueryOptions, 'stream'>, onEvent: (ev: RagStreamEvent) => void): Promise<void>;
|
|
505
|
-
writeJsonDocument(identifier: string, data: unknown
|
|
526
|
+
writeJsonDocument(identifier: string, data: unknown, opts?: {
|
|
527
|
+
overwrite?: boolean;
|
|
528
|
+
}): Promise<void>;
|
|
506
529
|
readJsonDocument<T = unknown>(identifier: string): Promise<T>;
|
|
507
530
|
deleteJsonDocument(identifier: string): Promise<void>;
|
|
508
531
|
listJsonDocuments(match?: string, limit?: number, offset?: number): Promise<ListIdentifiersResult>;
|
|
509
532
|
/** JSON document shorthand — same as {@link writeJsonDocument}. */
|
|
510
|
-
put(identifier: string, data: unknown
|
|
533
|
+
put(identifier: string, data: unknown, opts?: {
|
|
534
|
+
overwrite?: boolean;
|
|
535
|
+
}): Promise<void>;
|
|
511
536
|
/** JSON document read — same as {@link readJsonDocument}. */
|
|
512
537
|
get<T = unknown>(identifier: string): Promise<T>;
|
|
513
538
|
/** JSON document delete — same as {@link deleteJsonDocument}. */
|
package/dist/client.js
CHANGED
|
@@ -18,6 +18,30 @@ function buildQuery(params) {
|
|
|
18
18
|
const s = sp.toString();
|
|
19
19
|
return s ? `?${s}` : '';
|
|
20
20
|
}
|
|
21
|
+
function warnIfHttpOnTlsPort(baseUrl) {
|
|
22
|
+
try {
|
|
23
|
+
const u = new URL(baseUrl);
|
|
24
|
+
if (u.protocol !== 'http:')
|
|
25
|
+
return;
|
|
26
|
+
if (u.port === '443') {
|
|
27
|
+
if (typeof console !== 'undefined' && typeof console.warn === 'function') {
|
|
28
|
+
console.warn('[@xcitedbs/client] baseUrl uses http: on port 443; use https:// to avoid hangs or TLS errors.');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* ignore invalid baseUrl */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Uses `AbortSignal.timeout` when the runtime supports it. */
|
|
37
|
+
function requestTimeoutSignal(ms) {
|
|
38
|
+
if (ms === undefined || ms <= 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
const ctor = AbortSignal;
|
|
41
|
+
if (typeof ctor.timeout === 'function')
|
|
42
|
+
return ctor.timeout(ms);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
21
45
|
class XCiteDBClient {
|
|
22
46
|
constructor(options) {
|
|
23
47
|
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
@@ -34,10 +58,14 @@ class XCiteDBClient {
|
|
|
34
58
|
this.testSessionToken = options.testSessionToken;
|
|
35
59
|
this.testRequireAuth = options.testRequireAuth === true;
|
|
36
60
|
this.userIsolation = options.userIsolation;
|
|
61
|
+
this.requestTimeoutMs = options.requestTimeoutMs;
|
|
62
|
+
warnIfHttpOnTlsPort(this.baseUrl);
|
|
37
63
|
}
|
|
38
64
|
/**
|
|
39
|
-
* Create an ephemeral
|
|
65
|
+
* Create an ephemeral test database: calls `POST /api/v1/test/sessions` with your API key or Bearer,
|
|
40
66
|
* then returns a client that sends `X-Test-Session` (auth-free by default).
|
|
67
|
+
* With `opts.overlay === true`, the server stores overlay mode: reads merge the empty `_test/...` LMDB
|
|
68
|
+
* over the current project's production data (read-only base); writes stay under `_test/...` only.
|
|
41
69
|
*/
|
|
42
70
|
static async createTestSession(opts) {
|
|
43
71
|
const temp = new XCiteDBClient({
|
|
@@ -53,8 +81,9 @@ class XCiteDBClient {
|
|
|
53
81
|
onAppUserTokensUpdated: opts.onAppUserTokensUpdated,
|
|
54
82
|
onSessionInvalid: opts.onSessionInvalid,
|
|
55
83
|
userIsolation: opts.userIsolation,
|
|
84
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
56
85
|
});
|
|
57
|
-
const data = await temp.request('POST', '/api/v1/test/sessions', undefined, undefined, { no401Retry: true });
|
|
86
|
+
const data = await temp.request('POST', '/api/v1/test/sessions', opts.overlay === true ? { overlay: true } : undefined, undefined, { no401Retry: true });
|
|
58
87
|
return new XCiteDBClient({
|
|
59
88
|
baseUrl: opts.baseUrl,
|
|
60
89
|
apiKey: opts.testRequireAuth ? opts.apiKey : undefined,
|
|
@@ -70,6 +99,7 @@ class XCiteDBClient {
|
|
|
70
99
|
testSessionToken: data.session_token,
|
|
71
100
|
testRequireAuth: opts.testRequireAuth,
|
|
72
101
|
userIsolation: opts.userIsolation,
|
|
102
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
73
103
|
});
|
|
74
104
|
}
|
|
75
105
|
/**
|
|
@@ -444,6 +474,9 @@ class XCiteDBClient {
|
|
|
444
474
|
init.body = JSON.stringify(body);
|
|
445
475
|
}
|
|
446
476
|
}
|
|
477
|
+
const sig = requestTimeoutSignal(this.requestTimeoutMs);
|
|
478
|
+
if (sig)
|
|
479
|
+
init.signal = sig;
|
|
447
480
|
const res = await fetch(url, init);
|
|
448
481
|
const text = await res.text();
|
|
449
482
|
let data;
|
|
@@ -1319,6 +1352,8 @@ class XCiteDBClient {
|
|
|
1319
1352
|
const body = { identifier: this.isoPrefixId(identifier), value, path };
|
|
1320
1353
|
if (opts?.mode === 'append')
|
|
1321
1354
|
body.mode = 'append';
|
|
1355
|
+
if (opts?.overwrite)
|
|
1356
|
+
body.overwrite = true;
|
|
1322
1357
|
const r = await this.request('POST', '/api/v1/meta', body);
|
|
1323
1358
|
return r?.ok !== false;
|
|
1324
1359
|
}
|
|
@@ -1331,14 +1366,16 @@ class XCiteDBClient {
|
|
|
1331
1366
|
};
|
|
1332
1367
|
if (opts?.mode === 'append')
|
|
1333
1368
|
body.mode = 'append';
|
|
1369
|
+
if (opts?.overwrite)
|
|
1370
|
+
body.overwrite = true;
|
|
1334
1371
|
const r = await this.request('POST', '/api/v1/meta', body);
|
|
1335
1372
|
return r?.ok !== false;
|
|
1336
1373
|
}
|
|
1337
|
-
async appendMeta(identifier, value, path = '') {
|
|
1338
|
-
return this.addMeta(identifier, value, path, { mode: 'append' });
|
|
1374
|
+
async appendMeta(identifier, value, path = '', opts) {
|
|
1375
|
+
return this.addMeta(identifier, value, path, { mode: 'append', ...opts });
|
|
1339
1376
|
}
|
|
1340
|
-
async appendMetaByQuery(query, value, path = '', firstMatch = false) {
|
|
1341
|
-
return this.addMetaByQuery(query, value, path, firstMatch, { mode: 'append' });
|
|
1377
|
+
async appendMetaByQuery(query, value, path = '', firstMatch = false, opts) {
|
|
1378
|
+
return this.addMetaByQuery(query, value, path, firstMatch, { mode: 'append', ...opts });
|
|
1342
1379
|
}
|
|
1343
1380
|
async queryMeta(identifier, path = '') {
|
|
1344
1381
|
return this.request('GET', `/api/v1/meta${buildQuery({ identifier: this.isoPrefixId(identifier), path })}`);
|
|
@@ -1410,12 +1447,18 @@ class XCiteDBClient {
|
|
|
1410
1447
|
body.offset = q.offset;
|
|
1411
1448
|
if (q.limit !== undefined)
|
|
1412
1449
|
body.limit = q.limit;
|
|
1413
|
-
if (q.mode)
|
|
1450
|
+
if (q.mode !== undefined)
|
|
1414
1451
|
body.mode = q.mode;
|
|
1415
1452
|
if (q.min_score !== undefined)
|
|
1416
1453
|
body.min_score = q.min_score;
|
|
1417
1454
|
if (q.semantic_weight !== undefined)
|
|
1418
1455
|
body.semantic_weight = q.semantic_weight;
|
|
1456
|
+
if (q.at_date !== undefined && q.at_date !== '')
|
|
1457
|
+
body.at_date = q.at_date;
|
|
1458
|
+
if (q.date_from !== undefined && q.date_from !== '')
|
|
1459
|
+
body.date_from = q.date_from;
|
|
1460
|
+
if (q.date_to !== undefined && q.date_to !== '')
|
|
1461
|
+
body.date_to = q.date_to;
|
|
1419
1462
|
const data = await this.request('POST', '/api/v1/search', body);
|
|
1420
1463
|
const hits = [];
|
|
1421
1464
|
if (Array.isArray(data.hits)) {
|
|
@@ -1436,6 +1479,12 @@ class XCiteDBClient {
|
|
|
1436
1479
|
if (o.source === 'fts' || o.source === 'semantic' || o.source === 'both') {
|
|
1437
1480
|
hit.source = o.source;
|
|
1438
1481
|
}
|
|
1482
|
+
if (typeof o.valid_from === 'string' && o.valid_from.length > 0) {
|
|
1483
|
+
hit.valid_from = o.valid_from;
|
|
1484
|
+
}
|
|
1485
|
+
if (typeof o.valid_to === 'string' && o.valid_to.length > 0) {
|
|
1486
|
+
hit.valid_to = o.valid_to;
|
|
1487
|
+
}
|
|
1439
1488
|
hits.push(hit);
|
|
1440
1489
|
}
|
|
1441
1490
|
}
|
|
@@ -1460,6 +1509,22 @@ class XCiteDBClient {
|
|
|
1460
1509
|
async updateProjectSearchSettings(patch) {
|
|
1461
1510
|
return this.request('PUT', '/api/v1/project/settings/search', patch);
|
|
1462
1511
|
}
|
|
1512
|
+
/** Per-project `document.conf` override (`GET /api/v1/project/settings/doc-conf`). */
|
|
1513
|
+
async getProjectDocConf() {
|
|
1514
|
+
return this.request('GET', '/api/v1/project/settings/doc-conf');
|
|
1515
|
+
}
|
|
1516
|
+
/** Save or clear project `document.conf` (`PUT /api/v1/project/settings/doc-conf`). */
|
|
1517
|
+
async updateProjectDocConf(body) {
|
|
1518
|
+
return this.request('PUT', '/api/v1/project/settings/doc-conf', body);
|
|
1519
|
+
}
|
|
1520
|
+
/** Remove project override; server uses platform default (`DELETE /api/v1/project/settings/doc-conf`). */
|
|
1521
|
+
async deleteProjectDocConf() {
|
|
1522
|
+
return this.request('DELETE', '/api/v1/project/settings/doc-conf');
|
|
1523
|
+
}
|
|
1524
|
+
/** Embedded platform default `document.conf` text (`GET /api/v1/platform/default-doc-conf`). */
|
|
1525
|
+
async getPlatformDefaultDocConf() {
|
|
1526
|
+
return this.request('GET', '/api/v1/platform/default-doc-conf');
|
|
1527
|
+
}
|
|
1463
1528
|
/** Blocking full DB scan (admin; no calls to embedding API). Prefer {@link postVectorIndexEstimateSession} for UI. */
|
|
1464
1529
|
async getVectorIndexEstimate() {
|
|
1465
1530
|
return this.request('GET', '/api/v1/project/settings/search/vector-index-estimate', undefined);
|
|
@@ -1593,8 +1658,11 @@ class XCiteDBClient {
|
|
|
1593
1658
|
}
|
|
1594
1659
|
throw new types_1.XCiteDBError('RAG stream failed after retry', 401, null);
|
|
1595
1660
|
}
|
|
1596
|
-
async writeJsonDocument(identifier, data) {
|
|
1597
|
-
|
|
1661
|
+
async writeJsonDocument(identifier, data, opts) {
|
|
1662
|
+
const body = { identifier: this.isoPrefixId(identifier), data };
|
|
1663
|
+
if (opts?.overwrite)
|
|
1664
|
+
body.overwrite = true;
|
|
1665
|
+
await this.request('POST', '/api/v1/json-documents', body);
|
|
1598
1666
|
}
|
|
1599
1667
|
async readJsonDocument(identifier) {
|
|
1600
1668
|
return this.request('GET', `/api/v1/json-documents${buildQuery({ identifier: this.isoPrefixId(identifier) })}`);
|
|
@@ -1623,8 +1691,8 @@ class XCiteDBClient {
|
|
|
1623
1691
|
return { identifiers: [], total: 0, offset: 0, limit: 0 };
|
|
1624
1692
|
}
|
|
1625
1693
|
/** JSON document shorthand — same as {@link writeJsonDocument}. */
|
|
1626
|
-
async put(identifier, data) {
|
|
1627
|
-
return this.writeJsonDocument(identifier, data);
|
|
1694
|
+
async put(identifier, data, opts) {
|
|
1695
|
+
return this.writeJsonDocument(identifier, data, opts);
|
|
1628
1696
|
}
|
|
1629
1697
|
/** JSON document read — same as {@link readJsonDocument}. */
|
|
1630
1698
|
async get(identifier) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { XCiteDBClient } from './client';
|
|
2
2
|
export { WebSocketSubscription } from './websocket';
|
|
3
|
-
export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationOptions, WorkspaceInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
|
|
3
|
+
export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationOptions, WorkspaceInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
|
|
4
4
|
export { XCiteDBError } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -41,12 +41,20 @@ export interface TextSearchQuery {
|
|
|
41
41
|
branch?: string;
|
|
42
42
|
offset?: number;
|
|
43
43
|
limit?: number;
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Default server behavior when omitted: `auto` (hybrid if FTS+vector enabled, else semantic or FTS).
|
|
46
|
+
* Explicit `fts` / `semantic` / `hybrid` require the matching capabilities to be enabled.
|
|
47
|
+
*/
|
|
48
|
+
mode?: 'auto' | 'fts' | 'semantic' | 'hybrid';
|
|
46
49
|
/** Minimum cosine similarity for semantic / hybrid vector leg (default 0.3). */
|
|
47
50
|
min_score?: number;
|
|
48
51
|
/** Hybrid only: weight of semantic vs FTS in weighted RRF (0–1, default 0.5). */
|
|
49
52
|
semantic_weight?: number;
|
|
53
|
+
/** Point-in-time FTS filter (ISO date string; server maps to internal date key). */
|
|
54
|
+
at_date?: string;
|
|
55
|
+
/** Range FTS filter: interval overlap with [date_from, date_to) (ISO dates). */
|
|
56
|
+
date_from?: string;
|
|
57
|
+
date_to?: string;
|
|
50
58
|
}
|
|
51
59
|
export interface TextSearchHit {
|
|
52
60
|
identifier: string;
|
|
@@ -59,6 +67,9 @@ export interface TextSearchHit {
|
|
|
59
67
|
score: number;
|
|
60
68
|
/** Present for semantic / hybrid search. */
|
|
61
69
|
source?: 'fts' | 'semantic' | 'both';
|
|
70
|
+
/** FTS temporal window for this hit (internal 7-char keys), when returned by the server. */
|
|
71
|
+
valid_from?: string;
|
|
72
|
+
valid_to?: string;
|
|
62
73
|
}
|
|
63
74
|
export interface TextSearchResult {
|
|
64
75
|
hits: TextSearchHit[];
|
|
@@ -153,6 +164,15 @@ export interface ProjectSearchSettingsUpdate {
|
|
|
153
164
|
llm_base_url?: string | null;
|
|
154
165
|
llm_api_key?: string;
|
|
155
166
|
}
|
|
167
|
+
/** `GET /api/v1/project/settings/doc-conf` (and returned after `PUT` / `DELETE`). */
|
|
168
|
+
export interface ProjectDocConfResponse {
|
|
169
|
+
has_project_override: boolean;
|
|
170
|
+
doc_conf_text: string | null;
|
|
171
|
+
}
|
|
172
|
+
/** `GET /api/v1/platform/default-doc-conf` */
|
|
173
|
+
export interface PlatformDefaultDocConfResponse {
|
|
174
|
+
doc_conf_text: string;
|
|
175
|
+
}
|
|
156
176
|
/** `POST /api/v1/rag/query` (non-streaming: set `stream: false` or omit). */
|
|
157
177
|
export interface RagQueryOptions {
|
|
158
178
|
question: string;
|
|
@@ -314,6 +334,11 @@ export interface DatabaseContext {
|
|
|
314
334
|
workspace?: string;
|
|
315
335
|
/** @deprecated Prefer {@link DatabaseContext.workspace}. Sent as `X-Branch` when `workspace` is unset. */
|
|
316
336
|
branch?: string;
|
|
337
|
+
/**
|
|
338
|
+
* As-of revision time, sent as `X-Date`. Accepted forms (whole string must match; trailing spaces ignored):
|
|
339
|
+
* `mm/dd/yyyy`, `mm/dd/yyyy:HH:MM:SS`, ISO `YYYY-MM-DDTHH:MM:SS` with optional numeric timezone,
|
|
340
|
+
* `YYYY-MM-DD HH:MM:SS`, and ISO date-only `YYYY-MM-DD`.
|
|
341
|
+
*/
|
|
317
342
|
date?: string;
|
|
318
343
|
prefix?: string;
|
|
319
344
|
/**
|
|
@@ -392,6 +417,11 @@ export interface XCiteDBClientOptions {
|
|
|
392
417
|
testRequireAuth?: boolean;
|
|
393
418
|
/** Auto-prefix identifiers for app-user sessions (see {@link UserIsolationOptions}). */
|
|
394
419
|
userIsolation?: UserIsolationOptions;
|
|
420
|
+
/**
|
|
421
|
+
* Per-request timeout in milliseconds for normal REST calls (uses `AbortSignal.timeout` when available).
|
|
422
|
+
* Omit for no timeout. Streaming RAG (`ragQueryStream`) does not apply this.
|
|
423
|
+
*/
|
|
424
|
+
requestTimeoutMs?: number;
|
|
395
425
|
}
|
|
396
426
|
/** Options for {@link XCiteDBClient.createTestSession} (provisions via API key or Bearer). */
|
|
397
427
|
export interface CreateTestSessionOptions {
|
|
@@ -403,12 +433,17 @@ export interface CreateTestSessionOptions {
|
|
|
403
433
|
context?: DatabaseContext;
|
|
404
434
|
platformConsole?: boolean;
|
|
405
435
|
projectId?: string;
|
|
436
|
+
/**
|
|
437
|
+
* When true, creates an overlay test session: writable ephemeral LMDB with production project data as read-only base.
|
|
438
|
+
*/
|
|
439
|
+
overlay?: boolean;
|
|
406
440
|
/** Keep `apiKey` / `accessToken` on the client and send `X-Test-Auth: required` on each request. */
|
|
407
441
|
testRequireAuth?: boolean;
|
|
408
442
|
onSessionTokensUpdated?: (pair: TokenPair) => void;
|
|
409
443
|
onAppUserTokensUpdated?: (pair: AppUserTokenPair) => void;
|
|
410
444
|
onSessionInvalid?: () => void;
|
|
411
445
|
userIsolation?: UserIsolationOptions;
|
|
446
|
+
requestTimeoutMs?: number;
|
|
412
447
|
}
|
|
413
448
|
/** Application user (tenant-scoped), distinct from developer users. */
|
|
414
449
|
export interface AppUser {
|
package/llms-full.txt
CHANGED
|
@@ -26,7 +26,9 @@ Before reading the full reference, note these critical differences from typical
|
|
|
26
26
|
|
|
27
27
|
9. **OpenAPI:** See repository `docs/openapi.yaml` for a machine-readable route map.
|
|
28
28
|
|
|
29
|
-
10. **Ephemeral test sessions.** `POST /api/v1/test/sessions` (authenticated) returns a UUID **`session_token`**. Clients send **`X-Test-Session: <token>`** on API calls to use an isolated, TTL- and quota-limited LMDB instead of production project data. Unless **`X-Test-Auth: required`** is set, **developer** JWT/API-key checks are bypassed (synthetic admin for wet tests), but **app-user** identity via **`X-App-User-Token`** or Bearer app-user JWT is still recognized. Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`.
|
|
29
|
+
10. **Ephemeral test sessions.** `POST /api/v1/test/sessions` (authenticated) returns a UUID **`session_token`**. Clients send **`X-Test-Session: <token>`** on API calls to use an isolated, TTL- and quota-limited LMDB instead of production project data. Unless **`X-Test-Auth: required`** is set, **developer** JWT/API-key checks are bypassed (synthetic admin for wet tests), but **app-user** identity via **`X-App-User-Token`** or Bearer app-user JWT is still recognized. Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`. With a default body (omit or `{}`), the test LMDB starts **empty** (no cloned production project config).
|
|
30
|
+
|
|
31
|
+
11. **Overlay test sessions.** Same **`POST`**, with JSON **`{"overlay":true}`**, while authenticated for the **project to debug** (project-scoped API key, or platform Bearer + **`X-Project-Id`**). The session metadata records overlay mode; subsequent requests need only **`X-Test-Session`**. The server opens **`XCiteDB(_test/<uuid>/data, <production data path>)`**: production is used as a **read-only base**; reads merge overlay + base; **writes never modify production**. If the production data directory is missing, opening the session database fails. JS **`createTestSession({ …, overlay: true })`**, C++ **`test_session_overlay`** + **`create_test_session`**, MCP **`create_test_session`** tool **`overlay: true`**.
|
|
30
32
|
|
|
31
33
|
## Choosing the Right Versioning Approach
|
|
32
34
|
|
|
@@ -58,7 +60,7 @@ Legacy REST paths under `/api/v1/branches`, `/commits`, `/tags`, `/diff` remain
|
|
|
58
60
|
|
|
59
61
|
6. **Self-registration uses server-configured default groups.** `registerAppUser()` assigns groups from the server's `auth.app_users.default_groups` config, not from the client request. To assign specific groups, use the admin endpoint `createAppUser()` instead, or update groups after registration via `updateAppUserGroups()`.
|
|
60
62
|
|
|
61
|
-
7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real
|
|
63
|
+
7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real LMDB under `_test/<uuid>/` (empty by default, or **overlay** on read-only production with **`{"overlay":true}`** / **`overlay: true`** / **`test_session_overlay`**). See "Ephemeral test sessions" below.
|
|
62
64
|
|
|
63
65
|
---
|
|
64
66
|
|
|
@@ -205,7 +207,7 @@ For **integration and wet tests** against a shared BaaS host without touching pr
|
|
|
205
207
|
|
|
206
208
|
| Step | What to do |
|
|
207
209
|
|------|------------|
|
|
208
|
-
| **Create** | **`POST /api/v1/test/sessions`** with normal **`Authorization: Bearer …`** or **`X-API-Key`**. Response includes a **`session_token`** (UUID). Server enforces per-credential limits (`test.max_sessions_per_key`, `test.session_ttl_seconds`, `test.max_test_db_size_bytes` in server config). |
|
|
210
|
+
| **Create** | **`POST /api/v1/test/sessions`** with normal **`Authorization: Bearer …`** or **`X-API-Key`**. Response includes a **`session_token`** (UUID). Server enforces per-credential limits (`test.max_sessions_per_key`, `test.session_ttl_seconds`, `test.max_test_db_size_bytes` in server config). Optional JSON body **`{"overlay":true}`** provisions a **read-through production** session (writable delta only under `_test/<uuid>/`; production LMDB is read-only base). |
|
|
209
211
|
| **Use** | Send **`X-Test-Session: <session_token>`** on document and other data API requests. The server routes to a dedicated LMDB under its data root (`_test/<id>/`), not the caller’s production tenant. **`tenant_id` / `X-Project-Id` semantics do not select production** while the test header is present—the synthetic test tenant is implied. |
|
|
210
212
|
| **Auth** | **Default:** developer auth (API key / platform JWT) is **bypassed** with a synthetic admin identity. However, **app-user identity is still recognized**: if `X-App-User-Token` or a Bearer app-user JWT is present, the request runs as that app user (for routes like `/app/auth/me`). **`X-Test-Auth: required`:** all auth is validated normally; ABAC applies, but data still comes from the test session DB. |
|
|
211
213
|
| **Manage** | **`GET /api/v1/test/sessions`** — list sessions for the current credential. **`DELETE /api/v1/test/sessions/current`** — destroy the session named by **`X-Test-Session`** (no other auth). **`DELETE /api/v1/test/sessions/all`** — destroy all sessions for the credential. **`DELETE /api/v1/test/sessions/{token}`** — destroy one session if owned by the credential. Do **not** send **`X-Test-Session`** on these `/api/v1/test/*` routes. |
|
|
@@ -213,9 +215,9 @@ For **integration and wet tests** against a shared BaaS host without touching pr
|
|
|
213
215
|
|
|
214
216
|
**SDK usage (summary):**
|
|
215
217
|
|
|
216
|
-
- **JavaScript/TypeScript:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })` returns a client configured with `testSessionToken`; optional `testRequireAuth: true` maps to `X-Test-Auth: required`. `destroyTestSession()` calls `DELETE …/test/sessions/current`.
|
|
217
|
-
- **Python:** `async with XCiteDBClient.test_session(base_url, api_key=…, …)` provisions and tears down; or pass `test_session_token` / `test_require_auth` to the constructor.
|
|
218
|
-
- **C++:** `XCiteDBClient::create_test_session(options)` after setting `api_key
|
|
218
|
+
- **JavaScript/TypeScript:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })` returns a client configured with `testSessionToken`; optional **`overlay: true`** for overlay mode; optional `testRequireAuth: true` maps to `X-Test-Auth: required`. `destroyTestSession()` calls `DELETE …/test/sessions/current`.
|
|
219
|
+
- **Python:** `async with XCiteDBClient.test_session(base_url, api_key=…, …)` provisions and tears down; or pass `test_session_token` / `test_require_auth` to the constructor. For overlay until the helper accepts a flag, call **`POST /api/v1/test/sessions`** with JSON **`{"overlay":true}`** then construct the client with the returned token.
|
|
220
|
+
- **C++:** `XCiteDBClient::create_test_session(options)` after setting `api_key`, optional **`test_session_overlay = true`**, and optional `test_require_auth`; `destroy_test_session()`.
|
|
219
221
|
|
|
220
222
|
**JavaScript/TypeScript — complete test scaffold (Vitest / Jest):**
|
|
221
223
|
|
|
@@ -450,14 +452,23 @@ Returns `{ parent_path, parent_is_identifier, children: [{ segment, full_path, i
|
|
|
450
452
|
|
|
451
453
|
**`POST /api/v1/json-documents`**
|
|
452
454
|
|
|
455
|
+
JSON body fields:
|
|
456
|
+
|
|
457
|
+
| Field | Required | Meaning |
|
|
458
|
+
|-------|----------|---------|
|
|
459
|
+
| `identifier` | yes | Document path key |
|
|
460
|
+
| `data` | yes | JSON value to merge or store |
|
|
461
|
+
| `overwrite` | no (default `false`) | When `true`, delete all metadata under the document root first, then write `data` only. When `false`, **`data` is merged** into any existing document (nested objects combine fields; nested arrays follow meta merge rules—see Metadata). |
|
|
462
|
+
|
|
453
463
|
```json
|
|
454
464
|
{
|
|
455
465
|
"identifier": "app.settings",
|
|
456
|
-
"data": { "theme": "dark", "maxUploadMb": 25 }
|
|
466
|
+
"data": { "theme": "dark", "maxUploadMb": 25 },
|
|
467
|
+
"overwrite": false
|
|
457
468
|
}
|
|
458
469
|
```
|
|
459
470
|
|
|
460
|
-
Note:
|
|
471
|
+
Note: SDKs use `identifier` and `data` (optional `overwrite`). Some older documentation shows `key` and `value`.
|
|
461
472
|
|
|
462
473
|
## Read JSON document
|
|
463
474
|
|
|
@@ -501,15 +512,26 @@ Attach structured **JSON metadata** to documents or nodes.
|
|
|
501
512
|
|
|
502
513
|
**`POST /api/v1/meta`**
|
|
503
514
|
|
|
515
|
+
| Field | Required | Meaning |
|
|
516
|
+
|-------|----------|---------|
|
|
517
|
+
| `identifier` **or** `query` | one required | Target document id or document query |
|
|
518
|
+
| `value` | yes | JSON to write (omit only for string-specific query batch paths handled by the server) |
|
|
519
|
+
| `path` | no (default `""`) | Meta path (dot-separated keys; `[i]` for array indices) |
|
|
520
|
+
| `mode` | no (default `"set"`) | `"set"` — write/replace at `path`. For **arrays** at `path`, indices `0..n-1` are written and any previous tail beyond the new length is cleared. `"append"` — for **arrays** at `path`, new elements are written after existing indices (extend in place). |
|
|
521
|
+
| `overwrite` | no (default `false`) | When `true`, delete existing metadata under `path` before applying `value`. |
|
|
522
|
+
| `first_match` | no | With `query`, only the first matching identifier is updated when `true`. |
|
|
523
|
+
|
|
504
524
|
```json
|
|
505
525
|
{
|
|
506
526
|
"identifier": "/book/ch1",
|
|
507
527
|
"value": { "status": "review", "owner": "alice" },
|
|
508
|
-
"path": ""
|
|
528
|
+
"path": "",
|
|
529
|
+
"mode": "set",
|
|
530
|
+
"overwrite": false
|
|
509
531
|
}
|
|
510
532
|
```
|
|
511
533
|
|
|
512
|
-
|
|
534
|
+
Use `"mode": "append"` to extend arrays at `path` instead of replacing them.
|
|
513
535
|
|
|
514
536
|
Can also use `"query"` instead of `"identifier"` to target multiple documents by query filter.
|
|
515
537
|
|
|
@@ -525,11 +547,17 @@ Can also use `"query"` instead of `"identifier"` to target multiple documents by
|
|
|
525
547
|
|
|
526
548
|
**`DELETE /api/v1/meta`** — `{ "query": {...} }`
|
|
527
549
|
|
|
550
|
+
## JSON metadata storage model (best practices)
|
|
551
|
+
|
|
552
|
+
- **Field-indexed objects.** Metadata and standalone JSON documents share the same shredded-key storage. **JSON objects** map to named paths so fields can be read or updated without loading a monolithic blob.
|
|
553
|
+
- **Dictionary threshold.** When the **set of distinct field names** on an object reaches the server threshold (default **32**, native setting `meta_dict_field_threshold`), XCiteDB switches that object to **dictionary storage** (`{*}` plus per-field keys) for scalable indexed access.
|
|
554
|
+
- **Modeling guidance.** Prefer stable **`{ "key": value, ... }`** / nested-object shapes for records you look up by key. Arrays are appropriate for ordered lists; use **`mode: "append"`** when appending to a stored array.
|
|
555
|
+
|
|
528
556
|
---
|
|
529
557
|
|
|
530
558
|
# Search
|
|
531
559
|
|
|
532
|
-
Full-text search (
|
|
560
|
+
Full-text search uses **embedded XciteFTS** (LMDB index per project). **Semantic** and **hybrid** (FTS + vector) search are available when vector search is enabled in project settings. The JSON field **`mode`** selects behavior (`auto` picks the best option for the project, or set `fts`, `semantic`, or `hybrid` explicitly).
|
|
533
561
|
|
|
534
562
|
**Base path:** `/api/v1/search`
|
|
535
563
|
|
|
@@ -543,15 +571,26 @@ Full-text search (backed by Meilisearch or Elasticsearch).
|
|
|
543
571
|
"doc_types": ["xml", "json"],
|
|
544
572
|
"branch": "",
|
|
545
573
|
"limit": 20,
|
|
546
|
-
"offset": 0
|
|
574
|
+
"offset": 0,
|
|
575
|
+
"mode": "fts"
|
|
547
576
|
}
|
|
548
577
|
```
|
|
549
578
|
|
|
550
|
-
|
|
579
|
+
Optional fields (also on SDK `TextSearchQuery` and the MCP `search` tool): **`min_score`**, **`semantic_weight`** for semantic/hybrid.
|
|
580
|
+
|
|
581
|
+
**Temporal FTS** (posting intervals; applies to the FTS keyword index—use **`mode`: `"fts"`** for pure temporal keyword search, or hybrid where the FTS leg participates):
|
|
582
|
+
|
|
583
|
+
| Field | Meaning |
|
|
584
|
+
|-------|---------|
|
|
585
|
+
| **`at_date`** | Date string (e.g. ISO `YYYY-MM-DD` or `mm/dd/yyyy` as accepted by the server). A posting matches if it is valid at that instant: `start <= at_date < end` (internal 7-char keys after `date2key`). |
|
|
586
|
+
| **`date_from`**, **`date_to`** | Date strings defining a half-open range `[date_from, date_to)`. A posting matches if its `[start, end)` **overlaps** that range. Either bound may be omitted (open-ended). |
|
|
587
|
+
| *(none of the three)* | **Current-time** view: only postings whose interval is still “open” (end at the internal max sentinel) are included. |
|
|
588
|
+
|
|
589
|
+
**Response:** `{ hits: [...], total, query }`. Each hit has **`identifier`**, **`path`**, **`doc_type`**, **`branch`**, **`snippet`**, **`score`**, and optionally **`xcitepath`** (XML). Semantic/hybrid hits may include **`source`**: `fts` \| `semantic` \| `both`. When the server infers a temporal window for the matched term(s), hits may include **`valid_from`** and **`valid_to`** as **7-character internal date keys** (same alphabet as revision keys—not ISO). Omitted for full-range/unversioned postings or when not computed.
|
|
551
590
|
|
|
552
591
|
## Reindex
|
|
553
592
|
|
|
554
|
-
**`POST /api/v1/search/reindex`** — Rebuilds the search index.
|
|
593
|
+
**`POST /api/v1/search/reindex`** — Rebuilds the full-text search index.
|
|
555
594
|
|
|
556
595
|
---
|
|
557
596
|
|
|
@@ -1361,17 +1400,17 @@ interface DatabaseContext {
|
|
|
1361
1400
|
- `queryLog(query, fromDate, toDate)` → `LogEntry[]`
|
|
1362
1401
|
|
|
1363
1402
|
### JSON Documents
|
|
1364
|
-
- `writeJsonDocument(identifier, data)` → `void`
|
|
1403
|
+
- `writeJsonDocument(identifier, data, opts?)` → `void` — merge by default; `opts?.overwrite` replaces root
|
|
1365
1404
|
- `readJsonDocument<T>(identifier)` → `T` (default `unknown`)
|
|
1366
1405
|
- `deleteJsonDocument(identifier)` → `void`
|
|
1367
1406
|
- `listJsonDocuments(match?, limit?, offset?)` → `ListIdentifiersResult`
|
|
1368
|
-
- `put(identifier, data)` / `get<T>(identifier)` / `remove(identifier)` / `list(match?, limit?, offset?)` — JSON CRUD aliases
|
|
1407
|
+
- `put(identifier, data, opts?)` / `get<T>(identifier)` / `remove(identifier)` / `list(match?, limit?, offset?)` — JSON CRUD aliases (`opts` same as `writeJsonDocument`)
|
|
1369
1408
|
|
|
1370
1409
|
### Metadata
|
|
1371
|
-
- `addMeta(identifier, value, path?, opts?)` → `boolean`
|
|
1410
|
+
- `addMeta(identifier, value, path?, opts?)` → `boolean` — `opts`: `mode?: 'set'|'append'`, `overwrite?: boolean`
|
|
1372
1411
|
- `addMetaByQuery(query, value, path?, firstMatch?, opts?)` → `boolean`
|
|
1373
|
-
- `appendMeta(identifier, value, path?)` → `boolean`
|
|
1374
|
-
- `appendMetaByQuery(query, value, path?, firstMatch?)` → `boolean`
|
|
1412
|
+
- `appendMeta(identifier, value, path?, opts?)` → `boolean` — same as `addMeta` with `mode: 'append'`
|
|
1413
|
+
- `appendMetaByQuery(query, value, path?, firstMatch?, opts?)` → `boolean`
|
|
1375
1414
|
- `queryMeta<T>(identifier, path?)` → `T`
|
|
1376
1415
|
- `queryMetaByQuery<T>(query, path?)` → `T`
|
|
1377
1416
|
- `clearMeta(query)` → `boolean`
|
|
@@ -1411,7 +1450,7 @@ interface DatabaseContext {
|
|
|
1411
1450
|
- `findLocks(identifier)` → `LockInfo[]`
|
|
1412
1451
|
|
|
1413
1452
|
### Search
|
|
1414
|
-
- `search(query: TextSearchQuery)` → `TextSearchResult`
|
|
1453
|
+
- `search(query: TextSearchQuery)` → `TextSearchResult`. Query may include **`at_date`**, **`date_from`**, **`date_to`** (ISO-style or `mm/dd/yyyy` strings) for temporal FTS; set **`mode`: `"fts"`** for keyword-only temporal search. Hits may include **`valid_from`** / **`valid_to`** (7-char internal keys) when the server returns an inferred validity window.
|
|
1415
1454
|
- `reindex()` → `{ status, message }`
|
|
1416
1455
|
|
|
1417
1456
|
### Unquery
|
|
@@ -1544,6 +1583,7 @@ async def main():
|
|
|
1544
1583
|
print(await client.read_json_document("app.settings"))
|
|
1545
1584
|
print(await client.list_identifiers(XCiteQuery(match_start="/manual/")))
|
|
1546
1585
|
print(await client.search(TextSearchQuery(query="guide", limit=10)))
|
|
1586
|
+
# Temporal FTS: await client.search(TextSearchQuery(query="guide", mode="fts", at_date="2024-06-01"))
|
|
1547
1587
|
await client.platform_login("admin@localhost", "password")
|
|
1548
1588
|
# await client.login_app_user("user@example.com", "pw") # set context.project_id / tenant_id if needed
|
|
1549
1589
|
async with client.with_workspace("feature-x", message="WIP", auto_merge=True):
|
package/llms.txt
CHANGED
|
@@ -22,7 +22,7 @@ These are the most common sources of confusion for developers and AI assistants:
|
|
|
22
22
|
|
|
23
23
|
8. **Project vs tenant id.** In the SDK, prefer `context.project_id` (and `listMyProjects` / `switchProject`). Many JSON bodies and JWT claims still use the field name `tenant_id` for the same value — the client sends that wire name automatically.
|
|
24
24
|
|
|
25
|
-
9. **Ephemeral test sessions (wet tests).** Call **`POST /api/v1/test/sessions`** with a normal API key or Bearer token to get a `session_token` (UUID). Send **`X-Test-Session: <token>`** on subsequent document/API calls to use an isolated, short-lived LMDB instead of production data. By default **developer** auth (API key / platform JWT) is bypassed, but **app-user** identity (`X-App-User-Token` or Bearer app-user JWT) is still recognized. Send **`X-Test-Auth: required`** to exercise full developer JWT/API-key auth and ABAC against the same test database. Do not send `X-Test-Session` on `/api/v1/test/*` management routes. Server limits apply (`test.session_ttl_seconds`, `test.max_sessions_per_key`, `test.max_test_db_size_bytes` in config).
|
|
25
|
+
9. **Ephemeral test sessions (wet tests).** Call **`POST /api/v1/test/sessions`** with a normal API key or Bearer token to get a `session_token` (UUID). Send **`X-Test-Session: <token>`** on subsequent document/API calls to use an isolated, short-lived LMDB instead of production data. By default **developer** auth (API key / platform JWT) is bypassed, but **app-user** identity (`X-App-User-Token` or Bearer app-user JWT) is still recognized. Send **`X-Test-Auth: required`** to exercise full developer JWT/API-key auth and ABAC against the same test database. Do not send `X-Test-Session` on `/api/v1/test/*` management routes. Server limits apply (`test.session_ttl_seconds`, `test.max_sessions_per_key`, `test.max_test_db_size_bytes` in config). By default the test LMDB starts **empty** (no production data). **Overlay mode:** same **`POST`** with JSON body **`{"overlay":true}`** (while authenticated for the project you want to inspect—project-scoped API key, or platform Bearer + **`X-Project-Id`**). The server opens a **writable** LMDB under `_test/<uuid>/` with that project’s on-disk store as a **read-only base** (dual LMDB): reads see production + overlay deltas; **writes never touch production**. **`XCiteDBClient.createTestSession({ …, overlay: true })`** sends that body. After creation, only **`X-Test-Session`** is required on requests (overlay is stored in session metadata).
|
|
26
26
|
|
|
27
27
|
## Choosing the Right Versioning Approach
|
|
28
28
|
|
|
@@ -58,7 +58,7 @@ Legacy REST paths (`/api/v1/branches`, `/commits`, `/tags`, `/diff`) remain as *
|
|
|
58
58
|
|
|
59
59
|
6. **Self-registration uses server-configured default groups.** `registerAppUser()` assigns groups from the server's `auth.app_users.default_groups` config, not from the client request. To set groups explicitly, use **`createAppUser`** with a `groups` array (e.g. `[XCiteDBClient.buildProjectGroup(projectId, 'editor')]`) or **`updateAppUserGroups`**. The server rejects `project:<x>:*` groups when `<x>` is not a known internal project id (avoids mistaking the display name for the tenant id).
|
|
60
60
|
|
|
61
|
-
7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real
|
|
61
|
+
7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real LMDB that is scoped under `_test/<uuid>/` and destroyed after the test (empty by default, or **overlay** on read-only production when you pass **`{"overlay":true}`** / **`overlay: true`**). See "Test mode" below.
|
|
62
62
|
|
|
63
63
|
8. **403 on writes with ABAC is often a JWT/group string mismatch.** Decode the app-user access token early: log **`tenant_id`**, **`groups`**, **`sub`**. The middle segment of every **`project:<x>:role`** group must equal **`tenant_id`** exactly. Document write denials may return JSON fields **`policy_id`** and **`hint`** alongside `"Forbidden"`.
|
|
64
64
|
|
|
@@ -105,11 +105,17 @@ await app.writeJsonDocument('userdata/alice/profile', { ok: true });
|
|
|
105
105
|
|
|
106
106
|
> **AI assistant instruction:** XciteDB has built-in ephemeral test session support — this capability is **not standard** in most BaaS platforms and is specifically designed to enable meaningful wet integration tests. When writing tests for code that uses XciteDB, **do not mock or stub the XciteDB client**. Instead, use the test session pattern below to run tests against a real, isolated, throwaway database. This produces far more reliable tests because real storage, versioning, querying, and access-control behavior is exercised.
|
|
107
107
|
|
|
108
|
-
1. **Provision:** `POST /api/v1/test/sessions` with `Authorization: Bearer …` or `X-API-Key` (same as normal API access). Response JSON includes the session token.
|
|
109
|
-
2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-Workspace` / `context` as needed). Data
|
|
108
|
+
1. **Provision:** `POST /api/v1/test/sessions` with `Authorization: Bearer …` or `X-API-Key` (same as normal API access). Optional JSON body **`{"overlay":true}`** creates an **overlay** session (read-through production, writes only under `_test/<session>/`). Response JSON includes the session token (and **`"overlay": true`** when applicable).
|
|
109
|
+
2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-Workspace` / `context` as needed). Data writes go under the server’s `_test/<session>/` tree; overlay sessions **do not** write to production paths.
|
|
110
110
|
3. **Auth behavior:** Developer auth (API key / platform JWT) is bypassed by default for frictionless tests. **App-user identity** (`X-App-User-Token` or Bearer app-user JWT) **is still recognized** in default mode, so `registerAppUser` → `loginAppUser` → `appUserMe` works inside a test session. To also exercise developer auth and ABAC policies, set **`X-Test-Auth: required`** and send normal credentials; the DB is still the test session’s.
|
|
111
111
|
4. **Cleanup:** `DELETE /api/v1/test/sessions/current` with `X-Test-Session` (no other auth), or `DELETE /api/v1/test/sessions/all` / `DELETE /api/v1/test/sessions/{token}` with normal auth for the owning key or JWT.
|
|
112
|
-
5. **SDKs:** **JS/TS:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`, optional `testRequireAuth`, then `destroyTestSession()`. **Python:** `async with XCiteDBClient.test_session(...)` or
|
|
112
|
+
5. **SDKs:** **JS/TS:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`, optional `overlay: true`, optional `testRequireAuth`, then `destroyTestSession()`. **Python:** `async with XCiteDBClient.test_session(...)` or provision with **`POST /api/v1/test/sessions`** and JSON **`{"overlay":true}`** when you need overlay, then pass `test_session_token` / `test_require_auth` to the constructor. **C++:** `XCiteDBClient::create_test_session(options)` with optional `test_session_overlay = true`, `destroy_test_session()`, optional `test_require_auth` in options.
|
|
113
|
+
|
|
114
|
+
## JSON documents and metadata (merge, overwrite, efficiency)
|
|
115
|
+
|
|
116
|
+
- **JSON documents merge by default.** `POST /api/v1/json-documents` merges the posted `data` into the existing document (object fields combined per XCiteDB meta merge rules). Send **`overwrite: true`** to clear all stored JSON under that document root first, then write only the new payload. SDKs accept the same flag (e.g. `writeJsonDocument(id, data, { overwrite: true })` in JavaScript).
|
|
117
|
+
- **Metadata `mode` and arrays.** **`POST /api/v1/meta`** uses **`mode`**: default **`set`** writes or replaces at `path` (arrays are replaced in range; excess old indices cleared); **`append`** appends array elements after existing ones at `path`. Optional **`overwrite: true`** clears metadata under `path` before writing. JavaScript: **`appendMeta`** or **`addMeta(..., { mode: 'append' })`**; Python/C++: **`append_meta`** or **`add_meta`** with **`mode`** / **`overwrite`** (see SDK sections below).
|
|
118
|
+
- **Prefer dictionary-style objects.** Shredded JSON metadata is keyed by field names. **Object maps** get per-field storage; when an object accumulates enough distinct field names (server default threshold **32**), XCiteDB switches automatically to **dictionary storage** (`{*}` plus per-field keys) for efficient indexed access. For lookup-heavy or wide records, use **`{ "key": value, ... }`** shapes (or one document per logical row) rather than opaque arrays when you need keyed reads.
|
|
113
119
|
|
|
114
120
|
## JavaScript/TypeScript SDK (`@xcitedbs/client`)
|
|
115
121
|
|
|
@@ -177,8 +183,10 @@ const lock = await client.acquireLock('/manual/v1/intro');
|
|
|
177
183
|
// ... edit ...
|
|
178
184
|
await client.releaseLock('/manual/v1/intro', lock.lock_id);
|
|
179
185
|
|
|
180
|
-
// Full-text search (
|
|
181
|
-
const results = await client.search({ query: 'installation guide', limit: 20 });
|
|
186
|
+
// Full-text search (embedded XciteFTS; optional temporal filters on the query body)
|
|
187
|
+
const results = await client.search({ query: 'installation guide', limit: 20, mode: 'fts' });
|
|
188
|
+
// Point-in-time keyword search: { query: '...', mode: 'fts', at_date: '2024-06-01' }
|
|
189
|
+
// Range overlap: { query: '...', mode: 'fts', date_from: '2024-01-01', date_to: '2025-01-01' }
|
|
182
190
|
|
|
183
191
|
// Subscribe to real-time changes via WebSocket
|
|
184
192
|
const sub = client.subscribe(
|
|
@@ -230,15 +238,15 @@ interface XCiteDBClientOptions {
|
|
|
230
238
|
- `listIdentifierChildren(parentPath?)` — Navigate identifier hierarchy
|
|
231
239
|
|
|
232
240
|
**JSON Documents:**
|
|
233
|
-
- `writeJsonDocument(identifier, data)` — Store a JSON document
|
|
241
|
+
- `writeJsonDocument(identifier, data, opts?)` — Store or merge a JSON document (`opts?.overwrite` replaces entire document root)
|
|
234
242
|
- `readJsonDocument(identifier)` — Read a JSON document (generic `readJsonDocument<T>()` supported)
|
|
235
243
|
- `deleteJsonDocument(identifier)` — Delete a JSON document
|
|
236
244
|
- `listJsonDocuments(match?, limit?, offset?)` — List JSON document keys
|
|
237
245
|
- **Quick JSON aliases:** `put`, `get`, `remove`, `list` — same as the four methods above
|
|
238
246
|
|
|
239
247
|
**Metadata (JSON on XML):**
|
|
240
|
-
- `addMeta(identifier, value, path?)` — Set metadata
|
|
241
|
-
- `appendMeta(identifier, value, path?)` — Append
|
|
248
|
+
- `addMeta(identifier, value, path?, opts?)` — Set or append metadata (`opts?.mode`: `set`|`append`; `opts?.overwrite`)
|
|
249
|
+
- `appendMeta(identifier, value, path?, opts?)` — Append at `path` (same as `addMeta` with `mode: 'append'`)
|
|
242
250
|
- `queryMeta(identifier, path?)` — Read metadata
|
|
243
251
|
- `clearMeta(query)` — Remove metadata
|
|
244
252
|
|
|
@@ -263,7 +271,7 @@ interface XCiteDBClientOptions {
|
|
|
263
271
|
- `findLocks(identifier)` — Query active locks
|
|
264
272
|
|
|
265
273
|
**Search & Analytics:**
|
|
266
|
-
- `search(query)` — Full-text search
|
|
274
|
+
- `search(query)` — Full-text search (embedded FTS). Optional **`at_date`**, **`date_from`**, **`date_to`** on the query for temporal keyword search (use **`mode: 'fts'`**). Hits may include **`valid_from`** / **`valid_to`** (7-char internal keys).
|
|
267
275
|
- `reindex()` — Rebuild search index
|
|
268
276
|
- `unquery(query, unqueryDoc)` — Execute Unquery DSL
|
|
269
277
|
|
|
@@ -338,6 +346,7 @@ async def main():
|
|
|
338
346
|
await client.write_json_document("app.settings", {"theme": "dark"})
|
|
339
347
|
print(await client.read_json_document("app.settings"))
|
|
340
348
|
print(await client.search(TextSearchQuery(query="guide", limit=10)))
|
|
349
|
+
# Temporal FTS: TextSearchQuery(query="guide", mode="fts", at_date="2024-06-01")
|
|
341
350
|
pair = await client.platform_login("admin@localhost", "password")
|
|
342
351
|
# await client.login_app_user("user@example.com", "secret", tenant_id="...")
|
|
343
352
|
|
|
@@ -378,7 +387,7 @@ auto ids = client.query_documents(q);
|
|
|
378
387
|
- Documents — XML document CRUD, identifiers, hierarchy
|
|
379
388
|
- JSON documents — JSON document CRUD
|
|
380
389
|
- Metadata — JSON metadata on documents
|
|
381
|
-
- Search — Full-text
|
|
390
|
+
- Search — Full-text (embedded FTS; optional temporal `at_date` / `date_from` / `date_to`; hit `valid_from` / `valid_to`)
|
|
382
391
|
- Unquery — Declarative query DSL
|
|
383
392
|
- Workspaces — Isolated editing environments
|
|
384
393
|
- Checkpoints & bookmarks — Named snapshots and references
|