@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/README.md +22 -0
- package/dist/client.d.ts +85 -7
- package/dist/client.js +199 -52
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +101 -28
- package/llms-full.txt +140 -103
- package/llms.txt +68 -47
- package/package.json +1 -1
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
|
|
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
|
|
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.
|
|
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).
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
//
|
|
111
|
-
context: {
|
|
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
|
|
118
|
-
const
|
|
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
|
-
//
|
|
144
|
-
await client.
|
|
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
|
|
150
|
-
await client.
|
|
151
|
-
client.setContext({
|
|
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
|
|
154
|
-
await client.
|
|
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;
|
|
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
|
-
|
|
184
|
-
|
|
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
|
|
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
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
232
|
-
- `
|
|
233
|
-
- `
|
|
234
|
-
- `
|
|
235
|
-
- `
|
|
236
|
-
- `
|
|
237
|
-
- `
|
|
238
|
-
- `
|
|
239
|
-
- `
|
|
240
|
-
- `
|
|
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
|
|
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(
|
|
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.
|
|
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**:
|
|
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
|
|
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
|
|
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
|
-
-
|
|
366
|
-
-
|
|
386
|
+
- Workspaces — Isolated editing environments
|
|
387
|
+
- Checkpoints & bookmarks — Named 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
|