@xcitedbs/client 0.2.10 → 0.2.12

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/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
 
@@ -26,7 +26,19 @@ Before reading the full reference, note these critical differences from typical
26
26
 
27
27
  9. **OpenAPI:** See repository `docs/openapi.yaml` for a machine-readable route map.
28
28
 
29
- 10. **Ephemeral test sessions.** `POST /api/v1/test/sessions` (authenticated) returns a UUID **`session_token`**. Clients send **`X-Test-Session: <token>`** on API calls to use an isolated, TTL- and quota-limited LMDB instead of production project data. Unless **`X-Test-Auth: required`** is set, **developer** JWT/API-key checks are bypassed (synthetic admin for wet tests), but **app-user** identity via **`X-App-User-Token`** or Bearer app-user JWT is still recognized. Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`. The test store starts empty (no cloned production project config).
29
+ 10. **Ephemeral test sessions.** `POST /api/v1/test/sessions` (authenticated) returns a UUID **`session_token`**. Clients send **`X-Test-Session: <token>`** on API calls to use an isolated, TTL- and quota-limited LMDB instead of production project data. Unless **`X-Test-Auth: required`** is set, **developer** JWT/API-key checks are bypassed (synthetic admin for wet tests), but **app-user** identity via **`X-App-User-Token`** or Bearer app-user JWT is still recognized. Management routes under **`/api/v1/test/*`** must not include `X-Test-Session`. With a default body (omit or `{}`), the test LMDB starts **empty** (no cloned production project config).
30
+
31
+ 11. **Overlay test sessions.** Same **`POST`**, with JSON **`{"overlay":true}`**, while authenticated for the **project to debug** (project-scoped API key, or platform Bearer + **`X-Project-Id`**). The session metadata records overlay mode; subsequent requests need only **`X-Test-Session`**. The server opens **`XCiteDB(_test/<uuid>/data, <production data path>)`**: production is used as a **read-only base**; reads merge overlay + base; **writes never modify production**. If the production data directory is missing, opening the session database fails. JS **`createTestSession({ …, overlay: true })`**, C++ **`test_session_overlay`** + **`create_test_session`**, MCP **`create_test_session`** tool **`overlay: true`**.
32
+
33
+ ## Choosing the Right Versioning Approach
34
+
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
+ - **Read as-of a date:** Set `X-Date` and read; the engine returns the revision at or before that instant.
37
+ - **Isolated editing (draft → publish):** Create a **workspace**, edit, then **publish** to the target timeline; optional **checkpoints** for named snapshots.
38
+ - **Audit trail:** Checkpoints carry messages and affected identifiers; **bookmarks** name a checkpoint.
39
+ - **Undo a batch:** **Revert** to a prior checkpoint on that workspace.
40
+
41
+ Legacy REST paths under `/api/v1/branches`, `/commits`, `/tags`, `/diff` remain **deprecated** aliases; prefer **`/api/v1/workspaces`**, **`/checkpoints`**, **`/bookmarks`**, **`/compare`**.
30
42
 
31
43
  ## Common Pitfalls
32
44
 
@@ -48,7 +60,7 @@ Before reading the full reference, note these critical differences from typical
48
60
 
49
61
  6. **Self-registration uses server-configured default groups.** `registerAppUser()` assigns groups from the server's `auth.app_users.default_groups` config, not from the client request. To assign specific groups, use the admin endpoint `createAppUser()` instead, or update groups after registration via `updateAppUserGroups()`.
50
62
 
51
- 7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real, empty, isolated LMDB that is automatically scoped away from production and destroyed after the test. See "Ephemeral test sessions" below.
63
+ 7. **Do not mock XciteDB in tests — use ephemeral test sessions instead.** Unlike most BaaS platforms, XciteDB has built-in support for isolated, throwaway database sessions specifically designed for wet integration tests. Mocking the client skips the actual storage, versioning, querying, and ABAC behavior, producing tests that don't catch real integration issues. Use `createTestSession()` / `test_session()` / `create_test_session()` (SDK helpers) or `POST /api/v1/test/sessions` directly to get a real LMDB under `_test/<uuid>/` (empty by default, or **overlay** on read-only production with **`{"overlay":true}`** / **`overlay: true`** / **`test_session_overlay`**). See "Ephemeral test sessions" below.
52
64
 
53
65
  ---
54
66
 
@@ -65,25 +77,25 @@ Before reading the full reference, note these critical differences from typical
65
77
  ### 1.1 What is XciteDB?
66
78
  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
79
 
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.
80
+ **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
81
 
70
82
  ### 1.2 The Problem It Solves
71
83
  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
84
 
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.
85
+ 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
86
 
75
87
  ### 1.3 Target Workloads
76
88
  XciteDB shines in domains requiring strict auditability, collaborative authoring, and complex document hierarchies:
77
89
  - **Legal and Legislative Drafting:** Tracking amendments, clauses, and exact historical states of laws.
78
90
  - **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.
91
+ - **Collaborative Content Management:** Systems requiring isolated workspaces, publish workflows, and cooperative locking to prevent editor conflicts.
80
92
  - **Application State and Configuration:** Storing deeply structured JSON configuration, feature flags, or workflow state that benefits from versioning and path-level querying.
81
93
 
82
94
  ### 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.
95
+ - **First-Class XML & JSON:** Both formats are shredded, indexed by path, versioned, and fully queryable through the same engine.
84
96
  - **Embedded LMDB Engine:** A memory-mapped, ACID-compliant storage core purpose-built for structured document workloads.
85
97
  - **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.
98
+ - **Document-centric versioning:** Temporal revisions (`X-Date`), workspaces, checkpoints, bookmarks, compare, publish, revert, and apply — plus time-travel reads.
87
99
  - **Unquery DSL:** A purpose-built declarative query language for navigating, filtering, and aggregating data across both XML trees and JSON structures.
88
100
  - **Native Office Ingestion:** Directly convert and ingest DOCX, ODF, RTF, and PDF formats into clean, versioned XML documents.
89
101
  - **Transparent Tenant Routing:** Scale out horizontally using a coordinator/worker topology without breaking the client API contract.
@@ -106,10 +118,10 @@ XciteDB shines in domains requiring strict auditability, collaborative authoring
106
118
  - **JSON Metadata on XML:** JSON metadata can be attached to any XML document or path.
107
119
  - **Hierarchical Identifiers:** Both XML and JSON documents are addressed via logical, path-like strings. The engine natively indexes parent/child relationships.
108
120
 
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**).
121
+ ### 2.3 Document versioning model
122
+ - **Temporal revisions & workspaces:** Every write with `X-Date` records under that revision instant. **Workspaces** isolate draft edits from the root timeline.
123
+ - **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.
124
+ - **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
125
  - **Cooperative Locking:** TTL locks prevent conflicting writes (HTTP 409 on conflict).
114
126
 
115
127
  ### 2.4 Unquery: Declarative Query DSL
@@ -142,7 +154,8 @@ Welcome to the **XciteDB HTTP API**. This reference describes REST endpoints und
142
154
  |--------|------|-------------|
143
155
  | `Authorization` | Usually required | `Bearer <JWT>` or `Bearer <api_key>` |
144
156
  | `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 |
157
+ | `X-Workspace` | Optional | Active workspace name for document and versioning operations (preferred) |
158
+ | `X-Branch` | Optional | Deprecated alias of `X-Workspace` |
146
159
  | `X-Date` | Optional | Point-in-time / revision context (ISO-like string as used by your deployment) |
147
160
  | `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
161
 
@@ -194,7 +207,7 @@ For **integration and wet tests** against a shared BaaS host without touching pr
194
207
 
195
208
  | Step | What to do |
196
209
  |------|------------|
197
- | **Create** | **`POST /api/v1/test/sessions`** with normal **`Authorization: Bearer …`** or **`X-API-Key`**. Response includes a **`session_token`** (UUID). Server enforces per-credential limits (`test.max_sessions_per_key`, `test.session_ttl_seconds`, `test.max_test_db_size_bytes` in server config). |
210
+ | **Create** | **`POST /api/v1/test/sessions`** with normal **`Authorization: Bearer …`** or **`X-API-Key`**. Response includes a **`session_token`** (UUID). Server enforces per-credential limits (`test.max_sessions_per_key`, `test.session_ttl_seconds`, `test.max_test_db_size_bytes` in server config). Optional JSON body **`{"overlay":true}`** provisions a **read-through production** session (writable delta only under `_test/<uuid>/`; production LMDB is read-only base). |
198
211
  | **Use** | Send **`X-Test-Session: <session_token>`** on document and other data API requests. The server routes to a dedicated LMDB under its data root (`_test/<id>/`), not the caller’s production tenant. **`tenant_id` / `X-Project-Id` semantics do not select production** while the test header is present—the synthetic test tenant is implied. |
199
212
  | **Auth** | **Default:** developer auth (API key / platform JWT) is **bypassed** with a synthetic admin identity. However, **app-user identity is still recognized**: if `X-App-User-Token` or a Bearer app-user JWT is present, the request runs as that app user (for routes like `/app/auth/me`). **`X-Test-Auth: required`:** all auth is validated normally; ABAC applies, but data still comes from the test session DB. |
200
213
  | **Manage** | **`GET /api/v1/test/sessions`** — list sessions for the current credential. **`DELETE /api/v1/test/sessions/current`** — destroy the session named by **`X-Test-Session`** (no other auth). **`DELETE /api/v1/test/sessions/all`** — destroy all sessions for the credential. **`DELETE /api/v1/test/sessions/{token}`** — destroy one session if owned by the credential. Do **not** send **`X-Test-Session`** on these `/api/v1/test/*` routes. |
@@ -202,9 +215,9 @@ For **integration and wet tests** against a shared BaaS host without touching pr
202
215
 
203
216
  **SDK usage (summary):**
204
217
 
205
- - **JavaScript/TypeScript:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })` returns a client configured with `testSessionToken`; optional `testRequireAuth: true` maps to `X-Test-Auth: required`. `destroyTestSession()` calls `DELETE …/test/sessions/current`.
206
- - **Python:** `async with XCiteDBClient.test_session(base_url, api_key=…, …)` provisions and tears down; or pass `test_session_token` / `test_require_auth` to the constructor.
207
- - **C++:** `XCiteDBClient::create_test_session(options)` after setting `api_key` (and optional `test_require_auth`); `destroy_test_session()`.
218
+ - **JavaScript/TypeScript:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })` returns a client configured with `testSessionToken`; optional **`overlay: true`** for overlay mode; optional `testRequireAuth: true` maps to `X-Test-Auth: required`. `destroyTestSession()` calls `DELETE …/test/sessions/current`.
219
+ - **Python:** `async with XCiteDBClient.test_session(base_url, api_key=…, …)` provisions and tears down; or pass `test_session_token` / `test_require_auth` to the constructor. For overlay until the helper accepts a flag, call **`POST /api/v1/test/sessions`** with JSON **`{"overlay":true}`** then construct the client with the returned token.
220
+ - **C++:** `XCiteDBClient::create_test_session(options)` after setting `api_key`, optional **`test_session_overlay = true`**, and optional `test_require_auth`; `destroy_test_session()`.
208
221
 
209
222
  **JavaScript/TypeScript — complete test scaffold (Vitest / Jest):**
210
223
 
@@ -241,8 +254,8 @@ describe('XciteDB integration', () => {
241
254
  expect(xml).toContain('<title>Hello</title>');
242
255
  });
243
256
 
244
- it('creates a branch, commits, and merges', async () => {
245
- await client.withBranch('feature-test', async (c) => {
257
+ it('creates a workspace, checkpoints, and publishes', async () => {
258
+ await client.withWorkspace('feature-test', async (c) => {
246
259
  await c.writeJsonDocument('test.feature', { active: true });
247
260
  }, { message: 'Add feature flag', autoMerge: true });
248
261
  const doc = await client.readJsonDocument<{ active: boolean }>('test.feature');
@@ -283,8 +296,8 @@ async def test_xml_document(db):
283
296
  assert "<title>Hello</title>" in result
284
297
 
285
298
  @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):
299
+ async def test_workspace_and_publish(db):
300
+ async with db.with_workspace("feature-test", message="Add flag", auto_merge=True):
288
301
  await db.put("test.feature", {"active": True})
289
302
  doc = await db.get("test.feature")
290
303
  assert doc["active"] is True
@@ -518,7 +531,7 @@ Can also use `"query"` instead of `"identifier"` to target multiple documents by
518
531
 
519
532
  # Search
520
533
 
521
- Full-text search uses embedded XciteFTS (enable per project in server settings).
534
+ Full-text search uses **embedded XciteFTS** (LMDB index per project). **Semantic** and **hybrid** (FTS + vector) search are available when vector search is enabled in project settings. The JSON field **`mode`** selects behavior (`auto` picks the best option for the project, or set `fts`, `semantic`, or `hybrid` explicitly).
522
535
 
523
536
  **Base path:** `/api/v1/search`
524
537
 
@@ -532,15 +545,26 @@ Full-text search uses embedded XciteFTS (enable per project in server settings).
532
545
  "doc_types": ["xml", "json"],
533
546
  "branch": "",
534
547
  "limit": 20,
535
- "offset": 0
548
+ "offset": 0,
549
+ "mode": "fts"
536
550
  }
537
551
  ```
538
552
 
539
- Response: `{ hits: [{ identifier, path, doc_type, branch, snippet, score, xcitepath? }], total, query }`.
553
+ Optional fields (also on SDK `TextSearchQuery` and the MCP `search` tool): **`min_score`**, **`semantic_weight`** for semantic/hybrid.
554
+
555
+ **Temporal FTS** (posting intervals; applies to the FTS keyword index—use **`mode`: `"fts"`** for pure temporal keyword search, or hybrid where the FTS leg participates):
556
+
557
+ | Field | Meaning |
558
+ |-------|---------|
559
+ | **`at_date`** | Date string (e.g. ISO `YYYY-MM-DD` or `mm/dd/yyyy` as accepted by the server). A posting matches if it is valid at that instant: `start <= at_date < end` (internal 7-char keys after `date2key`). |
560
+ | **`date_from`**, **`date_to`** | Date strings defining a half-open range `[date_from, date_to)`. A posting matches if its `[start, end)` **overlaps** that range. Either bound may be omitted (open-ended). |
561
+ | *(none of the three)* | **Current-time** view: only postings whose interval is still “open” (end at the internal max sentinel) are included. |
562
+
563
+ **Response:** `{ hits: [...], total, query }`. Each hit has **`identifier`**, **`path`**, **`doc_type`**, **`branch`**, **`snippet`**, **`score`**, and optionally **`xcitepath`** (XML). Semantic/hybrid hits may include **`source`**: `fts` \| `semantic` \| `both`. When the server infers a temporal window for the matched term(s), hits may include **`valid_from`** and **`valid_to`** as **7-character internal date keys** (same alphabet as revision keys—not ISO). Omitted for full-range/unversioned postings or when not computed.
540
564
 
541
565
  ## Reindex
542
566
 
543
- **`POST /api/v1/search/reindex`** — Rebuilds the search index.
567
+ **`POST /api/v1/search/reindex`** — Rebuilds the full-text search index.
544
568
 
545
569
  ---
546
570
 
@@ -680,100 +704,107 @@ Dry-run: **`POST /api/v1/security/check`** with `subject`, `identifier`, `action
680
704
 
681
705
  ---
682
706
 
683
- # Branches
707
+ # Workspaces (branches)
684
708
 
685
- **Base path:** `/api/v1/branches`
709
+ **Preferred base path:** `/api/v1/workspaces`
710
+ **Deprecated alias:** `/api/v1/branches` (same handlers)
686
711
 
687
- **IMPORTANT: The default branch is the empty string `""`.** When no branch is specified, the server uses the root timeline.
712
+ **IMPORTANT: The default workspace is the empty string `""`.** When no workspace is specified, the server uses the root timeline.
688
713
 
689
- ## List branches
714
+ ## List workspaces
690
715
 
691
- **`GET /api/v1/branches`** — Returns `{ branches: [{ name, from_branch, from_date, from_date_key, tip_commit }] }`.
716
+ **`GET /api/v1/workspaces`** — Returns `{ workspaces: [...], branches: [...] }` where each item includes `name`, `from_branch`, `from_date`, `tip_checkpoint` (and `tip_commit` mirror).
692
717
 
693
- ## Create branch
718
+ ## Create workspace
694
719
 
695
- **`POST /api/v1/branches`** — `{ "name": "feature-x", "from_branch": "", "from_date": "" }`
720
+ **`POST /api/v1/workspaces`** — `{ "name": "feature-x", "from_branch": "", "from_date": "" }`
696
721
 
697
- ## Get branch
722
+ ## Get workspace
698
723
 
699
- **`GET /api/v1/branches/{name}`** — Returns `{ branch: {...} }`.
724
+ **`GET /api/v1/workspaces/{name}`** — Returns `{ workspace: {...}, branch: {...} }`.
700
725
 
701
- ## Delete branch
726
+ ## Delete workspace
702
727
 
703
- **`DELETE /api/v1/branches/{name}`**
728
+ **`DELETE /api/v1/workspaces/{name}`**
704
729
 
705
- ## Merge branch
730
+ ## Publish workspace
706
731
 
707
- **`POST /api/v1/branches/{target}/merge`**
732
+ **`POST /api/v1/workspaces/{target}/publish`**
708
733
 
709
734
  ```json
710
735
  {
736
+ "source_workspace": "feature-x",
711
737
  "source_branch": "feature-x",
712
- "message": "Merge feature",
738
+ "message": "Publish feature",
713
739
  "auto_resolve": "none"
714
740
  }
715
741
  ```
716
742
 
717
743
  `auto_resolve`: `"none"` | `"source"` | `"target"`.
718
744
 
719
- Returns `{ status: "completed"|"conflicts", commit?, merged_identifiers?, conflicts? }`.
745
+ Returns `{ status: "completed"|"conflicts", checkpoint?, commit?, merged_identifiers?, conflicts? }` (`commit` mirrors `checkpoint`).
720
746
 
721
- ## Delete revision on branch
747
+ ## Delete revision on workspace
722
748
 
723
- **`DELETE /api/v1/branches/{name}/revisions/{date}`**
749
+ **`DELETE /api/v1/workspaces/{name}/revisions/{date}`** (human-readable date in path; legacy `/branches/...` path still works)
724
750
 
725
751
  ---
726
752
 
727
- # Commits, tags & diff
753
+ # Checkpoints, bookmarks & compare
728
754
 
729
- ## Create commit
755
+ ## Create checkpoint
730
756
 
731
- **`POST /api/v1/commits`** — `{ "message": "...", "author": "..." }`
757
+ **`POST /api/v1/checkpoints`** — `{ "message": "...", "author": "..." }`
758
+ (Deprecated: **`POST /api/v1/commits`**)
732
759
 
733
- Returns `{ commit: { id, branch, date_key, message, author, ... } }`.
760
+ Returns `{ checkpoint: { id, checkpoint_id, branch, date, message, author, ... }, commit: <same> }`. Public JSON uses human-readable **`date`** (no `date_key`).
734
761
 
735
- ## List commits
762
+ ## List checkpoints
736
763
 
737
- **`GET /api/v1/commits?branch=...&limit=...&offset=...`**
764
+ **`GET /api/v1/checkpoints?branch=...&limit=...&offset=...`** (query param name unchanged; means workspace)
765
+ Returns `{ checkpoints: [...], commits: [...], total, branch }` (mirrors).
738
766
 
739
- Returns `{ commits: [...], total, branch }`.
767
+ ## Get checkpoint
740
768
 
741
- ## Get commit
769
+ **`GET /api/v1/checkpoints/{id}`** — Returns `{ checkpoint, commit }`.
742
770
 
743
- **`GET /api/v1/commits/{id}`** Returns `{ commit: {...} }`.
771
+ ## Revert to checkpoint
744
772
 
745
- ## Rollback to commit
773
+ **`POST /api/v1/checkpoints/{id}/revert`** `{ "confirm": true }`
774
+ (Deprecated: **`.../rollback`**)
746
775
 
747
- **`POST /api/v1/commits/{id}/rollback`** — `{ "confirm": true }`
776
+ ## Apply checkpoint
748
777
 
749
- ## Cherry-pick commit
778
+ **`POST /api/v1/checkpoints/{id}/apply`** — `{ "message": "...", "author": "..." }`
779
+ (Deprecated: **`.../cherry-pick`**)
750
780
 
751
- **`POST /api/v1/commits/{id}/cherry-pick`** — `{ "message": "...", "author": "..." }`
781
+ ## Create bookmark
752
782
 
753
- ## Create tag
783
+ **`POST /api/v1/bookmarks`** — `{ "name": "v1.0", "checkpoint_id": "...", "commit_id": "...", "message": "..." }`
784
+ (Deprecated: **`POST /api/v1/tags`**)
754
785
 
755
- **`POST /api/v1/tags`** — `{ "name": "v1.0", "commit_id": "...", "message": "..." }`
786
+ ## List bookmarks
756
787
 
757
- ## List tags
788
+ **`GET /api/v1/bookmarks?limit=...&offset=...`** — Returns `{ bookmarks: [...], tags: [...], total }` (mirrors where applicable).
758
789
 
759
- **`GET /api/v1/tags?limit=...&offset=...`** Returns `{ tags: [...], total }`.
790
+ ## Get / Delete bookmark
760
791
 
761
- ## Get / Delete tag
792
+ **`GET /api/v1/bookmarks/{name}`** / **`DELETE /api/v1/bookmarks/{name}`** (deprecated `/tags/...`)
762
793
 
763
- **`GET /api/v1/tags/{name}`** / **`DELETE /api/v1/tags/{name}`**
794
+ ## Compare
764
795
 
765
- ## Diff
766
-
767
- **`POST /api/v1/diff`**
796
+ **`POST /api/v1/compare`**
768
797
 
769
798
  ```json
770
799
  {
771
- "from": { "branch": "", "date_key": "..." },
800
+ "from": { "branch": "", "date": "2024-01-15T00:00:00" },
772
801
  "to": { "branch": "feature-x" },
773
802
  "include_content": true
774
803
  }
775
804
  ```
776
805
 
806
+ (Deprecated: **`POST /api/v1/diff`**; `date_key` still accepted internally but prefer **`date`**.)
807
+
777
808
  Returns `{ changes: [{ identifier, action: "added"|"modified"|"deleted", from_content?, to_content? }], total_changes }`.
778
809
 
779
810
  ---
@@ -1243,7 +1274,7 @@ import { XCiteDBClient } from '@xcitedbs/client';
1243
1274
  const client = new XCiteDBClient({
1244
1275
  baseUrl: 'http://localhost:8080',
1245
1276
  apiKey: 'your-api-key',
1246
- context: { branch: '', date: '' },
1277
+ context: { workspace: '', date: '' },
1247
1278
  });
1248
1279
 
1249
1280
  // Health check
@@ -1263,12 +1294,12 @@ await client.writeJsonDocument('app.settings', { theme: 'dark' });
1263
1294
  // Read JSON document
1264
1295
  const settings = await client.readJsonDocument('app.settings');
1265
1296
 
1266
- // Branch, edit, commit, merge
1267
- await client.createBranch('feature-x');
1268
- client.setContext({ branch: 'feature-x' });
1297
+ // Workspace, edit, checkpoint, publish
1298
+ await client.createWorkspace('feature-x');
1299
+ client.setContext({ workspace: 'feature-x' });
1269
1300
  await client.writeJsonDocument('app.settings', { theme: 'light' });
1270
- await client.createCommit('Switch to light theme');
1271
- await client.mergeBranch('', 'feature-x');
1301
+ await client.createCheckpoint('Switch to light theme');
1302
+ await client.publishWorkspace('', 'feature-x');
1272
1303
  ```
1273
1304
 
1274
1305
  ## Constructor Options
@@ -1289,7 +1320,8 @@ interface XCiteDBClientOptions {
1289
1320
  }
1290
1321
 
1291
1322
  interface DatabaseContext {
1292
- branch?: string; // '' = default (root timeline)
1323
+ workspace?: string; // '' = default (root timeline); preferred
1324
+ branch?: string; // @deprecated alias of workspace
1293
1325
  date?: string; // Point-in-time
1294
1326
  prefix?: string; // Identifier prefix filter
1295
1327
  unversioned?: boolean; // Sends X-Unversioned: true (flat writes; do not combine with date)
@@ -1357,30 +1389,34 @@ interface DatabaseContext {
1357
1389
  - `queryMetaByQuery<T>(query, path?)` → `T`
1358
1390
  - `clearMeta(query)` → `boolean`
1359
1391
 
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`
1392
+ ### Workspaces & checkpoints
1393
+ - `withWorkspace(name, fn, options?)` → `{ result, checkpoint?, publish? }` — workspace, callback, checkpoint, publish back
1394
+ - `createWorkspace(name, fromBranch?, fromDate?)` → `void`
1395
+ - `listWorkspaces()` → `WorkspaceInfo[]`
1396
+ - `getWorkspace(name)` → `WorkspaceInfo`
1397
+ - `deleteWorkspace(name)` → `void`
1398
+ - `deleteRevision(branch, date)` → `void` (workspace name + human date)
1399
+ - `publishWorkspace(targetWorkspace, sourceWorkspace, options?)` → `PublishResult`
1400
+ - **Deprecated:** `withBranch`, `createBranch`, `listBranches`, `getBranch`, `deleteBranch`, `mergeBranch` (same HTTP behavior)
1401
+
1402
+ ### Checkpoints
1403
+ - `createCheckpoint(message, author?)` → `CheckpointRecord`
1404
+ - `listCheckpoints(options?)` → `{ checkpoints, total, branch }` (wire JSON may also include `commits` mirror)
1405
+ - `getCheckpoint(checkpointId)` → `CheckpointRecord`
1406
+ - `revertToCheckpoint(checkpointId)` → `{ rolled_back_checkpoints, rolled_back_commits?, current_tip }`
1407
+ - `applyCheckpoint(checkpointId, message?, author?)` → `CheckpointRecord`
1408
+ - **Deprecated:** `createCommit`, `listCommits`, `getCommit`, `rollbackToCommit`, `cherryPick`
1409
+
1410
+ ### Bookmarks
1411
+ - `createBookmark(name, checkpointId, message?, author?)` → `BookmarkRecord`
1412
+ - `listBookmarks(options?)` → `{ bookmarks, tags, total, limit?, offset? }`
1413
+ - `getBookmark(name)` → `BookmarkRecord`
1414
+ - `deleteBookmark(name)` → `void`
1415
+ - **Deprecated:** `createTag`, `listTags`, `getTag`, `deleteTag`
1416
+
1417
+ ### Compare
1418
+ - `compare(from: CompareRef, to: CompareRef, includeContent?)` → `CompareResult`
1419
+ - **Deprecated:** `diff(from: DiffRef, …)` — alias of `compare`
1384
1420
 
1385
1421
  ### Locks
1386
1422
  - `acquireLock(identifier, expires?)` → `LockInfo`
@@ -1388,7 +1424,7 @@ interface DatabaseContext {
1388
1424
  - `findLocks(identifier)` → `LockInfo[]`
1389
1425
 
1390
1426
  ### Search
1391
- - `search(query: TextSearchQuery)` → `TextSearchResult`
1427
+ - `search(query: TextSearchQuery)` → `TextSearchResult`. Query may include **`at_date`**, **`date_from`**, **`date_to`** (ISO-style or `mm/dd/yyyy` strings) for temporal FTS; set **`mode`: `"fts"`** for keyword-only temporal search. Hits may include **`valid_from`** / **`valid_to`** (7-char internal keys) when the server returns an inferred validity window.
1392
1428
  - `reindex()` → `{ status, message }`
1393
1429
 
1394
1430
  ### Unquery
@@ -1513,7 +1549,7 @@ async def main():
1513
1549
  async with XCiteDBClient(
1514
1550
  "http://localhost:8080",
1515
1551
  api_key="your-key",
1516
- context=DatabaseContext(branch="", date=""),
1552
+ context=DatabaseContext(workspace="", date=""),
1517
1553
  ) as client:
1518
1554
  print(await client.health())
1519
1555
  print(await client.query_documents(XCiteQuery(match_start="/manual/")))
@@ -1521,15 +1557,16 @@ async def main():
1521
1557
  print(await client.read_json_document("app.settings"))
1522
1558
  print(await client.list_identifiers(XCiteQuery(match_start="/manual/")))
1523
1559
  print(await client.search(TextSearchQuery(query="guide", limit=10)))
1560
+ # Temporal FTS: await client.search(TextSearchQuery(query="guide", mode="fts", at_date="2024-06-01"))
1524
1561
  await client.platform_login("admin@localhost", "password")
1525
1562
  # 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):
1563
+ async with client.with_workspace("feature-x", message="WIP", auto_merge=True):
1527
1564
  await client.put("app.settings", {"theme": "light"})
1528
1565
 
1529
1566
  asyncio.run(main())
1530
1567
  ```
1531
1568
 
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).
1569
+ 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
1570
 
1534
1571
  ---
1535
1572
 
@@ -1541,7 +1578,7 @@ Async client: `write_xml_document` / `write_document_json` (deprecated), `write_
1541
1578
  xcitedb::XCiteDBClientOptions opt;
1542
1579
  opt.base_url = "http://127.0.0.1:8080";
1543
1580
  opt.api_key = "your-key";
1544
- opt.context.branch = ""; // root timeline
1581
+ opt.context.workspace = ""; // root timeline (`branch` is deprecated alias)
1545
1582
 
1546
1583
  xcitedb::XCiteDBClient client(opt);
1547
1584
  auto health = client.health();
@@ -1553,4 +1590,4 @@ auto ids = client.query_documents(q);
1553
1590
 
1554
1591
  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
1592
 
1556
- Includes optional `xcitevcs` CLI for command-line operations (branches, commits, documents, search, import/export).
1593
+ Includes optional `xcitevcs` CLI for command-line operations (legacy branch/commit vocabulary on the wire, documents, search, import/export).