@xcitedbs/client 0.2.10 → 0.2.11

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, CommitRecord, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, XCiteQuery, UserIsolationConfig } from './types';
1
+ import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, Flags, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, LogEntry, MergeResult, PublishResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, StoredPolicyResponse, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, XCiteQuery, UserIsolationConfig } from './types';
2
2
  import { WebSocketSubscription } from './websocket';
3
3
  export declare class XCiteDBClient {
4
4
  private baseUrl;
@@ -293,12 +293,34 @@ export declare class XCiteDBClient {
293
293
  * the server (namespace + shared paths). Does not send `X-Prefix`; identifiers in requests are rewritten.
294
294
  */
295
295
  enableUserIsolation(): Promise<UserIsolationConfig>;
296
+ createWorkspace(name: string, fromBranch?: string, fromDate?: string): Promise<void>;
297
+ /** @deprecated Use {@link createWorkspace}. */
296
298
  createBranch(name: string, fromBranch?: string, fromDate?: string): Promise<void>;
299
+ deleteWorkspace(name: string): Promise<void>;
300
+ /** @deprecated Use {@link deleteWorkspace}. */
297
301
  deleteBranch(name: string): Promise<void>;
302
+ deleteWorkspaceRevision(workspace: string, date: string): Promise<void>;
303
+ /** @deprecated Use {@link deleteWorkspaceRevision}. */
298
304
  deleteRevision(branch: string, date: string): Promise<void>;
305
+ listWorkspaces(): Promise<WorkspaceInfo[]>;
306
+ /** @deprecated Use {@link listWorkspaces}. */
299
307
  listBranches(): Promise<BranchInfo[]>;
308
+ getWorkspace(name: string): Promise<WorkspaceInfo>;
309
+ /** @deprecated Use {@link getWorkspace}. */
300
310
  getBranch(name: string): Promise<BranchInfo>;
311
+ createCheckpoint(message: string, author?: string): Promise<CheckpointRecord>;
312
+ /** @deprecated Use {@link createCheckpoint}. */
301
313
  createCommit(message: string, author?: string): Promise<CommitRecord>;
314
+ listCheckpoints(options?: {
315
+ branch?: string;
316
+ limit?: number;
317
+ offset?: number;
318
+ }): Promise<{
319
+ checkpoints: CheckpointRecord[];
320
+ total: number;
321
+ branch: string;
322
+ }>;
323
+ /** @deprecated Use {@link listCheckpoints}. */
302
324
  listCommits(options?: {
303
325
  branch?: string;
304
326
  limit?: number;
@@ -308,13 +330,33 @@ export declare class XCiteDBClient {
308
330
  total: number;
309
331
  branch: string;
310
332
  }>;
333
+ getCheckpoint(checkpointId: string): Promise<CheckpointRecord>;
334
+ /** @deprecated Use {@link getCheckpoint}. */
311
335
  getCommit(commitId: string): Promise<CommitRecord>;
336
+ revertToCheckpoint(checkpointId: string, _confirm?: boolean): Promise<{
337
+ rolled_back_checkpoints: string[];
338
+ rolled_back_commits?: string[];
339
+ current_tip: string;
340
+ }>;
341
+ /** @deprecated Use {@link revertToCheckpoint}. */
312
342
  rollbackToCommit(commitId: string, _confirm?: boolean): Promise<{
313
343
  rolled_back_commits: string[];
314
344
  current_tip: string;
315
345
  }>;
346
+ applyCheckpoint(checkpointId: string, message?: string, author?: string): Promise<CheckpointRecord>;
347
+ /** @deprecated Use {@link applyCheckpoint}. */
316
348
  cherryPick(commitId: string, message?: string, author?: string): Promise<CommitRecord>;
349
+ createBookmark(name: string, checkpointId: string, message?: string, author?: string): Promise<BookmarkRecord>;
350
+ /** @deprecated Use {@link createBookmark}. */
317
351
  createTag(name: string, commitId: string, message?: string, author?: string): Promise<TagRecord>;
352
+ listBookmarks(options?: {
353
+ limit?: number;
354
+ offset?: number;
355
+ }): Promise<{
356
+ bookmarks: BookmarkRecord[];
357
+ total: number;
358
+ }>;
359
+ /** @deprecated Use {@link listBookmarks}. */
318
360
  listTags(options?: {
319
361
  limit?: number;
320
362
  offset?: number;
@@ -322,23 +364,44 @@ export declare class XCiteDBClient {
322
364
  tags: TagRecord[];
323
365
  total: number;
324
366
  }>;
367
+ getBookmark(name: string): Promise<BookmarkRecord>;
368
+ /** @deprecated Use {@link getBookmark}. */
325
369
  getTag(name: string): Promise<TagRecord>;
370
+ deleteBookmark(name: string): Promise<void>;
371
+ /** @deprecated Use {@link deleteBookmark}. */
326
372
  deleteTag(name: string): Promise<void>;
373
+ compare(from: CompareRef, to: CompareRef, includeContent?: boolean): Promise<CompareResult>;
374
+ /** @deprecated Use {@link compare}. */
327
375
  diff(from: DiffRef, to: DiffRef, includeContent?: boolean): Promise<DiffResult>;
376
+ publishWorkspace(targetWorkspace: string, sourceWorkspace: string, options?: {
377
+ message?: string;
378
+ autoResolve?: 'none' | 'source' | 'target';
379
+ }): Promise<PublishResult>;
380
+ /** @deprecated Use {@link publishWorkspace}. */
328
381
  mergeBranch(targetBranch: string, sourceBranch: string, options?: {
329
382
  message?: string;
330
383
  autoResolve?: 'none' | 'source' | 'target';
331
384
  }): Promise<MergeResult>;
332
385
  /**
333
- * Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
334
- * create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
335
- * Restores previous {@link DatabaseContext} afterward.
386
+ * Create `workspaceName` from {@link options.fromBranch} (or current context), run `fn` scoped to that workspace,
387
+ * create a checkpoint, then publish back unless {@link options.autoMerge} is `false`.
336
388
  */
389
+ withWorkspace<T>(workspaceName: string, fn: (client: XCiteDBClient) => Promise<T>, options?: {
390
+ message?: string;
391
+ /** When true (default), publish `workspaceName` into the parent workspace after checkpoint. */
392
+ autoMerge?: boolean;
393
+ /** Workspace to fork from (default: current `context.branch` / `context.workspace`, or `""`). */
394
+ fromBranch?: string;
395
+ author?: string;
396
+ }): Promise<{
397
+ result: T;
398
+ checkpoint?: CheckpointRecord;
399
+ publish?: PublishResult;
400
+ }>;
401
+ /** @deprecated Use {@link withWorkspace}. */
337
402
  withBranch<T>(branchName: string, fn: (client: XCiteDBClient) => Promise<T>, options?: {
338
403
  message?: string;
339
- /** When true (default), merge `branchName` into the parent branch after commit. */
340
404
  autoMerge?: boolean;
341
- /** Branch to fork from (default: current `context.branch`, or `""`). */
342
405
  fromBranch?: string;
343
406
  author?: string;
344
407
  }): Promise<{
package/dist/client.js CHANGED
@@ -345,7 +345,11 @@ class XCiteDBClient {
345
345
  this.projectId = projectId;
346
346
  }
347
347
  setContext(ctx) {
348
- this.defaultContext = { ...this.defaultContext, ...ctx };
348
+ const next = { ...this.defaultContext, ...ctx };
349
+ if (ctx.workspace !== undefined) {
350
+ next.branch = ctx.workspace;
351
+ }
352
+ this.defaultContext = next;
349
353
  }
350
354
  setTokens(access, refresh) {
351
355
  this.accessToken = access;
@@ -367,8 +371,9 @@ class XCiteDBClient {
367
371
  contextHeaders() {
368
372
  const h = {};
369
373
  const c = this.defaultContext;
370
- if (c.branch)
371
- h['X-Branch'] = c.branch;
374
+ const ws = c.workspace ?? c.branch;
375
+ if (ws)
376
+ h['X-Workspace'] = ws;
372
377
  if (c.date)
373
378
  h['X-Date'] = c.date;
374
379
  if (c.prefix)
@@ -941,121 +946,202 @@ class XCiteDBClient {
941
946
  }
942
947
  return cfg;
943
948
  }
944
- async createBranch(name, fromBranch, fromDate) {
949
+ async createWorkspace(name, fromBranch, fromDate) {
945
950
  const body = { name };
946
951
  if (fromBranch)
947
952
  body.from_branch = fromBranch;
948
953
  if (fromDate)
949
954
  body.from_date = fromDate;
950
- await this.request('POST', '/api/v1/branches', body);
955
+ await this.request('POST', '/api/v1/workspaces', body);
956
+ }
957
+ /** @deprecated Use {@link createWorkspace}. */
958
+ async createBranch(name, fromBranch, fromDate) {
959
+ return this.createWorkspace(name, fromBranch, fromDate);
960
+ }
961
+ async deleteWorkspace(name) {
962
+ await this.request('DELETE', `/api/v1/workspaces/${encodeURIComponent(name)}`);
951
963
  }
964
+ /** @deprecated Use {@link deleteWorkspace}. */
952
965
  async deleteBranch(name) {
953
- await this.request('DELETE', `/api/v1/branches/${encodeURIComponent(name)}`);
966
+ return this.deleteWorkspace(name);
967
+ }
968
+ async deleteWorkspaceRevision(workspace, date) {
969
+ await this.request('DELETE', `/api/v1/workspaces/${encodeURIComponent(workspace)}/revisions/${encodeURIComponent(date)}`);
954
970
  }
971
+ /** @deprecated Use {@link deleteWorkspaceRevision}. */
955
972
  async deleteRevision(branch, date) {
956
- await this.request('DELETE', `/api/v1/branches/${encodeURIComponent(branch)}/revisions/${encodeURIComponent(date)}`);
973
+ return this.deleteWorkspaceRevision(branch, date);
957
974
  }
975
+ async listWorkspaces() {
976
+ const r = await this.request('GET', '/api/v1/workspaces');
977
+ return r.workspaces ?? r.branches ?? [];
978
+ }
979
+ /** @deprecated Use {@link listWorkspaces}. */
958
980
  async listBranches() {
959
- const r = await this.request('GET', '/api/v1/branches');
960
- return r.branches ?? [];
981
+ return this.listWorkspaces();
982
+ }
983
+ async getWorkspace(name) {
984
+ const r = await this.request('GET', `/api/v1/workspaces/${encodeURIComponent(name)}`);
985
+ return (r.workspace ?? r.branch);
961
986
  }
987
+ /** @deprecated Use {@link getWorkspace}. */
962
988
  async getBranch(name) {
963
- const r = await this.request('GET', `/api/v1/branches/${encodeURIComponent(name)}`);
964
- return r.branch;
989
+ return this.getWorkspace(name);
965
990
  }
966
- async createCommit(message, author) {
991
+ async createCheckpoint(message, author) {
967
992
  const body = { message };
968
993
  if (author)
969
994
  body.author = author;
970
- const r = await this.request('POST', '/api/v1/commits', body);
971
- return r.commit;
995
+ const r = await this.request('POST', '/api/v1/checkpoints', body);
996
+ return (r.checkpoint ?? r.commit);
972
997
  }
973
- async listCommits(options) {
998
+ /** @deprecated Use {@link createCheckpoint}. */
999
+ async createCommit(message, author) {
1000
+ return this.createCheckpoint(message, author);
1001
+ }
1002
+ async listCheckpoints(options) {
974
1003
  const q = buildQuery({
975
1004
  branch: options?.branch,
976
1005
  limit: options?.limit,
977
1006
  offset: options?.offset,
978
1007
  });
979
- return this.request('GET', `/api/v1/commits${q}`);
1008
+ const r = await this.request('GET', `/api/v1/checkpoints${q}`);
1009
+ const list = r.checkpoints ?? r.commits ?? [];
1010
+ return { checkpoints: list, total: r.total, branch: r.branch };
1011
+ }
1012
+ /** @deprecated Use {@link listCheckpoints}. */
1013
+ async listCommits(options) {
1014
+ const r = await this.listCheckpoints(options);
1015
+ return { commits: r.checkpoints, total: r.total, branch: r.branch };
980
1016
  }
1017
+ async getCheckpoint(checkpointId) {
1018
+ const r = await this.request('GET', `/api/v1/checkpoints/${encodeURIComponent(checkpointId)}`);
1019
+ return (r.checkpoint ?? r.commit);
1020
+ }
1021
+ /** @deprecated Use {@link getCheckpoint}. */
981
1022
  async getCommit(commitId) {
982
- const r = await this.request('GET', `/api/v1/commits/${encodeURIComponent(commitId)}`);
983
- return r.commit;
1023
+ return this.getCheckpoint(commitId);
984
1024
  }
985
- async rollbackToCommit(commitId, _confirm) {
986
- return this.request('POST', `/api/v1/commits/${encodeURIComponent(commitId)}/rollback`, {
1025
+ async revertToCheckpoint(checkpointId, _confirm) {
1026
+ return this.request('POST', `/api/v1/checkpoints/${encodeURIComponent(checkpointId)}/revert`, {
987
1027
  confirm: true,
988
1028
  });
989
1029
  }
990
- async cherryPick(commitId, message, author) {
1030
+ /** @deprecated Use {@link revertToCheckpoint}. */
1031
+ async rollbackToCommit(commitId, _confirm) {
1032
+ const r = await this.revertToCheckpoint(commitId, _confirm);
1033
+ return {
1034
+ rolled_back_commits: r.rolled_back_commits ?? r.rolled_back_checkpoints,
1035
+ current_tip: r.current_tip,
1036
+ };
1037
+ }
1038
+ async applyCheckpoint(checkpointId, message, author) {
991
1039
  const body = {};
992
1040
  if (message)
993
1041
  body.message = message;
994
1042
  if (author)
995
1043
  body.author = author;
996
- const r = await this.request('POST', `/api/v1/commits/${encodeURIComponent(commitId)}/cherry-pick`, body);
997
- return r.commit;
1044
+ const r = await this.request('POST', `/api/v1/checkpoints/${encodeURIComponent(checkpointId)}/apply`, body);
1045
+ return (r.checkpoint ?? r.commit);
998
1046
  }
999
- async createTag(name, commitId, message, author) {
1000
- const body = { name, commit_id: commitId };
1047
+ /** @deprecated Use {@link applyCheckpoint}. */
1048
+ async cherryPick(commitId, message, author) {
1049
+ return this.applyCheckpoint(commitId, message, author);
1050
+ }
1051
+ async createBookmark(name, checkpointId, message, author) {
1052
+ const body = { name, checkpoint_id: checkpointId };
1001
1053
  if (message)
1002
1054
  body.message = message;
1003
1055
  if (author)
1004
1056
  body.author = author;
1005
- const r = await this.request('POST', '/api/v1/tags', body);
1006
- return r.tag;
1057
+ const r = await this.request('POST', '/api/v1/bookmarks', body);
1058
+ return (r.bookmark ?? r.tag);
1007
1059
  }
1008
- async listTags(options) {
1060
+ /** @deprecated Use {@link createBookmark}. */
1061
+ async createTag(name, commitId, message, author) {
1062
+ return this.createBookmark(name, commitId, message, author);
1063
+ }
1064
+ async listBookmarks(options) {
1009
1065
  const q = buildQuery({ limit: options?.limit, offset: options?.offset });
1010
- return this.request('GET', `/api/v1/tags${q}`);
1066
+ const r = await this.request('GET', `/api/v1/bookmarks${q}`);
1067
+ const list = r.bookmarks ?? r.tags ?? [];
1068
+ return { bookmarks: list, total: r.total };
1011
1069
  }
1070
+ /** @deprecated Use {@link listBookmarks}. */
1071
+ async listTags(options) {
1072
+ const r = await this.listBookmarks(options);
1073
+ return { tags: r.bookmarks, total: r.total };
1074
+ }
1075
+ async getBookmark(name) {
1076
+ const r = await this.request('GET', `/api/v1/bookmarks/${encodeURIComponent(name)}`);
1077
+ return (r.bookmark ?? r.tag);
1078
+ }
1079
+ /** @deprecated Use {@link getBookmark}. */
1012
1080
  async getTag(name) {
1013
- const r = await this.request('GET', `/api/v1/tags/${encodeURIComponent(name)}`);
1014
- return r.tag;
1081
+ return this.getBookmark(name);
1015
1082
  }
1083
+ async deleteBookmark(name) {
1084
+ await this.request('DELETE', `/api/v1/bookmarks/${encodeURIComponent(name)}`);
1085
+ }
1086
+ /** @deprecated Use {@link deleteBookmark}. */
1016
1087
  async deleteTag(name) {
1017
- await this.request('DELETE', `/api/v1/tags/${encodeURIComponent(name)}`);
1088
+ return this.deleteBookmark(name);
1018
1089
  }
1019
- async diff(from, to, includeContent) {
1020
- return this.request('POST', '/api/v1/diff', {
1090
+ async compare(from, to, includeContent) {
1091
+ return this.request('POST', '/api/v1/compare', {
1021
1092
  from,
1022
1093
  to,
1023
1094
  include_content: includeContent ?? false,
1024
1095
  });
1025
1096
  }
1026
- async mergeBranch(targetBranch, sourceBranch, options) {
1027
- const body = { source_branch: sourceBranch };
1097
+ /** @deprecated Use {@link compare}. */
1098
+ async diff(from, to, includeContent) {
1099
+ return this.compare(from, to, includeContent);
1100
+ }
1101
+ async publishWorkspace(targetWorkspace, sourceWorkspace, options) {
1102
+ const body = {
1103
+ source_workspace: sourceWorkspace,
1104
+ source_branch: sourceWorkspace,
1105
+ };
1028
1106
  if (options?.message)
1029
1107
  body.message = options.message;
1030
1108
  body.auto_resolve = options?.autoResolve ?? 'none';
1031
- return this.request('POST', `/api/v1/branches/${encodeURIComponent(targetBranch)}/merge`, body);
1109
+ return this.request('POST', `/api/v1/workspaces/${encodeURIComponent(targetWorkspace)}/publish`, body);
1110
+ }
1111
+ /** @deprecated Use {@link publishWorkspace}. */
1112
+ async mergeBranch(targetBranch, sourceBranch, options) {
1113
+ return this.publishWorkspace(targetBranch, sourceBranch, options);
1032
1114
  }
1033
1115
  /**
1034
- * Create `branchName` from {@link options.fromBranch} (or current context branch), run `fn` scoped to that branch,
1035
- * create a commit, then merge back into the parent branch unless {@link options.autoMerge} is `false`.
1036
- * Restores previous {@link DatabaseContext} afterward.
1116
+ * Create `workspaceName` from {@link options.fromBranch} (or current context), run `fn` scoped to that workspace,
1117
+ * create a checkpoint, then publish back unless {@link options.autoMerge} is `false`.
1037
1118
  */
1038
- async withBranch(branchName, fn, options) {
1119
+ async withWorkspace(workspaceName, fn, options) {
1039
1120
  const prev = { ...this.defaultContext };
1040
- const fromBranch = options?.fromBranch ?? prev.branch ?? '';
1041
- let commit;
1042
- let merge;
1121
+ const fromWs = options?.fromBranch ?? prev.branch ?? prev.workspace ?? '';
1122
+ let checkpoint;
1123
+ let publish;
1043
1124
  try {
1044
- await this.createBranch(branchName, fromBranch || undefined, prev.date || undefined);
1045
- this.setContext({ branch: branchName });
1125
+ await this.createWorkspace(workspaceName, fromWs || undefined, prev.date || undefined);
1126
+ this.setContext({ workspace: workspaceName, branch: workspaceName });
1046
1127
  const result = await fn(this);
1047
- commit = await this.createCommit(options?.message ?? `Branch ${branchName}`, options?.author);
1128
+ checkpoint = await this.createCheckpoint(options?.message ?? `Workspace ${workspaceName}`, options?.author);
1048
1129
  if (options?.autoMerge !== false) {
1049
- merge = await this.mergeBranch(fromBranch, branchName, {
1130
+ publish = await this.publishWorkspace(fromWs, workspaceName, {
1050
1131
  message: options?.message,
1051
1132
  });
1052
1133
  }
1053
- return { result, commit, merge };
1134
+ return { result, checkpoint, publish };
1054
1135
  }
1055
1136
  finally {
1056
1137
  this.setContext(prev);
1057
1138
  }
1058
1139
  }
1140
+ /** @deprecated Use {@link withWorkspace}. */
1141
+ async withBranch(branchName, fn, options) {
1142
+ const r = await this.withWorkspace(branchName, fn, options);
1143
+ return { result: r.result, commit: r.checkpoint, merge: r.publish };
1144
+ }
1059
1145
  /** Send raw XML body (`Content-Type: application/xml`). For JSON wrapper + options use `writeXmlDocument`. */
1060
1146
  async writeXML(xml, _options) {
1061
1147
  const payload = this.isoApplyXmlDbIdentifier(xml);
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, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationOptions, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
3
+ export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationOptions, WorkspaceInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
4
4
  export { XCiteDBError } from './types';
package/dist/types.d.ts CHANGED
@@ -307,6 +307,12 @@ export interface ApiKeyInfo {
307
307
  }
308
308
  export type Flags = 'None' | 'FirstMatch' | 'IncludeChildren' | 'NoChildren' | 'KeepIndexNodes' | 'PrevIfDeleted' | string;
309
309
  export interface DatabaseContext {
310
+ /**
311
+ * Workspace name (isolated editing environment). Empty / unset = main timeline.
312
+ * Sent as `X-Workspace` (preferred); see also {@link DatabaseContext.branch}.
313
+ */
314
+ workspace?: string;
315
+ /** @deprecated Prefer {@link DatabaseContext.workspace}. Sent as `X-Branch` when `workspace` is unset. */
310
316
  branch?: string;
311
317
  date?: string;
312
318
  prefix?: string;
@@ -596,83 +602,115 @@ export interface StoredTriggerResponse {
596
602
  trigger_id: string;
597
603
  trigger: TriggerDefinition;
598
604
  }
599
- /** Version control / commit metadata (git-like layer). */
600
- export interface CommitRecord {
605
+ /** Named snapshot on a workspace (checkpoint). */
606
+ export interface CheckpointRecord {
601
607
  id: string;
608
+ /** Same as `id` when returned by newer APIs. */
609
+ checkpoint_id?: string;
602
610
  branch: string;
603
- /** Internal LMDB date key for this commit snapshot. */
604
- date_key: string;
605
- date_human?: string;
606
- /** Alias for human-readable date (same as date_human when present). */
611
+ /** Human-readable revision date/time. */
607
612
  date?: string;
613
+ date_human?: string;
614
+ /** @deprecated Internal key; omitted from newer API responses. */
615
+ date_key?: string;
608
616
  message: string;
609
617
  author: string;
610
618
  parent_id: string | null;
611
619
  merge_source?: {
612
620
  branch: string;
613
- commit_id: string;
621
+ checkpoint_id?: string;
622
+ commit_id?: string;
614
623
  };
615
624
  cherry_picked_from?: string;
616
625
  affected_identifiers: string[];
617
626
  status: 'active' | 'rolled_back';
618
627
  created_at: number;
619
628
  }
620
- /** Branch row from GET /api/v1/branches (also exposed as BranchListItem). */
621
- export interface BranchInfo {
629
+ /** @deprecated Use {@link CheckpointRecord}. */
630
+ export type CommitRecord = CheckpointRecord;
631
+ /** Workspace row from `GET /api/v1/workspaces` (legacy: `/api/v1/branches`). */
632
+ export interface WorkspaceInfo {
622
633
  name: string;
623
634
  from_branch: string;
624
635
  from_date: string;
625
- /** Internal LMDB fork date key when present. */
626
- from_date_key?: string;
627
- tip_commit?: CommitRecord;
628
- }
629
- export type BranchListItem = BranchInfo;
630
- export interface TagRecord {
636
+ tip_checkpoint?: CheckpointRecord;
637
+ /** @deprecated Alias of {@link WorkspaceInfo.tip_checkpoint}. */
638
+ tip_commit?: CheckpointRecord;
639
+ }
640
+ /** @deprecated Use {@link WorkspaceInfo}. */
641
+ export type BranchInfo = WorkspaceInfo;
642
+ /** @deprecated Use {@link WorkspaceInfo}. */
643
+ export type BranchListItem = WorkspaceInfo;
644
+ export interface BookmarkRecord {
631
645
  name: string;
632
- commit_id: string;
646
+ checkpoint_id: string;
647
+ /** @deprecated Same as `checkpoint_id`. */
648
+ commit_id?: string;
633
649
  branch: string;
634
- date_key: string;
650
+ date?: string;
651
+ /** @deprecated */
652
+ date_key?: string;
635
653
  message?: string;
636
654
  author?: string;
637
655
  created_at: number;
638
656
  }
639
- export interface DiffEntry {
657
+ /** @deprecated Use {@link BookmarkRecord}. */
658
+ export type TagRecord = BookmarkRecord;
659
+ export interface CompareEntry {
640
660
  identifier: string;
641
661
  action: 'added' | 'modified' | 'deleted';
642
662
  from_content?: string;
643
663
  to_content?: string;
644
664
  }
645
- export interface DiffRef {
665
+ /** @deprecated Use {@link CompareEntry}. */
666
+ export type DiffEntry = CompareEntry;
667
+ export interface CompareRef {
646
668
  branch?: string;
647
- /** Internal date key (preferred for exact revision). */
669
+ /** Human-readable date (preferred). */
670
+ date?: string;
671
+ checkpoint_id?: string;
672
+ /** @deprecated Undocumented fallback. */
648
673
  date_key?: string;
674
+ /** @deprecated Use {@link CompareRef.checkpoint_id}. */
649
675
  commit_id?: string;
650
676
  }
651
- export interface DiffResult {
652
- changes: DiffEntry[];
677
+ /** @deprecated Use {@link CompareRef}. */
678
+ export type DiffRef = CompareRef;
679
+ export interface CompareResult {
680
+ changes: CompareEntry[];
653
681
  from: {
654
682
  branch?: string;
683
+ date?: string;
655
684
  date_key?: string;
656
685
  };
657
686
  to: {
658
687
  branch?: string;
688
+ date?: string;
659
689
  date_key?: string;
660
690
  };
661
691
  total_changes: number;
662
692
  }
663
- export interface MergeConflict {
693
+ /** @deprecated Use {@link CompareResult}. */
694
+ export type DiffResult = CompareResult;
695
+ export interface PublishConflict {
664
696
  identifier: string;
665
697
  source_action: string;
666
698
  target_action: string;
667
699
  }
668
- export interface MergeResult {
700
+ /** @deprecated Use {@link PublishConflict}. */
701
+ export type MergeConflict = PublishConflict;
702
+ export interface PublishResult {
669
703
  status: 'completed' | 'conflicts';
670
- commit?: CommitRecord;
704
+ checkpoint?: CheckpointRecord;
705
+ /** @deprecated Alias of {@link PublishResult.checkpoint}. */
706
+ commit?: CheckpointRecord;
671
707
  merged_identifiers?: string[];
672
- conflicts?: MergeConflict[];
708
+ conflicts?: PublishConflict[];
673
709
  auto_mergeable?: string[];
674
710
  message?: string;
675
711
  }
712
+ /** @deprecated Use {@link PublishResult}. */
713
+ export type MergeResult = PublishResult;
676
714
  export declare class XCiteDBError extends Error {
677
715
  readonly status: number;
678
716
  readonly body?: unknown | undefined;
package/llms-full.txt CHANGED
@@ -8,13 +8,13 @@
8
8
 
9
9
  Before reading the full reference, note these critical differences from typical databases:
10
10
 
11
- 1. **The default branch is the empty string `""`, not `"main"`.** When no `X-Branch` header is sent (or `context.branch` is omitted/empty), the server operates on the root timeline. A branch named `"main"` may exist as a user-created branch, but it is not required or special.
11
+ 1. **The default workspace is the empty string `""`, not `"main"`.** When no `X-Workspace` header is sent (or `context.workspace` / `context.branch` is omitted/empty), the server operates on the root timeline. `X-Branch` is a supported alias of `X-Workspace`. A workspace named `"main"` may exist as user-created metadata, but it is not required or special.
12
12
 
13
13
  2. **Identifiers are hierarchical, path-like strings** — e.g. `/us/bills/hr1`, `/manual/v2/chapter3`. They are NOT auto-generated UUIDs. The leading `/` is part of the identifier. Parent/child relationships are derived from the path structure.
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`, and optionally `X-Unversioned` for explicit flat writes), not URL path segments.
17
+ 4. **Context (workspace + date) travels as HTTP headers** (`X-Workspace` preferred, `X-Branch` alias, `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
 
@@ -28,6 +28,16 @@ Before reading the full reference, note these critical differences from typical
28
28
 
29
29
  10. **Ephemeral test sessions.** `POST /api/v1/test/sessions` (authenticated) returns a UUID **`session_token`**. Clients send **`X-Test-Session: <token>`** on API calls to use an isolated, TTL- and quota-limited LMDB instead of production project data. Unless **`X-Test-Auth: required`** is set, **developer** JWT/API-key checks are bypassed (synthetic admin for wet tests), but **app-user** identity via **`X-App-User-Token`** or Bearer app-user JWT is still recognized. Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`. The test store starts empty (no cloned production project config).
30
30
 
31
+ ## Choosing the Right Versioning Approach
32
+
33
+ - **Write at a specific date:** Set `X-Date` / `context.date` and write — every dated write is a **temporal revision**; no workspace or checkpoint required.
34
+ - **Read as-of a date:** Set `X-Date` and read; the engine returns the revision at or before that instant.
35
+ - **Isolated editing (draft → publish):** Create a **workspace**, edit, then **publish** to the target timeline; optional **checkpoints** for named snapshots.
36
+ - **Audit trail:** Checkpoints carry messages and affected identifiers; **bookmarks** name a checkpoint.
37
+ - **Undo a batch:** **Revert** to a prior checkpoint on that workspace.
38
+
39
+ Legacy REST paths under `/api/v1/branches`, `/commits`, `/tags`, `/diff` remain **deprecated** aliases; prefer **`/api/v1/workspaces`**, **`/checkpoints`**, **`/bookmarks`**, **`/compare`**.
40
+
31
41
  ## Common Pitfalls
32
42
 
33
43
  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`.
@@ -65,25 +75,25 @@ Before reading the full reference, note these critical differences from typical
65
75
  ### 1.1 What is XciteDB?
66
76
  XciteDB is an enterprise-grade **Backend-as-a-Service (BaaS)** built on top of a highly optimized **embedded LMDB** database engine. Powered by a high-performance C++17 HTTP/WebSocket API, XciteDB exposes a multi-tenant, secure platform for managing complex, versioned, structured documents.
67
77
 
68
- **Both XML and JSON are first-class citizens.** XciteDB can operate as a pure **XML document store**, a pure **JSON document database**, or any combination of the two — including the common pattern of **XML documents enriched with structured JSON metadata**. In every mode, data is deeply shredded into its structural components (elements, attributes, objects, fields, array items), versioned, branched, indexed by path, and fully queryable through the same Unquery analytics engine.
78
+ **Both XML and JSON are first-class citizens.** XciteDB can operate as a pure **XML document store**, a pure **JSON document database**, or any combination of the two — including the common pattern of **XML documents enriched with structured JSON metadata**. In every mode, data is deeply shredded into its structural components (elements, attributes, objects, fields, array items), versioned, indexed by path, and fully queryable through the same Unquery analytics engine — with **temporal revisions**, **workspaces**, and **checkpoints** when you need collaborative workflow.
69
79
 
70
80
  ### 1.2 The Problem It Solves
71
81
  Standard relational databases and generic NoSQL stores struggle with highly structured, hierarchical, and deeply versioned content. Building collaborative authoring tools, legislative drafting systems, or complex content management platforms usually requires bolting a Git-like versioning layer and document-aware parsers onto an ill-fitting database.
72
82
 
73
- XciteDB bridges this gap. It provides **Git-like versioning for structured XML and JSON data** out of the box, delivered as a modern, API-first cloud platform with multi-tenancy, Attribute-Based Access Control (ABAC), and S3-compatible backups.
83
+ XciteDB bridges this gap. It provides **temporal versioning plus optional workspaces and checkpoints** for structured XML and JSON data out of the box, delivered as a modern, API-first cloud platform with multi-tenancy, Attribute-Based Access Control (ABAC), and S3-compatible backups.
74
84
 
75
85
  ### 1.3 Target Workloads
76
86
  XciteDB shines in domains requiring strict auditability, collaborative authoring, and complex document hierarchies:
77
87
  - **Legal and Legislative Drafting:** Tracking amendments, clauses, and exact historical states of laws.
78
88
  - **Structured Technical Documentation:** Managing manuals, specifications, and compliance documents where every change must be versioned and attributable.
79
- - **Collaborative Content Management:** Systems requiring branching, merging, and cooperative locking to prevent editor conflicts.
89
+ - **Collaborative Content Management:** Systems requiring isolated workspaces, publish workflows, and cooperative locking to prevent editor conflicts.
80
90
  - **Application State and Configuration:** Storing deeply structured JSON configuration, feature flags, or workflow state that benefits from versioning and path-level querying.
81
91
 
82
92
  ### 1.4 Unique Differentiators
83
- - **First-Class XML & JSON:** Both formats are shredded, indexed by path, versioned, branched, and fully queryable through the same engine.
93
+ - **First-Class XML & JSON:** Both formats are shredded, indexed by path, versioned, and fully queryable through the same engine.
84
94
  - **Embedded LMDB Engine:** A memory-mapped, ACID-compliant storage core purpose-built for structured document workloads.
85
95
  - **Bare-Metal Speed:** Reads hit memory-mapped pages with no network round-trip. Microsecond-class read latency on warm data.
86
- - **"Git for Data" Versioning:** Branches, commits, tags, diffs, and time-travel are first-class database primitives.
96
+ - **Document-centric versioning:** Temporal revisions (`X-Date`), workspaces, checkpoints, bookmarks, compare, publish, revert, and apply — plus time-travel reads.
87
97
  - **Unquery DSL:** A purpose-built declarative query language for navigating, filtering, and aggregating data across both XML trees and JSON structures.
88
98
  - **Native Office Ingestion:** Directly convert and ingest DOCX, ODF, RTF, and PDF formats into clean, versioned XML documents.
89
99
  - **Transparent Tenant Routing:** Scale out horizontally using a coordinator/worker topology without breaking the client API contract.
@@ -106,10 +116,10 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
106
116
  - **JSON Metadata on XML:** JSON metadata can be attached to any XML document or path.
107
117
  - **Hierarchical Identifiers:** Both XML and JSON documents are addressed via logical, path-like strings. The engine natively indexes parent/child relationships.
108
118
 
109
- ### 2.3 Git-Like Document Versioning
110
- - **Branches & Commits:** Isolate collaborative edits into branches. Create atomic commits with descriptive messages.
111
- - **Merge & Cherry-Pick:** Merge branches or cherry-pick specific commits.
112
- - **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**).
119
+ ### 2.3 Document versioning model
120
+ - **Temporal revisions & workspaces:** Every write with `X-Date` records under that revision instant. **Workspaces** isolate draft edits from the root timeline.
121
+ - **Checkpoints, publish, apply, revert:** Optional named snapshots (**checkpoints**); **publish** merges a workspace into a target; **apply** brings another checkpoint’s changes; **revert** rolls the workspace back to a checkpoint.
122
+ - **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**).
113
123
  - **Cooperative Locking:** TTL locks prevent conflicting writes (HTTP 409 on conflict).
114
124
 
115
125
  ### 2.4 Unquery: Declarative Query DSL
@@ -142,7 +152,8 @@ Welcome to the **XciteDB HTTP API**. This reference describes REST endpoints und
142
152
  |--------|------|-------------|
143
153
  | `Authorization` | Usually required | `Bearer <JWT>` or `Bearer <api_key>` |
144
154
  | `X-Project-Id` | Platform console / multi-project JWT | Selects the **tenant project** when the token is not already bound to one tenant |
145
- | `X-Branch` | Optional | Active branch name for document and versioning operations |
155
+ | `X-Workspace` | Optional | Active workspace name for document and versioning operations (preferred) |
156
+ | `X-Branch` | Optional | Deprecated alias of `X-Workspace` |
146
157
  | `X-Date` | Optional | Point-in-time / revision context (ISO-like string as used by your deployment) |
147
158
  | `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). |
148
159
 
@@ -241,8 +252,8 @@ describe('XciteDB integration', () => {
241
252
  expect(xml).toContain('<title>Hello</title>');
242
253
  });
243
254
 
244
- it('creates a branch, commits, and merges', async () => {
245
- await client.withBranch('feature-test', async (c) => {
255
+ it('creates a workspace, checkpoints, and publishes', async () => {
256
+ await client.withWorkspace('feature-test', async (c) => {
246
257
  await c.writeJsonDocument('test.feature', { active: true });
247
258
  }, { message: 'Add feature flag', autoMerge: true });
248
259
  const doc = await client.readJsonDocument<{ active: boolean }>('test.feature');
@@ -283,8 +294,8 @@ async def test_xml_document(db):
283
294
  assert "<title>Hello</title>" in result
284
295
 
285
296
  @pytest.mark.asyncio
286
- async def test_branch_and_merge(db):
287
- async with db.with_branch("feature-test", message="Add flag", auto_merge=True):
297
+ async def test_workspace_and_publish(db):
298
+ async with db.with_workspace("feature-test", message="Add flag", auto_merge=True):
288
299
  await db.put("test.feature", {"active": True})
289
300
  doc = await db.get("test.feature")
290
301
  assert doc["active"] is True
@@ -518,7 +529,7 @@ Can also use `"query"` instead of `"identifier"` to target multiple documents by
518
529
 
519
530
  # Search
520
531
 
521
- Full-text search uses embedded XciteFTS (enable per project in server settings).
532
+ Full-text search (backed by Meilisearch or Elasticsearch).
522
533
 
523
534
  **Base path:** `/api/v1/search`
524
535
 
@@ -680,100 +691,107 @@ Dry-run: **`POST /api/v1/security/check`** with `subject`, `identifier`, `action
680
691
 
681
692
  ---
682
693
 
683
- # Branches
694
+ # Workspaces (branches)
684
695
 
685
- **Base path:** `/api/v1/branches`
696
+ **Preferred base path:** `/api/v1/workspaces`
697
+ **Deprecated alias:** `/api/v1/branches` (same handlers)
686
698
 
687
- **IMPORTANT: The default branch is the empty string `""`.** When no branch is specified, the server uses the root timeline.
699
+ **IMPORTANT: The default workspace is the empty string `""`.** When no workspace is specified, the server uses the root timeline.
688
700
 
689
- ## List branches
701
+ ## List workspaces
690
702
 
691
- **`GET /api/v1/branches`** — Returns `{ branches: [{ name, from_branch, from_date, from_date_key, tip_commit }] }`.
703
+ **`GET /api/v1/workspaces`** — Returns `{ workspaces: [...], branches: [...] }` where each item includes `name`, `from_branch`, `from_date`, `tip_checkpoint` (and `tip_commit` mirror).
692
704
 
693
- ## Create branch
705
+ ## Create workspace
694
706
 
695
- **`POST /api/v1/branches`** — `{ "name": "feature-x", "from_branch": "", "from_date": "" }`
707
+ **`POST /api/v1/workspaces`** — `{ "name": "feature-x", "from_branch": "", "from_date": "" }`
696
708
 
697
- ## Get branch
709
+ ## Get workspace
698
710
 
699
- **`GET /api/v1/branches/{name}`** — Returns `{ branch: {...} }`.
711
+ **`GET /api/v1/workspaces/{name}`** — Returns `{ workspace: {...}, branch: {...} }`.
700
712
 
701
- ## Delete branch
713
+ ## Delete workspace
702
714
 
703
- **`DELETE /api/v1/branches/{name}`**
715
+ **`DELETE /api/v1/workspaces/{name}`**
704
716
 
705
- ## Merge branch
717
+ ## Publish workspace
706
718
 
707
- **`POST /api/v1/branches/{target}/merge`**
719
+ **`POST /api/v1/workspaces/{target}/publish`**
708
720
 
709
721
  ```json
710
722
  {
723
+ "source_workspace": "feature-x",
711
724
  "source_branch": "feature-x",
712
- "message": "Merge feature",
725
+ "message": "Publish feature",
713
726
  "auto_resolve": "none"
714
727
  }
715
728
  ```
716
729
 
717
730
  `auto_resolve`: `"none"` | `"source"` | `"target"`.
718
731
 
719
- Returns `{ status: "completed"|"conflicts", commit?, merged_identifiers?, conflicts? }`.
732
+ Returns `{ status: "completed"|"conflicts", checkpoint?, commit?, merged_identifiers?, conflicts? }` (`commit` mirrors `checkpoint`).
720
733
 
721
- ## Delete revision on branch
734
+ ## Delete revision on workspace
722
735
 
723
- **`DELETE /api/v1/branches/{name}/revisions/{date}`**
736
+ **`DELETE /api/v1/workspaces/{name}/revisions/{date}`** (human-readable date in path; legacy `/branches/...` path still works)
724
737
 
725
738
  ---
726
739
 
727
- # Commits, tags & diff
740
+ # Checkpoints, bookmarks & compare
728
741
 
729
- ## Create commit
742
+ ## Create checkpoint
730
743
 
731
- **`POST /api/v1/commits`** — `{ "message": "...", "author": "..." }`
744
+ **`POST /api/v1/checkpoints`** — `{ "message": "...", "author": "..." }`
745
+ (Deprecated: **`POST /api/v1/commits`**)
732
746
 
733
- Returns `{ commit: { id, branch, date_key, message, author, ... } }`.
747
+ Returns `{ checkpoint: { id, checkpoint_id, branch, date, message, author, ... }, commit: <same> }`. Public JSON uses human-readable **`date`** (no `date_key`).
734
748
 
735
- ## List commits
749
+ ## List checkpoints
736
750
 
737
- **`GET /api/v1/commits?branch=...&limit=...&offset=...`**
751
+ **`GET /api/v1/checkpoints?branch=...&limit=...&offset=...`** (query param name unchanged; means workspace)
752
+ Returns `{ checkpoints: [...], commits: [...], total, branch }` (mirrors).
738
753
 
739
- Returns `{ commits: [...], total, branch }`.
754
+ ## Get checkpoint
740
755
 
741
- ## Get commit
756
+ **`GET /api/v1/checkpoints/{id}`** — Returns `{ checkpoint, commit }`.
742
757
 
743
- **`GET /api/v1/commits/{id}`** Returns `{ commit: {...} }`.
758
+ ## Revert to checkpoint
744
759
 
745
- ## Rollback to commit
760
+ **`POST /api/v1/checkpoints/{id}/revert`** `{ "confirm": true }`
761
+ (Deprecated: **`.../rollback`**)
746
762
 
747
- **`POST /api/v1/commits/{id}/rollback`** — `{ "confirm": true }`
763
+ ## Apply checkpoint
748
764
 
749
- ## Cherry-pick commit
765
+ **`POST /api/v1/checkpoints/{id}/apply`** — `{ "message": "...", "author": "..." }`
766
+ (Deprecated: **`.../cherry-pick`**)
750
767
 
751
- **`POST /api/v1/commits/{id}/cherry-pick`** — `{ "message": "...", "author": "..." }`
768
+ ## Create bookmark
752
769
 
753
- ## Create tag
770
+ **`POST /api/v1/bookmarks`** — `{ "name": "v1.0", "checkpoint_id": "...", "commit_id": "...", "message": "..." }`
771
+ (Deprecated: **`POST /api/v1/tags`**)
754
772
 
755
- **`POST /api/v1/tags`** — `{ "name": "v1.0", "commit_id": "...", "message": "..." }`
773
+ ## List bookmarks
756
774
 
757
- ## List tags
775
+ **`GET /api/v1/bookmarks?limit=...&offset=...`** — Returns `{ bookmarks: [...], tags: [...], total }` (mirrors where applicable).
758
776
 
759
- **`GET /api/v1/tags?limit=...&offset=...`** Returns `{ tags: [...], total }`.
777
+ ## Get / Delete bookmark
760
778
 
761
- ## Get / Delete tag
779
+ **`GET /api/v1/bookmarks/{name}`** / **`DELETE /api/v1/bookmarks/{name}`** (deprecated `/tags/...`)
762
780
 
763
- **`GET /api/v1/tags/{name}`** / **`DELETE /api/v1/tags/{name}`**
781
+ ## Compare
764
782
 
765
- ## Diff
766
-
767
- **`POST /api/v1/diff`**
783
+ **`POST /api/v1/compare`**
768
784
 
769
785
  ```json
770
786
  {
771
- "from": { "branch": "", "date_key": "..." },
787
+ "from": { "branch": "", "date": "2024-01-15T00:00:00" },
772
788
  "to": { "branch": "feature-x" },
773
789
  "include_content": true
774
790
  }
775
791
  ```
776
792
 
793
+ (Deprecated: **`POST /api/v1/diff`**; `date_key` still accepted internally but prefer **`date`**.)
794
+
777
795
  Returns `{ changes: [{ identifier, action: "added"|"modified"|"deleted", from_content?, to_content? }], total_changes }`.
778
796
 
779
797
  ---
@@ -1243,7 +1261,7 @@ import { XCiteDBClient } from '@xcitedbs/client';
1243
1261
  const client = new XCiteDBClient({
1244
1262
  baseUrl: 'http://localhost:8080',
1245
1263
  apiKey: 'your-api-key',
1246
- context: { branch: '', date: '' },
1264
+ context: { workspace: '', date: '' },
1247
1265
  });
1248
1266
 
1249
1267
  // Health check
@@ -1263,12 +1281,12 @@ await client.writeJsonDocument('app.settings', { theme: 'dark' });
1263
1281
  // Read JSON document
1264
1282
  const settings = await client.readJsonDocument('app.settings');
1265
1283
 
1266
- // Branch, edit, commit, merge
1267
- await client.createBranch('feature-x');
1268
- client.setContext({ branch: 'feature-x' });
1284
+ // Workspace, edit, checkpoint, publish
1285
+ await client.createWorkspace('feature-x');
1286
+ client.setContext({ workspace: 'feature-x' });
1269
1287
  await client.writeJsonDocument('app.settings', { theme: 'light' });
1270
- await client.createCommit('Switch to light theme');
1271
- await client.mergeBranch('', 'feature-x');
1288
+ await client.createCheckpoint('Switch to light theme');
1289
+ await client.publishWorkspace('', 'feature-x');
1272
1290
  ```
1273
1291
 
1274
1292
  ## Constructor Options
@@ -1289,7 +1307,8 @@ interface XCiteDBClientOptions {
1289
1307
  }
1290
1308
 
1291
1309
  interface DatabaseContext {
1292
- branch?: string; // '' = default (root timeline)
1310
+ workspace?: string; // '' = default (root timeline); preferred
1311
+ branch?: string; // @deprecated alias of workspace
1293
1312
  date?: string; // Point-in-time
1294
1313
  prefix?: string; // Identifier prefix filter
1295
1314
  unversioned?: boolean; // Sends X-Unversioned: true (flat writes; do not combine with date)
@@ -1357,30 +1376,34 @@ interface DatabaseContext {
1357
1376
  - `queryMetaByQuery<T>(query, path?)` → `T`
1358
1377
  - `clearMeta(query)` → `boolean`
1359
1378
 
1360
- ### Branches
1361
- - `withBranch(name, fn, options?)` → `{ result, commit?, merge? }` — branch, callback, commit, merge back
1362
- - `createBranch(name, fromBranch?, fromDate?)` → `void`
1363
- - `listBranches()` → `BranchInfo[]`
1364
- - `getBranch(name)` → `BranchInfo`
1365
- - `deleteBranch(name)` → `void`
1366
- - `deleteRevision(branch, date)` → `void`
1367
- - `mergeBranch(targetBranch, sourceBranch, options?)` → `MergeResult`
1368
-
1369
- ### Commits
1370
- - `createCommit(message, author?)` → `CommitRecord`
1371
- - `listCommits(options?)` → `{ commits, total, branch }`
1372
- - `getCommit(commitId)` → `CommitRecord`
1373
- - `rollbackToCommit(commitId)` → `{ rolled_back_commits, current_tip }`
1374
- - `cherryPick(commitId, message?, author?)` → `CommitRecord`
1375
-
1376
- ### Tags
1377
- - `createTag(name, commitId, message?, author?)` → `TagRecord`
1378
- - `listTags(options?)` → `{ tags, total }`
1379
- - `getTag(name)` → `TagRecord`
1380
- - `deleteTag(name)` → `void`
1381
-
1382
- ### Diff
1383
- - `diff(from: DiffRef, to: DiffRef, includeContent?)` `DiffResult`
1379
+ ### Workspaces & checkpoints
1380
+ - `withWorkspace(name, fn, options?)` → `{ result, checkpoint?, publish? }` — workspace, callback, checkpoint, publish back
1381
+ - `createWorkspace(name, fromBranch?, fromDate?)` → `void`
1382
+ - `listWorkspaces()` → `WorkspaceInfo[]`
1383
+ - `getWorkspace(name)` → `WorkspaceInfo`
1384
+ - `deleteWorkspace(name)` → `void`
1385
+ - `deleteRevision(branch, date)` → `void` (workspace name + human date)
1386
+ - `publishWorkspace(targetWorkspace, sourceWorkspace, options?)` → `PublishResult`
1387
+ - **Deprecated:** `withBranch`, `createBranch`, `listBranches`, `getBranch`, `deleteBranch`, `mergeBranch` (same HTTP behavior)
1388
+
1389
+ ### Checkpoints
1390
+ - `createCheckpoint(message, author?)` → `CheckpointRecord`
1391
+ - `listCheckpoints(options?)` → `{ checkpoints, total, branch }` (wire JSON may also include `commits` mirror)
1392
+ - `getCheckpoint(checkpointId)` → `CheckpointRecord`
1393
+ - `revertToCheckpoint(checkpointId)` → `{ rolled_back_checkpoints, rolled_back_commits?, current_tip }`
1394
+ - `applyCheckpoint(checkpointId, message?, author?)` → `CheckpointRecord`
1395
+ - **Deprecated:** `createCommit`, `listCommits`, `getCommit`, `rollbackToCommit`, `cherryPick`
1396
+
1397
+ ### Bookmarks
1398
+ - `createBookmark(name, checkpointId, message?, author?)` → `BookmarkRecord`
1399
+ - `listBookmarks(options?)` → `{ bookmarks, tags, total, limit?, offset? }`
1400
+ - `getBookmark(name)` → `BookmarkRecord`
1401
+ - `deleteBookmark(name)` → `void`
1402
+ - **Deprecated:** `createTag`, `listTags`, `getTag`, `deleteTag`
1403
+
1404
+ ### Compare
1405
+ - `compare(from: CompareRef, to: CompareRef, includeContent?)` → `CompareResult`
1406
+ - **Deprecated:** `diff(from: DiffRef, …)` — alias of `compare`
1384
1407
 
1385
1408
  ### Locks
1386
1409
  - `acquireLock(identifier, expires?)` → `LockInfo`
@@ -1513,7 +1536,7 @@ async def main():
1513
1536
  async with XCiteDBClient(
1514
1537
  "http://localhost:8080",
1515
1538
  api_key="your-key",
1516
- context=DatabaseContext(branch="", date=""),
1539
+ context=DatabaseContext(workspace="", date=""),
1517
1540
  ) as client:
1518
1541
  print(await client.health())
1519
1542
  print(await client.query_documents(XCiteQuery(match_start="/manual/")))
@@ -1523,13 +1546,13 @@ async def main():
1523
1546
  print(await client.search(TextSearchQuery(query="guide", limit=10)))
1524
1547
  await client.platform_login("admin@localhost", "password")
1525
1548
  # await client.login_app_user("user@example.com", "pw") # set context.project_id / tenant_id if needed
1526
- async with client.with_branch("feature-x", message="WIP", auto_merge=True):
1549
+ async with client.with_workspace("feature-x", message="WIP", auto_merge=True):
1527
1550
  await client.put("app.settings", {"theme": "light"})
1528
1551
 
1529
1552
  asyncio.run(main())
1530
1553
  ```
1531
1554
 
1532
- 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).
1555
+ 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_workspace` (async context manager; `with_branch` deprecated alias).
1533
1556
 
1534
1557
  ---
1535
1558
 
@@ -1541,7 +1564,7 @@ Async client: `write_xml_document` / `write_document_json` (deprecated), `write_
1541
1564
  xcitedb::XCiteDBClientOptions opt;
1542
1565
  opt.base_url = "http://127.0.0.1:8080";
1543
1566
  opt.api_key = "your-key";
1544
- opt.context.branch = ""; // root timeline
1567
+ opt.context.workspace = ""; // root timeline (`branch` is deprecated alias)
1545
1568
 
1546
1569
  xcitedb::XCiteDBClient client(opt);
1547
1570
  auto health = client.health();
@@ -1553,4 +1576,4 @@ auto ids = client.query_documents(q);
1553
1576
 
1554
1577
  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()`.
1555
1578
 
1556
- Includes optional `xcitevcs` CLI for command-line operations (branches, commits, documents, search, import/export).
1579
+ Includes optional `xcitevcs` CLI for command-line operations (legacy branch/commit vocabulary on the wire, documents, search, import/export).
package/llms.txt CHANGED
@@ -1,18 +1,18 @@
1
1
  # XciteDB
2
2
 
3
- > XciteDB is a versioned XML and JSON document database delivered as a Backend-as-a-Service (BaaS). It provides Git-like branching, commits, time-travel, and hierarchical document identifiers over a high-performance embedded LMDB engine, exposed through a REST + WebSocket API.
3
+ > XciteDB is a versioned XML and JSON document database delivered as a Backend-as-a-Service (BaaS). It provides **temporal revisions** (every dated write is a revision), **workspaces** for isolated editing, **checkpoints** for optional named snapshots, time-travel reads, and hierarchical document identifiers over a high-performance embedded LMDB engine, exposed through a REST + WebSocket API.
4
4
 
5
5
  ## Important: Non-Standard Conventions
6
6
 
7
7
  These are the most common sources of confusion for developers and AI assistants:
8
8
 
9
- 1. **The default branch is the empty string `""`, not `"main"`.** When no `X-Branch` header is sent (or `context.branch` is omitted/empty in the SDK), the server operates on the root timeline. A branch named `"main"` may exist as a user-created branch, but it is not required or special.
9
+ 1. **The default workspace is the empty string `""`, not `"main"`.** When no `X-Workspace` header is sent (or `context.workspace` / `context.branch` is omitted/empty in the SDK), the server operates on the root timeline. `X-Branch` is a supported alias of `X-Workspace`. A workspace named `"main"` may exist as user-created metadata, but it is not required or special.
10
10
 
11
11
  2. **Identifiers are hierarchical, path-like strings** — e.g. `/us/bills/hr1`, `/manual/v2/chapter3`. They are NOT auto-generated UUIDs. The leading `/` is part of the identifier. Parent/child relationships are derived from the path structure (like a filesystem). The server indexes this hierarchy natively.
12
12
 
13
13
  3. **Documents are XML or JSON, not arbitrary blobs.** XML documents are the primary document type, stored with structural shredding. JSON documents are a parallel store keyed by identifier string. Both are fully versioned.
14
14
 
15
- 4. **Context (branch + date) travels as HTTP headers**, not URL path segments. Use `X-Branch` and `X-Date` headers (or the SDK `context` option) to select which branch and point-in-time you are reading/writing.
15
+ 4. **`X-Date` and temporal revision (not “only now”).** Workspace and date context travel as HTTP headers (`X-Workspace` preferred, `X-Branch` alias, `X-Date`), or the SDK `context` option — not as URL path segments. **`X-Date` is not limited to the current server time.** Pass any instant you need as **ISO 8601** (e.g. `2024-01-15T00:00:00`) or **`mm/dd/yyyy`** (optional **`:HH:MM:SS`**) — for example an **official publication or approval date**, or any historical “as of” moment. When **`X-Date` is set**, that value applies to **both** **reads** (query as-of that instant) **and** **writes** (the new revision is stored under that revision instant). When **`X-Date` is omitted** and you do **not** send **`X-Unversioned: true`**, **writes** use **flat** keys (no date suffix on that write); **reads** still resolve **as of the current time**. **`X-Unversioned: true`** requests explicit flat keys and must **not** be combined with **`X-Date`** (the server returns 400).
16
16
 
17
17
  5. **XML documents carry their identifier inside the XML** — specifically via a `db:identifier` attribute on the root element (e.g. `<chapter db:identifier="/book/ch1">...</chapter>`). When writing XML, the server extracts the identifier from the document body.
18
18
 
@@ -24,6 +24,20 @@ These are the most common sources of confusion for developers and AI assistants:
24
24
 
25
25
  9. **Ephemeral test sessions (wet tests).** Call **`POST /api/v1/test/sessions`** with a normal API key or Bearer token to get a `session_token` (UUID). Send **`X-Test-Session: <token>`** on subsequent document/API calls to use an isolated, short-lived LMDB instead of production data. By default **developer** auth (API key / platform JWT) is bypassed, but **app-user** identity (`X-App-User-Token` or Bearer app-user JWT) is still recognized. Send **`X-Test-Auth: required`** to exercise full developer JWT/API-key auth and ABAC against the same test database. Do not send `X-Test-Session` on `/api/v1/test/*` management routes. Server limits apply (`test.session_ttl_seconds`, `test.max_sessions_per_key`, `test.max_test_db_size_bytes` in config). The test DB starts **empty** (no copy of production project config or keys).
26
26
 
27
+ ## Choosing the Right Versioning Approach
28
+
29
+ - **Write at a specific date** (law effective date, publication date, approval date): Set `context.date` / `X-Date` and write. No workspaces or checkpoints needed. Every write with a date creates a **temporal revision** automatically.
30
+
31
+ - **Read historical data** (“what was the document on March 1, 2024?”): Set `context.date` / `X-Date` and read. The engine returns the revision at or before that instant.
32
+
33
+ - **Isolated editing** (draft/review/publish): Create a **workspace**, make changes, then **publish** to the main timeline. Optionally create **checkpoints** for named snapshots within the workspace.
34
+
35
+ - **Audit trail with named snapshots:** Create checkpoints with messages. List/inspect checkpoints for history.
36
+
37
+ - **Undo a batch of changes:** **Revert** to a prior checkpoint (removes all changes after that point on the workspace).
38
+
39
+ Legacy REST paths (`/api/v1/branches`, `/commits`, `/tags`, `/diff`) remain as **deprecated** aliases; prefer **`/api/v1/workspaces`**, **`/checkpoints`**, **`/bookmarks`**, **`/compare`**.
40
+
27
41
  ## Glossary: Project id, display name, and groups
28
42
 
29
43
  - **Project display name** (human-readable, e.g. `invoices`): Shown in the console. The **`X-Project-Id`** header may match either this name **or** the internal project id (server convenience). Do **not** put the display name in JWT claims or in `project:<…>:role` group strings.
@@ -54,6 +68,8 @@ These are the most common sources of confusion for developers and AI assistants:
54
68
 
55
69
  11. **Integration tests should assert token claims**, not only **`/app/auth/me`**. The profile endpoint can look fine while the JWT still carries viewer-style **`groups`**; for ABAC the token payload is authoritative. Use **`getTokenClaims()`** (JS SDK) or decode the JWT in your harness.
56
70
 
71
+ 12. **`X-Date` / `context.date` is not “server clock only.”** Revisions are keyed by the **instant you send**, not an implicit “now” when you set the header. To record a document under a **business date** (published, approved, effective), set **`X-Date`** or **`context.date`** to that instant before writing. Omitting **`X-Date`** does **not** substitute the current time on the write path — it selects **flat** writes (see convention 4).
72
+
57
73
  ## API key capability matrix (typical)
58
74
 
59
75
  | Capability | API key `role` | Public key allowed? |
@@ -90,7 +106,7 @@ await app.writeJsonDocument('userdata/alice/profile', { ok: true });
90
106
  > **AI assistant instruction:** XciteDB has built-in ephemeral test session support — this capability is **not standard** in most BaaS platforms and is specifically designed to enable meaningful wet integration tests. When writing tests for code that uses XciteDB, **do not mock or stub the XciteDB client**. Instead, use the test session pattern below to run tests against a real, isolated, throwaway database. This produces far more reliable tests because real storage, versioning, querying, and access-control behavior is exercised.
91
107
 
92
108
  1. **Provision:** `POST /api/v1/test/sessions` with `Authorization: Bearer …` or `X-API-Key` (same as normal API access). Response JSON includes the session token.
93
- 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.
109
+ 2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-Workspace` / `context` as needed). Data is stored under the server’s `_test/<session>/` area, not your production tenant.
94
110
  3. **Auth behavior:** Developer auth (API key / platform JWT) is bypassed by default for frictionless tests. **App-user identity** (`X-App-User-Token` or Bearer app-user JWT) **is still recognized** in default mode, so `registerAppUser` → `loginAppUser` → `appUserMe` works inside a test session. To also exercise developer auth and ABAC policies, set **`X-Test-Auth: required`** and send normal credentials; the DB is still the test session’s.
95
111
  4. **Cleanup:** `DELETE /api/v1/test/sessions/current` with `X-Test-Session` (no other auth), or `DELETE /api/v1/test/sessions/all` / `DELETE /api/v1/test/sessions/{token}` with normal auth for the owning key or JWT.
96
112
  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.
@@ -107,15 +123,15 @@ import { XCiteDBClient } from '@xcitedbs/client';
107
123
  const client = new XCiteDBClient({
108
124
  baseUrl: 'http://localhost:8080',
109
125
  apiKey: 'your-api-key',
110
- // branch: '' means root timeline (the default); omit or use '' for default
111
- context: { branch: '', date: '' },
126
+ // workspace: '' means root timeline (the default); omit or use '' for default
127
+ context: { workspace: '', date: '' },
112
128
  });
113
129
 
114
130
  // Health check (no auth required)
115
131
  await client.health();
116
132
 
117
- // List all branches
118
- const branches = await client.listBranches();
133
+ // List all workspaces
134
+ const workspaces = await client.listWorkspaces();
119
135
 
120
136
  // Query documents by identifier prefix
121
137
  const docs = await client.queryDocuments({ match_start: '/manual/' });
@@ -140,18 +156,18 @@ const prefs = await client.get<Record<string, unknown>>('app.prefs');
140
156
  await client.remove('app.prefs');
141
157
  const keys = await client.list(undefined, 50, 0);
142
158
 
143
- // Branch helper: create branch, run work, commit, merge back (restores context)
144
- await client.withBranch('feature-x', async (c) => {
159
+ // Workspace helper: create workspace, run work, checkpoint, publish back (restores context)
160
+ await client.withWorkspace('feature-x', async (c) => {
145
161
  await c.writeJsonDocument('app.settings', { theme: 'light' });
146
162
  return 'ok';
147
163
  }, { message: 'Light theme', autoMerge: true, fromBranch: '' });
148
164
 
149
- // Create a branch, make changes, commit, merge
150
- await client.createBranch('feature-x');
151
- client.setContext({ branch: 'feature-x' });
165
+ // Create a workspace, make changes, checkpoint, publish
166
+ await client.createWorkspace('feature-x');
167
+ client.setContext({ workspace: 'feature-x' });
152
168
  await client.writeJsonDocument('app.settings', { theme: 'light' });
153
- const commit = await client.createCommit('Switch to light theme');
154
- await client.mergeBranch('', 'feature-x'); // merge into root (default) branch
169
+ const checkpoint = await client.createCheckpoint('Switch to light theme');
170
+ await client.publishWorkspace('', 'feature-x'); // publish into root (default) workspace
155
171
 
156
172
  // Attach JSON metadata to a document
157
173
  await client.addMeta('/manual/v1/intro', { status: 'draft', owner: 'alice' });
@@ -161,7 +177,7 @@ const lock = await client.acquireLock('/manual/v1/intro');
161
177
  // ... edit ...
162
178
  await client.releaseLock('/manual/v1/intro', lock.lock_id);
163
179
 
164
- // Full-text search (embedded XciteFTS; enable per project in server settings)
180
+ // Full-text search (requires Meilisearch or Elasticsearch backend)
165
181
  const results = await client.search({ query: 'installation guide', limit: 20 });
166
182
 
167
183
  // Subscribe to real-time changes via WebSocket
@@ -180,8 +196,9 @@ interface XCiteDBClientOptions {
180
196
  accessToken?: string; // Platform JWT (from platformLogin)
181
197
  appUserAccessToken?: string; // End-user JWT (from loginAppUser)
182
198
  context?: {
183
- branch?: string; // Branch name; '' = default/root timeline
184
- date?: string; // Point-in-time (ISO-like or internal date key)
199
+ workspace?: string; // Workspace name; '' = default/root timeline (preferred)
200
+ branch?: string; // @deprecated alias of workspace; sends X-Workspace
201
+ date?: string; // X-Date: as-of reads + write revision; any ISO/mm-dd-yyyy instant, not only "now"
185
202
  prefix?: string; // Optional identifier prefix filter
186
203
  project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
187
204
  tenant_id?: string; // Deprecated alias of project_id
@@ -200,7 +217,7 @@ interface XCiteDBClientOptions {
200
217
  - `loginAppUser(email, password)` — App end-user sign-in
201
218
  - `XCiteDBClient.buildProjectGroup(projectId, 'admin'|'editor'|'viewer')` — Static helper: canonical `project:<tenant_id>:<role>` string
202
219
  - `getTokenClaims()` — Decode current `appUserAccessToken` or `accessToken` payload (no signature verification); use for ABAC debugging
203
- - `setContext(ctx)` — Update branch/date context
220
+ - `setContext(ctx)` — Update workspace/date context
204
221
  - `setProjectId(id)` — Switch active project (platform console)
205
222
 
206
223
  **XML Documents:**
@@ -225,19 +242,20 @@ interface XCiteDBClientOptions {
225
242
  - `queryMeta(identifier, path?)` — Read metadata
226
243
  - `clearMeta(query)` — Remove metadata
227
244
 
228
- **Versioning:**
229
- - `withBranch(name, fn, options?)` — Create branch, run callback on client, commit, merge back (optional `autoMerge: false`)
230
- - `createBranch(name, fromBranch?, fromDate?)` — Create branch
231
- - `listBranches()` — List all branches
232
- - `mergeBranch(target, source, options?)` — Merge branches
233
- - `deleteBranch(name)` — Delete branch
234
- - `createCommit(message, author?)` — Commit current state
235
- - `listCommits(options?)` — List commits
236
- - `rollbackToCommit(commitId)` — Rollback to prior commit
237
- - `cherryPick(commitId, message?)` — Cherry-pick a commit
238
- - `diff(from, to, includeContent?)` — Diff between revisions
239
- - `createTag(name, commitId, message?)` — Tag a commit
240
- - `listTags()` / `deleteTag(name)` — Manage tags
245
+ **Versioning (workspaces & checkpoints):**
246
+ - `withWorkspace(name, fn, options?)` — Create workspace, run callback, checkpoint, publish back (optional `autoMerge: false`; `fromBranch` = parent workspace)
247
+ - `createWorkspace(name, fromBranch?, fromDate?)` — Create workspace
248
+ - `listWorkspaces()` — List workspaces
249
+ - `publishWorkspace(target, source, options?)` — Publish workspace changes to a target timeline
250
+ - `deleteWorkspace(name)` — Delete workspace
251
+ - `createCheckpoint(message, author?)` — Named snapshot of current state
252
+ - `listCheckpoints(options?)` — List checkpoints
253
+ - `revertToCheckpoint(checkpointId)` — Revert workspace to a prior checkpoint
254
+ - `applyCheckpoint(checkpointId, message?)` — Apply another checkpoint’s changes here
255
+ - `compare(from, to, includeContent?)` — Compare revisions
256
+ - `createBookmark(name, checkpointId, message?)` — Bookmark a checkpoint
257
+ - `listBookmarks()` / `deleteBookmark(name)` — Manage bookmarks
258
+ - **Deprecated aliases:** `withBranch`, `createBranch`, `listBranches`, `mergeBranch`, `deleteBranch`, `createCommit`, `listCommits`, `rollbackToCommit`, `cherryPick`, `diff`, `createTag`, `listTags`, `deleteTag` (same HTTP behavior)
241
259
 
242
260
  **Locks:**
243
261
  - `acquireLock(identifier, expires?)` — Acquire cooperative lock
@@ -260,7 +278,7 @@ interface XCiteDBClientOptions {
260
278
  ### Advanced: policy expressions (ABAC)
261
279
 
262
280
  - **Actions** (use in `policy.actions`): `read`, `write`, `delete`, `list`, `meta:read`, `meta:write`, `unquery`.
263
- - **`conditions.expression`** uses the same predicate syntax as Unquery `?` filters. **Context:** `subject.id` and **`subject.user_id`** (same value for app users), `subject.email`, `subject.role`, `subject.groups`, `subject.attr.*` (app-user JSON attributes), `resource.identifier`, `resource.path` (array of path segments; indices follow non-empty `/` segments after API canonicalization, e.g. `/userdata/u1/x` → `[userdata,u1,x]`), `env.branch`. Policy **`match_start` / `match_end` / `exact`** strings are canonicalized like API identifiers (add leading `/` when omitted).
281
+ - **`conditions.expression`** uses the same predicate syntax as Unquery `?` filters. **Context:** `subject.id` and **`subject.user_id`** (same value for app users), `subject.email`, `subject.role`, `subject.groups`, `subject.attr.*` (app-user JSON attributes), `resource.identifier`, `resource.path` (array of path segments; indices follow non-empty `/` segments after API canonicalization, e.g. `/userdata/u1/x` → `[userdata,u1,x]`), `env.branch` (workspace name; legacy name retained in policy engine). Policy **`match_start` / `match_end` / `exact`** strings are canonicalized like API identifiers (add leading `/` when omitted).
264
282
  - **Operators:** `=`, `!=`, `>=`, `<=`, `>`, `<`, `in`, `contains`, `starts_with`, `ends_with`, `&`, `|`, `!`, `+` (string concat), postfix `!` (exists).
265
283
  - **Examples:** `resource.path[0] = subject.attr.tenant_code` — tenant isolation; `subject.attr.level >= 5` — numeric attribute gate.
266
284
 
@@ -269,7 +287,7 @@ interface XCiteDBClientOptions {
269
287
  - **Events:** `meta_changed`, `document_written`, `document_deleted`.
270
288
  - **`match`:** required non-empty **`identifiers`** (same pattern objects as policy `resources.identifiers`); optional **`match_meta_path`** (exact or `prefix*`); optional **`match_operation`:** `set` | `append` | `delete`.
271
289
  - **`action`:** **`query`** (document query), **`unquery`** (Unquery template), **`target_identifier`** (or `"$trigger_identifier"`), **`meta_path`**, **`mode`:** `set` | `append`.
272
- - **Unquery vars:** `$trigger_identifier`, `$trigger_meta_path`, `$trigger_operation`, `$trigger_value`. **Trigger `conditions.expression` context:** `trigger.{event,meta_path,operation}`, `value`, `resource`, `env.branch` (no `subject`).
290
+ - **Unquery vars:** `$trigger_identifier`, `$trigger_meta_path`, `$trigger_operation`, `$trigger_value`. **Trigger `conditions.expression` context:** `trigger.{event,meta_path,operation}`, `value`, `resource`, `env.branch` (workspace; no `subject`).
273
291
 
274
292
  ### Advanced: Unquery DSL (`unquery(query, unqueryDoc)`)
275
293
 
@@ -312,7 +330,7 @@ async def main():
312
330
  async with XCiteDBClient(
313
331
  "http://localhost:8080",
314
332
  api_key="your-key",
315
- context=DatabaseContext(branch="", date=""),
333
+ context=DatabaseContext(workspace="", date=""),
316
334
  ) as client:
317
335
  print(await client.health())
318
336
  ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
@@ -334,7 +352,7 @@ asyncio.run(main())
334
352
  xcitedb::XCiteDBClientOptions opt;
335
353
  opt.base_url = "http://127.0.0.1:8080";
336
354
  opt.api_key = "your-key";
337
- opt.context.branch = ""; // root timeline
355
+ opt.context.workspace = ""; // root timeline (branch is deprecated alias)
338
356
 
339
357
  xcitedb::XCiteDBClient client(opt);
340
358
  auto health = client.health();
@@ -347,9 +365,9 @@ auto ids = client.query_documents(q);
347
365
 
348
366
  - **Multi-tenant**: Each project is an isolated tenant with its own data, users, keys, and policies.
349
367
  - **LMDB engine**: Data is memory-mapped; reads are microsecond-class on warm data.
350
- - **Versioning**: Every write is versioned. Branches isolate work. Commits snapshot state. Time-travel reads any historical state via `X-Date`.
368
+ - **Versioning**: **Temporal revisions** are always on when using dates. **Workspaces** isolate draft work; **checkpoints** are optional named snapshots; **publish** merges a workspace into a target timeline. **`X-Date`** selects an instant for **as-of reads** and, when set, for **writes** under that revision (any calendar time you choose — e.g. publication date — not only “now”). Omit **`X-Date`** for **flat** writes on that request, or use **`X-Unversioned: true`** to state that explicitly.
351
369
  - **Two user tiers**: Platform operators manage infrastructure; App users are end-users of applications built on XciteDB.
352
- - **Security policies (ABAC)**: Fine-grained allow/deny rules on actions, resources, roles, and branches.
370
+ - **Security policies (ABAC)**: Fine-grained allow/deny rules on actions, resources, roles, and workspaces (`env.branch` in expressions).
353
371
  - **WebSocket**: Real-time notifications for document changes at `/api/v1/ws`.
354
372
 
355
373
  ## Documentation
@@ -362,8 +380,8 @@ auto ids = client.query_documents(q);
362
380
  - Metadata — JSON metadata on documents
363
381
  - Search — Full-text search
364
382
  - Unquery — Declarative query DSL
365
- - BranchesBranch management
366
- - Commits & tagsVersion snapshots
383
+ - WorkspacesIsolated editing environments
384
+ - Checkpoints & bookmarksNamed snapshots and references
367
385
  - Locks — Cooperative editing locks
368
386
  - Authentication — JWT, API keys, platform vs app users
369
387
  - Security policies — ABAC access control
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcitedbs/client",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "XCiteDB BaaS client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",