@milaboratories/pl-drivers 1.10.9 → 1.10.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/download.cjs +1 -0
- package/dist/clients/download.cjs.map +1 -1
- package/dist/clients/download.d.ts +4 -3
- package/dist/clients/download.d.ts.map +1 -1
- package/dist/clients/download.js +1 -0
- package/dist/clients/download.js.map +1 -1
- package/dist/drivers/download_blob/download_blob.cjs +38 -12
- package/dist/drivers/download_blob/download_blob.cjs.map +1 -1
- package/dist/drivers/download_blob/download_blob.d.ts +8 -1
- package/dist/drivers/download_blob/download_blob.d.ts.map +1 -1
- package/dist/drivers/download_blob/download_blob.js +39 -13
- package/dist/drivers/download_blob/download_blob.js.map +1 -1
- package/dist/drivers/download_blob/sparse_cache/ranges.d.ts.map +1 -1
- package/dist/drivers/helpers/helpers.cjs.map +1 -1
- package/dist/drivers/helpers/helpers.d.ts.map +1 -1
- package/dist/drivers/helpers/helpers.js.map +1 -1
- package/dist/drivers/helpers/read_file.cjs +19 -11
- package/dist/drivers/helpers/read_file.cjs.map +1 -1
- package/dist/drivers/helpers/read_file.d.ts +6 -1
- package/dist/drivers/helpers/read_file.d.ts.map +1 -1
- package/dist/drivers/helpers/read_file.js +18 -11
- package/dist/drivers/helpers/read_file.js.map +1 -1
- package/dist/helpers/download.cjs +3 -0
- package/dist/helpers/download.cjs.map +1 -1
- package/dist/helpers/download.d.ts +2 -6
- package/dist/helpers/download.d.ts.map +1 -1
- package/dist/helpers/download.js +3 -0
- package/dist/helpers/download.js.map +1 -1
- package/package.json +8 -8
- package/src/clients/download.ts +5 -4
- package/src/drivers/download_blob/download_blob.ts +68 -19
- package/src/drivers/helpers/helpers.ts +0 -9
- package/src/drivers/helpers/read_file.ts +29 -11
- package/src/helpers/download.ts +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.cjs","sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { GrpcClientProvider, GrpcClientProviderFactory } from '@milaboratories/pl-client';\nimport { addRTypeToMetadata, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type { ResourceInfo } from '@milaboratories/pl-tree';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { ConcurrencyLimitingExecutor } from '@milaboratories/ts-helpers';\nimport type { RpcOptions } from '@protobuf-ts/runtime-rpc';\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { Dispatcher } from 'undici';\nimport type { LocalStorageProjection } from '../drivers/types';\nimport type { DownloadOps, ContentHandler } from '../helpers/download';\nimport { RemoteFileDownloader } from '../helpers/download';\nimport { validateAbsolute } from '../helpers/validate';\nimport type { DownloadAPI_GetDownloadURL_Response } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol';\nimport { DownloadClient } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client';\n\n/** Gets URLs for downloading from pl-core, parses them and reads or downloads\n * files locally and from the web. */\nexport class ClientDownload {\n public readonly grpcClient: GrpcClientProvider<DownloadClient>;\n private readonly remoteFileDownloader: RemoteFileDownloader;\n\n /** Helps to find a storage root directory by a storage id from URL scheme. */\n private readonly localStorageIdsToRoot: Map<string, string>;\n\n /** Concurrency limiter for local file reads - limit to 32 parallel reads */\n private readonly localFileReadLimiter = new ConcurrencyLimitingExecutor(32);\n\n constructor(\n grpcClientProviderFactory: GrpcClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.grpcClient = grpcClientProviderFactory.createGrpcClientProvider((transport) => new DownloadClient(transport));\n this.remoteFileDownloader = new RemoteFileDownloader(httpClient);\n this.localStorageIdsToRoot = newLocalStorageIdsToRoot(localProjections);\n }\n\n close() {}\n\n /**\n * Gets a presign URL and downloads the file.\n * An optional range with 2 numbers from what byte and to what byte to download can be provided.\n * @param fromBytes - from byte including this byte\n * @param toBytes - to byte excluding this byte\n */\n async withBlobContent<T>(\n info: ResourceInfo,\n options: RpcOptions | undefined,\n ops: DownloadOps,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { downloadUrl, headers } = await this.grpcGetDownloadUrl(info, options, ops.signal);\n\n const remoteHeaders = Object.fromEntries(headers.map(({ name, value }) => [name, value]));\n this.logger.info(`download blob ${stringifyWithResourceId(info)} from url ${downloadUrl}, ops: ${JSON.stringify(ops)}`);\n\n return isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n }\n\n async withLocalFileContent<T>(\n url: string,\n ops: DownloadOps,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { storageId, relativePath } = parseLocalUrl(url);\n const fullPath = getFullPath(storageId, this.localStorageIdsToRoot, relativePath);\n\n return await this.localFileReadLimiter.run(async () => {\n const readOps = {\n start: ops.range?.from,\n end: ops.range?.to !== undefined ? ops.range.to - 1 : undefined,\n };\n let stream: fs.ReadStream | undefined;\n let handlerSuccess = false;\n\n try {\n const stat = await fsp.stat(fullPath);\n stream = fs.createReadStream(fullPath, readOps);\n const webStream = Readable.toWeb(stream);\n\n const result = await handler(webStream, stat.size);\n handlerSuccess = true;\n return result;\n } catch (error) {\n // Cleanup on error (including handler errors)\n if (!handlerSuccess && stream && !stream.destroyed) {\n stream.destroy();\n }\n throw error;\n }\n });\n }\n\n private async grpcGetDownloadUrl(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n signal?: AbortSignal,\n ): Promise<DownloadAPI_GetDownloadURL_Response> {\n const withAbort = options ?? {};\n withAbort.abort = signal;\n\n return await this.grpcClient.get().getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n }\n}\n\nexport function parseLocalUrl(url: string) {\n const parsed = new URL(url);\n if (parsed.pathname == '')\n throw new WrongLocalFileUrl(`url for local filepath ${url} does not match url scheme`);\n\n return {\n storageId: parsed.host,\n relativePath: decodeURIComponent(parsed.pathname.slice(1)),\n };\n}\n\nexport function getFullPath(\n storageId: string,\n localStorageIdsToRoot: Map<string, string>,\n relativePath: string,\n) {\n const root = localStorageIdsToRoot.get(storageId);\n if (root === undefined) throw new UnknownStorageError(`Unknown storage location: ${storageId}`);\n\n if (root === '') return relativePath;\n\n return path.join(root, relativePath);\n}\n\nconst storageProtocol = 'storage://';\nfunction isLocal(url: string) {\n return url.startsWith(storageProtocol);\n}\n\n/** Throws when a local URL have invalid scheme. */\nexport class WrongLocalFileUrl extends Error {\n name = 'WrongLocalFileUrl';\n}\n\n/** Happens when a storage for a local file can't be found. */\nexport class UnknownStorageError extends Error {\n name = 'UnknownStorageError';\n}\n\nexport function newLocalStorageIdsToRoot(projections: LocalStorageProjection[]) {\n const idToRoot: Map<string, string> = new Map();\n for (const lp of projections) {\n // Empty string means no prefix, i.e. any file on this machine can be got from the storage.\n if (lp.localPath !== '') {\n validateAbsolute(lp.localPath);\n }\n idToRoot.set(lp.storageId, lp.localPath);\n }\n\n return idToRoot;\n}\n"],"names":["ConcurrencyLimitingExecutor","DownloadClient","RemoteFileDownloader","stringifyWithResourceId","fsp","fs","Readable","addRTypeToMetadata","path","validateAbsolute"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA;AACqC;MACxB,cAAc,CAAA;AAYP,IAAA,UAAA;AACA,IAAA,MAAA;AAZF,IAAA,UAAU;AACT,IAAA,oBAAoB;;AAGpB,IAAA,qBAAqB;;AAGrB,IAAA,oBAAoB,GAAG,IAAIA,qCAA2B,CAAC,EAAE,CAAC;AAE3E,IAAA,WAAA,CACE,yBAAoD,EACpC,UAAsB,EACtB,MAAgB;;IAEhC,gBAA0C,EAAA;QAH1B,IAAA,CAAA,UAAU,GAAV,UAAU;QACV,IAAA,CAAA,MAAM,GAAN,MAAM;AAItB,QAAA,IAAI,CAAC,UAAU,GAAG,yBAAyB,CAAC,wBAAwB,CAAC,CAAC,SAAS,KAAK,IAAIC,8BAAc,CAAC,SAAS,CAAC,CAAC;QAClH,IAAI,CAAC,oBAAoB,GAAG,IAAIC,6BAAoB,CAAC,UAAU,CAAC;AAChE,QAAA,IAAI,CAAC,qBAAqB,GAAG,wBAAwB,CAAC,gBAAgB,CAAC;IACzE;AAEA,IAAA,KAAK,KAAI;AAET;;;;;AAKG;IACH,MAAM,eAAe,CACnB,IAAkB,EAClB,OAA+B,EAC/B,GAAgB,EAChB,OAA0B,EAAA;AAE1B,QAAA,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC;QAEzF,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA,cAAA,EAAiBC,gCAAuB,CAAC,IAAI,CAAC,aAAa,WAAW,CAAA,OAAA,EAAU,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA,CAAE,CAAC;QAEvH,OAAO,OAAO,CAAC,WAAW;cACtB,MAAM,IAAI,CAAC,oBAAoB,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO;AAC3D,cAAE,MAAM,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,WAAW,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,CAAC;IAC3F;AAEA,IAAA,MAAM,oBAAoB,CACxB,GAAW,EACX,GAAgB,EAChB,OAA0B,EAAA;QAE1B,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC;AACtD,QAAA,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,CAAC;QAEjF,OAAO,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,YAAW;AACpD,YAAA,MAAM,OAAO,GAAG;AACd,gBAAA,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI;gBACtB,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,GAAG,SAAS;aAChE;AACD,YAAA,IAAI,MAAiC;YACrC,IAAI,cAAc,GAAG,KAAK;AAE1B,YAAA,IAAI;gBACF,MAAM,IAAI,GAAG,MAAMC,cAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrC,MAAM,GAAGC,aAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAC/C,MAAM,SAAS,GAAGC,oBAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;gBAExC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC;gBAClD,cAAc,GAAG,IAAI;AACrB,gBAAA,OAAO,MAAM;YACf;YAAE,OAAO,KAAK,EAAE;;gBAEd,IAAI,CAAC,cAAc,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;oBAClD,MAAM,CAAC,OAAO,EAAE;gBAClB;AACA,gBAAA,MAAM,KAAK;YACb;AACF,QAAA,CAAC,CAAC;IACJ;IAEQ,MAAM,kBAAkB,CAC9B,EAAE,EAAE,EAAE,IAAI,EAAgB,EAC1B,OAAoB,EACpB,MAAoB,EAAA;AAEpB,QAAA,MAAM,SAAS,GAAG,OAAO,IAAI,EAAE;AAC/B,QAAA,SAAS,CAAC,KAAK,GAAG,MAAM;AAExB,QAAA,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAC/C,EAAE,UAAU,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,EACxCC,2BAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CACpC,CAAC,QAAQ;IACZ;AACD;AAEK,SAAU,aAAa,CAAC,GAAW,EAAA;AACvC,IAAA,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC;AAC3B,IAAA,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE;AACvB,QAAA,MAAM,IAAI,iBAAiB,CAAC,0BAA0B,GAAG,CAAA,0BAAA,CAA4B,CAAC;IAExF,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,IAAI;QACtB,YAAY,EAAE,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAC3D;AACH;SAEgB,WAAW,CACzB,SAAiB,EACjB,qBAA0C,EAC1C,YAAoB,EAAA;IAEpB,MAAM,IAAI,GAAG,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC;IACjD,IAAI,IAAI,KAAK,SAAS;AAAE,QAAA,MAAM,IAAI,mBAAmB,CAAC,6BAA6B,SAAS,CAAA,CAAE,CAAC;IAE/F,IAAI,IAAI,KAAK,EAAE;AAAE,QAAA,OAAO,YAAY;IAEpC,OAAOC,eAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;AACtC;AAEA,MAAM,eAAe,GAAG,YAAY;AACpC,SAAS,OAAO,CAAC,GAAW,EAAA;AAC1B,IAAA,OAAO,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;AACxC;AAEA;AACM,MAAO,iBAAkB,SAAQ,KAAK,CAAA;IAC1C,IAAI,GAAG,mBAAmB;AAC3B;AAED;AACM,MAAO,mBAAoB,SAAQ,KAAK,CAAA;IAC5C,IAAI,GAAG,qBAAqB;AAC7B;AAEK,SAAU,wBAAwB,CAAC,WAAqC,EAAA;AAC5E,IAAA,MAAM,QAAQ,GAAwB,IAAI,GAAG,EAAE;AAC/C,IAAA,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE;;AAE5B,QAAA,IAAI,EAAE,CAAC,SAAS,KAAK,EAAE,EAAE;AACvB,YAAAC,yBAAgB,CAAC,EAAE,CAAC,SAAS,CAAC;QAChC;QACA,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC;IAC1C;AAEA,IAAA,OAAO,QAAQ;AACjB;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"download.cjs","sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { GrpcClientProvider, GrpcClientProviderFactory } from '@milaboratories/pl-client';\nimport { addRTypeToMetadata, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type { ResourceInfo } from '@milaboratories/pl-tree';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { ConcurrencyLimitingExecutor } from '@milaboratories/ts-helpers';\nimport type { RpcOptions } from '@protobuf-ts/runtime-rpc';\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { Dispatcher } from 'undici';\nimport type { LocalStorageProjection } from '../drivers/types';\nimport { type ContentHandler, RemoteFileDownloader } from '../helpers/download';\nimport { validateAbsolute } from '../helpers/validate';\nimport type { DownloadAPI_GetDownloadURL_Response } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol';\nimport { DownloadClient } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client';\nimport { type GetContentOptions } from '@milaboratories/pl-model-common';\n\n/** Gets URLs for downloading from pl-core, parses them and reads or downloads\n * files locally and from the web. */\nexport class ClientDownload {\n public readonly grpcClient: GrpcClientProvider<DownloadClient>;\n private readonly remoteFileDownloader: RemoteFileDownloader;\n\n /** Helps to find a storage root directory by a storage id from URL scheme. */\n private readonly localStorageIdsToRoot: Map<string, string>;\n\n /** Concurrency limiter for local file reads - limit to 32 parallel reads */\n private readonly localFileReadLimiter = new ConcurrencyLimitingExecutor(32);\n\n constructor(\n grpcClientProviderFactory: GrpcClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.grpcClient = grpcClientProviderFactory.createGrpcClientProvider((transport) => new DownloadClient(transport));\n this.remoteFileDownloader = new RemoteFileDownloader(httpClient);\n this.localStorageIdsToRoot = newLocalStorageIdsToRoot(localProjections);\n }\n\n close() {}\n\n /**\n * Gets a presign URL and downloads the file.\n * An optional range with 2 numbers from what byte and to what byte to download can be provided.\n * @param fromBytes - from byte including this byte\n * @param toBytes - to byte excluding this byte\n */\n async withBlobContent<T>(\n info: ResourceInfo,\n options: RpcOptions | undefined,\n ops: GetContentOptions,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { downloadUrl, headers } = await this.grpcGetDownloadUrl(info, options, ops.signal);\n\n const remoteHeaders = Object.fromEntries(headers.map(({ name, value }) => [name, value]));\n this.logger.info(`download blob ${stringifyWithResourceId(info)} from url ${downloadUrl}, ops: ${JSON.stringify(ops)}`);\n\n return isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n }\n\n async withLocalFileContent<T>(\n url: string,\n ops: GetContentOptions,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { storageId, relativePath } = parseLocalUrl(url);\n const fullPath = getFullPath(storageId, this.localStorageIdsToRoot, relativePath);\n\n return await this.localFileReadLimiter.run(async () => {\n const readOps = {\n start: ops.range?.from,\n end: ops.range?.to !== undefined ? ops.range.to - 1 : undefined,\n signal: ops.signal,\n };\n let stream: fs.ReadStream | undefined;\n let handlerSuccess = false;\n\n try {\n const stat = await fsp.stat(fullPath);\n stream = fs.createReadStream(fullPath, readOps);\n const webStream = Readable.toWeb(stream);\n\n const result = await handler(webStream, stat.size);\n handlerSuccess = true;\n return result;\n } catch (error) {\n // Cleanup on error (including handler errors)\n if (!handlerSuccess && stream && !stream.destroyed) {\n stream.destroy();\n }\n throw error;\n }\n });\n }\n\n private async grpcGetDownloadUrl(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n signal?: AbortSignal,\n ): Promise<DownloadAPI_GetDownloadURL_Response> {\n const withAbort = options ?? {};\n withAbort.abort = signal;\n\n return await this.grpcClient.get().getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n }\n}\n\nexport function parseLocalUrl(url: string) {\n const parsed = new URL(url);\n if (parsed.pathname == '')\n throw new WrongLocalFileUrl(`url for local filepath ${url} does not match url scheme`);\n\n return {\n storageId: parsed.host,\n relativePath: decodeURIComponent(parsed.pathname.slice(1)),\n };\n}\n\nexport function getFullPath(\n storageId: string,\n localStorageIdsToRoot: Map<string, string>,\n relativePath: string,\n) {\n const root = localStorageIdsToRoot.get(storageId);\n if (root === undefined) throw new UnknownStorageError(`Unknown storage location: ${storageId}`);\n\n if (root === '') return relativePath;\n\n return path.join(root, relativePath);\n}\n\nconst storageProtocol = 'storage://';\nfunction isLocal(url: string) {\n return url.startsWith(storageProtocol);\n}\n\n/** Throws when a local URL have invalid scheme. */\nexport class WrongLocalFileUrl extends Error {\n name = 'WrongLocalFileUrl';\n}\n\n/** Happens when a storage for a local file can't be found. */\nexport class UnknownStorageError extends Error {\n name = 'UnknownStorageError';\n}\n\nexport function newLocalStorageIdsToRoot(projections: LocalStorageProjection[]) {\n const idToRoot: Map<string, string> = new Map();\n for (const lp of projections) {\n // Empty string means no prefix, i.e. any file on this machine can be got from the storage.\n if (lp.localPath !== '') {\n validateAbsolute(lp.localPath);\n }\n idToRoot.set(lp.storageId, lp.localPath);\n }\n\n return idToRoot;\n}\n"],"names":["ConcurrencyLimitingExecutor","DownloadClient","RemoteFileDownloader","stringifyWithResourceId","fsp","fs","Readable","addRTypeToMetadata","path","validateAbsolute"],"mappings":"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"}
|
|
@@ -4,8 +4,9 @@ import type { MiLogger } from '@milaboratories/ts-helpers';
|
|
|
4
4
|
import type { RpcOptions } from '@protobuf-ts/runtime-rpc';
|
|
5
5
|
import type { Dispatcher } from 'undici';
|
|
6
6
|
import type { LocalStorageProjection } from '../drivers/types';
|
|
7
|
-
import
|
|
7
|
+
import { type ContentHandler } from '../helpers/download';
|
|
8
8
|
import { DownloadClient } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client';
|
|
9
|
+
import { type GetContentOptions } from '@milaboratories/pl-model-common';
|
|
9
10
|
/** Gets URLs for downloading from pl-core, parses them and reads or downloads
|
|
10
11
|
* files locally and from the web. */
|
|
11
12
|
export declare class ClientDownload {
|
|
@@ -27,8 +28,8 @@ export declare class ClientDownload {
|
|
|
27
28
|
* @param fromBytes - from byte including this byte
|
|
28
29
|
* @param toBytes - to byte excluding this byte
|
|
29
30
|
*/
|
|
30
|
-
withBlobContent<T>(info: ResourceInfo, options: RpcOptions | undefined, ops:
|
|
31
|
-
withLocalFileContent<T>(url: string, ops:
|
|
31
|
+
withBlobContent<T>(info: ResourceInfo, options: RpcOptions | undefined, ops: GetContentOptions, handler: ContentHandler<T>): Promise<T>;
|
|
32
|
+
withLocalFileContent<T>(url: string, ops: GetContentOptions, handler: ContentHandler<T>): Promise<T>;
|
|
32
33
|
private grpcGetDownloadUrl;
|
|
33
34
|
}
|
|
34
35
|
export declare function parseLocalUrl(url: string): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/clients/download.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAE/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAK3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/clients/download.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAE/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAK3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,qBAAqB,CAAC;AAGhF,OAAO,EAAE,cAAc,EAAE,MAAM,yFAAyF,CAAC;AACzH,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;qCACqC;AACrC,qBAAa,cAAc;aAYP,UAAU,EAAE,UAAU;aACtB,MAAM,EAAE,QAAQ;IAZlC,SAAgB,UAAU,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAC/D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAuB;IAE5D,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAsB;IAE5D,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAuC;gBAG1E,yBAAyB,EAAE,yBAAyB,EACpC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ;IAChC,oCAAoC;IACpC,gBAAgB,EAAE,sBAAsB,EAAE;IAO5C,KAAK;IAEL;;;;;OAKG;IACG,eAAe,CAAC,CAAC,EACrB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC;IAWP,oBAAoB,CAAC,CAAC,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC;YA+BC,kBAAkB;CAajC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM;;;EASxC;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1C,YAAY,EAAE,MAAM,UAQrB;AAOD,mDAAmD;AACnD,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,IAAI,SAAuB;CAC5B;AAED,+DAA+D;AAC/D,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,IAAI,SAAyB;CAC9B;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,sBAAsB,EAAE,uBAW7E"}
|
package/dist/clients/download.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.js","sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { GrpcClientProvider, GrpcClientProviderFactory } from '@milaboratories/pl-client';\nimport { addRTypeToMetadata, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type { ResourceInfo } from '@milaboratories/pl-tree';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { ConcurrencyLimitingExecutor } from '@milaboratories/ts-helpers';\nimport type { RpcOptions } from '@protobuf-ts/runtime-rpc';\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { Dispatcher } from 'undici';\nimport type { LocalStorageProjection } from '../drivers/types';\nimport type { DownloadOps, ContentHandler } from '../helpers/download';\nimport { RemoteFileDownloader } from '../helpers/download';\nimport { validateAbsolute } from '../helpers/validate';\nimport type { DownloadAPI_GetDownloadURL_Response } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol';\nimport { DownloadClient } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client';\n\n/** Gets URLs for downloading from pl-core, parses them and reads or downloads\n * files locally and from the web. */\nexport class ClientDownload {\n public readonly grpcClient: GrpcClientProvider<DownloadClient>;\n private readonly remoteFileDownloader: RemoteFileDownloader;\n\n /** Helps to find a storage root directory by a storage id from URL scheme. */\n private readonly localStorageIdsToRoot: Map<string, string>;\n\n /** Concurrency limiter for local file reads - limit to 32 parallel reads */\n private readonly localFileReadLimiter = new ConcurrencyLimitingExecutor(32);\n\n constructor(\n grpcClientProviderFactory: GrpcClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.grpcClient = grpcClientProviderFactory.createGrpcClientProvider((transport) => new DownloadClient(transport));\n this.remoteFileDownloader = new RemoteFileDownloader(httpClient);\n this.localStorageIdsToRoot = newLocalStorageIdsToRoot(localProjections);\n }\n\n close() {}\n\n /**\n * Gets a presign URL and downloads the file.\n * An optional range with 2 numbers from what byte and to what byte to download can be provided.\n * @param fromBytes - from byte including this byte\n * @param toBytes - to byte excluding this byte\n */\n async withBlobContent<T>(\n info: ResourceInfo,\n options: RpcOptions | undefined,\n ops: DownloadOps,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { downloadUrl, headers } = await this.grpcGetDownloadUrl(info, options, ops.signal);\n\n const remoteHeaders = Object.fromEntries(headers.map(({ name, value }) => [name, value]));\n this.logger.info(`download blob ${stringifyWithResourceId(info)} from url ${downloadUrl}, ops: ${JSON.stringify(ops)}`);\n\n return isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n }\n\n async withLocalFileContent<T>(\n url: string,\n ops: DownloadOps,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { storageId, relativePath } = parseLocalUrl(url);\n const fullPath = getFullPath(storageId, this.localStorageIdsToRoot, relativePath);\n\n return await this.localFileReadLimiter.run(async () => {\n const readOps = {\n start: ops.range?.from,\n end: ops.range?.to !== undefined ? ops.range.to - 1 : undefined,\n };\n let stream: fs.ReadStream | undefined;\n let handlerSuccess = false;\n\n try {\n const stat = await fsp.stat(fullPath);\n stream = fs.createReadStream(fullPath, readOps);\n const webStream = Readable.toWeb(stream);\n\n const result = await handler(webStream, stat.size);\n handlerSuccess = true;\n return result;\n } catch (error) {\n // Cleanup on error (including handler errors)\n if (!handlerSuccess && stream && !stream.destroyed) {\n stream.destroy();\n }\n throw error;\n }\n });\n }\n\n private async grpcGetDownloadUrl(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n signal?: AbortSignal,\n ): Promise<DownloadAPI_GetDownloadURL_Response> {\n const withAbort = options ?? {};\n withAbort.abort = signal;\n\n return await this.grpcClient.get().getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n }\n}\n\nexport function parseLocalUrl(url: string) {\n const parsed = new URL(url);\n if (parsed.pathname == '')\n throw new WrongLocalFileUrl(`url for local filepath ${url} does not match url scheme`);\n\n return {\n storageId: parsed.host,\n relativePath: decodeURIComponent(parsed.pathname.slice(1)),\n };\n}\n\nexport function getFullPath(\n storageId: string,\n localStorageIdsToRoot: Map<string, string>,\n relativePath: string,\n) {\n const root = localStorageIdsToRoot.get(storageId);\n if (root === undefined) throw new UnknownStorageError(`Unknown storage location: ${storageId}`);\n\n if (root === '') return relativePath;\n\n return path.join(root, relativePath);\n}\n\nconst storageProtocol = 'storage://';\nfunction isLocal(url: string) {\n return url.startsWith(storageProtocol);\n}\n\n/** Throws when a local URL have invalid scheme. */\nexport class WrongLocalFileUrl extends Error {\n name = 'WrongLocalFileUrl';\n}\n\n/** Happens when a storage for a local file can't be found. */\nexport class UnknownStorageError extends Error {\n name = 'UnknownStorageError';\n}\n\nexport function newLocalStorageIdsToRoot(projections: LocalStorageProjection[]) {\n const idToRoot: Map<string, string> = new Map();\n for (const lp of projections) {\n // Empty string means no prefix, i.e. any file on this machine can be got from the storage.\n if (lp.localPath !== '') {\n validateAbsolute(lp.localPath);\n }\n idToRoot.set(lp.storageId, lp.localPath);\n }\n\n return idToRoot;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"download.js","sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { GrpcClientProvider, GrpcClientProviderFactory } from '@milaboratories/pl-client';\nimport { addRTypeToMetadata, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type { ResourceInfo } from '@milaboratories/pl-tree';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { ConcurrencyLimitingExecutor } from '@milaboratories/ts-helpers';\nimport type { RpcOptions } from '@protobuf-ts/runtime-rpc';\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { Dispatcher } from 'undici';\nimport type { LocalStorageProjection } from '../drivers/types';\nimport { type ContentHandler, RemoteFileDownloader } from '../helpers/download';\nimport { validateAbsolute } from '../helpers/validate';\nimport type { DownloadAPI_GetDownloadURL_Response } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol';\nimport { DownloadClient } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client';\nimport { type GetContentOptions } from '@milaboratories/pl-model-common';\n\n/** Gets URLs for downloading from pl-core, parses them and reads or downloads\n * files locally and from the web. */\nexport class ClientDownload {\n public readonly grpcClient: GrpcClientProvider<DownloadClient>;\n private readonly remoteFileDownloader: RemoteFileDownloader;\n\n /** Helps to find a storage root directory by a storage id from URL scheme. */\n private readonly localStorageIdsToRoot: Map<string, string>;\n\n /** Concurrency limiter for local file reads - limit to 32 parallel reads */\n private readonly localFileReadLimiter = new ConcurrencyLimitingExecutor(32);\n\n constructor(\n grpcClientProviderFactory: GrpcClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.grpcClient = grpcClientProviderFactory.createGrpcClientProvider((transport) => new DownloadClient(transport));\n this.remoteFileDownloader = new RemoteFileDownloader(httpClient);\n this.localStorageIdsToRoot = newLocalStorageIdsToRoot(localProjections);\n }\n\n close() {}\n\n /**\n * Gets a presign URL and downloads the file.\n * An optional range with 2 numbers from what byte and to what byte to download can be provided.\n * @param fromBytes - from byte including this byte\n * @param toBytes - to byte excluding this byte\n */\n async withBlobContent<T>(\n info: ResourceInfo,\n options: RpcOptions | undefined,\n ops: GetContentOptions,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { downloadUrl, headers } = await this.grpcGetDownloadUrl(info, options, ops.signal);\n\n const remoteHeaders = Object.fromEntries(headers.map(({ name, value }) => [name, value]));\n this.logger.info(`download blob ${stringifyWithResourceId(info)} from url ${downloadUrl}, ops: ${JSON.stringify(ops)}`);\n\n return isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n }\n\n async withLocalFileContent<T>(\n url: string,\n ops: GetContentOptions,\n handler: ContentHandler<T>,\n ): Promise<T> {\n const { storageId, relativePath } = parseLocalUrl(url);\n const fullPath = getFullPath(storageId, this.localStorageIdsToRoot, relativePath);\n\n return await this.localFileReadLimiter.run(async () => {\n const readOps = {\n start: ops.range?.from,\n end: ops.range?.to !== undefined ? ops.range.to - 1 : undefined,\n signal: ops.signal,\n };\n let stream: fs.ReadStream | undefined;\n let handlerSuccess = false;\n\n try {\n const stat = await fsp.stat(fullPath);\n stream = fs.createReadStream(fullPath, readOps);\n const webStream = Readable.toWeb(stream);\n\n const result = await handler(webStream, stat.size);\n handlerSuccess = true;\n return result;\n } catch (error) {\n // Cleanup on error (including handler errors)\n if (!handlerSuccess && stream && !stream.destroyed) {\n stream.destroy();\n }\n throw error;\n }\n });\n }\n\n private async grpcGetDownloadUrl(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n signal?: AbortSignal,\n ): Promise<DownloadAPI_GetDownloadURL_Response> {\n const withAbort = options ?? {};\n withAbort.abort = signal;\n\n return await this.grpcClient.get().getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n }\n}\n\nexport function parseLocalUrl(url: string) {\n const parsed = new URL(url);\n if (parsed.pathname == '')\n throw new WrongLocalFileUrl(`url for local filepath ${url} does not match url scheme`);\n\n return {\n storageId: parsed.host,\n relativePath: decodeURIComponent(parsed.pathname.slice(1)),\n };\n}\n\nexport function getFullPath(\n storageId: string,\n localStorageIdsToRoot: Map<string, string>,\n relativePath: string,\n) {\n const root = localStorageIdsToRoot.get(storageId);\n if (root === undefined) throw new UnknownStorageError(`Unknown storage location: ${storageId}`);\n\n if (root === '') return relativePath;\n\n return path.join(root, relativePath);\n}\n\nconst storageProtocol = 'storage://';\nfunction isLocal(url: string) {\n return url.startsWith(storageProtocol);\n}\n\n/** Throws when a local URL have invalid scheme. */\nexport class WrongLocalFileUrl extends Error {\n name = 'WrongLocalFileUrl';\n}\n\n/** Happens when a storage for a local file can't be found. */\nexport class UnknownStorageError extends Error {\n name = 'UnknownStorageError';\n}\n\nexport function newLocalStorageIdsToRoot(projections: LocalStorageProjection[]) {\n const idToRoot: Map<string, string> = new Map();\n for (const lp of projections) {\n // Empty string means no prefix, i.e. any file on this machine can be got from the storage.\n if (lp.localPath !== '') {\n validateAbsolute(lp.localPath);\n }\n idToRoot.set(lp.storageId, lp.localPath);\n }\n\n return idToRoot;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -166,24 +166,50 @@ class DownloadDriver {
|
|
|
166
166
|
const { path } = download_local_handle.parseLocalHandle(handle, this.signer);
|
|
167
167
|
return path;
|
|
168
168
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
169
|
+
async getContent(handle, optionsOrRange) {
|
|
170
|
+
let options = {};
|
|
171
|
+
if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {
|
|
172
|
+
if ('range' in optionsOrRange) {
|
|
173
|
+
options = optionsOrRange;
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
const range = optionsOrRange;
|
|
177
|
+
plModelCommon.validateRangeBytes(range, `getContent`);
|
|
178
|
+
options = { range };
|
|
179
|
+
}
|
|
173
180
|
}
|
|
181
|
+
return await this.withContent(handle, {
|
|
182
|
+
...options,
|
|
183
|
+
handler: async (content) => {
|
|
184
|
+
const chunks = [];
|
|
185
|
+
for await (const chunk of content) {
|
|
186
|
+
options.signal?.throwIfAborted();
|
|
187
|
+
chunks.push(chunk);
|
|
188
|
+
}
|
|
189
|
+
return Buffer.concat(chunks);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/** Gets a content stream of a blob by a handle and calls handler with it. */
|
|
194
|
+
async withContent(handle, options) {
|
|
195
|
+
const { range, signal, handler } = options;
|
|
174
196
|
if (download_local_handle.isLocalBlobHandle(handle)) {
|
|
175
|
-
return await read_file.
|
|
197
|
+
return await read_file.withFileContent({ path: this.getLocalPath(handle), range, signal, handler });
|
|
176
198
|
}
|
|
177
199
|
if (download_remote_handle.isRemoteBlobHandle(handle)) {
|
|
178
200
|
const result = download_remote_handle.parseRemoteHandle(handle, this.signer);
|
|
179
201
|
const key = blob_key.blobKey(result.info.id);
|
|
180
202
|
const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
203
|
+
signal?.throwIfAborted();
|
|
204
|
+
if (filePath)
|
|
205
|
+
return await read_file.withFileContent({ path: filePath, range, signal, handler });
|
|
206
|
+
return await this.clientDownload.withBlobContent(result.info, { signal }, options, async (content, size) => {
|
|
207
|
+
const [handlerStream, cacheStream] = content.tee();
|
|
208
|
+
const handlerPromise = handler(handlerStream, size);
|
|
209
|
+
consumers.buffer(cacheStream)
|
|
210
|
+
.then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data));
|
|
211
|
+
return await handlerPromise;
|
|
212
|
+
});
|
|
187
213
|
}
|
|
188
214
|
throw new Error('Malformed remote handle');
|
|
189
215
|
}
|
|
@@ -196,7 +222,7 @@ class DownloadDriver {
|
|
|
196
222
|
plModelCommon.validateRangeBytes(range, `getComputableContent`);
|
|
197
223
|
}
|
|
198
224
|
return computable.Computable.make((ctx) => this.getDownloadedBlob(res, ctx), {
|
|
199
|
-
postprocessValue: (v) => v ? this.getContent(v.handle, range) : undefined
|
|
225
|
+
postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined
|
|
200
226
|
}).withStableType();
|
|
201
227
|
}
|
|
202
228
|
getLastLogs(res, lines, ctx) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download_blob.cjs","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { Writable } from 'node:stream';\nimport { buffer } from 'node:stream/consumers';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { readFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(handle: LocalBlobHandle | RemoteBlobHandle, range?: RangeBytes): Promise<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getContent`);\n }\n\n if (isLocalBlobHandle(handle)) {\n return await readFileContent(this.getLocalPath(handle), range);\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n if (filePath) {\n return await readFileContent(filePath, range);\n }\n\n const data = await this.clientDownload.withBlobContent(\n { id: result.info.id, type: result.info.type },\n undefined,\n { range },\n async (content) => await buffer(content)\n );\n await this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data);\n\n return data;\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, range) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":["FilesCache","SparseCacheFsRanges","SparseCacheFsFile","SparseCache","TaskProcessor","path","Computable","treeEntryToResourceInfo","randomUUID","blobKey","DownloadBlobTask","newLocalHandle","nonRecoverableError","isPlTreeEntry","makeResourceSnapshot","OnDemandBlobResourceSnapshot","getSize","newRemoteHandle","parseLocalHandle","validateRangeBytes","isLocalBlobHandle","readFileContent","isRemoteBlobHandle","parseRemoteHandle","buffer","newLogHandle","getResourceInfoFromLogHandle","fsp","mapGet","pathToKey","stringifyWithResourceId","resourceIdToString","CallersCounter","ChangeSource","Updater","fs","readline","os","WrongResourceTypeError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"download_blob.cjs","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n ContentHandler,\n GetContentOptions,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { buffer } from 'node:stream/consumers';\nimport { Readable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { withFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(handle: LocalBlobHandle | RemoteBlobHandle): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options?: GetContentOptions,\n ): Promise<Uint8Array>;\n /** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n range?: RangeBytes,\n ): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n optionsOrRange?: GetContentOptions | RangeBytes,\n ): Promise<Uint8Array> {\n let options: GetContentOptions = {};\n if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {\n if ('range' in optionsOrRange) {\n options = optionsOrRange;\n } else {\n const range = optionsOrRange as RangeBytes;\n validateRangeBytes(range, `getContent`);\n options = { range };\n }\n }\n\n return await this.withContent(handle, {\n ...options,\n handler: async (content) => {\n const chunks: Uint8Array[] = [];\n for await (const chunk of content) {\n options.signal?.throwIfAborted();\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n }\n });\n }\n\n /** Gets a content stream of a blob by a handle and calls handler with it. */\n public async withContent<T>(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options: GetContentOptions & {\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n const { range, signal, handler } = options;\n\n if (isLocalBlobHandle(handle)) {\n return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n signal?.throwIfAborted();\n\n if (filePath) return await withFileContent({ path: filePath, range, signal, handler });\n\n return await this.clientDownload.withBlobContent(\n result.info,\n { signal },\n options,\n async (content, size) => {\n const [handlerStream, cacheStream] = content.tee();\n \n const handlerPromise = handler(handlerStream, size);\n const _cachePromise = buffer(cacheStream)\n .then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data));\n\n return await handlerPromise;\n }\n );\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":["FilesCache","SparseCacheFsRanges","SparseCacheFsFile","SparseCache","TaskProcessor","path","Computable","treeEntryToResourceInfo","randomUUID","blobKey","DownloadBlobTask","newLocalHandle","nonRecoverableError","isPlTreeEntry","makeResourceSnapshot","OnDemandBlobResourceSnapshot","getSize","newRemoteHandle","parseLocalHandle","validateRangeBytes","isLocalBlobHandle","withFileContent","isRemoteBlobHandle","parseRemoteHandle","buffer","newLogHandle","getResourceInfoFromLogHandle","fsp","mapGet","pathToKey","stringifyWithResourceId","resourceIdToString","CallersCounter","ChangeSource","Updater","fs","readline","os","WrongResourceTypeError"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA;AAC6D;MAChD,cAAc,CAAA;AAoBN,IAAA,MAAA;AACA,IAAA,cAAA;AACA,IAAA,UAAA;AAEA,IAAA,cAAA;AACA,IAAA,MAAA;AACA,IAAA,GAAA;;AAxBX,IAAA,aAAa,GAAkC,IAAI,GAAG,EAAE;AAEhE;AAC+B;AACvB,IAAA,KAAK;AACL,IAAA,WAAW;;AAGX,IAAA,aAAa;AAEb,IAAA,aAAa,GAAoC,IAAI,GAAG,EAAE;AAE1D,IAAA,aAAa,GAAiC,IAAI,GAAG,EAAE;AACvD,IAAA,eAAe,GAAiC,IAAI,GAAG,EAAE;AAEhD,IAAA,OAAO;AAExB,IAAA,WAAA,CACmB,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACvC,OAAe,EACE,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;QANtB,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,UAAU,GAAV,UAAU;QAEV,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,GAAG,GAAH,GAAG;AAEpB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAIA,sBAAU,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAExD,QAAA,MAAM,QAAQ,GAAG,IAAIC,yBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;AAC1E,QAAA,MAAM,SAAS,GAAG,IAAIC,uBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;QACzE,IAAI,CAAC,WAAW,GAAG,IAAIC,iBAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,QAAQ,EAAE,SAAS,CAAC;AAEtG,QAAA,IAAI,CAAC,aAAa,GAAG,IAAIC,uBAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,oBAAoB,CAAC;QAE7E,IAAI,CAAC,OAAO,GAAGC,eAAI,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC;AAEA,IAAA,aAAa,IAAI,CACf,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACtB,OAAe,EACf,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;AAEtB,QAAA,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC;AAC3G,QAAA,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE;AAEhC,QAAA,OAAO,MAAM;IACf;IAUO,iBAAiB,CACtB,GAA+B,EAC/B,GAAmB,EAAA;AAEnB,QAAA,IAAI,GAAG,KAAK,SAAS,EAAE;AACrB,YAAA,OAAOC,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnE;QAEA,MAAM,KAAK,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE/C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEzD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,EAAE,KAAyB,EAAE,QAAQ,CAAC;AAC5F,QAAA,IAAI,MAAM,IAAI,SAAS,EAAE;AACvB,YAAA,GAAG,CAAC,YAAY,CAAC,kCAAkC,CAAC;QACtD;AAEA,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,sBAAsB,CAC5B,CAAU,EACV,KAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC;;;QAKjE,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;AAClD,QAAA,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC;AAExB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;AAChB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;AACpB,YAAA,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK;QAC5B;AACA,QAAA,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK;IAC3B;IAEQ,eAAe,CACrB,KAAuB,EACvB,QAAgB,EAAA;QAEhB,MAAM,GAAG,GAAGC,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAE7B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAChD,IAAI,YAAY,EAAE;AAChB,YAAA,OAAO,YAAY;QACrB;;AAGA,QAAA,MAAM,KAAK,GAAGJ,eAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC;QAE7C,MAAM,OAAO,GAAG,IAAIK,mCAAgB,CAClC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,cAAc,EACnB,KAAK,EACLC,oCAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAClC,KAAK,CACN;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC;AAEpC,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACtB,EAAE,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC9C,yBAAyB,EAAE,CAAC,CAAC,KAAK,CAACC,sCAAmB,CAAC,CAAC,CAAC;AAC1D,SAAA,CAAC;AAEF,QAAA,OAAO,OAAO;IAChB;AAEQ,IAAA,MAAM,YAAY,CAAC,IAAsB,EAAE,QAAgB,EAAA;AACjE,QAAA,MAAM,IAAI,CAAC,QAAQ,EAAE;AACrB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3B,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;YAC/B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC;IACF;IAkBO,eAAe,CACpB,GAA+C,EAC/C,GAAmB,EAAA;QAEnB,IAAI,GAAG,KAAK,SAAS;AAAE,YAAA,OAAON,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEtF,QAAA,MAAM,KAAK,GAAiCO,oBAAa,CAAC,GAAG;cACzDC,2BAAoB,CAAC,GAAG,EAAEC,kCAA4B,EAAE,GAAG;cAC3D,GAAG;AAEP,QAAA,MAAM,QAAQ,GAAGP,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;;;QAIpE,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;AAEzD,QAAA,OAAO,MAAM;IACf;IAEQ,oBAAoB,CAC1B,IAAkC,EAClC,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC;AAE9D,QAAA,IAAI,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACC,gBAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEnD,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;AACtB,YAAA,IAAI,GAAG,IAAI,kBAAkB,CAACO,aAAO,CAAC,IAAI,CAAC,EAAEC,sCAAe,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AAChF,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAACR,gBAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAChD;AAEA,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;AAErB,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;;AAGO,IAAA,YAAY,CAAC,MAAuB,EAAA;AACzC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGS,sCAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AACtD,QAAA,OAAO,IAAI;IACb;AAaO,IAAA,MAAM,UAAU,CACrB,MAA0C,EAC1C,cAA+C,EAAA;QAE/C,IAAI,OAAO,GAAsB,EAAE;QACnC,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE;AACjE,YAAA,IAAI,OAAO,IAAI,cAAc,EAAE;gBAC7B,OAAO,GAAG,cAAc;YAC1B;iBAAO;gBACL,MAAM,KAAK,GAAG,cAA4B;AAC1C,gBAAAC,gCAAkB,CAAC,KAAK,EAAE,CAAA,UAAA,CAAY,CAAC;AACvC,gBAAA,OAAO,GAAG,EAAE,KAAK,EAAE;YACrB;QACF;AAEA,QAAA,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;AACpC,YAAA,GAAG,OAAO;AACV,YAAA,OAAO,EAAE,OAAO,OAAO,KAAI;gBACzB,MAAM,MAAM,GAAiB,EAAE;AAC/B,gBAAA,WAAW,MAAM,KAAK,IAAI,OAAO,EAAE;AACjC,oBAAA,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE;AAChC,oBAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBACpB;AACA,gBAAA,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YAC9B;AACD,SAAA,CAAC;IACJ;;AAGO,IAAA,MAAM,WAAW,CACtB,MAA0C,EAC1C,OAEC,EAAA;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;AAE1C,QAAA,IAAIC,uCAAiB,CAAC,MAAM,CAAC,EAAE;YAC7B,OAAO,MAAMC,yBAAe,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC3F;AAEA,QAAA,IAAIC,yCAAkB,CAAC,MAAM,CAAC,EAAE;YAC9B,MAAM,MAAM,GAAGC,wCAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;YAErD,MAAM,GAAG,GAAGd,gBAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YACvF,MAAM,EAAE,cAAc,EAAE;AAExB,YAAA,IAAI,QAAQ;AAAE,gBAAA,OAAO,MAAMY,yBAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAEtF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAC9C,MAAM,CAAC,IAAI,EACX,EAAE,MAAM,EAAE,EACV,OAAO,EACP,OAAO,OAAO,EAAE,IAAI,KAAI;gBACtB,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE;gBAElD,MAAM,cAAc,GAAG,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC;AACnD,gBAAsBG,gBAAM,CAAC,WAAW;AACrC,qBAAA,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC;gBAExF,OAAO,MAAM,cAAc;AAC7B,YAAA,CAAC,CACF;QACH;AAEA,QAAA,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAC5C;AAEA;;;AAGG;IACI,oBAAoB,CACzB,GAA+B,EAC/B,KAAkB,EAAA;QAElB,IAAI,KAAK,EAAE;AACT,YAAAL,gCAAkB,CAAC,KAAK,EAAE,CAAA,oBAAA,CAAsB,CAAC;QACnD;AAEA,QAAA,OAAOb,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KACzB,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;YAClC,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG;SACrE,CACA,CAAC,cAAc,EAAE;IACpB;AAaO,IAAA,WAAW,CAChB,GAA+B,EAC/B,KAAa,EACb,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAAE,OAAOA,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAExF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,CAAqB,EAAE,KAAK,EAAE,QAAQ,CAAC;QACzF,IAAI,MAAM,IAAI,SAAS;AACrB,YAAA,GAAG,CAAC,YAAY,CAAC,uDAAuD,CAAC;AAE3E,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,gBAAgB,CACtB,CAAU,EACV,KAAuB,EACvB,KAAa,EACb,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC;AAC3D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AAEvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGU,sCAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACT,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAEzD,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC;AACrD,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YACvD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;AAaO,IAAA,cAAc,CACnB,GAA+B,EAC/B,eAAuB,EACvB,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAClB,OAAOH,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC;QAEjF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CACrC,GAAG,CAAC,OAAO,EACX,CAAqB,EACrB,eAAe,EACf,QAAQ,CACT;QACD,IAAI,MAAM,KAAK,SAAS;AACtB,YAAA,GAAG,CAAC,YAAY,CAAC,iEAAiE,CAAC;AAErF,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,mBAAmB,CACzB,CAAU,EACV,KAAuB,EACvB,eAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC;AAE9D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AACvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGU,sCAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAACT,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE3D,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,eAAe,CAAC;AAClE,YAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YAEzD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;IAMO,YAAY,CACjB,GAA+B,EAC/B,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;AAAE,YAAA,OAAOH,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAElF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE3C,QAAA,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAqB,CAAC;IACtD;AAEQ,IAAA,iBAAiB,CAAC,KAAuB,EAAA;AAC/C,QAAA,gCAAgC,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC;AAC5D,QAAA,OAAOkB,wBAAY,CAAC,KAAK,EAAE,KAAK,CAAC;IACnC;IAEO,MAAM,SAAS,CACpB,MAAsB,EACtB,SAAiB,EACjB,WAAoB;IACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAC1CC,wCAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;IAEO,MAAM,QAAQ,CACnB,MAAsB,EACtB,SAAiB,EACjB,WAAoB,EACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CACzCA,wCAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;AAEQ,IAAA,MAAM,WAAW,CAAC,KAAmB,EAAE,QAAgB,EAAA;AAC7D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACjB,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACtD,QAAA,IAAI,IAAI,IAAI,SAAS,EAAE;YACrB;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE;AAC5C,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC;AAEnE,YAAA,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,OAAO,UAAU,KAAI;gBAChC,MAAMkB,cAAG,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;AAE7B,gBAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC;gBAElC,IAAI,CAAC,UAAU,CACbC,gBAAM,CAAC,IAAI,CAAC,aAAa,EAAEC,kBAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EACtD,YAAYC,gCAAuB,CAAC,UAAU,CAAC,CAAA,YAAA;AAC7C,sBAAA,CAAA,sBAAA,EAAyBA,gCAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,CAAE,CAClF;YACH,CAAC,CAAC,CACH;QACH;aAAO;;YAEL,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1C,IAAI,OAAO,EAAE;AACX,gBAAA,IAAI,CAAC,UAAU,CACb,IAAI,EACJ,CAAA,SAAA,EAAYA,gCAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA,uBAAA,CAAyB,CAC1E;YACH;QACF;IACF;IAEQ,UAAU,CAAC,IAAsB,EAAE,MAAc,EAAA;AACvD,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;AAClB,QAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,kBAAA,EAAqB,IAAI,CAAC,IAAI,CAAA,UAAA,EAAa,MAAM,CAAA,CAAE,CAAC;AAC5E,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAACD,kBAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAACpB,gBAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACjD,QAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAACA,gBAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrD;AAEQ,IAAA,MAAM,mBAAmB,CAAC,MAAkB,EAAE,QAAgB,EAAA;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACA,gBAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK;AACnF,QAAA,IAAI,OAAO;YAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAACA,gBAAO,CAAC,MAAM,CAAC,CAAC;IACzD;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;QAEzB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,KAAI;AACvC,YAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;AAC9B,YAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQsB,2BAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA,SAAA,CAAW,CAAC;AAC/E,QAAA,CAAC,CAAC;IACJ;AACD;AAED;AACA,MAAM,kBAAkB,CAAA;AAIH,IAAA,IAAA;AACA,IAAA,MAAA;AAJF,IAAA,OAAO,GAAG,IAAIC,wBAAc,EAAE;IAE/C,WAAA,CACmB,IAAY,EACZ,MAAwB,EAAA;QADxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,MAAM,GAAN,MAAM;IACtB;IAEI,SAAS,GAAA;AACd,QAAA,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IACjD;AAEO,IAAA,MAAM,CAAC,QAAgB,EAAA;AAC5B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5B;AAEO,IAAA,OAAO,CAAC,QAAgB,EAAA;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnC;AACD;AAED,MAAM,eAAe,CAAA;AAOA,IAAA,IAAA;AACA,IAAA,KAAA;AACA,IAAA,eAAA;AARX,IAAA,OAAO;AACP,IAAA,GAAG;AACM,IAAA,MAAM,GAAiB,IAAIC,uBAAY,EAAE;IAClD,KAAK,GAAoB,SAAS;AAE1C,IAAA,WAAA,CACmB,IAAY,EACZ,KAAa,EACb,eAAwB,EAAA;QAFxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,eAAe,GAAf,eAAe;AAEhC,QAAA,IAAI,CAAC,OAAO,GAAG,IAAIC,eAAO,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC;IACvD;AAEA,IAAA,aAAa,CAAC,CAAU,EAAA;AAItB,QAAA,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAE5B,QAAA,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;QAEvB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB;IACH;AAEA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC;AAE/E,YAAA,IAAI,IAAI,CAAC,GAAG,IAAI,OAAO;gBAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,SAAA,EAAY,IAAI,CAAC,IAAI,CAAA,QAAA,CAAU,CAAC;AACjF,YAAA,IAAI,CAAC,GAAG,GAAG,OAAO;QACpB;QAAE,OAAO,CAAM,EAAE;AACf,YAAA,IAAI,CAAC,CAAC,IAAI,IAAI,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,WAAW,EAAE;;AAEjD,gBAAA,IAAI,CAAC,GAAG,GAAG,EAAE;AACb,gBAAA,IAAI,CAAC,KAAK,GAAG,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,eAAA,EAAkB,IAAI,CAAC,IAAI,CAAA,2BAAA,CAA6B,CAAC;gBACjF;YACF;AAEA,YAAA,MAAM,CAAC;QACT;IACF;AACD;AAED;AACqC;AACrC,eAAe,YAAY,CAAC,KAAa,EAAE,MAAc,EAAE,eAAwB,EAAA;AACjF,IAAA,IAAI,QAAmC;AACvC,IAAA,IAAI,EAAkC;AAEtC,IAAA,IAAI;AACF,QAAA,QAAQ,GAAGC,aAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;AACrC,QAAA,EAAE,GAAGC,mBAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAEvE,QAAA,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE;AAE1B,QAAA,WAAW,MAAM,IAAI,IAAI,EAAE,EAAE;YAC3B,IAAI,eAAe,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE;AAErE,YAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAChB,YAAA,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE;gBACzB,KAAK,CAAC,KAAK,EAAE;YACf;QACF;;AAGA,QAAA,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAACC,aAAE,CAAC,GAAG,CAAC,GAAGA,aAAE,CAAC,GAAG;IAC9C;YAAU;;AAER,QAAA,IAAI;YACF,IAAI,EAAE,EAAE;gBACN,EAAE,CAAC,KAAK,EAAE;YACZ;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,YAAY,CAAC;QAClE;AAEA,QAAA,IAAI;AACF,YAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;gBACnC,QAAQ,CAAC,OAAO,EAAE;YACpB;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,YAAY,CAAC;QAC9D;IACF;AACF;AAEA,SAAS,gCAAgC,CAAC,UAAkB,EAAE,KAAmB,EAAA;IAC/E,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QACnC,IAAI,OAAO,GAAG,CAAA,EAAG,UAAU,0BAA0B,KAAK,CAAC,IAAI,CAAA,wDAAA,CAA0D;AACzH,QAAA,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM;YACtB,OAAO,IAAI,2FAA2F;AAExG,QAAA,MAAM,IAAIC,8BAAsB,CAAC,OAAO,CAAC;IAC3C;AACF;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComputableCtx, ComputableStableDefined } from '@milaboratories/computable';
|
|
2
2
|
import { Computable } from '@milaboratories/computable';
|
|
3
|
-
import type { AnyLogHandle, BlobDriver, LocalBlobHandle, LocalBlobHandleAndSize, ReadyLogHandle, RemoteBlobHandle, RemoteBlobHandleAndSize, StreamingApiResponse } from '@milaboratories/pl-model-common';
|
|
3
|
+
import type { AnyLogHandle, BlobDriver, ContentHandler, GetContentOptions, LocalBlobHandle, LocalBlobHandleAndSize, ReadyLogHandle, RemoteBlobHandle, RemoteBlobHandleAndSize, StreamingApiResponse } from '@milaboratories/pl-model-common';
|
|
4
4
|
import { type RangeBytes } from '@milaboratories/pl-model-common';
|
|
5
5
|
import type { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';
|
|
6
6
|
import type { MiLogger, Signer } from '@milaboratories/ts-helpers';
|
|
@@ -64,7 +64,14 @@ export declare class DownloadDriver implements BlobDriver {
|
|
|
64
64
|
/** Gets a path from a handle. */
|
|
65
65
|
getLocalPath(handle: LocalBlobHandle): string;
|
|
66
66
|
/** Gets a content of a blob by a handle. */
|
|
67
|
+
getContent(handle: LocalBlobHandle | RemoteBlobHandle): Promise<Uint8Array>;
|
|
68
|
+
getContent(handle: LocalBlobHandle | RemoteBlobHandle, options?: GetContentOptions): Promise<Uint8Array>;
|
|
69
|
+
/** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */
|
|
67
70
|
getContent(handle: LocalBlobHandle | RemoteBlobHandle, range?: RangeBytes): Promise<Uint8Array>;
|
|
71
|
+
/** Gets a content stream of a blob by a handle and calls handler with it. */
|
|
72
|
+
withContent<T>(handle: LocalBlobHandle | RemoteBlobHandle, options: GetContentOptions & {
|
|
73
|
+
handler: ContentHandler<T>;
|
|
74
|
+
}): Promise<T>;
|
|
68
75
|
/**
|
|
69
76
|
* Creates computable that will return blob content once it is downloaded.
|
|
70
77
|
* Uses downloaded blob handle under the hood, so stores corresponding blob in file system.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download_blob.d.ts","sourceRoot":"","sources":["../../../src/drivers/download_blob/download_blob.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"download_blob.d.ts","sourceRoot":"","sources":["../../../src/drivers/download_blob/download_blob.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ import * as os from 'node:os';
|
|
|
11
11
|
import * as path from 'node:path';
|
|
12
12
|
import * as readline from 'node:readline/promises';
|
|
13
13
|
import { buffer } from 'node:stream/consumers';
|
|
14
|
-
import {
|
|
14
|
+
import { withFileContent } from '../helpers/read_file.js';
|
|
15
15
|
import { newLocalHandle, parseLocalHandle, isLocalBlobHandle } from '../helpers/download_local_handle.js';
|
|
16
16
|
import { newRemoteHandle, isRemoteBlobHandle, parseRemoteHandle } from '../helpers/download_remote_handle.js';
|
|
17
17
|
import { WrongResourceTypeError, Updater } from '../helpers/helpers.js';
|
|
@@ -141,24 +141,50 @@ class DownloadDriver {
|
|
|
141
141
|
const { path } = parseLocalHandle(handle, this.signer);
|
|
142
142
|
return path;
|
|
143
143
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
144
|
+
async getContent(handle, optionsOrRange) {
|
|
145
|
+
let options = {};
|
|
146
|
+
if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {
|
|
147
|
+
if ('range' in optionsOrRange) {
|
|
148
|
+
options = optionsOrRange;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const range = optionsOrRange;
|
|
152
|
+
validateRangeBytes(range, `getContent`);
|
|
153
|
+
options = { range };
|
|
154
|
+
}
|
|
148
155
|
}
|
|
156
|
+
return await this.withContent(handle, {
|
|
157
|
+
...options,
|
|
158
|
+
handler: async (content) => {
|
|
159
|
+
const chunks = [];
|
|
160
|
+
for await (const chunk of content) {
|
|
161
|
+
options.signal?.throwIfAborted();
|
|
162
|
+
chunks.push(chunk);
|
|
163
|
+
}
|
|
164
|
+
return Buffer.concat(chunks);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** Gets a content stream of a blob by a handle and calls handler with it. */
|
|
169
|
+
async withContent(handle, options) {
|
|
170
|
+
const { range, signal, handler } = options;
|
|
149
171
|
if (isLocalBlobHandle(handle)) {
|
|
150
|
-
return await
|
|
172
|
+
return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });
|
|
151
173
|
}
|
|
152
174
|
if (isRemoteBlobHandle(handle)) {
|
|
153
175
|
const result = parseRemoteHandle(handle, this.signer);
|
|
154
176
|
const key = blobKey(result.info.id);
|
|
155
177
|
const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
signal?.throwIfAborted();
|
|
179
|
+
if (filePath)
|
|
180
|
+
return await withFileContent({ path: filePath, range, signal, handler });
|
|
181
|
+
return await this.clientDownload.withBlobContent(result.info, { signal }, options, async (content, size) => {
|
|
182
|
+
const [handlerStream, cacheStream] = content.tee();
|
|
183
|
+
const handlerPromise = handler(handlerStream, size);
|
|
184
|
+
buffer(cacheStream)
|
|
185
|
+
.then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data));
|
|
186
|
+
return await handlerPromise;
|
|
187
|
+
});
|
|
162
188
|
}
|
|
163
189
|
throw new Error('Malformed remote handle');
|
|
164
190
|
}
|
|
@@ -171,7 +197,7 @@ class DownloadDriver {
|
|
|
171
197
|
validateRangeBytes(range, `getComputableContent`);
|
|
172
198
|
}
|
|
173
199
|
return Computable.make((ctx) => this.getDownloadedBlob(res, ctx), {
|
|
174
|
-
postprocessValue: (v) => v ? this.getContent(v.handle, range) : undefined
|
|
200
|
+
postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined
|
|
175
201
|
}).withStableType();
|
|
176
202
|
}
|
|
177
203
|
getLastLogs(res, lines, ctx) {
|