@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/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { XCiteDBClient } from './client';
|
|
2
2
|
export { WebSocketSubscription } from './websocket';
|
|
3
|
-
export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, DatabaseContext, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
|
|
3
|
+
export type { AccessCheckResult, ApiKeyInfo, AppAuthConfig, AppEmailConfig, AppEmailSmtpConfig, AppEmailTemplateEntry, AppEmailTemplates, AppEmailWebhookConfig, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BookmarkRecord, BranchInfo, BranchListItem, CheckpointRecord, CommitRecord, CompareEntry, CompareRef, CompareResult, DatabaseContext, DiffEntry, DiffRef, DiffResult, Flags, JsonDocumentData, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TokenPair, UserInfo, UserIsolationConfig, UserIsolationOptions, WorkspaceInfo, WriteDocumentOptions, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
|
|
4
4
|
export { XCiteDBError } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -41,6 +41,12 @@ export interface TextSearchQuery {
|
|
|
41
41
|
branch?: string;
|
|
42
42
|
offset?: number;
|
|
43
43
|
limit?: number;
|
|
44
|
+
/** Default `fts`. `semantic` / `hybrid` require vector search (and hybrid requires FTS). */
|
|
45
|
+
mode?: 'fts' | 'semantic' | 'hybrid';
|
|
46
|
+
/** Minimum cosine similarity for semantic / hybrid vector leg (default 0.3). */
|
|
47
|
+
min_score?: number;
|
|
48
|
+
/** Hybrid only: weight of semantic vs FTS in weighted RRF (0–1, default 0.5). */
|
|
49
|
+
semantic_weight?: number;
|
|
44
50
|
}
|
|
45
51
|
export interface TextSearchHit {
|
|
46
52
|
identifier: string;
|
|
@@ -51,12 +57,138 @@ export interface TextSearchHit {
|
|
|
51
57
|
branch: string;
|
|
52
58
|
snippet: string;
|
|
53
59
|
score: number;
|
|
60
|
+
/** Present for semantic / hybrid search. */
|
|
61
|
+
source?: 'fts' | 'semantic' | 'both';
|
|
54
62
|
}
|
|
55
63
|
export interface TextSearchResult {
|
|
56
64
|
hits: TextSearchHit[];
|
|
57
65
|
total: number;
|
|
58
66
|
query: string;
|
|
59
67
|
}
|
|
68
|
+
/** Progress object when the server is indexing (FTS or vector). */
|
|
69
|
+
export interface SearchIndexingProgress {
|
|
70
|
+
done: number;
|
|
71
|
+
total: number;
|
|
72
|
+
}
|
|
73
|
+
/** Last finished vector re-embed job (`vector_index_job` in project settings). */
|
|
74
|
+
export interface VectorIndexingLastJob {
|
|
75
|
+
ok?: boolean;
|
|
76
|
+
finished_at_ms?: number;
|
|
77
|
+
message?: string;
|
|
78
|
+
detail?: string;
|
|
79
|
+
phase?: string;
|
|
80
|
+
rows_indexed?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Vector index workload + indicative cost.
|
|
84
|
+
* Blocking scan: `GET /api/v1/project/settings/search/vector-index-estimate` (no `session`).
|
|
85
|
+
* Incremental UI: `POST` same path, then `GET` / `DELETE` with `?session=`.
|
|
86
|
+
*/
|
|
87
|
+
export interface VectorIndexEstimate {
|
|
88
|
+
document_count?: number;
|
|
89
|
+
chunk_count?: number;
|
|
90
|
+
approx_input_tokens?: number;
|
|
91
|
+
approx_input_tokens_xml?: number;
|
|
92
|
+
approx_input_tokens_json?: number;
|
|
93
|
+
/** Serialized XML on disk (only docs that produced ≥1 embed chunk). */
|
|
94
|
+
xml_source_serialized_bytes_total?: number;
|
|
95
|
+
/** UTF-8 bytes of XML embed text (text nodes only; same as embedding input). */
|
|
96
|
+
xml_embed_text_bytes_total?: number;
|
|
97
|
+
/** Raw JSON document bytes (only docs that produced ≥1 embed chunk). */
|
|
98
|
+
json_source_bytes_total?: number;
|
|
99
|
+
/** UTF-8 bytes of JSON scalar chunks sent to the embedder. */
|
|
100
|
+
json_embed_text_bytes_total?: number;
|
|
101
|
+
token_estimate_note?: string;
|
|
102
|
+
embedding_provider?: string;
|
|
103
|
+
embedding_model?: string;
|
|
104
|
+
embedding_configured?: boolean;
|
|
105
|
+
pricing_info_url?: string;
|
|
106
|
+
/** Null when unknown (e.g. openai_compatible). */
|
|
107
|
+
usd_per_million_tokens_indicative?: number | null;
|
|
108
|
+
estimated_cost_usd_indicative?: number | null;
|
|
109
|
+
cost_disclaimer?: string;
|
|
110
|
+
/** Async session responses only (`POST` / `GET ?session=`). */
|
|
111
|
+
session_id?: string;
|
|
112
|
+
branches_done?: number;
|
|
113
|
+
branches_total?: number;
|
|
114
|
+
estimate_complete?: boolean;
|
|
115
|
+
estimate_cancelled?: boolean;
|
|
116
|
+
estimate_error?: string;
|
|
117
|
+
}
|
|
118
|
+
/** `GET /api/v1/project/settings/search` (and returned after `PUT`). */
|
|
119
|
+
export interface ProjectSearchSettings {
|
|
120
|
+
search_enabled?: boolean;
|
|
121
|
+
search_language?: string;
|
|
122
|
+
indexing_status?: 'indexing' | 'idle';
|
|
123
|
+
indexing_progress?: SearchIndexingProgress;
|
|
124
|
+
vector_search_enabled?: boolean;
|
|
125
|
+
embedding_provider?: string;
|
|
126
|
+
embedding_model?: string;
|
|
127
|
+
embedding_base_url?: string;
|
|
128
|
+
llm_provider?: string;
|
|
129
|
+
llm_model?: string;
|
|
130
|
+
llm_base_url?: string;
|
|
131
|
+
embedding_configured?: boolean;
|
|
132
|
+
llm_configured?: boolean;
|
|
133
|
+
vector_indexing_status?: 'indexing' | 'scheduled' | 'idle';
|
|
134
|
+
vector_indexing_progress?: SearchIndexingProgress | null;
|
|
135
|
+
vector_indexing_last_job?: VectorIndexingLastJob | null;
|
|
136
|
+
[key: string]: unknown;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* `PUT /api/v1/project/settings/search` body (all keys optional).
|
|
140
|
+
* `embedding_api_key` / `llm_api_key` are write-only: send a new secret or empty string to clear.
|
|
141
|
+
* While `vector_search_enabled` is true, changing embedding provider/model/base URL triggers a full vector re-embed on the server.
|
|
142
|
+
*/
|
|
143
|
+
export interface ProjectSearchSettingsUpdate {
|
|
144
|
+
search_enabled?: boolean;
|
|
145
|
+
search_language?: string;
|
|
146
|
+
vector_search_enabled?: boolean;
|
|
147
|
+
embedding_provider?: string;
|
|
148
|
+
embedding_model?: string;
|
|
149
|
+
embedding_base_url?: string | null;
|
|
150
|
+
embedding_api_key?: string;
|
|
151
|
+
llm_provider?: string;
|
|
152
|
+
llm_model?: string;
|
|
153
|
+
llm_base_url?: string | null;
|
|
154
|
+
llm_api_key?: string;
|
|
155
|
+
}
|
|
156
|
+
/** `POST /api/v1/rag/query` (non-streaming: set `stream: false` or omit). */
|
|
157
|
+
export interface RagQueryOptions {
|
|
158
|
+
question: string;
|
|
159
|
+
branch?: string;
|
|
160
|
+
max_context_docs?: number;
|
|
161
|
+
doc_types?: ('xml' | 'json')[];
|
|
162
|
+
/** Default `false` for JSON response with `answer` and `sources`. */
|
|
163
|
+
stream?: boolean;
|
|
164
|
+
/** Default `auto`: hybrid if FTS+vector enabled, else best available mode. */
|
|
165
|
+
search_mode?: 'auto' | 'hybrid' | 'semantic' | 'fts';
|
|
166
|
+
/** Minimum cosine similarity for semantic / hybrid vector leg (default 0.35). */
|
|
167
|
+
min_score?: number;
|
|
168
|
+
/** Hybrid retrieval only: semantic vs FTS weight in weighted RRF (0–1, default 0.5). */
|
|
169
|
+
semantic_weight?: number;
|
|
170
|
+
}
|
|
171
|
+
export interface RagQueryResult {
|
|
172
|
+
answer: string;
|
|
173
|
+
sources: unknown;
|
|
174
|
+
}
|
|
175
|
+
/** One SSE JSON payload from streaming RAG (`stream: true`). */
|
|
176
|
+
export type RagStreamEvent = {
|
|
177
|
+
text: string;
|
|
178
|
+
done: false;
|
|
179
|
+
sources?: undefined;
|
|
180
|
+
error?: undefined;
|
|
181
|
+
} | {
|
|
182
|
+
text?: string;
|
|
183
|
+
done: true;
|
|
184
|
+
sources?: unknown;
|
|
185
|
+
error?: undefined;
|
|
186
|
+
} | {
|
|
187
|
+
error: string;
|
|
188
|
+
done: true;
|
|
189
|
+
text?: undefined;
|
|
190
|
+
sources?: undefined;
|
|
191
|
+
};
|
|
60
192
|
/** Response from `GET /api/v1/documents/identifiers` */
|
|
61
193
|
export interface ListIdentifiersResult {
|
|
62
194
|
identifiers: string[];
|
|
@@ -175,6 +307,12 @@ export interface ApiKeyInfo {
|
|
|
175
307
|
}
|
|
176
308
|
export type Flags = 'None' | 'FirstMatch' | 'IncludeChildren' | 'NoChildren' | 'KeepIndexNodes' | 'PrevIfDeleted' | string;
|
|
177
309
|
export interface DatabaseContext {
|
|
310
|
+
/**
|
|
311
|
+
* Workspace name (isolated editing environment). Empty / unset = main timeline.
|
|
312
|
+
* Sent as `X-Workspace` (preferred); see also {@link DatabaseContext.branch}.
|
|
313
|
+
*/
|
|
314
|
+
workspace?: string;
|
|
315
|
+
/** @deprecated Prefer {@link DatabaseContext.workspace}. Sent as `X-Branch` when `workspace` is unset. */
|
|
178
316
|
branch?: string;
|
|
179
317
|
date?: string;
|
|
180
318
|
prefix?: string;
|
|
@@ -206,6 +344,24 @@ export interface RealtimeEvent {
|
|
|
206
344
|
timestamp?: number;
|
|
207
345
|
tenant_id?: string;
|
|
208
346
|
}
|
|
347
|
+
/** When enabled, prefixes document/meta paths with the app user's namespace (e.g. `/users/{userId}/…`) for convenience; ABAC still enforces access on the server. */
|
|
348
|
+
export interface UserIsolationOptions {
|
|
349
|
+
enabled: boolean;
|
|
350
|
+
/** Template with `{userId}` or server-style `${user.id}` replaced from JWT or login response; default `/users/{userId}`. */
|
|
351
|
+
namespace?: string;
|
|
352
|
+
/** Paths left unchanged by prefixing (optional; {@link XCiteDBClient.enableUserIsolation} fills from server). */
|
|
353
|
+
shared_read_paths?: string[];
|
|
354
|
+
shared_write_paths?: string[];
|
|
355
|
+
}
|
|
356
|
+
/** Effective user isolation settings from `GET /api/v1/security/user-isolation`. */
|
|
357
|
+
export interface UserIsolationConfig {
|
|
358
|
+
enabled: boolean;
|
|
359
|
+
namespace_pattern: string;
|
|
360
|
+
shared_read_paths: string[];
|
|
361
|
+
shared_write_paths: string[];
|
|
362
|
+
shared_write_groups: string[];
|
|
363
|
+
generated_policies?: string[];
|
|
364
|
+
}
|
|
209
365
|
export interface XCiteDBClientOptions {
|
|
210
366
|
baseUrl: string;
|
|
211
367
|
apiKey?: string;
|
|
@@ -234,6 +390,8 @@ export interface XCiteDBClientOptions {
|
|
|
234
390
|
testSessionToken?: string;
|
|
235
391
|
/** When true with `testSessionToken`, sends `X-Test-Auth: required` so real credentials are validated. */
|
|
236
392
|
testRequireAuth?: boolean;
|
|
393
|
+
/** Auto-prefix identifiers for app-user sessions (see {@link UserIsolationOptions}). */
|
|
394
|
+
userIsolation?: UserIsolationOptions;
|
|
237
395
|
}
|
|
238
396
|
/** Options for {@link XCiteDBClient.createTestSession} (provisions via API key or Bearer). */
|
|
239
397
|
export interface CreateTestSessionOptions {
|
|
@@ -250,6 +408,7 @@ export interface CreateTestSessionOptions {
|
|
|
250
408
|
onSessionTokensUpdated?: (pair: TokenPair) => void;
|
|
251
409
|
onAppUserTokensUpdated?: (pair: AppUserTokenPair) => void;
|
|
252
410
|
onSessionInvalid?: () => void;
|
|
411
|
+
userIsolation?: UserIsolationOptions;
|
|
253
412
|
}
|
|
254
413
|
/** Application user (tenant-scoped), distinct from developer users. */
|
|
255
414
|
export interface AppUser {
|
|
@@ -443,83 +602,115 @@ export interface StoredTriggerResponse {
|
|
|
443
602
|
trigger_id: string;
|
|
444
603
|
trigger: TriggerDefinition;
|
|
445
604
|
}
|
|
446
|
-
/**
|
|
447
|
-
export interface
|
|
605
|
+
/** Named snapshot on a workspace (checkpoint). */
|
|
606
|
+
export interface CheckpointRecord {
|
|
448
607
|
id: string;
|
|
608
|
+
/** Same as `id` when returned by newer APIs. */
|
|
609
|
+
checkpoint_id?: string;
|
|
449
610
|
branch: string;
|
|
450
|
-
/**
|
|
451
|
-
date_key: string;
|
|
452
|
-
date_human?: string;
|
|
453
|
-
/** Alias for human-readable date (same as date_human when present). */
|
|
611
|
+
/** Human-readable revision date/time. */
|
|
454
612
|
date?: string;
|
|
613
|
+
date_human?: string;
|
|
614
|
+
/** @deprecated Internal key; omitted from newer API responses. */
|
|
615
|
+
date_key?: string;
|
|
455
616
|
message: string;
|
|
456
617
|
author: string;
|
|
457
618
|
parent_id: string | null;
|
|
458
619
|
merge_source?: {
|
|
459
620
|
branch: string;
|
|
460
|
-
|
|
621
|
+
checkpoint_id?: string;
|
|
622
|
+
commit_id?: string;
|
|
461
623
|
};
|
|
462
624
|
cherry_picked_from?: string;
|
|
463
625
|
affected_identifiers: string[];
|
|
464
626
|
status: 'active' | 'rolled_back';
|
|
465
627
|
created_at: number;
|
|
466
628
|
}
|
|
467
|
-
/**
|
|
468
|
-
export
|
|
629
|
+
/** @deprecated Use {@link CheckpointRecord}. */
|
|
630
|
+
export type CommitRecord = CheckpointRecord;
|
|
631
|
+
/** Workspace row from `GET /api/v1/workspaces` (legacy: `/api/v1/branches`). */
|
|
632
|
+
export interface WorkspaceInfo {
|
|
469
633
|
name: string;
|
|
470
634
|
from_branch: string;
|
|
471
635
|
from_date: string;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
tip_commit?:
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
export
|
|
636
|
+
tip_checkpoint?: CheckpointRecord;
|
|
637
|
+
/** @deprecated Alias of {@link WorkspaceInfo.tip_checkpoint}. */
|
|
638
|
+
tip_commit?: CheckpointRecord;
|
|
639
|
+
}
|
|
640
|
+
/** @deprecated Use {@link WorkspaceInfo}. */
|
|
641
|
+
export type BranchInfo = WorkspaceInfo;
|
|
642
|
+
/** @deprecated Use {@link WorkspaceInfo}. */
|
|
643
|
+
export type BranchListItem = WorkspaceInfo;
|
|
644
|
+
export interface BookmarkRecord {
|
|
478
645
|
name: string;
|
|
479
|
-
|
|
646
|
+
checkpoint_id: string;
|
|
647
|
+
/** @deprecated Same as `checkpoint_id`. */
|
|
648
|
+
commit_id?: string;
|
|
480
649
|
branch: string;
|
|
481
|
-
|
|
650
|
+
date?: string;
|
|
651
|
+
/** @deprecated */
|
|
652
|
+
date_key?: string;
|
|
482
653
|
message?: string;
|
|
483
654
|
author?: string;
|
|
484
655
|
created_at: number;
|
|
485
656
|
}
|
|
486
|
-
|
|
657
|
+
/** @deprecated Use {@link BookmarkRecord}. */
|
|
658
|
+
export type TagRecord = BookmarkRecord;
|
|
659
|
+
export interface CompareEntry {
|
|
487
660
|
identifier: string;
|
|
488
661
|
action: 'added' | 'modified' | 'deleted';
|
|
489
662
|
from_content?: string;
|
|
490
663
|
to_content?: string;
|
|
491
664
|
}
|
|
492
|
-
|
|
665
|
+
/** @deprecated Use {@link CompareEntry}. */
|
|
666
|
+
export type DiffEntry = CompareEntry;
|
|
667
|
+
export interface CompareRef {
|
|
493
668
|
branch?: string;
|
|
494
|
-
/**
|
|
669
|
+
/** Human-readable date (preferred). */
|
|
670
|
+
date?: string;
|
|
671
|
+
checkpoint_id?: string;
|
|
672
|
+
/** @deprecated Undocumented fallback. */
|
|
495
673
|
date_key?: string;
|
|
674
|
+
/** @deprecated Use {@link CompareRef.checkpoint_id}. */
|
|
496
675
|
commit_id?: string;
|
|
497
676
|
}
|
|
498
|
-
|
|
499
|
-
|
|
677
|
+
/** @deprecated Use {@link CompareRef}. */
|
|
678
|
+
export type DiffRef = CompareRef;
|
|
679
|
+
export interface CompareResult {
|
|
680
|
+
changes: CompareEntry[];
|
|
500
681
|
from: {
|
|
501
682
|
branch?: string;
|
|
683
|
+
date?: string;
|
|
502
684
|
date_key?: string;
|
|
503
685
|
};
|
|
504
686
|
to: {
|
|
505
687
|
branch?: string;
|
|
688
|
+
date?: string;
|
|
506
689
|
date_key?: string;
|
|
507
690
|
};
|
|
508
691
|
total_changes: number;
|
|
509
692
|
}
|
|
510
|
-
|
|
693
|
+
/** @deprecated Use {@link CompareResult}. */
|
|
694
|
+
export type DiffResult = CompareResult;
|
|
695
|
+
export interface PublishConflict {
|
|
511
696
|
identifier: string;
|
|
512
697
|
source_action: string;
|
|
513
698
|
target_action: string;
|
|
514
699
|
}
|
|
515
|
-
|
|
700
|
+
/** @deprecated Use {@link PublishConflict}. */
|
|
701
|
+
export type MergeConflict = PublishConflict;
|
|
702
|
+
export interface PublishResult {
|
|
516
703
|
status: 'completed' | 'conflicts';
|
|
517
|
-
|
|
704
|
+
checkpoint?: CheckpointRecord;
|
|
705
|
+
/** @deprecated Alias of {@link PublishResult.checkpoint}. */
|
|
706
|
+
commit?: CheckpointRecord;
|
|
518
707
|
merged_identifiers?: string[];
|
|
519
|
-
conflicts?:
|
|
708
|
+
conflicts?: PublishConflict[];
|
|
520
709
|
auto_mergeable?: string[];
|
|
521
710
|
message?: string;
|
|
522
711
|
}
|
|
712
|
+
/** @deprecated Use {@link PublishResult}. */
|
|
713
|
+
export type MergeResult = PublishResult;
|
|
523
714
|
export declare class XCiteDBError extends Error {
|
|
524
715
|
readonly status: number;
|
|
525
716
|
readonly body?: unknown | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/**
|
|
7
|
+
* Wet tests: set XCITEDB_BASE_URL, XCITEDB_ADMIN_TOKEN, XCITEDB_TENANT_ID (platform + project).
|
|
8
|
+
* Run: npm test (builds then runs node:test).
|
|
9
|
+
*/
|
|
10
|
+
const node_test_1 = require("node:test");
|
|
11
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
12
|
+
const node_crypto_1 = require("node:crypto");
|
|
13
|
+
const client_js_1 = require("./client.js");
|
|
14
|
+
function wetEnv() {
|
|
15
|
+
const baseUrl = process.env.XCITEDB_BASE_URL?.trim();
|
|
16
|
+
const accessToken = process.env.XCITEDB_ADMIN_TOKEN?.trim();
|
|
17
|
+
const tenantId = process.env.XCITEDB_TENANT_ID?.trim();
|
|
18
|
+
if (!baseUrl || !accessToken || !tenantId)
|
|
19
|
+
return null;
|
|
20
|
+
return { baseUrl, accessToken, tenantId };
|
|
21
|
+
}
|
|
22
|
+
function adminClient(e) {
|
|
23
|
+
return new client_js_1.XCiteDBClient({
|
|
24
|
+
baseUrl: e.baseUrl,
|
|
25
|
+
accessToken: e.accessToken,
|
|
26
|
+
platformConsole: true,
|
|
27
|
+
projectId: e.tenantId,
|
|
28
|
+
context: { branch: 'main', project_id: e.tenantId },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function openTestSession(e) {
|
|
32
|
+
const url = `${e.baseUrl.replace(/\/+$/, '')}/api/v1/test/sessions`;
|
|
33
|
+
const r = await fetch(url, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
Authorization: `Bearer ${e.accessToken}`,
|
|
38
|
+
'X-Project-Id': e.tenantId,
|
|
39
|
+
'X-Branch': 'main',
|
|
40
|
+
},
|
|
41
|
+
body: '{}',
|
|
42
|
+
});
|
|
43
|
+
const data = (await r.json());
|
|
44
|
+
if (!r.ok || !data.session_token) {
|
|
45
|
+
throw new Error(`test session failed ${r.status}: ${JSON.stringify(data)}`);
|
|
46
|
+
}
|
|
47
|
+
const client = new client_js_1.XCiteDBClient({
|
|
48
|
+
baseUrl: e.baseUrl,
|
|
49
|
+
accessToken: e.accessToken,
|
|
50
|
+
platformConsole: true,
|
|
51
|
+
projectId: e.tenantId,
|
|
52
|
+
context: { branch: 'main', project_id: e.tenantId },
|
|
53
|
+
testSessionToken: data.session_token,
|
|
54
|
+
testRequireAuth: true,
|
|
55
|
+
});
|
|
56
|
+
return { client, token: data.session_token };
|
|
57
|
+
}
|
|
58
|
+
const w = wetEnv();
|
|
59
|
+
const wd = w ? node_test_1.describe : node_test_1.describe.skip;
|
|
60
|
+
wd('user isolation (wet)', () => {
|
|
61
|
+
(0, node_test_1.it)('setUserIsolationConfig enables isolation (round-trip)', async () => {
|
|
62
|
+
const e = wetEnv();
|
|
63
|
+
if (!e)
|
|
64
|
+
throw new Error('missing env');
|
|
65
|
+
const c = adminClient(e);
|
|
66
|
+
try {
|
|
67
|
+
const out = await c.setUserIsolationConfig({
|
|
68
|
+
enabled: true,
|
|
69
|
+
namespace_pattern: '/users/${user.id}',
|
|
70
|
+
});
|
|
71
|
+
strict_1.default.equal(out.enabled, true);
|
|
72
|
+
const again = await c.getUserIsolationConfig();
|
|
73
|
+
strict_1.default.equal(again.enabled, true);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await c.disableUserIsolation().catch(() => { });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
(0, node_test_1.it)('getUserIsolationConfig returns disabled in fresh test session', async () => {
|
|
80
|
+
const e = wetEnv();
|
|
81
|
+
if (!e)
|
|
82
|
+
throw new Error('missing env');
|
|
83
|
+
const { client: s, token } = await openTestSession(e);
|
|
84
|
+
try {
|
|
85
|
+
const cfg = await s.getUserIsolationConfig();
|
|
86
|
+
strict_1.default.equal(cfg.enabled, false);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
await fetch(`${e.baseUrl.replace(/\/+$/, '')}/api/v1/test/sessions/current`, {
|
|
90
|
+
method: 'DELETE',
|
|
91
|
+
headers: { 'X-Test-Session': token },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
(0, node_test_1.it)('disableUserIsolation after enable', async () => {
|
|
96
|
+
const e = wetEnv();
|
|
97
|
+
if (!e)
|
|
98
|
+
throw new Error('missing env');
|
|
99
|
+
const c = adminClient(e);
|
|
100
|
+
try {
|
|
101
|
+
await c.setUserIsolationConfig({ enabled: true, namespace_pattern: '/users/${user.id}' });
|
|
102
|
+
await c.disableUserIsolation();
|
|
103
|
+
const cfg = await c.getUserIsolationConfig();
|
|
104
|
+
strict_1.default.equal(cfg.enabled, false);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await c.disableUserIsolation().catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
(0, node_test_1.it)('document write uses transparent prefix for app user', async () => {
|
|
111
|
+
const e = wetEnv();
|
|
112
|
+
if (!e)
|
|
113
|
+
throw new Error('missing env');
|
|
114
|
+
const admin = adminClient(e);
|
|
115
|
+
const suffix = (0, node_crypto_1.randomUUID)().slice(0, 8);
|
|
116
|
+
const email = `js_iso_${suffix}@apitest.invalid`;
|
|
117
|
+
const password = `Js_${suffix}!aA1`;
|
|
118
|
+
const slug = `js-iso-doc-${suffix}`;
|
|
119
|
+
try {
|
|
120
|
+
await admin.setUserIsolationConfig({ enabled: true, namespace_pattern: '/users/${user.id}' });
|
|
121
|
+
const u = await admin.createAppUser(email, password, undefined, [
|
|
122
|
+
client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
|
|
123
|
+
]);
|
|
124
|
+
const app = new client_js_1.XCiteDBClient({
|
|
125
|
+
baseUrl: e.baseUrl,
|
|
126
|
+
context: { branch: 'main', project_id: e.tenantId },
|
|
127
|
+
userIsolation: { enabled: true },
|
|
128
|
+
});
|
|
129
|
+
await app.loginAppUser(email, password);
|
|
130
|
+
await app.writeJsonDocument(`/${slug}`, { _xcite_json_doc: true, v: 1 });
|
|
131
|
+
const doc = await app.readJsonDocument(`/${slug}`);
|
|
132
|
+
strict_1.default.equal(doc.v, 1);
|
|
133
|
+
await admin.deleteJsonDocument(`/users/${u.user_id}/${slug}`);
|
|
134
|
+
await admin.deleteAppUser(u.user_id);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await admin.disableUserIsolation().catch(() => { });
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
(0, node_test_1.it)('shared_read_paths passthrough: app reads shared path without double-prefix', async () => {
|
|
141
|
+
const e = wetEnv();
|
|
142
|
+
if (!e)
|
|
143
|
+
throw new Error('missing env');
|
|
144
|
+
const admin = adminClient(e);
|
|
145
|
+
const suffix = (0, node_crypto_1.randomUUID)().slice(0, 8);
|
|
146
|
+
const email = `js_shr_${suffix}@apitest.invalid`;
|
|
147
|
+
const password = `Js_${suffix}!aA1`;
|
|
148
|
+
const sharedPath = `/shared-read-${suffix}`;
|
|
149
|
+
const docPath = `${sharedPath}/doc`;
|
|
150
|
+
try {
|
|
151
|
+
await admin.setUserIsolationConfig({
|
|
152
|
+
enabled: true,
|
|
153
|
+
namespace_pattern: '/users/${user.id}',
|
|
154
|
+
shared_read_paths: [sharedPath],
|
|
155
|
+
});
|
|
156
|
+
await admin.writeJsonDocument(docPath, { _xcite_json_doc: true, tag: 'shared' });
|
|
157
|
+
const created = await admin.createAppUser(email, password, undefined, [
|
|
158
|
+
client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
|
|
159
|
+
]);
|
|
160
|
+
const app = new client_js_1.XCiteDBClient({
|
|
161
|
+
baseUrl: e.baseUrl,
|
|
162
|
+
context: { branch: 'main', project_id: e.tenantId },
|
|
163
|
+
userIsolation: { enabled: true, shared_read_paths: [sharedPath] },
|
|
164
|
+
});
|
|
165
|
+
await app.loginAppUser(email, password);
|
|
166
|
+
const doc = await app.readJsonDocument(docPath);
|
|
167
|
+
strict_1.default.equal(doc.tag, 'shared');
|
|
168
|
+
await admin.deleteJsonDocument(docPath);
|
|
169
|
+
await admin.deleteAppUser(created.user_id);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
await admin.disableUserIsolation().catch(() => { });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|