@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.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
 
@@ -22,7 +22,21 @@ These are the most common sources of confusion for developers and AI assistants:
22
22
 
23
23
  8. **Project vs tenant id.** In the SDK, prefer `context.project_id` (and `listMyProjects` / `switchProject`). Many JSON bodies and JWT claims still use the field name `tenant_id` for the same value — the client sends that wire name automatically.
24
24
 
25
- 9. **Ephemeral test sessions (wet tests).** Call **`POST /api/v1/test/sessions`** with a normal API key or Bearer token to get a `session_token` (UUID). Send **`X-Test-Session: <token>`** on subsequent document/API calls to use an isolated, short-lived LMDB instead of production data. By default **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).
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). By default the test LMDB starts **empty** (no production data). **Overlay mode:** same **`POST`** with JSON body **`{"overlay":true}`** (while authenticated for the project you want to inspect—project-scoped API key, or platform Bearer + **`X-Project-Id`**). The server opens a **writable** LMDB under `_test/<uuid>/` with that project’s on-disk store as a **read-only base** (dual LMDB): reads see production + overlay deltas; **writes never touch production**. **`XCiteDBClient.createTestSession({ …, overlay: true })`** sends that body. After creation, only **`X-Test-Session`** is required on requests (overlay is stored in session metadata).
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`**.
26
40
 
27
41
  ## Glossary: Project id, display name, and groups
28
42
 
@@ -44,7 +58,7 @@ These are the most common sources of confusion for developers and AI assistants:
44
58
 
45
59
  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 set groups explicitly, use **`createAppUser`** with a `groups` array (e.g. `[XCiteDBClient.buildProjectGroup(projectId, 'editor')]`) or **`updateAppUserGroups`**. The server rejects `project:<x>:*` groups when `<x>` is not a known internal project id (avoids mistaking the display name for the tenant id).
46
60
 
47
- 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 "Test mode" below.
61
+ 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 that is scoped under `_test/<uuid>/` and destroyed after the test (empty by default, or **overlay** on read-only production when you pass **`{"overlay":true}`** / **`overlay: true`**). See "Test mode" below.
48
62
 
49
63
  8. **403 on writes with ABAC is often a JWT/group string mismatch.** Decode the app-user access token early: log **`tenant_id`**, **`groups`**, **`sub`**. The middle segment of every **`project:<x>:role`** group must equal **`tenant_id`** exactly. Document write denials may return JSON fields **`policy_id`** and **`hint`** alongside `"Forbidden"`.
50
64
 
@@ -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? |
@@ -89,11 +105,11 @@ await app.writeJsonDocument('userdata/alice/profile', { ok: true });
89
105
 
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
- 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.
108
+ 1. **Provision:** `POST /api/v1/test/sessions` with `Authorization: Bearer …` or `X-API-Key` (same as normal API access). Optional JSON body **`{"overlay":true}`** creates an **overlay** session (read-through production, writes only under `_test/<session>/`). Response JSON includes the session token (and **`"overlay": true`** when applicable).
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 writes go under the server’s `_test/<session>/` tree; overlay sessions **do not** write to production paths.
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
- 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.
112
+ 5. **SDKs:** **JS/TS:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`, optional `overlay: true`, optional `testRequireAuth`, then `destroyTestSession()`. **Python:** `async with XCiteDBClient.test_session(...)` or provision with **`POST /api/v1/test/sessions`** and JSON **`{"overlay":true}`** when you need overlay, then pass `test_session_token` / `test_require_auth` to the constructor. **C++:** `XCiteDBClient::create_test_session(options)` with optional `test_session_overlay = true`, `destroy_test_session()`, optional `test_require_auth` in options.
97
113
 
98
114
  ## JavaScript/TypeScript SDK (`@xcitedbs/client`)
99
115
 
@@ -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,8 +177,10 @@ 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)
165
- const results = await client.search({ query: 'installation guide', limit: 20 });
180
+ // Full-text search (embedded XciteFTS; optional temporal filters on the query body)
181
+ const results = await client.search({ query: 'installation guide', limit: 20, mode: 'fts' });
182
+ // Point-in-time keyword search: { query: '...', mode: 'fts', at_date: '2024-06-01' }
183
+ // Range overlap: { query: '...', mode: 'fts', date_from: '2024-01-01', date_to: '2025-01-01' }
166
184
 
167
185
  // Subscribe to real-time changes via WebSocket
168
186
  const sub = client.subscribe(
@@ -180,8 +198,9 @@ interface XCiteDBClientOptions {
180
198
  accessToken?: string; // Platform JWT (from platformLogin)
181
199
  appUserAccessToken?: string; // End-user JWT (from loginAppUser)
182
200
  context?: {
183
- branch?: string; // Branch name; '' = default/root timeline
184
- date?: string; // Point-in-time (ISO-like or internal date key)
201
+ workspace?: string; // Workspace name; '' = default/root timeline (preferred)
202
+ branch?: string; // @deprecated alias of workspace; sends X-Workspace
203
+ date?: string; // X-Date: as-of reads + write revision; any ISO/mm-dd-yyyy instant, not only "now"
185
204
  prefix?: string; // Optional identifier prefix filter
186
205
  project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
187
206
  tenant_id?: string; // Deprecated alias of project_id
@@ -200,7 +219,7 @@ interface XCiteDBClientOptions {
200
219
  - `loginAppUser(email, password)` — App end-user sign-in
201
220
  - `XCiteDBClient.buildProjectGroup(projectId, 'admin'|'editor'|'viewer')` — Static helper: canonical `project:<tenant_id>:<role>` string
202
221
  - `getTokenClaims()` — Decode current `appUserAccessToken` or `accessToken` payload (no signature verification); use for ABAC debugging
203
- - `setContext(ctx)` — Update branch/date context
222
+ - `setContext(ctx)` — Update workspace/date context
204
223
  - `setProjectId(id)` — Switch active project (platform console)
205
224
 
206
225
  **XML Documents:**
@@ -225,19 +244,20 @@ interface XCiteDBClientOptions {
225
244
  - `queryMeta(identifier, path?)` — Read metadata
226
245
  - `clearMeta(query)` — Remove metadata
227
246
 
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
247
+ **Versioning (workspaces & checkpoints):**
248
+ - `withWorkspace(name, fn, options?)` — Create workspace, run callback, checkpoint, publish back (optional `autoMerge: false`; `fromBranch` = parent workspace)
249
+ - `createWorkspace(name, fromBranch?, fromDate?)` — Create workspace
250
+ - `listWorkspaces()` — List workspaces
251
+ - `publishWorkspace(target, source, options?)` — Publish workspace changes to a target timeline
252
+ - `deleteWorkspace(name)` — Delete workspace
253
+ - `createCheckpoint(message, author?)` — Named snapshot of current state
254
+ - `listCheckpoints(options?)` — List checkpoints
255
+ - `revertToCheckpoint(checkpointId)` — Revert workspace to a prior checkpoint
256
+ - `applyCheckpoint(checkpointId, message?)` — Apply another checkpoint’s changes here
257
+ - `compare(from, to, includeContent?)` — Compare revisions
258
+ - `createBookmark(name, checkpointId, message?)` — Bookmark a checkpoint
259
+ - `listBookmarks()` / `deleteBookmark(name)` — Manage bookmarks
260
+ - **Deprecated aliases:** `withBranch`, `createBranch`, `listBranches`, `mergeBranch`, `deleteBranch`, `createCommit`, `listCommits`, `rollbackToCommit`, `cherryPick`, `diff`, `createTag`, `listTags`, `deleteTag` (same HTTP behavior)
241
261
 
242
262
  **Locks:**
243
263
  - `acquireLock(identifier, expires?)` — Acquire cooperative lock
@@ -245,7 +265,7 @@ interface XCiteDBClientOptions {
245
265
  - `findLocks(identifier)` — Query active locks
246
266
 
247
267
  **Search & Analytics:**
248
- - `search(query)` — Full-text search
268
+ - `search(query)` — Full-text search (embedded FTS). Optional **`at_date`**, **`date_from`**, **`date_to`** on the query for temporal keyword search (use **`mode: 'fts'`**). Hits may include **`valid_from`** / **`valid_to`** (7-char internal keys).
249
269
  - `reindex()` — Rebuild search index
250
270
  - `unquery(query, unqueryDoc)` — Execute Unquery DSL
251
271
 
@@ -260,7 +280,7 @@ interface XCiteDBClientOptions {
260
280
  ### Advanced: policy expressions (ABAC)
261
281
 
262
282
  - **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).
283
+ - **`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
284
  - **Operators:** `=`, `!=`, `>=`, `<=`, `>`, `<`, `in`, `contains`, `starts_with`, `ends_with`, `&`, `|`, `!`, `+` (string concat), postfix `!` (exists).
265
285
  - **Examples:** `resource.path[0] = subject.attr.tenant_code` — tenant isolation; `subject.attr.level >= 5` — numeric attribute gate.
266
286
 
@@ -269,7 +289,7 @@ interface XCiteDBClientOptions {
269
289
  - **Events:** `meta_changed`, `document_written`, `document_deleted`.
270
290
  - **`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
291
  - **`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`).
292
+ - **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
293
 
274
294
  ### Advanced: Unquery DSL (`unquery(query, unqueryDoc)`)
275
295
 
@@ -312,7 +332,7 @@ async def main():
312
332
  async with XCiteDBClient(
313
333
  "http://localhost:8080",
314
334
  api_key="your-key",
315
- context=DatabaseContext(branch="", date=""),
335
+ context=DatabaseContext(workspace="", date=""),
316
336
  ) as client:
317
337
  print(await client.health())
318
338
  ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
@@ -320,6 +340,7 @@ async def main():
320
340
  await client.write_json_document("app.settings", {"theme": "dark"})
321
341
  print(await client.read_json_document("app.settings"))
322
342
  print(await client.search(TextSearchQuery(query="guide", limit=10)))
343
+ # Temporal FTS: TextSearchQuery(query="guide", mode="fts", at_date="2024-06-01")
323
344
  pair = await client.platform_login("admin@localhost", "password")
324
345
  # await client.login_app_user("user@example.com", "secret", tenant_id="...")
325
346
 
@@ -334,7 +355,7 @@ asyncio.run(main())
334
355
  xcitedb::XCiteDBClientOptions opt;
335
356
  opt.base_url = "http://127.0.0.1:8080";
336
357
  opt.api_key = "your-key";
337
- opt.context.branch = ""; // root timeline
358
+ opt.context.workspace = ""; // root timeline (branch is deprecated alias)
338
359
 
339
360
  xcitedb::XCiteDBClient client(opt);
340
361
  auto health = client.health();
@@ -347,9 +368,9 @@ auto ids = client.query_documents(q);
347
368
 
348
369
  - **Multi-tenant**: Each project is an isolated tenant with its own data, users, keys, and policies.
349
370
  - **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`.
371
+ - **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
372
  - **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.
373
+ - **Security policies (ABAC)**: Fine-grained allow/deny rules on actions, resources, roles, and workspaces (`env.branch` in expressions).
353
374
  - **WebSocket**: Real-time notifications for document changes at `/api/v1/ws`.
354
375
 
355
376
  ## Documentation
@@ -360,10 +381,10 @@ auto ids = client.query_documents(q);
360
381
  - Documents — XML document CRUD, identifiers, hierarchy
361
382
  - JSON documents — JSON document CRUD
362
383
  - Metadata — JSON metadata on documents
363
- - Search — Full-text search
384
+ - Search — Full-text (embedded FTS; optional temporal `at_date` / `date_from` / `date_to`; hit `valid_from` / `valid_to`)
364
385
  - Unquery — Declarative query DSL
365
- - BranchesBranch management
366
- - Commits & tagsVersion snapshots
386
+ - WorkspacesIsolated editing environments
387
+ - Checkpoints & bookmarksNamed snapshots and references
367
388
  - Locks — Cooperative editing locks
368
389
  - Authentication — JWT, API keys, platform vs app users
369
390
  - 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.12",
4
4
  "description": "XCiteDB BaaS client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",