@milaboratories/pl-client 3.3.3 → 3.4.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 (32) hide show
  1. package/dist/core/client.cjs +22 -8
  2. package/dist/core/client.cjs.map +1 -1
  3. package/dist/core/client.d.ts +7 -1
  4. package/dist/core/client.d.ts.map +1 -1
  5. package/dist/core/client.js +22 -8
  6. package/dist/core/client.js.map +1 -1
  7. package/dist/core/ll_client.cjs +2 -2
  8. package/dist/core/ll_client.cjs.map +1 -1
  9. package/dist/core/ll_client.d.ts +1 -1
  10. package/dist/core/ll_client.d.ts.map +1 -1
  11. package/dist/core/ll_client.js +2 -2
  12. package/dist/core/ll_client.js.map +1 -1
  13. package/dist/core/user_resources.cjs +5 -5
  14. package/dist/core/user_resources.cjs.map +1 -1
  15. package/dist/core/user_resources.d.ts +5 -9
  16. package/dist/core/user_resources.d.ts.map +1 -1
  17. package/dist/core/user_resources.js +5 -5
  18. package/dist/core/user_resources.js.map +1 -1
  19. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +6 -6
  20. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
  21. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +2 -2
  22. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
  23. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +6 -6
  24. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  25. package/dist/proto-rest/plapi.d.ts +1 -1
  26. package/dist/proto-rest/plapi.d.ts.map +1 -1
  27. package/package.json +2 -2
  28. package/src/core/client.ts +36 -4
  29. package/src/core/ll_client.ts +3 -3
  30. package/src/core/user_resources.ts +45 -29
  31. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +9 -9
  32. package/src/proto-rest/plapi.ts +1 -1
@@ -382,12 +382,12 @@ var LLPlClient = class LLPlClient {
382
382
  const cl = this.clientProvider.get();
383
383
  if (cl instanceof PlatformClient) return (await cl.getUserRoot({
384
384
  login: opts.login ?? "",
385
- doNotCreate: opts.doNotCreate ?? false
385
+ createIfNotExists: opts.createIfNotExists ?? false
386
386
  })).response;
387
387
  else {
388
388
  const resp = notEmpty((await cl.POST("/v1/auth/user-root", { body: {
389
389
  login: opts.login ?? "",
390
- doNotCreate: opts.doNotCreate ?? false
390
+ createIfNotExists: opts.createIfNotExists ?? false
391
391
  } })).data, "REST: empty response for getUserRoot request");
392
392
  return { userRoot: resp.userRoot ? {
393
393
  resourceId: BigInt(resp.userRoot.resourceId),
@@ -1 +1 @@
1
- {"version":3,"file":"ll_client.js","names":["GrpcPlApiClient","status","GrpcStatus"],"sources":["../../src/core/ll_client.ts"],"sourcesContent":["import { PlatformClient as GrpcPlApiClient } from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client\";\nimport type { ClientOptions, Interceptor } from \"@grpc/grpc-js\";\nimport {\n ChannelCredentials,\n InterceptingCall,\n status as GrpcStatus,\n compressionAlgorithms,\n} from \"@grpc/grpc-js\";\nimport type {\n AuthInformation,\n AuthOps,\n PlClientConfig,\n PlConnectionStatus,\n PlConnectionStatusListener,\n} from \"./config\";\nimport { plAddressToConfig, type wireProtocol, SUPPORTED_WIRE_PROTOCOLS } from \"./config\";\nimport type { GrpcOptions } from \"@protobuf-ts/grpc-transport\";\nimport { GrpcTransport } from \"@protobuf-ts/grpc-transport\";\nimport { LLPlTransaction } from \"./ll_transaction\";\nimport { parsePlJwt } from \"../util/pl\";\nimport { type Dispatcher, interceptors } from \"undici\";\nimport type { Middleware } from \"openapi-fetch\";\nimport { inferAuthRefreshTime } from \"./auth\";\nimport { defaultHttpDispatcher } from \"@milaboratories/pl-http\";\nimport type { WireClientProvider, WireClientProviderFactory, WireConnection } from \"./wire\";\nimport { parseHttpAuth } from \"@milaboratories/pl-model-common\";\nimport type * as grpcTypes from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport {\n type PlApiPaths,\n type PlRestClientType,\n createClient,\n parseResponseError,\n} from \"../proto-rest\";\nimport { notEmpty, retry, withTimeout, type RetryOptions } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\nimport { WebSocketBiDiStream } from \"./websocket_stream\";\nimport {\n AuthAPI_Role,\n TxAPI_ClientMessage,\n TxAPI_ServerMessage,\n} from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { isAbortedError } from \"./errors\";\n\nexport interface PlCallOps {\n timeout?: number;\n abortSignal?: AbortSignal;\n}\n\nclass WireClientProviderImpl<Client> implements WireClientProvider<Client> {\n private client: Client | undefined = undefined;\n\n constructor(\n private readonly wireOpts: () => WireConnection,\n private readonly clientConstructor: (wireOpts: WireConnection) => Client,\n ) {}\n\n public reset(): void {\n this.client = undefined;\n }\n\n public get(): Client {\n if (this.client === undefined) this.client = this.clientConstructor(this.wireOpts());\n return this.client;\n }\n}\n\n/** Abstract out low level networking and authorization details */\nexport class LLPlClient implements WireClientProviderFactory {\n /** Initial authorization information */\n private authInformation?: AuthInformation;\n /** Will be executed by the client when it is required */\n private readonly onAuthUpdate?: (newInfo: AuthInformation) => void;\n /** Will be executed if auth-related error happens during normal client operation */\n private readonly onAuthError?: () => void;\n /** Will be executed by the client when it is required */\n private readonly onAuthRefreshProblem?: (error: unknown) => void;\n /** Threshold after which auth info refresh is required */\n private refreshTimestamp?: number;\n\n private _status: PlConnectionStatus = \"OK\";\n private readonly statusListener?: PlConnectionStatusListener;\n\n private _wireProto: wireProtocol = \"grpc\";\n private _wireConn!: WireConnection;\n\n private readonly _restInterceptors: Dispatcher.DispatcherComposeInterceptor[];\n private readonly _restMiddlewares: Middleware[];\n private readonly _grpcInterceptors: Interceptor[];\n private readonly providers: WeakRef<WireClientProviderImpl<any>>[] = [];\n\n public readonly clientProvider: WireClientProvider<PlRestClientType | GrpcPlApiClient>;\n\n public readonly httpDispatcher: Dispatcher;\n\n public static async build(\n configOrAddress: PlClientConfig | string,\n ops: {\n auth?: AuthOps;\n statusListener?: PlConnectionStatusListener;\n shouldUseGzip?: boolean;\n logger?: MiLogger;\n useAutoDetectWireProtocol?: boolean;\n } = {},\n ) {\n const conf =\n typeof configOrAddress === \"string\" ? plAddressToConfig(configOrAddress) : configOrAddress;\n\n const pl = new LLPlClient(conf, ops);\n\n // FIXME(rfiskov)[MILAB-5275]: Investigate why autodetect randomly fails; temporary turn it off.\n if (ops.useAutoDetectWireProtocol) {\n await pl.detectOptimalWireProtocol();\n }\n return pl;\n }\n\n private constructor(\n public readonly conf: PlClientConfig,\n private readonly ops: {\n auth?: AuthOps;\n statusListener?: PlConnectionStatusListener;\n shouldUseGzip?: boolean;\n logger?: MiLogger;\n } = {},\n ) {\n const { auth, statusListener } = ops;\n\n if (auth !== undefined) {\n this.refreshTimestamp = inferAuthRefreshTime(\n auth.authInformation,\n this.conf.authMaxRefreshSeconds,\n );\n this.authInformation = auth.authInformation;\n this.onAuthUpdate = auth.onUpdate;\n this.onAuthRefreshProblem = auth.onUpdateError;\n this.onAuthError = auth.onAuthError;\n }\n\n this._restInterceptors = [];\n this._restMiddlewares = [];\n this._grpcInterceptors = [];\n\n if (auth !== undefined) {\n this._restInterceptors.push(this.createRestAuthInterceptor());\n this._grpcInterceptors.push(this.createGrpcAuthInterceptor());\n }\n this._restInterceptors.push(interceptors.retry({ statusCodes: [] })); // Handle errors with openapi-fetch middleware.\n this._restMiddlewares.push(this.createRestErrorMiddleware());\n this._grpcInterceptors.push(this.createGrpcErrorInterceptor());\n\n this.httpDispatcher = defaultHttpDispatcher(this.conf.httpProxy);\n if (this.conf.wireProtocol) {\n this._wireProto = this.conf.wireProtocol;\n }\n\n this.initWireConnection(this._wireProto);\n\n if (statusListener !== undefined) {\n this.statusListener = statusListener;\n statusListener(this._status);\n }\n\n this.clientProvider = this.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new GrpcPlApiClient(wireConn.Transport);\n } else {\n return createClient<PlApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\n }\n\n private initWireConnection(protocol: wireProtocol) {\n switch (protocol) {\n case \"rest\":\n this.initRestConnection();\n return;\n case \"grpc\":\n this.initGrpcConnection(this.ops.shouldUseGzip ?? false);\n return;\n default:\n ((v: never) => {\n throw new Error(\n `Unsupported wire protocol '${v as string}'. Use one of: ${SUPPORTED_WIRE_PROTOCOLS.join(\", \")}`,\n );\n })(protocol);\n }\n }\n\n private initRestConnection(): void {\n const dispatcher = defaultHttpDispatcher(this.conf.grpcProxy, this._restInterceptors);\n this._replaceWireConnection({\n type: \"rest\",\n Config: this.conf,\n Dispatcher: dispatcher,\n Middlewares: this._restMiddlewares,\n });\n }\n\n /**\n * Initializes (or reinitializes) _grpcTransport\n * @param gzip - whether to enable gzip compression\n */\n private initGrpcConnection(gzip: boolean) {\n const clientOptions: ClientOptions = {\n \"grpc.keepalive_time_ms\": 30_000, // 30 seconds\n \"grpc.service_config_disable_resolution\": 1, // Disable DNS TXT lookups for service config\n interceptors: this._grpcInterceptors,\n };\n\n if (gzip) clientOptions[\"grpc.default_compression_algorithm\"] = compressionAlgorithms.gzip;\n\n //\n // Leaving it here for now\n // https://github.com/grpc/grpc-node/issues/2788\n //\n // We should implement message pooling algorithm to overcome hardcoded NO_DELAY behaviour\n // of HTTP/2 and allow our small messages to batch together.\n //\n const grpcOptions: GrpcOptions = {\n host: this.conf.hostAndPort,\n timeout: this.conf.defaultRequestTimeout,\n channelCredentials: this.conf.ssl\n ? ChannelCredentials.createSsl()\n : ChannelCredentials.createInsecure(),\n clientOptions,\n };\n\n const grpcProxy =\n typeof this.conf.grpcProxy === \"string\" ? { url: this.conf.grpcProxy } : this.conf.grpcProxy;\n\n if (grpcProxy?.url) {\n const url = new URL(grpcProxy.url);\n if (grpcProxy.auth) {\n const parsed = parseHttpAuth(grpcProxy.auth);\n if (parsed.scheme !== \"Basic\") {\n throw new Error(`Unsupported auth scheme: ${parsed.scheme as string}.`);\n }\n url.username = parsed.username;\n url.password = parsed.password;\n }\n process.env.grpc_proxy = url.toString();\n } else {\n delete process.env.grpc_proxy;\n }\n\n this._replaceWireConnection({ type: \"grpc\", Transport: new GrpcTransport(grpcOptions) });\n }\n\n private _replaceWireConnection(newConn: WireConnection): void {\n const oldConn = this._wireConn;\n this._wireConn = newConn;\n this._wireProto = newConn.type;\n\n // Reset all providers to let them reinitialize their clients\n for (let i = 0; i < this.providers.length; i++) {\n const provider = this.providers[i].deref();\n if (provider === undefined) {\n // at the same time we need to remove providers that are no longer valid\n this.providers.splice(i, 1);\n i--;\n } else {\n provider.reset();\n }\n }\n\n if (oldConn !== undefined && oldConn.type === \"grpc\") oldConn.Transport.close();\n }\n\n private providerCleanupCounter = 0;\n\n /**\n * Creates a provider for a grpc client. Returned provider will create fresh client whenever the underlying transport is reset.\n *\n * @param clientConstructor - a factory function that creates a grpc client\n */\n public createWireClientProvider<Client>(\n clientConstructor: (transport: WireConnection) => Client,\n ): WireClientProvider<Client> {\n // We need to cleanup providers periodically to avoid memory leaks.\n // This is a simple heuristic to avoid memory leaks.\n // We could use a more sophisticated algorithm, but this is good enough for now.\n this.providerCleanupCounter++;\n if (this.providerCleanupCounter >= 16) {\n for (let i = 0; i < this.providers.length; i++) {\n const provider = this.providers[i].deref();\n if (provider === undefined) {\n this.providers.splice(i, 1);\n i--;\n }\n }\n this.providerCleanupCounter = 0;\n }\n\n const provider = new WireClientProviderImpl<Client>(() => this._wireConn, clientConstructor);\n this.providers.push(new WeakRef(provider));\n return provider;\n }\n\n public get wireConnection(): WireConnection {\n return this._wireConn;\n }\n\n public get wireProtocol(): wireProtocol | undefined {\n return this._wireProto;\n }\n\n /** Returns true if client is authenticated. Even with anonymous auth information\n * connection is considered authenticated. Unauthenticated clients are used for\n * login and similar tasks, see {@link UnauthenticatedPlClient}. */\n public get authenticated(): boolean {\n return this.authInformation !== undefined;\n }\n\n /** null means anonymous connection */\n public get authUser(): string | null {\n if (!this.authenticated) throw new Error(\"Client is not authenticated\");\n if (this.authInformation?.jwtToken)\n return parsePlJwt(this.authInformation?.jwtToken).user.login;\n else return null;\n }\n\n private updateStatus(newStatus: PlConnectionStatus) {\n process.nextTick(() => {\n if (this._status !== newStatus) {\n this._status = newStatus;\n if (this.statusListener !== undefined) this.statusListener(this._status);\n if (newStatus === \"Unauthenticated\" && this.onAuthError !== undefined) this.onAuthError();\n }\n });\n }\n\n public get status(): PlConnectionStatus {\n return this._status;\n }\n\n private authRefreshInProgress: boolean = false;\n\n private refreshAuthInformationIfNeeded(): void {\n if (\n this.refreshTimestamp === undefined ||\n Date.now() < this.refreshTimestamp ||\n this.authRefreshInProgress ||\n this._status === \"Unauthenticated\"\n )\n return;\n\n // Running refresh in background`\n this.authRefreshInProgress = true;\n void (async () => {\n try {\n const token = await this.getJwtToken(BigInt(this.conf.authTTLSeconds));\n this.authInformation = { jwtToken: token };\n this.refreshTimestamp = inferAuthRefreshTime(\n this.authInformation,\n this.conf.authMaxRefreshSeconds,\n );\n if (this.onAuthUpdate) this.onAuthUpdate(this.authInformation);\n } catch (e: unknown) {\n if (this.onAuthRefreshProblem) this.onAuthRefreshProblem(e);\n } finally {\n this.authRefreshInProgress = false;\n }\n })();\n }\n\n /**\n * Creates middleware that parses error responses and handles them centrally.\n * This middleware runs before openapi-fetch parses the response, so we need to\n * manually parse the response body for error responses.\n */\n private createRestErrorMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const { body, ...resOptions } = response;\n\n if ([502, 503, 504].includes(response.status)) {\n // Service unavailable, bad gateway, gateway timeout\n this.updateStatus(\"Disconnected\");\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n // No error: nice!\n return new Response(respErr.origBody ?? body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === \"string\") {\n // Non-standard error or normal response: let later middleware to deal wit it.\n return new Response(respErr.error, { ...resOptions, status: response.status });\n }\n\n if (respErr.error.code === Code.UNAUTHENTICATED) {\n this.updateStatus(\"Unauthenticated\");\n }\n\n // Let later middleware to deal with standard gRPC error.\n return new Response(respErr.origBody, { ...resOptions, status: response.status });\n },\n };\n }\n\n /** Detects certain errors and update client status accordingly when using GRPC wire connection */\n private createGrpcErrorInterceptor(): Interceptor {\n return (options, nextCall) => {\n return new InterceptingCall(nextCall(options), {\n start: (metadata, listener, next) => {\n next(metadata, {\n onReceiveStatus: (status, next) => {\n if (status.code == GrpcStatus.UNAUTHENTICATED)\n // (!!!) don't change to \"===\"\n this.updateStatus(\"Unauthenticated\");\n if (status.code == GrpcStatus.UNAVAILABLE)\n // (!!!) don't change to \"===\"\n this.updateStatus(\"Disconnected\");\n next(status);\n },\n });\n },\n });\n };\n }\n\n private createRestAuthInterceptor(): Dispatcher.DispatcherComposeInterceptor {\n return (dispatch) => {\n return (options, handler) => {\n if (this.authInformation?.jwtToken !== undefined) {\n // TODO: check this magic really works and gets called\n options.headers = {\n ...options.headers,\n authorization: \"Bearer \" + this.authInformation.jwtToken,\n };\n this.refreshAuthInformationIfNeeded();\n }\n\n return dispatch(options, handler);\n };\n };\n }\n\n /** Injects authentication information if needed */\n private createGrpcAuthInterceptor(): Interceptor {\n return (options, nextCall) => {\n return new InterceptingCall(nextCall(options), {\n start: (metadata, listener, next) => {\n if (this.authInformation?.jwtToken !== undefined) {\n metadata.set(\"authorization\", \"Bearer \" + this.authInformation.jwtToken);\n this.refreshAuthInformationIfNeeded();\n next(metadata, listener);\n } else {\n next(metadata, listener);\n }\n },\n });\n };\n }\n\n public async getJwtToken(\n ttlSeconds: bigint,\n options?: { authorization?: string; role?: AuthAPI_Role },\n ): Promise<string> {\n const cl = this.clientProvider.get();\n const role = options?.role ?? AuthAPI_Role.UNSPECIFIED;\n\n if (cl instanceof GrpcPlApiClient) {\n const meta: Record<string, string> = {};\n if (options?.authorization) meta.authorization = options.authorization;\n return (\n await cl.getJWTToken(\n {\n expiration: { seconds: ttlSeconds, nanos: 0 },\n requestedRole: role,\n },\n { meta },\n ).response\n ).token;\n } else {\n const headers: Record<string, string> = {};\n if (options?.authorization) headers.authorization = options.authorization;\n const resp = cl.POST(\"/v1/auth/jwt-token\", {\n body: { expiration: `${ttlSeconds}s`, requestedRole: role },\n headers,\n });\n return notEmpty((await resp).data, \"REST: empty response for JWT token request\").token;\n }\n }\n\n public async ping(): Promise<grpcTypes.MaintenanceAPI_Ping_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.ping({})).response;\n } else {\n return notEmpty((await cl.GET(\"/v1/ping\")).data, \"REST: empty response for ping request\");\n }\n }\n\n /**\n * Detects the best available wire protocol.\n * If wireProtocol is explicitly configured, does nothing.\n * Otherwise probes the current protocol via ping; if it fails, switches to the alternative.\n */\n private async detectOptimalWireProtocol() {\n if (this.conf.wireProtocol) {\n return;\n }\n\n // Each retry is:\n // - ping request timeout (100 to 3_000ms)\n // - backoff delay (30 to 500ms)\n //\n // 30 attempts are ~43 seconds of overall waiting time.\n // Think twice on overall time this thing takes to complete when changing these parameters.\n // It may block UI when connecting to the server and loading projects list.\n const pingTimeoutFactor = 1.3;\n const maxPingTimeoutMs = 3_000;\n const retryOptions: RetryOptions = {\n type: \"exponentialBackoff\",\n maxAttempts: 30,\n initialDelay: 30,\n backoffMultiplier: 1.3,\n jitter: 0.2,\n maxDelay: 500,\n };\n\n let attempt = 1;\n let pingTimeoutMs = 100;\n await retry(\n () => withTimeout(this.ping(), pingTimeoutMs),\n retryOptions,\n (e: unknown) => {\n if (isAbortedError(e)) {\n this.ops.logger?.info(\n `Wire proto autodetect: ping timed out after ${pingTimeoutMs}ms: attempt=${attempt}, wire=${this._wireProto}`,\n );\n\n if (attempt % 2 === 0) {\n // We have 2 wire protocols to check. Increase timeout each 2 attempts.\n pingTimeoutMs = Math.min(\n Math.round(pingTimeoutMs * pingTimeoutFactor),\n maxPingTimeoutMs,\n );\n }\n } else {\n this.ops.logger?.info(\n `Wire proto autodetect: ping failed: attempt=${attempt}, wire=${this._wireProto}, err=${String(e)}`,\n );\n }\n\n attempt++;\n const protocol = this._wireProto === \"grpc\" ? \"rest\" : \"grpc\";\n this.ops.logger?.info(\n `Wire protocol autodetect next attempt: will try wire '${protocol}' with timeout ${pingTimeoutMs}ms`,\n );\n this.initWireConnection(protocol);\n return true;\n },\n );\n }\n\n public async license(): Promise<grpcTypes.MaintenanceAPI_License_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.license({})).response;\n } else {\n const resp = notEmpty(\n (await cl.GET(\"/v1/license\")).data,\n \"REST: empty response for license request\",\n );\n return {\n status: resp.status,\n isOk: resp.isOk,\n responseBody: Uint8Array.from(Buffer.from(resp.responseBody)),\n };\n }\n }\n\n public async authMethods(): Promise<grpcTypes.AuthAPI_ListMethods_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.authMethods({})).response;\n } else {\n return notEmpty(\n (await cl.GET(\"/v1/auth/methods\")).data,\n \"REST: empty response for auth methods request\",\n );\n }\n }\n\n public async getUserRoot(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<grpcTypes.AuthAPI_GetUserRoot_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (\n await cl.getUserRoot({\n login: opts.login ?? \"\",\n doNotCreate: opts.doNotCreate ?? false,\n })\n ).response;\n } else {\n const resp = notEmpty(\n (\n await cl.POST(\"/v1/auth/user-root\", {\n body: {\n login: opts.login ?? \"\",\n doNotCreate: opts.doNotCreate ?? false,\n },\n })\n ).data,\n \"REST: empty response for getUserRoot request\",\n );\n return {\n userRoot: resp.userRoot\n ? {\n resourceId: BigInt(resp.userRoot.resourceId),\n resourceSignature: Uint8Array.from(\n Buffer.from(resp.userRoot.resourceSignature, \"base64\"),\n ),\n }\n : undefined,\n };\n }\n }\n\n public async listUserResources(\n opts: { login?: string; startFrom?: bigint; limit?: number } = {},\n ): Promise<grpcTypes.AuthAPI_ListUserResources_Response[]> {\n const cl = this.clientProvider.get();\n\n if (!(cl instanceof GrpcPlApiClient)) {\n throw new Error(\"ListUserResources requires gRPC wire protocol; REST is not supported\");\n }\n\n const call = cl.listUserResources({\n login: opts.login ?? \"\",\n startFrom: opts.startFrom ?? 0n,\n limit: opts.limit ?? 0,\n });\n const responses: grpcTypes.AuthAPI_ListUserResources_Response[] = [];\n for await (const msg of call.responses) {\n responses.push(msg);\n }\n return responses;\n }\n\n public async txSync(txId: bigint): Promise<void> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n await cl.txSync({ txId: BigInt(txId) });\n } else {\n await cl.POST(\"/v1/tx-sync\", { body: { txId: txId.toString() } });\n }\n }\n\n createTx(rw: boolean, ops: PlCallOps = {}): LLPlTransaction {\n return new LLPlTransaction((abortSignal) => {\n let totalAbortSignal = abortSignal;\n if (ops.abortSignal) totalAbortSignal = AbortSignal.any([totalAbortSignal, ops.abortSignal]);\n\n const timeout =\n ops.timeout ??\n (rw ? this.conf.defaultRWTransactionTimeout : this.conf.defaultROTransactionTimeout);\n\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return cl.tx({\n abort: totalAbortSignal,\n timeout,\n });\n }\n\n const wireConn = this.wireConnection;\n if (wireConn.type === \"rest\") {\n // For REST/WebSocket protocol, timeout needs to be converted to AbortSignal\n if (timeout !== undefined) {\n totalAbortSignal = AbortSignal.any([totalAbortSignal, AbortSignal.timeout(timeout)]);\n }\n\n // The gRPC transport has the auth interceptor that already handles it, but here we need to refresh the auth information to be safe.\n this.refreshAuthInformationIfNeeded();\n\n const wsUrl = this.conf.ssl\n ? `wss://${this.conf.hostAndPort}/v1/ws/tx`\n : `ws://${this.conf.hostAndPort}/v1/ws/tx`;\n\n return new WebSocketBiDiStream(\n wsUrl,\n (msg) => TxAPI_ClientMessage.toBinary(msg),\n (data) => TxAPI_ServerMessage.fromBinary(new Uint8Array(data)),\n {\n abortSignal: totalAbortSignal,\n jwtToken: this.authInformation?.jwtToken,\n dispatcher: wireConn.Dispatcher,\n\n onComplete: async (stream) =>\n stream.requests.send({\n // Ask server to gracefully close the stream on its side, if not done yet.\n requestId: 0,\n request: { oneofKind: \"streamClose\", streamClose: {} },\n }),\n },\n );\n }\n\n throw new Error(`transactions are not supported for wire protocol ${this._wireProto}`);\n });\n }\n\n /** Closes underlying transport */\n public async close() {\n if (this.wireConnection.type === \"grpc\") {\n this.wireConnection.Transport.close();\n } else {\n // TODO: close all WS connections\n }\n await this.httpDispatcher.destroy();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiDA,IAAM,yBAAN,MAA2E;CACzE,SAAqC,KAAA;CAErC,YACE,UACA,mBACA;AAFiB,OAAA,WAAA;AACA,OAAA,oBAAA;;CAGnB,QAAqB;AACnB,OAAK,SAAS,KAAA;;CAGhB,MAAqB;AACnB,MAAI,KAAK,WAAW,KAAA,EAAW,MAAK,SAAS,KAAK,kBAAkB,KAAK,UAAU,CAAC;AACpF,SAAO,KAAK;;;;AAKhB,IAAa,aAAb,MAAa,WAAgD;;CAE3D;;CAEA;;CAEA;;CAEA;;CAEA;CAEA,UAAsC;CACtC;CAEA,aAAmC;CACnC;CAEA;CACA;CACA;CACA,YAAqE,EAAE;CAEvE;CAEA;CAEA,aAAoB,MAClB,iBACA,MAMI,EAAE,EACN;EAIA,MAAM,KAAK,IAAI,WAFb,OAAO,oBAAoB,WAAW,kBAAkB,gBAAgB,GAAG,iBAE7C,IAAI;AAGpC,MAAI,IAAI,0BACN,OAAM,GAAG,2BAA2B;AAEtC,SAAO;;CAGT,YACE,MACA,MAKI,EAAE,EACN;AAPgB,OAAA,OAAA;AACC,OAAA,MAAA;EAOjB,MAAM,EAAE,MAAM,mBAAmB;AAEjC,MAAI,SAAS,KAAA,GAAW;AACtB,QAAK,mBAAmB,qBACtB,KAAK,iBACL,KAAK,KAAK,sBACX;AACD,QAAK,kBAAkB,KAAK;AAC5B,QAAK,eAAe,KAAK;AACzB,QAAK,uBAAuB,KAAK;AACjC,QAAK,cAAc,KAAK;;AAG1B,OAAK,oBAAoB,EAAE;AAC3B,OAAK,mBAAmB,EAAE;AAC1B,OAAK,oBAAoB,EAAE;AAE3B,MAAI,SAAS,KAAA,GAAW;AACtB,QAAK,kBAAkB,KAAK,KAAK,2BAA2B,CAAC;AAC7D,QAAK,kBAAkB,KAAK,KAAK,2BAA2B,CAAC;;AAE/D,OAAK,kBAAkB,KAAK,aAAa,MAAM,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC;AACpE,OAAK,iBAAiB,KAAK,KAAK,2BAA2B,CAAC;AAC5D,OAAK,kBAAkB,KAAK,KAAK,4BAA4B,CAAC;AAE9D,OAAK,iBAAiB,sBAAsB,KAAK,KAAK,UAAU;AAChE,MAAI,KAAK,KAAK,aACZ,MAAK,aAAa,KAAK,KAAK;AAG9B,OAAK,mBAAmB,KAAK,WAAW;AAExC,MAAI,mBAAmB,KAAA,GAAW;AAChC,QAAK,iBAAiB;AACtB,kBAAe,KAAK,QAAQ;;AAG9B,OAAK,iBAAiB,KAAK,0BAA0B,aAAa;AAChE,OAAI,SAAS,SAAS,OACpB,QAAO,IAAIA,eAAgB,SAAS,UAAU;OAE9C,QAAO,aAAyB;IAC9B,aAAa,SAAS,OAAO;IAC7B,KAAK,SAAS,OAAO;IACrB,YAAY,SAAS;IACrB,aAAa,SAAS;IACvB,CAAC;IAEJ;;CAGJ,mBAA2B,UAAwB;AACjD,UAAQ,UAAR;GACE,KAAK;AACH,SAAK,oBAAoB;AACzB;GACF,KAAK;AACH,SAAK,mBAAmB,KAAK,IAAI,iBAAiB,MAAM;AACxD;GACF,QACE,GAAE,MAAa;AACb,UAAM,IAAI,MACR,8BAA8B,EAAY,iBAAiB,yBAAyB,KAAK,KAAK,GAC/F;MACA,SAAS;;;CAIlB,qBAAmC;EACjC,MAAM,aAAa,sBAAsB,KAAK,KAAK,WAAW,KAAK,kBAAkB;AACrF,OAAK,uBAAuB;GAC1B,MAAM;GACN,QAAQ,KAAK;GACb,YAAY;GACZ,aAAa,KAAK;GACnB,CAAC;;;;;;CAOJ,mBAA2B,MAAe;EACxC,MAAM,gBAA+B;GACnC,0BAA0B;GAC1B,0CAA0C;GAC1C,cAAc,KAAK;GACpB;AAED,MAAI,KAAM,eAAc,wCAAwC,sBAAsB;EAStF,MAAM,cAA2B;GAC/B,MAAM,KAAK,KAAK;GAChB,SAAS,KAAK,KAAK;GACnB,oBAAoB,KAAK,KAAK,MAC1B,mBAAmB,WAAW,GAC9B,mBAAmB,gBAAgB;GACvC;GACD;EAED,MAAM,YACJ,OAAO,KAAK,KAAK,cAAc,WAAW,EAAE,KAAK,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK;AAErF,MAAI,WAAW,KAAK;GAClB,MAAM,MAAM,IAAI,IAAI,UAAU,IAAI;AAClC,OAAI,UAAU,MAAM;IAClB,MAAM,SAAS,cAAc,UAAU,KAAK;AAC5C,QAAI,OAAO,WAAW,QACpB,OAAM,IAAI,MAAM,4BAA4B,OAAO,OAAiB,GAAG;AAEzE,QAAI,WAAW,OAAO;AACtB,QAAI,WAAW,OAAO;;AAExB,WAAQ,IAAI,aAAa,IAAI,UAAU;QAEvC,QAAO,QAAQ,IAAI;AAGrB,OAAK,uBAAuB;GAAE,MAAM;GAAQ,WAAW,IAAI,cAAc,YAAY;GAAE,CAAC;;CAG1F,uBAA+B,SAA+B;EAC5D,MAAM,UAAU,KAAK;AACrB,OAAK,YAAY;AACjB,OAAK,aAAa,QAAQ;AAG1B,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,UAAU,QAAQ,KAAK;GAC9C,MAAM,WAAW,KAAK,UAAU,GAAG,OAAO;AAC1C,OAAI,aAAa,KAAA,GAAW;AAE1B,SAAK,UAAU,OAAO,GAAG,EAAE;AAC3B;SAEA,UAAS,OAAO;;AAIpB,MAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,OAAQ,SAAQ,UAAU,OAAO;;CAGjF,yBAAiC;;;;;;CAOjC,yBACE,mBAC4B;AAI5B,OAAK;AACL,MAAI,KAAK,0BAA0B,IAAI;AACrC,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,UAAU,QAAQ,IAEzC,KADiB,KAAK,UAAU,GAAG,OAAO,KACzB,KAAA,GAAW;AAC1B,SAAK,UAAU,OAAO,GAAG,EAAE;AAC3B;;AAGJ,QAAK,yBAAyB;;EAGhC,MAAM,WAAW,IAAI,6BAAqC,KAAK,WAAW,kBAAkB;AAC5F,OAAK,UAAU,KAAK,IAAI,QAAQ,SAAS,CAAC;AAC1C,SAAO;;CAGT,IAAW,iBAAiC;AAC1C,SAAO,KAAK;;CAGd,IAAW,eAAyC;AAClD,SAAO,KAAK;;;;;CAMd,IAAW,gBAAyB;AAClC,SAAO,KAAK,oBAAoB,KAAA;;;CAIlC,IAAW,WAA0B;AACnC,MAAI,CAAC,KAAK,cAAe,OAAM,IAAI,MAAM,8BAA8B;AACvE,MAAI,KAAK,iBAAiB,SACxB,QAAO,WAAW,KAAK,iBAAiB,SAAS,CAAC,KAAK;MACpD,QAAO;;CAGd,aAAqB,WAA+B;AAClD,UAAQ,eAAe;AACrB,OAAI,KAAK,YAAY,WAAW;AAC9B,SAAK,UAAU;AACf,QAAI,KAAK,mBAAmB,KAAA,EAAW,MAAK,eAAe,KAAK,QAAQ;AACxE,QAAI,cAAc,qBAAqB,KAAK,gBAAgB,KAAA,EAAW,MAAK,aAAa;;IAE3F;;CAGJ,IAAW,SAA6B;AACtC,SAAO,KAAK;;CAGd,wBAAyC;CAEzC,iCAA+C;AAC7C,MACE,KAAK,qBAAqB,KAAA,KAC1B,KAAK,KAAK,GAAG,KAAK,oBAClB,KAAK,yBACL,KAAK,YAAY,kBAEjB;AAGF,OAAK,wBAAwB;AAC7B,GAAM,YAAY;AAChB,OAAI;AAEF,SAAK,kBAAkB,EAAE,UADX,MAAM,KAAK,YAAY,OAAO,KAAK,KAAK,eAAe,CAAC,EAC5B;AAC1C,SAAK,mBAAmB,qBACtB,KAAK,iBACL,KAAK,KAAK,sBACX;AACD,QAAI,KAAK,aAAc,MAAK,aAAa,KAAK,gBAAgB;YACvD,GAAY;AACnB,QAAI,KAAK,qBAAsB,MAAK,qBAAqB,EAAE;aACnD;AACR,SAAK,wBAAwB;;MAE7B;;;;;;;CAQN,4BAAgD;AAC9C,SAAO,EACL,YAAY,OAAO,EAAE,SAAS,UAAU,UAAU,SAAS,eAAe;GACxE,MAAM,EAAE,MAAM,GAAG,eAAe;AAEhC,OAAI;IAAC;IAAK;IAAK;IAAI,CAAC,SAAS,SAAS,OAAO,EAAE;AAE7C,SAAK,aAAa,eAAe;AACjC,WAAO,IAAI,SAAS,MAAM;KAAE,GAAG;KAAY,QAAQ,SAAS;KAAQ,CAAC;;GAGvE,MAAM,UAAU,MAAM,mBAAmB,SAAS;AAClD,OAAI,CAAC,QAAQ,MAEX,QAAO,IAAI,SAAS,QAAQ,YAAY,MAAM;IAAE,GAAG;IAAY,QAAQ,SAAS;IAAQ,CAAC;AAG3F,OAAI,OAAO,QAAQ,UAAU,SAE3B,QAAO,IAAI,SAAS,QAAQ,OAAO;IAAE,GAAG;IAAY,QAAQ,SAAS;IAAQ,CAAC;AAGhF,OAAI,QAAQ,MAAM,SAAS,KAAK,gBAC9B,MAAK,aAAa,kBAAkB;AAItC,UAAO,IAAI,SAAS,QAAQ,UAAU;IAAE,GAAG;IAAY,QAAQ,SAAS;IAAQ,CAAC;KAEpF;;;CAIH,6BAAkD;AAChD,UAAQ,SAAS,aAAa;AAC5B,UAAO,IAAI,iBAAiB,SAAS,QAAQ,EAAE,EAC7C,QAAQ,UAAU,UAAU,SAAS;AACnC,SAAK,UAAU,EACb,kBAAkB,UAAQ,SAAS;AACjC,SAAIC,SAAO,QAAQC,OAAW,gBAE5B,MAAK,aAAa,kBAAkB;AACtC,SAAID,SAAO,QAAQC,OAAW,YAE5B,MAAK,aAAa,eAAe;AACnC,UAAKD,SAAO;OAEf,CAAC;MAEL,CAAC;;;CAIN,4BAA6E;AAC3E,UAAQ,aAAa;AACnB,WAAQ,SAAS,YAAY;AAC3B,QAAI,KAAK,iBAAiB,aAAa,KAAA,GAAW;AAEhD,aAAQ,UAAU;MAChB,GAAG,QAAQ;MACX,eAAe,YAAY,KAAK,gBAAgB;MACjD;AACD,UAAK,gCAAgC;;AAGvC,WAAO,SAAS,SAAS,QAAQ;;;;;CAMvC,4BAAiD;AAC/C,UAAQ,SAAS,aAAa;AAC5B,UAAO,IAAI,iBAAiB,SAAS,QAAQ,EAAE,EAC7C,QAAQ,UAAU,UAAU,SAAS;AACnC,QAAI,KAAK,iBAAiB,aAAa,KAAA,GAAW;AAChD,cAAS,IAAI,iBAAiB,YAAY,KAAK,gBAAgB,SAAS;AACxE,UAAK,gCAAgC;AACrC,UAAK,UAAU,SAAS;UAExB,MAAK,UAAU,SAAS;MAG7B,CAAC;;;CAIN,MAAa,YACX,YACA,SACiB;EACjB,MAAM,KAAK,KAAK,eAAe,KAAK;EACpC,MAAM,OAAO,SAAS,QAAQ,aAAa;AAE3C,MAAI,cAAcD,gBAAiB;GACjC,MAAM,OAA+B,EAAE;AACvC,OAAI,SAAS,cAAe,MAAK,gBAAgB,QAAQ;AACzD,WACE,MAAM,GAAG,YACP;IACE,YAAY;KAAE,SAAS;KAAY,OAAO;KAAG;IAC7C,eAAe;IAChB,EACD,EAAE,MAAM,CACT,CAAC,UACF;SACG;GACL,MAAM,UAAkC,EAAE;AAC1C,OAAI,SAAS,cAAe,SAAQ,gBAAgB,QAAQ;AAK5D,UAAO,UAAU,MAJJ,GAAG,KAAK,sBAAsB;IACzC,MAAM;KAAE,YAAY,GAAG,WAAW;KAAI,eAAe;KAAM;IAC3D;IACD,CAAC,EAC2B,MAAM,6CAA6C,CAAC;;;CAIrF,MAAa,OAAwD;EACnE,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,MAAI,cAAcA,eAChB,SAAQ,MAAM,GAAG,KAAK,EAAE,CAAC,EAAE;MAE3B,QAAO,UAAU,MAAM,GAAG,IAAI,WAAW,EAAE,MAAM,wCAAwC;;;;;;;CAS7F,MAAc,4BAA4B;AACxC,MAAI,KAAK,KAAK,aACZ;EAUF,MAAM,oBAAoB;EAC1B,MAAM,mBAAmB;EACzB,MAAM,eAA6B;GACjC,MAAM;GACN,aAAa;GACb,cAAc;GACd,mBAAmB;GACnB,QAAQ;GACR,UAAU;GACX;EAED,IAAI,UAAU;EACd,IAAI,gBAAgB;AACpB,QAAM,YACE,YAAY,KAAK,MAAM,EAAE,cAAc,EAC7C,eACC,MAAe;AACd,OAAI,eAAe,EAAE,EAAE;AACrB,SAAK,IAAI,QAAQ,KACf,+CAA+C,cAAc,cAAc,QAAQ,SAAS,KAAK,aAClG;AAED,QAAI,UAAU,MAAM,EAElB,iBAAgB,KAAK,IACnB,KAAK,MAAM,gBAAgB,kBAAkB,EAC7C,iBACD;SAGH,MAAK,IAAI,QAAQ,KACf,+CAA+C,QAAQ,SAAS,KAAK,WAAW,QAAQ,OAAO,EAAE,GAClG;AAGH;GACA,MAAM,WAAW,KAAK,eAAe,SAAS,SAAS;AACvD,QAAK,IAAI,QAAQ,KACf,yDAAyD,SAAS,iBAAiB,cAAc,IAClG;AACD,QAAK,mBAAmB,SAAS;AACjC,UAAO;IAEV;;CAGH,MAAa,UAA8D;EACzE,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,MAAI,cAAcA,eAChB,SAAQ,MAAM,GAAG,QAAQ,EAAE,CAAC,EAAE;OACzB;GACL,MAAM,OAAO,UACV,MAAM,GAAG,IAAI,cAAc,EAAE,MAC9B,2CACD;AACD,UAAO;IACL,QAAQ,KAAK;IACb,MAAM,KAAK;IACX,cAAc,WAAW,KAAK,OAAO,KAAK,KAAK,aAAa,CAAC;IAC9D;;;CAIL,MAAa,cAA+D;EAC1E,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,MAAI,cAAcA,eAChB,SAAQ,MAAM,GAAG,YAAY,EAAE,CAAC,EAAE;MAElC,QAAO,UACJ,MAAM,GAAG,IAAI,mBAAmB,EAAE,MACnC,gDACD;;CAIL,MAAa,YACX,OAAkD,EAAE,EACH;EACjD,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,MAAI,cAAcA,eAChB,SACE,MAAM,GAAG,YAAY;GACnB,OAAO,KAAK,SAAS;GACrB,aAAa,KAAK,eAAe;GAClC,CAAC,EACF;OACG;GACL,MAAM,OAAO,UAET,MAAM,GAAG,KAAK,sBAAsB,EAClC,MAAM;IACJ,OAAO,KAAK,SAAS;IACrB,aAAa,KAAK,eAAe;IAClC,EACF,CAAC,EACF,MACF,+CACD;AACD,UAAO,EACL,UAAU,KAAK,WACX;IACE,YAAY,OAAO,KAAK,SAAS,WAAW;IAC5C,mBAAmB,WAAW,KAC5B,OAAO,KAAK,KAAK,SAAS,mBAAmB,SAAS,CACvD;IACF,GACD,KAAA,GACL;;;CAIL,MAAa,kBACX,OAA+D,EAAE,EACR;EACzD,MAAM,KAAK,KAAK,eAAe,KAAK;AAEpC,MAAI,EAAE,cAAcA,gBAClB,OAAM,IAAI,MAAM,uEAAuE;EAGzF,MAAM,OAAO,GAAG,kBAAkB;GAChC,OAAO,KAAK,SAAS;GACrB,WAAW,KAAK,aAAa;GAC7B,OAAO,KAAK,SAAS;GACtB,CAAC;EACF,MAAM,YAA4D,EAAE;AACpE,aAAW,MAAM,OAAO,KAAK,UAC3B,WAAU,KAAK,IAAI;AAErB,SAAO;;CAGT,MAAa,OAAO,MAA6B;EAC/C,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,MAAI,cAAcA,eAChB,OAAM,GAAG,OAAO,EAAE,MAAM,OAAO,KAAK,EAAE,CAAC;MAEvC,OAAM,GAAG,KAAK,eAAe,EAAE,MAAM,EAAE,MAAM,KAAK,UAAU,EAAE,EAAE,CAAC;;CAIrE,SAAS,IAAa,MAAiB,EAAE,EAAmB;AAC1D,SAAO,IAAI,iBAAiB,gBAAgB;GAC1C,IAAI,mBAAmB;AACvB,OAAI,IAAI,YAAa,oBAAmB,YAAY,IAAI,CAAC,kBAAkB,IAAI,YAAY,CAAC;GAE5F,MAAM,UACJ,IAAI,YACH,KAAK,KAAK,KAAK,8BAA8B,KAAK,KAAK;GAE1D,MAAM,KAAK,KAAK,eAAe,KAAK;AACpC,OAAI,cAAcA,eAChB,QAAO,GAAG,GAAG;IACX,OAAO;IACP;IACD,CAAC;GAGJ,MAAM,WAAW,KAAK;AACtB,OAAI,SAAS,SAAS,QAAQ;AAE5B,QAAI,YAAY,KAAA,EACd,oBAAmB,YAAY,IAAI,CAAC,kBAAkB,YAAY,QAAQ,QAAQ,CAAC,CAAC;AAItF,SAAK,gCAAgC;AAMrC,WAAO,IAAI,oBAJG,KAAK,KAAK,MACpB,SAAS,KAAK,KAAK,YAAY,aAC/B,QAAQ,KAAK,KAAK,YAAY,aAI/B,QAAQ,oBAAoB,SAAS,IAAI,GACzC,SAAS,oBAAoB,WAAW,IAAI,WAAW,KAAK,CAAC,EAC9D;KACE,aAAa;KACb,UAAU,KAAK,iBAAiB;KAChC,YAAY,SAAS;KAErB,YAAY,OAAO,WACjB,OAAO,SAAS,KAAK;MAEnB,WAAW;MACX,SAAS;OAAE,WAAW;OAAe,aAAa,EAAE;OAAE;MACvD,CAAC;KACL,CACF;;AAGH,SAAM,IAAI,MAAM,oDAAoD,KAAK,aAAa;IACtF;;;CAIJ,MAAa,QAAQ;AACnB,MAAI,KAAK,eAAe,SAAS,OAC/B,MAAK,eAAe,UAAU,OAAO;AAIvC,QAAM,KAAK,eAAe,SAAS"}
1
+ {"version":3,"file":"ll_client.js","names":["GrpcPlApiClient","status","GrpcStatus"],"sources":["../../src/core/ll_client.ts"],"sourcesContent":["import { PlatformClient as GrpcPlApiClient } from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client\";\nimport type { ClientOptions, Interceptor } from \"@grpc/grpc-js\";\nimport {\n ChannelCredentials,\n InterceptingCall,\n status as GrpcStatus,\n compressionAlgorithms,\n} from \"@grpc/grpc-js\";\nimport type {\n AuthInformation,\n AuthOps,\n PlClientConfig,\n PlConnectionStatus,\n PlConnectionStatusListener,\n} from \"./config\";\nimport { plAddressToConfig, type wireProtocol, SUPPORTED_WIRE_PROTOCOLS } from \"./config\";\nimport type { GrpcOptions } from \"@protobuf-ts/grpc-transport\";\nimport { GrpcTransport } from \"@protobuf-ts/grpc-transport\";\nimport { LLPlTransaction } from \"./ll_transaction\";\nimport { parsePlJwt } from \"../util/pl\";\nimport { type Dispatcher, interceptors } from \"undici\";\nimport type { Middleware } from \"openapi-fetch\";\nimport { inferAuthRefreshTime } from \"./auth\";\nimport { defaultHttpDispatcher } from \"@milaboratories/pl-http\";\nimport type { WireClientProvider, WireClientProviderFactory, WireConnection } from \"./wire\";\nimport { parseHttpAuth } from \"@milaboratories/pl-model-common\";\nimport type * as grpcTypes from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport {\n type PlApiPaths,\n type PlRestClientType,\n createClient,\n parseResponseError,\n} from \"../proto-rest\";\nimport { notEmpty, retry, withTimeout, type RetryOptions } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\nimport { WebSocketBiDiStream } from \"./websocket_stream\";\nimport {\n AuthAPI_Role,\n TxAPI_ClientMessage,\n TxAPI_ServerMessage,\n} from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { isAbortedError } from \"./errors\";\n\nexport interface PlCallOps {\n timeout?: number;\n abortSignal?: AbortSignal;\n}\n\nclass WireClientProviderImpl<Client> implements WireClientProvider<Client> {\n private client: Client | undefined = undefined;\n\n constructor(\n private readonly wireOpts: () => WireConnection,\n private readonly clientConstructor: (wireOpts: WireConnection) => Client,\n ) {}\n\n public reset(): void {\n this.client = undefined;\n }\n\n public get(): Client {\n if (this.client === undefined) this.client = this.clientConstructor(this.wireOpts());\n return this.client;\n }\n}\n\n/** Abstract out low level networking and authorization details */\nexport class LLPlClient implements WireClientProviderFactory {\n /** Initial authorization information */\n private authInformation?: AuthInformation;\n /** Will be executed by the client when it is required */\n private readonly onAuthUpdate?: (newInfo: AuthInformation) => void;\n /** Will be executed if auth-related error happens during normal client operation */\n private readonly onAuthError?: () => void;\n /** Will be executed by the client when it is required */\n private readonly onAuthRefreshProblem?: (error: unknown) => void;\n /** Threshold after which auth info refresh is required */\n private refreshTimestamp?: number;\n\n private _status: PlConnectionStatus = \"OK\";\n private readonly statusListener?: PlConnectionStatusListener;\n\n private _wireProto: wireProtocol = \"grpc\";\n private _wireConn!: WireConnection;\n\n private readonly _restInterceptors: Dispatcher.DispatcherComposeInterceptor[];\n private readonly _restMiddlewares: Middleware[];\n private readonly _grpcInterceptors: Interceptor[];\n private readonly providers: WeakRef<WireClientProviderImpl<any>>[] = [];\n\n public readonly clientProvider: WireClientProvider<PlRestClientType | GrpcPlApiClient>;\n\n public readonly httpDispatcher: Dispatcher;\n\n public static async build(\n configOrAddress: PlClientConfig | string,\n ops: {\n auth?: AuthOps;\n statusListener?: PlConnectionStatusListener;\n shouldUseGzip?: boolean;\n logger?: MiLogger;\n useAutoDetectWireProtocol?: boolean;\n } = {},\n ) {\n const conf =\n typeof configOrAddress === \"string\" ? plAddressToConfig(configOrAddress) : configOrAddress;\n\n const pl = new LLPlClient(conf, ops);\n\n // FIXME(rfiskov)[MILAB-5275]: Investigate why autodetect randomly fails; temporary turn it off.\n if (ops.useAutoDetectWireProtocol) {\n await pl.detectOptimalWireProtocol();\n }\n return pl;\n }\n\n private constructor(\n public readonly conf: PlClientConfig,\n private readonly ops: {\n auth?: AuthOps;\n statusListener?: PlConnectionStatusListener;\n shouldUseGzip?: boolean;\n logger?: MiLogger;\n } = {},\n ) {\n const { auth, statusListener } = ops;\n\n if (auth !== undefined) {\n this.refreshTimestamp = inferAuthRefreshTime(\n auth.authInformation,\n this.conf.authMaxRefreshSeconds,\n );\n this.authInformation = auth.authInformation;\n this.onAuthUpdate = auth.onUpdate;\n this.onAuthRefreshProblem = auth.onUpdateError;\n this.onAuthError = auth.onAuthError;\n }\n\n this._restInterceptors = [];\n this._restMiddlewares = [];\n this._grpcInterceptors = [];\n\n if (auth !== undefined) {\n this._restInterceptors.push(this.createRestAuthInterceptor());\n this._grpcInterceptors.push(this.createGrpcAuthInterceptor());\n }\n this._restInterceptors.push(interceptors.retry({ statusCodes: [] })); // Handle errors with openapi-fetch middleware.\n this._restMiddlewares.push(this.createRestErrorMiddleware());\n this._grpcInterceptors.push(this.createGrpcErrorInterceptor());\n\n this.httpDispatcher = defaultHttpDispatcher(this.conf.httpProxy);\n if (this.conf.wireProtocol) {\n this._wireProto = this.conf.wireProtocol;\n }\n\n this.initWireConnection(this._wireProto);\n\n if (statusListener !== undefined) {\n this.statusListener = statusListener;\n statusListener(this._status);\n }\n\n this.clientProvider = this.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new GrpcPlApiClient(wireConn.Transport);\n } else {\n return createClient<PlApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\n }\n\n private initWireConnection(protocol: wireProtocol) {\n switch (protocol) {\n case \"rest\":\n this.initRestConnection();\n return;\n case \"grpc\":\n this.initGrpcConnection(this.ops.shouldUseGzip ?? false);\n return;\n default:\n ((v: never) => {\n throw new Error(\n `Unsupported wire protocol '${v as string}'. Use one of: ${SUPPORTED_WIRE_PROTOCOLS.join(\", \")}`,\n );\n })(protocol);\n }\n }\n\n private initRestConnection(): void {\n const dispatcher = defaultHttpDispatcher(this.conf.grpcProxy, this._restInterceptors);\n this._replaceWireConnection({\n type: \"rest\",\n Config: this.conf,\n Dispatcher: dispatcher,\n Middlewares: this._restMiddlewares,\n });\n }\n\n /**\n * Initializes (or reinitializes) _grpcTransport\n * @param gzip - whether to enable gzip compression\n */\n private initGrpcConnection(gzip: boolean) {\n const clientOptions: ClientOptions = {\n \"grpc.keepalive_time_ms\": 30_000, // 30 seconds\n \"grpc.service_config_disable_resolution\": 1, // Disable DNS TXT lookups for service config\n interceptors: this._grpcInterceptors,\n };\n\n if (gzip) clientOptions[\"grpc.default_compression_algorithm\"] = compressionAlgorithms.gzip;\n\n //\n // Leaving it here for now\n // https://github.com/grpc/grpc-node/issues/2788\n //\n // We should implement message pooling algorithm to overcome hardcoded NO_DELAY behaviour\n // of HTTP/2 and allow our small messages to batch together.\n //\n const grpcOptions: GrpcOptions = {\n host: this.conf.hostAndPort,\n timeout: this.conf.defaultRequestTimeout,\n channelCredentials: this.conf.ssl\n ? ChannelCredentials.createSsl()\n : ChannelCredentials.createInsecure(),\n clientOptions,\n };\n\n const grpcProxy =\n typeof this.conf.grpcProxy === \"string\" ? { url: this.conf.grpcProxy } : this.conf.grpcProxy;\n\n if (grpcProxy?.url) {\n const url = new URL(grpcProxy.url);\n if (grpcProxy.auth) {\n const parsed = parseHttpAuth(grpcProxy.auth);\n if (parsed.scheme !== \"Basic\") {\n throw new Error(`Unsupported auth scheme: ${parsed.scheme as string}.`);\n }\n url.username = parsed.username;\n url.password = parsed.password;\n }\n process.env.grpc_proxy = url.toString();\n } else {\n delete process.env.grpc_proxy;\n }\n\n this._replaceWireConnection({ type: \"grpc\", Transport: new GrpcTransport(grpcOptions) });\n }\n\n private _replaceWireConnection(newConn: WireConnection): void {\n const oldConn = this._wireConn;\n this._wireConn = newConn;\n this._wireProto = newConn.type;\n\n // Reset all providers to let them reinitialize their clients\n for (let i = 0; i < this.providers.length; i++) {\n const provider = this.providers[i].deref();\n if (provider === undefined) {\n // at the same time we need to remove providers that are no longer valid\n this.providers.splice(i, 1);\n i--;\n } else {\n provider.reset();\n }\n }\n\n if (oldConn !== undefined && oldConn.type === \"grpc\") oldConn.Transport.close();\n }\n\n private providerCleanupCounter = 0;\n\n /**\n * Creates a provider for a grpc client. Returned provider will create fresh client whenever the underlying transport is reset.\n *\n * @param clientConstructor - a factory function that creates a grpc client\n */\n public createWireClientProvider<Client>(\n clientConstructor: (transport: WireConnection) => Client,\n ): WireClientProvider<Client> {\n // We need to cleanup providers periodically to avoid memory leaks.\n // This is a simple heuristic to avoid memory leaks.\n // We could use a more sophisticated algorithm, but this is good enough for now.\n this.providerCleanupCounter++;\n if (this.providerCleanupCounter >= 16) {\n for (let i = 0; i < this.providers.length; i++) {\n const provider = this.providers[i].deref();\n if (provider === undefined) {\n this.providers.splice(i, 1);\n i--;\n }\n }\n this.providerCleanupCounter = 0;\n }\n\n const provider = new WireClientProviderImpl<Client>(() => this._wireConn, clientConstructor);\n this.providers.push(new WeakRef(provider));\n return provider;\n }\n\n public get wireConnection(): WireConnection {\n return this._wireConn;\n }\n\n public get wireProtocol(): wireProtocol | undefined {\n return this._wireProto;\n }\n\n /** Returns true if client is authenticated. Even with anonymous auth information\n * connection is considered authenticated. Unauthenticated clients are used for\n * login and similar tasks, see {@link UnauthenticatedPlClient}. */\n public get authenticated(): boolean {\n return this.authInformation !== undefined;\n }\n\n /** null means anonymous connection */\n public get authUser(): string | null {\n if (!this.authenticated) throw new Error(\"Client is not authenticated\");\n if (this.authInformation?.jwtToken)\n return parsePlJwt(this.authInformation?.jwtToken).user.login;\n else return null;\n }\n\n private updateStatus(newStatus: PlConnectionStatus) {\n process.nextTick(() => {\n if (this._status !== newStatus) {\n this._status = newStatus;\n if (this.statusListener !== undefined) this.statusListener(this._status);\n if (newStatus === \"Unauthenticated\" && this.onAuthError !== undefined) this.onAuthError();\n }\n });\n }\n\n public get status(): PlConnectionStatus {\n return this._status;\n }\n\n private authRefreshInProgress: boolean = false;\n\n private refreshAuthInformationIfNeeded(): void {\n if (\n this.refreshTimestamp === undefined ||\n Date.now() < this.refreshTimestamp ||\n this.authRefreshInProgress ||\n this._status === \"Unauthenticated\"\n )\n return;\n\n // Running refresh in background`\n this.authRefreshInProgress = true;\n void (async () => {\n try {\n const token = await this.getJwtToken(BigInt(this.conf.authTTLSeconds));\n this.authInformation = { jwtToken: token };\n this.refreshTimestamp = inferAuthRefreshTime(\n this.authInformation,\n this.conf.authMaxRefreshSeconds,\n );\n if (this.onAuthUpdate) this.onAuthUpdate(this.authInformation);\n } catch (e: unknown) {\n if (this.onAuthRefreshProblem) this.onAuthRefreshProblem(e);\n } finally {\n this.authRefreshInProgress = false;\n }\n })();\n }\n\n /**\n * Creates middleware that parses error responses and handles them centrally.\n * This middleware runs before openapi-fetch parses the response, so we need to\n * manually parse the response body for error responses.\n */\n private createRestErrorMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const { body, ...resOptions } = response;\n\n if ([502, 503, 504].includes(response.status)) {\n // Service unavailable, bad gateway, gateway timeout\n this.updateStatus(\"Disconnected\");\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n // No error: nice!\n return new Response(respErr.origBody ?? body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === \"string\") {\n // Non-standard error or normal response: let later middleware to deal wit it.\n return new Response(respErr.error, { ...resOptions, status: response.status });\n }\n\n if (respErr.error.code === Code.UNAUTHENTICATED) {\n this.updateStatus(\"Unauthenticated\");\n }\n\n // Let later middleware to deal with standard gRPC error.\n return new Response(respErr.origBody, { ...resOptions, status: response.status });\n },\n };\n }\n\n /** Detects certain errors and update client status accordingly when using GRPC wire connection */\n private createGrpcErrorInterceptor(): Interceptor {\n return (options, nextCall) => {\n return new InterceptingCall(nextCall(options), {\n start: (metadata, listener, next) => {\n next(metadata, {\n onReceiveStatus: (status, next) => {\n if (status.code == GrpcStatus.UNAUTHENTICATED)\n // (!!!) don't change to \"===\"\n this.updateStatus(\"Unauthenticated\");\n if (status.code == GrpcStatus.UNAVAILABLE)\n // (!!!) don't change to \"===\"\n this.updateStatus(\"Disconnected\");\n next(status);\n },\n });\n },\n });\n };\n }\n\n private createRestAuthInterceptor(): Dispatcher.DispatcherComposeInterceptor {\n return (dispatch) => {\n return (options, handler) => {\n if (this.authInformation?.jwtToken !== undefined) {\n // TODO: check this magic really works and gets called\n options.headers = {\n ...options.headers,\n authorization: \"Bearer \" + this.authInformation.jwtToken,\n };\n this.refreshAuthInformationIfNeeded();\n }\n\n return dispatch(options, handler);\n };\n };\n }\n\n /** Injects authentication information if needed */\n private createGrpcAuthInterceptor(): Interceptor {\n return (options, nextCall) => {\n return new InterceptingCall(nextCall(options), {\n start: (metadata, listener, next) => {\n if (this.authInformation?.jwtToken !== undefined) {\n metadata.set(\"authorization\", \"Bearer \" + this.authInformation.jwtToken);\n this.refreshAuthInformationIfNeeded();\n next(metadata, listener);\n } else {\n next(metadata, listener);\n }\n },\n });\n };\n }\n\n public async getJwtToken(\n ttlSeconds: bigint,\n options?: { authorization?: string; role?: AuthAPI_Role },\n ): Promise<string> {\n const cl = this.clientProvider.get();\n const role = options?.role ?? AuthAPI_Role.UNSPECIFIED;\n\n if (cl instanceof GrpcPlApiClient) {\n const meta: Record<string, string> = {};\n if (options?.authorization) meta.authorization = options.authorization;\n return (\n await cl.getJWTToken(\n {\n expiration: { seconds: ttlSeconds, nanos: 0 },\n requestedRole: role,\n },\n { meta },\n ).response\n ).token;\n } else {\n const headers: Record<string, string> = {};\n if (options?.authorization) headers.authorization = options.authorization;\n const resp = cl.POST(\"/v1/auth/jwt-token\", {\n body: { expiration: `${ttlSeconds}s`, requestedRole: role },\n headers,\n });\n return notEmpty((await resp).data, \"REST: empty response for JWT token request\").token;\n }\n }\n\n public async ping(): Promise<grpcTypes.MaintenanceAPI_Ping_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.ping({})).response;\n } else {\n return notEmpty((await cl.GET(\"/v1/ping\")).data, \"REST: empty response for ping request\");\n }\n }\n\n /**\n * Detects the best available wire protocol.\n * If wireProtocol is explicitly configured, does nothing.\n * Otherwise probes the current protocol via ping; if it fails, switches to the alternative.\n */\n private async detectOptimalWireProtocol() {\n if (this.conf.wireProtocol) {\n return;\n }\n\n // Each retry is:\n // - ping request timeout (100 to 3_000ms)\n // - backoff delay (30 to 500ms)\n //\n // 30 attempts are ~43 seconds of overall waiting time.\n // Think twice on overall time this thing takes to complete when changing these parameters.\n // It may block UI when connecting to the server and loading projects list.\n const pingTimeoutFactor = 1.3;\n const maxPingTimeoutMs = 3_000;\n const retryOptions: RetryOptions = {\n type: \"exponentialBackoff\",\n maxAttempts: 30,\n initialDelay: 30,\n backoffMultiplier: 1.3,\n jitter: 0.2,\n maxDelay: 500,\n };\n\n let attempt = 1;\n let pingTimeoutMs = 100;\n await retry(\n () => withTimeout(this.ping(), pingTimeoutMs),\n retryOptions,\n (e: unknown) => {\n if (isAbortedError(e)) {\n this.ops.logger?.info(\n `Wire proto autodetect: ping timed out after ${pingTimeoutMs}ms: attempt=${attempt}, wire=${this._wireProto}`,\n );\n\n if (attempt % 2 === 0) {\n // We have 2 wire protocols to check. Increase timeout each 2 attempts.\n pingTimeoutMs = Math.min(\n Math.round(pingTimeoutMs * pingTimeoutFactor),\n maxPingTimeoutMs,\n );\n }\n } else {\n this.ops.logger?.info(\n `Wire proto autodetect: ping failed: attempt=${attempt}, wire=${this._wireProto}, err=${String(e)}`,\n );\n }\n\n attempt++;\n const protocol = this._wireProto === \"grpc\" ? \"rest\" : \"grpc\";\n this.ops.logger?.info(\n `Wire protocol autodetect next attempt: will try wire '${protocol}' with timeout ${pingTimeoutMs}ms`,\n );\n this.initWireConnection(protocol);\n return true;\n },\n );\n }\n\n public async license(): Promise<grpcTypes.MaintenanceAPI_License_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.license({})).response;\n } else {\n const resp = notEmpty(\n (await cl.GET(\"/v1/license\")).data,\n \"REST: empty response for license request\",\n );\n return {\n status: resp.status,\n isOk: resp.isOk,\n responseBody: Uint8Array.from(Buffer.from(resp.responseBody)),\n };\n }\n }\n\n public async authMethods(): Promise<grpcTypes.AuthAPI_ListMethods_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (await cl.authMethods({})).response;\n } else {\n return notEmpty(\n (await cl.GET(\"/v1/auth/methods\")).data,\n \"REST: empty response for auth methods request\",\n );\n }\n }\n\n public async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<grpcTypes.AuthAPI_GetUserRoot_Response> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return (\n await cl.getUserRoot({\n login: opts.login ?? \"\",\n createIfNotExists: opts.createIfNotExists ?? false,\n })\n ).response;\n } else {\n const resp = notEmpty(\n (\n await cl.POST(\"/v1/auth/user-root\", {\n body: {\n login: opts.login ?? \"\",\n createIfNotExists: opts.createIfNotExists ?? false,\n },\n })\n ).data,\n \"REST: empty response for getUserRoot request\",\n );\n return {\n userRoot: resp.userRoot\n ? {\n resourceId: BigInt(resp.userRoot.resourceId),\n resourceSignature: Uint8Array.from(\n Buffer.from(resp.userRoot.resourceSignature, \"base64\"),\n ),\n }\n : undefined,\n };\n }\n }\n\n public async listUserResources(\n opts: { login?: string; startFrom?: bigint; limit?: number } = {},\n ): Promise<grpcTypes.AuthAPI_ListUserResources_Response[]> {\n const cl = this.clientProvider.get();\n\n if (!(cl instanceof GrpcPlApiClient)) {\n throw new Error(\"ListUserResources requires gRPC wire protocol; REST is not supported\");\n }\n\n const call = cl.listUserResources({\n login: opts.login ?? \"\",\n startFrom: opts.startFrom ?? 0n,\n limit: opts.limit ?? 0,\n });\n const responses: grpcTypes.AuthAPI_ListUserResources_Response[] = [];\n for await (const msg of call.responses) {\n responses.push(msg);\n }\n return responses;\n }\n\n public async txSync(txId: bigint): Promise<void> {\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n await cl.txSync({ txId: BigInt(txId) });\n } else {\n await cl.POST(\"/v1/tx-sync\", { body: { txId: txId.toString() } });\n }\n }\n\n createTx(rw: boolean, ops: PlCallOps = {}): LLPlTransaction {\n return new LLPlTransaction((abortSignal) => {\n let totalAbortSignal = abortSignal;\n if (ops.abortSignal) totalAbortSignal = AbortSignal.any([totalAbortSignal, ops.abortSignal]);\n\n const timeout =\n ops.timeout ??\n (rw ? this.conf.defaultRWTransactionTimeout : this.conf.defaultROTransactionTimeout);\n\n const cl = this.clientProvider.get();\n if (cl instanceof GrpcPlApiClient) {\n return cl.tx({\n abort: totalAbortSignal,\n timeout,\n });\n }\n\n const wireConn = this.wireConnection;\n if (wireConn.type === \"rest\") {\n // For REST/WebSocket protocol, timeout needs to be converted to AbortSignal\n if (timeout !== undefined) {\n totalAbortSignal = AbortSignal.any([totalAbortSignal, AbortSignal.timeout(timeout)]);\n }\n\n // The gRPC transport has the auth interceptor that already handles it, but here we need to refresh the auth information to be safe.\n this.refreshAuthInformationIfNeeded();\n\n const wsUrl = this.conf.ssl\n ? `wss://${this.conf.hostAndPort}/v1/ws/tx`\n : `ws://${this.conf.hostAndPort}/v1/ws/tx`;\n\n return new WebSocketBiDiStream(\n wsUrl,\n (msg) => TxAPI_ClientMessage.toBinary(msg),\n (data) => TxAPI_ServerMessage.fromBinary(new Uint8Array(data)),\n {\n abortSignal: totalAbortSignal,\n jwtToken: this.authInformation?.jwtToken,\n dispatcher: wireConn.Dispatcher,\n\n onComplete: async (stream) =>\n stream.requests.send({\n // Ask server to gracefully close the stream on its side, if not done yet.\n requestId: 0,\n request: { oneofKind: \"streamClose\", streamClose: {} },\n }),\n },\n );\n }\n\n throw new Error(`transactions are not supported for wire protocol ${this._wireProto}`);\n });\n }\n\n /** Closes underlying transport */\n public async close() {\n if (this.wireConnection.type === \"grpc\") {\n this.wireConnection.Transport.close();\n } else {\n // TODO: close all WS connections\n }\n await this.httpDispatcher.destroy();\n }\n}\n"],"mappings":"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"}
@@ -13,7 +13,7 @@ const LsProviderFieldPrefix = "storage/";
13
13
  *
14
14
  * Detects backend capability on the first getUserRoot() call and remembers
15
15
  * the result. Three-tier fallback:
16
- * 1. getUserRoot RPC (newest, supports doNotCreate)
16
+ * 1. getUserRoot RPC (newest, supports createIfNotExists)
17
17
  * 2. listUserResources RPC (streams all resources, picks userRoot)
18
18
  * 3. Named resource lookup/creation via transaction (legacy)
19
19
  */
@@ -73,10 +73,10 @@ var UserResources = class {
73
73
  async getUserRootViaRpc(opts = {}) {
74
74
  const resp = await this.ll.getUserRoot({
75
75
  login: opts.login,
76
- doNotCreate: opts.doNotCreate
76
+ createIfNotExists: opts.createIfNotExists
77
77
  });
78
78
  if (resp.userRoot === void 0) {
79
- if (opts.doNotCreate) return void 0;
79
+ if (!opts.createIfNotExists) return void 0;
80
80
  throw new Error("getUserRoot returned no userRoot entry");
81
81
  }
82
82
  return require_types.createSignedResourceId(resp.userRoot.resourceId, require_types.toResourceSignature(resp.userRoot.resourceSignature));
@@ -87,7 +87,7 @@ var UserResources = class {
87
87
  limit: 1
88
88
  });
89
89
  for (const msg of responses) if (msg.entry.oneofKind === "userRoot") return require_types.createSignedResourceId(msg.entry.userRoot.resourceId, require_types.toResourceSignature(msg.entry.userRoot.resourceSignature));
90
- throw new Error("listUserResources returned no userRoot entry");
90
+ if (opts.createIfNotExists) throw new Error("listUserResources returned no userRoot entry");
91
91
  }
92
92
  async getDataLibrariesViaList(opts = {}) {
93
93
  const responses = await this.ll.listUserResources({ login: opts.login });
@@ -145,7 +145,7 @@ var UserResources = class {
145
145
  const mainRootName = login === null ? AnonymousClientRoot : (0, node_crypto.createHash)("sha256").update(login).digest("hex");
146
146
  return await this.runTx("initialization", true, "", async (tx) => {
147
147
  if (await tx.checkResourceNameExists(mainRootName)) return await tx.getResourceByName(mainRootName);
148
- if (opts.doNotCreateUserRoot) return;
148
+ if (!opts.createIfNotExists) return;
149
149
  const mainRoot = tx.createRoot(require_pl.ClientRoot);
150
150
  tx.setResourceName(mainRootName, mainRoot);
151
151
  await tx.commit();
@@ -1 +1 @@
1
- {"version":3,"file":"user_resources.cjs","names":["isUnimplementedError","createSignedResourceId","toResourceSignature","ClientRoot","toGlobalResourceId","isNotNullSignedResourceId"],"sources":["../../src/core/user_resources.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { LLPlClient } from \"./ll_client\";\nimport type { PlTransaction } from \"./transaction\";\nimport { toGlobalResourceId } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId, ResourceType } from \"./types\";\nimport {\n createSignedResourceId,\n isNotNullSignedResourceId,\n NullSignedResourceId,\n toResourceSignature,\n} from \"./types\";\nimport { isUnimplementedError } from \"./errors\";\nimport { ClientRoot } from \"../helpers/pl\";\n\nconst AnonymousClientRoot = \"AnonymousRoot\";\nconst LsStorageTypePrefix = \"LS/\"; // implements ls API in particular storage\nconst LsProviderFieldPrefix = \"storage/\"; // provides access to storages list\n\n/** Information about a single data library (LS storage). */\nexport interface StorageInfo {\n /** Machine-stable identifier, e.g. \"library\". Used for filtering and map keys. */\n readonly storageId: string;\n /** Human-readable display name. For V1/legacy equals storageId; for V2 from resource JSON data. */\n readonly storageName: string;\n /** Signed resource ID for this storage resource. */\n readonly resourceId: SignedResourceId;\n /** Full resource type including correct version (\"1\" or \"2\"). */\n readonly resourceType: ResourceType;\n}\n\n/** V2 LsStorage resource JSON data shape (contract with backend). */\ninterface LsStorageV2Data {\n storageName: string;\n storageID: string;\n}\n\n/**\n * Callback type for running transactions. Matches PlClient._withTx signature\n * so the index can run transactions before PlClient is fully initialized.\n */\nexport type TxRunner = <T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalSignedResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n) => Promise<T>;\n\ntype BackendCapability = \"getUserRoot\" | \"listUserResources\" | \"legacy\";\n\n/**\n * Abstracts user resource discovery with backward compatibility.\n *\n * Detects backend capability on the first getUserRoot() call and remembers\n * the result. Three-tier fallback:\n * 1. getUserRoot RPC (newest, supports doNotCreate)\n * 2. listUserResources RPC (streams all resources, picks userRoot)\n * 3. Named resource lookup/creation via transaction (legacy)\n */\nexport class UserResources {\n private backendCapability: BackendCapability | undefined;\n\n constructor(\n private readonly ll: LLPlClient,\n private readonly runTx: TxRunner,\n public readonly authUser: string | null,\n ) {}\n\n /**\n * Returns the user's root resource ID.\n *\n * On first call, detects backend capability by trying methods in order:\n * 1. getUserRoot RPC (newest)\n * 2. listUserResources RPC\n * 3. Named resource lookup/creation via transaction (legacy)\n */\n async getUserRoot(): Promise<SignedResourceId>;\n async getUserRoot(opts: { login?: string }): Promise<SignedResourceId>;\n async getUserRoot(opts: { login?: string; doNotCreate: false }): Promise<SignedResourceId>;\n async getUserRoot(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n async getUserRoot(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n if (this.backendCapability === undefined) {\n return await this.detectAndGetUserRoot(opts);\n }\n return await this.getUserRootWith(this.backendCapability, opts);\n }\n\n private async detectAndGetUserRoot(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n // 1. Try getUserRoot RPC\n try {\n const root = await this.getUserRootViaRpc(opts);\n this.backendCapability = \"getUserRoot\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 2. Try listUserResources\n try {\n const root = await this.getUserRootViaList(opts);\n this.backendCapability = \"listUserResources\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 3. Legacy fallback\n this.backendCapability = \"legacy\";\n return await this.getUserRootViaLegacy(opts);\n }\n\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n switch (capability) {\n case \"getUserRoot\":\n return await this.getUserRootViaRpc(opts);\n case \"listUserResources\":\n return await this.getUserRootViaList(opts);\n case \"legacy\":\n return await this.getUserRootViaLegacy(opts);\n }\n }\n\n /**\n * Returns all data libraries the user has access to.\n * Always fetches fresh from the server (no caching).\n */\n async getDataLibraries(\n opts: { login?: string; doNotCreateUserRoot?: boolean } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n if (this.backendCapability === undefined) {\n // First call — detect backend capability\n try {\n const libs = await this.getDataLibrariesViaList(opts);\n // getUserRoot RPC doesn't return libraries, but listUserResources does;\n // record at least \"listUserResources\" so future getUserRoot calls don't re-detect.\n this.backendCapability = \"listUserResources\";\n return libs;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n this.backendCapability = \"legacy\";\n return await this.getDataLibrariesViaLegacy();\n }\n }\n\n // A server that supports getUserRoot definitely supports listUserResources.\n if (this.backendCapability !== \"legacy\") {\n return await this.getDataLibrariesViaList(opts);\n }\n return await this.getDataLibrariesViaLegacy();\n }\n\n private async getUserRootViaRpc(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts: {\n login?: string;\n doNotCreate: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaRpc(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const resp = await this.ll.getUserRoot({\n login: opts.login,\n doNotCreate: opts.doNotCreate,\n });\n if (resp.userRoot === undefined) {\n if (opts.doNotCreate) return undefined;\n throw new Error(\"getUserRoot returned no userRoot entry\");\n }\n return createSignedResourceId(\n resp.userRoot.resourceId,\n toResourceSignature(resp.userRoot.resourceSignature),\n );\n }\n\n private async getUserRootViaList(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts: {\n login?: string;\n doNotCreate: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaList(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const responses = await this.ll.listUserResources({ login: opts.login, limit: 1 });\n for (const msg of responses) {\n if (msg.entry.oneofKind === \"userRoot\") {\n return createSignedResourceId(\n msg.entry.userRoot.resourceId,\n toResourceSignature(msg.entry.userRoot.resourceSignature),\n );\n }\n }\n throw new Error(\"listUserResources returned no userRoot entry\");\n }\n\n private async getDataLibrariesViaList(\n opts: { login?: string } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n const responses = await this.ll.listUserResources({ login: opts.login });\n\n // Collect all LS/* shared resources, separating V1 and V2\n const v1Entries: StorageInfo[] = [];\n const v2ResourceIds: { resourceId: SignedResourceId; resourceType: ResourceType }[] = [];\n\n for (const msg of responses) {\n if (msg.entry.oneofKind !== \"sharedResource\") continue;\n const sr = msg.entry.sharedResource;\n\n if (!sr.resourceType) continue;\n const typeName = sr.resourceType.name;\n const typeVersion = sr.resourceType.version;\n if (!typeName.startsWith(LsStorageTypePrefix)) continue;\n\n const rId = createSignedResourceId(sr.resourceId, toResourceSignature(sr.resourceSignature));\n const rType: ResourceType = { name: typeName, version: typeVersion };\n\n if (typeVersion === \"2\") {\n v2ResourceIds.push({ resourceId: rId, resourceType: rType });\n } else {\n // V1 or unknown version: derive storageId from type name\n const storageId = typeName.substring(LsStorageTypePrefix.length);\n v1Entries.push({\n storageId,\n storageName: storageId,\n resourceId: rId,\n resourceType: rType,\n });\n }\n }\n\n // Read V2 resource data in a single transaction\n let v2Entries: StorageInfo[] = [];\n if (v2ResourceIds.length > 0) {\n v2Entries = await this.runTx(\n \"ReadLsStorageV2Data\",\n false,\n NullSignedResourceId,\n async (tx) => {\n const entries: StorageInfo[] = [];\n for (const { resourceId, resourceType } of v2ResourceIds) {\n const rd = await tx.getResourceData(resourceId, false);\n if (rd.data) {\n const v2Data = JSON.parse(Buffer.from(rd.data).toString(\"utf-8\")) as LsStorageV2Data;\n entries.push({\n storageId: v2Data.storageID,\n storageName: v2Data.storageName,\n resourceId,\n resourceType,\n });\n }\n }\n return entries;\n },\n );\n }\n\n const result = new Map<string, StorageInfo>();\n for (const entry of [...v1Entries, ...v2Entries]) {\n result.set(entry.storageId, entry);\n }\n return result;\n }\n\n private async getUserRootViaLegacy(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts: {\n login?: string;\n doNotCreateUserRoot: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts: {\n login?: string;\n doNotCreateUserRoot: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaLegacy(\n opts: { login?: string; doNotCreateUserRoot?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const login = opts.login ?? this.authUser;\n const mainRootName =\n login === null ? AnonymousClientRoot : createHash(\"sha256\").update(login).digest(\"hex\");\n\n return await this.runTx(\"initialization\", true, NullSignedResourceId, async (tx) => {\n if (await tx.checkResourceNameExists(mainRootName)) {\n return await tx.getResourceByName(mainRootName);\n }\n\n if (opts.doNotCreateUserRoot) {\n return undefined;\n }\n\n const mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n });\n }\n\n private async getDataLibrariesViaLegacy(): Promise<ReadonlyMap<string, StorageInfo>> {\n return await this.runTx(\"GetAvailableStorageIds\", false, NullSignedResourceId, async (tx) => {\n const lsProviderId = await tx.getResourceByName(\"LSProvider\");\n const provider = await tx.getResourceData(lsProviderId, true);\n\n const result = new Map<string, StorageInfo>();\n for (const field of provider.fields) {\n if (field.type !== \"Dynamic\" || !isNotNullSignedResourceId(field.value)) continue;\n if (!field.name.startsWith(LsProviderFieldPrefix)) continue;\n\n const storageId = field.name.substring(LsProviderFieldPrefix.length);\n result.set(storageId, {\n storageId,\n storageName: storageId,\n resourceId: field.value,\n resourceType: { name: `${LsStorageTypePrefix}${storageId}`, version: \"1\" },\n });\n }\n return result;\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"user_resources.cjs","names":["isUnimplementedError","createSignedResourceId","toResourceSignature","ClientRoot","toGlobalResourceId","isNotNullSignedResourceId"],"sources":["../../src/core/user_resources.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { LLPlClient } from \"./ll_client\";\nimport type { PlTransaction } from \"./transaction\";\nimport { toGlobalResourceId } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId, ResourceType } from \"./types\";\nimport {\n createSignedResourceId,\n isNotNullSignedResourceId,\n NullSignedResourceId,\n toResourceSignature,\n} from \"./types\";\nimport { isUnimplementedError } from \"./errors\";\nimport { ClientRoot } from \"../helpers/pl\";\n\nconst AnonymousClientRoot = \"AnonymousRoot\";\nconst LsStorageTypePrefix = \"LS/\"; // implements ls API in particular storage\nconst LsProviderFieldPrefix = \"storage/\"; // provides access to storages list\n\n/** Information about a single data library (LS storage). */\nexport interface StorageInfo {\n /** Machine-stable identifier, e.g. \"library\". Used for filtering and map keys. */\n readonly storageId: string;\n /** Human-readable display name. For V1/legacy equals storageId; for V2 from resource JSON data. */\n readonly storageName: string;\n /** Signed resource ID for this storage resource. */\n readonly resourceId: SignedResourceId;\n /** Full resource type including correct version (\"1\" or \"2\"). */\n readonly resourceType: ResourceType;\n}\n\n/** V2 LsStorage resource JSON data shape (contract with backend). */\ninterface LsStorageV2Data {\n storageName: string;\n storageID: string;\n}\n\n/**\n * Callback type for running transactions. Matches PlClient._withTx signature\n * so the index can run transactions before PlClient is fully initialized.\n */\nexport type TxRunner = <T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalSignedResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n) => Promise<T>;\n\ntype BackendCapability = \"getUserRoot\" | \"listUserResources\" | \"legacy\";\n\n/**\n * Abstracts user resource discovery with backward compatibility.\n *\n * Detects backend capability on the first getUserRoot() call and remembers\n * the result. Three-tier fallback:\n * 1. getUserRoot RPC (newest, supports createIfNotExists)\n * 2. listUserResources RPC (streams all resources, picks userRoot)\n * 3. Named resource lookup/creation via transaction (legacy)\n */\nexport class UserResources {\n private backendCapability: BackendCapability | undefined;\n\n constructor(\n private readonly ll: LLPlClient,\n private readonly runTx: TxRunner,\n public readonly authUser: string | null,\n ) {}\n\n /**\n * Returns the user's root resource ID.\n *\n * On first call, detects backend capability by trying methods in order:\n * 1. getUserRoot RPC (newest)\n * 2. listUserResources RPC\n * 3. Named resource lookup/creation via transaction (legacy)\n */\n async getUserRoot(opts: { login?: string; createIfNotExists: true }): Promise<SignedResourceId>;\n async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n if (this.backendCapability === undefined) {\n return await this.detectAndGetUserRoot(opts);\n }\n return await this.getUserRootWith(this.backendCapability, opts);\n }\n\n private async detectAndGetUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async detectAndGetUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async detectAndGetUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n // 1. Try getUserRoot RPC\n try {\n const root = await this.getUserRootViaRpc(opts);\n this.backendCapability = \"getUserRoot\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 2. Try listUserResources\n try {\n const root = await this.getUserRootViaList(opts);\n this.backendCapability = \"listUserResources\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 3. Legacy fallback\n this.backendCapability = \"legacy\";\n return await this.getUserRootViaLegacy(opts);\n }\n\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; createIfNotExists: true },\n ): Promise<SignedResourceId>;\n private async getUserRootWith(\n capability: BackendCapability,\n opts?: { login?: string; createIfNotExists?: boolean },\n ): Promise<SignedResourceId | undefined>;\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n switch (capability) {\n case \"getUserRoot\":\n return await this.getUserRootViaRpc(opts);\n case \"listUserResources\":\n return await this.getUserRootViaList(opts);\n case \"legacy\":\n return await this.getUserRootViaLegacy(opts);\n }\n }\n\n /**\n * Returns all data libraries the user has access to.\n * Always fetches fresh from the server (no caching).\n */\n async getDataLibraries(\n opts: { login?: string; createUserRootIfNotExists?: boolean } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n if (this.backendCapability === undefined) {\n // First call — detect backend capability\n try {\n const libs = await this.getDataLibrariesViaList(opts);\n // getUserRoot RPC doesn't return libraries, but listUserResources does;\n // record at least \"listUserResources\" so future getUserRoot calls don't re-detect.\n this.backendCapability = \"listUserResources\";\n return libs;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n this.backendCapability = \"legacy\";\n return await this.getDataLibrariesViaLegacy();\n }\n }\n\n // A server that supports getUserRoot definitely supports listUserResources.\n if (this.backendCapability !== \"legacy\") {\n return await this.getDataLibrariesViaList(opts);\n }\n return await this.getDataLibrariesViaLegacy();\n }\n\n private async getUserRootViaRpc(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaRpc(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const resp = await this.ll.getUserRoot({\n login: opts.login,\n createIfNotExists: opts.createIfNotExists,\n });\n if (resp.userRoot === undefined) {\n if (!opts.createIfNotExists) return undefined;\n throw new Error(\"getUserRoot returned no userRoot entry\");\n }\n return createSignedResourceId(\n resp.userRoot.resourceId,\n toResourceSignature(resp.userRoot.resourceSignature),\n );\n }\n\n private async getUserRootViaList(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaList(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const responses = await this.ll.listUserResources({ login: opts.login, limit: 1 });\n for (const msg of responses) {\n if (msg.entry.oneofKind === \"userRoot\") {\n return createSignedResourceId(\n msg.entry.userRoot.resourceId,\n toResourceSignature(msg.entry.userRoot.resourceSignature),\n );\n }\n }\n\n if (opts.createIfNotExists) {\n throw new Error(\"listUserResources returned no userRoot entry\");\n }\n\n return undefined;\n }\n\n private async getDataLibrariesViaList(\n opts: { login?: string } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n const responses = await this.ll.listUserResources({ login: opts.login });\n\n // Collect all LS/* shared resources, separating V1 and V2\n const v1Entries: StorageInfo[] = [];\n const v2ResourceIds: { resourceId: SignedResourceId; resourceType: ResourceType }[] = [];\n\n for (const msg of responses) {\n if (msg.entry.oneofKind !== \"sharedResource\") continue;\n const sr = msg.entry.sharedResource;\n\n if (!sr.resourceType) continue;\n const typeName = sr.resourceType.name;\n const typeVersion = sr.resourceType.version;\n if (!typeName.startsWith(LsStorageTypePrefix)) continue;\n\n const rId = createSignedResourceId(sr.resourceId, toResourceSignature(sr.resourceSignature));\n const rType: ResourceType = { name: typeName, version: typeVersion };\n\n if (typeVersion === \"2\") {\n v2ResourceIds.push({ resourceId: rId, resourceType: rType });\n } else {\n // V1 or unknown version: derive storageId from type name\n const storageId = typeName.substring(LsStorageTypePrefix.length);\n v1Entries.push({\n storageId,\n storageName: storageId,\n resourceId: rId,\n resourceType: rType,\n });\n }\n }\n\n // Read V2 resource data in a single transaction\n let v2Entries: StorageInfo[] = [];\n if (v2ResourceIds.length > 0) {\n v2Entries = await this.runTx(\n \"ReadLsStorageV2Data\",\n false,\n NullSignedResourceId,\n async (tx) => {\n const entries: StorageInfo[] = [];\n for (const { resourceId, resourceType } of v2ResourceIds) {\n const rd = await tx.getResourceData(resourceId, false);\n if (rd.data) {\n const v2Data = JSON.parse(Buffer.from(rd.data).toString(\"utf-8\")) as LsStorageV2Data;\n entries.push({\n storageId: v2Data.storageID,\n storageName: v2Data.storageName,\n resourceId,\n resourceType,\n });\n }\n }\n return entries;\n },\n );\n }\n\n const result = new Map<string, StorageInfo>();\n for (const entry of [...v1Entries, ...v2Entries]) {\n result.set(entry.storageId, entry);\n }\n return result;\n }\n\n private async getUserRootViaLegacy(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaLegacy(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const login = opts.login ?? this.authUser;\n const mainRootName =\n login === null ? AnonymousClientRoot : createHash(\"sha256\").update(login).digest(\"hex\");\n\n return await this.runTx(\"initialization\", true, NullSignedResourceId, async (tx) => {\n if (await tx.checkResourceNameExists(mainRootName)) {\n return await tx.getResourceByName(mainRootName);\n }\n\n if (!opts.createIfNotExists) {\n return undefined;\n }\n\n const mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n });\n }\n\n private async getDataLibrariesViaLegacy(): Promise<ReadonlyMap<string, StorageInfo>> {\n return await this.runTx(\"GetAvailableStorageIds\", false, NullSignedResourceId, async (tx) => {\n const lsProviderId = await tx.getResourceByName(\"LSProvider\");\n const provider = await tx.getResourceData(lsProviderId, true);\n\n const result = new Map<string, StorageInfo>();\n for (const field of provider.fields) {\n if (field.type !== \"Dynamic\" || !isNotNullSignedResourceId(field.value)) continue;\n if (!field.name.startsWith(LsProviderFieldPrefix)) continue;\n\n const storageId = field.name.substring(LsProviderFieldPrefix.length);\n result.set(storageId, {\n storageId,\n storageName: storageId,\n resourceId: field.value,\n resourceType: { name: `${LsStorageTypePrefix}${storageId}`, version: \"1\" },\n });\n }\n return result;\n });\n }\n}\n"],"mappings":"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"}
@@ -24,7 +24,7 @@ type TxRunner = <T>(name: string, writable: boolean, clientRoot: OptionalSignedR
24
24
  *
25
25
  * Detects backend capability on the first getUserRoot() call and remembers
26
26
  * the result. Three-tier fallback:
27
- * 1. getUserRoot RPC (newest, supports doNotCreate)
27
+ * 1. getUserRoot RPC (newest, supports createIfNotExists)
28
28
  * 2. listUserResources RPC (streams all resources, picks userRoot)
29
29
  * 3. Named resource lookup/creation via transaction (legacy)
30
30
  */
@@ -42,17 +42,13 @@ declare class UserResources {
42
42
  * 2. listUserResources RPC
43
43
  * 3. Named resource lookup/creation via transaction (legacy)
44
44
  */
45
- getUserRoot(): Promise<SignedResourceId>;
46
45
  getUserRoot(opts: {
47
46
  login?: string;
47
+ createIfNotExists: true;
48
48
  }): Promise<SignedResourceId>;
49
- getUserRoot(opts: {
50
- login?: string;
51
- doNotCreate: false;
52
- }): Promise<SignedResourceId>;
53
- getUserRoot(opts: {
49
+ getUserRoot(opts?: {
54
50
  login?: string;
55
- doNotCreate: true;
51
+ createIfNotExists?: boolean;
56
52
  }): Promise<SignedResourceId | undefined>;
57
53
  private detectAndGetUserRoot;
58
54
  private getUserRootWith;
@@ -62,7 +58,7 @@ declare class UserResources {
62
58
  */
63
59
  getDataLibraries(opts?: {
64
60
  login?: string;
65
- doNotCreateUserRoot?: boolean;
61
+ createUserRootIfNotExists?: boolean;
66
62
  }): Promise<ReadonlyMap<string, StorageInfo>>;
67
63
  private getUserRootViaRpc;
68
64
  private getUserRootViaList;
@@ -1 +1 @@
1
- {"version":3,"file":"user_resources.d.ts","names":[],"sources":["../../src/core/user_resources.ts"],"mappings":";;;;;;UAmBiB,WAAA;EAAA;EAAA,SAEN,SAAA;;WAEA,WAAA;EAFA;EAAA,SAIA,UAAA,EAAY,gBAAA;EAAZ;EAAA,SAEA,YAAA,EAAc,YAAA;AAAA;;;;AAazB;KAAY,QAAA,OACV,IAAA,UACA,QAAA,WACA,UAAA,EAAY,wBAAA,EACZ,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,MAClC,OAAA,CAAQ,CAAA;;;;;;;;;;cAaA,aAAA;EAAA,iBAIQ,EAAA;EAAA,iBACA,KAAA;EAAA,SACD,QAAA;EAAA,QALV,iBAAA;cAGW,EAAA,EAAI,UAAA,EACJ,KAAA,EAAO,QAAA,EACR,QAAA;EApBX;;;;;;;;EA+BD,WAAA,CAAA,GAAe,OAAA,CAAQ,gBAAA;EACvB,WAAA,CAAY,IAAA;IAAQ,KAAA;EAAA,IAAmB,OAAA,CAAQ,gBAAA;EAC/C,WAAA,CAAY,IAAA;IAAQ,KAAA;IAAgB,WAAA;EAAA,IAAuB,OAAA,CAAQ,gBAAA;EACnE,WAAA,CAAY,IAAA;IAChB,KAAA;IACA,WAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EAAA,QAUE,oBAAA;EAAA,QA0BA,eAAA;EAoBH;;;;EAFL,gBAAA,CACJ,IAAA;IAAQ,KAAA;IAAgB,mBAAA;EAAA,IACvB,OAAA,CAAQ,WAAA,SAAoB,WAAA;EAAA,QAuBjB,iBAAA;EAAA,QA0BA,kBAAA;EAAA,QAwBA,uBAAA;EAAA,QAoEA,oBAAA;EAAA,QAgCA,yBAAA;AAAA"}
1
+ {"version":3,"file":"user_resources.d.ts","names":[],"sources":["../../src/core/user_resources.ts"],"mappings":";;;;;;UAmBiB,WAAA;EAAA;EAAA,SAEN,SAAA;;WAEA,WAAA;EAFA;EAAA,SAIA,UAAA,EAAY,gBAAA;EAAZ;EAAA,SAEA,YAAA,EAAc,YAAA;AAAA;;;;AAazB;KAAY,QAAA,OACV,IAAA,UACA,QAAA,WACA,UAAA,EAAY,wBAAA,EACZ,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,MAClC,OAAA,CAAQ,CAAA;;;;;;;;;;cAaA,aAAA;EAAA,iBAIQ,EAAA;EAAA,iBACA,KAAA;EAAA,SACD,QAAA;EAAA,QALV,iBAAA;cAGW,EAAA,EAAI,UAAA,EACJ,KAAA,EAAO,QAAA,EACR,QAAA;EApBX;;;;;;;;EA+BD,WAAA,CAAY,IAAA;IAAQ,KAAA;IAAgB,iBAAA;EAAA,IAA4B,OAAA,CAAQ,gBAAA;EACxE,WAAA,CAAY,IAAA;IAChB,KAAA;IACA,iBAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EAAA,QAUE,oBAAA;EAAA,QAkCA,eAAA;EA4BH;;;;EAFL,gBAAA,CACJ,IAAA;IAAQ,KAAA;IAAgB,yBAAA;EAAA,IACvB,OAAA,CAAQ,WAAA,SAAoB,WAAA;EAAA,QAuBjB,iBAAA;EAAA,QAyBA,kBAAA;EAAA,QA4BA,uBAAA;EAAA,QAoEA,oBAAA;EAAA,QA+BA,yBAAA;AAAA"}
@@ -12,7 +12,7 @@ const LsProviderFieldPrefix = "storage/";
12
12
  *
13
13
  * Detects backend capability on the first getUserRoot() call and remembers
14
14
  * the result. Three-tier fallback:
15
- * 1. getUserRoot RPC (newest, supports doNotCreate)
15
+ * 1. getUserRoot RPC (newest, supports createIfNotExists)
16
16
  * 2. listUserResources RPC (streams all resources, picks userRoot)
17
17
  * 3. Named resource lookup/creation via transaction (legacy)
18
18
  */
@@ -72,10 +72,10 @@ var UserResources = class {
72
72
  async getUserRootViaRpc(opts = {}) {
73
73
  const resp = await this.ll.getUserRoot({
74
74
  login: opts.login,
75
- doNotCreate: opts.doNotCreate
75
+ createIfNotExists: opts.createIfNotExists
76
76
  });
77
77
  if (resp.userRoot === void 0) {
78
- if (opts.doNotCreate) return void 0;
78
+ if (!opts.createIfNotExists) return void 0;
79
79
  throw new Error("getUserRoot returned no userRoot entry");
80
80
  }
81
81
  return createSignedResourceId(resp.userRoot.resourceId, toResourceSignature(resp.userRoot.resourceSignature));
@@ -86,7 +86,7 @@ var UserResources = class {
86
86
  limit: 1
87
87
  });
88
88
  for (const msg of responses) if (msg.entry.oneofKind === "userRoot") return createSignedResourceId(msg.entry.userRoot.resourceId, toResourceSignature(msg.entry.userRoot.resourceSignature));
89
- throw new Error("listUserResources returned no userRoot entry");
89
+ if (opts.createIfNotExists) throw new Error("listUserResources returned no userRoot entry");
90
90
  }
91
91
  async getDataLibrariesViaList(opts = {}) {
92
92
  const responses = await this.ll.listUserResources({ login: opts.login });
@@ -144,7 +144,7 @@ var UserResources = class {
144
144
  const mainRootName = login === null ? AnonymousClientRoot : createHash("sha256").update(login).digest("hex");
145
145
  return await this.runTx("initialization", true, "", async (tx) => {
146
146
  if (await tx.checkResourceNameExists(mainRootName)) return await tx.getResourceByName(mainRootName);
147
- if (opts.doNotCreateUserRoot) return;
147
+ if (!opts.createIfNotExists) return;
148
148
  const mainRoot = tx.createRoot(ClientRoot);
149
149
  tx.setResourceName(mainRootName, mainRoot);
150
150
  await tx.commit();
@@ -1 +1 @@
1
- {"version":3,"file":"user_resources.js","names":[],"sources":["../../src/core/user_resources.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { LLPlClient } from \"./ll_client\";\nimport type { PlTransaction } from \"./transaction\";\nimport { toGlobalResourceId } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId, ResourceType } from \"./types\";\nimport {\n createSignedResourceId,\n isNotNullSignedResourceId,\n NullSignedResourceId,\n toResourceSignature,\n} from \"./types\";\nimport { isUnimplementedError } from \"./errors\";\nimport { ClientRoot } from \"../helpers/pl\";\n\nconst AnonymousClientRoot = \"AnonymousRoot\";\nconst LsStorageTypePrefix = \"LS/\"; // implements ls API in particular storage\nconst LsProviderFieldPrefix = \"storage/\"; // provides access to storages list\n\n/** Information about a single data library (LS storage). */\nexport interface StorageInfo {\n /** Machine-stable identifier, e.g. \"library\". Used for filtering and map keys. */\n readonly storageId: string;\n /** Human-readable display name. For V1/legacy equals storageId; for V2 from resource JSON data. */\n readonly storageName: string;\n /** Signed resource ID for this storage resource. */\n readonly resourceId: SignedResourceId;\n /** Full resource type including correct version (\"1\" or \"2\"). */\n readonly resourceType: ResourceType;\n}\n\n/** V2 LsStorage resource JSON data shape (contract with backend). */\ninterface LsStorageV2Data {\n storageName: string;\n storageID: string;\n}\n\n/**\n * Callback type for running transactions. Matches PlClient._withTx signature\n * so the index can run transactions before PlClient is fully initialized.\n */\nexport type TxRunner = <T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalSignedResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n) => Promise<T>;\n\ntype BackendCapability = \"getUserRoot\" | \"listUserResources\" | \"legacy\";\n\n/**\n * Abstracts user resource discovery with backward compatibility.\n *\n * Detects backend capability on the first getUserRoot() call and remembers\n * the result. Three-tier fallback:\n * 1. getUserRoot RPC (newest, supports doNotCreate)\n * 2. listUserResources RPC (streams all resources, picks userRoot)\n * 3. Named resource lookup/creation via transaction (legacy)\n */\nexport class UserResources {\n private backendCapability: BackendCapability | undefined;\n\n constructor(\n private readonly ll: LLPlClient,\n private readonly runTx: TxRunner,\n public readonly authUser: string | null,\n ) {}\n\n /**\n * Returns the user's root resource ID.\n *\n * On first call, detects backend capability by trying methods in order:\n * 1. getUserRoot RPC (newest)\n * 2. listUserResources RPC\n * 3. Named resource lookup/creation via transaction (legacy)\n */\n async getUserRoot(): Promise<SignedResourceId>;\n async getUserRoot(opts: { login?: string }): Promise<SignedResourceId>;\n async getUserRoot(opts: { login?: string; doNotCreate: false }): Promise<SignedResourceId>;\n async getUserRoot(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n async getUserRoot(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n if (this.backendCapability === undefined) {\n return await this.detectAndGetUserRoot(opts);\n }\n return await this.getUserRootWith(this.backendCapability, opts);\n }\n\n private async detectAndGetUserRoot(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n // 1. Try getUserRoot RPC\n try {\n const root = await this.getUserRootViaRpc(opts);\n this.backendCapability = \"getUserRoot\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 2. Try listUserResources\n try {\n const root = await this.getUserRootViaList(opts);\n this.backendCapability = \"listUserResources\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 3. Legacy fallback\n this.backendCapability = \"legacy\";\n return await this.getUserRootViaLegacy(opts);\n }\n\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n switch (capability) {\n case \"getUserRoot\":\n return await this.getUserRootViaRpc(opts);\n case \"listUserResources\":\n return await this.getUserRootViaList(opts);\n case \"legacy\":\n return await this.getUserRootViaLegacy(opts);\n }\n }\n\n /**\n * Returns all data libraries the user has access to.\n * Always fetches fresh from the server (no caching).\n */\n async getDataLibraries(\n opts: { login?: string; doNotCreateUserRoot?: boolean } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n if (this.backendCapability === undefined) {\n // First call — detect backend capability\n try {\n const libs = await this.getDataLibrariesViaList(opts);\n // getUserRoot RPC doesn't return libraries, but listUserResources does;\n // record at least \"listUserResources\" so future getUserRoot calls don't re-detect.\n this.backendCapability = \"listUserResources\";\n return libs;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n this.backendCapability = \"legacy\";\n return await this.getDataLibrariesViaLegacy();\n }\n }\n\n // A server that supports getUserRoot definitely supports listUserResources.\n if (this.backendCapability !== \"legacy\") {\n return await this.getDataLibrariesViaList(opts);\n }\n return await this.getDataLibrariesViaLegacy();\n }\n\n private async getUserRootViaRpc(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts: {\n login?: string;\n doNotCreate: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaRpc(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const resp = await this.ll.getUserRoot({\n login: opts.login,\n doNotCreate: opts.doNotCreate,\n });\n if (resp.userRoot === undefined) {\n if (opts.doNotCreate) return undefined;\n throw new Error(\"getUserRoot returned no userRoot entry\");\n }\n return createSignedResourceId(\n resp.userRoot.resourceId,\n toResourceSignature(resp.userRoot.resourceSignature),\n );\n }\n\n private async getUserRootViaList(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts: {\n login?: string;\n doNotCreate: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts: {\n login?: string;\n doNotCreate: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaList(\n opts: { login?: string; doNotCreate?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const responses = await this.ll.listUserResources({ login: opts.login, limit: 1 });\n for (const msg of responses) {\n if (msg.entry.oneofKind === \"userRoot\") {\n return createSignedResourceId(\n msg.entry.userRoot.resourceId,\n toResourceSignature(msg.entry.userRoot.resourceSignature),\n );\n }\n }\n throw new Error(\"listUserResources returned no userRoot entry\");\n }\n\n private async getDataLibrariesViaList(\n opts: { login?: string } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n const responses = await this.ll.listUserResources({ login: opts.login });\n\n // Collect all LS/* shared resources, separating V1 and V2\n const v1Entries: StorageInfo[] = [];\n const v2ResourceIds: { resourceId: SignedResourceId; resourceType: ResourceType }[] = [];\n\n for (const msg of responses) {\n if (msg.entry.oneofKind !== \"sharedResource\") continue;\n const sr = msg.entry.sharedResource;\n\n if (!sr.resourceType) continue;\n const typeName = sr.resourceType.name;\n const typeVersion = sr.resourceType.version;\n if (!typeName.startsWith(LsStorageTypePrefix)) continue;\n\n const rId = createSignedResourceId(sr.resourceId, toResourceSignature(sr.resourceSignature));\n const rType: ResourceType = { name: typeName, version: typeVersion };\n\n if (typeVersion === \"2\") {\n v2ResourceIds.push({ resourceId: rId, resourceType: rType });\n } else {\n // V1 or unknown version: derive storageId from type name\n const storageId = typeName.substring(LsStorageTypePrefix.length);\n v1Entries.push({\n storageId,\n storageName: storageId,\n resourceId: rId,\n resourceType: rType,\n });\n }\n }\n\n // Read V2 resource data in a single transaction\n let v2Entries: StorageInfo[] = [];\n if (v2ResourceIds.length > 0) {\n v2Entries = await this.runTx(\n \"ReadLsStorageV2Data\",\n false,\n NullSignedResourceId,\n async (tx) => {\n const entries: StorageInfo[] = [];\n for (const { resourceId, resourceType } of v2ResourceIds) {\n const rd = await tx.getResourceData(resourceId, false);\n if (rd.data) {\n const v2Data = JSON.parse(Buffer.from(rd.data).toString(\"utf-8\")) as LsStorageV2Data;\n entries.push({\n storageId: v2Data.storageID,\n storageName: v2Data.storageName,\n resourceId,\n resourceType,\n });\n }\n }\n return entries;\n },\n );\n }\n\n const result = new Map<string, StorageInfo>();\n for (const entry of [...v1Entries, ...v2Entries]) {\n result.set(entry.storageId, entry);\n }\n return result;\n }\n\n private async getUserRootViaLegacy(opts: { login?: string }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts: {\n login?: string;\n doNotCreateUserRoot: false;\n }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts: {\n login?: string;\n doNotCreateUserRoot: true;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaLegacy(\n opts: { login?: string; doNotCreateUserRoot?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const login = opts.login ?? this.authUser;\n const mainRootName =\n login === null ? AnonymousClientRoot : createHash(\"sha256\").update(login).digest(\"hex\");\n\n return await this.runTx(\"initialization\", true, NullSignedResourceId, async (tx) => {\n if (await tx.checkResourceNameExists(mainRootName)) {\n return await tx.getResourceByName(mainRootName);\n }\n\n if (opts.doNotCreateUserRoot) {\n return undefined;\n }\n\n const mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n });\n }\n\n private async getDataLibrariesViaLegacy(): Promise<ReadonlyMap<string, StorageInfo>> {\n return await this.runTx(\"GetAvailableStorageIds\", false, NullSignedResourceId, async (tx) => {\n const lsProviderId = await tx.getResourceByName(\"LSProvider\");\n const provider = await tx.getResourceData(lsProviderId, true);\n\n const result = new Map<string, StorageInfo>();\n for (const field of provider.fields) {\n if (field.type !== \"Dynamic\" || !isNotNullSignedResourceId(field.value)) continue;\n if (!field.name.startsWith(LsProviderFieldPrefix)) continue;\n\n const storageId = field.name.substring(LsProviderFieldPrefix.length);\n result.set(storageId, {\n storageId,\n storageName: storageId,\n resourceId: field.value,\n resourceType: { name: `${LsStorageTypePrefix}${storageId}`, version: \"1\" },\n });\n }\n return result;\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"user_resources.js","names":[],"sources":["../../src/core/user_resources.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { LLPlClient } from \"./ll_client\";\nimport type { PlTransaction } from \"./transaction\";\nimport { toGlobalResourceId } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId, ResourceType } from \"./types\";\nimport {\n createSignedResourceId,\n isNotNullSignedResourceId,\n NullSignedResourceId,\n toResourceSignature,\n} from \"./types\";\nimport { isUnimplementedError } from \"./errors\";\nimport { ClientRoot } from \"../helpers/pl\";\n\nconst AnonymousClientRoot = \"AnonymousRoot\";\nconst LsStorageTypePrefix = \"LS/\"; // implements ls API in particular storage\nconst LsProviderFieldPrefix = \"storage/\"; // provides access to storages list\n\n/** Information about a single data library (LS storage). */\nexport interface StorageInfo {\n /** Machine-stable identifier, e.g. \"library\". Used for filtering and map keys. */\n readonly storageId: string;\n /** Human-readable display name. For V1/legacy equals storageId; for V2 from resource JSON data. */\n readonly storageName: string;\n /** Signed resource ID for this storage resource. */\n readonly resourceId: SignedResourceId;\n /** Full resource type including correct version (\"1\" or \"2\"). */\n readonly resourceType: ResourceType;\n}\n\n/** V2 LsStorage resource JSON data shape (contract with backend). */\ninterface LsStorageV2Data {\n storageName: string;\n storageID: string;\n}\n\n/**\n * Callback type for running transactions. Matches PlClient._withTx signature\n * so the index can run transactions before PlClient is fully initialized.\n */\nexport type TxRunner = <T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalSignedResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n) => Promise<T>;\n\ntype BackendCapability = \"getUserRoot\" | \"listUserResources\" | \"legacy\";\n\n/**\n * Abstracts user resource discovery with backward compatibility.\n *\n * Detects backend capability on the first getUserRoot() call and remembers\n * the result. Three-tier fallback:\n * 1. getUserRoot RPC (newest, supports createIfNotExists)\n * 2. listUserResources RPC (streams all resources, picks userRoot)\n * 3. Named resource lookup/creation via transaction (legacy)\n */\nexport class UserResources {\n private backendCapability: BackendCapability | undefined;\n\n constructor(\n private readonly ll: LLPlClient,\n private readonly runTx: TxRunner,\n public readonly authUser: string | null,\n ) {}\n\n /**\n * Returns the user's root resource ID.\n *\n * On first call, detects backend capability by trying methods in order:\n * 1. getUserRoot RPC (newest)\n * 2. listUserResources RPC\n * 3. Named resource lookup/creation via transaction (legacy)\n */\n async getUserRoot(opts: { login?: string; createIfNotExists: true }): Promise<SignedResourceId>;\n async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n if (this.backendCapability === undefined) {\n return await this.detectAndGetUserRoot(opts);\n }\n return await this.getUserRootWith(this.backendCapability, opts);\n }\n\n private async detectAndGetUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async detectAndGetUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async detectAndGetUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n // 1. Try getUserRoot RPC\n try {\n const root = await this.getUserRootViaRpc(opts);\n this.backendCapability = \"getUserRoot\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 2. Try listUserResources\n try {\n const root = await this.getUserRootViaList(opts);\n this.backendCapability = \"listUserResources\";\n return root;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n }\n\n // 3. Legacy fallback\n this.backendCapability = \"legacy\";\n return await this.getUserRootViaLegacy(opts);\n }\n\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; createIfNotExists: true },\n ): Promise<SignedResourceId>;\n private async getUserRootWith(\n capability: BackendCapability,\n opts?: { login?: string; createIfNotExists?: boolean },\n ): Promise<SignedResourceId | undefined>;\n private async getUserRootWith(\n capability: BackendCapability,\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n switch (capability) {\n case \"getUserRoot\":\n return await this.getUserRootViaRpc(opts);\n case \"listUserResources\":\n return await this.getUserRootViaList(opts);\n case \"legacy\":\n return await this.getUserRootViaLegacy(opts);\n }\n }\n\n /**\n * Returns all data libraries the user has access to.\n * Always fetches fresh from the server (no caching).\n */\n async getDataLibraries(\n opts: { login?: string; createUserRootIfNotExists?: boolean } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n if (this.backendCapability === undefined) {\n // First call — detect backend capability\n try {\n const libs = await this.getDataLibrariesViaList(opts);\n // getUserRoot RPC doesn't return libraries, but listUserResources does;\n // record at least \"listUserResources\" so future getUserRoot calls don't re-detect.\n this.backendCapability = \"listUserResources\";\n return libs;\n } catch (err) {\n if (!isUnimplementedError(err)) throw err;\n this.backendCapability = \"legacy\";\n return await this.getDataLibrariesViaLegacy();\n }\n }\n\n // A server that supports getUserRoot definitely supports listUserResources.\n if (this.backendCapability !== \"legacy\") {\n return await this.getDataLibrariesViaList(opts);\n }\n return await this.getDataLibrariesViaLegacy();\n }\n\n private async getUserRootViaRpc(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaRpc(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaRpc(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const resp = await this.ll.getUserRoot({\n login: opts.login,\n createIfNotExists: opts.createIfNotExists,\n });\n if (resp.userRoot === undefined) {\n if (!opts.createIfNotExists) return undefined;\n throw new Error(\"getUserRoot returned no userRoot entry\");\n }\n return createSignedResourceId(\n resp.userRoot.resourceId,\n toResourceSignature(resp.userRoot.resourceSignature),\n );\n }\n\n private async getUserRootViaList(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaList(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaList(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const responses = await this.ll.listUserResources({ login: opts.login, limit: 1 });\n for (const msg of responses) {\n if (msg.entry.oneofKind === \"userRoot\") {\n return createSignedResourceId(\n msg.entry.userRoot.resourceId,\n toResourceSignature(msg.entry.userRoot.resourceSignature),\n );\n }\n }\n\n if (opts.createIfNotExists) {\n throw new Error(\"listUserResources returned no userRoot entry\");\n }\n\n return undefined;\n }\n\n private async getDataLibrariesViaList(\n opts: { login?: string } = {},\n ): Promise<ReadonlyMap<string, StorageInfo>> {\n const responses = await this.ll.listUserResources({ login: opts.login });\n\n // Collect all LS/* shared resources, separating V1 and V2\n const v1Entries: StorageInfo[] = [];\n const v2ResourceIds: { resourceId: SignedResourceId; resourceType: ResourceType }[] = [];\n\n for (const msg of responses) {\n if (msg.entry.oneofKind !== \"sharedResource\") continue;\n const sr = msg.entry.sharedResource;\n\n if (!sr.resourceType) continue;\n const typeName = sr.resourceType.name;\n const typeVersion = sr.resourceType.version;\n if (!typeName.startsWith(LsStorageTypePrefix)) continue;\n\n const rId = createSignedResourceId(sr.resourceId, toResourceSignature(sr.resourceSignature));\n const rType: ResourceType = { name: typeName, version: typeVersion };\n\n if (typeVersion === \"2\") {\n v2ResourceIds.push({ resourceId: rId, resourceType: rType });\n } else {\n // V1 or unknown version: derive storageId from type name\n const storageId = typeName.substring(LsStorageTypePrefix.length);\n v1Entries.push({\n storageId,\n storageName: storageId,\n resourceId: rId,\n resourceType: rType,\n });\n }\n }\n\n // Read V2 resource data in a single transaction\n let v2Entries: StorageInfo[] = [];\n if (v2ResourceIds.length > 0) {\n v2Entries = await this.runTx(\n \"ReadLsStorageV2Data\",\n false,\n NullSignedResourceId,\n async (tx) => {\n const entries: StorageInfo[] = [];\n for (const { resourceId, resourceType } of v2ResourceIds) {\n const rd = await tx.getResourceData(resourceId, false);\n if (rd.data) {\n const v2Data = JSON.parse(Buffer.from(rd.data).toString(\"utf-8\")) as LsStorageV2Data;\n entries.push({\n storageId: v2Data.storageID,\n storageName: v2Data.storageName,\n resourceId,\n resourceType,\n });\n }\n }\n return entries;\n },\n );\n }\n\n const result = new Map<string, StorageInfo>();\n for (const entry of [...v1Entries, ...v2Entries]) {\n result.set(entry.storageId, entry);\n }\n return result;\n }\n\n private async getUserRootViaLegacy(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n private async getUserRootViaLegacy(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n private async getUserRootViaLegacy(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n const login = opts.login ?? this.authUser;\n const mainRootName =\n login === null ? AnonymousClientRoot : createHash(\"sha256\").update(login).digest(\"hex\");\n\n return await this.runTx(\"initialization\", true, NullSignedResourceId, async (tx) => {\n if (await tx.checkResourceNameExists(mainRootName)) {\n return await tx.getResourceByName(mainRootName);\n }\n\n if (!opts.createIfNotExists) {\n return undefined;\n }\n\n const mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n });\n }\n\n private async getDataLibrariesViaLegacy(): Promise<ReadonlyMap<string, StorageInfo>> {\n return await this.runTx(\"GetAvailableStorageIds\", false, NullSignedResourceId, async (tx) => {\n const lsProviderId = await tx.getResourceByName(\"LSProvider\");\n const provider = await tx.getResourceData(lsProviderId, true);\n\n const result = new Map<string, StorageInfo>();\n for (const field of provider.fields) {\n if (field.type !== \"Dynamic\" || !isNotNullSignedResourceId(field.value)) continue;\n if (!field.name.startsWith(LsProviderFieldPrefix)) continue;\n\n const storageId = field.name.substring(LsProviderFieldPrefix.length);\n result.set(storageId, {\n storageId,\n storageName: storageId,\n resourceId: field.value,\n resourceType: { name: `${LsStorageTypePrefix}${storageId}`, version: \"1\" },\n });\n }\n return result;\n });\n }\n}\n"],"mappings":"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"}
@@ -13533,8 +13533,8 @@ var AuthAPI_GetUserRoot_Request$Type = class extends _protobuf_ts_runtime.Messag
13533
13533
  kind: "scalar",
13534
13534
  T: 9
13535
13535
  }, {
13536
- no: 2,
13537
- name: "do_not_create",
13536
+ no: 3,
13537
+ name: "create_if_not_exists",
13538
13538
  kind: "scalar",
13539
13539
  T: 8
13540
13540
  }]);
@@ -13542,7 +13542,7 @@ var AuthAPI_GetUserRoot_Request$Type = class extends _protobuf_ts_runtime.Messag
13542
13542
  create(value) {
13543
13543
  const message = globalThis.Object.create(this.messagePrototype);
13544
13544
  message.login = "";
13545
- message.doNotCreate = false;
13545
+ message.createIfNotExists = false;
13546
13546
  if (value !== void 0) (0, _protobuf_ts_runtime.reflectionMergePartial)(this, message, value);
13547
13547
  return message;
13548
13548
  }
@@ -13554,8 +13554,8 @@ var AuthAPI_GetUserRoot_Request$Type = class extends _protobuf_ts_runtime.Messag
13554
13554
  case 1:
13555
13555
  message.login = reader.string();
13556
13556
  break;
13557
- case 2:
13558
- message.doNotCreate = reader.bool();
13557
+ case 3:
13558
+ message.createIfNotExists = reader.bool();
13559
13559
  break;
13560
13560
  default:
13561
13561
  let u = options.readUnknownField;
@@ -13568,7 +13568,7 @@ var AuthAPI_GetUserRoot_Request$Type = class extends _protobuf_ts_runtime.Messag
13568
13568
  }
13569
13569
  internalBinaryWrite(message, writer, options) {
13570
13570
  if (message.login !== "") writer.tag(1, _protobuf_ts_runtime.WireType.LengthDelimited).string(message.login);
13571
- if (message.doNotCreate !== false) writer.tag(2, _protobuf_ts_runtime.WireType.Varint).bool(message.doNotCreate);
13571
+ if (message.createIfNotExists !== false) writer.tag(3, _protobuf_ts_runtime.WireType.Varint).bool(message.createIfNotExists);
13572
13572
  let u = options.writeUnknownFields;
13573
13573
  if (u !== false) (u == true ? _protobuf_ts_runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
13574
13574
  return writer;