@milaboratories/pl-drivers 1.12.7 → 1.12.8
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.js +1 -0
- package/dist/clients/download.js.map +1 -1
- package/dist/clients/logs.cjs +2 -0
- package/dist/clients/logs.cjs.map +1 -1
- package/dist/clients/logs.js +2 -0
- package/dist/clients/logs.js.map +1 -1
- package/dist/clients/ls_api.cjs +1 -0
- package/dist/clients/ls_api.cjs.map +1 -1
- package/dist/clients/ls_api.js +1 -0
- package/dist/clients/ls_api.js.map +1 -1
- package/dist/clients/progress.cjs +4 -1
- package/dist/clients/progress.cjs.map +1 -1
- package/dist/clients/progress.js +4 -1
- package/dist/clients/progress.js.map +1 -1
- package/dist/clients/upload.cjs +17 -3
- package/dist/clients/upload.cjs.map +1 -1
- package/dist/clients/upload.js +17 -3
- package/dist/clients/upload.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs +28 -16
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.cjs +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts +2 -2
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.js +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts +11 -5
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js +28 -16
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs +26 -12
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.cjs +4 -4
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.js +4 -4
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts +16 -16
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js +26 -12
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs +36 -12
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.cjs +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts +2 -2
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.js +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts +8 -0
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js +36 -12
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs +29 -189
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.cjs +20 -41
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts +36 -66
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.js +20 -41
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts +52 -137
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js +29 -189
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs +225 -24
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.cjs +21 -7
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.js +21 -7
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js +225 -24
- package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js.map +1 -1
- package/dist/proto-rest/downloadapi.d.ts +8 -3
- package/dist/proto-rest/progressapi.d.ts +4 -2
- package/dist/proto-rest/streamingapi.d.ts +64 -203
- package/package.json +5 -5
- package/src/clients/download.ts +1 -0
- package/src/clients/logs.ts +2 -0
- package/src/clients/ls_api.ts +1 -0
- package/src/clients/progress.ts +1 -1
- package/src/clients/upload.ts +14 -1
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.ts +2 -2
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.ts +24 -11
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.ts +8 -8
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.ts +31 -18
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.ts +2 -2
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.ts +23 -1
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.ts +39 -78
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.ts +72 -263
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.ts +41 -14
- package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.ts +286 -44
- package/src/proto-grpc/google/protobuf/descriptor.ts +2 -5
- package/src/proto-rest/downloadapi.ts +8 -3
- package/src/proto-rest/lsapi.ts +23 -18
- package/src/proto-rest/progressapi.ts +4 -0
- package/src/proto-rest/streamingapi.ts +65 -211
- package/src/proto-rest/uploadapi.ts +141 -39
|
@@ -88,6 +88,7 @@ var ClientDownload = class {
|
|
|
88
88
|
else return (await client.POST("/v1/get-download-url", {
|
|
89
89
|
body: {
|
|
90
90
|
resourceId: id.toString(),
|
|
91
|
+
resourceSignature: "",
|
|
91
92
|
isInternalUse: false
|
|
92
93
|
},
|
|
93
94
|
headers: { ...(0, _milaboratories_pl_client.createRTypeRoutingHeader)(type) }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.cjs","names":["ConcurrencyLimitingExecutor","DownloadClient","RestAPI","RemoteFileDownloader","PerfTimer","fsp","fs","Readable","path"],"sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport {\n addRTypeToMetadata,\n stringifyWithResourceId,\n RestAPI,\n createRTypeRoutingHeader,\n} from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { PerfTimer } from \"@milaboratories/helpers\";\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-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol\";\nimport { DownloadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client\";\nimport type { DownloadApiPaths, DownloadRestClientType } from \"../proto-rest\";\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 wire: WireClientProvider<DownloadRestClientType | 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 wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new DownloadClient(wireConn.Transport);\n } else {\n return RestAPI.createClient<DownloadApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\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(\n `blob ${stringifyWithResourceId(info)} download started, ` +\n `url: ${downloadUrl}, ` +\n `range: ${JSON.stringify(ops.range ?? null)}`,\n );\n\n const timer = PerfTimer.start();\n const result = isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n\n this.logger.info(\n `blob ${stringifyWithResourceId(info)} download finished, ` + `took: ${timer.elapsed()}`,\n );\n return result;\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 const client = this.wire.get();\n if (client instanceof DownloadClient) {\n return await client.getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n } else {\n return (\n await client.POST(\"/v1/get-download-url\", {\n body: {\n resourceId: id.toString(),\n isInternalUse: false,\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n }\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"],"mappings":";;;;;;;;;;;;;;;;;;AA4BA,IAAa,iBAAb,MAA4B;CAC1B,AAAgB;CAChB,AAAiB;;CAGjB,AAAiB;;CAGjB,AAAiB,uBAAuB,IAAIA,uDAA4B,GAAG;CAE3E,YACE,2BACA,AAAgB,YAChB,AAAgB,QAEhB,kBACA;EAJgB;EACA;AAIhB,OAAK,OAAO,0BAA0B,0BAA0B,aAAa;AAC3E,OAAI,SAAS,SAAS,OACpB,QAAO,IAAIC,uCAAe,SAAS,UAAU;OAE7C,QAAOC,kCAAQ,aAA+B;IAC5C,aAAa,SAAS,OAAO;IAC7B,KAAK,SAAS,OAAO;IACrB,YAAY,SAAS;IACrB,aAAa,SAAS;IACvB,CAAC;IAEJ;AACF,OAAK,uBAAuB,IAAIC,sCAAqB,WAAW;AAChE,OAAK,wBAAwB,yBAAyB,iBAAiB;;CAGzE,QAAQ;;;;;;;CAQR,MAAM,gBACJ,MACA,SACA,KACA,SACY;EACZ,MAAM,EAAE,aAAa,YAAY,MAAM,KAAK,mBAAmB,MAAM,SAAS,IAAI,OAAO;EAEzF,MAAM,gBAAgB,OAAO,YAAY,QAAQ,KAAK,EAAE,MAAM,YAAY,CAAC,MAAM,MAAM,CAAC,CAAC;AACzF,OAAK,OAAO,KACV,+DAAgC,KAAK,CAAC,0BAC5B,YAAY,WACV,KAAK,UAAU,IAAI,SAAS,KAAK,GAC9C;EAED,MAAM,QAAQC,kCAAU,OAAO;EAC/B,MAAM,SAAS,QAAQ,YAAY,GAC/B,MAAM,KAAK,qBAAqB,aAAa,KAAK,QAAQ,GAC1D,MAAM,KAAK,qBAAqB,YAAY,aAAa,eAAe,KAAK,QAAQ;AAEzF,OAAK,OAAO,KACV,+DAAgC,KAAK,CAAC,4BAAiC,MAAM,SAAS,GACvF;AACD,SAAO;;CAGT,MAAM,qBACJ,KACA,KACA,SACY;EACZ,MAAM,EAAE,WAAW,iBAAiB,cAAc,IAAI;EACtD,MAAM,WAAW,YAAY,WAAW,KAAK,uBAAuB,aAAa;AAEjF,SAAO,MAAM,KAAK,qBAAqB,IAAI,YAAY;GACrD,MAAM,UAAU;IACd,OAAO,IAAI,OAAO;IAClB,KAAK,IAAI,OAAO,OAAO,SAAY,IAAI,MAAM,KAAK,IAAI;IACtD,QAAQ,IAAI;IACb;GACD,IAAI;GACJ,IAAI,iBAAiB;AAErB,OAAI;IACF,MAAM,OAAO,MAAMC,iBAAI,KAAK,SAAS;AACrC,aAASC,QAAG,iBAAiB,UAAU,QAAQ;IAG/C,MAAM,SAAS,MAAM,QAFHC,qBAAS,MAAM,OAAO,EAEA,KAAK,KAAK;AAClD,qBAAiB;AACjB,WAAO;YACA,OAAO;AAEd,QAAI,CAAC,kBAAkB,UAAU,CAAC,OAAO,UACvC,QAAO,SAAS;AAElB,UAAM;;IAER;;CAGJ,MAAc,mBACZ,EAAE,IAAI,QACN,SACA,QAC8C;EAC9C,MAAM,YAAY,WAAW,EAAE;AAC/B,YAAU,QAAQ;EAElB,MAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,MAAI,kBAAkBN,uCACpB,QAAO,MAAM,OAAO,eAClB;GAAE,YAAY;GAAI,eAAe;GAAO,oDACrB,MAAM,UAAU,CACpC,CAAC;MAEF,SACE,MAAM,OAAO,KAAK,wBAAwB;GACxC,MAAM;IACJ,YAAY,GAAG,UAAU;IACzB,eAAe;IAChB;GACD,SAAS,EAAE,2DAA4B,KAAK,EAAE;GAC/C,CAAC,EACF;;;AAKR,SAAgB,cAAc,KAAa;CACzC,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,KAAI,OAAO,YAAY,GACrB,OAAM,IAAI,kBAAkB,0BAA0B,IAAI,4BAA4B;AAExF,QAAO;EACL,WAAW,OAAO;EAClB,cAAc,mBAAmB,OAAO,SAAS,MAAM,EAAE,CAAC;EAC3D;;AAGH,SAAgB,YACd,WACA,uBACA,cACA;CACA,MAAM,OAAO,sBAAsB,IAAI,UAAU;AACjD,KAAI,SAAS,OAAW,OAAM,IAAI,oBAAoB,6BAA6B,YAAY;AAE/F,KAAI,SAAS,GAAI,QAAO;AAExB,QAAOO,UAAK,KAAK,MAAM,aAAa;;AAGtC,MAAM,kBAAkB;AACxB,SAAS,QAAQ,KAAa;AAC5B,QAAO,IAAI,WAAW,gBAAgB;;;AAIxC,IAAa,oBAAb,cAAuC,MAAM;CAC3C,OAAO;;;AAIT,IAAa,sBAAb,cAAyC,MAAM;CAC7C,OAAO;;AAGT,SAAgB,yBAAyB,aAAuC;CAC9E,MAAM,2BAAgC,IAAI,KAAK;AAC/C,MAAK,MAAM,MAAM,aAAa;AAE5B,MAAI,GAAG,cAAc,GACnB,mCAAiB,GAAG,UAAU;AAEhC,WAAS,IAAI,GAAG,WAAW,GAAG,UAAU;;AAG1C,QAAO"}
|
|
1
|
+
{"version":3,"file":"download.cjs","names":["ConcurrencyLimitingExecutor","DownloadClient","RestAPI","RemoteFileDownloader","PerfTimer","fsp","fs","Readable","path"],"sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport {\n addRTypeToMetadata,\n stringifyWithResourceId,\n RestAPI,\n createRTypeRoutingHeader,\n} from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { PerfTimer } from \"@milaboratories/helpers\";\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-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol\";\nimport { DownloadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client\";\nimport type { DownloadApiPaths, DownloadRestClientType } from \"../proto-rest\";\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 wire: WireClientProvider<DownloadRestClientType | 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 wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new DownloadClient(wireConn.Transport);\n } else {\n return RestAPI.createClient<DownloadApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\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(\n `blob ${stringifyWithResourceId(info)} download started, ` +\n `url: ${downloadUrl}, ` +\n `range: ${JSON.stringify(ops.range ?? null)}`,\n );\n\n const timer = PerfTimer.start();\n const result = isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n\n this.logger.info(\n `blob ${stringifyWithResourceId(info)} download finished, ` + `took: ${timer.elapsed()}`,\n );\n return result;\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 const client = this.wire.get();\n if (client instanceof DownloadClient) {\n return await client.getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n } else {\n return (\n await client.POST(\"/v1/get-download-url\", {\n body: {\n resourceId: id.toString(),\n resourceSignature: \"\",\n isInternalUse: false,\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n }\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"],"mappings":"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"}
|
package/dist/clients/download.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.js","names":["fs","path"],"sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport {\n addRTypeToMetadata,\n stringifyWithResourceId,\n RestAPI,\n createRTypeRoutingHeader,\n} from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { PerfTimer } from \"@milaboratories/helpers\";\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-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol\";\nimport { DownloadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client\";\nimport type { DownloadApiPaths, DownloadRestClientType } from \"../proto-rest\";\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 wire: WireClientProvider<DownloadRestClientType | 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 wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new DownloadClient(wireConn.Transport);\n } else {\n return RestAPI.createClient<DownloadApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\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(\n `blob ${stringifyWithResourceId(info)} download started, ` +\n `url: ${downloadUrl}, ` +\n `range: ${JSON.stringify(ops.range ?? null)}`,\n );\n\n const timer = PerfTimer.start();\n const result = isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n\n this.logger.info(\n `blob ${stringifyWithResourceId(info)} download finished, ` + `took: ${timer.elapsed()}`,\n );\n return result;\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 const client = this.wire.get();\n if (client instanceof DownloadClient) {\n return await client.getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n } else {\n return (\n await client.POST(\"/v1/get-download-url\", {\n body: {\n resourceId: id.toString(),\n isInternalUse: false,\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n }\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"download.js","names":["fs","path"],"sources":["../../src/clients/download.ts"],"sourcesContent":["/* eslint-disable n/no-unsupported-features/node-builtins */\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport {\n addRTypeToMetadata,\n stringifyWithResourceId,\n RestAPI,\n createRTypeRoutingHeader,\n} from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { PerfTimer } from \"@milaboratories/helpers\";\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-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol\";\nimport { DownloadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client\";\nimport type { DownloadApiPaths, DownloadRestClientType } from \"../proto-rest\";\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 wire: WireClientProvider<DownloadRestClientType | 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 wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n /** Pl storages available locally */\n localProjections: LocalStorageProjection[],\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wireConn) => {\n if (wireConn.type === \"grpc\") {\n return new DownloadClient(wireConn.Transport);\n } else {\n return RestAPI.createClient<DownloadApiPaths>({\n hostAndPort: wireConn.Config.hostAndPort,\n ssl: wireConn.Config.ssl,\n dispatcher: wireConn.Dispatcher,\n middlewares: wireConn.Middlewares,\n });\n }\n });\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(\n `blob ${stringifyWithResourceId(info)} download started, ` +\n `url: ${downloadUrl}, ` +\n `range: ${JSON.stringify(ops.range ?? null)}`,\n );\n\n const timer = PerfTimer.start();\n const result = isLocal(downloadUrl)\n ? await this.withLocalFileContent(downloadUrl, ops, handler)\n : await this.remoteFileDownloader.withContent(downloadUrl, remoteHeaders, ops, handler);\n\n this.logger.info(\n `blob ${stringifyWithResourceId(info)} download finished, ` + `took: ${timer.elapsed()}`,\n );\n return result;\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 const client = this.wire.get();\n if (client instanceof DownloadClient) {\n return await client.getDownloadURL(\n { resourceId: id, isInternalUse: false },\n addRTypeToMetadata(type, withAbort),\n ).response;\n } else {\n return (\n await client.POST(\"/v1/get-download-url\", {\n body: {\n resourceId: id.toString(),\n resourceSignature: \"\",\n isInternalUse: false,\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n }\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"],"mappings":"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"}
|
package/dist/clients/logs.cjs
CHANGED
|
@@ -34,6 +34,7 @@ var ClientLogs = class {
|
|
|
34
34
|
const resp = (await client.POST("/v1/last-lines", {
|
|
35
35
|
body: {
|
|
36
36
|
resourceId: rId.toString(),
|
|
37
|
+
resourceSignature: "",
|
|
37
38
|
lineCount,
|
|
38
39
|
offset: offsetBytes.toString(),
|
|
39
40
|
search: searchStr ?? "",
|
|
@@ -61,6 +62,7 @@ var ClientLogs = class {
|
|
|
61
62
|
const resp = (await client.POST("/v1/read/text", {
|
|
62
63
|
body: {
|
|
63
64
|
resourceId: rId.toString(),
|
|
65
|
+
resourceSignature: "",
|
|
64
66
|
readLimit: lineCount.toString(),
|
|
65
67
|
offset: offsetBytes.toString(),
|
|
66
68
|
search: searchStr ?? "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs.cjs","names":["StreamingClient","RestAPI"],"sources":["../../src/clients/logs.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { StreamingAPI_Response } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol\";\nimport { StreamingClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client\";\nimport type { StreamingApiPaths, StreamingRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLogs {\n public readonly wire: WireClientProvider<StreamingRestClientType | StreamingClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new StreamingClient(wire.Transport);\n }\n\n return RestAPI.createClient<StreamingApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** Reads text back and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async lastLines(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the end.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n if (client instanceof StreamingClient) {\n return (\n await client.lastLines(\n { resourceId: rId, lineCount: lineCount, offset: offsetBytes, search: searchStr },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/last-lines\", {\n body: {\n resourceId: rId.toString(),\n lineCount: lineCount,\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n\n /** Reads the file forward and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async readText(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the beginning.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n\n if (client instanceof StreamingClient) {\n return (\n await client.readText(\n {\n resourceId: notEmpty(rId),\n readLimit: BigInt(lineCount),\n offset: offsetBytes,\n search: searchStr,\n },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/read/text\", {\n body: {\n resourceId: rId.toString(),\n readLimit: lineCount.toString(),\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,aAAb,MAAwB;CACtB,AAAgB;CAEhB,YACE,2BACA,AAAgB,YAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,wCAAgB,KAAK,UAAU;AAG5C,UAAOC,kCAAQ,aAAgC;IAC7C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;;;CAKR,MAAa,UACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,MAAI,kBAAkBD,wCACpB,SACE,MAAM,OAAO,UACX;GAAE,YAAY;GAAgB;GAAW,QAAQ;GAAa,QAAQ;GAAW,oDAC9D,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,kBAAkB;GAClC,MAAM;IACJ,YAAY,IAAI,UAAU;
|
|
1
|
+
{"version":3,"file":"logs.cjs","names":["StreamingClient","RestAPI"],"sources":["../../src/clients/logs.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { StreamingAPI_Response } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol\";\nimport { StreamingClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client\";\nimport type { StreamingApiPaths, StreamingRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLogs {\n public readonly wire: WireClientProvider<StreamingRestClientType | StreamingClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new StreamingClient(wire.Transport);\n }\n\n return RestAPI.createClient<StreamingApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** Reads text back and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async lastLines(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the end.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n if (client instanceof StreamingClient) {\n return (\n await client.lastLines(\n { resourceId: rId, lineCount: lineCount, offset: offsetBytes, search: searchStr },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/last-lines\", {\n body: {\n resourceId: rId.toString(),\n resourceSignature: \"\",\n lineCount: lineCount,\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n\n /** Reads the file forward and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async readText(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the beginning.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n\n if (client instanceof StreamingClient) {\n return (\n await client.readText(\n {\n resourceId: notEmpty(rId),\n readLimit: BigInt(lineCount),\n offset: offsetBytes,\n search: searchStr,\n },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/read/text\", {\n body: {\n resourceId: rId.toString(),\n resourceSignature: \"\",\n readLimit: lineCount.toString(),\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,aAAb,MAAwB;CACtB,AAAgB;CAEhB,YACE,2BACA,AAAgB,YAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,wCAAgB,KAAK,UAAU;AAG5C,UAAOC,kCAAQ,aAAgC;IAC7C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;;;CAKR,MAAa,UACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,MAAI,kBAAkBD,wCACpB,SACE,MAAM,OAAO,UACX;GAAE,YAAY;GAAgB;GAAW,QAAQ;GAAa,QAAQ;GAAW,oDAC9D,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,kBAAkB;GAClC,MAAM;IACJ,YAAY,IAAI,UAAU;IAC1B,mBAAmB;IACR;IACX,QAAQ,YAAY,UAAU;IAC9B,QAAQ,aAAa;IACrB,UAAU;IACX;GACD,SAAS,EAAE,2DAA4B,MAAM,EAAE;GAChD,CAAC,EACF;AAEF,SAAO;GACL,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS;GACtC,MAAM,OAAO,KAAK,KAAK;GACvB,WAAW,OAAO,KAAK,UAAU;GAClC;;;;;CAMH,MAAa,SACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkBA,wCACpB,SACE,MAAM,OAAO,SACX;GACE,qDAAqB,IAAI;GACzB,WAAW,OAAO,UAAU;GAC5B,QAAQ;GACR,QAAQ;GACT,oDACkB,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,iBAAiB;GACjC,MAAM;IACJ,YAAY,IAAI,UAAU;IAC1B,mBAAmB;IACnB,WAAW,UAAU,UAAU;IAC/B,QAAQ,YAAY,UAAU;IAC9B,QAAQ,aAAa;IACrB,UAAU;IACX;GACD,SAAS,EAAE,2DAA4B,MAAM,EAAE;GAChD,CAAC,EACF;AAEF,SAAO;GACL,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS;GACtC,MAAM,OAAO,KAAK,KAAK;GACvB,WAAW,OAAO,KAAK,UAAU;GAClC"}
|
package/dist/clients/logs.js
CHANGED
|
@@ -33,6 +33,7 @@ var ClientLogs = class {
|
|
|
33
33
|
const resp = (await client.POST("/v1/last-lines", {
|
|
34
34
|
body: {
|
|
35
35
|
resourceId: rId.toString(),
|
|
36
|
+
resourceSignature: "",
|
|
36
37
|
lineCount,
|
|
37
38
|
offset: offsetBytes.toString(),
|
|
38
39
|
search: searchStr ?? "",
|
|
@@ -60,6 +61,7 @@ var ClientLogs = class {
|
|
|
60
61
|
const resp = (await client.POST("/v1/read/text", {
|
|
61
62
|
body: {
|
|
62
63
|
resourceId: rId.toString(),
|
|
64
|
+
resourceSignature: "",
|
|
63
65
|
readLimit: lineCount.toString(),
|
|
64
66
|
offset: offsetBytes.toString(),
|
|
65
67
|
search: searchStr ?? "",
|
package/dist/clients/logs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logs.js","names":[],"sources":["../../src/clients/logs.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { StreamingAPI_Response } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol\";\nimport { StreamingClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client\";\nimport type { StreamingApiPaths, StreamingRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLogs {\n public readonly wire: WireClientProvider<StreamingRestClientType | StreamingClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new StreamingClient(wire.Transport);\n }\n\n return RestAPI.createClient<StreamingApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** Reads text back and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async lastLines(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the end.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n if (client instanceof StreamingClient) {\n return (\n await client.lastLines(\n { resourceId: rId, lineCount: lineCount, offset: offsetBytes, search: searchStr },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/last-lines\", {\n body: {\n resourceId: rId.toString(),\n lineCount: lineCount,\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n\n /** Reads the file forward and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async readText(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the beginning.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n\n if (client instanceof StreamingClient) {\n return (\n await client.readText(\n {\n resourceId: notEmpty(rId),\n readLimit: BigInt(lineCount),\n offset: offsetBytes,\n search: searchStr,\n },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/read/text\", {\n body: {\n resourceId: rId.toString(),\n readLimit: lineCount.toString(),\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n}\n"],"mappings":";;;;;AAWA,IAAa,aAAb,MAAwB;CACtB,AAAgB;CAEhB,YACE,2BACA,AAAgB,YAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,gBAAgB,KAAK,UAAU;AAG5C,UAAO,QAAQ,aAAgC;IAC7C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;;;CAKR,MAAa,UACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,MAAI,kBAAkB,gBACpB,SACE,MAAM,OAAO,UACX;GAAE,YAAY;GAAgB;GAAW,QAAQ;GAAa,QAAQ;GAAW,EACjF,mBAAmB,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,kBAAkB;GAClC,MAAM;IACJ,YAAY,IAAI,UAAU;
|
|
1
|
+
{"version":3,"file":"logs.js","names":[],"sources":["../../src/clients/logs.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { StreamingAPI_Response } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol\";\nimport { StreamingClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client\";\nimport type { StreamingApiPaths, StreamingRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLogs {\n public readonly wire: WireClientProvider<StreamingRestClientType | StreamingClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new StreamingClient(wire.Transport);\n }\n\n return RestAPI.createClient<StreamingApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** Reads text back and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async lastLines(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the end.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n if (client instanceof StreamingClient) {\n return (\n await client.lastLines(\n { resourceId: rId, lineCount: lineCount, offset: offsetBytes, search: searchStr },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/last-lines\", {\n body: {\n resourceId: rId.toString(),\n resourceSignature: \"\",\n lineCount: lineCount,\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n\n /** Reads the file forward and returns the text,\n * the new offset\n * and the total size of the (currently existing) file. */\n public async readText(\n { id: rId, type: rType }: ResourceInfo,\n lineCount: number,\n offsetBytes: bigint = 0n, // if 0n, then start from the beginning.\n searchStr?: string,\n options?: RpcOptions,\n ): Promise<StreamingAPI_Response> {\n const client = this.wire.get();\n\n if (client instanceof StreamingClient) {\n return (\n await client.readText(\n {\n resourceId: notEmpty(rId),\n readLimit: BigInt(lineCount),\n offset: offsetBytes,\n search: searchStr,\n },\n addRTypeToMetadata(rType, options),\n )\n ).response;\n }\n\n const resp = (\n await client.POST(\"/v1/read/text\", {\n body: {\n resourceId: rId.toString(),\n resourceSignature: \"\",\n readLimit: lineCount.toString(),\n offset: offsetBytes.toString(),\n search: searchStr ?? \"\",\n searchRe: \"\",\n },\n headers: { ...createRTypeRoutingHeader(rType) },\n })\n ).data!;\n\n return {\n data: Buffer.from(resp.data, \"base64\"),\n size: BigInt(resp.size),\n newOffset: BigInt(resp.newOffset),\n };\n }\n}\n"],"mappings":";;;;;AAWA,IAAa,aAAb,MAAwB;CACtB,AAAgB;CAEhB,YACE,2BACA,AAAgB,YAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,gBAAgB,KAAK,UAAU;AAG5C,UAAO,QAAQ,aAAgC;IAC7C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;;;CAKR,MAAa,UACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAC9B,MAAI,kBAAkB,gBACpB,SACE,MAAM,OAAO,UACX;GAAE,YAAY;GAAgB;GAAW,QAAQ;GAAa,QAAQ;GAAW,EACjF,mBAAmB,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,kBAAkB;GAClC,MAAM;IACJ,YAAY,IAAI,UAAU;IAC1B,mBAAmB;IACR;IACX,QAAQ,YAAY,UAAU;IAC9B,QAAQ,aAAa;IACrB,UAAU;IACX;GACD,SAAS,EAAE,GAAG,yBAAyB,MAAM,EAAE;GAChD,CAAC,EACF;AAEF,SAAO;GACL,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS;GACtC,MAAM,OAAO,KAAK,KAAK;GACvB,WAAW,OAAO,KAAK,UAAU;GAClC;;;;;CAMH,MAAa,SACX,EAAE,IAAI,KAAK,MAAM,SACjB,WACA,cAAsB,IACtB,WACA,SACgC;EAChC,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkB,gBACpB,SACE,MAAM,OAAO,SACX;GACE,YAAY,SAAS,IAAI;GACzB,WAAW,OAAO,UAAU;GAC5B,QAAQ;GACR,QAAQ;GACT,EACD,mBAAmB,OAAO,QAAQ,CACnC,EACD;EAGJ,MAAM,QACJ,MAAM,OAAO,KAAK,iBAAiB;GACjC,MAAM;IACJ,YAAY,IAAI,UAAU;IAC1B,mBAAmB;IACnB,WAAW,UAAU,UAAU;IAC/B,QAAQ,YAAY,UAAU;IAC9B,QAAQ,aAAa;IACrB,UAAU;IACX;GACD,SAAS,EAAE,GAAG,yBAAyB,MAAM,EAAE;GAChD,CAAC,EACF;AAEF,SAAO;GACL,MAAM,OAAO,KAAK,KAAK,MAAM,SAAS;GACtC,MAAM,OAAO,KAAK,KAAK;GACvB,WAAW,OAAO,KAAK,UAAU;GAClC"}
|
package/dist/clients/ls_api.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ls_api.cjs","names":["LSClient","RestAPI"],"sources":["../../src/clients/ls_api.ts"],"sourcesContent":["import type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { RestAPI } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader } from \"@milaboratories/pl-client\";\nimport type {\n LsAPI_List_Response,\n LsAPI_ListItem,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol\";\nimport { LSClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client\";\nimport type { LsApiPaths, LsRestClientType } from \"../proto-rest\";\n\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLs {\n private readonly wire: WireClientProvider<LsRestClientType | LSClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n private readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new LSClient(wire.Transport);\n }\n\n return RestAPI.createClient<LsApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async list(\n rInfo: ResourceInfo,\n path: string,\n options?: RpcOptions,\n ): Promise<LsAPI_List_Response> {\n const client = this.wire.get();\n\n if (client instanceof LSClient) {\n return await client.list(\n {\n resourceId: rInfo.id,\n location: path,\n },\n addRTypeToMetadata(rInfo.type, options),\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/list\", {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\n })\n ).data!;\n\n const items: LsAPI_ListItem[] = resp.items.map((item) => ({\n name: item.name,\n size: BigInt(item.size),\n isDir: item.isDir,\n fullName: item.fullName,\n directory: item.directory,\n lastModified: parseTimestamp(item.lastModified),\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n\nfunction parseTimestamp(timestamp: string): { seconds: bigint; nanos: number } {\n // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z\n const date = new Date(timestamp);\n const seconds = BigInt(Math.floor(date.getTime() / 1000));\n\n // Extract fractional seconds from the original string\n const fractionalMatch = timestamp.match(/\\.(\\d+)Z?$/);\n let nanos = 0;\n if (fractionalMatch) {\n const fractionalPart = fractionalMatch[1];\n // Pad or truncate to 9 digits (nanoseconds)\n const padded = fractionalPart.padEnd(9, \"0\").slice(0, 9);\n nanos = parseInt(padded, 10);\n }\n\n return { seconds, nanos };\n}\n"],"mappings":";;;;;AAcA,IAAa,WAAb,MAAsB;CACpB,AAAiB;CAEjB,YACE,2BACA,AAAiB,QACjB;EADiB;AAEjB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,iCAAS,KAAK,UAAU;AAGrC,UAAOC,kCAAQ,aAAyB;IACtC,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;CAER,MAAa,KACX,OACA,MACA,SAC8B;EAC9B,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkBD,iCACpB,QAAO,MAAM,OAAO,KAClB;GACE,YAAY,MAAM;GAClB,UAAU;GACX,oDACkB,MAAM,MAAM,QAAQ,CACxC,CAAC;OACG;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,YAAY;IAC5B,MAAM;KACJ,YAAY,MAAM,GAAG,UAAU;KAC/B,UAAU;KACX;IACD,SAAS,EAAE,2DAA4B,MAAM,KAAK,EAAE;IACrD,CAAC,EACF;AAYF,UAAO;IACL,OAX8B,KAAK,MAAM,KAAK,UAAU;KACxD,MAAM,KAAK;KACX,MAAM,OAAO,KAAK,KAAK;KACvB,OAAO,KAAK;KACZ,UAAU,KAAK;KACf,WAAW,KAAK;KAChB,cAAc,eAAe,KAAK,aAAa;KAC/C,SAAS,KAAK;KACf,EAAE;IAID,WAAW,KAAK;IACjB;;;;AAKP,SAAS,eAAe,WAAuD;CAE7E,MAAM,OAAO,IAAI,KAAK,UAAU;CAChC,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,SAAS,GAAG,IAAK,CAAC;CAGzD,MAAM,kBAAkB,UAAU,MAAM,aAAa;CACrD,IAAI,QAAQ;AACZ,KAAI,iBAAiB;EAGnB,MAAM,SAFiB,gBAAgB,GAET,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE;AACxD,UAAQ,SAAS,QAAQ,GAAG;;AAG9B,QAAO;EAAE;EAAS;EAAO"}
|
|
1
|
+
{"version":3,"file":"ls_api.cjs","names":["LSClient","RestAPI"],"sources":["../../src/clients/ls_api.ts"],"sourcesContent":["import type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { RestAPI } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader } from \"@milaboratories/pl-client\";\nimport type {\n LsAPI_List_Response,\n LsAPI_ListItem,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol\";\nimport { LSClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client\";\nimport type { LsApiPaths, LsRestClientType } from \"../proto-rest\";\n\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLs {\n private readonly wire: WireClientProvider<LsRestClientType | LSClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n private readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new LSClient(wire.Transport);\n }\n\n return RestAPI.createClient<LsApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async list(\n rInfo: ResourceInfo,\n path: string,\n options?: RpcOptions,\n ): Promise<LsAPI_List_Response> {\n const client = this.wire.get();\n\n if (client instanceof LSClient) {\n return await client.list(\n {\n resourceId: rInfo.id,\n location: path,\n },\n addRTypeToMetadata(rInfo.type, options),\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/list\", {\n body: {\n resourceId: rInfo.id.toString(),\n resourceSignature: \"\",\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\n })\n ).data!;\n\n const items: LsAPI_ListItem[] = resp.items.map((item) => ({\n name: item.name,\n size: BigInt(item.size),\n isDir: item.isDir,\n fullName: item.fullName,\n directory: item.directory,\n lastModified: parseTimestamp(item.lastModified),\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n\nfunction parseTimestamp(timestamp: string): { seconds: bigint; nanos: number } {\n // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z\n const date = new Date(timestamp);\n const seconds = BigInt(Math.floor(date.getTime() / 1000));\n\n // Extract fractional seconds from the original string\n const fractionalMatch = timestamp.match(/\\.(\\d+)Z?$/);\n let nanos = 0;\n if (fractionalMatch) {\n const fractionalPart = fractionalMatch[1];\n // Pad or truncate to 9 digits (nanoseconds)\n const padded = fractionalPart.padEnd(9, \"0\").slice(0, 9);\n nanos = parseInt(padded, 10);\n }\n\n return { seconds, nanos };\n}\n"],"mappings":";;;;;AAcA,IAAa,WAAb,MAAsB;CACpB,AAAiB;CAEjB,YACE,2BACA,AAAiB,QACjB;EADiB;AAEjB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,iCAAS,KAAK,UAAU;AAGrC,UAAOC,kCAAQ,aAAyB;IACtC,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;CAER,MAAa,KACX,OACA,MACA,SAC8B;EAC9B,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkBD,iCACpB,QAAO,MAAM,OAAO,KAClB;GACE,YAAY,MAAM;GAClB,UAAU;GACX,oDACkB,MAAM,MAAM,QAAQ,CACxC,CAAC;OACG;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,YAAY;IAC5B,MAAM;KACJ,YAAY,MAAM,GAAG,UAAU;KAC/B,mBAAmB;KACnB,UAAU;KACX;IACD,SAAS,EAAE,2DAA4B,MAAM,KAAK,EAAE;IACrD,CAAC,EACF;AAYF,UAAO;IACL,OAX8B,KAAK,MAAM,KAAK,UAAU;KACxD,MAAM,KAAK;KACX,MAAM,OAAO,KAAK,KAAK;KACvB,OAAO,KAAK;KACZ,UAAU,KAAK;KACf,WAAW,KAAK;KAChB,cAAc,eAAe,KAAK,aAAa;KAC/C,SAAS,KAAK;KACf,EAAE;IAID,WAAW,KAAK;IACjB;;;;AAKP,SAAS,eAAe,WAAuD;CAE7E,MAAM,OAAO,IAAI,KAAK,UAAU;CAChC,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,SAAS,GAAG,IAAK,CAAC;CAGzD,MAAM,kBAAkB,UAAU,MAAM,aAAa;CACrD,IAAI,QAAQ;AACZ,KAAI,iBAAiB;EAGnB,MAAM,SAFiB,gBAAgB,GAET,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE;AACxD,UAAQ,SAAS,QAAQ,GAAG;;AAG9B,QAAO;EAAE;EAAS;EAAO"}
|
package/dist/clients/ls_api.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ls_api.js","names":[],"sources":["../../src/clients/ls_api.ts"],"sourcesContent":["import type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { RestAPI } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader } from \"@milaboratories/pl-client\";\nimport type {\n LsAPI_List_Response,\n LsAPI_ListItem,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol\";\nimport { LSClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client\";\nimport type { LsApiPaths, LsRestClientType } from \"../proto-rest\";\n\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLs {\n private readonly wire: WireClientProvider<LsRestClientType | LSClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n private readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new LSClient(wire.Transport);\n }\n\n return RestAPI.createClient<LsApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async list(\n rInfo: ResourceInfo,\n path: string,\n options?: RpcOptions,\n ): Promise<LsAPI_List_Response> {\n const client = this.wire.get();\n\n if (client instanceof LSClient) {\n return await client.list(\n {\n resourceId: rInfo.id,\n location: path,\n },\n addRTypeToMetadata(rInfo.type, options),\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/list\", {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\n })\n ).data!;\n\n const items: LsAPI_ListItem[] = resp.items.map((item) => ({\n name: item.name,\n size: BigInt(item.size),\n isDir: item.isDir,\n fullName: item.fullName,\n directory: item.directory,\n lastModified: parseTimestamp(item.lastModified),\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n\nfunction parseTimestamp(timestamp: string): { seconds: bigint; nanos: number } {\n // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z\n const date = new Date(timestamp);\n const seconds = BigInt(Math.floor(date.getTime() / 1000));\n\n // Extract fractional seconds from the original string\n const fractionalMatch = timestamp.match(/\\.(\\d+)Z?$/);\n let nanos = 0;\n if (fractionalMatch) {\n const fractionalPart = fractionalMatch[1];\n // Pad or truncate to 9 digits (nanoseconds)\n const padded = fractionalPart.padEnd(9, \"0\").slice(0, 9);\n nanos = parseInt(padded, 10);\n }\n\n return { seconds, nanos };\n}\n"],"mappings":";;;;AAcA,IAAa,WAAb,MAAsB;CACpB,AAAiB;CAEjB,YACE,2BACA,AAAiB,QACjB;EADiB;AAEjB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,SAAS,KAAK,UAAU;AAGrC,UAAO,QAAQ,aAAyB;IACtC,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;CAER,MAAa,KACX,OACA,MACA,SAC8B;EAC9B,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkB,SACpB,QAAO,MAAM,OAAO,KAClB;GACE,YAAY,MAAM;GAClB,UAAU;GACX,EACD,mBAAmB,MAAM,MAAM,QAAQ,CACxC,CAAC;OACG;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,YAAY;IAC5B,MAAM;KACJ,YAAY,MAAM,GAAG,UAAU;KAC/B,UAAU;KACX;IACD,SAAS,EAAE,GAAG,yBAAyB,MAAM,KAAK,EAAE;IACrD,CAAC,EACF;AAYF,UAAO;IACL,OAX8B,KAAK,MAAM,KAAK,UAAU;KACxD,MAAM,KAAK;KACX,MAAM,OAAO,KAAK,KAAK;KACvB,OAAO,KAAK;KACZ,UAAU,KAAK;KACf,WAAW,KAAK;KAChB,cAAc,eAAe,KAAK,aAAa;KAC/C,SAAS,KAAK;KACf,EAAE;IAID,WAAW,KAAK;IACjB;;;;AAKP,SAAS,eAAe,WAAuD;CAE7E,MAAM,OAAO,IAAI,KAAK,UAAU;CAChC,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,SAAS,GAAG,IAAK,CAAC;CAGzD,MAAM,kBAAkB,UAAU,MAAM,aAAa;CACrD,IAAI,QAAQ;AACZ,KAAI,iBAAiB;EAGnB,MAAM,SAFiB,gBAAgB,GAET,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE;AACxD,UAAQ,SAAS,QAAQ,GAAG;;AAG9B,QAAO;EAAE;EAAS;EAAO"}
|
|
1
|
+
{"version":3,"file":"ls_api.js","names":[],"sources":["../../src/clients/ls_api.ts"],"sourcesContent":["import type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type { WireClientProvider, WireClientProviderFactory } from \"@milaboratories/pl-client\";\nimport { RestAPI } from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader } from \"@milaboratories/pl-client\";\nimport type {\n LsAPI_List_Response,\n LsAPI_ListItem,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol\";\nimport { LSClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client\";\nimport type { LsApiPaths, LsRestClientType } from \"../proto-rest\";\n\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport class ClientLs {\n private readonly wire: WireClientProvider<LsRestClientType | LSClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n private readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new LSClient(wire.Transport);\n }\n\n return RestAPI.createClient<LsApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async list(\n rInfo: ResourceInfo,\n path: string,\n options?: RpcOptions,\n ): Promise<LsAPI_List_Response> {\n const client = this.wire.get();\n\n if (client instanceof LSClient) {\n return await client.list(\n {\n resourceId: rInfo.id,\n location: path,\n },\n addRTypeToMetadata(rInfo.type, options),\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/list\", {\n body: {\n resourceId: rInfo.id.toString(),\n resourceSignature: \"\",\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\n })\n ).data!;\n\n const items: LsAPI_ListItem[] = resp.items.map((item) => ({\n name: item.name,\n size: BigInt(item.size),\n isDir: item.isDir,\n fullName: item.fullName,\n directory: item.directory,\n lastModified: parseTimestamp(item.lastModified),\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n\nfunction parseTimestamp(timestamp: string): { seconds: bigint; nanos: number } {\n // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z\n const date = new Date(timestamp);\n const seconds = BigInt(Math.floor(date.getTime() / 1000));\n\n // Extract fractional seconds from the original string\n const fractionalMatch = timestamp.match(/\\.(\\d+)Z?$/);\n let nanos = 0;\n if (fractionalMatch) {\n const fractionalPart = fractionalMatch[1];\n // Pad or truncate to 9 digits (nanoseconds)\n const padded = fractionalPart.padEnd(9, \"0\").slice(0, 9);\n nanos = parseInt(padded, 10);\n }\n\n return { seconds, nanos };\n}\n"],"mappings":";;;;AAcA,IAAa,WAAb,MAAsB;CACpB,AAAiB;CAEjB,YACE,2BACA,AAAiB,QACjB;EADiB;AAEjB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,SAAS,KAAK,UAAU;AAGrC,UAAO,QAAQ,aAAyB;IACtC,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;CAER,MAAa,KACX,OACA,MACA,SAC8B;EAC9B,MAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,MAAI,kBAAkB,SACpB,QAAO,MAAM,OAAO,KAClB;GACE,YAAY,MAAM;GAClB,UAAU;GACX,EACD,mBAAmB,MAAM,MAAM,QAAQ,CACxC,CAAC;OACG;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,YAAY;IAC5B,MAAM;KACJ,YAAY,MAAM,GAAG,UAAU;KAC/B,mBAAmB;KACnB,UAAU;KACX;IACD,SAAS,EAAE,GAAG,yBAAyB,MAAM,KAAK,EAAE;IACrD,CAAC,EACF;AAYF,UAAO;IACL,OAX8B,KAAK,MAAM,KAAK,UAAU;KACxD,MAAM,KAAK;KACX,MAAM,OAAO,KAAK,KAAK;KACvB,OAAO,KAAK;KACZ,UAAU,KAAK;KACf,WAAW,KAAK;KAChB,cAAc,eAAe,KAAK,aAAa;KAC/C,SAAS,KAAK;KACf,EAAE;IAID,WAAW,KAAK;IACjB;;;;AAKP,SAAS,eAAe,WAAuD;CAE7E,MAAM,OAAO,IAAI,KAAK,UAAU;CAChC,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,SAAS,GAAG,IAAK,CAAC;CAGzD,MAAM,kBAAkB,UAAU,MAAM,aAAa;CACrD,IAAI,QAAQ;AACZ,KAAI,iBAAiB;EAGnB,MAAM,SAFiB,gBAAgB,GAET,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE;AACxD,UAAQ,SAAS,QAAQ,GAAG;;AAG9B,QAAO;EAAE;EAAS;EAAO"}
|
|
@@ -27,7 +27,10 @@ var ClientProgress = class {
|
|
|
27
27
|
if (client instanceof require_protocol_client.ProgressClient) report = (0, _milaboratories_ts_helpers.notEmpty)((await client.getStatus({ resourceId: id }, (0, _milaboratories_pl_client.addRTypeToMetadata)(type, options)).response).report);
|
|
28
28
|
else {
|
|
29
29
|
const resp = (await client.POST("/v1/get-progress", {
|
|
30
|
-
body: {
|
|
30
|
+
body: {
|
|
31
|
+
resourceId: id.toString(),
|
|
32
|
+
resourceSignature: ""
|
|
33
|
+
},
|
|
31
34
|
headers: { ...(0, _milaboratories_pl_client.createRTypeRoutingHeader)(type) }
|
|
32
35
|
})).data.report;
|
|
33
36
|
report = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.cjs","names":["ProgressClient","RestAPI"],"sources":["../../src/clients/progress.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport { ProgressClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client\";\nimport type { ProgressAPI_Report } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol\";\nimport type { ProgressApiPaths, ProgressRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport type ProgressStatus = {\n done: boolean;\n progress: number;\n bytesProcessed?: string;\n bytesTotal?: string;\n};\n\n// ClientProgress holds a grpc connection to the platform\n// but for Progress API service.\n// When blobs are transfered, one can got a status of transfering\n// using this API.\nexport class ClientProgress {\n public readonly wire: WireClientProvider<ProgressRestClientType | ProgressClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n _: Dispatcher,\n public readonly client: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new ProgressClient(wire.Transport);\n }\n\n return RestAPI.createClient<ProgressApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** getStatus gets a progress status by given rId and rType. */\n async getStatus({ id, type }: ResourceInfo, options?: RpcOptions): Promise<ProgressStatus> {\n const client = this.wire.get();\n\n let report: ProgressAPI_Report;\n if (client instanceof ProgressClient) {\n report = notEmpty(\n (await client.getStatus({ resourceId: id }, addRTypeToMetadata(type, options)).response)\n .report,\n );\n } else {\n const resp = (\n await client.POST(\"/v1/get-progress\", {\n body: { resourceId: id.toString() },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!.report;\n report = {\n done: resp.done,\n progress: resp.progress,\n bytesProcessed: BigInt(resp.bytesProcessed),\n bytesTotal: BigInt(resp.bytesTotal),\n name: resp.name,\n };\n }\n\n return {\n done: report.done,\n progress: report.progress,\n bytesProcessed: String(report.bytesProcessed),\n bytesTotal: String(report.bytesTotal),\n };\n }\n}\n"],"mappings":";;;;;;AA0BA,IAAa,iBAAb,MAA4B;CAC1B,AAAgB;CAEhB,YACE,2BACA,GACA,AAAgB,QAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,uCAAe,KAAK,UAAU;AAG3C,UAAOC,kCAAQ,aAA+B;IAC5C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;CAGR,MAAM,UAAU,EAAE,IAAI,QAAsB,SAA+C;EACzF,MAAM,SAAS,KAAK,KAAK,KAAK;EAE9B,IAAI;AACJ,MAAI,kBAAkBD,uCACpB,oDACG,MAAM,OAAO,UAAU,EAAE,YAAY,IAAI,oDAAqB,MAAM,QAAQ,CAAC,CAAC,UAC5E,OACJ;OACI;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,oBAAoB;IACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"progress.cjs","names":["ProgressClient","RestAPI"],"sources":["../../src/clients/progress.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport { ProgressClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client\";\nimport type { ProgressAPI_Report } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol\";\nimport type { ProgressApiPaths, ProgressRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport type ProgressStatus = {\n done: boolean;\n progress: number;\n bytesProcessed?: string;\n bytesTotal?: string;\n};\n\n// ClientProgress holds a grpc connection to the platform\n// but for Progress API service.\n// When blobs are transfered, one can got a status of transfering\n// using this API.\nexport class ClientProgress {\n public readonly wire: WireClientProvider<ProgressRestClientType | ProgressClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n _: Dispatcher,\n public readonly client: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new ProgressClient(wire.Transport);\n }\n\n return RestAPI.createClient<ProgressApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** getStatus gets a progress status by given rId and rType. */\n async getStatus({ id, type }: ResourceInfo, options?: RpcOptions): Promise<ProgressStatus> {\n const client = this.wire.get();\n\n let report: ProgressAPI_Report;\n if (client instanceof ProgressClient) {\n report = notEmpty(\n (await client.getStatus({ resourceId: id }, addRTypeToMetadata(type, options)).response)\n .report,\n );\n } else {\n const resp = (\n await client.POST(\"/v1/get-progress\", {\n body: { resourceId: id.toString(), resourceSignature: \"\" },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!.report;\n report = {\n done: resp.done,\n progress: resp.progress,\n bytesProcessed: BigInt(resp.bytesProcessed),\n bytesTotal: BigInt(resp.bytesTotal),\n name: resp.name,\n };\n }\n\n return {\n done: report.done,\n progress: report.progress,\n bytesProcessed: String(report.bytesProcessed),\n bytesTotal: String(report.bytesTotal),\n };\n }\n}\n"],"mappings":";;;;;;AA0BA,IAAa,iBAAb,MAA4B;CAC1B,AAAgB;CAEhB,YACE,2BACA,GACA,AAAgB,QAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAIA,uCAAe,KAAK,UAAU;AAG3C,UAAOC,kCAAQ,aAA+B;IAC5C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;CAGR,MAAM,UAAU,EAAE,IAAI,QAAsB,SAA+C;EACzF,MAAM,SAAS,KAAK,KAAK,KAAK;EAE9B,IAAI;AACJ,MAAI,kBAAkBD,uCACpB,oDACG,MAAM,OAAO,UAAU,EAAE,YAAY,IAAI,oDAAqB,MAAM,QAAQ,CAAC,CAAC,UAC5E,OACJ;OACI;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,oBAAoB;IACpC,MAAM;KAAE,YAAY,GAAG,UAAU;KAAE,mBAAmB;KAAI;IAC1D,SAAS,EAAE,2DAA4B,KAAK,EAAE;IAC/C,CAAC,EACF,KAAM;AACR,YAAS;IACP,MAAM,KAAK;IACX,UAAU,KAAK;IACf,gBAAgB,OAAO,KAAK,eAAe;IAC3C,YAAY,OAAO,KAAK,WAAW;IACnC,MAAM,KAAK;IACZ;;AAGH,SAAO;GACL,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,gBAAgB,OAAO,OAAO,eAAe;GAC7C,YAAY,OAAO,OAAO,WAAW;GACtC"}
|
package/dist/clients/progress.js
CHANGED
|
@@ -26,7 +26,10 @@ var ClientProgress = class {
|
|
|
26
26
|
if (client instanceof ProgressClient) report = notEmpty((await client.getStatus({ resourceId: id }, addRTypeToMetadata(type, options)).response).report);
|
|
27
27
|
else {
|
|
28
28
|
const resp = (await client.POST("/v1/get-progress", {
|
|
29
|
-
body: {
|
|
29
|
+
body: {
|
|
30
|
+
resourceId: id.toString(),
|
|
31
|
+
resourceSignature: ""
|
|
32
|
+
},
|
|
30
33
|
headers: { ...createRTypeRoutingHeader(type) }
|
|
31
34
|
})).data.report;
|
|
32
35
|
report = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","names":[],"sources":["../../src/clients/progress.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport { ProgressClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client\";\nimport type { ProgressAPI_Report } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol\";\nimport type { ProgressApiPaths, ProgressRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport type ProgressStatus = {\n done: boolean;\n progress: number;\n bytesProcessed?: string;\n bytesTotal?: string;\n};\n\n// ClientProgress holds a grpc connection to the platform\n// but for Progress API service.\n// When blobs are transfered, one can got a status of transfering\n// using this API.\nexport class ClientProgress {\n public readonly wire: WireClientProvider<ProgressRestClientType | ProgressClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n _: Dispatcher,\n public readonly client: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new ProgressClient(wire.Transport);\n }\n\n return RestAPI.createClient<ProgressApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** getStatus gets a progress status by given rId and rType. */\n async getStatus({ id, type }: ResourceInfo, options?: RpcOptions): Promise<ProgressStatus> {\n const client = this.wire.get();\n\n let report: ProgressAPI_Report;\n if (client instanceof ProgressClient) {\n report = notEmpty(\n (await client.getStatus({ resourceId: id }, addRTypeToMetadata(type, options)).response)\n .report,\n );\n } else {\n const resp = (\n await client.POST(\"/v1/get-progress\", {\n body: { resourceId: id.toString() },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!.report;\n report = {\n done: resp.done,\n progress: resp.progress,\n bytesProcessed: BigInt(resp.bytesProcessed),\n bytesTotal: BigInt(resp.bytesTotal),\n name: resp.name,\n };\n }\n\n return {\n done: report.done,\n progress: report.progress,\n bytesProcessed: String(report.bytesProcessed),\n bytesTotal: String(report.bytesTotal),\n };\n }\n}\n"],"mappings":";;;;;AA0BA,IAAa,iBAAb,MAA4B;CAC1B,AAAgB;CAEhB,YACE,2BACA,GACA,AAAgB,QAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,eAAe,KAAK,UAAU;AAG3C,UAAO,QAAQ,aAA+B;IAC5C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;CAGR,MAAM,UAAU,EAAE,IAAI,QAAsB,SAA+C;EACzF,MAAM,SAAS,KAAK,KAAK,KAAK;EAE9B,IAAI;AACJ,MAAI,kBAAkB,eACpB,UAAS,UACN,MAAM,OAAO,UAAU,EAAE,YAAY,IAAI,EAAE,mBAAmB,MAAM,QAAQ,CAAC,CAAC,UAC5E,OACJ;OACI;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,oBAAoB;IACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"progress.js","names":[],"sources":["../../src/clients/progress.ts"],"sourcesContent":["import type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { notEmpty } from \"@milaboratories/ts-helpers\";\nimport type { Dispatcher } from \"undici\";\nimport { ProgressClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client\";\nimport type { ProgressAPI_Report } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol\";\nimport type { ProgressApiPaths, ProgressRestClientType } from \"../proto-rest\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\n\nexport type ProgressStatus = {\n done: boolean;\n progress: number;\n bytesProcessed?: string;\n bytesTotal?: string;\n};\n\n// ClientProgress holds a grpc connection to the platform\n// but for Progress API service.\n// When blobs are transfered, one can got a status of transfering\n// using this API.\nexport class ClientProgress {\n public readonly wire: WireClientProvider<ProgressRestClientType | ProgressClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n _: Dispatcher,\n public readonly client: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new ProgressClient(wire.Transport);\n }\n\n return RestAPI.createClient<ProgressApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n /** getStatus gets a progress status by given rId and rType. */\n async getStatus({ id, type }: ResourceInfo, options?: RpcOptions): Promise<ProgressStatus> {\n const client = this.wire.get();\n\n let report: ProgressAPI_Report;\n if (client instanceof ProgressClient) {\n report = notEmpty(\n (await client.getStatus({ resourceId: id }, addRTypeToMetadata(type, options)).response)\n .report,\n );\n } else {\n const resp = (\n await client.POST(\"/v1/get-progress\", {\n body: { resourceId: id.toString(), resourceSignature: \"\" },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!.report;\n report = {\n done: resp.done,\n progress: resp.progress,\n bytesProcessed: BigInt(resp.bytesProcessed),\n bytesTotal: BigInt(resp.bytesTotal),\n name: resp.name,\n };\n }\n\n return {\n done: report.done,\n progress: report.progress,\n bytesProcessed: String(report.bytesProcessed),\n bytesTotal: String(report.bytesTotal),\n };\n }\n}\n"],"mappings":";;;;;AA0BA,IAAa,iBAAb,MAA4B;CAC1B,AAAgB;CAEhB,YACE,2BACA,GACA,AAAgB,QAChB,AAAgB,QAChB;EAFgB;EACA;AAEhB,OAAK,OAAO,0BAA0B,0BAA0B,SAAS;AACvE,OAAI,KAAK,SAAS,OAChB,QAAO,IAAI,eAAe,KAAK,UAAU;AAG3C,UAAO,QAAQ,aAA+B;IAC5C,aAAa,KAAK,OAAO;IACzB,KAAK,KAAK,OAAO;IACjB,YAAY,KAAK;IACjB,aAAa,KAAK;IACnB,CAAC;IACF;;CAGJ,QAAQ;;CAGR,MAAM,UAAU,EAAE,IAAI,QAAsB,SAA+C;EACzF,MAAM,SAAS,KAAK,KAAK,KAAK;EAE9B,IAAI;AACJ,MAAI,kBAAkB,eACpB,UAAS,UACN,MAAM,OAAO,UAAU,EAAE,YAAY,IAAI,EAAE,mBAAmB,MAAM,QAAQ,CAAC,CAAC,UAC5E,OACJ;OACI;GACL,MAAM,QACJ,MAAM,OAAO,KAAK,oBAAoB;IACpC,MAAM;KAAE,YAAY,GAAG,UAAU;KAAE,mBAAmB;KAAI;IAC1D,SAAS,EAAE,GAAG,yBAAyB,KAAK,EAAE;IAC/C,CAAC,EACF,KAAM;AACR,YAAS;IACP,MAAM,KAAK;IACX,UAAU,KAAK;IACf,gBAAgB,OAAO,KAAK,eAAe;IAC3C,YAAY,OAAO,KAAK,WAAW;IACnC,MAAM,KAAK;IACZ;;AAGH,SAAO;GACL,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,gBAAgB,OAAO,OAAO,eAAe;GAC7C,YAAY,OAAO,OAAO,WAAW;GACtC"}
|
package/dist/clients/upload.cjs
CHANGED
|
@@ -55,7 +55,10 @@ var ClientUpload = class {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
const init = (await client.POST("/v1/upload/init", {
|
|
58
|
-
body: {
|
|
58
|
+
body: {
|
|
59
|
+
resourceId: id.toString(),
|
|
60
|
+
resourceSignature: ""
|
|
61
|
+
},
|
|
59
62
|
headers: { ...(0, _milaboratories_pl_client.createRTypeRoutingHeader)(type) }
|
|
60
63
|
})).data;
|
|
61
64
|
return {
|
|
@@ -79,6 +82,7 @@ var ClientUpload = class {
|
|
|
79
82
|
const resp = (await client.POST("/v1/upload/get-part-url", {
|
|
80
83
|
body: {
|
|
81
84
|
resourceId: id.toString(),
|
|
85
|
+
resourceSignature: "",
|
|
82
86
|
partNumber: partNumber.toString(),
|
|
83
87
|
uploadedPartSize: "0",
|
|
84
88
|
isInternalUse: false,
|
|
@@ -126,9 +130,18 @@ var ClientUpload = class {
|
|
|
126
130
|
}
|
|
127
131
|
async finalize(info, options) {
|
|
128
132
|
const client = this.wire.get();
|
|
129
|
-
if (client instanceof require_protocol_client.UploadClient) await client.finalize({
|
|
133
|
+
if (client instanceof require_protocol_client.UploadClient) await client.finalize({
|
|
134
|
+
resourceId: info.id,
|
|
135
|
+
checksumAlgorithm: require_protocol.UploadAPI_ChecksumAlgorithm.UNSPECIFIED,
|
|
136
|
+
checksum: new Uint8Array(0)
|
|
137
|
+
}, (0, _milaboratories_pl_client.addRTypeToMetadata)(info.type, options));
|
|
130
138
|
else await client.POST("/v1/upload/finalize", {
|
|
131
|
-
body: {
|
|
139
|
+
body: {
|
|
140
|
+
resourceId: info.id.toString(),
|
|
141
|
+
resourceSignature: "",
|
|
142
|
+
checksumAlgorithm: 0,
|
|
143
|
+
checksum: ""
|
|
144
|
+
},
|
|
132
145
|
headers: { ...(0, _milaboratories_pl_client.createRTypeRoutingHeader)(info.type) }
|
|
133
146
|
});
|
|
134
147
|
}
|
|
@@ -152,6 +165,7 @@ var ClientUpload = class {
|
|
|
152
165
|
await client.POST("/v1/upload/update-progress", {
|
|
153
166
|
body: {
|
|
154
167
|
resourceId: id.toString(),
|
|
168
|
+
resourceSignature: "",
|
|
155
169
|
bytesProcessed: bytesProcessed.toString()
|
|
156
170
|
},
|
|
157
171
|
headers: { ...(0, _milaboratories_pl_client.createRTypeRoutingHeader)(type) }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.cjs","names":["UploadClient","RestAPI","UploadAPI_ChecksumAlgorithm","fs","crc32c"],"sources":["../../src/clients/upload.ts"],"sourcesContent":["import type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport * as fs from \"node:fs/promises\";\nimport type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport {\n UploadAPI_ChecksumAlgorithm,\n type UploadAPI_GetPartURL_Response,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol\";\nimport { UploadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client\";\nimport type { UploadApiPaths, UploadRestClientType } from \"../proto-rest\";\nimport { crc32c } from \"./crc32c\";\n\nimport type { IncomingHttpHeaders } from \"undici/types/header\";\n\nexport class MTimeError extends Error {\n name = \"MTimeError\";\n}\n\nexport class UnexpectedEOF extends Error {\n name = \"UnexpectedEOF\";\n}\n\nexport class NetworkError extends Error {\n name = \"NetworkError\";\n}\n\n/** Happens when the file doesn't exist */\nexport class NoFileForUploading extends Error {\n name = \"NoFileForUploading\";\n}\n\nexport class BadRequestError extends Error {\n name = \"BadRequestError\";\n}\n\n/** Low-level client for grpc uploadapi.\n * The user should pass here a concrete BlobUpload/<storageId> resource,\n * it can be got from handle field of BlobUpload. */\nexport class ClientUpload {\n private readonly wire: WireClientProvider<UploadRestClientType | UploadClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n _: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new UploadClient(wire.Transport);\n }\n\n return RestAPI.createClient<UploadApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async initUpload(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n ): Promise<{\n overall: bigint;\n toUpload: bigint[];\n checksumAlgorithm: UploadAPI_ChecksumAlgorithm;\n checksumHeader: string;\n }> {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n const init = (await client.init({ resourceId: id }, addRTypeToMetadata(type, options)))\n .response;\n\n return {\n overall: init.partsCount,\n toUpload: this.partsToUpload(init.partsCount, init.uploadedParts),\n checksumAlgorithm: init.checksumAlgorithm,\n checksumHeader: init.checksumHeader,\n };\n }\n\n const init = (\n await client.POST(\"/v1/upload/init\", {\n body: {\n resourceId: id.toString(),\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n\n return {\n overall: BigInt(init.partsCount),\n toUpload: this.partsToUpload(BigInt(init.partsCount), init.uploadedParts.map(BigInt)),\n checksumAlgorithm: init.checksumAlgorithm,\n checksumHeader: init.checksumHeader,\n };\n }\n\n public async partUpload(\n { id, type }: ResourceInfo,\n path: string,\n expectedMTimeUnix: bigint,\n partNumber: bigint,\n checksumAlgorithm: UploadAPI_ChecksumAlgorithm,\n checksumHeader: string,\n options?: RpcOptions,\n ) {\n const client = this.wire.get();\n\n let info: UploadAPI_GetPartURL_Response;\n if (client instanceof UploadClient) {\n // partChecksum isn't used for now but protoc requires it to be set\n info = (\n await client.getPartURL(\n {\n resourceId: id,\n partNumber,\n uploadedPartSize: 0n,\n isInternalUse: false,\n partChecksum: \"\",\n },\n addRTypeToMetadata(type, options),\n )\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/upload/get-part-url\", {\n body: {\n resourceId: id.toString(),\n partNumber: partNumber.toString(),\n uploadedPartSize: \"0\",\n isInternalUse: false,\n partChecksum: \"\",\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n info = {\n uploadUrl: resp.uploadUrl,\n method: resp.method,\n headers: resp.headers,\n chunkStart: BigInt(resp.chunkStart),\n chunkEnd: BigInt(resp.chunkEnd),\n };\n }\n\n const chunk = await readFileChunk(path, info.chunkStart, info.chunkEnd);\n await checkExpectedMTime(path, expectedMTimeUnix);\n\n if (checksumHeader && checksumAlgorithm === UploadAPI_ChecksumAlgorithm.CRC32C) {\n info.headers.push({ name: checksumHeader, value: calculateCrc32cChecksum(chunk) });\n }\n\n const contentLength = Number(info.chunkEnd - info.chunkStart);\n if (chunk.length !== contentLength) {\n throw new Error(\n `Chunk size mismatch: expected ${contentLength} bytes, but read ${chunk.length} bytes from file`,\n );\n }\n\n const headers = Object.fromEntries(\n info.headers.map(({ name, value }) => [name.toLowerCase(), value]),\n );\n\n try {\n const {\n body: rawBody,\n statusCode,\n headers: responseHeaders,\n } = await request(info.uploadUrl, {\n dispatcher: this.httpClient,\n body: chunk,\n // We got headers only after we send\n // the whole body (in case of S3 PUT requests it's 5 MB).\n // It might be slow with a slow connection (or with SSH),\n // that's why we got big timeout here.\n headersTimeout: 60000,\n bodyTimeout: 60000,\n // Prevent connection reuse by setting \"Connection: close\" header.\n // This works around an issue with the backend's built-in S3 implementation\n // that caused HTTP/1.1 protocol lines to be included in the uploaded file content.\n reset: true,\n headers,\n method: info.method.toUpperCase() as Dispatcher.HttpMethod,\n });\n\n // always read the body for resources to be garbage collected.\n const body = await rawBody.text();\n checkStatusCodeOk(statusCode, body, responseHeaders, info);\n } catch (e: unknown) {\n if (e instanceof NetworkError) throw e;\n\n if (e instanceof BadRequestError) throw e;\n\n throw new Error(\n `partUpload: error ${JSON.stringify(e)} happened while trying to do part upload to the url ${info.uploadUrl}, headers: ${JSON.stringify(info.headers)}`,\n );\n }\n\n await this.updateProgress({ id, type }, BigInt(info.chunkEnd - info.chunkStart), options);\n }\n\n public async finalize(info: ResourceInfo, options?: RpcOptions) {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n await client.finalize({ resourceId: info.id }, addRTypeToMetadata(info.type, options));\n } else {\n await client.POST(\"/v1/upload/finalize\", {\n body: {\n resourceId: info.id.toString(),\n },\n headers: { ...createRTypeRoutingHeader(info.type) },\n });\n }\n }\n\n /** Calculates parts that need to be uploaded from the parts that were\n * already uploaded. */\n private partsToUpload(partsCount: bigint, uploadedParts: bigint[]): bigint[] {\n const toUpload: bigint[] = [];\n const uploaded = new Set(uploadedParts);\n\n for (let i = 1n; i <= partsCount; i++) {\n if (!uploaded.has(i)) toUpload.push(i);\n }\n\n return toUpload;\n }\n\n private async updateProgress(\n { id, type }: ResourceInfo,\n bytesProcessed: bigint,\n options?: RpcOptions,\n ): Promise<void> {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n await client.updateProgress(\n {\n resourceId: id,\n bytesProcessed,\n },\n addRTypeToMetadata(type, options),\n ).response;\n return;\n }\n\n await client.POST(\"/v1/upload/update-progress\", {\n body: {\n resourceId: id.toString(),\n bytesProcessed: bytesProcessed.toString(),\n },\n headers: { ...createRTypeRoutingHeader(type) },\n });\n return;\n }\n}\n\nasync function readFileChunk(path: string, chunkStart: bigint, chunkEnd: bigint): Promise<Buffer> {\n let f: fs.FileHandle | undefined;\n try {\n f = await fs.open(path);\n const len = Number(chunkEnd - chunkStart);\n const pos = Number(chunkStart);\n const b = Buffer.alloc(len);\n const bytesRead = await readBytesFromPosition(f, b, len, pos);\n\n return b.subarray(0, bytesRead);\n } catch (e: unknown) {\n if (e && typeof e === \"object\" && \"code\" in e && e.code == \"ENOENT\")\n throw new NoFileForUploading(`there is no file ${path} for uploading`);\n throw e;\n } finally {\n await f?.close();\n }\n}\n\n/** Read len bytes from a given position.\n * Without this, `FileHandle.read` can read less bytes than needed. */\nasync function readBytesFromPosition(f: fs.FileHandle, b: Buffer, len: number, position: number) {\n let bytesReadTotal = 0;\n while (bytesReadTotal < len) {\n const { bytesRead } = await f.read(\n b,\n bytesReadTotal,\n len - bytesReadTotal,\n position + bytesReadTotal,\n );\n if (bytesRead === 0) {\n throw new UnexpectedEOF(\"file ended earlier than expected.\");\n }\n bytesReadTotal += bytesRead;\n }\n\n return bytesReadTotal;\n}\n\nasync function checkExpectedMTime(path: string, expectedMTimeUnix: bigint) {\n const mTime = BigInt(Math.floor((await fs.stat(path)).mtimeMs / 1000));\n if (mTime > expectedMTimeUnix) {\n throw new MTimeError(`file was modified, expected mtime: ${expectedMTimeUnix}, got: ${mTime}.`);\n }\n}\n\n/** S3 error codes that indicate expired/invalid temporary credentials.\n * These are transient: a retry with a fresh presigned URL will succeed\n * once the backend refreshes its STS credentials. */\nconst TRANSIENT_S3_ERROR_CODES = [\"ExpiredToken\", \"RequestExpired\", \"TokenRefreshRequired\"];\n\nfunction isTransientS3Error(body: string): boolean {\n return TRANSIENT_S3_ERROR_CODES.some((code) => body.includes(`<Code>${code}</Code>`));\n}\n\nfunction checkStatusCodeOk(\n statusCode: number,\n body: string,\n headers: IncomingHttpHeaders,\n info: UploadAPI_GetPartURL_Response,\n) {\n if (statusCode == 400) {\n if (isTransientS3Error(body)) {\n throw new NetworkError(\n `transient S3 credential error, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n throw new BadRequestError(\n `response is not ok, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n\n if (statusCode != 200) {\n throw new NetworkError(\n `response is not ok, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n}\n\n/** Calculate CRC32C checksum of a buffer and return as base64 string */\nfunction calculateCrc32cChecksum(data: Buffer): string {\n const checksum = crc32c(data);\n // Convert to unsigned 32-bit integer and then to base64\n const buffer = Buffer.alloc(4);\n\n buffer.writeUInt32BE(checksum, 0);\n return buffer.toString(\"base64\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"upload.cjs","names":["UploadClient","RestAPI","UploadAPI_ChecksumAlgorithm","fs","crc32c"],"sources":["../../src/clients/upload.ts"],"sourcesContent":["import type {\n WireClientProvider,\n WireClientProviderFactory,\n PlClient,\n} from \"@milaboratories/pl-client\";\nimport { addRTypeToMetadata, createRTypeRoutingHeader, RestAPI } from \"@milaboratories/pl-client\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { RpcOptions } from \"@protobuf-ts/runtime-rpc\";\nimport * as fs from \"node:fs/promises\";\nimport type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport {\n UploadAPI_ChecksumAlgorithm,\n type UploadAPI_GetPartURL_Response,\n} from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol\";\nimport { UploadClient } from \"../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client\";\nimport type { UploadApiPaths, UploadRestClientType } from \"../proto-rest\";\nimport { crc32c } from \"./crc32c\";\n\nimport type { IncomingHttpHeaders } from \"undici/types/header\";\n\nexport class MTimeError extends Error {\n name = \"MTimeError\";\n}\n\nexport class UnexpectedEOF extends Error {\n name = \"UnexpectedEOF\";\n}\n\nexport class NetworkError extends Error {\n name = \"NetworkError\";\n}\n\n/** Happens when the file doesn't exist */\nexport class NoFileForUploading extends Error {\n name = \"NoFileForUploading\";\n}\n\nexport class BadRequestError extends Error {\n name = \"BadRequestError\";\n}\n\n/** Low-level client for grpc uploadapi.\n * The user should pass here a concrete BlobUpload/<storageId> resource,\n * it can be got from handle field of BlobUpload. */\nexport class ClientUpload {\n private readonly wire: WireClientProvider<UploadRestClientType | UploadClient>;\n\n constructor(\n wireClientProviderFactory: WireClientProviderFactory,\n public readonly httpClient: Dispatcher,\n _: PlClient,\n public readonly logger: MiLogger,\n ) {\n this.wire = wireClientProviderFactory.createWireClientProvider((wire) => {\n if (wire.type === \"grpc\") {\n return new UploadClient(wire.Transport);\n }\n\n return RestAPI.createClient<UploadApiPaths>({\n hostAndPort: wire.Config.hostAndPort,\n ssl: wire.Config.ssl,\n dispatcher: wire.Dispatcher,\n middlewares: wire.Middlewares,\n });\n });\n }\n\n close() {}\n\n public async initUpload(\n { id, type }: ResourceInfo,\n options?: RpcOptions,\n ): Promise<{\n overall: bigint;\n toUpload: bigint[];\n checksumAlgorithm: UploadAPI_ChecksumAlgorithm;\n checksumHeader: string;\n }> {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n const init = (await client.init({ resourceId: id }, addRTypeToMetadata(type, options)))\n .response;\n\n return {\n overall: init.partsCount,\n toUpload: this.partsToUpload(init.partsCount, init.uploadedParts),\n checksumAlgorithm: init.checksumAlgorithm,\n checksumHeader: init.checksumHeader,\n };\n }\n\n const init = (\n await client.POST(\"/v1/upload/init\", {\n body: {\n resourceId: id.toString(),\n resourceSignature: \"\",\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n\n return {\n overall: BigInt(init.partsCount),\n toUpload: this.partsToUpload(BigInt(init.partsCount), init.uploadedParts.map(BigInt)),\n checksumAlgorithm: init.checksumAlgorithm,\n checksumHeader: init.checksumHeader,\n };\n }\n\n public async partUpload(\n { id, type }: ResourceInfo,\n path: string,\n expectedMTimeUnix: bigint,\n partNumber: bigint,\n checksumAlgorithm: UploadAPI_ChecksumAlgorithm,\n checksumHeader: string,\n options?: RpcOptions,\n ) {\n const client = this.wire.get();\n\n let info: UploadAPI_GetPartURL_Response;\n if (client instanceof UploadClient) {\n // partChecksum isn't used for now but protoc requires it to be set\n info = (\n await client.getPartURL(\n {\n resourceId: id,\n partNumber,\n uploadedPartSize: 0n,\n isInternalUse: false,\n partChecksum: \"\",\n },\n addRTypeToMetadata(type, options),\n )\n ).response;\n } else {\n const resp = (\n await client.POST(\"/v1/upload/get-part-url\", {\n body: {\n resourceId: id.toString(),\n resourceSignature: \"\",\n partNumber: partNumber.toString(),\n uploadedPartSize: \"0\",\n isInternalUse: false,\n partChecksum: \"\",\n },\n headers: { ...createRTypeRoutingHeader(type) },\n })\n ).data!;\n info = {\n uploadUrl: resp.uploadUrl,\n method: resp.method,\n headers: resp.headers,\n chunkStart: BigInt(resp.chunkStart),\n chunkEnd: BigInt(resp.chunkEnd),\n };\n }\n\n const chunk = await readFileChunk(path, info.chunkStart, info.chunkEnd);\n await checkExpectedMTime(path, expectedMTimeUnix);\n\n if (checksumHeader && checksumAlgorithm === UploadAPI_ChecksumAlgorithm.CRC32C) {\n info.headers.push({ name: checksumHeader, value: calculateCrc32cChecksum(chunk) });\n }\n\n const contentLength = Number(info.chunkEnd - info.chunkStart);\n if (chunk.length !== contentLength) {\n throw new Error(\n `Chunk size mismatch: expected ${contentLength} bytes, but read ${chunk.length} bytes from file`,\n );\n }\n\n const headers = Object.fromEntries(\n info.headers.map(({ name, value }) => [name.toLowerCase(), value]),\n );\n\n try {\n const {\n body: rawBody,\n statusCode,\n headers: responseHeaders,\n } = await request(info.uploadUrl, {\n dispatcher: this.httpClient,\n body: chunk,\n // We got headers only after we send\n // the whole body (in case of S3 PUT requests it's 5 MB).\n // It might be slow with a slow connection (or with SSH),\n // that's why we got big timeout here.\n headersTimeout: 60000,\n bodyTimeout: 60000,\n // Prevent connection reuse by setting \"Connection: close\" header.\n // This works around an issue with the backend's built-in S3 implementation\n // that caused HTTP/1.1 protocol lines to be included in the uploaded file content.\n reset: true,\n headers,\n method: info.method.toUpperCase() as Dispatcher.HttpMethod,\n });\n\n // always read the body for resources to be garbage collected.\n const body = await rawBody.text();\n checkStatusCodeOk(statusCode, body, responseHeaders, info);\n } catch (e: unknown) {\n if (e instanceof NetworkError) throw e;\n\n if (e instanceof BadRequestError) throw e;\n\n throw new Error(\n `partUpload: error ${JSON.stringify(e)} happened while trying to do part upload to the url ${info.uploadUrl}, headers: ${JSON.stringify(info.headers)}`,\n );\n }\n\n await this.updateProgress({ id, type }, BigInt(info.chunkEnd - info.chunkStart), options);\n }\n\n public async finalize(info: ResourceInfo, options?: RpcOptions) {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n await client.finalize(\n {\n resourceId: info.id,\n checksumAlgorithm: UploadAPI_ChecksumAlgorithm.UNSPECIFIED,\n checksum: new Uint8Array(0),\n },\n addRTypeToMetadata(info.type, options),\n );\n } else {\n await client.POST(\"/v1/upload/finalize\", {\n body: {\n resourceId: info.id.toString(),\n resourceSignature: \"\",\n checksumAlgorithm: 0,\n checksum: \"\",\n },\n headers: { ...createRTypeRoutingHeader(info.type) },\n });\n }\n }\n\n /** Calculates parts that need to be uploaded from the parts that were\n * already uploaded. */\n private partsToUpload(partsCount: bigint, uploadedParts: bigint[]): bigint[] {\n const toUpload: bigint[] = [];\n const uploaded = new Set(uploadedParts);\n\n for (let i = 1n; i <= partsCount; i++) {\n if (!uploaded.has(i)) toUpload.push(i);\n }\n\n return toUpload;\n }\n\n private async updateProgress(\n { id, type }: ResourceInfo,\n bytesProcessed: bigint,\n options?: RpcOptions,\n ): Promise<void> {\n const client = this.wire.get();\n\n if (client instanceof UploadClient) {\n await client.updateProgress(\n {\n resourceId: id,\n bytesProcessed,\n },\n addRTypeToMetadata(type, options),\n ).response;\n return;\n }\n\n await client.POST(\"/v1/upload/update-progress\", {\n body: {\n resourceId: id.toString(),\n resourceSignature: \"\",\n bytesProcessed: bytesProcessed.toString(),\n },\n headers: { ...createRTypeRoutingHeader(type) },\n });\n return;\n }\n}\n\nasync function readFileChunk(path: string, chunkStart: bigint, chunkEnd: bigint): Promise<Buffer> {\n let f: fs.FileHandle | undefined;\n try {\n f = await fs.open(path);\n const len = Number(chunkEnd - chunkStart);\n const pos = Number(chunkStart);\n const b = Buffer.alloc(len);\n const bytesRead = await readBytesFromPosition(f, b, len, pos);\n\n return b.subarray(0, bytesRead);\n } catch (e: unknown) {\n if (e && typeof e === \"object\" && \"code\" in e && e.code == \"ENOENT\")\n throw new NoFileForUploading(`there is no file ${path} for uploading`);\n throw e;\n } finally {\n await f?.close();\n }\n}\n\n/** Read len bytes from a given position.\n * Without this, `FileHandle.read` can read less bytes than needed. */\nasync function readBytesFromPosition(f: fs.FileHandle, b: Buffer, len: number, position: number) {\n let bytesReadTotal = 0;\n while (bytesReadTotal < len) {\n const { bytesRead } = await f.read(\n b,\n bytesReadTotal,\n len - bytesReadTotal,\n position + bytesReadTotal,\n );\n if (bytesRead === 0) {\n throw new UnexpectedEOF(\"file ended earlier than expected.\");\n }\n bytesReadTotal += bytesRead;\n }\n\n return bytesReadTotal;\n}\n\nasync function checkExpectedMTime(path: string, expectedMTimeUnix: bigint) {\n const mTime = BigInt(Math.floor((await fs.stat(path)).mtimeMs / 1000));\n if (mTime > expectedMTimeUnix) {\n throw new MTimeError(`file was modified, expected mtime: ${expectedMTimeUnix}, got: ${mTime}.`);\n }\n}\n\n/** S3 error codes that indicate expired/invalid temporary credentials.\n * These are transient: a retry with a fresh presigned URL will succeed\n * once the backend refreshes its STS credentials. */\nconst TRANSIENT_S3_ERROR_CODES = [\"ExpiredToken\", \"RequestExpired\", \"TokenRefreshRequired\"];\n\nfunction isTransientS3Error(body: string): boolean {\n return TRANSIENT_S3_ERROR_CODES.some((code) => body.includes(`<Code>${code}</Code>`));\n}\n\nfunction checkStatusCodeOk(\n statusCode: number,\n body: string,\n headers: IncomingHttpHeaders,\n info: UploadAPI_GetPartURL_Response,\n) {\n if (statusCode == 400) {\n if (isTransientS3Error(body)) {\n throw new NetworkError(\n `transient S3 credential error, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n throw new BadRequestError(\n `response is not ok, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n\n if (statusCode != 200) {\n throw new NetworkError(\n `response is not ok, status code: ${statusCode},` +\n ` body: ${body}, headers: ${JSON.stringify(headers)}, url: ${info.uploadUrl}`,\n );\n }\n}\n\n/** Calculate CRC32C checksum of a buffer and return as base64 string */\nfunction calculateCrc32cChecksum(data: Buffer): string {\n const checksum = crc32c(data);\n // Convert to unsigned 32-bit integer and then to base64\n const buffer = Buffer.alloc(4);\n\n buffer.writeUInt32BE(checksum, 0);\n return buffer.toString(\"base64\");\n}\n"],"mappings":"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"}
|
package/dist/clients/upload.js
CHANGED
|
@@ -53,7 +53,10 @@ var ClientUpload = class {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
const init = (await client.POST("/v1/upload/init", {
|
|
56
|
-
body: {
|
|
56
|
+
body: {
|
|
57
|
+
resourceId: id.toString(),
|
|
58
|
+
resourceSignature: ""
|
|
59
|
+
},
|
|
57
60
|
headers: { ...createRTypeRoutingHeader(type) }
|
|
58
61
|
})).data;
|
|
59
62
|
return {
|
|
@@ -77,6 +80,7 @@ var ClientUpload = class {
|
|
|
77
80
|
const resp = (await client.POST("/v1/upload/get-part-url", {
|
|
78
81
|
body: {
|
|
79
82
|
resourceId: id.toString(),
|
|
83
|
+
resourceSignature: "",
|
|
80
84
|
partNumber: partNumber.toString(),
|
|
81
85
|
uploadedPartSize: "0",
|
|
82
86
|
isInternalUse: false,
|
|
@@ -124,9 +128,18 @@ var ClientUpload = class {
|
|
|
124
128
|
}
|
|
125
129
|
async finalize(info, options) {
|
|
126
130
|
const client = this.wire.get();
|
|
127
|
-
if (client instanceof UploadClient) await client.finalize({
|
|
131
|
+
if (client instanceof UploadClient) await client.finalize({
|
|
132
|
+
resourceId: info.id,
|
|
133
|
+
checksumAlgorithm: UploadAPI_ChecksumAlgorithm.UNSPECIFIED,
|
|
134
|
+
checksum: new Uint8Array(0)
|
|
135
|
+
}, addRTypeToMetadata(info.type, options));
|
|
128
136
|
else await client.POST("/v1/upload/finalize", {
|
|
129
|
-
body: {
|
|
137
|
+
body: {
|
|
138
|
+
resourceId: info.id.toString(),
|
|
139
|
+
resourceSignature: "",
|
|
140
|
+
checksumAlgorithm: 0,
|
|
141
|
+
checksum: ""
|
|
142
|
+
},
|
|
130
143
|
headers: { ...createRTypeRoutingHeader(info.type) }
|
|
131
144
|
});
|
|
132
145
|
}
|
|
@@ -150,6 +163,7 @@ var ClientUpload = class {
|
|
|
150
163
|
await client.POST("/v1/upload/update-progress", {
|
|
151
164
|
body: {
|
|
152
165
|
resourceId: id.toString(),
|
|
166
|
+
resourceSignature: "",
|
|
153
167
|
bytesProcessed: bytesProcessed.toString()
|
|
154
168
|
},
|
|
155
169
|
headers: { ...createRTypeRoutingHeader(type) }
|