@xcitedbs/client 0.2.0 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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: 'main', date: '03/27/2026' },
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, OwnedTenantInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, XCiteDBClientOptions, XCiteQuery } from './types';
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, XCiteDBClientOptions, XCiteQuery } from './types';
2
2
  import { WebSocketSubscription } from './websocket';
3
3
  export declare class XCiteDBClient {
4
4
  private baseUrl;
@@ -48,8 +48,8 @@ export declare class XCiteDBClient {
48
48
  api_version: string;
49
49
  }>;
50
50
  /**
51
- * Platform console sign-in. The first argument is the account **email** (e.g. `admin@localhost`).
52
- * Legacy `/api/v1/auth/login` has been removed.
51
+ * @deprecated Use {@link platformLogin} for platform operator sign-in, or {@link loginAppUser} for app end-users.
52
+ * This method only performs platform console login.
53
53
  */
54
54
  login(email: string, password: string): Promise<TokenPair>;
55
55
  /** Platform console sign-in (`email` + `password`). Project context is `X-Project-Id`, not the JWT. */
@@ -67,14 +67,14 @@ export declare class XCiteDBClient {
67
67
  org_name?: string;
68
68
  org_id?: string;
69
69
  attributes?: Record<string, unknown>;
70
- }): Promise<unknown>;
70
+ }): Promise<PlatformRegisterResult>;
71
71
  platformWorkspaces(): Promise<PlatformWorkspacesResponse>;
72
- listMyTenants(): Promise<OwnedTenantInfo[]>;
73
- /** Alias for {@link listMyTenants} (organization/project terminology). */
74
- listMyProjects(): Promise<OwnedTenantInfo[]>;
72
+ /** @deprecated Prefer {@link listMyProjects}. */
73
+ listMyTenants(): Promise<ProjectInfo[]>;
74
+ /** Lists projects the platform user can access (from workspaces). */
75
+ listMyProjects(): Promise<ProjectInfo[]>;
75
76
  /**
76
- * Switch active tenant/project for API calls. Platform console: updates `X-Project-Id` only (no token exchange).
77
- * Legacy `/api/v1/auth/switch-tenant` has been removed; non-platform callers should set context instead.
77
+ * @deprecated Use {@link switchProject}. Platform console: updates `X-Project-Id` only (no token exchange).
78
78
  */
79
79
  switchTenant(tenantId: string): Promise<void>;
80
80
  /** Alias for {@link switchTenant}. */
@@ -165,8 +165,33 @@ export declare class XCiteDBClient {
165
165
  message?: string;
166
166
  autoResolve?: 'none' | 'source' | 'target';
167
167
  }): Promise<MergeResult>;
168
- /** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeDocumentJson`. */
168
+ /**
169
+ * Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
170
+ * create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
171
+ * Restores previous {@link DatabaseContext} afterward.
172
+ */
173
+ withBranch<T>(branchName: string, fn: (client: XCiteDBClient) => Promise<T>, options?: {
174
+ message?: string;
175
+ /** When true (default), merge `branchName` into the parent branch after commit. */
176
+ autoMerge?: boolean;
177
+ /** Branch to fork from (default: current `context.branch`, or `""`). */
178
+ fromBranch?: string;
179
+ author?: string;
180
+ }): Promise<{
181
+ result: T;
182
+ commit?: CommitRecord;
183
+ merge?: MergeResult;
184
+ }>;
185
+ /** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeXmlDocument`. */
169
186
  writeXML(xml: string, _options?: WriteDocumentOptions): Promise<void>;
187
+ /**
188
+ * Write an **XML** document using a JSON request body (`xml` field). The identifier is taken from `db:identifier` on the root element.
189
+ * For storing JSON data by key, use `writeJsonDocument`.
190
+ */
191
+ writeXmlDocument(xml: string, options?: WriteDocumentOptions): Promise<void>;
192
+ /**
193
+ * @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
194
+ */
170
195
  writeDocumentJson(xml: string, options?: WriteDocumentOptions): Promise<void>;
171
196
  queryByIdentifier(identifier: string, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
172
197
  queryDocuments(query: XCiteQuery, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
@@ -187,22 +212,30 @@ export declare class XCiteDBClient {
187
212
  }): Promise<boolean>;
188
213
  appendMeta(identifier: string, value: unknown, path?: string): Promise<boolean>;
189
214
  appendMetaByQuery(query: XCiteQuery, value: unknown, path?: string, firstMatch?: boolean): Promise<boolean>;
190
- queryMeta(identifier: string, path?: string): Promise<unknown>;
191
- queryMetaByQuery(query: XCiteQuery, path?: string): Promise<unknown>;
215
+ queryMeta<T = MetaValue>(identifier: string, path?: string): Promise<T>;
216
+ queryMetaByQuery<T = MetaValue>(query: XCiteQuery, path?: string): Promise<T>;
192
217
  clearMeta(query: XCiteQuery): Promise<boolean>;
193
218
  acquireLock(identifier: string, expires?: number): Promise<LockInfo>;
194
219
  releaseLock(identifier: string, lockId: string): Promise<boolean>;
195
220
  findLocks(identifier: string): Promise<LockInfo[]>;
196
- unquery(query: XCiteQuery, unquery: unknown): Promise<unknown>;
221
+ unquery<T = UnqueryResult>(query: XCiteQuery, unquery: unknown): Promise<T>;
197
222
  search(q: TextSearchQuery): Promise<TextSearchResult>;
198
223
  reindex(): Promise<{
199
224
  status: string;
200
225
  message: string;
201
226
  }>;
202
227
  writeJsonDocument(identifier: string, data: unknown): Promise<void>;
203
- readJsonDocument(identifier: string): Promise<unknown>;
228
+ readJsonDocument<T = unknown>(identifier: string): Promise<T>;
204
229
  deleteJsonDocument(identifier: string): Promise<void>;
205
230
  listJsonDocuments(match?: string, limit?: number, offset?: number): Promise<ListIdentifiersResult>;
231
+ /** JSON document shorthand — same as {@link writeJsonDocument}. */
232
+ put(identifier: string, data: unknown): Promise<void>;
233
+ /** JSON document read — same as {@link readJsonDocument}. */
234
+ get<T = unknown>(identifier: string): Promise<T>;
235
+ /** JSON document delete — same as {@link deleteJsonDocument}. */
236
+ remove(identifier: string): Promise<void>;
237
+ /** List JSON document keys — same as {@link listJsonDocuments}. */
238
+ list(match?: string, limit?: number, offset?: number): Promise<ListIdentifiersResult>;
206
239
  /**
207
240
  * WebSocket `/api/v1/ws` — optional initial subscription pattern.
208
241
  * Uses `access_token` or `api_key` query params when headers are not available (browser).
package/dist/client.js CHANGED
@@ -101,11 +101,13 @@ class XCiteDBClient {
101
101
  h['X-Date'] = c.date;
102
102
  if (c.prefix)
103
103
  h['X-Prefix'] = c.prefix;
104
+ if (c.unversioned)
105
+ h['X-Unversioned'] = 'true';
104
106
  return h;
105
107
  }
106
108
  /** Include `tenant_id` for public app-auth routes when using only app-user tokens (no developer key/JWT). */
107
109
  mergeAppTenant(body) {
108
- const tid = this.defaultContext.tenant_id;
110
+ const tid = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
109
111
  if (tid)
110
112
  return { ...body, tenant_id: tid };
111
113
  return body;
@@ -214,8 +216,8 @@ class XCiteDBClient {
214
216
  return this.request('GET', '/api/v1/version');
215
217
  }
216
218
  /**
217
- * Platform console sign-in. The first argument is the account **email** (e.g. `admin@localhost`).
218
- * Legacy `/api/v1/auth/login` has been removed.
219
+ * @deprecated Use {@link platformLogin} for platform operator sign-in, or {@link loginAppUser} for app end-users.
220
+ * This method only performs platform console login.
219
221
  */
220
222
  async login(email, password) {
221
223
  return this.platformLogin(email, password);
@@ -257,12 +259,19 @@ class XCiteDBClient {
257
259
  async platformWorkspaces() {
258
260
  return this.request('GET', '/api/v1/platform/auth/workspaces');
259
261
  }
262
+ /** @deprecated Prefer {@link listMyProjects}. */
260
263
  async listMyTenants() {
264
+ return this.listMyProjects();
265
+ }
266
+ /** Lists projects the platform user can access (from workspaces). */
267
+ async listMyProjects() {
261
268
  const w = await this.platformWorkspaces();
262
269
  const tenants = (w.projects ?? []).map((p) => {
263
270
  const r = p;
271
+ const id = r.tenant_id || r.project_id || '';
264
272
  return {
265
- tenant_id: r.tenant_id || r.project_id || '',
273
+ project_id: id,
274
+ tenant_id: id,
266
275
  org_id: r.org_id,
267
276
  name: r.name,
268
277
  status: r.status,
@@ -277,13 +286,8 @@ class XCiteDBClient {
277
286
  }
278
287
  return tenants;
279
288
  }
280
- /** Alias for {@link listMyTenants} (organization/project terminology). */
281
- async listMyProjects() {
282
- return this.listMyTenants();
283
- }
284
289
  /**
285
- * Switch active tenant/project for API calls. Platform console: updates `X-Project-Id` only (no token exchange).
286
- * Legacy `/api/v1/auth/switch-tenant` has been removed; non-platform callers should set context instead.
290
+ * @deprecated Use {@link switchProject}. Platform console: updates `X-Project-Id` only (no token exchange).
287
291
  */
288
292
  async switchTenant(tenantId) {
289
293
  if (this.platformConsole) {
@@ -327,15 +331,14 @@ class XCiteDBClient {
327
331
  return this.request('POST', '/api/v1/app/auth/register', this.mergeAppTenant(body));
328
332
  }
329
333
  async getOAuthProviders() {
330
- const tid = this.defaultContext.tenant_id;
334
+ const tid = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
331
335
  const q = buildQuery({ tenant_id: tid && String(tid).length > 0 ? String(tid) : 'default' });
332
336
  return this.request('GET', `/api/v1/app/auth/oauth/providers${q}`);
333
337
  }
334
338
  /** Relative path + query for browser navigation to start OAuth (append to API base URL). */
335
339
  oauthAuthorizePath(provider) {
336
- const tid = this.defaultContext.tenant_id && String(this.defaultContext.tenant_id).length > 0
337
- ? String(this.defaultContext.tenant_id)
338
- : 'default';
340
+ const raw = this.defaultContext.project_id ?? this.defaultContext.tenant_id;
341
+ const tid = raw && String(raw).length > 0 ? String(raw) : 'default';
339
342
  return `/api/v1/app/auth/oauth/${encodeURIComponent(provider)}/authorize${buildQuery({ tenant_id: tid })}`;
340
343
  }
341
344
  /** Exchange one-time session code from OAuth browser redirect (public + tenant_id). */
@@ -591,19 +594,55 @@ class XCiteDBClient {
591
594
  body.auto_resolve = options?.autoResolve ?? 'none';
592
595
  return this.request('POST', `/api/v1/branches/${encodeURIComponent(targetBranch)}/merge`, body);
593
596
  }
594
- /** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeDocumentJson`. */
597
+ /**
598
+ * Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
599
+ * create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
600
+ * Restores previous {@link DatabaseContext} afterward.
601
+ */
602
+ async withBranch(branchName, fn, options) {
603
+ const prev = { ...this.defaultContext };
604
+ const fromBranch = options?.fromBranch ?? prev.branch ?? '';
605
+ let commit;
606
+ let merge;
607
+ try {
608
+ await this.createBranch(branchName, fromBranch || undefined, prev.date || undefined);
609
+ this.setContext({ branch: branchName });
610
+ const result = await fn(this);
611
+ commit = await this.createCommit(options?.message ?? `Branch ${branchName}`, options?.author);
612
+ if (options?.autoMerge !== false) {
613
+ merge = await this.mergeBranch(fromBranch, branchName, {
614
+ message: options?.message,
615
+ });
616
+ }
617
+ return { result, commit, merge };
618
+ }
619
+ finally {
620
+ this.setContext(prev);
621
+ }
622
+ }
623
+ /** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeXmlDocument`. */
595
624
  async writeXML(xml, _options) {
596
625
  await this.request('POST', '/api/v1/documents', xml, {
597
626
  'Content-Type': 'application/xml',
598
627
  });
599
628
  }
600
- async writeDocumentJson(xml, options) {
629
+ /**
630
+ * Write an **XML** document using a JSON request body (`xml` field). The identifier is taken from `db:identifier` on the root element.
631
+ * For storing JSON data by key, use `writeJsonDocument`.
632
+ */
633
+ async writeXmlDocument(xml, options) {
601
634
  await this.request('POST', '/api/v1/documents', {
602
635
  xml,
603
636
  is_top: options?.is_top ?? true,
604
637
  compare_attributes: options?.compare_attributes ?? false,
605
638
  });
606
639
  }
640
+ /**
641
+ * @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
642
+ */
643
+ async writeDocumentJson(xml, options) {
644
+ return this.writeXmlDocument(xml, options);
645
+ }
607
646
  async queryByIdentifier(identifier, flags, filter, pathFilter) {
608
647
  const q = buildQuery({
609
648
  identifier,
@@ -865,6 +904,22 @@ class XCiteDBClient {
865
904
  }
866
905
  return { identifiers: [], total: 0, offset: 0, limit: 0 };
867
906
  }
907
+ /** JSON document shorthand — same as {@link writeJsonDocument}. */
908
+ async put(identifier, data) {
909
+ return this.writeJsonDocument(identifier, data);
910
+ }
911
+ /** JSON document read — same as {@link readJsonDocument}. */
912
+ async get(identifier) {
913
+ return this.readJsonDocument(identifier);
914
+ }
915
+ /** JSON document delete — same as {@link deleteJsonDocument}. */
916
+ async remove(identifier) {
917
+ return this.deleteJsonDocument(identifier);
918
+ }
919
+ /** List JSON document keys — same as {@link listJsonDocuments}. */
920
+ async list(match, limit, offset) {
921
+ return this.listJsonDocuments(match, limit, offset);
922
+ }
868
923
  /**
869
924
  * WebSocket `/api/v1/ws` — optional initial subscription pattern.
870
925
  * Uses `access_token` or `api_key` query params when headers are not available (browser).
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, 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
- /** Projects from `GET /api/v1/platform/auth/workspaces` (mapped for compatibility). */
81
- export interface OwnedTenantInfo {
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: OwnedTenantInfo[];
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
- /** Project/tenant id; sent as `tenant_id` in app-user public auth bodies when no developer token is used. */
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;
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,10 @@ 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
+
25
29
  ---
26
30
 
27
31
  # Part 1: Product Overview
@@ -72,7 +76,7 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
72
76
  - **Response:** Data is returned as structured JSON or XML.
73
77
 
74
78
  ### 2.2 Storage & Document Model
75
- - **LMDB Backed:** Memory-mapped key-value store organized into specialized sub-databases (`nodes`, `ids`, `meta`, `json_docs`).
79
+ - **LMDB Backed:** Memory-mapped key-value store organized into specialized sub-databases (`nodes`, `ids`, `meta`, `vcs_data`).
76
80
  - **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
81
  - **JSON Documents:** Stored as standalone structured documents keyed by string. Shredded into objects, fields, and array elements.
78
82
  - **JSON Metadata on XML:** JSON metadata can be attached to any XML document or path.
@@ -81,7 +85,7 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
81
85
  ### 2.3 Git-Like Document Versioning
82
86
  - **Branches & Commits:** Isolate collaborative edits into branches. Create atomic commits with descriptive messages.
83
87
  - **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.
88
+ - **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
89
  - **Cooperative Locking:** TTL locks prevent conflicting writes (HTTP 409 on conflict).
86
90
 
87
91
  ### 2.4 Unquery: Declarative Query DSL
@@ -116,6 +120,7 @@ Welcome to the **XciteDB HTTP API**. This reference describes REST endpoints und
116
120
  | `X-Project-Id` | Platform console / multi-project JWT | Selects the **tenant project** when the token is not already bound to one tenant |
117
121
  | `X-Branch` | Optional | Active branch name for document and versioning operations |
118
122
  | `X-Date` | Optional | Point-in-time / revision context (ISO-like string as used by your deployment) |
123
+ | `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
124
 
120
125
  ## Errors
121
126
 
@@ -876,7 +881,7 @@ const client = new XCiteDBClient({
876
881
  await client.health();
877
882
 
878
883
  // Write XML document
879
- await client.writeDocumentJson(
884
+ await client.writeXmlDocument(
880
885
  '<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
881
886
  );
882
887
 
@@ -918,7 +923,9 @@ interface DatabaseContext {
918
923
  branch?: string; // '' = default (root timeline)
919
924
  date?: string; // Point-in-time
920
925
  prefix?: string; // Identifier prefix filter
921
- tenant_id?: string; // For app-user auth
926
+ unversioned?: boolean; // Sends X-Unversioned: true (flat writes; do not combine with date)
927
+ project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
928
+ tenant_id?: string; // Deprecated alias of project_id
922
929
  }
923
930
  ```
924
931
 
@@ -930,15 +937,17 @@ interface DatabaseContext {
930
937
 
931
938
  ### Platform Auth
932
939
  - `platformLogin(email, password)` → `TokenPair`
933
- - `login(email, password)` → `TokenPair` (alias for platformLogin)
940
+ - `login(email, password)` → `TokenPair` **deprecated**; use `platformLogin` (operator) or `loginAppUser` (end-user)
934
941
  - `refresh()` → `TokenPair`
935
942
  - `logout()` → `void`
936
943
  - `me()` → `UserInfo`
937
- - `platformRegister(body)` → `unknown`
944
+ - `platformRegister(body)` → `PlatformRegisterResult`
938
945
  - `platformRegistrationConfig()` → `PlatformRegistrationConfig`
939
946
  - `platformWorkspaces()` → `PlatformWorkspacesResponse`
940
- - `listMyTenants()` / `listMyProjects()` → `OwnedTenantInfo[]`
941
- - `switchTenant(tenantId)` / `switchProject(projectId)` `void` (platform console only)
947
+ - `listMyProjects()` → `ProjectInfo[]` — preferred
948
+ - `listMyTenants()` `ProjectInfo[]` **deprecated**; same as `listMyProjects`
949
+ - `switchProject(projectId)` → `void` (platform console only) — preferred
950
+ - `switchTenant(tenantId)` → `void` — **deprecated**; same as `switchProject`
942
951
  - `changePassword(current, new)` → `void`
943
952
 
944
953
  ### Context & Configuration
@@ -949,7 +958,7 @@ interface DatabaseContext {
949
958
  - `clearAppUserTokens()` → `void`
950
959
 
951
960
  ### XML Documents
952
- - `writeDocumentJson(xml, options?)` → `void` — JSON wrapper (recommended)
961
+ - `writeXmlDocument(xml, options?)` → `void` — XML via JSON body (recommended). Deprecated: `writeDocumentJson`.
953
962
  - `writeXML(xml)` → `void` — Raw XML body
954
963
  - `queryByIdentifier(identifier, flags?, filter?, pathFilter?)` → `string[]`
955
964
  - `queryDocuments(query: XCiteQuery, flags?, filter?, pathFilter?)` → `string[]`
@@ -965,20 +974,22 @@ interface DatabaseContext {
965
974
 
966
975
  ### JSON Documents
967
976
  - `writeJsonDocument(identifier, data)` → `void`
968
- - `readJsonDocument(identifier)` → `unknown`
977
+ - `readJsonDocument<T>(identifier)` → `T` (default `unknown`)
969
978
  - `deleteJsonDocument(identifier)` → `void`
970
979
  - `listJsonDocuments(match?, limit?, offset?)` → `ListIdentifiersResult`
980
+ - `put(identifier, data)` / `get<T>(identifier)` / `remove(identifier)` / `list(match?, limit?, offset?)` — JSON CRUD aliases
971
981
 
972
982
  ### Metadata
973
983
  - `addMeta(identifier, value, path?, opts?)` → `boolean`
974
984
  - `addMetaByQuery(query, value, path?, firstMatch?, opts?)` → `boolean`
975
985
  - `appendMeta(identifier, value, path?)` → `boolean`
976
986
  - `appendMetaByQuery(query, value, path?, firstMatch?)` → `boolean`
977
- - `queryMeta(identifier, path?)` → `unknown`
978
- - `queryMetaByQuery(query, path?)` → `unknown`
987
+ - `queryMeta<T>(identifier, path?)` → `T`
988
+ - `queryMetaByQuery<T>(query, path?)` → `T`
979
989
  - `clearMeta(query)` → `boolean`
980
990
 
981
991
  ### Branches
992
+ - `withBranch(name, fn, options?)` → `{ result, commit?, merge? }` — branch, callback, commit, merge back
982
993
  - `createBranch(name, fromBranch?, fromDate?)` → `void`
983
994
  - `listBranches()` → `BranchInfo[]`
984
995
  - `getBranch(name)` → `BranchInfo`
@@ -1012,7 +1023,7 @@ interface DatabaseContext {
1012
1023
  - `reindex()` → `{ status, message }`
1013
1024
 
1014
1025
  ### Unquery
1015
- - `unquery(query: XCiteQuery, unqueryDoc)` → `unknown`
1026
+ - `unquery<T>(query: XCiteQuery, unqueryDoc)` → `T`
1016
1027
 
1017
1028
  ### Security Policies
1018
1029
  - `createPolicy(policyId, policy: SecurityPolicy)` → `StoredPolicyResponse`
@@ -1116,7 +1127,12 @@ Install: `pip install xcitedb`
1116
1127
 
1117
1128
  ```python
1118
1129
  import asyncio
1119
- from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext
1130
+ from xcitedb import (
1131
+ XCiteDBClient,
1132
+ XCiteQuery,
1133
+ DatabaseContext,
1134
+ TextSearchQuery,
1135
+ )
1120
1136
 
1121
1137
  async def main():
1122
1138
  async with XCiteDBClient(
@@ -1125,13 +1141,20 @@ async def main():
1125
1141
  context=DatabaseContext(branch="", date=""),
1126
1142
  ) as client:
1127
1143
  print(await client.health())
1128
- ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
1129
- print(ids)
1144
+ print(await client.query_documents(XCiteQuery(match_start="/manual/")))
1145
+ await client.write_json_document("app.settings", {"theme": "dark"})
1146
+ print(await client.read_json_document("app.settings"))
1147
+ print(await client.list_identifiers(XCiteQuery(match_start="/manual/")))
1148
+ print(await client.search(TextSearchQuery(query="guide", limit=10)))
1149
+ await client.platform_login("admin@localhost", "password")
1150
+ # await client.login_app_user("user@example.com", "pw") # set context.project_id / tenant_id if needed
1151
+ async with client.with_branch("feature-x", message="WIP", auto_merge=True):
1152
+ await client.put("app.settings", {"theme": "light"})
1130
1153
 
1131
1154
  asyncio.run(main())
1132
1155
  ```
1133
1156
 
1134
- Methods mirror the JavaScript SDK with Python naming conventions (e.g. `query_documents`, `write_json_document`).
1157
+ 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
1158
 
1136
1159
  ---
1137
1160
 
@@ -1153,6 +1176,6 @@ q.match_start = "/manual/";
1153
1176
  auto ids = client.query_documents(q);
1154
1177
  ```
1155
1178
 
1156
- Synchronous (blocking) HTTP client. Methods mirror the JavaScript SDK with C++ naming. Errors throw `xcitedb::XCiteDBError` with `.status()` and `.body()`.
1179
+ 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
1180
 
1158
1181
  Includes optional `xcitevcs` CLI for command-line operations (branches, commits, documents, search, import/export).
package/llms.txt CHANGED
@@ -20,6 +20,8 @@ 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
+
23
25
  ## JavaScript/TypeScript SDK (`@xcitedbs/client`)
24
26
 
25
27
  Install: `npm install @xcitedbs/client`
@@ -49,7 +51,7 @@ const docs = await client.queryDocuments({ match_start: '/manual/' });
49
51
  const xml = await client.queryByIdentifier('/manual/v1/intro', 'FirstMatch');
50
52
 
51
53
  // Write an XML document (identifier is inside the XML body)
52
- await client.writeDocumentJson(
54
+ await client.writeXmlDocument(
53
55
  '<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
54
56
  );
55
57
 
@@ -59,6 +61,18 @@ await client.writeJsonDocument('app.settings', { theme: 'dark', locale: 'en' });
59
61
  // Read a JSON document
60
62
  const settings = await client.readJsonDocument('app.settings');
61
63
 
64
+ // Quick JSON CRUD aliases (same as writeJsonDocument / readJsonDocument / deleteJsonDocument / listJsonDocuments)
65
+ await client.put('app.prefs', { theme: 'dark' });
66
+ const prefs = await client.get<Record<string, unknown>>('app.prefs');
67
+ await client.remove('app.prefs');
68
+ const keys = await client.list(undefined, 50, 0);
69
+
70
+ // Branch helper: create branch, run work, commit, merge back (restores context)
71
+ await client.withBranch('feature-x', async (c) => {
72
+ await c.writeJsonDocument('app.settings', { theme: 'light' });
73
+ return 'ok';
74
+ }, { message: 'Light theme', autoMerge: true, fromBranch: '' });
75
+
62
76
  // Create a branch, make changes, commit, merge
63
77
  await client.createBranch('feature-x');
64
78
  client.setContext({ branch: 'feature-x' });
@@ -96,7 +110,8 @@ interface XCiteDBClientOptions {
96
110
  branch?: string; // Branch name; '' = default/root timeline
97
111
  date?: string; // Point-in-time (ISO-like or internal date key)
98
112
  prefix?: string; // Optional identifier prefix filter
99
- tenant_id?: string; // Project/tenant ID for app-user auth
113
+ project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
114
+ tenant_id?: string; // Deprecated alias of project_id
100
115
  };
101
116
  platformConsole?: boolean; // true = use X-Project-Id header
102
117
  projectId?: string; // Active project for platform console mode
@@ -114,7 +129,7 @@ interface XCiteDBClientOptions {
114
129
  - `setProjectId(id)` — Switch active project (platform console)
115
130
 
116
131
  **XML Documents:**
117
- - `writeDocumentJson(xml, options?)` — Store XML (identifier inside XML body)
132
+ - `writeXmlDocument(xml, options?)` — Store XML via JSON body (identifier inside XML). Deprecated alias: `writeDocumentJson`.
118
133
  - `writeXML(xml)` — Store raw XML with `Content-Type: application/xml`
119
134
  - `queryByIdentifier(id, flags?, filter?)` — Get document(s) by identifier
120
135
  - `queryDocuments(query, flags?)` — List/filter documents
@@ -124,9 +139,10 @@ interface XCiteDBClientOptions {
124
139
 
125
140
  **JSON Documents:**
126
141
  - `writeJsonDocument(identifier, data)` — Store a JSON document
127
- - `readJsonDocument(identifier)` — Read a JSON document
142
+ - `readJsonDocument(identifier)` — Read a JSON document (generic `readJsonDocument<T>()` supported)
128
143
  - `deleteJsonDocument(identifier)` — Delete a JSON document
129
144
  - `listJsonDocuments(match?, limit?, offset?)` — List JSON document keys
145
+ - **Quick JSON aliases:** `put`, `get`, `remove`, `list` — same as the four methods above
130
146
 
131
147
  **Metadata (JSON on XML):**
132
148
  - `addMeta(identifier, value, path?)` — Set metadata on a document
@@ -135,6 +151,7 @@ interface XCiteDBClientOptions {
135
151
  - `clearMeta(query)` — Remove metadata
136
152
 
137
153
  **Versioning:**
154
+ - `withBranch(name, fn, options?)` — Create branch, run callback on client, commit, merge back (optional `autoMerge: false`)
138
155
  - `createBranch(name, fromBranch?, fromDate?)` — Create branch
139
156
  - `listBranches()` — List all branches
140
157
  - `mergeBranch(target, source, options?)` — Merge branches
@@ -191,7 +208,7 @@ Install: `pip install xcitedb`
191
208
 
192
209
  ```python
193
210
  import asyncio
194
- from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext
211
+ from xcitedb import XCiteDBClient, XCiteQuery, DatabaseContext, TextSearchQuery
195
212
 
196
213
  async def main():
197
214
  async with XCiteDBClient(
@@ -202,6 +219,11 @@ async def main():
202
219
  print(await client.health())
203
220
  ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
204
221
  print(ids)
222
+ await client.write_json_document("app.settings", {"theme": "dark"})
223
+ print(await client.read_json_document("app.settings"))
224
+ print(await client.search(TextSearchQuery(query="guide", limit=10)))
225
+ pair = await client.platform_login("admin@localhost", "password")
226
+ # await client.login_app_user("user@example.com", "secret", tenant_id="...")
205
227
 
206
228
  asyncio.run(main())
207
229
  ```
@@ -234,7 +256,8 @@ auto ids = client.query_documents(q);
234
256
 
235
257
  ## Documentation
236
258
 
237
- Full API reference is available at your XciteDB server's `/docs` path. Key sections:
259
+ - **OpenAPI 3.1:** Repository file `docs/openapi.yaml` (machine-readable route map and shared schemas).
260
+ - Full API reference is also available at your XciteDB server's `/docs` path. Key sections:
238
261
  - Getting started — Base URL, headers, errors, pagination
239
262
  - Documents — XML document CRUD, identifiers, hierarchy
240
263
  - JSON documents — JSON document CRUD
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcitedbs/client",
3
- "version": "0.2.0",
3
+ "version": "0.2.5",
4
4
  "description": "XCiteDB BaaS client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",