@xcitedbs/client 0.2.21 → 0.2.23

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/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, DocumentBatchResponse, Flags, JsonDocumentBatchItem, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LogEntry, MergeResult, PublishResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, TestSessionBootstrapSummary, TestSessionInfo, XCiteQuery, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationShareResult } from './types';
1
+ import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, DocumentBatchResponse, DocumentExportFormat, ExportDocumentResult, Flags, JsonDocumentBatchItem, ImportDocumentOptions, ImportDocumentResult, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LogEntry, MergeResult, PublishResult, RebaseUserWorkspaceResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, TestSessionBootstrapSummary, TestSessionInfo, XCiteQuery, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationShareResult } from './types';
2
2
  import { WebSocketSubscription } from './websocket';
3
3
  export declare class XCiteDBClient {
4
4
  private baseUrl;
@@ -93,6 +93,12 @@ export declare class XCiteDBClient {
93
93
  private testHeaders;
94
94
  private requestHeaders;
95
95
  private request;
96
+ /**
97
+ * POST `multipart/form-data` without forcing JSON `Content-Type` (boundary is set by the runtime).
98
+ */
99
+ private requestFormPost;
100
+ /** GET binary response (e.g. document export); errors are parsed as JSON when possible. */
101
+ private requestBinaryGet;
96
102
  /** Developer Bearer refresh first, then app-user refresh (no API key refresh). */
97
103
  private tryRefreshSessionAfter401;
98
104
  private refreshAppUserNoRetry;
@@ -399,8 +405,16 @@ export declare class XCiteDBClient {
399
405
  /** @deprecated Use {@link deleteBookmark}. */
400
406
  deleteTag(name: string): Promise<void>;
401
407
  compare(from: CompareRef, to: CompareRef, includeContent?: boolean): Promise<CompareResult>;
408
+ compare(from: CompareRef, to: CompareRef, options?: {
409
+ includeContent?: boolean;
410
+ matchStart?: string;
411
+ }): Promise<CompareResult>;
402
412
  /** @deprecated Use {@link compare}. */
403
413
  diff(from: DiffRef, to: DiffRef, includeContent?: boolean): Promise<DiffResult>;
414
+ diff(from: DiffRef, to: DiffRef, options?: {
415
+ includeContent?: boolean;
416
+ matchStart?: string;
417
+ }): Promise<DiffResult>;
404
418
  publishWorkspace(targetWorkspace: string, sourceWorkspace: string, options?: {
405
419
  message?: string;
406
420
  autoResolve?: 'none' | 'source' | 'target';
@@ -440,6 +454,13 @@ export declare class XCiteDBClient {
440
454
  message?: string;
441
455
  autoResolve?: string;
442
456
  }): Promise<Record<string, unknown>>;
457
+ /**
458
+ * Advance the user workspace fork to the current parent tip (`POST /api/v1/user-workspaces/{id}/rebase`).
459
+ * On overlap with local edits, returns **409** with `conflicts` (same shape as publish) unless `autoResolve` resolves it.
460
+ */
461
+ rebaseUserWorkspace(id: string, options?: {
462
+ autoResolve?: 'none' | 'source' | 'target';
463
+ }): Promise<RebaseUserWorkspaceResult>;
443
464
  /**
444
465
  * Create `workspaceName` from {@link options.fromBranch} (or current context), run `fn` scoped to that workspace,
445
466
  * create a checkpoint, then publish back unless {@link options.autoMerge} is `false`.
@@ -481,11 +502,37 @@ export declare class XCiteDBClient {
481
502
  * Rows with `error === 'lock_conflict'` include `current_lock` (code **423** per row).
482
503
  */
483
504
  writeXmlDocumentsBatch(items: XmlDocumentBatchItem[]): Promise<DocumentBatchResponse>;
505
+ /**
506
+ * Import a document (`POST /api/v1/documents/import`). Server converts DOCX, ODF, RTF, PDF, Markdown, AsciiDoc, or plain text
507
+ * to shredded XML. Field name must be `file` (multipart).
508
+ */
509
+ importDocument(file: Blob | ArrayBuffer | Uint8Array, options?: ImportDocumentOptions): Promise<ImportDocumentResult>;
510
+ /**
511
+ * Export a document (`GET /api/v1/documents/export`). Returns binary bytes and response metadata.
512
+ */
513
+ exportDocument(identifier: string, format?: DocumentExportFormat, options?: {
514
+ strict?: boolean;
515
+ }): Promise<ExportDocumentResult>;
484
516
  /**
485
517
  * @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
486
518
  */
487
519
  writeDocumentJson(xml: string, options?: WriteDocumentOptions): Promise<void>;
488
520
  queryByIdentifier(identifier: string, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
521
+ /**
522
+ * Shallow read: root element with inline leaves plus `db:N*` placeholders for shredded child slots
523
+ * (`flags=NoChildren,KeepIndexNodes,FirstMatch` on `GET /api/v1/documents/by-id`). Use with {@link listChildIdentifiers}
524
+ * or {@link queryByIdentifierWithChildren} for sidebar / AST navigation without loading the full subtree.
525
+ */
526
+ queryByIdentifierShallow(identifier: string, filter?: string, pathFilter?: string): Promise<string[]>;
527
+ /** Alias of {@link listIdentifierChildren} — immediate child segments under `parentPath` (identifier hierarchy). */
528
+ listChildIdentifiers(parentPath?: string): Promise<ListIdentifierChildrenResult>;
529
+ /**
530
+ * Parallel shallow node + one level of identifier children (`GET /by-id` + `GET /identifier-children`).
531
+ */
532
+ queryByIdentifierWithChildren(identifier: string, filter?: string, pathFilter?: string): Promise<{
533
+ node: string[];
534
+ children: IdentifierChildNode[];
535
+ }>;
489
536
  queryDocuments(query: XCiteQuery, flags?: Flags, filter?: string, pathFilter?: string): Promise<string[]>;
490
537
  deleteDocument(identifier: string): Promise<void>;
491
538
  addIdentifier(identifier: string): Promise<boolean>;
package/dist/client.js CHANGED
@@ -56,6 +56,24 @@ function buildQuery(params) {
56
56
  const s = sp.toString();
57
57
  return s ? `?${s}` : '';
58
58
  }
59
+ /** Best-effort filename from `Content-Disposition` (attachment; filename="…"). */
60
+ function parseContentDispositionFilename(cd) {
61
+ if (!cd)
62
+ return undefined;
63
+ const mStar = cd.match(/filename\*=(?:UTF-8'')?([^;]+)/i);
64
+ if (mStar?.[1]) {
65
+ try {
66
+ return decodeURIComponent(mStar[1].trim().replace(/^"|"$/g, ''));
67
+ }
68
+ catch {
69
+ return mStar[1].trim().replace(/^"|"$/g, '');
70
+ }
71
+ }
72
+ const m = cd.match(/filename\s*=\s*("?)([^";]+)\1/i);
73
+ if (m?.[2])
74
+ return m[2].trim();
75
+ return undefined;
76
+ }
59
77
  function warnIfHttpOnTlsPort(baseUrl) {
60
78
  try {
61
79
  const u = new URL(baseUrl);
@@ -590,6 +608,122 @@ class XCiteDBClient {
590
608
  this.notifySessionInvalidIfNeeded(path, 401);
591
609
  throw new types_1.XCiteDBError('Request failed after retry', 401, null);
592
610
  }
611
+ /**
612
+ * POST `multipart/form-data` without forcing JSON `Content-Type` (boundary is set by the runtime).
613
+ */
614
+ async requestFormPost(path, form, opts) {
615
+ const no401Retry = opts?.no401Retry === true;
616
+ const suppressTestSessionHeader = opts?.suppressTestSessionHeader === true;
617
+ for (let attempt = 0; attempt < 2; attempt++) {
618
+ const url = joinUrl(this.baseUrl, path);
619
+ const outgoingRequestId = newClientRequestId();
620
+ const headers = {
621
+ ...this.authHeaders(),
622
+ ...this.contextHeaders(),
623
+ ...(suppressTestSessionHeader ? {} : this.testHeaders()),
624
+ 'X-Request-Id': outgoingRequestId,
625
+ };
626
+ const init = { method: 'POST', headers, body: form };
627
+ const sig = requestTimeoutSignal(this.requestTimeoutMs);
628
+ if (sig)
629
+ init.signal = sig;
630
+ let res;
631
+ try {
632
+ res = await fetch(url, init);
633
+ }
634
+ catch (e) {
635
+ const m = e instanceof Error ? e.message : 'Network error';
636
+ throw new types_1.XCiteDBError(m, 0, null, { clientRequestId: outgoingRequestId });
637
+ }
638
+ const text = await res.text();
639
+ let data;
640
+ try {
641
+ data = text ? JSON.parse(text) : null;
642
+ }
643
+ catch {
644
+ data = text;
645
+ }
646
+ if (res.ok) {
647
+ return data;
648
+ }
649
+ if (res.status === 401 &&
650
+ attempt === 0 &&
651
+ !no401Retry &&
652
+ (await this.tryRefreshSessionAfter401())) {
653
+ continue;
654
+ }
655
+ const msg = typeof data === 'object' && data !== null && 'message' in data
656
+ ? String(data.message)
657
+ : res.statusText;
658
+ this.notifySessionInvalidIfNeeded(path, res.status);
659
+ throwForFailedHttp(res.status, path, data, msg || `HTTP ${res.status}`, res.headers.get('X-Request-Id') ?? undefined, res.headers.get('X-Client-Request-Id') ?? undefined);
660
+ }
661
+ this.notifySessionInvalidIfNeeded(path, 401);
662
+ throw new types_1.XCiteDBError('Request failed after retry', 401, null);
663
+ }
664
+ /** GET binary response (e.g. document export); errors are parsed as JSON when possible. */
665
+ async requestBinaryGet(path, opts) {
666
+ const no401Retry = opts?.no401Retry === true;
667
+ const suppressTestSessionHeader = opts?.suppressTestSessionHeader === true;
668
+ for (let attempt = 0; attempt < 2; attempt++) {
669
+ const url = joinUrl(this.baseUrl, path);
670
+ const outgoingRequestId = newClientRequestId();
671
+ const headers = {
672
+ ...this.authHeaders(),
673
+ ...this.contextHeaders(),
674
+ ...(suppressTestSessionHeader ? {} : this.testHeaders()),
675
+ 'X-Request-Id': outgoingRequestId,
676
+ };
677
+ const init = { method: 'GET', headers };
678
+ const sig = requestTimeoutSignal(this.requestTimeoutMs);
679
+ if (sig)
680
+ init.signal = sig;
681
+ let res;
682
+ try {
683
+ res = await fetch(url, init);
684
+ }
685
+ catch (e) {
686
+ const m = e instanceof Error ? e.message : 'Network error';
687
+ throw new types_1.XCiteDBError(m, 0, null, { clientRequestId: outgoingRequestId });
688
+ }
689
+ if (res.ok) {
690
+ const buf = new Uint8Array(await res.arrayBuffer());
691
+ const contentType = res.headers.get('Content-Type') ?? 'application/octet-stream';
692
+ const fn = parseContentDispositionFilename(res.headers.get('Content-Disposition')) ?? 'export.bin';
693
+ const rt = res.headers.get('X-XciteDB-Export-RoundTrip');
694
+ const roundTrip = rt === 'exact' || rt === 'lossy' ? rt : undefined;
695
+ const w = res.headers.get('X-XciteDB-Export-Warning');
696
+ return {
697
+ bytes: buf,
698
+ contentType,
699
+ filename: fn,
700
+ ...(roundTrip ? { roundTrip } : {}),
701
+ ...(w ? { warning: w } : {}),
702
+ };
703
+ }
704
+ if (res.status === 401 &&
705
+ attempt === 0 &&
706
+ !no401Retry &&
707
+ (await this.tryRefreshSessionAfter401())) {
708
+ continue;
709
+ }
710
+ const text = await res.text();
711
+ let data;
712
+ try {
713
+ data = text ? JSON.parse(text) : null;
714
+ }
715
+ catch {
716
+ data = text;
717
+ }
718
+ const msg = typeof data === 'object' && data !== null && 'message' in data
719
+ ? String(data.message)
720
+ : res.statusText;
721
+ this.notifySessionInvalidIfNeeded(path, res.status);
722
+ throwForFailedHttp(res.status, path, data, msg || `HTTP ${res.status}`, res.headers.get('X-Request-Id') ?? undefined, res.headers.get('X-Client-Request-Id') ?? undefined);
723
+ }
724
+ this.notifySessionInvalidIfNeeded(path, 401);
725
+ throw new types_1.XCiteDBError('Request failed after retry', 401, null);
726
+ }
593
727
  /** Developer Bearer refresh first, then app-user refresh (no API key refresh). */
594
728
  async tryRefreshSessionAfter401() {
595
729
  if (this.accessToken && this.refreshToken) {
@@ -1221,16 +1355,35 @@ class XCiteDBClient {
1221
1355
  async deleteTag(name) {
1222
1356
  return this.deleteBookmark(name);
1223
1357
  }
1224
- async compare(from, to, includeContent) {
1225
- return this.request('POST', '/api/v1/compare', {
1358
+ async compare(from, to, third) {
1359
+ let include_content = false;
1360
+ let match_start;
1361
+ if (typeof third === 'boolean') {
1362
+ include_content = third;
1363
+ }
1364
+ else if (third !== undefined && typeof third === 'object') {
1365
+ include_content = third.includeContent ?? false;
1366
+ match_start = third.matchStart;
1367
+ }
1368
+ const body = {
1226
1369
  from,
1227
1370
  to,
1228
- include_content: includeContent ?? false,
1229
- });
1371
+ include_content,
1372
+ };
1373
+ if (match_start !== undefined && match_start !== '') {
1374
+ body.match_start = this.isoPrefixId(match_start);
1375
+ }
1376
+ const r = await this.request('POST', '/api/v1/compare', body);
1377
+ if (r && typeof r === 'object' && typeof r.match_start === 'string' && r.match_start.length > 0) {
1378
+ r.match_start = this.isoUnprefixId(r.match_start);
1379
+ }
1380
+ return r;
1230
1381
  }
1231
- /** @deprecated Use {@link compare}. */
1232
- async diff(from, to, includeContent) {
1233
- return this.compare(from, to, includeContent);
1382
+ async diff(from, to, third) {
1383
+ if (third === undefined || typeof third === 'boolean') {
1384
+ return this.compare(from, to, third);
1385
+ }
1386
+ return this.compare(from, to, third);
1234
1387
  }
1235
1388
  async publishWorkspace(targetWorkspace, sourceWorkspace, options) {
1236
1389
  const body = {
@@ -1277,6 +1430,14 @@ class XCiteDBClient {
1277
1430
  body.message = options.message;
1278
1431
  return this.request('POST', `/api/v1/user-workspaces/${encodeURIComponent(id)}/publish`, body);
1279
1432
  }
1433
+ /**
1434
+ * Advance the user workspace fork to the current parent tip (`POST /api/v1/user-workspaces/{id}/rebase`).
1435
+ * On overlap with local edits, returns **409** with `conflicts` (same shape as publish) unless `autoResolve` resolves it.
1436
+ */
1437
+ async rebaseUserWorkspace(id, options) {
1438
+ const body = { auto_resolve: options?.autoResolve ?? 'none' };
1439
+ return this.request('POST', `/api/v1/user-workspaces/${encodeURIComponent(id)}/rebase`, body);
1440
+ }
1280
1441
  /**
1281
1442
  * Create `workspaceName` from {@link options.fromBranch} (or current context), run `fn` scoped to that workspace,
1282
1443
  * create a checkpoint, then publish back unless {@link options.autoMerge} is `false`.
@@ -1354,6 +1515,49 @@ class XCiteDBClient {
1354
1515
  }
1355
1516
  return r;
1356
1517
  }
1518
+ /**
1519
+ * Import a document (`POST /api/v1/documents/import`). Server converts DOCX, ODF, RTF, PDF, Markdown, AsciiDoc, or plain text
1520
+ * to shredded XML. Field name must be `file` (multipart).
1521
+ */
1522
+ async importDocument(file, options) {
1523
+ const form = new FormData();
1524
+ const blob = file instanceof Blob
1525
+ ? file
1526
+ : file instanceof ArrayBuffer
1527
+ ? new Blob([file], { type: 'application/octet-stream' })
1528
+ : new Blob([Uint8Array.from(file)], { type: 'application/octet-stream' });
1529
+ const uploadName = options?.filename ??
1530
+ (typeof File !== 'undefined' && file instanceof File ? file.name : 'upload.bin');
1531
+ form.append('file', blob, uploadName);
1532
+ const q = buildQuery({
1533
+ ...(options?.identifier !== undefined && options.identifier !== ''
1534
+ ? { identifier: this.isoPrefixId(options.identifier) }
1535
+ : {}),
1536
+ });
1537
+ const r = await this.requestFormPost(`/api/v1/documents/import${q}`, form);
1538
+ if (r && typeof r === 'object' && 'identifier' in r && typeof r.identifier === 'string') {
1539
+ r.identifier = this.isoUnprefixId(r.identifier);
1540
+ }
1541
+ return r;
1542
+ }
1543
+ /**
1544
+ * Export a document (`GET /api/v1/documents/export`). Returns binary bytes and response metadata.
1545
+ */
1546
+ async exportDocument(identifier, format = 'docx', options) {
1547
+ const q = buildQuery({
1548
+ identifier: this.isoPrefixId(identifier),
1549
+ format,
1550
+ strict: options?.strict ? '1' : undefined,
1551
+ });
1552
+ const r = await this.requestBinaryGet(`/api/v1/documents/export${q}`);
1553
+ return {
1554
+ bytes: r.bytes,
1555
+ contentType: r.contentType,
1556
+ filename: r.filename,
1557
+ ...(r.roundTrip ? { roundTrip: r.roundTrip } : {}),
1558
+ ...(r.warning ? { warning: r.warning } : {}),
1559
+ };
1560
+ }
1357
1561
  /**
1358
1562
  * @deprecated Use {@link writeXmlDocument}. This name was misleading: it writes **XML** via a JSON wrapper, not a JSON document.
1359
1563
  */
@@ -1370,6 +1574,28 @@ class XCiteDBClient {
1370
1574
  const rows = await this.request('GET', `/api/v1/documents/by-id${q}`);
1371
1575
  return Array.isArray(rows) ? rows.map((x) => this.isoUnprefixId(String(x))) : rows;
1372
1576
  }
1577
+ /**
1578
+ * Shallow read: root element with inline leaves plus `db:N*` placeholders for shredded child slots
1579
+ * (`flags=NoChildren,KeepIndexNodes,FirstMatch` on `GET /api/v1/documents/by-id`). Use with {@link listChildIdentifiers}
1580
+ * or {@link queryByIdentifierWithChildren} for sidebar / AST navigation without loading the full subtree.
1581
+ */
1582
+ async queryByIdentifierShallow(identifier, filter, pathFilter) {
1583
+ return this.queryByIdentifier(identifier, 'NoChildren,KeepIndexNodes,FirstMatch', filter, pathFilter);
1584
+ }
1585
+ /** Alias of {@link listIdentifierChildren} — immediate child segments under `parentPath` (identifier hierarchy). */
1586
+ async listChildIdentifiers(parentPath) {
1587
+ return this.listIdentifierChildren(parentPath);
1588
+ }
1589
+ /**
1590
+ * Parallel shallow node + one level of identifier children (`GET /by-id` + `GET /identifier-children`).
1591
+ */
1592
+ async queryByIdentifierWithChildren(identifier, filter, pathFilter) {
1593
+ const [node, listed] = await Promise.all([
1594
+ this.queryByIdentifierShallow(identifier, filter, pathFilter),
1595
+ this.listIdentifierChildren(identifier),
1596
+ ]);
1597
+ return { node, children: listed.children };
1598
+ }
1373
1599
  async queryDocuments(query, flags, filter, pathFilter) {
1374
1600
  const pq = this.isoPrefixQuery(query);
1375
1601
  const params = {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { XCiteDBClient } from './client';
2
2
  export { WebSocketSubscription } from './websocket';
3
- export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, DocumentBatchResponse, DocumentBatchResultRow, Flags, JsonDocumentData, JsonDocumentBatchItem, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LockConflictBody, LockExpiredBody, LockUnknownBody, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationOptions, UserIsolationShareMode, UserIsolationShareResult, WorkspaceInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, TestSessionBootstrap, TestSessionBootstrapSummary, TestSessionInfo, XCiteDBClientOptions, XCiteDBErrorExtras, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
3
+ export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, DocumentBatchResponse, DocumentBatchResultRow, DocumentExportFormat, DocumentImportFormat, ExportDocumentResult, Flags, ImportDocumentOptions, ImportDocumentResult, JsonDocumentData, JsonDocumentBatchItem, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LockConflictBody, LockExpiredBody, LockUnknownBody, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, PlatformDefaultDocConfResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, RebaseUserWorkspaceResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationOptions, UserIsolationShareMode, UserIsolationShareResult, WorkspaceInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, TestSessionBootstrap, TestSessionBootstrapSummary, TestSessionInfo, XCiteDBClientOptions, XCiteDBErrorExtras, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
4
4
  export { XCiteDBError, XCiteDBForbiddenError, XCiteDBNotFoundError, XCiteDBAuthError, XCiteDBLockConflictError, } from './types';
package/dist/types.d.ts CHANGED
@@ -399,6 +399,31 @@ export interface WriteDocumentOptions {
399
399
  is_top?: boolean;
400
400
  compare_attributes?: boolean;
401
401
  }
402
+ /** Supported source formats for `POST /api/v1/documents/import` (server detects from bytes / filename). */
403
+ export type DocumentImportFormat = 'docx' | 'odt' | 'rtf' | 'pdf' | 'txt' | 'md' | 'adoc';
404
+ /** JSON body on successful import (`201 Created`). */
405
+ export interface ImportDocumentResult {
406
+ identifier: string;
407
+ section_count: number;
408
+ source_format: string;
409
+ }
410
+ export interface ImportDocumentOptions {
411
+ /** Override root `db:identifier`; default is `/imported/<sanitized-filename>`. */
412
+ identifier?: string;
413
+ /** Original filename (helps sniff format when `File` is not available, e.g. Node `Blob` only). */
414
+ filename?: string;
415
+ }
416
+ /** `GET /api/v1/documents/export?format=…` */
417
+ export type DocumentExportFormat = 'xml' | 'docx' | 'odt' | 'pdf' | 'txt' | 'md' | 'adoc';
418
+ export interface ExportDocumentResult {
419
+ bytes: Uint8Array;
420
+ contentType: string;
421
+ filename: string;
422
+ /** Present for txt/md/adoc when server sends `X-XciteDB-Export-RoundTrip`. */
423
+ roundTrip?: 'exact' | 'lossy';
424
+ /** From `X-XciteDB-Export-Warning` when present. */
425
+ warning?: string;
426
+ }
402
427
  /** One row in `POST /api/v1/documents/batch` or `POST /api/v1/json-documents/batch` response `results`. */
403
428
  export interface DocumentBatchResultRow {
404
429
  index: number;
@@ -835,6 +860,8 @@ export interface CompareResult {
835
860
  date_key?: string;
836
861
  };
837
862
  total_changes: number;
863
+ /** Echo of request `match_start` when path-scoped compare was used. */
864
+ match_start?: string;
838
865
  }
839
866
  /** @deprecated Use {@link CompareResult}. */
840
867
  export type DiffResult = CompareResult;
@@ -857,6 +884,16 @@ export interface PublishResult {
857
884
  }
858
885
  /** @deprecated Use {@link PublishResult}. */
859
886
  export type MergeResult = PublishResult;
887
+ /** `POST /api/v1/user-workspaces/{id}/rebase` */
888
+ export interface RebaseUserWorkspaceResult {
889
+ status: 'completed' | 'conflicts';
890
+ message?: string;
891
+ old_from_date_key?: string;
892
+ new_from_date_key?: string;
893
+ conflicts?: PublishConflict[];
894
+ auto_mergeable?: string[];
895
+ would_expose?: string[];
896
+ }
860
897
  export type XCiteDBErrorExtras = {
861
898
  reason?: string;
862
899
  policyId?: string;
package/llms-full.txt CHANGED
@@ -35,7 +35,7 @@ Before reading the full reference, note these critical differences from typical
35
35
  - **Write at a specific date:** Set `X-Date` / `context.date` and write — every dated write is a **temporal revision**; no workspace or checkpoint required.
36
36
  - **Read as-of a date:** Set `X-Date` and read; the engine returns the revision at or before that instant.
37
37
  - **Isolated editing (draft → publish):** Create a **workspace**, edit, then **publish** to the target timeline; optional **checkpoints** for named snapshots.
38
- - **App-user user workspaces (`_uw/<owner>/<slug>`):** **`GET/POST /api/v1/user-workspaces`**, grants, publish — see repository **`web/src/docs/content/user-workspaces.md`**.
38
+ - **App-user user workspaces (`_uw/<owner>/<slug>`):** **`GET/POST /api/v1/user-workspaces`**, grants, publish, **rebase** — see repository **`web/src/docs/content/user-workspaces.md`**.
39
39
  - **Audit trail:** Checkpoints carry messages and affected identifiers; **bookmarks** name a checkpoint.
40
40
  - **Undo a batch:** **Revert** to a prior checkpoint on that workspace.
41
41
 
@@ -434,6 +434,16 @@ The identifier is extracted from the `db:identifier` attribute in the root XML e
434
434
 
435
435
  Query parameters: `identifier` (required), `flags` (optional: `FirstMatch`, `IncludeChildren`, `NoChildren`, `KeepIndexNodes`), `filter`, `path_filter`.
436
436
 
437
+ ### Shallow node + subtree navigation
438
+
439
+ - **`flags=NoChildren,KeepIndexNodes,FirstMatch`**: returns the matching node with inline leaf content plus **`db:N1`**, **`db:N2`**, … placeholders for shredded child slots (avoids loading the full deep subtree in one response).
440
+ - Combine with **`GET /api/v1/documents/identifier-children?parent_path=…`** for the next level of hierarchy (`segment`, `full_path`, `is_identifier`, `has_children`).
441
+ - **JavaScript SDK:** `queryByIdentifierShallow(id)`, `listChildIdentifiers(parentPath?)`, `queryByIdentifierWithChildren(id)` (shallow XML + children in parallel).
442
+
443
+ ### Standalone subtree write (shredded model)
444
+
445
+ You may `POST /api/v1/documents` with a root element whose `db:identifier` is a nested path (e.g. `/spaces/u/docs/doc1/sec-1`) without re-posting the parent document: the engine writes that subtree only and updates the identifier hierarchy index for the parent automatically. Keep `is_top: true` (default) on the write.
446
+
437
447
  ## Delete document
438
448
 
439
449
  **`DELETE /api/v1/documents/by-id?identifier=/book/ch1`**
@@ -526,7 +536,7 @@ Returns `{ identifiers: string[], total, offset, limit }`.
526
536
 
527
537
  ## Export document
528
538
 
529
- **`GET /api/v1/documents/export?id=/book/ch1&format=...`**
539
+ **`GET /api/v1/documents/export?identifier=/book/ch1&format=...`**
530
540
 
531
541
  ## Export provision
532
542
 
@@ -847,6 +857,22 @@ Returns `{ status: "completed"|"conflicts", checkpoint?, commit?, merged_identif
847
857
 
848
858
  ---
849
859
 
860
+ # App-user workspaces (`/api/v1/user-workspaces`)
861
+
862
+ Per-user draft branches (`_uw/<owner>/<slug>`). See also **`web/src/docs/content/user-workspaces.md`**.
863
+
864
+ ## Rebase user workspace
865
+
866
+ **`POST /api/v1/user-workspaces/{id}/rebase`**
867
+
868
+ ```json
869
+ { "auto_resolve": "none" }
870
+ ```
871
+
872
+ Advances the workspace branch fork metadata to the **current tip** of its parent branch. Returns **`200`** with `{ status: "completed", message?, old_from_date_key, new_from_date_key, would_expose[], auto_mergeable[], conflicts: [] }`, or **`409`** with `{ status: "conflicts", conflicts: [...], auto_mergeable?, would_expose? }` when the same identifiers changed on both parent and workspace since the fork (same conflict shape as workspace publish).
873
+
874
+ ---
875
+
850
876
  # Checkpoints, bookmarks & compare
851
877
 
852
878
  ## Create checkpoint
@@ -896,13 +922,16 @@ Returns `{ checkpoints: [...], commits: [...], total, branch }` (mirrors).
896
922
  {
897
923
  "from": { "branch": "", "date": "2024-01-15T00:00:00" },
898
924
  "to": { "branch": "feature-x" },
899
- "include_content": true
925
+ "include_content": true,
926
+ "match_start": "/spaces/u/docs/doc1"
900
927
  }
901
928
  ```
902
929
 
930
+ Optional **`match_start`**: when set, only identifiers equal to that path or under it (prefix + `/`) are included in **`changes`** (tenant-wide noise is dropped).
931
+
903
932
  (Deprecated: **`POST /api/v1/diff`**; `date_key` still accepted internally but prefer **`date`**.)
904
933
 
905
- Returns `{ changes: [{ identifier, action: "added"|"modified"|"deleted", from_content?, to_content? }], total_changes }`.
934
+ Returns `{ changes: [{ identifier, action: "added"|"modified"|"deleted", from_content?, to_content? }], total_changes, match_start? }` ( **`match_start`** echoed when the filter was used).
906
935
 
907
936
  ---
908
937
 
@@ -1506,7 +1535,12 @@ interface DatabaseContext {
1506
1535
  ### XML Documents
1507
1536
  - `writeXmlDocument(xml, options?)` → `void` — XML via JSON body (recommended). Deprecated: `writeDocumentJson`.
1508
1537
  - `writeXML(xml)` → `void` — Raw XML body
1538
+ - `importDocument(file, options?)` → `ImportDocumentResult` — multipart `POST /api/v1/documents/import`
1539
+ - `exportDocument(identifier, format?, options?)` → `ExportDocumentResult` — binary `GET /api/v1/documents/export`
1509
1540
  - `queryByIdentifier(identifier, flags?, filter?, pathFilter?)` → `string[]`
1541
+ - `queryByIdentifierShallow(identifier, filter?, pathFilter?)` → `string[]` — `NoChildren,KeepIndexNodes,FirstMatch`
1542
+ - `listChildIdentifiers(parentPath?)` → `ListIdentifierChildrenResult` — alias of `listIdentifierChildren`
1543
+ - `queryByIdentifierWithChildren(identifier, filter?, pathFilter?)` → `{ node, children }`
1510
1544
  - `queryDocuments(query: XCiteQuery, flags?, filter?, pathFilter?)` → `string[]`
1511
1545
  - `deleteDocument(identifier)` → `void`
1512
1546
  - `listIdentifiers(query: XCiteQuery)` → `ListIdentifiersResult`
@@ -1544,6 +1578,13 @@ interface DatabaseContext {
1544
1578
  - `publishWorkspace(targetWorkspace, sourceWorkspace, options?)` → `PublishResult`
1545
1579
  - **Deprecated:** `withBranch`, `createBranch`, `listBranches`, `getBranch`, `deleteBranch`, `mergeBranch` (same HTTP behavior)
1546
1580
 
1581
+ ### App-user user workspaces (`_uw/…`)
1582
+ - `listUserWorkspaces()` → `{ user_workspaces: unknown[] }`
1583
+ - `createUserWorkspace(name, options?)` → `Record<string, unknown>`
1584
+ - `getUserWorkspace(id)` / `deleteUserWorkspace(id)` / `addUserWorkspaceGrant(id, body)` / `removeUserWorkspaceGrant(id, body)`
1585
+ - `publishUserWorkspace(id, options?)` → `PublishResult`-shaped JSON
1586
+ - `rebaseUserWorkspace(id, options?)` → `RebaseUserWorkspaceResult` — advances fork onto parent tip; **409** + `conflicts` on overlap (optional `autoResolve`: `none` | `source` | `target`)
1587
+
1547
1588
  ### Checkpoints
1548
1589
  - `createCheckpoint(message, author?)` → `CheckpointRecord`
1549
1590
  - `listCheckpoints(options?)` → `{ checkpoints, total, branch }` (wire JSON may also include `commits` mirror)
@@ -1560,7 +1601,7 @@ interface DatabaseContext {
1560
1601
  - **Deprecated:** `createTag`, `listTags`, `getTag`, `deleteTag`
1561
1602
 
1562
1603
  ### Compare
1563
- - `compare(from: CompareRef, to: CompareRef, includeContent?)` → `CompareResult`
1604
+ - `compare(from: CompareRef, to: CompareRef, includeContent?)` or `compare(from, to, { includeContent?, matchStart? })` → `CompareResult`
1564
1605
  - **Deprecated:** `diff(from: DiffRef, …)` — alias of `compare`
1565
1606
 
1566
1607
  ### Locks
package/llms.txt CHANGED
@@ -158,6 +158,10 @@ When you build a backend that calls XCiteDB on behalf of users:
158
158
  - **Metadata `mode` and arrays.** **`POST /api/v1/meta`** uses **`mode`**: default **`set`** writes or replaces at `path` (arrays are replaced in range; excess old indices cleared); **`append`** appends array elements after existing ones at `path`. Optional **`overwrite: true`** clears metadata under `path` before writing. JavaScript: **`appendMeta`** or **`addMeta(..., { mode: 'append' })`**; Python/C++: **`append_meta`** or **`add_meta`** with **`mode`** / **`overwrite`** (see SDK sections below).
159
159
  - **Prefer dictionary-style objects.** **Shredded** JSON metadata is stored in **fragment-level** keys (e.g. per field name). **Object maps** get per-field storage; when an object accumulates enough distinct field names (server default threshold **32**), XCiteDB switches automatically to **dictionary storage** (`{*}` plus per-field keys) for efficient indexed access. For lookup-heavy or wide records, use **`{ "key": value, ... }`** shapes (or one document per logical row) rather than opaque arrays when you need keyed reads.
160
160
 
161
+ ## XML subtree writes (shredded model)
162
+
163
+ - **Standalone subtree root.** Because XCiteDB shreds XML per identifier, you can `writeXmlDocument('<sec db:identifier="/spaces/u/docs/doc1/sec-1">…</sec>')` and the engine writes only that subtree — you do **not** need to re-send the parent document. The parent's children index (`id_hier`) is updated automatically. Keep `is_top: true` (default) on `POST /api/v1/documents` even when the `db:identifier` is nested under another path. Use `compare_attributes: true` only when you need attribute-level diffing for triggers.
164
+
161
165
  ## JavaScript/TypeScript SDK (`@xcitedbs/client`)
162
166
 
163
167
  Install: `npm install @xcitedbs/client`
@@ -191,6 +195,11 @@ await client.writeXmlDocument(
191
195
  '<chapter db:identifier="/manual/v1/intro"><title>Introduction</title></chapter>'
192
196
  );
193
197
 
198
+ // Import DOCX/PDF (etc.) server-side — shredded into XML (`POST /api/v1/documents/import`, multipart field `file`)
199
+ // const fileBlob = …; // Blob from <input type="file"> or fetch
200
+ // await client.importDocument(fileBlob, { identifier: '/manual/imported/spec', filename: 'spec.docx' });
201
+ // const out = await client.exportDocument('/manual/imported/spec', 'docx'); // bytes in `out.bytes`
202
+
194
203
  // Write a JSON document
195
204
  await client.writeJsonDocument('app.settings', { theme: 'dark', locale: 'en' });
196
205
 
@@ -274,7 +283,12 @@ interface XCiteDBClientOptions {
274
283
  **XML Documents:**
275
284
  - `writeXmlDocument(xml, options?)` — Store XML via JSON body (identifier inside XML). Deprecated alias: `writeDocumentJson`.
276
285
  - `writeXML(xml)` — Store raw XML with `Content-Type: application/xml`
286
+ - `importDocument(file, options?)` — `POST /api/v1/documents/import` (multipart `file`). Server converts DOCX, ODF, RTF, PDF, Markdown, AsciiDoc, or plain text into shredded XML. Optional `identifier` query override; optional `filename` for sniffing when not a `File`.
287
+ - `exportDocument(id, format?, options?)` — `GET /api/v1/documents/export` binary (`xml` | `docx` | `odt` | `pdf` | `txt` | `md` | `adoc`). Optional `strict` for text exports.
277
288
  - `queryByIdentifier(id, flags?, filter?)` — Get document(s) by identifier
289
+ - `queryByIdentifierShallow(id, filter?, pathFilter?)` — Same as `flags=NoChildren,KeepIndexNodes,FirstMatch` (skeleton: placeholders `db:N*` for shredded slots; avoids full subtree)
290
+ - `listChildIdentifiers(parentPath?)` — Alias of `listIdentifierChildren`; next level of identifier hierarchy
291
+ - `queryByIdentifierWithChildren(id, filter?, pathFilter?)` — `Promise.all` of shallow node + `identifier-children` for one expansion step
278
292
  - `queryDocuments(query, flags?)` — List/filter documents
279
293
  - `deleteDocument(identifier)` — Delete by identifier
280
294
  - `listIdentifiers(query)` — List known identifiers with pagination
@@ -299,12 +313,12 @@ interface XCiteDBClientOptions {
299
313
  - `listWorkspaces()` — List workspaces
300
314
  - `publishWorkspace(target, source, options?)` — Publish workspace changes to a target timeline
301
315
  - `deleteWorkspace(name)` — Delete workspace
302
- - `listUserWorkspaces()` / `createUserWorkspace(name, options?)` / `getUserWorkspace(id)` / `deleteUserWorkspace(id)` / `addUserWorkspaceGrant(id, body)` / `removeUserWorkspaceGrant(id, body)` / `publishUserWorkspace(id, options?)` — App-user **`_uw/…`** workspaces
316
+ - `listUserWorkspaces()` / `createUserWorkspace(name, options?)` / `getUserWorkspace(id)` / `deleteUserWorkspace(id)` / `addUserWorkspaceGrant(id, body)` / `removeUserWorkspaceGrant(id, body)` / `publishUserWorkspace(id, options?)` / `rebaseUserWorkspace(id, options?)` — App-user **`_uw/…`** workspaces (`rebase` advances the fork onto the parent tip; **409** + `conflicts` on overlap, like publish)
303
317
  - `createCheckpoint(message, author?)` — Named snapshot of current state
304
318
  - `listCheckpoints(options?)` — List checkpoints
305
319
  - `revertToCheckpoint(checkpointId)` — Revert workspace to a prior checkpoint
306
320
  - `applyCheckpoint(checkpointId, message?)` — Apply another checkpoint’s changes here
307
- - `compare(from, to, includeContent?)` — Compare revisions
321
+ - `compare(from, to, includeContent?)` or `compare(from, to, { includeContent?, matchStart? })` — Compare revisions; optional **`matchStart`** restricts results to that identifier and descendants (same as document `match_start` query semantics)
308
322
  - `createBookmark(name, checkpointId, message?)` — Bookmark a checkpoint
309
323
  - `listBookmarks()` / `deleteBookmark(name)` — Manage bookmarks
310
324
  - **Deprecated aliases:** `withBranch`, `createBranch`, `listBranches`, `mergeBranch`, `deleteBranch`, `createCommit`, `listCommits`, `rollbackToCommit`, `cherryPick`, `diff`, `createTag`, `listTags`, `deleteTag` (same HTTP behavior)
@@ -369,6 +383,8 @@ When calling `queryByIdentifier` or `queryDocuments`, the `flags` parameter cont
369
383
  - `'NoChildren'` — Exclude children
370
384
  - `'KeepIndexNodes'` — Include index/structural nodes
371
385
 
386
+ **Sidebar / AST navigation:** combine **`'NoChildren,KeepIndexNodes,FirstMatch'`** (or `queryByIdentifierShallow`) with **`listChildIdentifiers`** / **`listIdentifierChildren`** so each expansion is one shallow XML read plus one child list, instead of `IncludeChildren` loading the entire subtree.
387
+
372
388
  ### Errors
373
389
 
374
390
  All API errors throw `XCiteDBError` with `.status` (HTTP code) and `.body` (parsed response). Common codes: `401` unauthenticated, `403` forbidden by policy or RBAC, `404` not found, `409` conflict (lock), `422` validation, `423` project encrypted and locked, `429` rate limited. Many **ABAC** denials return `403` with `"message":"Forbidden"` plus optional **`policy_id`** and **`hint`** (check JWT `tenant_id` vs `project:` group middle segment).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcitedbs/client",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "description": "XCiteDB BaaS client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",