@milaboratories/pl-drivers 1.11.24 → 1.11.26

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.
@@ -43,8 +43,7 @@ class ClientLs {
43
43
  isDir: item.isDir,
44
44
  fullName: item.fullName,
45
45
  directory: item.directory,
46
- // TODO: check we get value in a correct way here
47
- lastModified: { seconds: BigInt(item.lastModified), nanos: 0 },
46
+ lastModified: parseTimestamp(item.lastModified),
48
47
  version: item.version,
49
48
  }));
50
49
  return {
@@ -54,6 +53,21 @@ class ClientLs {
54
53
  }
55
54
  }
56
55
  }
56
+ function parseTimestamp(timestamp) {
57
+ // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z
58
+ const date = new Date(timestamp);
59
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
60
+ // Extract fractional seconds from the original string
61
+ const fractionalMatch = timestamp.match(/\.(\d+)Z?$/);
62
+ let nanos = 0;
63
+ if (fractionalMatch) {
64
+ const fractionalPart = fractionalMatch[1];
65
+ // Pad or truncate to 9 digits (nanoseconds)
66
+ const padded = fractionalPart.padEnd(9, '0').slice(0, 9);
67
+ nanos = parseInt(padded, 10);
68
+ }
69
+ return { seconds, nanos };
70
+ }
57
71
 
58
72
  exports.ClientLs = ClientLs;
59
73
  //# sourceMappingURL=ls_api.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ls_api.cjs","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 { LsAPI_List_Response, LsAPI_ListItem } 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(\n (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\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 = (await client.POST('/v1/list', {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\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 // TODO: check we get value in a correct way here\n lastModified: { seconds: BigInt(item.lastModified), nanos: 0 },\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n"],"names":["LSClient","RestAPI","addRTypeToMetadata","createRTypeRoutingHeader"],"mappings":";;;;;MAWa,QAAQ,CAAA;AAKA,IAAA,MAAA;AAJF,IAAA,IAAI;IAErB,WAAA,CACE,yBAAoD,EACnC,MAAgB,EAAA;QAAhB,IAAA,CAAA,MAAM,GAAN,MAAM;QAEvB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC,wBAAwB,CAC5D,CAAC,IAAI,KAAI;AACP,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE;AACxB,gBAAA,OAAO,IAAIA,wBAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;YACrC;YAEA,OAAOC,gBAAO,CAAC,YAAY,CAAa;AACtC,gBAAA,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;AACpC,gBAAA,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;gBACpB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;AAC9B,aAAA,CAAC;AACJ,QAAA,CAAC,CACF;IACH;AAEA,IAAA,KAAK,KAAI;AAEF,IAAA,MAAM,IAAI,CACf,KAAmB,EACnB,IAAY,EACZ,OAAoB,EAAA;QAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;AAE9B,QAAA,IAAI,MAAM,YAAYD,wBAAQ,EAAE;AAC9B,YAAA,OAAO,MAAM,MAAM,CAAC,IAAI,CACtB;gBACE,UAAU,EAAE,KAAK,CAAC,EAAE;AACpB,gBAAA,QAAQ,EAAE,IAAI;aACf,EACDE,2BAAkB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CACxC,CAAC,QAAQ;QACZ;aAAO;YACL,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE;AAC1C,gBAAA,IAAI,EAAE;AACJ,oBAAA,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE;AAC/B,oBAAA,QAAQ,EAAE,IAAI;AACf,iBAAA;gBACD,OAAO,EAAE,EAAE,GAAGC,iCAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;aACrD,CAAC,EAAE,IAAK;AAET,YAAA,MAAM,KAAK,GAAqB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;gBACxD,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,gBAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;;AAEzB,gBAAA,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;gBAC9D,OAAO,EAAE,IAAI,CAAC,OAAO;AACtB,aAAA,CAAC,CAAC;YAEH,OAAO;gBACL,KAAK;gBACL,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B;QACH;IACF;AACD;;;;"}
1
+ {"version":3,"file":"ls_api.cjs","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 { LsAPI_List_Response, LsAPI_ListItem } 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(\n (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\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 = (await client.POST('/v1/list', {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\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"],"names":["LSClient","RestAPI","addRTypeToMetadata","createRTypeRoutingHeader"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ls_api.d.ts","sourceRoot":"","sources":["../../src/clients/ls_api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAsB,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAG/F,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,iFAAiF,CAAC;AAI3I,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,qBAAa,QAAQ;IAKjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkD;gBAGrE,yBAAyB,EAAE,yBAAyB,EACnC,MAAM,EAAE,QAAQ;IAkBnC,KAAK;IAEQ,IAAI,CACf,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,mBAAmB,CAAC;CAqChC"}
1
+ {"version":3,"file":"ls_api.d.ts","sourceRoot":"","sources":["../../src/clients/ls_api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAsB,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAG/F,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,iFAAiF,CAAC;AAI3I,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,qBAAa,QAAQ;IAKjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkD;gBAGrE,yBAAyB,EAAE,yBAAyB,EACnC,MAAM,EAAE,QAAQ;IAkBnC,KAAK;IAEQ,IAAI,CACf,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,mBAAmB,CAAC;CAoChC"}
@@ -41,8 +41,7 @@ class ClientLs {
41
41
  isDir: item.isDir,
42
42
  fullName: item.fullName,
43
43
  directory: item.directory,
44
- // TODO: check we get value in a correct way here
45
- lastModified: { seconds: BigInt(item.lastModified), nanos: 0 },
44
+ lastModified: parseTimestamp(item.lastModified),
46
45
  version: item.version,
47
46
  }));
48
47
  return {
@@ -52,6 +51,21 @@ class ClientLs {
52
51
  }
53
52
  }
54
53
  }
54
+ function parseTimestamp(timestamp) {
55
+ // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z
56
+ const date = new Date(timestamp);
57
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
58
+ // Extract fractional seconds from the original string
59
+ const fractionalMatch = timestamp.match(/\.(\d+)Z?$/);
60
+ let nanos = 0;
61
+ if (fractionalMatch) {
62
+ const fractionalPart = fractionalMatch[1];
63
+ // Pad or truncate to 9 digits (nanoseconds)
64
+ const padded = fractionalPart.padEnd(9, '0').slice(0, 9);
65
+ nanos = parseInt(padded, 10);
66
+ }
67
+ return { seconds, nanos };
68
+ }
55
69
 
56
70
  export { ClientLs };
57
71
  //# sourceMappingURL=ls_api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ls_api.js","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 { LsAPI_List_Response, LsAPI_ListItem } 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(\n (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\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 = (await client.POST('/v1/list', {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\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 // TODO: check we get value in a correct way here\n lastModified: { seconds: BigInt(item.lastModified), nanos: 0 },\n version: item.version,\n }));\n\n return {\n items,\n delimiter: resp.delimiter,\n };\n }\n }\n}\n"],"names":[],"mappings":";;;MAWa,QAAQ,CAAA;AAKA,IAAA,MAAA;AAJF,IAAA,IAAI;IAErB,WAAA,CACE,yBAAoD,EACnC,MAAgB,EAAA;QAAhB,IAAA,CAAA,MAAM,GAAN,MAAM;QAEvB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC,wBAAwB,CAC5D,CAAC,IAAI,KAAI;AACP,YAAA,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE;AACxB,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;YACrC;YAEA,OAAO,OAAO,CAAC,YAAY,CAAa;AACtC,gBAAA,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;AACpC,gBAAA,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG;gBACpB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;AAC9B,aAAA,CAAC;AACJ,QAAA,CAAC,CACF;IACH;AAEA,IAAA,KAAK,KAAI;AAEF,IAAA,MAAM,IAAI,CACf,KAAmB,EACnB,IAAY,EACZ,OAAoB,EAAA;QAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;AAE9B,QAAA,IAAI,MAAM,YAAY,QAAQ,EAAE;AAC9B,YAAA,OAAO,MAAM,MAAM,CAAC,IAAI,CACtB;gBACE,UAAU,EAAE,KAAK,CAAC,EAAE;AACpB,gBAAA,QAAQ,EAAE,IAAI;aACf,EACD,kBAAkB,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CACxC,CAAC,QAAQ;QACZ;aAAO;YACL,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE;AAC1C,gBAAA,IAAI,EAAE;AACJ,oBAAA,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE;AAC/B,oBAAA,QAAQ,EAAE,IAAI;AACf,iBAAA;gBACD,OAAO,EAAE,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;aACrD,CAAC,EAAE,IAAK;AAET,YAAA,MAAM,KAAK,GAAqB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;gBACxD,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,gBAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS,EAAE,IAAI,CAAC,SAAS;;AAEzB,gBAAA,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;gBAC9D,OAAO,EAAE,IAAI,CAAC,OAAO;AACtB,aAAA,CAAC,CAAC;YAEH,OAAO;gBACL,KAAK;gBACL,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B;QACH;IACF;AACD;;;;"}
1
+ {"version":3,"file":"ls_api.js","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 { LsAPI_List_Response, LsAPI_ListItem } 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(\n (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\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 = (await client.POST('/v1/list', {\n body: {\n resourceId: rInfo.id.toString(),\n location: path,\n },\n headers: { ...createRTypeRoutingHeader(rInfo.type) },\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"],"names":[],"mappings":"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"}
@@ -75,7 +75,7 @@ class DownloadBlobTask {
75
75
  catch (e) {
76
76
  this.logger.error(`blob ${plClient.stringifyWithResourceId(this.rInfo)} download failed, `
77
77
  + `state: ${JSON.stringify(this.state)}, `
78
- + `error: ${e}`);
78
+ + `error: ${JSON.stringify(e)}`);
79
79
  if (nonRecoverableError(e)) {
80
80
  this.setError(e);
81
81
  this.change.markChanged(`blob ${plClient.resourceIdToString(this.rInfo.id)} download failed`);
@@ -1 +1 @@
1
- {"version":3,"file":"download_blob_task.cjs","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { isDownloadNetworkError400 } from '../../helpers/download_errors';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download finished`);\n } catch (e: any) {\n this.logger.error(\n `blob ${stringifyWithResourceId(this.rInfo)} download failed, `\n + `state: ${JSON.stringify(this.state)}, `\n + `error: ${e}`,\n );\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(\n `blob ${stringifyWithResourceId(this.rInfo)} was already downloaded, `\n + `path: ${this.state.filePath}`,\n );\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || isDownloadNetworkError400(e)\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["ChangeSource","CallersCounter","resourceIdToString","stringifyWithResourceId","fsp","ensureDirExists","path","fileExists","createPathAtomically","Writable","isDownloadNetworkError400","UnknownStorageError","WrongLocalFileUrl"],"mappings":"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"}
1
+ {"version":3,"file":"download_blob_task.cjs","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { isDownloadNetworkError400 } from '../../helpers/download_errors';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download finished`);\n } catch (e: any) {\n this.logger.error(\n `blob ${stringifyWithResourceId(this.rInfo)} download failed, `\n + `state: ${JSON.stringify(this.state)}, `\n + `error: ${JSON.stringify(e)}`,\n );\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(\n `blob ${stringifyWithResourceId(this.rInfo)} was already downloaded, `\n + `path: ${this.state.filePath}`,\n );\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || isDownloadNetworkError400(e)\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["ChangeSource","CallersCounter","resourceIdToString","stringifyWithResourceId","fsp","ensureDirExists","path","fileExists","createPathAtomically","Writable","isDownloadNetworkError400","UnknownStorageError","WrongLocalFileUrl"],"mappings":"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"}
@@ -53,7 +53,7 @@ class DownloadBlobTask {
53
53
  catch (e) {
54
54
  this.logger.error(`blob ${stringifyWithResourceId(this.rInfo)} download failed, `
55
55
  + `state: ${JSON.stringify(this.state)}, `
56
- + `error: ${e}`);
56
+ + `error: ${JSON.stringify(e)}`);
57
57
  if (nonRecoverableError(e)) {
58
58
  this.setError(e);
59
59
  this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download failed`);
@@ -1 +1 @@
1
- {"version":3,"file":"download_blob_task.js","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { isDownloadNetworkError400 } from '../../helpers/download_errors';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download finished`);\n } catch (e: any) {\n this.logger.error(\n `blob ${stringifyWithResourceId(this.rInfo)} download failed, `\n + `state: ${JSON.stringify(this.state)}, `\n + `error: ${e}`,\n );\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(\n `blob ${stringifyWithResourceId(this.rInfo)} was already downloaded, `\n + `path: ${this.state.filePath}`,\n );\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || isDownloadNetworkError400(e)\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["fs"],"mappings":"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"}
1
+ {"version":3,"file":"download_blob_task.js","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { isDownloadNetworkError400 } from '../../helpers/download_errors';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download finished`);\n } catch (e: any) {\n this.logger.error(\n `blob ${stringifyWithResourceId(this.rInfo)} download failed, `\n + `state: ${JSON.stringify(this.state)}, `\n + `error: ${JSON.stringify(e)}`,\n );\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob ${resourceIdToString(this.rInfo.id)} download failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(\n `blob ${stringifyWithResourceId(this.rInfo)} was already downloaded, `\n + `path: ${this.state.filePath}`,\n );\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || isDownloadNetworkError400(e)\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["fs"],"mappings":"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"}
@@ -222,7 +222,7 @@ class LogGetter {
222
222
  this.change.markChanged();
223
223
  return;
224
224
  }
225
- this.logger.error(`Stream log lines for ${plClient.stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`);
225
+ this.logger.error(`Stream log lines for ${plClient.stringifyWithResourceId(this.rInfo.id)} failed, reason: ${JSON.stringify(e)}`);
226
226
  throw e;
227
227
  }
228
228
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logs_stream.cjs","sources":["../../src/drivers/logs_stream.ts"],"sourcesContent":["import type {\n ComputableCtx,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n PollingComputableHooks,\n} from '@milaboratories/computable';\nimport type {\n ResourceId,\n ResourceType } from '@milaboratories/pl-client';\nimport {\n resourceIdToString,\n stringifyWithResourceId,\n} from '@milaboratories/pl-client';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { asyncPool, CallersCounter } from '@milaboratories/ts-helpers';\nimport type { ClientLogs } from '../clients/logs';\nimport { randomUUID } from 'node:crypto';\nimport type { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';\nimport { treeEntryToResourceInfo } from '@milaboratories/pl-tree';\nimport { scheduler } from 'node:timers/promises';\nimport type { StreamingAPI_Response } from '../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol';\nimport type * as sdk from '@milaboratories/pl-model-common';\nimport type { PollingOps } from './helpers/polling_ops';\nimport type { RpcError } from '@protobuf-ts/runtime-rpc';\nimport { getResourceInfoFromLogHandle, isLiveLogHandle, newLogHandle } from './helpers/logs_handle';\nimport { WrongResourceTypeError } from './helpers/helpers';\n\nexport type LogsStreamDriverOps = PollingOps & {\n /** Max number of concurrent requests to log streaming backend while calculating computable states */\n nConcurrentGetLogs: number;\n};\n\nexport class LogsStreamDriver implements sdk.LogsDriver {\n /** Holds a map of StreamManager Resource Id to all logs of this stream. */\n private readonly idToLastLines: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to the last log line of this stream. */\n private readonly idToProgressLog: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to log id smart object. */\n private readonly hooks: PollingComputableHooks;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly opts: LogsStreamDriverOps = {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n ) {\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: opts.stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n getLastLogs(res: ResourceInfo | PlTreeEntry, lines: number): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseLastLogs(r.id, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r, lines, callerId);\n ctx.markUnstable(\n 'The logs are from stream, so we consider them unstable. Final values will be got from blobs.',\n );\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateResourceType('getLastLogs', rInfo.type);\n\n let logGetter = this.idToLastLines.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, lines);\n this.idToLastLines.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error != undefined) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseProgressLog(r.id, callerId));\n\n const result = this.getProgressLogNoCtx(ctx.watcher, r, patternToSearch, callerId);\n ctx.markUnstable(\n 'The progress log is from the stream, so we consider it unstable. Final value will be got from blobs.',\n );\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateResourceType('getProgressLog', rInfo.type);\n\n let logGetter = this.idToProgressLog.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, 1, patternToSearch);\n this.idToProgressLog.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<sdk.AnyLogHandle>;\n getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): sdk.AnyLogHandle;\n getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<sdk.AnyLogHandle> | sdk.AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n const result = this.getLogHandleNoCtx(r);\n\n // All logs from streams should be considered unstable,\n // final value will be got from blobs.\n ctx.markUnstable(`live_log:${resourceIdToString(r.id)}`);\n\n return result;\n }\n\n private getLogHandleNoCtx(rInfo: ResourceInfo): sdk.AnyLogHandle {\n validateResourceType('getLogHandle', rInfo.type);\n\n return newLogHandle(true, rInfo);\n }\n\n async lastLines(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n async readText(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n private async tryWithNotFound(\n handle: sdk.AnyLogHandle,\n method: () => Promise<StreamingAPI_Response>,\n ): Promise<sdk.StreamingApiResponse> {\n if (!isLiveLogHandle(handle))\n throw new Error(`Not live log handle was passed to live log driver, handle: ${handle}`);\n\n try {\n const resp = await method();\n return {\n live: true,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n return { shouldUpdateHandle: true };\n }\n\n throw e;\n }\n }\n\n private async releaseLastLogs(rId: ResourceId, callerId: string) {\n const deleted = this.idToLastLines.get(rId)?.release(callerId);\n if (deleted) this.idToLastLines.delete(rId);\n }\n\n private async releaseProgressLog(rId: ResourceId, callerId: string) {\n const deleted = this.idToProgressLog.get(rId)?.release(callerId);\n if (deleted) this.idToProgressLog.delete(rId);\n }\n\n async releaseAll() {}\n\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n this.scheduledOnNextState.push({ resolve, reject });\n }\n\n /** Called from observer */\n private startUpdating(): void {\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n private async mainLoop() {\n while (this.keepRunning) {\n const toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n\n try {\n const logs = this.getAllLogs();\n await asyncPool(\n this.opts.nConcurrentGetLogs,\n logs.map((getter) => async () => await getter.update()),\n );\n\n toNotify.forEach((n) => n.resolve());\n } catch (e: any) {\n console.error(e);\n toNotify.forEach((n) => n.reject(e));\n }\n\n if (!this.keepRunning) break;\n await scheduler.wait(this.opts.pollingInterval);\n }\n\n this.currentLoop = undefined;\n }\n\n private getAllLogs(): Array<LogGetter> {\n return Array.from(this.idToLastLines.entries())\n .concat(Array.from(this.idToProgressLog.entries()))\n .map(([_, getter]) => getter);\n }\n}\n\n/** A job that gets last lines from a StreamWorkdir resource. */\nclass LogGetter {\n private logs: string | undefined;\n private error: any | undefined = undefined;\n\n private readonly change: ChangeSource = new ChangeSource();\n private readonly counter: CallersCounter = new CallersCounter();\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly rInfo: ResourceInfo,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {}\n\n getLog(): {\n log: string | undefined;\n error?: any | undefined;\n } {\n return {\n log: this.logs,\n error: this.error,\n };\n }\n\n attach(w: Watcher, callerId: string) {\n this.change.attachWatcher(w);\n this.counter.inc(callerId);\n }\n\n release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n\n async update() {\n try {\n const resp = await this.clientLogs.lastLines(\n this.rInfo,\n this.lines,\n 0n,\n this.patternToSearch,\n );\n\n const newLogs = new TextDecoder().decode(resp.data);\n if (this.logs != newLogs) this.change.markChanged(`logs for ${resourceIdToString(this.rInfo.id)} updated`);\n this.logs = newLogs;\n this.error = undefined;\n\n return;\n } catch (e: any) {\n e as RpcError;\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.logs = '';\n this.error = e;\n this.change.markChanged();\n return;\n }\n\n this.logger.error(\n `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`,\n );\n throw e;\n }\n }\n}\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nfunction validateResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('StreamWorkdir')) {\n throw new WrongResourceTypeError(\n `${methodName}: wrong resource type: ${rType.name}, `\n + `expected: a resource of type 'StreamWorkdir'.`,\n );\n }\n}\n"],"names":["PollingComputableHooks","Computable","treeEntryToResourceInfo","randomUUID","resourceIdToString","newLogHandle","getResourceInfoFromLogHandle","isLiveLogHandle","asyncPool","scheduler","ChangeSource","CallersCounter","stringifyWithResourceId","WrongResourceTypeError"],"mappings":"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"}
1
+ {"version":3,"file":"logs_stream.cjs","sources":["../../src/drivers/logs_stream.ts"],"sourcesContent":["import type {\n ComputableCtx,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n PollingComputableHooks,\n} from '@milaboratories/computable';\nimport type {\n ResourceId,\n ResourceType } from '@milaboratories/pl-client';\nimport {\n resourceIdToString,\n stringifyWithResourceId,\n} from '@milaboratories/pl-client';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { asyncPool, CallersCounter } from '@milaboratories/ts-helpers';\nimport type { ClientLogs } from '../clients/logs';\nimport { randomUUID } from 'node:crypto';\nimport type { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';\nimport { treeEntryToResourceInfo } from '@milaboratories/pl-tree';\nimport { scheduler } from 'node:timers/promises';\nimport type { StreamingAPI_Response } from '../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol';\nimport type * as sdk from '@milaboratories/pl-model-common';\nimport type { PollingOps } from './helpers/polling_ops';\nimport type { RpcError } from '@protobuf-ts/runtime-rpc';\nimport { getResourceInfoFromLogHandle, isLiveLogHandle, newLogHandle } from './helpers/logs_handle';\nimport { WrongResourceTypeError } from './helpers/helpers';\n\nexport type LogsStreamDriverOps = PollingOps & {\n /** Max number of concurrent requests to log streaming backend while calculating computable states */\n nConcurrentGetLogs: number;\n};\n\nexport class LogsStreamDriver implements sdk.LogsDriver {\n /** Holds a map of StreamManager Resource Id to all logs of this stream. */\n private readonly idToLastLines: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to the last log line of this stream. */\n private readonly idToProgressLog: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to log id smart object. */\n private readonly hooks: PollingComputableHooks;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly opts: LogsStreamDriverOps = {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n ) {\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: opts.stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n getLastLogs(res: ResourceInfo | PlTreeEntry, lines: number): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseLastLogs(r.id, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r, lines, callerId);\n ctx.markUnstable(\n 'The logs are from stream, so we consider them unstable. Final values will be got from blobs.',\n );\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateResourceType('getLastLogs', rInfo.type);\n\n let logGetter = this.idToLastLines.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, lines);\n this.idToLastLines.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error != undefined) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseProgressLog(r.id, callerId));\n\n const result = this.getProgressLogNoCtx(ctx.watcher, r, patternToSearch, callerId);\n ctx.markUnstable(\n 'The progress log is from the stream, so we consider it unstable. Final value will be got from blobs.',\n );\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateResourceType('getProgressLog', rInfo.type);\n\n let logGetter = this.idToProgressLog.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, 1, patternToSearch);\n this.idToProgressLog.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<sdk.AnyLogHandle>;\n getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): sdk.AnyLogHandle;\n getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<sdk.AnyLogHandle> | sdk.AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n const result = this.getLogHandleNoCtx(r);\n\n // All logs from streams should be considered unstable,\n // final value will be got from blobs.\n ctx.markUnstable(`live_log:${resourceIdToString(r.id)}`);\n\n return result;\n }\n\n private getLogHandleNoCtx(rInfo: ResourceInfo): sdk.AnyLogHandle {\n validateResourceType('getLogHandle', rInfo.type);\n\n return newLogHandle(true, rInfo);\n }\n\n async lastLines(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n async readText(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n private async tryWithNotFound(\n handle: sdk.AnyLogHandle,\n method: () => Promise<StreamingAPI_Response>,\n ): Promise<sdk.StreamingApiResponse> {\n if (!isLiveLogHandle(handle))\n throw new Error(`Not live log handle was passed to live log driver, handle: ${handle}`);\n\n try {\n const resp = await method();\n return {\n live: true,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n return { shouldUpdateHandle: true };\n }\n\n throw e;\n }\n }\n\n private async releaseLastLogs(rId: ResourceId, callerId: string) {\n const deleted = this.idToLastLines.get(rId)?.release(callerId);\n if (deleted) this.idToLastLines.delete(rId);\n }\n\n private async releaseProgressLog(rId: ResourceId, callerId: string) {\n const deleted = this.idToProgressLog.get(rId)?.release(callerId);\n if (deleted) this.idToProgressLog.delete(rId);\n }\n\n async releaseAll() {}\n\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n this.scheduledOnNextState.push({ resolve, reject });\n }\n\n /** Called from observer */\n private startUpdating(): void {\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n private async mainLoop() {\n while (this.keepRunning) {\n const toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n\n try {\n const logs = this.getAllLogs();\n await asyncPool(\n this.opts.nConcurrentGetLogs,\n logs.map((getter) => async () => await getter.update()),\n );\n\n toNotify.forEach((n) => n.resolve());\n } catch (e: any) {\n console.error(e);\n toNotify.forEach((n) => n.reject(e));\n }\n\n if (!this.keepRunning) break;\n await scheduler.wait(this.opts.pollingInterval);\n }\n\n this.currentLoop = undefined;\n }\n\n private getAllLogs(): Array<LogGetter> {\n return Array.from(this.idToLastLines.entries())\n .concat(Array.from(this.idToProgressLog.entries()))\n .map(([_, getter]) => getter);\n }\n}\n\n/** A job that gets last lines from a StreamWorkdir resource. */\nclass LogGetter {\n private logs: string | undefined;\n private error: any | undefined = undefined;\n\n private readonly change: ChangeSource = new ChangeSource();\n private readonly counter: CallersCounter = new CallersCounter();\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly rInfo: ResourceInfo,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {}\n\n getLog(): {\n log: string | undefined;\n error?: any | undefined;\n } {\n return {\n log: this.logs,\n error: this.error,\n };\n }\n\n attach(w: Watcher, callerId: string) {\n this.change.attachWatcher(w);\n this.counter.inc(callerId);\n }\n\n release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n\n async update() {\n try {\n const resp = await this.clientLogs.lastLines(\n this.rInfo,\n this.lines,\n 0n,\n this.patternToSearch,\n );\n\n const newLogs = new TextDecoder().decode(resp.data);\n if (this.logs != newLogs) this.change.markChanged(`logs for ${resourceIdToString(this.rInfo.id)} updated`);\n this.logs = newLogs;\n this.error = undefined;\n\n return;\n } catch (e: any) {\n e as RpcError;\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.logs = '';\n this.error = e;\n this.change.markChanged();\n return;\n }\n\n this.logger.error(\n `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${JSON.stringify(e)}`,\n );\n throw e;\n }\n }\n}\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nfunction validateResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('StreamWorkdir')) {\n throw new WrongResourceTypeError(\n `${methodName}: wrong resource type: ${rType.name}, `\n + `expected: a resource of type 'StreamWorkdir'.`,\n );\n }\n}\n"],"names":["PollingComputableHooks","Computable","treeEntryToResourceInfo","randomUUID","resourceIdToString","newLogHandle","getResourceInfoFromLogHandle","isLiveLogHandle","asyncPool","scheduler","ChangeSource","CallersCounter","stringifyWithResourceId","WrongResourceTypeError"],"mappings":"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"}
@@ -220,7 +220,7 @@ class LogGetter {
220
220
  this.change.markChanged();
221
221
  return;
222
222
  }
223
- this.logger.error(`Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`);
223
+ this.logger.error(`Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${JSON.stringify(e)}`);
224
224
  throw e;
225
225
  }
226
226
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logs_stream.js","sources":["../../src/drivers/logs_stream.ts"],"sourcesContent":["import type {\n ComputableCtx,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n PollingComputableHooks,\n} from '@milaboratories/computable';\nimport type {\n ResourceId,\n ResourceType } from '@milaboratories/pl-client';\nimport {\n resourceIdToString,\n stringifyWithResourceId,\n} from '@milaboratories/pl-client';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { asyncPool, CallersCounter } from '@milaboratories/ts-helpers';\nimport type { ClientLogs } from '../clients/logs';\nimport { randomUUID } from 'node:crypto';\nimport type { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';\nimport { treeEntryToResourceInfo } from '@milaboratories/pl-tree';\nimport { scheduler } from 'node:timers/promises';\nimport type { StreamingAPI_Response } from '../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol';\nimport type * as sdk from '@milaboratories/pl-model-common';\nimport type { PollingOps } from './helpers/polling_ops';\nimport type { RpcError } from '@protobuf-ts/runtime-rpc';\nimport { getResourceInfoFromLogHandle, isLiveLogHandle, newLogHandle } from './helpers/logs_handle';\nimport { WrongResourceTypeError } from './helpers/helpers';\n\nexport type LogsStreamDriverOps = PollingOps & {\n /** Max number of concurrent requests to log streaming backend while calculating computable states */\n nConcurrentGetLogs: number;\n};\n\nexport class LogsStreamDriver implements sdk.LogsDriver {\n /** Holds a map of StreamManager Resource Id to all logs of this stream. */\n private readonly idToLastLines: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to the last log line of this stream. */\n private readonly idToProgressLog: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to log id smart object. */\n private readonly hooks: PollingComputableHooks;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly opts: LogsStreamDriverOps = {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n ) {\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: opts.stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n getLastLogs(res: ResourceInfo | PlTreeEntry, lines: number): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseLastLogs(r.id, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r, lines, callerId);\n ctx.markUnstable(\n 'The logs are from stream, so we consider them unstable. Final values will be got from blobs.',\n );\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateResourceType('getLastLogs', rInfo.type);\n\n let logGetter = this.idToLastLines.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, lines);\n this.idToLastLines.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error != undefined) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseProgressLog(r.id, callerId));\n\n const result = this.getProgressLogNoCtx(ctx.watcher, r, patternToSearch, callerId);\n ctx.markUnstable(\n 'The progress log is from the stream, so we consider it unstable. Final value will be got from blobs.',\n );\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateResourceType('getProgressLog', rInfo.type);\n\n let logGetter = this.idToProgressLog.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, 1, patternToSearch);\n this.idToProgressLog.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<sdk.AnyLogHandle>;\n getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): sdk.AnyLogHandle;\n getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<sdk.AnyLogHandle> | sdk.AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n const result = this.getLogHandleNoCtx(r);\n\n // All logs from streams should be considered unstable,\n // final value will be got from blobs.\n ctx.markUnstable(`live_log:${resourceIdToString(r.id)}`);\n\n return result;\n }\n\n private getLogHandleNoCtx(rInfo: ResourceInfo): sdk.AnyLogHandle {\n validateResourceType('getLogHandle', rInfo.type);\n\n return newLogHandle(true, rInfo);\n }\n\n async lastLines(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n async readText(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n private async tryWithNotFound(\n handle: sdk.AnyLogHandle,\n method: () => Promise<StreamingAPI_Response>,\n ): Promise<sdk.StreamingApiResponse> {\n if (!isLiveLogHandle(handle))\n throw new Error(`Not live log handle was passed to live log driver, handle: ${handle}`);\n\n try {\n const resp = await method();\n return {\n live: true,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n return { shouldUpdateHandle: true };\n }\n\n throw e;\n }\n }\n\n private async releaseLastLogs(rId: ResourceId, callerId: string) {\n const deleted = this.idToLastLines.get(rId)?.release(callerId);\n if (deleted) this.idToLastLines.delete(rId);\n }\n\n private async releaseProgressLog(rId: ResourceId, callerId: string) {\n const deleted = this.idToProgressLog.get(rId)?.release(callerId);\n if (deleted) this.idToProgressLog.delete(rId);\n }\n\n async releaseAll() {}\n\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n this.scheduledOnNextState.push({ resolve, reject });\n }\n\n /** Called from observer */\n private startUpdating(): void {\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n private async mainLoop() {\n while (this.keepRunning) {\n const toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n\n try {\n const logs = this.getAllLogs();\n await asyncPool(\n this.opts.nConcurrentGetLogs,\n logs.map((getter) => async () => await getter.update()),\n );\n\n toNotify.forEach((n) => n.resolve());\n } catch (e: any) {\n console.error(e);\n toNotify.forEach((n) => n.reject(e));\n }\n\n if (!this.keepRunning) break;\n await scheduler.wait(this.opts.pollingInterval);\n }\n\n this.currentLoop = undefined;\n }\n\n private getAllLogs(): Array<LogGetter> {\n return Array.from(this.idToLastLines.entries())\n .concat(Array.from(this.idToProgressLog.entries()))\n .map(([_, getter]) => getter);\n }\n}\n\n/** A job that gets last lines from a StreamWorkdir resource. */\nclass LogGetter {\n private logs: string | undefined;\n private error: any | undefined = undefined;\n\n private readonly change: ChangeSource = new ChangeSource();\n private readonly counter: CallersCounter = new CallersCounter();\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly rInfo: ResourceInfo,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {}\n\n getLog(): {\n log: string | undefined;\n error?: any | undefined;\n } {\n return {\n log: this.logs,\n error: this.error,\n };\n }\n\n attach(w: Watcher, callerId: string) {\n this.change.attachWatcher(w);\n this.counter.inc(callerId);\n }\n\n release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n\n async update() {\n try {\n const resp = await this.clientLogs.lastLines(\n this.rInfo,\n this.lines,\n 0n,\n this.patternToSearch,\n );\n\n const newLogs = new TextDecoder().decode(resp.data);\n if (this.logs != newLogs) this.change.markChanged(`logs for ${resourceIdToString(this.rInfo.id)} updated`);\n this.logs = newLogs;\n this.error = undefined;\n\n return;\n } catch (e: any) {\n e as RpcError;\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.logs = '';\n this.error = e;\n this.change.markChanged();\n return;\n }\n\n this.logger.error(\n `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`,\n );\n throw e;\n }\n }\n}\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nfunction validateResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('StreamWorkdir')) {\n throw new WrongResourceTypeError(\n `${methodName}: wrong resource type: ${rType.name}, `\n + `expected: a resource of type 'StreamWorkdir'.`,\n );\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logs_stream.js","sources":["../../src/drivers/logs_stream.ts"],"sourcesContent":["import type {\n ComputableCtx,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n PollingComputableHooks,\n} from '@milaboratories/computable';\nimport type {\n ResourceId,\n ResourceType } from '@milaboratories/pl-client';\nimport {\n resourceIdToString,\n stringifyWithResourceId,\n} from '@milaboratories/pl-client';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { asyncPool, CallersCounter } from '@milaboratories/ts-helpers';\nimport type { ClientLogs } from '../clients/logs';\nimport { randomUUID } from 'node:crypto';\nimport type { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';\nimport { treeEntryToResourceInfo } from '@milaboratories/pl-tree';\nimport { scheduler } from 'node:timers/promises';\nimport type { StreamingAPI_Response } from '../proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol';\nimport type * as sdk from '@milaboratories/pl-model-common';\nimport type { PollingOps } from './helpers/polling_ops';\nimport type { RpcError } from '@protobuf-ts/runtime-rpc';\nimport { getResourceInfoFromLogHandle, isLiveLogHandle, newLogHandle } from './helpers/logs_handle';\nimport { WrongResourceTypeError } from './helpers/helpers';\n\nexport type LogsStreamDriverOps = PollingOps & {\n /** Max number of concurrent requests to log streaming backend while calculating computable states */\n nConcurrentGetLogs: number;\n};\n\nexport class LogsStreamDriver implements sdk.LogsDriver {\n /** Holds a map of StreamManager Resource Id to all logs of this stream. */\n private readonly idToLastLines: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to the last log line of this stream. */\n private readonly idToProgressLog: Map<ResourceId, LogGetter> = new Map();\n\n /** Holds a map of StreamManager Resource Id to log id smart object. */\n private readonly hooks: PollingComputableHooks;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly opts: LogsStreamDriverOps = {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n ) {\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: opts.stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n getLastLogs(res: ResourceInfo | PlTreeEntry, lines: number): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseLastLogs(r.id, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r, lines, callerId);\n ctx.markUnstable(\n 'The logs are from stream, so we consider them unstable. Final values will be got from blobs.',\n );\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateResourceType('getLastLogs', rInfo.type);\n\n let logGetter = this.idToLastLines.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, lines);\n this.idToLastLines.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error != undefined) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.attacheHooks(this.hooks);\n ctx.addOnDestroy(() => this.releaseProgressLog(r.id, callerId));\n\n const result = this.getProgressLogNoCtx(ctx.watcher, r, patternToSearch, callerId);\n ctx.markUnstable(\n 'The progress log is from the stream, so we consider it unstable. Final value will be got from blobs.',\n );\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceInfo,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateResourceType('getProgressLog', rInfo.type);\n\n let logGetter = this.idToProgressLog.get(rInfo.id);\n\n if (logGetter == undefined) {\n const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, 1, patternToSearch);\n this.idToProgressLog.set(rInfo.id, newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n logGetter.attach(w, callerId);\n const result = logGetter.getLog();\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<sdk.AnyLogHandle>;\n getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): sdk.AnyLogHandle;\n getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<sdk.AnyLogHandle> | sdk.AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n const result = this.getLogHandleNoCtx(r);\n\n // All logs from streams should be considered unstable,\n // final value will be got from blobs.\n ctx.markUnstable(`live_log:${resourceIdToString(r.id)}`);\n\n return result;\n }\n\n private getLogHandleNoCtx(rInfo: ResourceInfo): sdk.AnyLogHandle {\n validateResourceType('getLogHandle', rInfo.type);\n\n return newLogHandle(true, rInfo);\n }\n\n async lastLines(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n async readText(\n handle: sdk.AnyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string | undefined,\n ) {\n return await this.tryWithNotFound(handle, () =>\n this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n ),\n );\n }\n\n private async tryWithNotFound(\n handle: sdk.AnyLogHandle,\n method: () => Promise<StreamingAPI_Response>,\n ): Promise<sdk.StreamingApiResponse> {\n if (!isLiveLogHandle(handle))\n throw new Error(`Not live log handle was passed to live log driver, handle: ${handle}`);\n\n try {\n const resp = await method();\n return {\n live: true,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n return { shouldUpdateHandle: true };\n }\n\n throw e;\n }\n }\n\n private async releaseLastLogs(rId: ResourceId, callerId: string) {\n const deleted = this.idToLastLines.get(rId)?.release(callerId);\n if (deleted) this.idToLastLines.delete(rId);\n }\n\n private async releaseProgressLog(rId: ResourceId, callerId: string) {\n const deleted = this.idToProgressLog.get(rId)?.release(callerId);\n if (deleted) this.idToProgressLog.delete(rId);\n }\n\n async releaseAll() {}\n\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n this.scheduledOnNextState.push({ resolve, reject });\n }\n\n /** Called from observer */\n private startUpdating(): void {\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n private async mainLoop() {\n while (this.keepRunning) {\n const toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n\n try {\n const logs = this.getAllLogs();\n await asyncPool(\n this.opts.nConcurrentGetLogs,\n logs.map((getter) => async () => await getter.update()),\n );\n\n toNotify.forEach((n) => n.resolve());\n } catch (e: any) {\n console.error(e);\n toNotify.forEach((n) => n.reject(e));\n }\n\n if (!this.keepRunning) break;\n await scheduler.wait(this.opts.pollingInterval);\n }\n\n this.currentLoop = undefined;\n }\n\n private getAllLogs(): Array<LogGetter> {\n return Array.from(this.idToLastLines.entries())\n .concat(Array.from(this.idToProgressLog.entries()))\n .map(([_, getter]) => getter);\n }\n}\n\n/** A job that gets last lines from a StreamWorkdir resource. */\nclass LogGetter {\n private logs: string | undefined;\n private error: any | undefined = undefined;\n\n private readonly change: ChangeSource = new ChangeSource();\n private readonly counter: CallersCounter = new CallersCounter();\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientLogs: ClientLogs,\n private readonly rInfo: ResourceInfo,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {}\n\n getLog(): {\n log: string | undefined;\n error?: any | undefined;\n } {\n return {\n log: this.logs,\n error: this.error,\n };\n }\n\n attach(w: Watcher, callerId: string) {\n this.change.attachWatcher(w);\n this.counter.inc(callerId);\n }\n\n release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n\n async update() {\n try {\n const resp = await this.clientLogs.lastLines(\n this.rInfo,\n this.lines,\n 0n,\n this.patternToSearch,\n );\n\n const newLogs = new TextDecoder().decode(resp.data);\n if (this.logs != newLogs) this.change.markChanged(`logs for ${resourceIdToString(this.rInfo.id)} updated`);\n this.logs = newLogs;\n this.error = undefined;\n\n return;\n } catch (e: any) {\n e as RpcError;\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.logs = '';\n this.error = e;\n this.change.markChanged();\n return;\n }\n\n this.logger.error(\n `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${JSON.stringify(e)}`,\n );\n throw e;\n }\n }\n}\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nfunction validateResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('StreamWorkdir')) {\n throw new WrongResourceTypeError(\n `${methodName}: wrong resource type: ${rType.name}, `\n + `expected: a resource of type 'StreamWorkdir'.`,\n );\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-drivers",
3
- "version": "1.11.24",
3
+ "version": "1.11.26",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -33,11 +33,11 @@
33
33
  "upath": "^2.0.1",
34
34
  "zod": "~3.23.8",
35
35
  "@milaboratories/computable": "2.7.4",
36
- "@milaboratories/pl-model-common": "1.21.9",
36
+ "@milaboratories/pl-tree": "1.8.20",
37
+ "@milaboratories/pl-client": "2.16.13",
37
38
  "@milaboratories/ts-helpers": "1.5.4",
38
- "@milaboratories/pl-client": "2.16.11",
39
39
  "@milaboratories/helpers": "1.12.0",
40
- "@milaboratories/pl-tree": "1.8.18"
40
+ "@milaboratories/pl-model-common": "1.21.10"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/decompress": "^4.2.7",
@@ -48,9 +48,9 @@
48
48
  "eslint": "^9.25.1",
49
49
  "typescript": "~5.6.3",
50
50
  "vitest": "^4.0.7",
51
- "@milaboratories/build-configs": "1.0.8",
52
51
  "@milaboratories/eslint-config": "1.0.5",
53
- "@milaboratories/ts-builder": "1.0.5",
52
+ "@milaboratories/build-configs": "1.0.9",
53
+ "@milaboratories/ts-builder": "1.0.6",
54
54
  "@milaboratories/ts-configs": "1.0.6",
55
55
  "@milaboratories/test-helpers": "1.1.1"
56
56
  },
@@ -64,8 +64,7 @@ export class ClientLs {
64
64
  isDir: item.isDir,
65
65
  fullName: item.fullName,
66
66
  directory: item.directory,
67
- // TODO: check we get value in a correct way here
68
- lastModified: { seconds: BigInt(item.lastModified), nanos: 0 },
67
+ lastModified: parseTimestamp(item.lastModified),
69
68
  version: item.version,
70
69
  }));
71
70
 
@@ -76,3 +75,21 @@ export class ClientLs {
76
75
  }
77
76
  }
78
77
  }
78
+
79
+ function parseTimestamp(timestamp: string): { seconds: bigint; nanos: number } {
80
+ // Parse ISO 8601 format: 2025-08-08T11:12:44.145635532Z
81
+ const date = new Date(timestamp);
82
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
83
+
84
+ // Extract fractional seconds from the original string
85
+ const fractionalMatch = timestamp.match(/\.(\d+)Z?$/);
86
+ let nanos = 0;
87
+ if (fractionalMatch) {
88
+ const fractionalPart = fractionalMatch[1];
89
+ // Pad or truncate to 9 digits (nanoseconds)
90
+ const padded = fractionalPart.padEnd(9, '0').slice(0, 9);
91
+ nanos = parseInt(padded, 10);
92
+ }
93
+
94
+ return { seconds, nanos };
95
+ }
@@ -64,7 +64,7 @@ export class DownloadBlobTask {
64
64
  this.logger.error(
65
65
  `blob ${stringifyWithResourceId(this.rInfo)} download failed, `
66
66
  + `state: ${JSON.stringify(this.state)}, `
67
- + `error: ${e}`,
67
+ + `error: ${JSON.stringify(e)}`,
68
68
  );
69
69
  if (nonRecoverableError(e)) {
70
70
  this.setError(e);
@@ -41,7 +41,7 @@ test('should download a tar archive and extracts its content and then deleted',
41
41
 
42
42
  c.resetState();
43
43
  });
44
- }, 15000);
44
+ }, 45000);
45
45
 
46
46
  test('should show a error when 404 status code', async () => {
47
47
  try {
@@ -375,7 +375,7 @@ class LogGetter {
375
375
  }
376
376
 
377
377
  this.logger.error(
378
- `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`,
378
+ `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${JSON.stringify(e)}`,
379
379
  );
380
380
  throw e;
381
381
  }