@milaboratories/pl-client 3.11.5 → 3.12.1

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.
Files changed (206) hide show
  1. package/dist/core/PromiseTracker.cjs.map +1 -1
  2. package/dist/core/PromiseTracker.d.ts.map +1 -1
  3. package/dist/core/PromiseTracker.js.map +1 -1
  4. package/dist/core/StatefulPromise.cjs +2 -0
  5. package/dist/core/StatefulPromise.cjs.map +1 -1
  6. package/dist/core/StatefulPromise.js +2 -0
  7. package/dist/core/StatefulPromise.js.map +1 -1
  8. package/dist/core/abstract_stream.d.ts +1 -1
  9. package/dist/core/advisory_locks.cjs.map +1 -1
  10. package/dist/core/advisory_locks.js.map +1 -1
  11. package/dist/core/auth.cjs.map +1 -1
  12. package/dist/core/auth.d.ts.map +1 -1
  13. package/dist/core/auth.js.map +1 -1
  14. package/dist/core/cache.d.ts.map +1 -1
  15. package/dist/core/capabilities.cjs.map +1 -1
  16. package/dist/core/capabilities.d.ts +2 -3
  17. package/dist/core/capabilities.d.ts.map +1 -1
  18. package/dist/core/capabilities.js.map +1 -1
  19. package/dist/core/client.cjs +23 -6
  20. package/dist/core/client.cjs.map +1 -1
  21. package/dist/core/client.d.ts +14 -4
  22. package/dist/core/client.d.ts.map +1 -1
  23. package/dist/core/client.js +22 -5
  24. package/dist/core/client.js.map +1 -1
  25. package/dist/core/config.cjs.map +1 -1
  26. package/dist/core/config.d.ts.map +1 -1
  27. package/dist/core/config.js.map +1 -1
  28. package/dist/core/default_client.cjs +5 -5
  29. package/dist/core/default_client.cjs.map +1 -1
  30. package/dist/core/default_client.d.ts.map +1 -1
  31. package/dist/core/default_client.js.map +1 -1
  32. package/dist/core/driver.cjs.map +1 -1
  33. package/dist/core/driver.d.ts.map +1 -1
  34. package/dist/core/driver.js.map +1 -1
  35. package/dist/core/error_resource.cjs.map +1 -1
  36. package/dist/core/error_resource.js.map +1 -1
  37. package/dist/core/errors.cjs +11 -11
  38. package/dist/core/errors.cjs.map +1 -1
  39. package/dist/core/errors.d.ts.map +1 -1
  40. package/dist/core/errors.js +11 -10
  41. package/dist/core/errors.js.map +1 -1
  42. package/dist/core/final.cjs +6 -2
  43. package/dist/core/final.cjs.map +1 -1
  44. package/dist/core/final.d.ts.map +1 -1
  45. package/dist/core/final.js +4 -1
  46. package/dist/core/final.js.map +1 -1
  47. package/dist/core/ll_client.cjs +60 -16
  48. package/dist/core/ll_client.cjs.map +1 -1
  49. package/dist/core/ll_client.d.ts +23 -7
  50. package/dist/core/ll_client.d.ts.map +1 -1
  51. package/dist/core/ll_client.js +62 -17
  52. package/dist/core/ll_client.js.map +1 -1
  53. package/dist/core/ll_transaction.cjs +3 -1
  54. package/dist/core/ll_transaction.cjs.map +1 -1
  55. package/dist/core/ll_transaction.d.ts +1 -1
  56. package/dist/core/ll_transaction.d.ts.map +1 -1
  57. package/dist/core/ll_transaction.js +2 -1
  58. package/dist/core/ll_transaction.js.map +1 -1
  59. package/dist/core/stat.cjs.map +1 -1
  60. package/dist/core/stat.js.map +1 -1
  61. package/dist/core/transaction.cjs +91 -4
  62. package/dist/core/transaction.cjs.map +1 -1
  63. package/dist/core/transaction.d.ts +52 -2
  64. package/dist/core/transaction.d.ts.map +1 -1
  65. package/dist/core/transaction.js +90 -5
  66. package/dist/core/transaction.js.map +1 -1
  67. package/dist/core/tree_filter.cjs +73 -18
  68. package/dist/core/tree_filter.cjs.map +1 -1
  69. package/dist/core/tree_filter.d.ts +1 -1
  70. package/dist/core/tree_filter.d.ts.map +1 -1
  71. package/dist/core/tree_filter.js +72 -19
  72. package/dist/core/tree_filter.js.map +1 -1
  73. package/dist/core/type_conversion.cjs +19 -20
  74. package/dist/core/type_conversion.cjs.map +1 -1
  75. package/dist/core/type_conversion.js +19 -19
  76. package/dist/core/type_conversion.js.map +1 -1
  77. package/dist/core/types.cjs +2 -3
  78. package/dist/core/types.cjs.map +1 -1
  79. package/dist/core/types.d.ts.map +1 -1
  80. package/dist/core/types.js +2 -2
  81. package/dist/core/types.js.map +1 -1
  82. package/dist/core/unauth_client.cjs +7 -6
  83. package/dist/core/unauth_client.cjs.map +1 -1
  84. package/dist/core/unauth_client.d.ts +9 -0
  85. package/dist/core/unauth_client.d.ts.map +1 -1
  86. package/dist/core/unauth_client.js +7 -5
  87. package/dist/core/unauth_client.js.map +1 -1
  88. package/dist/core/user_resources.cjs +57 -1
  89. package/dist/core/user_resources.cjs.map +1 -1
  90. package/dist/core/user_resources.d.ts +55 -1
  91. package/dist/core/user_resources.d.ts.map +1 -1
  92. package/dist/core/user_resources.js +57 -0
  93. package/dist/core/user_resources.js.map +1 -1
  94. package/dist/core/websocket_stream.cjs +17 -20
  95. package/dist/core/websocket_stream.cjs.map +1 -1
  96. package/dist/core/websocket_stream.js +16 -19
  97. package/dist/core/websocket_stream.js.map +1 -1
  98. package/dist/core/wire.d.ts.map +1 -1
  99. package/dist/helpers/pl.cjs +25 -0
  100. package/dist/helpers/pl.cjs.map +1 -1
  101. package/dist/helpers/pl.d.ts +1 -1
  102. package/dist/helpers/pl.d.ts.map +1 -1
  103. package/dist/helpers/pl.js +1 -1
  104. package/dist/helpers/pl.js.map +1 -1
  105. package/dist/helpers/poll.cjs +5 -1
  106. package/dist/helpers/poll.cjs.map +1 -1
  107. package/dist/helpers/poll.d.ts.map +1 -1
  108. package/dist/helpers/poll.js +4 -0
  109. package/dist/helpers/poll.js.map +1 -1
  110. package/dist/helpers/retry_strategy.cjs +1 -0
  111. package/dist/helpers/retry_strategy.cjs.map +1 -1
  112. package/dist/helpers/retry_strategy.js +1 -1
  113. package/dist/helpers/retry_strategy.js.map +1 -1
  114. package/dist/helpers/tx_helpers.cjs +0 -1
  115. package/dist/helpers/tx_helpers.cjs.map +1 -1
  116. package/dist/helpers/tx_helpers.d.ts.map +1 -1
  117. package/dist/helpers/tx_helpers.js.map +1 -1
  118. package/dist/index.cjs +3 -1
  119. package/dist/index.d.ts +3 -3
  120. package/dist/index.js +2 -2
  121. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs +0 -1
  122. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
  123. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.d.ts.map +1 -1
  124. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
  125. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +537 -83
  126. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
  127. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs +11 -4
  128. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs.map +1 -1
  129. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts +12 -6
  130. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts.map +1 -1
  131. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js +11 -3
  132. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js.map +1 -1
  133. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +112 -7
  134. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
  135. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +336 -83
  136. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  137. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs +23 -7
  138. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs.map +1 -1
  139. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts +10 -3
  140. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts.map +1 -1
  141. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js +18 -7
  142. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js.map +1 -1
  143. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs +1 -2
  144. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs.map +1 -1
  145. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts.map +1 -1
  146. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js +1 -1
  147. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js.map +1 -1
  148. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs +2 -2
  149. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs.map +1 -1
  150. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts +1 -1
  151. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts.map +1 -1
  152. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js +2 -2
  153. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js.map +1 -1
  154. package/dist/proto-grpc/google/protobuf/any.cjs +1 -2
  155. package/dist/proto-grpc/google/protobuf/any.cjs.map +1 -1
  156. package/dist/proto-grpc/google/protobuf/any.d.ts.map +1 -1
  157. package/dist/proto-grpc/google/protobuf/any.js +1 -1
  158. package/dist/proto-grpc/google/protobuf/any.js.map +1 -1
  159. package/dist/proto-grpc/google/protobuf/duration.cjs +0 -1
  160. package/dist/proto-grpc/google/protobuf/duration.cjs.map +1 -1
  161. package/dist/proto-grpc/google/protobuf/duration.d.ts.map +1 -1
  162. package/dist/proto-grpc/google/protobuf/duration.js.map +1 -1
  163. package/dist/proto-grpc/google/protobuf/timestamp.cjs +0 -1
  164. package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
  165. package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -1
  166. package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
  167. package/dist/proto-grpc/google/rpc/code.d.ts.map +1 -1
  168. package/dist/proto-rest/index.cjs +1 -1
  169. package/dist/proto-rest/index.cjs.map +1 -1
  170. package/dist/proto-rest/index.d.ts +1 -1
  171. package/dist/proto-rest/index.d.ts.map +1 -1
  172. package/dist/proto-rest/index.js.map +1 -1
  173. package/dist/proto-rest/plapi.d.ts +30 -9
  174. package/dist/proto-rest/plapi.d.ts.map +1 -1
  175. package/dist/test/tcp-proxy.cjs +2 -2
  176. package/dist/test/tcp-proxy.cjs.map +1 -1
  177. package/dist/test/tcp-proxy.d.ts +1 -1
  178. package/dist/test/tcp-proxy.js.map +1 -1
  179. package/dist/test/test_config.cjs +8 -2
  180. package/dist/test/test_config.cjs.map +1 -1
  181. package/dist/test/test_config.d.ts +1 -1
  182. package/dist/test/test_config.d.ts.map +1 -1
  183. package/dist/test/test_config.js +1 -1
  184. package/dist/test/test_config.js.map +1 -1
  185. package/dist/util/pl.cjs.map +1 -1
  186. package/dist/util/pl.js.map +1 -1
  187. package/dist/util/util.cjs.map +1 -1
  188. package/dist/util/util.js.map +1 -1
  189. package/package.json +4 -4
  190. package/src/core/capabilities.ts +10 -3
  191. package/src/core/client.ts +30 -3
  192. package/src/core/errors.ts +2 -0
  193. package/src/core/final.ts +4 -0
  194. package/src/core/ll_client.test.ts +15 -2
  195. package/src/core/ll_client.ts +66 -11
  196. package/src/core/transaction.ts +103 -3
  197. package/src/core/unauth_client.ts +17 -2
  198. package/src/core/user_resources.ts +85 -0
  199. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.ts +16 -3
  200. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +335 -12
  201. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.ts +21 -7
  202. package/src/proto-rest/plapi.ts +29 -8
  203. package/dist/proto-grpc/google/rpc/code.cjs +0 -224
  204. package/dist/proto-grpc/google/rpc/code.cjs.map +0 -1
  205. package/dist/proto-grpc/google/rpc/code.js +0 -224
  206. package/dist/proto-grpc/google/rpc/code.js.map +0 -1
@@ -1,5 +1,3 @@
1
- // TODO: fix this
2
- /* eslint-disable no-prototype-builtins */
3
1
  import type {
4
2
  ColorProof,
5
3
  LocalResourceId,
@@ -31,10 +29,15 @@ import type {
31
29
  ServerMessageResponse,
32
30
  } from "./ll_transaction";
33
31
  import type {
32
+ AuthAPI_Grant,
34
33
  ResourceAPI_Tree_Filter,
35
34
  ResourceAPI_Tree_SeedResource,
36
35
  } from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
37
- import { TxAPI_Open_Request_WritableTx } from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
36
+ import {
37
+ TxAPI_Open_Request_WritableTx,
38
+ AuthAPI_GrantAccess_GrantType,
39
+ AuthAPI_Role,
40
+ } from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
38
41
  import type { NonUndefined } from "utility-types";
39
42
  import { toBytes } from "../util/util";
40
43
  import {
@@ -155,6 +158,42 @@ export interface ResourceIdWithSignature {
155
158
  resourceSignature: ResourceSignature;
156
159
  }
157
160
 
161
+ /** Access level handed to a grant recipient (see {@link PlTransaction.grantAccess}). */
162
+ export interface GrantPermissions {
163
+ /** Write access = ability to modify the resource subtree + re-share it. */
164
+ writable: boolean;
165
+ }
166
+
167
+ /**
168
+ * Distinguishes a regular user-to-user grant from a system-level public grant.
169
+ * Re-export of the wire enum so callers need not reach into the generated proto.
170
+ */
171
+ export const GrantType = AuthAPI_GrantAccess_GrantType;
172
+ export type GrantType = AuthAPI_GrantAccess_GrantType;
173
+
174
+ /**
175
+ * Effective role of an authenticated session (controller / workflow / user / admin).
176
+ * Re-export of the wire enum so callers need not reach into the generated proto.
177
+ * Returned by {@link LLPlClient.getSessionInfo} and surfaced as
178
+ * {@link PlClient.currentUserRole}.
179
+ */
180
+ export const Role = AuthAPI_Role;
181
+ export type Role = AuthAPI_Role;
182
+
183
+ /**
184
+ * Recognises the "all users on the server" sentinel login. A public
185
+ * ({@link GrantType.ANY_AUTHORISED}) grant is recorded against a reserved login, and
186
+ * recipients of an everyone-grant surface in `ListGrants` with that value — callers
187
+ * detect it here and map it to "*".
188
+ *
189
+ * Matched by exact token equality. The long random token guarantees no real user login
190
+ * collides with it.
191
+ */
192
+ const EveryoneUser = "everyone-Po9ahwahxai7Aejingaiyiequuecu3ei4moaNge4xahTh0Co7XeeLeiph6ahy3As";
193
+ export function isEveryoneUserLogin(login: string): boolean {
194
+ return login === EveryoneUser;
195
+ }
196
+
158
197
  const emptySignature = toResourceSignature(new Uint8Array(0));
159
198
 
160
199
  function toResourceIdAndSignature(ref: AnyResourceRef): ResourceIdWithSignature {
@@ -817,6 +856,67 @@ export class PlTransaction {
817
856
  });
818
857
  }
819
858
 
859
+ /**
860
+ * Grant another user access to a resource (and its subtree). Used by the
861
+ * project-sharing donor flow to hand a recipient the shared envelope.
862
+ *
863
+ * `grantType` defaults to a regular user-to-user grant
864
+ * ({@link GrantType.SINGLE_USER}); pass {@link GrantType.ANY_AUTHORISED}
865
+ * to make the resource public to everyone (requires backend `publicGrants:v1`
866
+ * and a role allowed to grant to everyone — `target` is ignored / rewritten
867
+ * to the everyone-user by the backend).
868
+ */
869
+ public grantAccess(
870
+ rId: AnyResourceRef,
871
+ target: string,
872
+ permissions: GrantPermissions,
873
+ grantType: GrantType = AuthAPI_GrantAccess_GrantType.SINGLE_USER,
874
+ ): void {
875
+ this.sendVoidAsync({
876
+ oneofKind: "grantAccess",
877
+ grantAccess: {
878
+ ...this.toSignedResourceId(rId),
879
+ targetUser: target,
880
+ permissions,
881
+ grantType,
882
+ },
883
+ });
884
+ }
885
+
886
+ /**
887
+ * Revoke a single user's grant on a resource — the inverse of {@link grantAccess}.
888
+ * Used by the sharing donor flow to pull one recipient out of a multi-recipient
889
+ * envelope without tearing the whole share down. A no-op on the backend if the user
890
+ * has no grant.
891
+ */
892
+ public revokeAccess(rId: AnyResourceRef, target: string): void {
893
+ this.sendVoidAsync({
894
+ oneofKind: "revokeAccess",
895
+ revokeAccess: {
896
+ ...this.toSignedResourceId(rId),
897
+ targetUser: target,
898
+ },
899
+ });
900
+ }
901
+
902
+ /**
903
+ * Enumerate the grants of a resource — the donor-side "who did I share with" view.
904
+ * The backend gates this on the signed, writable resource handle, so only the
905
+ * resource's owner can read its grants. Recipients of a public
906
+ * ({@link GrantType.ANY_AUTHORISED}) grant surface with `user` matching the
907
+ * everyone-sentinel — test with {@link isEveryoneUserLogin}.
908
+ *
909
+ * Transactional unary op (single response with all grants), so it rides the tx
910
+ * channel and works on every wire protocol — unlike the standalone server-streaming
911
+ * `ListGrants` RPC, which has no REST binding.
912
+ */
913
+ public listGrants(rId: AnyResourceRef): Promise<AuthAPI_Grant[]> {
914
+ return this.sendSingleAndParse(
915
+ { oneofKind: "listGrants", listGrants: this.toSignedResourceId(rId) },
916
+ (r) => r.listGrants.grants,
917
+ );
918
+ }
919
+
820
920
  //
821
921
  // Fields
822
922
  //
@@ -29,6 +29,10 @@ export type SSOAuthMethod = {
29
29
  userIdClaim: string;
30
30
  groupsClaim: string;
31
31
  flowType: SSOFlowType;
32
+ /** OIDC `access_type` auth-request param ("online" | "offline"). Google-specific:
33
+ * "offline" makes Google issue a refresh token; other IdPs ignore it. Absent when
34
+ * the server does not set it — never send an empty string to the IdP. */
35
+ accessType?: string;
32
36
  };
33
37
 
34
38
  /** Server-issued material returned by {@link UnauthenticatedPlClient.beginSSOLogin}.
@@ -37,6 +41,11 @@ export type SSOLoginAttempt = {
37
41
  flow: "public_pkce";
38
42
  nonce: string;
39
43
  expiresAt: Date;
44
+ /** Confidential-client secret for the IdP token exchange; absent for public clients.
45
+ * Google's OIDC has no public-client mode — it requires a client_secret at the token
46
+ * endpoint even with PKCE — so for Google the backend forwards its secret here and the
47
+ * desktop exchanges the code as a confidential client. */
48
+ clientSecret?: string;
40
49
  };
41
50
 
42
51
  /** Primarily used for initial authentication (login) */
@@ -104,6 +113,7 @@ export class UnauthenticatedPlClient {
104
113
  subjectTokenSource: sso.subjectTokenSource,
105
114
  userIdClaim: sso.userIdClaim,
106
115
  groupsClaim: sso.groupsClaim,
116
+ accessType: sso.accessType || undefined,
107
117
  flowType: "public_pkce",
108
118
  };
109
119
  }
@@ -149,8 +159,13 @@ export class UnauthenticatedPlClient {
149
159
  /** Request fresh server-issued login material. v1 only emits the public-PKCE flow;
150
160
  * desktop MUST place the returned `nonce` verbatim into the OIDC auth-request. */
151
161
  public async beginSSOLogin(): Promise<SSOLoginAttempt> {
152
- const { nonce, expiresAt } = await this.ll.beginSSOLogin();
153
- return { flow: "public_pkce", nonce, expiresAt };
162
+ const attempt = await this.ll.beginSSOLogin();
163
+ return {
164
+ flow: "public_pkce",
165
+ nonce: attempt.nonce,
166
+ expiresAt: attempt.expiresAt,
167
+ clientSecret: attempt.clientSecret,
168
+ };
154
169
  }
155
170
 
156
171
  /** Forward the verbatim IdP `/token` response body and receive a Platforma JWT. */
@@ -16,6 +16,26 @@ const AnonymousClientRoot = "AnonymousRoot";
16
16
  const LsStorageTypePrefix = "LS/"; // implements ls API in particular storage
17
17
  const LsProviderFieldPrefix = "storage/"; // provides access to storages list
18
18
 
19
+ /** One recipient of a resource, as returned by {@link UserResources.listGrants}. */
20
+ export interface GrantEntry {
21
+ /** Login the grant targets. An everyone-grant surfaces here as the backend's
22
+ * everyone-sentinel — test with {@link isEveryoneUserLogin} (from "./transaction"); callers
23
+ * map it to "*". */
24
+ readonly user: string;
25
+ /** True for a writable grant (copy / collaboration), false for read-only. */
26
+ readonly writable: boolean;
27
+ /** Login of the user who created the grant. */
28
+ readonly grantedBy: string;
29
+ /** When the grant was created (ms epoch). */
30
+ readonly grantedAt: number;
31
+ }
32
+
33
+ /** One user known to the server, as returned by {@link UserResources.listUsers}. */
34
+ export interface UserEntry {
35
+ /** Stable identifier of the user; the grant target and `GetUserRoot` key. */
36
+ readonly login: string;
37
+ }
38
+
19
39
  /** Information about a single data library (LS storage). */
20
40
  export interface StorageInfo {
21
41
  /** Machine-stable identifier, e.g. "library". Used for filtering and map keys. */
@@ -172,6 +192,71 @@ export class UserResources {
172
192
  return await this.getDataLibrariesViaLegacy();
173
193
  }
174
194
 
195
+ /**
196
+ * Discovers shared resources of a given type granted to the user, as signed resource ids.
197
+ *
198
+ * The public, type-filtered form of {@link getDataLibrariesViaList}: it polls the
199
+ * `ListUserResources` gRPC stream and returns every `sharedResource` entry whose
200
+ * `resourceType.name` matches `resourceTypeName`. Matching is by name only — the
201
+ * version is ignored (permissive, survives schema bumps).
202
+ *
203
+ * gRPC-only: `ListUserResources` throws on a REST-connected client
204
+ * ({@link LLPlClient.listUserResources}), so callers on REST get a thrown error.
205
+ */
206
+ async listSharedResourcesByType(
207
+ resourceTypeName: string,
208
+ opts: { login?: string } = {},
209
+ ): Promise<SignedResourceId[]> {
210
+ const responses = await this.ll.listUserResources({ login: opts.login });
211
+
212
+ const result: SignedResourceId[] = [];
213
+ for (const msg of responses) {
214
+ if (msg.entry.oneofKind !== "sharedResource") continue;
215
+ const sr = msg.entry.sharedResource;
216
+ if (!sr.resourceType) continue;
217
+ if (sr.resourceType.name !== resourceTypeName) continue;
218
+ result.push(createSignedResourceId(sr.resourceId, toResourceSignature(sr.resourceSignature)));
219
+ }
220
+ return result;
221
+ }
222
+
223
+ /**
224
+ * Enumerates the recipients of a single resource — the donor-side "who did I
225
+ * share with" view.
226
+ *
227
+ * Takes a signed, writable resource handle: the backend gates grant listing on a
228
+ * signed resource id with writable permission, so only the resource's owner can
229
+ * call it. An everyone-grant surfaces with `user` matching the backend's
230
+ * everyone-sentinel — test with {@link isEveryoneUserLogin} (from "./transaction");
231
+ * the caller maps that to "*".
232
+ *
233
+ * Runs over a read transaction ({@link PlTransaction.listGrants}), so it works on
234
+ * every wire protocol — including REST, where the standalone server-streaming
235
+ * `ListGrants` RPC has no binding.
236
+ */
237
+ async listGrants(resourceId: SignedResourceId): Promise<GrantEntry[]> {
238
+ const grants = await this.runTx("ListGrants", false, NullSignedResourceId, (tx) =>
239
+ tx.listGrants(resourceId),
240
+ );
241
+ return grants.map((grant) => ({
242
+ user: grant.user,
243
+ writable: grant.permissions?.writable ?? false,
244
+ grantedBy: grant.grantedBy,
245
+ grantedAt: Number(grant.grantedAt),
246
+ }));
247
+ }
248
+
249
+ /**
250
+ * Lists the logins of users known to the server, for the recipient picker. A user becomes
251
+ * known on first login; provisioned users who have never logged in do not appear. gRPC-only
252
+ * (the underlying {@link LLPlClient.listUsers} has no REST binding) — throws on a
253
+ * REST-connected client, so callers gate on the `userListing:v1` capability.
254
+ */
255
+ async listUsers(): Promise<UserEntry[]> {
256
+ const users = await this.ll.listUsers();
257
+ return users.map((user) => ({ login: user.login }));
258
+ }
259
+
175
260
  private async getUserRootViaRpc(opts: {
176
261
  login?: string;
177
262
  createIfNotExists: true;
@@ -10,6 +10,8 @@ import type { MaintenanceAPI_Ping_Response } from "./api";
10
10
  import type { MaintenanceAPI_Ping_Request } from "./api";
11
11
  import type { MiscAPI_ListResourceTypes_Response } from "./api";
12
12
  import type { MiscAPI_ListResourceTypes_Request } from "./api";
13
+ import type { AuthAPI_ListUsers_Response } from "./api";
14
+ import type { AuthAPI_ListUsers_Request } from "./api";
13
15
  import type { AuthAPI_ListUserResources_Response } from "./api";
14
16
  import type { AuthAPI_ListUserResources_Request } from "./api";
15
17
  import type { AuthAPI_GetUserRoot_Response } from "./api";
@@ -284,6 +286,10 @@ export interface IPlatformClient {
284
286
  * @generated from protobuf rpc: ListUserResources
285
287
  */
286
288
  listUserResources(input: AuthAPI_ListUserResources_Request, options?: RpcOptions): ServerStreamingCall<AuthAPI_ListUserResources_Request, AuthAPI_ListUserResources_Response>;
289
+ /**
290
+ * @generated from protobuf rpc: ListUsers
291
+ */
292
+ listUsers(input: AuthAPI_ListUsers_Request, options?: RpcOptions): UnaryCall<AuthAPI_ListUsers_Request, AuthAPI_ListUsers_Response>;
287
293
  /**
288
294
  *
289
295
  * Other stuff
@@ -613,6 +619,13 @@ export class PlatformClient implements IPlatformClient, ServiceInfo {
613
619
  const method = this.methods[33], opt = this._transport.mergeOptions(options);
614
620
  return stackIntercept<AuthAPI_ListUserResources_Request, AuthAPI_ListUserResources_Response>("serverStreaming", this._transport, method, opt, input);
615
621
  }
622
+ /**
623
+ * @generated from protobuf rpc: ListUsers
624
+ */
625
+ listUsers(input: AuthAPI_ListUsers_Request, options?: RpcOptions): UnaryCall<AuthAPI_ListUsers_Request, AuthAPI_ListUsers_Response> {
626
+ const method = this.methods[34], opt = this._transport.mergeOptions(options);
627
+ return stackIntercept<AuthAPI_ListUsers_Request, AuthAPI_ListUsers_Response>("unary", this._transport, method, opt, input);
628
+ }
616
629
  /**
617
630
  *
618
631
  * Other stuff
@@ -621,7 +634,7 @@ export class PlatformClient implements IPlatformClient, ServiceInfo {
621
634
  * @generated from protobuf rpc: ListResourceTypes
622
635
  */
623
636
  listResourceTypes(input: MiscAPI_ListResourceTypes_Request, options?: RpcOptions): UnaryCall<MiscAPI_ListResourceTypes_Request, MiscAPI_ListResourceTypes_Response> {
624
- const method = this.methods[34], opt = this._transport.mergeOptions(options);
637
+ const method = this.methods[35], opt = this._transport.mergeOptions(options);
625
638
  return stackIntercept<MiscAPI_ListResourceTypes_Request, MiscAPI_ListResourceTypes_Response>("unary", this._transport, method, opt, input);
626
639
  }
627
640
  /**
@@ -632,14 +645,14 @@ export class PlatformClient implements IPlatformClient, ServiceInfo {
632
645
  * @generated from protobuf rpc: Ping
633
646
  */
634
647
  ping(input: MaintenanceAPI_Ping_Request, options?: RpcOptions): UnaryCall<MaintenanceAPI_Ping_Request, MaintenanceAPI_Ping_Response> {
635
- const method = this.methods[35], opt = this._transport.mergeOptions(options);
648
+ const method = this.methods[36], opt = this._transport.mergeOptions(options);
636
649
  return stackIntercept<MaintenanceAPI_Ping_Request, MaintenanceAPI_Ping_Response>("unary", this._transport, method, opt, input);
637
650
  }
638
651
  /**
639
652
  * @generated from protobuf rpc: License
640
653
  */
641
654
  license(input: MaintenanceAPI_License_Request, options?: RpcOptions): UnaryCall<MaintenanceAPI_License_Request, MaintenanceAPI_License_Response> {
642
- const method = this.methods[36], opt = this._transport.mergeOptions(options);
655
+ const method = this.methods[37], opt = this._transport.mergeOptions(options);
643
656
  return stackIntercept<MaintenanceAPI_License_Request, MaintenanceAPI_License_Response>("unary", this._transport, method, opt, input);
644
657
  }
645
658
  }