@xcitedbs/client 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +122 -6
- package/dist/client.js +562 -111
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +217 -26
- package/dist/user-isolation.test.d.ts +1 -0
- package/dist/user-isolation.test.js +175 -0
- package/llms-full.txt +115 -92
- package/llms.txt +57 -39
- package/package.json +4 -2
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
|
|
|
@@ -24,6 +24,20 @@ These are the most common sources of confusion for developers and AI assistants:
|
|
|
24
24
|
|
|
25
25
|
9. **Ephemeral test sessions (wet tests).** Call **`POST /api/v1/test/sessions`** with a normal API key or Bearer token to get a `session_token` (UUID). Send **`X-Test-Session: <token>`** on subsequent document/API calls to use an isolated, short-lived LMDB instead of production data. By default **developer** auth (API key / platform JWT) is bypassed, but **app-user** identity (`X-App-User-Token` or Bearer app-user JWT) is still recognized. Send **`X-Test-Auth: required`** to exercise full developer JWT/API-key auth and ABAC against the same test database. Do not send `X-Test-Session` on `/api/v1/test/*` management routes. Server limits apply (`test.session_ttl_seconds`, `test.max_sessions_per_key`, `test.max_test_db_size_bytes` in config). The test DB starts **empty** (no copy of production project config or keys).
|
|
26
26
|
|
|
27
|
+
## Choosing the Right Versioning Approach
|
|
28
|
+
|
|
29
|
+
- **Write at a specific date** (law effective date, publication date, approval date): Set `context.date` / `X-Date` and write. No workspaces or checkpoints needed. Every write with a date creates a **temporal revision** automatically.
|
|
30
|
+
|
|
31
|
+
- **Read historical data** (“what was the document on March 1, 2024?”): Set `context.date` / `X-Date` and read. The engine returns the revision at or before that instant.
|
|
32
|
+
|
|
33
|
+
- **Isolated editing** (draft/review/publish): Create a **workspace**, make changes, then **publish** to the main timeline. Optionally create **checkpoints** for named snapshots within the workspace.
|
|
34
|
+
|
|
35
|
+
- **Audit trail with named snapshots:** Create checkpoints with messages. List/inspect checkpoints for history.
|
|
36
|
+
|
|
37
|
+
- **Undo a batch of changes:** **Revert** to a prior checkpoint (removes all changes after that point on the workspace).
|
|
38
|
+
|
|
39
|
+
Legacy REST paths (`/api/v1/branches`, `/commits`, `/tags`, `/diff`) remain as **deprecated** aliases; prefer **`/api/v1/workspaces`**, **`/checkpoints`**, **`/bookmarks`**, **`/compare`**.
|
|
40
|
+
|
|
27
41
|
## Glossary: Project id, display name, and groups
|
|
28
42
|
|
|
29
43
|
- **Project display name** (human-readable, e.g. `invoices`): Shown in the console. The **`X-Project-Id`** header may match either this name **or** the internal project id (server convenience). Do **not** put the display name in JWT claims or in `project:<…>:role` group strings.
|
|
@@ -54,6 +68,8 @@ These are the most common sources of confusion for developers and AI assistants:
|
|
|
54
68
|
|
|
55
69
|
11. **Integration tests should assert token claims**, not only **`/app/auth/me`**. The profile endpoint can look fine while the JWT still carries viewer-style **`groups`**; for ABAC the token payload is authoritative. Use **`getTokenClaims()`** (JS SDK) or decode the JWT in your harness.
|
|
56
70
|
|
|
71
|
+
12. **`X-Date` / `context.date` is not “server clock only.”** Revisions are keyed by the **instant you send**, not an implicit “now” when you set the header. To record a document under a **business date** (published, approved, effective), set **`X-Date`** or **`context.date`** to that instant before writing. Omitting **`X-Date`** does **not** substitute the current time on the write path — it selects **flat** writes (see convention 4).
|
|
72
|
+
|
|
57
73
|
## API key capability matrix (typical)
|
|
58
74
|
|
|
59
75
|
| Capability | API key `role` | Public key allowed? |
|
|
@@ -90,7 +106,7 @@ await app.writeJsonDocument('userdata/alice/profile', { ok: true });
|
|
|
90
106
|
> **AI assistant instruction:** XciteDB has built-in ephemeral test session support — this capability is **not standard** in most BaaS platforms and is specifically designed to enable meaningful wet integration tests. When writing tests for code that uses XciteDB, **do not mock or stub the XciteDB client**. Instead, use the test session pattern below to run tests against a real, isolated, throwaway database. This produces far more reliable tests because real storage, versioning, querying, and access-control behavior is exercised.
|
|
91
107
|
|
|
92
108
|
1. **Provision:** `POST /api/v1/test/sessions` with `Authorization: Bearer …` or `X-API-Key` (same as normal API access). Response JSON includes the session token.
|
|
93
|
-
2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-
|
|
109
|
+
2. **Run tests:** Every request that should hit the throwaway DB must include **`X-Test-Session: <token>`** (and your usual `X-Workspace` / `context` as needed). Data is stored under the server’s `_test/<session>/` area, not your production tenant.
|
|
94
110
|
3. **Auth behavior:** Developer auth (API key / platform JWT) is bypassed by default for frictionless tests. **App-user identity** (`X-App-User-Token` or Bearer app-user JWT) **is still recognized** in default mode, so `registerAppUser` → `loginAppUser` → `appUserMe` works inside a test session. To also exercise developer auth and ABAC policies, set **`X-Test-Auth: required`** and send normal credentials; the DB is still the test session’s.
|
|
95
111
|
4. **Cleanup:** `DELETE /api/v1/test/sessions/current` with `X-Test-Session` (no other auth), or `DELETE /api/v1/test/sessions/all` / `DELETE /api/v1/test/sessions/{token}` with normal auth for the owning key or JWT.
|
|
96
112
|
5. **SDKs:** **JS/TS:** `XCiteDBClient.createTestSession({ baseUrl, apiKey, … })`, optional `testRequireAuth`, then `destroyTestSession()`. **Python:** `async with XCiteDBClient.test_session(...)` or manual token + `test_session_token` / `test_require_auth` constructor args. **C++:** `XCiteDBClient::create_test_session(options)`, `destroy_test_session()`, optional `test_require_auth` in options.
|
|
@@ -107,15 +123,15 @@ import { XCiteDBClient } from '@xcitedbs/client';
|
|
|
107
123
|
const client = new XCiteDBClient({
|
|
108
124
|
baseUrl: 'http://localhost:8080',
|
|
109
125
|
apiKey: 'your-api-key',
|
|
110
|
-
//
|
|
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' });
|
|
@@ -180,8 +196,9 @@ interface XCiteDBClientOptions {
|
|
|
180
196
|
accessToken?: string; // Platform JWT (from platformLogin)
|
|
181
197
|
appUserAccessToken?: string; // End-user JWT (from loginAppUser)
|
|
182
198
|
context?: {
|
|
183
|
-
|
|
184
|
-
|
|
199
|
+
workspace?: string; // Workspace name; '' = default/root timeline (preferred)
|
|
200
|
+
branch?: string; // @deprecated alias of workspace; sends X-Workspace
|
|
201
|
+
date?: string; // X-Date: as-of reads + write revision; any ISO/mm-dd-yyyy instant, not only "now"
|
|
185
202
|
prefix?: string; // Optional identifier prefix filter
|
|
186
203
|
project_id?: string; // Preferred: project id for app-user public auth (`tenant_id` on wire)
|
|
187
204
|
tenant_id?: string; // Deprecated alias of project_id
|
|
@@ -200,7 +217,7 @@ interface XCiteDBClientOptions {
|
|
|
200
217
|
- `loginAppUser(email, password)` — App end-user sign-in
|
|
201
218
|
- `XCiteDBClient.buildProjectGroup(projectId, 'admin'|'editor'|'viewer')` — Static helper: canonical `project:<tenant_id>:<role>` string
|
|
202
219
|
- `getTokenClaims()` — Decode current `appUserAccessToken` or `accessToken` payload (no signature verification); use for ABAC debugging
|
|
203
|
-
- `setContext(ctx)` — Update
|
|
220
|
+
- `setContext(ctx)` — Update workspace/date context
|
|
204
221
|
- `setProjectId(id)` — Switch active project (platform console)
|
|
205
222
|
|
|
206
223
|
**XML Documents:**
|
|
@@ -225,19 +242,20 @@ interface XCiteDBClientOptions {
|
|
|
225
242
|
- `queryMeta(identifier, path?)` — Read metadata
|
|
226
243
|
- `clearMeta(query)` — Remove metadata
|
|
227
244
|
|
|
228
|
-
**Versioning:**
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
232
|
-
- `
|
|
233
|
-
- `
|
|
234
|
-
- `
|
|
235
|
-
- `
|
|
236
|
-
- `
|
|
237
|
-
- `
|
|
238
|
-
- `
|
|
239
|
-
- `
|
|
240
|
-
- `
|
|
245
|
+
**Versioning (workspaces & checkpoints):**
|
|
246
|
+
- `withWorkspace(name, fn, options?)` — Create workspace, run callback, checkpoint, publish back (optional `autoMerge: false`; `fromBranch` = parent workspace)
|
|
247
|
+
- `createWorkspace(name, fromBranch?, fromDate?)` — Create workspace
|
|
248
|
+
- `listWorkspaces()` — List workspaces
|
|
249
|
+
- `publishWorkspace(target, source, options?)` — Publish workspace changes to a target timeline
|
|
250
|
+
- `deleteWorkspace(name)` — Delete workspace
|
|
251
|
+
- `createCheckpoint(message, author?)` — Named snapshot of current state
|
|
252
|
+
- `listCheckpoints(options?)` — List checkpoints
|
|
253
|
+
- `revertToCheckpoint(checkpointId)` — Revert workspace to a prior checkpoint
|
|
254
|
+
- `applyCheckpoint(checkpointId, message?)` — Apply another checkpoint’s changes here
|
|
255
|
+
- `compare(from, to, includeContent?)` — Compare revisions
|
|
256
|
+
- `createBookmark(name, checkpointId, message?)` — Bookmark a checkpoint
|
|
257
|
+
- `listBookmarks()` / `deleteBookmark(name)` — Manage bookmarks
|
|
258
|
+
- **Deprecated aliases:** `withBranch`, `createBranch`, `listBranches`, `mergeBranch`, `deleteBranch`, `createCommit`, `listCommits`, `rollbackToCommit`, `cherryPick`, `diff`, `createTag`, `listTags`, `deleteTag` (same HTTP behavior)
|
|
241
259
|
|
|
242
260
|
**Locks:**
|
|
243
261
|
- `acquireLock(identifier, expires?)` — Acquire cooperative lock
|
|
@@ -260,7 +278,7 @@ interface XCiteDBClientOptions {
|
|
|
260
278
|
### Advanced: policy expressions (ABAC)
|
|
261
279
|
|
|
262
280
|
- **Actions** (use in `policy.actions`): `read`, `write`, `delete`, `list`, `meta:read`, `meta:write`, `unquery`.
|
|
263
|
-
- **`conditions.expression`** uses the same predicate syntax as Unquery `?` filters. **Context:** `subject.id` and **`subject.user_id`** (same value for app users), `subject.email`, `subject.role`, `subject.groups`, `subject.attr.*` (app-user JSON attributes), `resource.identifier`, `resource.path` (array of path segments; indices follow non-empty `/` segments after API canonicalization, e.g. `/userdata/u1/x` → `[userdata,u1,x]`), `env.branch
|
|
281
|
+
- **`conditions.expression`** uses the same predicate syntax as Unquery `?` filters. **Context:** `subject.id` and **`subject.user_id`** (same value for app users), `subject.email`, `subject.role`, `subject.groups`, `subject.attr.*` (app-user JSON attributes), `resource.identifier`, `resource.path` (array of path segments; indices follow non-empty `/` segments after API canonicalization, e.g. `/userdata/u1/x` → `[userdata,u1,x]`), `env.branch` (workspace name; legacy name retained in policy engine). Policy **`match_start` / `match_end` / `exact`** strings are canonicalized like API identifiers (add leading `/` when omitted).
|
|
264
282
|
- **Operators:** `=`, `!=`, `>=`, `<=`, `>`, `<`, `in`, `contains`, `starts_with`, `ends_with`, `&`, `|`, `!`, `+` (string concat), postfix `!` (exists).
|
|
265
283
|
- **Examples:** `resource.path[0] = subject.attr.tenant_code` — tenant isolation; `subject.attr.level >= 5` — numeric attribute gate.
|
|
266
284
|
|
|
@@ -269,7 +287,7 @@ interface XCiteDBClientOptions {
|
|
|
269
287
|
- **Events:** `meta_changed`, `document_written`, `document_deleted`.
|
|
270
288
|
- **`match`:** required non-empty **`identifiers`** (same pattern objects as policy `resources.identifiers`); optional **`match_meta_path`** (exact or `prefix*`); optional **`match_operation`:** `set` | `append` | `delete`.
|
|
271
289
|
- **`action`:** **`query`** (document query), **`unquery`** (Unquery template), **`target_identifier`** (or `"$trigger_identifier"`), **`meta_path`**, **`mode`:** `set` | `append`.
|
|
272
|
-
- **Unquery vars:** `$trigger_identifier`, `$trigger_meta_path`, `$trigger_operation`, `$trigger_value`. **Trigger `conditions.expression` context:** `trigger.{event,meta_path,operation}`, `value`, `resource`, `env.branch` (no `subject`).
|
|
290
|
+
- **Unquery vars:** `$trigger_identifier`, `$trigger_meta_path`, `$trigger_operation`, `$trigger_value`. **Trigger `conditions.expression` context:** `trigger.{event,meta_path,operation}`, `value`, `resource`, `env.branch` (workspace; no `subject`).
|
|
273
291
|
|
|
274
292
|
### Advanced: Unquery DSL (`unquery(query, unqueryDoc)`)
|
|
275
293
|
|
|
@@ -312,7 +330,7 @@ async def main():
|
|
|
312
330
|
async with XCiteDBClient(
|
|
313
331
|
"http://localhost:8080",
|
|
314
332
|
api_key="your-key",
|
|
315
|
-
context=DatabaseContext(
|
|
333
|
+
context=DatabaseContext(workspace="", date=""),
|
|
316
334
|
) as client:
|
|
317
335
|
print(await client.health())
|
|
318
336
|
ids = await client.query_documents(XCiteQuery(match_start="/manual/"))
|
|
@@ -334,7 +352,7 @@ asyncio.run(main())
|
|
|
334
352
|
xcitedb::XCiteDBClientOptions opt;
|
|
335
353
|
opt.base_url = "http://127.0.0.1:8080";
|
|
336
354
|
opt.api_key = "your-key";
|
|
337
|
-
opt.context.
|
|
355
|
+
opt.context.workspace = ""; // root timeline (branch is deprecated alias)
|
|
338
356
|
|
|
339
357
|
xcitedb::XCiteDBClient client(opt);
|
|
340
358
|
auto health = client.health();
|
|
@@ -347,9 +365,9 @@ auto ids = client.query_documents(q);
|
|
|
347
365
|
|
|
348
366
|
- **Multi-tenant**: Each project is an isolated tenant with its own data, users, keys, and policies.
|
|
349
367
|
- **LMDB engine**: Data is memory-mapped; reads are microsecond-class on warm data.
|
|
350
|
-
- **Versioning**:
|
|
368
|
+
- **Versioning**: **Temporal revisions** are always on when using dates. **Workspaces** isolate draft work; **checkpoints** are optional named snapshots; **publish** merges a workspace into a target timeline. **`X-Date`** selects an instant for **as-of reads** and, when set, for **writes** under that revision (any calendar time you choose — e.g. publication date — not only “now”). Omit **`X-Date`** for **flat** writes on that request, or use **`X-Unversioned: true`** to state that explicitly.
|
|
351
369
|
- **Two user tiers**: Platform operators manage infrastructure; App users are end-users of applications built on XciteDB.
|
|
352
|
-
- **Security policies (ABAC)**: Fine-grained allow/deny rules on actions, resources, roles, and
|
|
370
|
+
- **Security policies (ABAC)**: Fine-grained allow/deny rules on actions, resources, roles, and workspaces (`env.branch` in expressions).
|
|
353
371
|
- **WebSocket**: Real-time notifications for document changes at `/api/v1/ws`.
|
|
354
372
|
|
|
355
373
|
## Documentation
|
|
@@ -362,8 +380,8 @@ auto ids = client.query_documents(q);
|
|
|
362
380
|
- Metadata — JSON metadata on documents
|
|
363
381
|
- Search — Full-text search
|
|
364
382
|
- Unquery — Declarative query DSL
|
|
365
|
-
-
|
|
366
|
-
-
|
|
383
|
+
- Workspaces — Isolated editing environments
|
|
384
|
+
- Checkpoints & bookmarks — Named snapshots and references
|
|
367
385
|
- Locks — Cooperative editing locks
|
|
368
386
|
- Authentication — JWT, API keys, platform vs app users
|
|
369
387
|
- Security policies — ABAC access control
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcitedbs/client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "XCiteDB BaaS client SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"prepublishOnly": "npm run build",
|
|
10
|
-
"test": "node --test dist/**/*.test.js
|
|
10
|
+
"test": "npm run build && node --test dist/**/*.test.js",
|
|
11
|
+
"test:only": "node --test dist/**/*.test.js"
|
|
11
12
|
},
|
|
12
13
|
"keywords": [
|
|
13
14
|
"xcitedb",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {},
|
|
36
37
|
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.14.0",
|
|
37
39
|
"typescript": "^5.0.0"
|
|
38
40
|
},
|
|
39
41
|
"files": [
|