@xcitedbs/client 0.3.7 → 0.3.9

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 CHANGED
@@ -1,4 +1,4 @@
1
- import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, SmartDiffRef, SmartDiffResult, DocumentBatchResponse, DocumentExportFormat, ExportDocumentResult, Flags, JsonDocumentBatchItem, ImportDocumentOptions, ImportDocumentResult, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LogEntry, MergeResult, PublishResult, RebaseUserWorkspaceResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, TriggerEventsResponse, StoredPolicyResponse, TxnRequest, TxnResponse, VerifyAppUserTokenOptions, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, AssetGcDryRunResult, AssetHeadResult, AssetListResponse, AssetMagicLinkListResponse, AssetMagicLinkResult, AssetShareListResponse, AssetShareRequest, AssetUnshareRequest, AssetUploadResult, CreateAssetMagicLinkRequest, ListAssetsOptions, ProjectAssetStorageConfig, UploadAssetOptions, PlatformDefaultDocConfResponse, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, TestSessionBootstrapSummary, TestSessionInfo, XCiteQuery, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationShareResult } from './types';
1
+ import { AccessCheckResult, AppAuthConfig, AppEmailConfig, AppEmailTemplates, AppUser, AppUserTokenPair, EmailTestResponse, ForgotPasswordResponse, SendVerificationResponse, BranchInfo, BookmarkRecord, CheckpointRecord, CommitRecord, CompareRef, CompareResult, DatabaseContext, DiffRef, DiffResult, SmartDiffRef, SmartDiffResult, DocumentBatchResponse, DocumentExportFormat, ExportDocumentResult, Flags, JsonDocumentBatchItem, ImportDocumentOptions, ImportDocumentResult, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LogEntry, MergeResult, PublishResult, RebaseUserWorkspaceResult, WorkspaceInfo, MetaValue, PlatformRegisterResult, PolicySubjectInput, UnqueryResult, UnqueryTemplate, PolicyUpdateResponse, RealtimeEvent, SecurityConfig, SecurityPolicy, StoredTriggerResponse, TriggerDefinition, TriggerEventsResponse, StoredPolicyResponse, TxnRequest, TxnResponse, VerifyAppUserTokenOptions, SubscriptionOptions, TagRecord, TextSearchQuery, TextSearchResult, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, AssetGcDryRunResult, AssetHeadResult, AssetListResponse, AssetMagicLinkListResponse, AssetMagicLinkResult, AssetShareListResponse, AssetShareRequest, AssetUnshareRequest, AssetUploadResult, CreateAssetMagicLinkRequest, ListAssetsOptions, ProjectAssetStorageConfig, UploadAssetOptions, PlatformDefaultDocConfResponse, VectorIndexEstimate, RagQueryOptions, RagQueryResult, RagStreamEvent, OAuthProvidersResponse, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspacesResponse, TokenPair, UserInfo, ApiKeyInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateTestSessionOptions, XCiteDBClientOptions, XCiteDBJwtClaims, TestSessionBootstrapSummary, TestSessionInfo, XCiteQuery, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationShareResult, UserIsolationShareListResponse, UserIsolationUnshareParams, ShareDirection, AppUserGroup, CreateGroupParams, ListGroupsResponse } from './types';
2
2
  import { WebSocketSubscription } from './websocket';
3
3
  export declare class XCiteDBClient {
4
4
  private baseUrl;
@@ -500,15 +500,63 @@ export declare class XCiteDBClient {
500
500
  * When {@link XCiteDBClient.enableUserIsolation} / `userIsolation` is active, `identifier` is prefixed like other document APIs.
501
501
  */
502
502
  createUserIsolationShare(params: UserIsolationCreateShareParams): Promise<UserIsolationShareResult>;
503
+ /**
504
+ * Revoke a document share previously created with {@link XCiteDBClient.createUserIsolationShare}
505
+ * (`DELETE /api/v1/security/user-isolation/shares`). Requires an **app user** session.
506
+ */
507
+ removeUserIsolationShare(params: UserIsolationUnshareParams): Promise<void>;
508
+ /**
509
+ * List document share aliases visible to the caller (`GET /api/v1/security/user-isolation/shares`).
510
+ * `direction: "outgoing"` is not enumerated server-side — the server returns a `note` and an empty
511
+ * list; track outgoing shares in the app if needed.
512
+ */
513
+ listUserIsolationShares(opts: {
514
+ direction: ShareDirection;
515
+ scope?: string;
516
+ }): Promise<UserIsolationShareListResponse>;
503
517
  /** Share an asset path via prefix alias (`POST /api/v1/security/user-isolation/asset-shares`). */
504
518
  createAssetShare(params: AssetShareRequest): Promise<UserIsolationShareResult>;
505
519
  /** Remove an asset share (`DELETE /api/v1/security/user-isolation/asset-shares`). */
506
520
  deleteAssetShare(params: AssetUnshareRequest): Promise<void>;
507
521
  /** List incoming/public asset share aliases (`GET /api/v1/security/user-isolation/asset-shares`). */
508
522
  listAssetShares(opts?: {
509
- direction?: 'incoming' | 'outgoing' | 'public';
523
+ direction?: ShareDirection;
510
524
  scope?: string;
511
525
  }): Promise<AssetShareListResponse>;
526
+ /**
527
+ * Create an app-user group (`POST /api/v1/security/user-isolation/groups`). Requires an **app user**
528
+ * session for `kind: "team"` (caller becomes the owner) or a tenant admin for `kind: "admin"`. Use
529
+ * group ids as `ShareTarget` values to share documents/assets with all current and future members.
530
+ *
531
+ * Note: do **not** confuse with {@link XCiteDBClient.updateAppUserGroups}, which is a developer/admin
532
+ * endpoint that overwrites a user's group list — this endpoint creates the group itself.
533
+ */
534
+ createGroup(params: CreateGroupParams): Promise<AppUserGroup>;
535
+ /**
536
+ * List groups visible to the caller (`GET /api/v1/security/user-isolation/groups`). For app users,
537
+ * returns admin groups, groups they own, and groups they belong to.
538
+ */
539
+ listGroups(): Promise<ListGroupsResponse>;
540
+ /** Fetch a single group (`GET /api/v1/security/user-isolation/groups/{id}`). 404 if not visible. */
541
+ getGroup(id: string): Promise<AppUserGroup>;
542
+ /** Delete a group (`DELETE /api/v1/security/user-isolation/groups/{id}`). Owner or admin only. */
543
+ deleteGroup(id: string): Promise<void>;
544
+ /**
545
+ * Rename a group (`PATCH /api/v1/security/user-isolation/groups/{id}`). Owner or admin only.
546
+ * Returns the updated group record. The `group_id` is stable across renames, so existing shares
547
+ * targeted at this group continue to work.
548
+ */
549
+ renameGroup(id: string, name: string): Promise<AppUserGroup>;
550
+ /**
551
+ * Add an app user to a group (`POST /api/v1/security/user-isolation/groups/{id}/members`).
552
+ * Owner or admin only. Returns the updated group record.
553
+ */
554
+ addGroupMember(id: string, userId: string): Promise<AppUserGroup>;
555
+ /**
556
+ * Remove an app user from a group
557
+ * (`DELETE /api/v1/security/user-isolation/groups/{id}/members/{user_id}`). Owner or admin only.
558
+ */
559
+ removeGroupMember(id: string, userId: string): Promise<void>;
512
560
  /** Issue a time-limited magic link for one asset identifier (`POST /api/v1/security/asset-magic-links`). */
513
561
  createAssetMagicLink(body: CreateAssetMagicLinkRequest): Promise<AssetMagicLinkResult>;
514
562
  /** List issued magic links (no secrets) (`GET /api/v1/security/asset-magic-links`). */
package/dist/client.js CHANGED
@@ -71,6 +71,18 @@ function buildQuery(params) {
71
71
  const s = sp.toString();
72
72
  return s ? `?${s}` : '';
73
73
  }
74
+ /**
75
+ * Forward share-target fields (legacy `target_user_id`/`target_group_id` and the preferred `target`
76
+ * object) into a request body. Server `parseShareTarget` accepts either shape.
77
+ */
78
+ function applyShareTargetToBody(body, src) {
79
+ if (src.target_user_id !== undefined)
80
+ body.target_user_id = src.target_user_id;
81
+ if (src.target_group_id !== undefined)
82
+ body.target_group_id = src.target_group_id;
83
+ if (src.target !== undefined)
84
+ body.target = src.target;
85
+ }
74
86
  /** Best-effort filename from `Content-Disposition` (attachment; filename="…"). */
75
87
  function parseContentDispositionFilename(cd) {
76
88
  if (!cd)
@@ -1656,18 +1668,45 @@ class XCiteDBClient {
1656
1668
  * When {@link XCiteDBClient.enableUserIsolation} / `userIsolation` is active, `identifier` is prefixed like other document APIs.
1657
1669
  */
1658
1670
  async createUserIsolationShare(params) {
1659
- return this.request('POST', '/api/v1/security/user-isolation/shares', {
1671
+ const body = {
1660
1672
  identifier: this.isoPrefixId(params.identifier),
1661
- target_user_id: params.target_user_id,
1662
1673
  mode: params.mode,
1674
+ };
1675
+ applyShareTargetToBody(body, params);
1676
+ return this.request('POST', '/api/v1/security/user-isolation/shares', body);
1677
+ }
1678
+ /**
1679
+ * Revoke a document share previously created with {@link XCiteDBClient.createUserIsolationShare}
1680
+ * (`DELETE /api/v1/security/user-isolation/shares`). Requires an **app user** session.
1681
+ */
1682
+ async removeUserIsolationShare(params) {
1683
+ const body = {
1684
+ identifier: this.isoPrefixId(params.identifier),
1685
+ };
1686
+ applyShareTargetToBody(body, params);
1687
+ if (params.sharer_user_id !== undefined) {
1688
+ body.sharer_user_id = params.sharer_user_id;
1689
+ }
1690
+ await this.request('DELETE', '/api/v1/security/user-isolation/shares', body);
1691
+ }
1692
+ /**
1693
+ * List document share aliases visible to the caller (`GET /api/v1/security/user-isolation/shares`).
1694
+ * `direction: "outgoing"` is not enumerated server-side — the server returns a `note` and an empty
1695
+ * list; track outgoing shares in the app if needed.
1696
+ */
1697
+ async listUserIsolationShares(opts) {
1698
+ const q = buildQuery({
1699
+ direction: opts.direction,
1700
+ ...(opts.scope ? { scope: opts.scope } : {}),
1663
1701
  });
1702
+ return this.request('GET', `/api/v1/security/user-isolation/shares${q}`);
1664
1703
  }
1665
1704
  /** Share an asset path via prefix alias (`POST /api/v1/security/user-isolation/asset-shares`). */
1666
1705
  async createAssetShare(params) {
1667
1706
  const body = {
1668
- target_user_id: params.target_user_id,
1669
1707
  mode: params.mode,
1670
1708
  };
1709
+ applyShareTargetToBody(body, params);
1671
1710
  if (params.source_uri !== undefined && params.source_uri !== '') {
1672
1711
  body.source_uri = params.source_uri;
1673
1712
  }
@@ -1691,9 +1730,7 @@ class XCiteDBClient {
1691
1730
  else {
1692
1731
  throw new types_1.XCiteDBError('deleteAssetShare requires identifier or source_uri', 400, null);
1693
1732
  }
1694
- if (params.target_user_id !== undefined) {
1695
- body.target_user_id = params.target_user_id;
1696
- }
1733
+ applyShareTargetToBody(body, params);
1697
1734
  if (params.sharer_user_id !== undefined) {
1698
1735
  body.sharer_user_id = params.sharer_user_id;
1699
1736
  }
@@ -1707,6 +1744,57 @@ class XCiteDBClient {
1707
1744
  });
1708
1745
  return this.request('GET', `/api/v1/security/user-isolation/asset-shares${q}`);
1709
1746
  }
1747
+ /**
1748
+ * Create an app-user group (`POST /api/v1/security/user-isolation/groups`). Requires an **app user**
1749
+ * session for `kind: "team"` (caller becomes the owner) or a tenant admin for `kind: "admin"`. Use
1750
+ * group ids as `ShareTarget` values to share documents/assets with all current and future members.
1751
+ *
1752
+ * Note: do **not** confuse with {@link XCiteDBClient.updateAppUserGroups}, which is a developer/admin
1753
+ * endpoint that overwrites a user's group list — this endpoint creates the group itself.
1754
+ */
1755
+ async createGroup(params) {
1756
+ const body = { name: params.name };
1757
+ if (params.kind !== undefined)
1758
+ body.kind = params.kind;
1759
+ return this.request('POST', '/api/v1/security/user-isolation/groups', body);
1760
+ }
1761
+ /**
1762
+ * List groups visible to the caller (`GET /api/v1/security/user-isolation/groups`). For app users,
1763
+ * returns admin groups, groups they own, and groups they belong to.
1764
+ */
1765
+ async listGroups() {
1766
+ return this.request('GET', '/api/v1/security/user-isolation/groups');
1767
+ }
1768
+ /** Fetch a single group (`GET /api/v1/security/user-isolation/groups/{id}`). 404 if not visible. */
1769
+ async getGroup(id) {
1770
+ return this.request('GET', `/api/v1/security/user-isolation/groups/${encodeURIComponent(id)}`);
1771
+ }
1772
+ /** Delete a group (`DELETE /api/v1/security/user-isolation/groups/{id}`). Owner or admin only. */
1773
+ async deleteGroup(id) {
1774
+ await this.request('DELETE', `/api/v1/security/user-isolation/groups/${encodeURIComponent(id)}`);
1775
+ }
1776
+ /**
1777
+ * Rename a group (`PATCH /api/v1/security/user-isolation/groups/{id}`). Owner or admin only.
1778
+ * Returns the updated group record. The `group_id` is stable across renames, so existing shares
1779
+ * targeted at this group continue to work.
1780
+ */
1781
+ async renameGroup(id, name) {
1782
+ return this.request('PATCH', `/api/v1/security/user-isolation/groups/${encodeURIComponent(id)}`, { name });
1783
+ }
1784
+ /**
1785
+ * Add an app user to a group (`POST /api/v1/security/user-isolation/groups/{id}/members`).
1786
+ * Owner or admin only. Returns the updated group record.
1787
+ */
1788
+ async addGroupMember(id, userId) {
1789
+ return this.request('POST', `/api/v1/security/user-isolation/groups/${encodeURIComponent(id)}/members`, { user_id: userId });
1790
+ }
1791
+ /**
1792
+ * Remove an app user from a group
1793
+ * (`DELETE /api/v1/security/user-isolation/groups/{id}/members/{user_id}`). Owner or admin only.
1794
+ */
1795
+ async removeGroupMember(id, userId) {
1796
+ await this.request('DELETE', `/api/v1/security/user-isolation/groups/${encodeURIComponent(id)}/members/${encodeURIComponent(userId)}`);
1797
+ }
1710
1798
  /** Issue a time-limited magic link for one asset identifier (`POST /api/v1/security/asset-magic-links`). */
1711
1799
  async createAssetMagicLink(body) {
1712
1800
  return this.request('POST', '/api/v1/security/asset-magic-links', body);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { XCiteDBClient } from './client';
2
2
  export { parseAssetUri, formatAssetUri, collectIdentifiersFromText, ASSET_URI_PREFIX } from './assetUri';
3
3
  export { WebSocketSubscription } from './websocket';
4
- 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, SmartDiffRef, SmartDiffResult, SmartDiffStats, DocumentBatchResponse, DocumentBatchResultRow, DocumentExportFormat, DocumentImportFormat, ExportDocumentResult, Flags, ImportDocumentOptions, ImportDocumentResult, JsonDocumentData, JsonDocumentBatchItem, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LockConflictBody, LockExpiredBody, LockUnknownBody, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, AssetGcDryRunResult, AssetHeadResult, AssetListItem, AssetListResponse, AssetMagicLinkListResponse, AssetMagicLinkRecord, AssetMagicLinkResult, AssetShareListEntry, AssetShareListResponse, AssetShareRequest, AssetStorageImport, AssetStorageMount, AssetStorageTarget, AssetStorageTargetType, AssetUnshareRequest, AssetUploadResult, CreateAssetMagicLinkRequest, ListAssetsOptions, ProjectAssetStorageConfig, UploadAssetOptions, PlatformDefaultDocConfResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, RebaseUserWorkspaceResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SandboxApiKeyMintResult, SandboxInfo, SandboxMember, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TriggerEvent, TriggerEventsResponse, TxnOperation, TxnOperationResult, TxnPrecondition, TxnPreconditionResult, TxnRequest, TxnResponse, JwksKey, JwksResponse, VerifyAppUserTokenOptions, TokenPair, UserInfo, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationOptions, UserIsolationShareMode, UserIsolationShareResult, WorkspaceInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateSandboxOptions, CreateTestSessionOptions, TestSessionBootstrap, TestSessionBootstrapSummary, TestSessionInfo, XCiteDBClientOptions, XCiteDBErrorExtras, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
4
+ 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, SmartDiffRef, SmartDiffResult, SmartDiffStats, DocumentBatchResponse, DocumentBatchResultRow, DocumentExportFormat, DocumentImportFormat, ExportDocumentResult, Flags, ImportDocumentOptions, ImportDocumentResult, JsonDocumentData, JsonDocumentBatchItem, IdentifierChildNode, ListIdentifierChildrenResult, ListIdentifiersResult, LockInfo, AcquireLockOptions, LockConflictBody, LockExpiredBody, LockUnknownBody, MergeConflict, MergeResult, OAuthProviderInfo, OAuthProvidersResponse, OwnedTenantInfo, ProjectInfo, PlatformRegistrationConfig, PlatformWorkspaceOrg, PlatformWorkspacesResponse, ProjectSearchSettings, ProjectSearchSettingsUpdate, ProjectDocConfResponse, AssetGcDryRunResult, AssetHeadResult, AssetListItem, AssetListResponse, AssetMagicLinkListResponse, AssetMagicLinkRecord, AssetMagicLinkResult, AssetShareListEntry, AssetShareListResponse, AssetShareRequest, AssetStorageImport, AssetStorageMount, AssetStorageTarget, AssetStorageTargetType, AssetUnshareRequest, AssetUploadResult, CreateAssetMagicLinkRequest, ListAssetsOptions, ProjectAssetStorageConfig, UploadAssetOptions, PlatformDefaultDocConfResponse, LogEntry, MetaValue, PlatformRegisterResult, PolicyUpdateResponse, PublishConflict, PublishResult, RebaseUserWorkspaceResult, PolicyConditions, PolicyIdentifierPattern, PolicyResources, PolicySubjectInput, PolicySubjects, RagQueryOptions, RagQueryResult, RagStreamEvent, RealtimeEvent, SandboxApiKeyMintResult, SandboxInfo, SandboxMember, SearchIndexingProgress, SecurityConfig, SecurityPolicy, StoredPolicyResponse, StoredTriggerResponse, SubscriptionOptions, TagRecord, TextSearchHit, TextSearchQuery, TextSearchResult, TriggerDefinition, TriggerEvent, TriggerEventsResponse, TxnOperation, TxnOperationResult, TxnPrecondition, TxnPreconditionResult, TxnRequest, TxnResponse, JwksKey, JwksResponse, VerifyAppUserTokenOptions, TokenPair, UserInfo, UserIsolationConfig, UserIsolationCreateShareParams, UserIsolationOptions, UserIsolationShareMode, UserIsolationShareResult, UserIsolationShareListEntry, UserIsolationShareListResponse, UserIsolationUnshareParams, ShareTarget, ShareDirection, AppUserGroup, AppUserGroupKind, CreateGroupParams, ListGroupsResponse, WorkspaceInfo, WriteDocumentOptions, XmlDocumentBatchItem, CreateSandboxOptions, CreateTestSessionOptions, TestSessionBootstrap, TestSessionBootstrapSummary, TestSessionInfo, XCiteDBClientOptions, XCiteDBErrorExtras, XCiteDBJwtClaims, UnqueryResult, UnqueryTemplate, XCiteQuery, } from './types';
5
5
  export { XCiteDBError, XCiteDBForbiddenError, XCiteDBNotFoundError, XCiteDBAuthError, XCiteDBLockConflictError, } from './types';
package/dist/types.d.ts CHANGED
@@ -278,14 +278,24 @@ export interface AssetShareRequest {
278
278
  identifier?: string;
279
279
  /** `xcitedb:asset:/…` or raw `/…` path; not prefixed by the client. */
280
280
  source_uri?: string;
281
- target_user_id: string;
281
+ /** Legacy: prefer `target`. App user id, `"*"` (public), or `"#anonymous"`. */
282
+ target_user_id?: string;
283
+ /** Legacy: prefer `target`. */
284
+ target_group_id?: string;
285
+ /** Preferred share-target shape. */
286
+ target?: ShareTarget;
282
287
  mode: UserIsolationShareMode;
283
288
  }
284
289
  /** Body for `DELETE /api/v1/security/user-isolation/asset-shares`. */
285
290
  export interface AssetUnshareRequest {
286
291
  identifier?: string;
287
292
  source_uri?: string;
293
+ /** Legacy: prefer `target`. */
288
294
  target_user_id?: string;
295
+ /** Legacy: prefer `target`. */
296
+ target_group_id?: string;
297
+ /** Preferred share-target shape. */
298
+ target?: ShareTarget;
289
299
  sharer_user_id?: string;
290
300
  }
291
301
  export interface AssetShareListEntry {
@@ -652,11 +662,32 @@ export interface UserIsolationConfig {
652
662
  }
653
663
  /** `read` or `readwrite` for {@link XCiteDBClient.createUserIsolationShare}. */
654
664
  export type UserIsolationShareMode = 'read' | 'readwrite';
665
+ /**
666
+ * Share target for {@link XCiteDBClient.createUserIsolationShare}, {@link XCiteDBClient.createAssetShare},
667
+ * and the corresponding revoke methods. Server reference: `parseShareTarget` in `SecurityController.cpp`.
668
+ */
669
+ export type ShareTarget = {
670
+ type: 'user';
671
+ id: string;
672
+ } | {
673
+ type: 'group';
674
+ id: string;
675
+ } | {
676
+ type: 'public';
677
+ } | {
678
+ type: 'anonymous';
679
+ };
680
+ /** Direction filter for {@link XCiteDBClient.listUserIsolationShares} and {@link XCiteDBClient.listAssetShares}. */
681
+ export type ShareDirection = 'incoming' | 'outgoing' | 'public';
655
682
  /** Body for `POST /api/v1/security/user-isolation/shares` (app user). */
656
683
  export interface UserIsolationCreateShareParams {
657
684
  identifier: string;
658
- /** Another app user id on the same tenant, or `"*"` for all app users (public alias tree). */
659
- target_user_id: string;
685
+ /** Legacy: prefer `target`. App user id, `"*"` (public), or `"#anonymous"`. */
686
+ target_user_id?: string;
687
+ /** Legacy: prefer `target`. App user group id. */
688
+ target_group_id?: string;
689
+ /** Preferred share-target shape; supersedes the legacy fields. */
690
+ target?: ShareTarget;
660
691
  mode: UserIsolationShareMode;
661
692
  }
662
693
  /** JSON body returned with HTTP 201 from create share. */
@@ -664,6 +695,64 @@ export interface UserIsolationShareResult {
664
695
  alias: string;
665
696
  mode: string;
666
697
  }
698
+ /** Single entry in {@link UserIsolationShareListResponse.identifiers}. */
699
+ export interface UserIsolationShareListEntry {
700
+ identifier: string;
701
+ mode: UserIsolationShareMode;
702
+ /** App user id of the user who created this share, parsed from the alias path. */
703
+ granter_user_id?: string;
704
+ /** Display name of the granter, looked up at list time. */
705
+ granter_display_name?: string;
706
+ /** Email of the granter, looked up at list time. */
707
+ granter_email?: string;
708
+ /** Epoch seconds. Omitted for shares created before share-meta storage existed. */
709
+ created_at?: number;
710
+ }
711
+ /** Response from `GET /api/v1/security/user-isolation/shares?direction=…`. */
712
+ export interface UserIsolationShareListResponse {
713
+ direction: ShareDirection;
714
+ identifiers: UserIsolationShareListEntry[];
715
+ scope?: string;
716
+ /** Set for `direction: "outgoing"` — server does not enumerate outgoing shares. */
717
+ note?: string;
718
+ }
719
+ /** Body for `DELETE /api/v1/security/user-isolation/shares` (app user). */
720
+ export interface UserIsolationUnshareParams {
721
+ identifier: string;
722
+ /** Legacy: prefer `target`. */
723
+ target_user_id?: string;
724
+ /** Legacy: prefer `target`. */
725
+ target_group_id?: string;
726
+ /** Preferred share-target shape. */
727
+ target?: ShareTarget;
728
+ /** When the caller is the share recipient revoking incoming access; otherwise omit. */
729
+ sharer_user_id?: string;
730
+ }
731
+ /** App-user group kind. `team` is owned by the creating app user; `admin` is tenant-wide. */
732
+ export type AppUserGroupKind = 'team' | 'admin';
733
+ /** App-user group record returned by group endpoints (`AppUserGroupService::recordToPublicJson`). */
734
+ export interface AppUserGroup {
735
+ group_id: string;
736
+ name: string;
737
+ kind: AppUserGroupKind;
738
+ /** App user id of the owner. Empty for `kind: "admin"`. */
739
+ owner_id: string;
740
+ member_ids: string[];
741
+ /** Epoch seconds. */
742
+ created_at: number;
743
+ /** Epoch seconds. */
744
+ updated_at: number;
745
+ }
746
+ /** Body for `POST /api/v1/security/user-isolation/groups`. */
747
+ export interface CreateGroupParams {
748
+ name: string;
749
+ /** Defaults to `"team"`. `"admin"` requires a tenant-admin caller. */
750
+ kind?: AppUserGroupKind;
751
+ }
752
+ /** Response from `GET /api/v1/security/user-isolation/groups`. */
753
+ export interface ListGroupsResponse {
754
+ groups: AppUserGroup[];
755
+ }
667
756
  export interface XCiteDBClientOptions {
668
757
  baseUrl: string;
669
758
  apiKey?: string;
@@ -261,3 +261,165 @@ function slugifyForAssert(name) {
261
261
  .replace(/^-+|-+$/g, '')
262
262
  .slice(0, 64);
263
263
  }
264
+ wd('app-user groups + group-aware shares (wet)', () => {
265
+ (0, node_test_1.it)('groups lifecycle: owner creates, adds member, non-owner mutation rejected, owner deletes', async () => {
266
+ const e = wetEnv();
267
+ if (!e)
268
+ throw new Error('missing env');
269
+ const admin = adminClient(e);
270
+ const suffix = (0, node_crypto_1.randomUUID)().slice(0, 8);
271
+ const ownerEmail = `js_grp_o_${suffix}@apitest.invalid`;
272
+ const memberEmail = `js_grp_m_${suffix}@apitest.invalid`;
273
+ const password = `Js_${suffix}!aA1`;
274
+ let owner;
275
+ let member;
276
+ let groupId;
277
+ try {
278
+ await admin.setUserIsolationConfig({ enabled: true, namespace_pattern: '/users/${user.id}' });
279
+ owner = await admin.createAppUser(ownerEmail, password, undefined, [
280
+ client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
281
+ ]);
282
+ member = await admin.createAppUser(memberEmail, password, undefined, [
283
+ client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
284
+ ]);
285
+ const ownerClient = new client_js_1.XCiteDBClient({
286
+ baseUrl: e.baseUrl,
287
+ context: { branch: 'main', project_id: e.tenantId },
288
+ userIsolation: { enabled: true },
289
+ });
290
+ await ownerClient.loginAppUser(ownerEmail, password);
291
+ const created = await ownerClient.createGroup({ name: `g-${suffix}`, kind: 'team' });
292
+ groupId = created.group_id;
293
+ strict_1.default.equal(created.kind, 'team');
294
+ strict_1.default.equal(created.owner_id, owner.user_id);
295
+ const afterAdd = await ownerClient.addGroupMember(groupId, member.user_id);
296
+ strict_1.default.ok(afterAdd.member_ids.includes(member.user_id), `member_ids should include ${member.user_id}, got ${JSON.stringify(afterAdd.member_ids)}`);
297
+ const fetched = await ownerClient.getGroup(groupId);
298
+ strict_1.default.equal(fetched.name, `g-${suffix}`);
299
+ strict_1.default.deepEqual(fetched.member_ids.sort(), afterAdd.member_ids.sort());
300
+ const list = await ownerClient.listGroups();
301
+ strict_1.default.ok(list.groups.some((g) => g.group_id === groupId), 'listGroups missing created group');
302
+ // Non-owner cannot mutate.
303
+ const memberClient = new client_js_1.XCiteDBClient({
304
+ baseUrl: e.baseUrl,
305
+ context: { branch: 'main', project_id: e.tenantId },
306
+ userIsolation: { enabled: true },
307
+ });
308
+ await memberClient.loginAppUser(memberEmail, password);
309
+ await strict_1.default.rejects(() => memberClient.deleteGroup(groupId), (err) => {
310
+ const status = err.status;
311
+ return status === 403;
312
+ }, 'non-owner deleteGroup must be rejected with 403');
313
+ // Rename: owner can; non-owner cannot.
314
+ const newName = `g-${suffix}-renamed`;
315
+ const renamed = await ownerClient.renameGroup(groupId, newName);
316
+ strict_1.default.equal(renamed.name, newName);
317
+ strict_1.default.equal(renamed.group_id, groupId, 'group_id must be stable across rename');
318
+ strict_1.default.ok(renamed.updated_at >= created.updated_at, 'updated_at should advance on rename');
319
+ await strict_1.default.rejects(() => memberClient.renameGroup(groupId, `g-${suffix}-hijack`), (err) => err.status === 403, 'non-owner renameGroup must be rejected with 403');
320
+ await ownerClient.removeGroupMember(groupId, member.user_id);
321
+ const afterRemove = await ownerClient.getGroup(groupId);
322
+ strict_1.default.ok(!afterRemove.member_ids.includes(member.user_id), 'member_ids should not include removed user');
323
+ await ownerClient.deleteGroup(groupId);
324
+ groupId = undefined;
325
+ await strict_1.default.rejects(() => ownerClient.getGroup(created.group_id), (err) => err.status === 404);
326
+ }
327
+ finally {
328
+ if (groupId && owner) {
329
+ const cleanupClient = new client_js_1.XCiteDBClient({
330
+ baseUrl: e.baseUrl,
331
+ context: { branch: 'main', project_id: e.tenantId },
332
+ userIsolation: { enabled: true },
333
+ });
334
+ await cleanupClient.loginAppUser(ownerEmail, password).catch(() => { });
335
+ await cleanupClient.deleteGroup(groupId).catch(() => { });
336
+ }
337
+ if (member)
338
+ await admin.deleteAppUser(member.user_id).catch(() => { });
339
+ if (owner)
340
+ await admin.deleteAppUser(owner.user_id).catch(() => { });
341
+ await admin.disableUserIsolation().catch(() => { });
342
+ }
343
+ });
344
+ (0, node_test_1.it)('group-aware share: owner shares to group, member reads via alias, revoke removes access', async () => {
345
+ const e = wetEnv();
346
+ if (!e)
347
+ throw new Error('missing env');
348
+ const admin = adminClient(e);
349
+ const suffix = (0, node_crypto_1.randomUUID)().slice(0, 8);
350
+ const ownerEmail = `js_gs_o_${suffix}@apitest.invalid`;
351
+ const memberEmail = `js_gs_m_${suffix}@apitest.invalid`;
352
+ const password = `Js_${suffix}!aA1`;
353
+ const slug = `js-gs-doc-${suffix}`;
354
+ let owner;
355
+ let member;
356
+ let groupId;
357
+ try {
358
+ await admin.setUserIsolationConfig({ enabled: true, namespace_pattern: '/users/${user.id}' });
359
+ owner = await admin.createAppUser(ownerEmail, password, undefined, [
360
+ client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
361
+ ]);
362
+ member = await admin.createAppUser(memberEmail, password, undefined, [
363
+ client_js_1.XCiteDBClient.buildProjectGroup(e.tenantId, 'editor'),
364
+ ]);
365
+ const ownerClient = new client_js_1.XCiteDBClient({
366
+ baseUrl: e.baseUrl,
367
+ context: { branch: 'main', project_id: e.tenantId },
368
+ userIsolation: { enabled: true },
369
+ });
370
+ await ownerClient.loginAppUser(ownerEmail, password);
371
+ const grp = await ownerClient.createGroup({ name: `gs-${suffix}`, kind: 'team' });
372
+ groupId = grp.group_id;
373
+ await ownerClient.addGroupMember(groupId, member.user_id);
374
+ await ownerClient.writeJsonDocument(`/${slug}`, { _xcite_json_doc: true, who: 'owner', v: 1 });
375
+ const share = await ownerClient.createUserIsolationShare({
376
+ identifier: `/${slug}`,
377
+ target: { type: 'group', id: groupId },
378
+ mode: 'read',
379
+ });
380
+ strict_1.default.ok(share.alias && typeof share.alias === 'string', 'share.alias missing');
381
+ const memberClient = new client_js_1.XCiteDBClient({
382
+ baseUrl: e.baseUrl,
383
+ context: { branch: 'main', project_id: e.tenantId },
384
+ userIsolation: { enabled: true },
385
+ });
386
+ await memberClient.loginAppUser(memberEmail, password);
387
+ const incoming = await memberClient.listUserIsolationShares({ direction: 'incoming' });
388
+ const matched = incoming.identifiers.find((row) => row.identifier === share.alias);
389
+ strict_1.default.ok(matched, `incoming list should include alias ${share.alias}; got ${JSON.stringify(incoming.identifiers)}`);
390
+ strict_1.default.equal(matched.granter_user_id, owner.user_id, 'granter_user_id should match the sharer');
391
+ strict_1.default.equal(matched.granter_email, ownerEmail, 'granter_email should be the sharer email');
392
+ strict_1.default.ok(typeof matched.created_at === 'number' && matched.created_at > 0, `created_at should be a positive number; got ${matched.created_at}`);
393
+ const doc = await memberClient.readJsonDocument(share.alias);
394
+ strict_1.default.equal(doc.who, 'owner');
395
+ strict_1.default.equal(doc.v, 1);
396
+ await ownerClient.removeUserIsolationShare({
397
+ identifier: `/${slug}`,
398
+ target: { type: 'group', id: groupId },
399
+ });
400
+ await strict_1.default.rejects(() => memberClient.readJsonDocument(share.alias), (err) => {
401
+ const status = err.status;
402
+ return status === 403 || status === 404;
403
+ }, 'after revoke, member read should be 403/404');
404
+ }
405
+ finally {
406
+ if (owner) {
407
+ await admin.deleteJsonDocument(`/users/${owner.user_id}/${slug}`).catch(() => { });
408
+ }
409
+ if (groupId && owner) {
410
+ const cleanupClient = new client_js_1.XCiteDBClient({
411
+ baseUrl: e.baseUrl,
412
+ context: { branch: 'main', project_id: e.tenantId },
413
+ userIsolation: { enabled: true },
414
+ });
415
+ await cleanupClient.loginAppUser(ownerEmail, password).catch(() => { });
416
+ await cleanupClient.deleteGroup(groupId).catch(() => { });
417
+ }
418
+ if (member)
419
+ await admin.deleteAppUser(member.user_id).catch(() => { });
420
+ if (owner)
421
+ await admin.deleteAppUser(owner.user_id).catch(() => { });
422
+ await admin.disableUserIsolation().catch(() => { });
423
+ }
424
+ });
425
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcitedbs/client",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "XCiteDB BaaS client SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",