@xcitedbs/client 0.2.0 → 0.2.6
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 +2 -1
- package/dist/client.d.ts +60 -15
- package/dist/client.js +130 -19
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +60 -5
- package/dist/websocket.js +5 -0
- package/llms-full.txt +88 -20
- package/llms.txt +57 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,11 +26,12 @@ import { XCiteDBClient } from '@xcitedbs/client';
|
|
|
26
26
|
const client = new XCiteDBClient({
|
|
27
27
|
baseUrl: 'http://localhost:8080',
|
|
28
28
|
apiKey: process.env.XCITEDB_API_KEY,
|
|
29
|
-
context: { branch: '
|
|
29
|
+
context: { branch: '', date: '' },
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
await client.health();
|
|
33
33
|
const docs = await client.queryByIdentifier('/test1', 'FirstMatch');
|
|
34
|
+
await client.put('app.settings', { theme: 'dark' });
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
### WebSocket
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, CommitRecord, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, PolicySubjectInput, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, OAuthProvidersResponse,
|
|
1
|
+
import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, CommitRecord, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteQuery } from './types';
|
|
2
2
|
import { WebSocketSubscription } from './websocket';
|
|
3
3
|
export declare class XCiteDBClient {
|
|
4
4
|
private baseUrl;
|
|
@@ -14,7 +14,18 @@ export declare class XCiteDBClient {
|
|
|
14
14
|
private onSessionTokensUpdated?;
|
|
15
15
|
private onAppUserTokensUpdated?;
|
|
16
16
|
private onSessionInvalid?;
|
|
17
|
+
private testSessionToken?;
|
|
18
|
+
private testRequireAuth?;
|
|
17
19
|
constructor(options: XCiteDBClientOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Create an ephemeral isolated database: calls `POST /api/v1/test/sessions` with your API key or Bearer,
|
|
22
|
+
* then returns a client that sends `X-Test-Session` (auth-free by default).
|
|
23
|
+
*/
|
|
24
|
+
static createTestSession(opts: CreateTestSessionOptions): Promise<XCiteDBClient>;
|
|
25
|
+
/** Destroy this test session on the server (`DELETE /api/v1/test/sessions/current`). */
|
|
26
|
+
destroyTestSession(): Promise<{
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
18
29
|
/** True if this client would send API key or Bearer credentials on a normal request. */
|
|
19
30
|
private sentAuthCredentials;
|
|
20
31
|
/** 401 on these paths is an expected auth flow outcome, not a dead session. */
|
|
@@ -34,6 +45,7 @@ export declare class XCiteDBClient {
|
|
|
34
45
|
/** Include `tenant_id` for public app-auth routes when using only app-user tokens (no developer key/JWT). */
|
|
35
46
|
private mergeAppTenant;
|
|
36
47
|
private authHeaders;
|
|
48
|
+
private testHeaders;
|
|
37
49
|
private request;
|
|
38
50
|
/** Developer Bearer refresh first, then app-user refresh (no API key refresh). */
|
|
39
51
|
private tryRefreshSessionAfter401;
|
|
@@ -48,8 +60,8 @@ export declare class XCiteDBClient {
|
|
|
48
60
|
api_version: string;
|
|
49
61
|
}>;
|
|
50
62
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
63
|
+
* @deprecated Use {@link platformLogin} for platform operator sign-in, or {@link loginAppUser} for app end-users.
|
|
64
|
+
* This method only performs platform console login.
|
|
53
65
|
*/
|
|
54
66
|
login(email: string, password: string): Promise<TokenPair>;
|
|
55
67
|
/** Platform console sign-in (`email` + `password`). Project context is `X-Project-Id`, not the JWT. */
|
|
@@ -67,14 +79,14 @@ export declare class XCiteDBClient {
|
|
|
67
79
|
org_name?: string;
|
|
68
80
|
org_id?: string;
|
|
69
81
|
attributes?: Record<string, unknown>;
|
|
70
|
-
}): Promise<
|
|
82
|
+
}): Promise<PlatformRegisterResult>;
|
|
71
83
|
platformWorkspaces(): Promise<PlatformWorkspacesResponse>;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
/** @deprecated Prefer {@link listMyProjects}. */
|
|
85
|
+
listMyTenants(): Promise<ProjectInfo[]>;
|
|
86
|
+
/** Lists projects the platform user can access (from workspaces). */
|
|
87
|
+
listMyProjects(): Promise<ProjectInfo[]>;
|
|
75
88
|
/**
|
|
76
|
-
*
|
|
77
|
-
* Legacy `/api/v1/auth/switch-tenant` has been removed; non-platform callers should set context instead.
|
|
89
|
+
* @deprecated Use {@link switchProject}. Platform console: updates `X-Project-Id` only (no token exchange).
|
|
78
90
|
*/
|
|
79
91
|
switchTenant(tenantId: string): Promise<void>;
|
|
80
92
|
/** Alias for {@link switchTenant}. */
|
|
@@ -83,7 +95,7 @@ export declare class XCiteDBClient {
|
|
|
83
95
|
createApiKey(name: string, expiresAt?: number, keyType?: 'secret' | 'public'): Promise<unknown>;
|
|
84
96
|
changePassword(currentPassword: string, newPassword: string): Promise<void>;
|
|
85
97
|
revokeApiKey(keyId: string): Promise<void>;
|
|
86
|
-
registerAppUser(email: string, password: string, displayName?: string,
|
|
98
|
+
registerAppUser(email: string, password: string, displayName?: string, attributes?: Record<string, unknown>): Promise<AppUser>;
|
|
87
99
|
getOAuthProviders(): Promise<OAuthProvidersResponse>;
|
|
88
100
|
/** Relative path + query for browser navigation to start OAuth (append to API base URL). */
|
|
89
101
|
oauthAuthorizePath(provider: string): string;
|
|
@@ -165,8 +177,33 @@ export declare class XCiteDBClient {
|
|
|
165
177
|
message?: string;
|
|
166
178
|
autoResolve?: 'none' | 'source' | 'target';
|
|
167
179
|
}): Promise<MergeResult>;
|
|
168
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
|
|
182
|
+
* create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
|
|
183
|
+
* Restores previous {@link DatabaseContext} afterward.
|
|
184
|
+
*/
|
|
185
|
+
withBranch<T>(branchName: string, fn: (client: XCiteDBClient) => Promise<T>, options?: {
|
|
186
|
+
message?: string;
|
|
187
|
+
/** When true (default), merge `branchName` into the parent branch after commit. */
|
|
188
|
+
autoMerge?: boolean;
|
|
189
|
+
/** Branch to fork from (default: current `context.branch`, or `""`). */
|
|
190
|
+
fromBranch?: string;
|
|
191
|
+
author?: string;
|
|
192
|
+
}): Promise<{
|
|
193
|
+
result: T;
|
|
194
|
+
commit?: CommitRecord;
|
|
195
|
+
merge?: MergeResult;
|
|
196
|
+
}>;
|
|
197
|
+
/** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeXmlDocument`. */
|
|
169
198
|
writeXML(xml: string, _options?: WriteDocumentOptions): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Write an **XML** document using a JSON request body (`xml` field). The identifier is taken from `db:identifier` on the root element.
|
|
201
|
+
* For storing JSON data by key, use `writeJsonDocument`.
|
|
202
|
+
*/
|
|
203
|
+
writeXmlDocument(xml: string, options?: WriteDocumentOptions): Promise<void>;
|
|
204
|
+
/**
|
|
205
|
+
* @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
|
|
206
|
+
*/
|
|
170
207
|
writeDocumentJson(xml: string, options?: WriteDocumentOptions): Promise<void>;
|
|
171
208
|
queryByIdentifier(identifier: string, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
|
|
172
209
|
queryDocuments(query: XCiteQuery, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
|
|
@@ -187,22 +224,30 @@ export declare class XCiteDBClient {
|
|
|
187
224
|
}): Promise<boolean>;
|
|
188
225
|
appendMeta(identifier: string, value: unknown, path?: string): Promise<boolean>;
|
|
189
226
|
appendMetaByQuery(query: XCiteQuery, value: unknown, path?: string, firstMatch?: boolean): Promise<boolean>;
|
|
190
|
-
queryMeta(identifier: string, path?: string): Promise<
|
|
191
|
-
queryMetaByQuery(query: XCiteQuery, path?: string): Promise<
|
|
227
|
+
queryMeta<T = MetaValue>(identifier: string, path?: string): Promise<T>;
|
|
228
|
+
queryMetaByQuery<T = MetaValue>(query: XCiteQuery, path?: string): Promise<T>;
|
|
192
229
|
clearMeta(query: XCiteQuery): Promise<boolean>;
|
|
193
230
|
acquireLock(identifier: string, expires?: number): Promise<LockInfo>;
|
|
194
231
|
releaseLock(identifier: string, lockId: string): Promise<boolean>;
|
|
195
232
|
findLocks(identifier: string): Promise<LockInfo[]>;
|
|
196
|
-
unquery(query: XCiteQuery, unquery: unknown): Promise<
|
|
233
|
+
unquery<T = UnqueryResult>(query: XCiteQuery, unquery: unknown): Promise<T>;
|
|
197
234
|
search(q: TextSearchQuery): Promise<TextSearchResult>;
|
|
198
235
|
reindex(): Promise<{
|
|
199
236
|
status: string;
|
|
200
237
|
message: string;
|
|
201
238
|
}>;
|
|
202
239
|
writeJsonDocument(identifier: string, data: unknown): Promise<void>;
|
|
203
|
-
readJsonDocument(identifier: string): Promise<
|
|
240
|
+
readJsonDocument<T = unknown>(identifier: string): Promise<T>;
|
|
204
241
|
deleteJsonDocument(identifier: string): Promise<void>;
|
|
205
242
|
listJsonDocuments(match?: string, limit?: number, offset?: number): Promise<ListIdentifiersResult>;
|
|
243
|
+
/** JSON document shorthand — same as {@link writeJsonDocument}. */
|
|
244
|
+
put(identifier: string, data: unknown): Promise<void>;
|
|
245
|
+
/** JSON document read — same as {@link readJsonDocument}. */
|
|
246
|
+
get<T = unknown>(identifier: string): Promise<T>;
|
|
247
|
+
/** JSON document delete — same as {@link deleteJsonDocument}. */
|
|
248
|
+
remove(identifier: string): Promise<void>;
|
|
249
|
+
/** List JSON document keys — same as {@link listJsonDocuments}. */
|
|
250
|
+
list(match?: string, limit?: number, offset?: number): Promise<ListIdentifiersResult>;
|
|
206
251
|
/**
|
|
207
252
|
* WebSocket `/api/v1/ws` — optional initial subscription pattern.
|
|
208
253
|
* Uses `access_token` or `api_key` query params when headers are not available (browser).
|
package/dist/client.js
CHANGED
|
@@ -31,6 +31,50 @@ class XCiteDBClient {
|
|
|
31
31
|
this.onSessionTokensUpdated = options.onSessionTokensUpdated;
|
|
32
32
|
this.onAppUserTokensUpdated = options.onAppUserTokensUpdated;
|
|
33
33
|
this.onSessionInvalid = options.onSessionInvalid;
|
|
34
|
+
this.testSessionToken = options.testSessionToken;
|
|
35
|
+
this.testRequireAuth = options.testRequireAuth === true;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create an ephemeral isolated database: calls `POST /api/v1/test/sessions` with your API key or Bearer,
|
|
39
|
+
* then returns a client that sends `X-Test-Session` (auth-free by default).
|
|
40
|
+
*/
|
|
41
|
+
static async createTestSession(opts) {
|
|
42
|
+
const temp = new XCiteDBClient({
|
|
43
|
+
baseUrl: opts.baseUrl,
|
|
44
|
+
apiKey: opts.apiKey,
|
|
45
|
+
accessToken: opts.accessToken,
|
|
46
|
+
appUserAccessToken: opts.appUserAccessToken,
|
|
47
|
+
appUserRefreshToken: opts.appUserRefreshToken,
|
|
48
|
+
context: opts.context,
|
|
49
|
+
platformConsole: opts.platformConsole,
|
|
50
|
+
projectId: opts.projectId,
|
|
51
|
+
onSessionTokensUpdated: opts.onSessionTokensUpdated,
|
|
52
|
+
onAppUserTokensUpdated: opts.onAppUserTokensUpdated,
|
|
53
|
+
onSessionInvalid: opts.onSessionInvalid,
|
|
54
|
+
});
|
|
55
|
+
const data = await temp.request('POST', '/api/v1/test/sessions', undefined, undefined, { no401Retry: true });
|
|
56
|
+
return new XCiteDBClient({
|
|
57
|
+
baseUrl: opts.baseUrl,
|
|
58
|
+
apiKey: opts.testRequireAuth ? opts.apiKey : undefined,
|
|
59
|
+
accessToken: opts.testRequireAuth ? opts.accessToken : undefined,
|
|
60
|
+
appUserAccessToken: opts.testRequireAuth ? opts.appUserAccessToken : undefined,
|
|
61
|
+
appUserRefreshToken: opts.testRequireAuth ? opts.appUserRefreshToken : undefined,
|
|
62
|
+
context: opts.context,
|
|
63
|
+
platformConsole: opts.platformConsole,
|
|
64
|
+
projectId: opts.projectId,
|
|
65
|
+
onSessionTokensUpdated: opts.onSessionTokensUpdated,
|
|
66
|
+
onAppUserTokensUpdated: opts.onAppUserTokensUpdated,
|
|
67
|
+
onSessionInvalid: opts.onSessionInvalid,
|
|
68
|
+
testSessionToken: data.session_token,
|
|
69
|
+
testRequireAuth: opts.testRequireAuth,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Destroy this test session on the server (`DELETE /api/v1/test/sessions/current`). */
|
|
73
|
+
async destroyTestSession() {
|
|
74
|
+
if (!this.testSessionToken) {
|
|
75
|
+
throw new Error('destroyTestSession: client has no testSessionToken');
|
|
76
|
+
}
|
|
77
|
+
return this.request('DELETE', '/api/v1/test/sessions/current', undefined, undefined, { no401Retry: true });
|
|
34
78
|
}
|
|
35
79
|
/** True if this client would send API key or Bearer credentials on a normal request. */
|
|
36
80
|
sentAuthCredentials() {
|
|
@@ -101,11 +145,13 @@ class XCiteDBClient {
|
|
|
101
145
|
h['X-Date'] = c.date;
|
|
102
146
|
if (c.prefix)
|
|
103
147
|
h['X-Prefix'] = c.prefix;
|
|
148
|
+
if (c.unversioned)
|
|
149
|
+
h['X-Unversioned'] = 'true';
|
|
104
150
|
return h;
|
|
105
151
|
}
|
|
106
152
|
/** Include `tenant_id` for public app-auth routes when using only app-user tokens (no developer key/JWT). */
|
|
107
153
|
mergeAppTenant(body) {
|
|
108
|
-
const tid = this.defaultContext.tenant_id;
|
|
154
|
+
const tid = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
|
|
109
155
|
if (tid)
|
|
110
156
|
return { ...body, tenant_id: tid };
|
|
111
157
|
return body;
|
|
@@ -130,6 +176,16 @@ class XCiteDBClient {
|
|
|
130
176
|
}
|
|
131
177
|
return h;
|
|
132
178
|
}
|
|
179
|
+
testHeaders() {
|
|
180
|
+
const h = {};
|
|
181
|
+
if (this.testSessionToken) {
|
|
182
|
+
h['X-Test-Session'] = this.testSessionToken;
|
|
183
|
+
if (this.testRequireAuth) {
|
|
184
|
+
h['X-Test-Auth'] = 'required';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return h;
|
|
188
|
+
}
|
|
133
189
|
async request(method, path, body, extraHeaders, opts) {
|
|
134
190
|
const no401Retry = opts?.no401Retry === true;
|
|
135
191
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
@@ -137,6 +193,7 @@ class XCiteDBClient {
|
|
|
137
193
|
const headers = {
|
|
138
194
|
...this.authHeaders(),
|
|
139
195
|
...this.contextHeaders(),
|
|
196
|
+
...this.testHeaders(),
|
|
140
197
|
...extraHeaders,
|
|
141
198
|
};
|
|
142
199
|
let init = { method, headers };
|
|
@@ -214,8 +271,8 @@ class XCiteDBClient {
|
|
|
214
271
|
return this.request('GET', '/api/v1/version');
|
|
215
272
|
}
|
|
216
273
|
/**
|
|
217
|
-
*
|
|
218
|
-
*
|
|
274
|
+
* @deprecated Use {@link platformLogin} for platform operator sign-in, or {@link loginAppUser} for app end-users.
|
|
275
|
+
* This method only performs platform console login.
|
|
219
276
|
*/
|
|
220
277
|
async login(email, password) {
|
|
221
278
|
return this.platformLogin(email, password);
|
|
@@ -257,12 +314,19 @@ class XCiteDBClient {
|
|
|
257
314
|
async platformWorkspaces() {
|
|
258
315
|
return this.request('GET', '/api/v1/platform/auth/workspaces');
|
|
259
316
|
}
|
|
317
|
+
/** @deprecated Prefer {@link listMyProjects}. */
|
|
260
318
|
async listMyTenants() {
|
|
319
|
+
return this.listMyProjects();
|
|
320
|
+
}
|
|
321
|
+
/** Lists projects the platform user can access (from workspaces). */
|
|
322
|
+
async listMyProjects() {
|
|
261
323
|
const w = await this.platformWorkspaces();
|
|
262
324
|
const tenants = (w.projects ?? []).map((p) => {
|
|
263
325
|
const r = p;
|
|
326
|
+
const id = r.tenant_id || r.project_id || '';
|
|
264
327
|
return {
|
|
265
|
-
|
|
328
|
+
project_id: id,
|
|
329
|
+
tenant_id: id,
|
|
266
330
|
org_id: r.org_id,
|
|
267
331
|
name: r.name,
|
|
268
332
|
status: r.status,
|
|
@@ -277,13 +341,8 @@ class XCiteDBClient {
|
|
|
277
341
|
}
|
|
278
342
|
return tenants;
|
|
279
343
|
}
|
|
280
|
-
/** Alias for {@link listMyTenants} (organization/project terminology). */
|
|
281
|
-
async listMyProjects() {
|
|
282
|
-
return this.listMyTenants();
|
|
283
|
-
}
|
|
284
344
|
/**
|
|
285
|
-
*
|
|
286
|
-
* Legacy `/api/v1/auth/switch-tenant` has been removed; non-platform callers should set context instead.
|
|
345
|
+
* @deprecated Use {@link switchProject}. Platform console: updates `X-Project-Id` only (no token exchange).
|
|
287
346
|
*/
|
|
288
347
|
async switchTenant(tenantId) {
|
|
289
348
|
if (this.platformConsole) {
|
|
@@ -316,26 +375,23 @@ class XCiteDBClient {
|
|
|
316
375
|
await this.request('DELETE', `/api/v1/project/keys/${encodeURIComponent(keyId)}`);
|
|
317
376
|
}
|
|
318
377
|
// --- App user auth (requires developer API key or JWT on the same tenant) ---
|
|
319
|
-
async registerAppUser(email, password, displayName,
|
|
378
|
+
async registerAppUser(email, password, displayName, attributes) {
|
|
320
379
|
const body = { email, password };
|
|
321
380
|
if (displayName !== undefined)
|
|
322
381
|
body.display_name = displayName;
|
|
323
|
-
if (groups !== undefined)
|
|
324
|
-
body.groups = groups;
|
|
325
382
|
if (attributes !== undefined)
|
|
326
383
|
body.attributes = attributes;
|
|
327
384
|
return this.request('POST', '/api/v1/app/auth/register', this.mergeAppTenant(body));
|
|
328
385
|
}
|
|
329
386
|
async getOAuthProviders() {
|
|
330
|
-
const tid = this.defaultContext.tenant_id;
|
|
387
|
+
const tid = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
|
|
331
388
|
const q = buildQuery({ tenant_id: tid && String(tid).length > 0 ? String(tid) : 'default' });
|
|
332
389
|
return this.request('GET', `/api/v1/app/auth/oauth/providers${q}`);
|
|
333
390
|
}
|
|
334
391
|
/** Relative path + query for browser navigation to start OAuth (append to API base URL). */
|
|
335
392
|
oauthAuthorizePath(provider) {
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
: 'default';
|
|
393
|
+
const raw = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
|
|
394
|
+
const tid = raw && String(raw).length > 0 ? String(raw) : 'default';
|
|
339
395
|
return `/api/v1/app/auth/oauth/${encodeURIComponent(provider)}/authorize${buildQuery({ tenant_id: tid })}`;
|
|
340
396
|
}
|
|
341
397
|
/** Exchange one-time session code from OAuth browser redirect (public + tenant_id). */
|
|
@@ -591,19 +647,55 @@ class XCiteDBClient {
|
|
|
591
647
|
body.auto_resolve = options?.autoResolve ?? 'none';
|
|
592
648
|
return this.request('POST', `/api/v1/branches/${encodeURIComponent(targetBranch)}/merge`, body);
|
|
593
649
|
}
|
|
594
|
-
/**
|
|
650
|
+
/**
|
|
651
|
+
* Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
|
|
652
|
+
* create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
|
|
653
|
+
* Restores previous {@link DatabaseContext} afterward.
|
|
654
|
+
*/
|
|
655
|
+
async withBranch(branchName, fn, options) {
|
|
656
|
+
const prev = { ...this.defaultContext };
|
|
657
|
+
const fromBranch = options?.fromBranch ?? prev.branch ?? '';
|
|
658
|
+
let commit;
|
|
659
|
+
let merge;
|
|
660
|
+
try {
|
|
661
|
+
await this.createBranch(branchName, fromBranch || undefined, prev.date || undefined);
|
|
662
|
+
this.setContext({ branch: branchName });
|
|
663
|
+
const result = await fn(this);
|
|
664
|
+
commit = await this.createCommit(options?.message ?? `Branch ${branchName}`, options?.author);
|
|
665
|
+
if (options?.autoMerge !== false) {
|
|
666
|
+
merge = await this.mergeBranch(fromBranch, branchName, {
|
|
667
|
+
message: options?.message,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
return { result, commit, merge };
|
|
671
|
+
}
|
|
672
|
+
finally {
|
|
673
|
+
this.setContext(prev);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeXmlDocument`. */
|
|
595
677
|
async writeXML(xml, _options) {
|
|
596
678
|
await this.request('POST', '/api/v1/documents', xml, {
|
|
597
679
|
'Content-Type': 'application/xml',
|
|
598
680
|
});
|
|
599
681
|
}
|
|
600
|
-
|
|
682
|
+
/**
|
|
683
|
+
* Write an **XML** document using a JSON request body (`xml` field). The identifier is taken from `db:identifier` on the root element.
|
|
684
|
+
* For storing JSON data by key, use `writeJsonDocument`.
|
|
685
|
+
*/
|
|
686
|
+
async writeXmlDocument(xml, options) {
|
|
601
687
|
await this.request('POST', '/api/v1/documents', {
|
|
602
688
|
xml,
|
|
603
689
|
is_top: options?.is_top ?? true,
|
|
604
690
|
compare_attributes: options?.compare_attributes ?? false,
|
|
605
691
|
});
|
|
606
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
|
|
695
|
+
*/
|
|
696
|
+
async writeDocumentJson(xml, options) {
|
|
697
|
+
return this.writeXmlDocument(xml, options);
|
|
698
|
+
}
|
|
607
699
|
async queryByIdentifier(identifier, flags, filter, pathFilter) {
|
|
608
700
|
const q = buildQuery({
|
|
609
701
|
identifier,
|
|
@@ -865,6 +957,22 @@ class XCiteDBClient {
|
|
|
865
957
|
}
|
|
866
958
|
return { identifiers: [], total: 0, offset: 0, limit: 0 };
|
|
867
959
|
}
|
|
960
|
+
/** JSON document shorthand — same as {@link writeJsonDocument}. */
|
|
961
|
+
async put(identifier, data) {
|
|
962
|
+
return this.writeJsonDocument(identifier, data);
|
|
963
|
+
}
|
|
964
|
+
/** JSON document read — same as {@link readJsonDocument}. */
|
|
965
|
+
async get(identifier) {
|
|
966
|
+
return this.readJsonDocument(identifier);
|
|
967
|
+
}
|
|
968
|
+
/** JSON document delete — same as {@link deleteJsonDocument}. */
|
|
969
|
+
async remove(identifier) {
|
|
970
|
+
return this.deleteJsonDocument(identifier);
|
|
971
|
+
}
|
|
972
|
+
/** List JSON document keys — same as {@link listJsonDocuments}. */
|
|
973
|
+
async list(match, limit, offset) {
|
|
974
|
+
return this.listJsonDocuments(match, limit, offset);
|
|
975
|
+
}
|
|
868
976
|
/**
|
|
869
977
|
* WebSocket `/api/v1/ws` — optional initial subscription pattern.
|
|
870
978
|
* Uses `access_token` or `api_key` query params when headers are not available (browser).
|
|
@@ -882,6 +990,9 @@ class XCiteDBClient {
|
|
|
882
990
|
...this.authHeaders(),
|
|
883
991
|
...this.contextHeaders(),
|
|
884
992
|
};
|
|
993
|
+
const tid = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
|
|
994
|
+
if (tid)
|
|
995
|
+
headers['tenant_id'] = tid;
|
|
885
996
|
const sub = new websocket_1.WebSocketSubscription(wsBase, headers);
|
|
886
997
|
sub.onMessage(callback);
|
|
887
998
|
if (onError)
|
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, DatabaseContext, Flags, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, LogEntry, PolicyUpdateResponse, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, WriteDocumentOptions, XCiteDBClientOptions, XCiteQuery, } from './types';
|
|
3
|
+
export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, DatabaseContext, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, UnqueryResult, XCiteQuery, } from './types';
|
|
4
4
|
export { XCiteDBError } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -14,6 +14,19 @@ export interface XCiteQuery {
|
|
|
14
14
|
/** Skip this many rows (for pagination). */
|
|
15
15
|
offset?: number;
|
|
16
16
|
}
|
|
17
|
+
/** Root object returned by `readJsonDocument` (use generics for app-specific shapes). */
|
|
18
|
+
export type JsonDocumentData = Record<string, unknown>;
|
|
19
|
+
/** JSON metadata from meta APIs (structure is document-specific). */
|
|
20
|
+
export type MetaValue = unknown;
|
|
21
|
+
/** Result of `unquery` (shape depends on the unquery definition). */
|
|
22
|
+
export type UnqueryResult = unknown;
|
|
23
|
+
/** Typical `POST /api/v1/platform/auth/register` response (fields vary by registration policy). */
|
|
24
|
+
export interface PlatformRegisterResult {
|
|
25
|
+
user_id?: string;
|
|
26
|
+
status?: string;
|
|
27
|
+
message?: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
17
30
|
/** Full-text search (`POST /api/v1/search`) */
|
|
18
31
|
export interface TextSearchQuery {
|
|
19
32
|
query: string;
|
|
@@ -77,8 +90,14 @@ export interface TokenPair {
|
|
|
77
90
|
refresh_token: string;
|
|
78
91
|
expires_in: number;
|
|
79
92
|
}
|
|
80
|
-
/**
|
|
81
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Project row from `GET /api/v1/platform/auth/workspaces` and related APIs.
|
|
95
|
+
* The server often labels the id as `tenant_id`; both fields are set when mapped by the SDK.
|
|
96
|
+
*/
|
|
97
|
+
export interface ProjectInfo {
|
|
98
|
+
/** Project id (wire alias: `tenant_id` in many JSON bodies). */
|
|
99
|
+
project_id: string;
|
|
100
|
+
/** Same id as `project_id` (legacy / wire field name). */
|
|
82
101
|
tenant_id: string;
|
|
83
102
|
org_id?: string;
|
|
84
103
|
name: string;
|
|
@@ -88,6 +107,8 @@ export interface OwnedTenantInfo {
|
|
|
88
107
|
config?: unknown;
|
|
89
108
|
owner_user_id?: string;
|
|
90
109
|
}
|
|
110
|
+
/** @deprecated Use {@link ProjectInfo}. */
|
|
111
|
+
export type OwnedTenantInfo = ProjectInfo;
|
|
91
112
|
export interface UserInfo {
|
|
92
113
|
user_id: string;
|
|
93
114
|
username: string;
|
|
@@ -117,7 +138,7 @@ export interface PlatformWorkspaceOrg {
|
|
|
117
138
|
}
|
|
118
139
|
export interface PlatformWorkspacesResponse {
|
|
119
140
|
orgs: PlatformWorkspaceOrg[];
|
|
120
|
-
projects:
|
|
141
|
+
projects: ProjectInfo[];
|
|
121
142
|
}
|
|
122
143
|
/** One row from `GET /api/v1/project/keys` (secret is never returned). */
|
|
123
144
|
export interface ApiKeyInfo {
|
|
@@ -127,13 +148,24 @@ export interface ApiKeyInfo {
|
|
|
127
148
|
expires_at: number;
|
|
128
149
|
/** `secret` = server-side full access; `public` = client-safe, restricted. */
|
|
129
150
|
key_type?: 'secret' | 'public';
|
|
151
|
+
/** Project id this key belongs to. */
|
|
152
|
+
tenant_id?: string;
|
|
130
153
|
}
|
|
131
154
|
export type Flags = 'None' | 'FirstMatch' | 'IncludeChildren' | 'NoChildren' | 'KeepIndexNodes' | 'PrevIfDeleted' | string;
|
|
132
155
|
export interface DatabaseContext {
|
|
133
156
|
branch?: string;
|
|
134
157
|
date?: string;
|
|
135
158
|
prefix?: string;
|
|
136
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* When true, sends `X-Unversioned: true` so writes use flat LMDB keys (no date revision).
|
|
161
|
+
* Do not combine with `date`; the server returns 400 if both are set.
|
|
162
|
+
*/
|
|
163
|
+
unversioned?: boolean;
|
|
164
|
+
/** Preferred: project id; sent as `tenant_id` in app-user public auth bodies when no developer token is used. */
|
|
165
|
+
project_id?: string;
|
|
166
|
+
/**
|
|
167
|
+
* @deprecated Use {@link DatabaseContext.project_id} (same JSON field name on the wire: `tenant_id`).
|
|
168
|
+
*/
|
|
137
169
|
tenant_id?: string;
|
|
138
170
|
}
|
|
139
171
|
export interface WriteDocumentOptions {
|
|
@@ -162,7 +194,7 @@ export interface XCiteDBClientOptions {
|
|
|
162
194
|
context?: DatabaseContext;
|
|
163
195
|
/** When true, sends `X-Project-Id` for console requests; developer JWTs are always platform tokens. */
|
|
164
196
|
platformConsole?: boolean;
|
|
165
|
-
/** Active project id for platform console requests (`X-Project-Id` header). */
|
|
197
|
+
/** Active project id for platform console requests (`X-Project-Id` header). Alias of {@link DatabaseContext.project_id} for console mode. */
|
|
166
198
|
projectId?: string;
|
|
167
199
|
/** Persist developer session tokens after refresh (e.g. localStorage). */
|
|
168
200
|
onSessionTokensUpdated?: (pair: TokenPair) => void;
|
|
@@ -173,6 +205,29 @@ export interface XCiteDBClientOptions {
|
|
|
173
205
|
* Use to clear stored credentials and redirect to sign-in. Not invoked for login/register-style paths.
|
|
174
206
|
*/
|
|
175
207
|
onSessionInvalid?: () => void;
|
|
208
|
+
/**
|
|
209
|
+
* Ephemeral BaaS test session token from `POST /api/v1/test/sessions`.
|
|
210
|
+
* Sends `X-Test-Session`; use with {@link XCiteDBClient.createTestSession}.
|
|
211
|
+
*/
|
|
212
|
+
testSessionToken?: string;
|
|
213
|
+
/** When true with `testSessionToken`, sends `X-Test-Auth: required` so real credentials are validated. */
|
|
214
|
+
testRequireAuth?: boolean;
|
|
215
|
+
}
|
|
216
|
+
/** Options for {@link XCiteDBClient.createTestSession} (provisions via API key or Bearer). */
|
|
217
|
+
export interface CreateTestSessionOptions {
|
|
218
|
+
baseUrl: string;
|
|
219
|
+
apiKey?: string;
|
|
220
|
+
accessToken?: string;
|
|
221
|
+
appUserAccessToken?: string;
|
|
222
|
+
appUserRefreshToken?: string;
|
|
223
|
+
context?: DatabaseContext;
|
|
224
|
+
platformConsole?: boolean;
|
|
225
|
+
projectId?: string;
|
|
226
|
+
/** Keep `apiKey` / `accessToken` on the client and send `X-Test-Auth: required` on each request. */
|
|
227
|
+
testRequireAuth?: boolean;
|
|
228
|
+
onSessionTokensUpdated?: (pair: TokenPair) => void;
|
|
229
|
+
onAppUserTokensUpdated?: (pair: AppUserTokenPair) => void;
|
|
230
|
+
onSessionInvalid?: () => void;
|
|
176
231
|
}
|
|
177
232
|
/** Application user (tenant-scoped), distinct from developer users. */
|
|
178
233
|
export interface AppUser {
|
package/dist/websocket.js
CHANGED
|
@@ -19,6 +19,11 @@ class WebSocketSubscription {
|
|
|
19
19
|
const token = this.headerParams['Authorization']?.replace(/^Bearer\s+/i, '');
|
|
20
20
|
if (token)
|
|
21
21
|
u.searchParams.set('access_token', token);
|
|
22
|
+
if (!u.searchParams.has('access_token')) {
|
|
23
|
+
const appUser = this.headerParams['X-App-User-Token'];
|
|
24
|
+
if (appUser)
|
|
25
|
+
u.searchParams.set('access_token', appUser);
|
|
26
|
+
}
|
|
22
27
|
const apiKey = this.headerParams['X-API-Key'];
|
|
23
28
|
if (apiKey)
|
|
24
29
|
u.searchParams.set('api_key', apiKey);
|
package/llms-full.txt
CHANGED
|
@@ -14,7 +14,7 @@ Before reading the full reference, note these critical differences from typical
|
|
|
14
14
|
|
|
15
15
|
3. **Documents are XML or JSON, not arbitrary blobs.** XML documents are the primary document type. JSON documents are a parallel store. Both are fully versioned.
|
|
16
16
|
|
|
17
|
-
4. **Context (branch + date) travels as HTTP headers** (`X-Branch`, `X-Date`), not URL path segments.
|
|
17
|
+
4. **Context (branch + date) travels as HTTP headers** (`X-Branch`, `X-Date`, and optionally `X-Unversioned` for explicit flat writes), not URL path segments.
|
|
18
18
|
|
|
19
19
|
5. **XML documents carry their identifier inside the XML** via a `db:identifier` attribute on the root element.
|
|
20
20
|
|
|
@@ -22,6 +22,32 @@ Before reading the full reference, note these critical differences from typical
|
|
|
22
22
|
|
|
23
23
|
7. **The query model is NOT SQL.** Use REST query parameters (`match`, `match_start`, `contains`, `regex`) or the **Unquery** JSON DSL.
|
|
24
24
|
|
|
25
|
+
8. **Project vs tenant id.** Prefer SDK `context.project_id` and `listMyProjects` / `switchProject`. JSON bodies often use the field name `tenant_id` for the same value.
|
|
26
|
+
|
|
27
|
+
9. **OpenAPI:** See repository `docs/openapi.yaml` for a machine-readable route map.
|
|
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, normal JWT/API-key checks are bypassed for those requests (synthetic admin for wet tests). Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`. The test store starts empty (no cloned production project config).
|
|
30
|
+
|
|
31
|
+
## Common Pitfalls
|
|
32
|
+
|
|
33
|
+
1. **`baseUrl` must be origin-only (no `/api` or `/api/v1` path).** The SDK prepends `/api/v1/…` to every request. If `baseUrl` is `https://host/api/v1`, requests hit `/api/v1/api/v1/…` which typically returns **405 Not Allowed** from the reverse proxy. Use only the scheme + host + optional port: `https://host` or `http://localhost:8080`.
|
|
34
|
+
|
|
35
|
+
2. **Browser WebSocket requires correct CORS and proxy config.** Browsers add an `Origin` header to WebSocket connections — make sure your project's CORS allowed-origins includes your app's origin. Reverse proxies (nginx) must forward WebSocket `Upgrade` requests to XCiteDB's `/api/v1/ws` endpoint (the default `docker/nginx.conf` already does this). For production deployments that need fine-grained server-side filtering or short-lived auth tickets, consider proxying the WebSocket through your own backend:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
Browser ──ws──▶ Your API server ──ws──▶ XCiteDB /api/v1/ws
|
|
39
|
+
(same origin, (server-to-server,
|
|
40
|
+
short ticket) secret API key + user JWT)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
3. **Prefer `context.project_id` over `platformConsole` for project-scoped API keys.** `platformConsole` / `projectId` is designed for platform-operator keys that select a project dynamically via the `X-Project-Id` header. For project-scoped keys (which already identify their project), set **`context.project_id`** instead — it adds `tenant_id` to app-auth JSON bodies via `mergeAppTenant()` without sending `X-Project-Id`.
|
|
44
|
+
|
|
45
|
+
4. **Prefer `https://` in `baseUrl` for remote/production hosts.** XciteDB's default nginx uses a method-preserving **308** redirect (POST stays POST), but third-party proxies or older configurations may use **301** which silently downgrades POST to GET. Using `https://` directly avoids the redirect entirely.
|
|
46
|
+
|
|
47
|
+
5. **`context.project_id` (or `tenant_id`) is required for app-user self-registration.** `registerAppUser()` uses `mergeAppTenant(body)` to add `tenant_id` to the JSON body only when `context.project_id` or `context.tenant_id` is set. If both are omitted, the server cannot determine which project to register the user in. Always set `project_id` in the constructor `context` when calling `registerAppUser`, `loginAppUser`, and other public app-auth methods.
|
|
48
|
+
|
|
49
|
+
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()`.
|
|
50
|
+
|
|
25
51
|
---
|
|
26
52
|
|
|
27
53
|
# Part 1: Product Overview
|
|
@@ -72,7 +98,7 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
|
|
|
72
98
|
- **Response:** Data is returned as structured JSON or XML.
|
|
73
99
|
|
|
74
100
|
### 2.2 Storage & Document Model
|
|
75
|
-
- **LMDB Backed:** Memory-mapped key-value store organized into specialized sub-databases (`nodes`, `ids`, `meta`, `
|
|
101
|
+
- **LMDB Backed:** Memory-mapped key-value store organized into specialized sub-databases (`nodes`, `ids`, `meta`, `vcs_data`).
|
|
76
102
|
- **XML Documents:** Stored natively via pugixml and shredded into elements, attributes, and text nodes. Addressed by hierarchical, path-like identifiers (e.g., `/manual/v1/chapter1`).
|
|
77
103
|
- **JSON Documents:** Stored as standalone structured documents keyed by string. Shredded into objects, fields, and array elements.
|
|
78
104
|
- **JSON Metadata on XML:** JSON metadata can be attached to any XML document or path.
|
|
@@ -81,7 +107,7 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
|
|
|
81
107
|
### 2.3 Git-Like Document Versioning
|
|
82
108
|
- **Branches & Commits:** Isolate collaborative edits into branches. Create atomic commits with descriptive messages.
|
|
83
109
|
- **Merge & Cherry-Pick:** Merge branches or cherry-pick specific commits.
|
|
84
|
-
- **Time Travel:** Read historical state at any point in time using the `X-Date` header.
|
|
110
|
+
- **Time Travel:** Read historical state at any point in time using the `X-Date` header. For **writes**, `X-Unversioned: true` explicitly requests flat (unversioned) storage; it conflicts with `X-Date` (**400**).
|
|
85
111
|
- **Cooperative Locking:** TTL locks prevent conflicting writes (HTTP 409 on conflict).
|
|
86
112
|
|
|
87
113
|
### 2.4 Unquery: Declarative Query DSL
|
|
@@ -116,6 +142,7 @@ Welcome to the **XciteDB HTTP API**. This reference describes REST endpoints und
|
|
|
116
142
|
| `X-Project-Id` | Platform console / multi-project JWT | Selects the **tenant project** when the token is not already bound to one tenant |
|
|
117
143
|
| `X-Branch` | Optional | Active branch name for document and versioning operations |
|
|
118
144
|
| `X-Date` | Optional | Point-in-time / revision context (ISO-like string as used by your deployment) |
|
|
145
|
+
| `X-Unversioned` | Optional | When `true` or `1`, **writes** use flat LMDB keys (no date revision). Must not be combined with `X-Date` (**400**). Omitting `X-Date` remains valid (implicit unversioned). |
|
|
119
146
|
|
|
120
147
|
## Errors
|
|
121
148
|
|
|
@@ -157,6 +184,26 @@ Browser and mobile apps can use OAuth2-style flows against `/api/v1/app/auth/oau
|
|
|
157
184
|
|
|
158
185
|
---
|
|
159
186
|
|
|
187
|
+
# Ephemeral test sessions
|
|
188
|
+
|
|
189
|
+
For **integration and wet tests** against a shared BaaS host without touching production data:
|
|
190
|
+
|
|
191
|
+
| Step | What to do |
|
|
192
|
+
|------|------------|
|
|
193
|
+
| **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). |
|
|
194
|
+
| **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. |
|
|
195
|
+
| **Auth** | **Default:** test requests **skip** normal auth (convenient for automated tests). **`X-Test-Auth: required`:** require normal Bearer or API key; identity and ABAC apply, but data still comes from the test session DB. |
|
|
196
|
+
| **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. |
|
|
197
|
+
| **CORS** | Browsers may need **`X-Test-Session`** and **`X-Test-Auth`** in the deployment’s allowed CORS headers (defaults include them). |
|
|
198
|
+
|
|
199
|
+
**SDK usage (summary):**
|
|
200
|
+
|
|
201
|
+
- **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`.
|
|
202
|
+
- **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.
|
|
203
|
+
- **C++:** `XCiteDBClient::create_test_session(options)` after setting `api_key` (and optional `test_require_auth`); `destroy_test_session()`.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
160
207
|
# Health, version & discovery
|
|
161
208
|
|
|
162
209
|
## Health
|
|
@@ -876,7 +923,7 @@ const client = new XCiteDBClient({
|
|
|
876
923
|
await client.health();
|
|
877
924
|
|
|
878
925
|
// Write XML document
|
|
879
|
-
await client.
|
|
926
|
+
await client.writeXmlDocument(
|
|
880
927
|
'<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
|
|
881
928
|
);
|
|
882
929
|
|
|
@@ -918,7 +965,9 @@ interface DatabaseContext {
|
|
|
918
965
|
branch?: string; // '' = default (root timeline)
|
|
919
966
|
date?: string; // Point-in-time
|
|
920
967
|
prefix?: string; // Identifier prefix filter
|
|
921
|
-
|
|
968
|
+
unversioned?: boolean; // Sends X-Unversioned: true (flat writes; do not combine with date)
|
|
969
|
+
project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
|
|
970
|
+
tenant_id?: string; // Deprecated alias of project_id
|
|
922
971
|
}
|
|
923
972
|
```
|
|
924
973
|
|
|
@@ -930,15 +979,17 @@ interface DatabaseContext {
|
|
|
930
979
|
|
|
931
980
|
### Platform Auth
|
|
932
981
|
- `platformLogin(email, password)` → `TokenPair`
|
|
933
|
-
- `login(email, password)` → `TokenPair`
|
|
982
|
+
- `login(email, password)` → `TokenPair` — **deprecated**; use `platformLogin` (operator) or `loginAppUser` (end-user)
|
|
934
983
|
- `refresh()` → `TokenPair`
|
|
935
984
|
- `logout()` → `void`
|
|
936
985
|
- `me()` → `UserInfo`
|
|
937
|
-
- `platformRegister(body)` → `
|
|
986
|
+
- `platformRegister(body)` → `PlatformRegisterResult`
|
|
938
987
|
- `platformRegistrationConfig()` → `PlatformRegistrationConfig`
|
|
939
988
|
- `platformWorkspaces()` → `PlatformWorkspacesResponse`
|
|
940
|
-
- `
|
|
941
|
-
- `
|
|
989
|
+
- `listMyProjects()` → `ProjectInfo[]` — preferred
|
|
990
|
+
- `listMyTenants()` → `ProjectInfo[]` — **deprecated**; same as `listMyProjects`
|
|
991
|
+
- `switchProject(projectId)` → `void` (platform console only) — preferred
|
|
992
|
+
- `switchTenant(tenantId)` → `void` — **deprecated**; same as `switchProject`
|
|
942
993
|
- `changePassword(current, new)` → `void`
|
|
943
994
|
|
|
944
995
|
### Context & Configuration
|
|
@@ -949,7 +1000,7 @@ interface DatabaseContext {
|
|
|
949
1000
|
- `clearAppUserTokens()` → `void`
|
|
950
1001
|
|
|
951
1002
|
### XML Documents
|
|
952
|
-
- `
|
|
1003
|
+
- `writeXmlDocument(xml, options?)` → `void` — XML via JSON body (recommended). Deprecated: `writeDocumentJson`.
|
|
953
1004
|
- `writeXML(xml)` → `void` — Raw XML body
|
|
954
1005
|
- `queryByIdentifier(identifier, flags?, filter?, pathFilter?)` → `string[]`
|
|
955
1006
|
- `queryDocuments(query: XCiteQuery, flags?, filter?, pathFilter?)` → `string[]`
|
|
@@ -965,20 +1016,22 @@ interface DatabaseContext {
|
|
|
965
1016
|
|
|
966
1017
|
### JSON Documents
|
|
967
1018
|
- `writeJsonDocument(identifier, data)` → `void`
|
|
968
|
-
- `readJsonDocument(identifier)` → `unknown`
|
|
1019
|
+
- `readJsonDocument<T>(identifier)` → `T` (default `unknown`)
|
|
969
1020
|
- `deleteJsonDocument(identifier)` → `void`
|
|
970
1021
|
- `listJsonDocuments(match?, limit?, offset?)` → `ListIdentifiersResult`
|
|
1022
|
+
- `put(identifier, data)` / `get<T>(identifier)` / `remove(identifier)` / `list(match?, limit?, offset?)` — JSON CRUD aliases
|
|
971
1023
|
|
|
972
1024
|
### Metadata
|
|
973
1025
|
- `addMeta(identifier, value, path?, opts?)` → `boolean`
|
|
974
1026
|
- `addMetaByQuery(query, value, path?, firstMatch?, opts?)` → `boolean`
|
|
975
1027
|
- `appendMeta(identifier, value, path?)` → `boolean`
|
|
976
1028
|
- `appendMetaByQuery(query, value, path?, firstMatch?)` → `boolean`
|
|
977
|
-
- `queryMeta(identifier, path?)` → `
|
|
978
|
-
- `queryMetaByQuery(query, path?)` → `
|
|
1029
|
+
- `queryMeta<T>(identifier, path?)` → `T`
|
|
1030
|
+
- `queryMetaByQuery<T>(query, path?)` → `T`
|
|
979
1031
|
- `clearMeta(query)` → `boolean`
|
|
980
1032
|
|
|
981
1033
|
### Branches
|
|
1034
|
+
- `withBranch(name, fn, options?)` → `{ result, commit?, merge? }` — branch, callback, commit, merge back
|
|
982
1035
|
- `createBranch(name, fromBranch?, fromDate?)` → `void`
|
|
983
1036
|
- `listBranches()` → `BranchInfo[]`
|
|
984
1037
|
- `getBranch(name)` → `BranchInfo`
|
|
@@ -1012,7 +1065,7 @@ interface DatabaseContext {
|
|
|
1012
1065
|
- `reindex()` → `{ status, message }`
|
|
1013
1066
|
|
|
1014
1067
|
### Unquery
|
|
1015
|
-
- `unquery(query: XCiteQuery, unqueryDoc)` → `
|
|
1068
|
+
- `unquery<T>(query: XCiteQuery, unqueryDoc)` → `T`
|
|
1016
1069
|
|
|
1017
1070
|
### Security Policies
|
|
1018
1071
|
- `createPolicy(policyId, policy: SecurityPolicy)` → `StoredPolicyResponse`
|
|
@@ -1031,7 +1084,7 @@ interface DatabaseContext {
|
|
|
1031
1084
|
- `deleteTrigger(name)` → `void`
|
|
1032
1085
|
|
|
1033
1086
|
### App User Auth
|
|
1034
|
-
- `registerAppUser(email, password, displayName?,
|
|
1087
|
+
- `registerAppUser(email, password, displayName?, attributes?)` → `AppUser` (groups assigned from server config)
|
|
1035
1088
|
- `loginAppUser(email, password)` → `AppUserTokenPair`
|
|
1036
1089
|
- `refreshAppUser()` → `AppUserTokenPair`
|
|
1037
1090
|
- `logoutAppUser()` → `void`
|
|
@@ -1070,6 +1123,9 @@ interface DatabaseContext {
|
|
|
1070
1123
|
|
|
1071
1124
|
### WebSocket
|
|
1072
1125
|
- `subscribe(options: SubscriptionOptions, callback, onError?)` → `WebSocketSubscription`
|
|
1126
|
+
- Browser WebSocket cannot send HTTP headers. The SDK maps credentials to query params:
|
|
1127
|
+
`Authorization: Bearer <jwt>` → `?access_token=<jwt>`, `X-App-User-Token` → `?access_token=<jwt>` (fallback),
|
|
1128
|
+
`X-API-Key` → `?api_key=<key>`. The SDK also propagates `context.project_id` as `?tenant_id=`.
|
|
1073
1129
|
|
|
1074
1130
|
## Key Types
|
|
1075
1131
|
|
|
@@ -1116,7 +1172,12 @@ Install: `pip install xcitedb`
|
|
|
1116
1172
|
|
|
1117
1173
|
```python
|
|
1118
1174
|
import asyncio
|
|
1119
|
-
from xcitedb import
|
|
1175
|
+
from xcitedb import (
|
|
1176
|
+
XCiteDBClient,
|
|
1177
|
+
XCiteQuery,
|
|
1178
|
+
DatabaseContext,
|
|
1179
|
+
TextSearchQuery,
|
|
1180
|
+
)
|
|
1120
1181
|
|
|
1121
1182
|
async def main():
|
|
1122
1183
|
async with XCiteDBClient(
|
|
@@ -1125,13 +1186,20 @@ async def main():
|
|
|
1125
1186
|
context=DatabaseContext(branch="", date=""),
|
|
1126
1187
|
) as client:
|
|
1127
1188
|
print(await client.health())
|
|
1128
|
-
|
|
1129
|
-
|
|
1189
|
+
print(await client.query_documents(XCiteQuery(match_start="/manual/")))
|
|
1190
|
+
await client.write_json_document("app.settings", {"theme": "dark"})
|
|
1191
|
+
print(await client.read_json_document("app.settings"))
|
|
1192
|
+
print(await client.list_identifiers(XCiteQuery(match_start="/manual/")))
|
|
1193
|
+
print(await client.search(TextSearchQuery(query="guide", limit=10)))
|
|
1194
|
+
await client.platform_login("admin@localhost", "password")
|
|
1195
|
+
# await client.login_app_user("user@example.com", "pw") # set context.project_id / tenant_id if needed
|
|
1196
|
+
async with client.with_branch("feature-x", message="WIP", auto_merge=True):
|
|
1197
|
+
await client.put("app.settings", {"theme": "light"})
|
|
1130
1198
|
|
|
1131
1199
|
asyncio.run(main())
|
|
1132
1200
|
```
|
|
1133
1201
|
|
|
1134
|
-
|
|
1202
|
+
Async client: `write_xml_document` / `write_document_json` (deprecated), `write_json_document`, `read_json_document`, `list_json_documents`, `list_identifiers`, `search`, `reindex`, `platform_login` / `login` (deprecated), `login_app_user`, `refresh_app_user`, `logout_app_user`, `register_app_user`, `put` / `get` / `remove` / `list_documents` (JSON aliases), `with_branch` (async context manager).
|
|
1135
1203
|
|
|
1136
1204
|
---
|
|
1137
1205
|
|
|
@@ -1153,6 +1221,6 @@ q.match_start = "/manual/";
|
|
|
1153
1221
|
auto ids = client.query_documents(q);
|
|
1154
1222
|
```
|
|
1155
1223
|
|
|
1156
|
-
Synchronous (blocking) HTTP client. Methods mirror the JavaScript SDK with C++ naming. Errors throw `xcitedb::XCiteDBError` with `.status()` and `.body()`.
|
|
1224
|
+
Synchronous (blocking) HTTP client. Methods mirror the JavaScript SDK with C++ naming (`write_xml_document`, deprecated `write_document_json`). Errors throw `xcitedb::XCiteDBError` with `.status()` and `.body()`.
|
|
1157
1225
|
|
|
1158
1226
|
Includes optional `xcitevcs` CLI for command-line operations (branches, commits, documents, search, import/export).
|
package/llms.txt
CHANGED
|
@@ -20,6 +20,32 @@ These are the most common sources of confusion for developers and AI assistants:
|
|
|
20
20
|
|
|
21
21
|
7. **The query model is NOT SQL.** Listing/filtering documents uses query parameters on REST endpoints (`match`, `match_start`, `contains`, `regex`, `prefix`). Advanced analytics use **Unquery**, a JSON-defined declarative DSL posted to `POST /api/v1/unquery`.
|
|
22
22
|
|
|
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
|
+
|
|
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 those requests **skip** normal auth; send **`X-Test-Auth: required`** to exercise real JWT/API-key auth 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). The test DB starts **empty** (no copy of production project config or keys).
|
|
26
|
+
|
|
27
|
+
## Common Pitfalls
|
|
28
|
+
|
|
29
|
+
1. **`baseUrl` must be origin-only (no `/api` or `/api/v1` path).** The SDK prepends `/api/v1/…` to every request. If `baseUrl` is `https://host/api/v1`, requests hit `/api/v1/api/v1/…` which typically returns **405 Not Allowed** from the reverse proxy. Use only the scheme + host + optional port: `https://host` or `http://localhost:8080`.
|
|
30
|
+
|
|
31
|
+
2. **Browser WebSocket requires correct CORS and proxy config.** Browsers add an `Origin` header to WebSocket connections — make sure your project's CORS allowed-origins includes your app's origin. Reverse proxies (nginx) must forward WebSocket `Upgrade` requests to XCiteDB's `/api/v1/ws` endpoint (the default `docker/nginx.conf` already does this). For production deployments that need fine-grained server-side filtering or short-lived auth tickets, consider proxying the WebSocket through your own backend.
|
|
32
|
+
|
|
33
|
+
3. **Prefer `context.project_id` over `platformConsole` for project-scoped API keys.** `platformConsole` / `projectId` is designed for platform-operator keys that select a project dynamically via the `X-Project-Id` header. For project-scoped keys (which already identify their project), set **`context.project_id`** instead — it adds `tenant_id` to app-auth JSON bodies via `mergeAppTenant()` without sending `X-Project-Id`.
|
|
34
|
+
|
|
35
|
+
4. **Prefer `https://` in `baseUrl` for remote/production hosts.** XciteDB's default nginx uses a method-preserving **308** redirect (POST stays POST), but third-party proxies or older configurations may use **301** which silently downgrades POST to GET. Using `https://` directly avoids the redirect entirely.
|
|
36
|
+
|
|
37
|
+
5. **`context.project_id` (or `tenant_id`) is required for app-user self-registration.** `registerAppUser()` uses `mergeAppTenant(body)` to add `tenant_id` to the JSON body only when `context.project_id` or `context.tenant_id` is set. If both are omitted, the server cannot determine which project to register the user in. Always set `project_id` in the constructor `context` when calling `registerAppUser`, `loginAppUser`, and other public app-auth methods.
|
|
38
|
+
|
|
39
|
+
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()`.
|
|
40
|
+
|
|
41
|
+
## Test mode (how to use)
|
|
42
|
+
|
|
43
|
+
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.
|
|
44
|
+
2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-Branch` / `context` as needed). Data is stored under the server’s `_test/<session>/` area, not your production tenant.
|
|
45
|
+
3. **Auth behavior:** Omit extra headers for frictionless tests. To test policies and real identities, set **`X-Test-Auth: required`** and send normal credentials; the DB is still the test session’s.
|
|
46
|
+
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.
|
|
47
|
+
5. **SDKs:** **JS/TS:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`, optional `testRequireAuth`, then `destroyTestSession()`. **Python:** `async with XCiteDBClient.test_session(...)` or manual token + `test_session_token` / `test_require_auth` constructor args. **C++:** `XCiteDBClient::create_test_session(options)`, `destroy_test_session()`, optional `test_require_auth` in options.
|
|
48
|
+
|
|
23
49
|
## JavaScript/TypeScript SDK (`@xcitedbs/client`)
|
|
24
50
|
|
|
25
51
|
Install: `npm install @xcitedbs/client`
|
|
@@ -49,7 +75,7 @@ const docs = await client.queryDocuments({ match_start: '/manual/' });
|
|
|
49
75
|
const xml = await client.queryByIdentifier('/manual/v1/intro', 'FirstMatch');
|
|
50
76
|
|
|
51
77
|
// Write an XML document (identifier is inside the XML body)
|
|
52
|
-
await client.
|
|
78
|
+
await client.writeXmlDocument(
|
|
53
79
|
'<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
|
|
54
80
|
);
|
|
55
81
|
|
|
@@ -59,6 +85,18 @@ await client.writeJsonDocument('app.settings', { theme: 'dark', locale: 'en' });
|
|
|
59
85
|
// Read a JSON document
|
|
60
86
|
const settings = await client.readJsonDocument('app.settings');
|
|
61
87
|
|
|
88
|
+
// Quick JSON CRUD aliases (same as writeJsonDocument / readJsonDocument / deleteJsonDocument / listJsonDocuments)
|
|
89
|
+
await client.put('app.prefs', { theme: 'dark' });
|
|
90
|
+
const prefs = await client.get<Record<string, unknown>>('app.prefs');
|
|
91
|
+
await client.remove('app.prefs');
|
|
92
|
+
const keys = await client.list(undefined, 50, 0);
|
|
93
|
+
|
|
94
|
+
// Branch helper: create branch, run work, commit, merge back (restores context)
|
|
95
|
+
await client.withBranch('feature-x', async (c) => {
|
|
96
|
+
await c.writeJsonDocument('app.settings', { theme: 'light' });
|
|
97
|
+
return 'ok';
|
|
98
|
+
}, { message: 'Light theme', autoMerge: true, fromBranch: '' });
|
|
99
|
+
|
|
62
100
|
// Create a branch, make changes, commit, merge
|
|
63
101
|
await client.createBranch('feature-x');
|
|
64
102
|
client.setContext({ branch: 'feature-x' });
|
|
@@ -96,7 +134,8 @@ interface XCiteDBClientOptions {
|
|
|
96
134
|
branch?: string; // Branch name; '' = default/root timeline
|
|
97
135
|
date?: string; // Point-in-time (ISO-like or internal date key)
|
|
98
136
|
prefix?: string; // Optional identifier prefix filter
|
|
99
|
-
|
|
137
|
+
project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
|
|
138
|
+
tenant_id?: string; // Deprecated alias of project_id
|
|
100
139
|
};
|
|
101
140
|
platformConsole?: boolean; // true = use X-Project-Id header
|
|
102
141
|
projectId?: string; // Active project for platform console mode
|
|
@@ -114,7 +153,7 @@ interface XCiteDBClientOptions {
|
|
|
114
153
|
- `setProjectId(id)` — Switch active project (platform console)
|
|
115
154
|
|
|
116
155
|
**XML Documents:**
|
|
117
|
-
- `
|
|
156
|
+
- `writeXmlDocument(xml, options?)` — Store XML via JSON body (identifier inside XML). Deprecated alias: `writeDocumentJson`.
|
|
118
157
|
- `writeXML(xml)` — Store raw XML with `Content-Type: application/xml`
|
|
119
158
|
- `queryByIdentifier(id, flags?, filter?)` — Get document(s) by identifier
|
|
120
159
|
- `queryDocuments(query, flags?)` — List/filter documents
|
|
@@ -124,9 +163,10 @@ interface XCiteDBClientOptions {
|
|
|
124
163
|
|
|
125
164
|
**JSON Documents:**
|
|
126
165
|
- `writeJsonDocument(identifier, data)` — Store a JSON document
|
|
127
|
-
- `readJsonDocument(identifier)` — Read a JSON document
|
|
166
|
+
- `readJsonDocument(identifier)` — Read a JSON document (generic `readJsonDocument<T>()` supported)
|
|
128
167
|
- `deleteJsonDocument(identifier)` — Delete a JSON document
|
|
129
168
|
- `listJsonDocuments(match?, limit?, offset?)` — List JSON document keys
|
|
169
|
+
- **Quick JSON aliases:** `put`, `get`, `remove`, `list` — same as the four methods above
|
|
130
170
|
|
|
131
171
|
**Metadata (JSON on XML):**
|
|
132
172
|
- `addMeta(identifier, value, path?)` — Set metadata on a document
|
|
@@ -135,6 +175,7 @@ interface XCiteDBClientOptions {
|
|
|
135
175
|
- `clearMeta(query)` — Remove metadata
|
|
136
176
|
|
|
137
177
|
**Versioning:**
|
|
178
|
+
- `withBranch(name, fn, options?)` — Create branch, run callback on client, commit, merge back (optional `autoMerge: false`)
|
|
138
179
|
- `createBranch(name, fromBranch?, fromDate?)` — Create branch
|
|
139
180
|
- `listBranches()` — List all branches
|
|
140
181
|
- `mergeBranch(target, source, options?)` — Merge branches
|
|
@@ -167,11 +208,14 @@ interface XCiteDBClientOptions {
|
|
|
167
208
|
|
|
168
209
|
**App Users (admin):**
|
|
169
210
|
- `listAppUsers()` / `createAppUser()` / `deleteAppUser()` — Manage end-user accounts
|
|
170
|
-
- `registerAppUser()` — Self-registration
|
|
211
|
+
- `registerAppUser(email, password, displayName?, attributes?)` — Self-registration (groups assigned from server config)
|
|
171
212
|
- `getAppAuthConfig()` — Auth configuration
|
|
172
213
|
|
|
173
214
|
**WebSocket:**
|
|
174
215
|
- `subscribe(options, callback, onError?)` — Real-time document change notifications
|
|
216
|
+
- Browser WebSocket cannot send HTTP headers. The SDK maps credentials to query params:
|
|
217
|
+
`Authorization: Bearer <jwt>` → `?access_token=<jwt>`, `X-App-User-Token` → `?access_token=<jwt>` (fallback),
|
|
218
|
+
`X-API-Key` → `?api_key=<key>`. The SDK also propagates `context.project_id` as `?tenant_id=`.
|
|
175
219
|
|
|
176
220
|
### Query Flags
|
|
177
221
|
|
|
@@ -191,7 +235,7 @@ Install: `pip install xcitedb`
|
|
|
191
235
|
|
|
192
236
|
```python
|
|
193
237
|
import asyncio
|
|
194
|
-
from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext
|
|
238
|
+
from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext, TextSearchQuery
|
|
195
239
|
|
|
196
240
|
async def main():
|
|
197
241
|
async with XCiteDBClient(
|
|
@@ -202,6 +246,11 @@ async def main():
|
|
|
202
246
|
print(await client.health())
|
|
203
247
|
ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
|
|
204
248
|
print(ids)
|
|
249
|
+
await client.write_json_document("app.settings", {"theme": "dark"})
|
|
250
|
+
print(await client.read_json_document("app.settings"))
|
|
251
|
+
print(await client.search(TextSearchQuery(query="guide", limit=10)))
|
|
252
|
+
pair = await client.platform_login("admin@localhost", "password")
|
|
253
|
+
# await client.login_app_user("user@example.com", "secret", tenant_id="...")
|
|
205
254
|
|
|
206
255
|
asyncio.run(main())
|
|
207
256
|
```
|
|
@@ -234,7 +283,8 @@ auto ids = client.query_documents(q);
|
|
|
234
283
|
|
|
235
284
|
## Documentation
|
|
236
285
|
|
|
237
|
-
|
|
286
|
+
- **OpenAPI 3.1:** Repository file `docs/openapi.yaml` (machine-readable route map and shared schemas).
|
|
287
|
+
- Full API reference is also available at your XciteDB server's `/docs` path. Key sections:
|
|
238
288
|
- Getting started — Base URL, headers, errors, pagination
|
|
239
289
|
- Documents — XML document CRUD, identifiers, hierarchy
|
|
240
290
|
- JSON documents — JSON document CRUD
|