@milaboratories/pl-drivers 1.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +18 -0
  2. package/dist/clients/download.d.ts +30 -0
  3. package/dist/clients/download.d.ts.map +1 -0
  4. package/dist/clients/helpers.d.ts +14 -0
  5. package/dist/clients/helpers.d.ts.map +1 -0
  6. package/dist/clients/logs.d.ts +26 -0
  7. package/dist/clients/logs.d.ts.map +1 -0
  8. package/dist/clients/ls_api.d.ts +13 -0
  9. package/dist/clients/ls_api.d.ts.map +1 -0
  10. package/dist/clients/progress.d.ts +25 -0
  11. package/dist/clients/progress.d.ts.map +1 -0
  12. package/dist/clients/upload.d.ts +38 -0
  13. package/dist/clients/upload.d.ts.map +1 -0
  14. package/dist/drivers/download_and_logs_blob.d.ts +106 -0
  15. package/dist/drivers/download_and_logs_blob.d.ts.map +1 -0
  16. package/dist/drivers/download_url.d.ts +70 -0
  17. package/dist/drivers/download_url.d.ts.map +1 -0
  18. package/dist/drivers/helpers/files_cache.d.ts +28 -0
  19. package/dist/drivers/helpers/files_cache.d.ts.map +1 -0
  20. package/dist/drivers/helpers/helpers.d.ts +34 -0
  21. package/dist/drivers/helpers/helpers.d.ts.map +1 -0
  22. package/dist/drivers/helpers/ls_list_entry.d.ts +49 -0
  23. package/dist/drivers/helpers/ls_list_entry.d.ts.map +1 -0
  24. package/dist/drivers/helpers/ls_storage_entry.d.ts +25 -0
  25. package/dist/drivers/helpers/ls_storage_entry.d.ts.map +1 -0
  26. package/dist/drivers/helpers/polling_ops.d.ts +8 -0
  27. package/dist/drivers/helpers/polling_ops.d.ts.map +1 -0
  28. package/dist/drivers/helpers/test_helpers.d.ts +2 -0
  29. package/dist/drivers/helpers/test_helpers.d.ts.map +1 -0
  30. package/dist/drivers/logs.d.ts +29 -0
  31. package/dist/drivers/logs.d.ts.map +1 -0
  32. package/dist/drivers/logs_stream.d.ts +50 -0
  33. package/dist/drivers/logs_stream.d.ts.map +1 -0
  34. package/dist/drivers/ls.d.ts +30 -0
  35. package/dist/drivers/ls.d.ts.map +1 -0
  36. package/dist/drivers/upload.d.ts +87 -0
  37. package/dist/drivers/upload.d.ts.map +1 -0
  38. package/dist/helpers/download.d.ts +15 -0
  39. package/dist/helpers/download.d.ts.map +1 -0
  40. package/dist/index.cjs +2 -0
  41. package/dist/index.cjs.map +1 -0
  42. package/dist/index.d.ts +15 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +4627 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts +36 -0
  47. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts.map +1 -0
  48. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts +103 -0
  49. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts.map +1 -0
  50. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts +42 -0
  51. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts.map +1 -0
  52. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts +165 -0
  53. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts.map +1 -0
  54. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts +44 -0
  55. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts.map +1 -0
  56. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts +171 -0
  57. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts.map +1 -0
  58. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts +122 -0
  59. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts.map +1 -0
  60. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts +315 -0
  61. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts.map +1 -0
  62. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts +98 -0
  63. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts.map +1 -0
  64. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts +337 -0
  65. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts.map +1 -0
  66. package/dist/proto/google/api/http.d.ts +451 -0
  67. package/dist/proto/google/api/http.d.ts.map +1 -0
  68. package/dist/proto/google/protobuf/descriptor.d.ts +1646 -0
  69. package/dist/proto/google/protobuf/descriptor.d.ts.map +1 -0
  70. package/dist/proto/google/protobuf/duration.d.ts +106 -0
  71. package/dist/proto/google/protobuf/duration.d.ts.map +1 -0
  72. package/dist/proto/google/protobuf/timestamp.d.ts +151 -0
  73. package/dist/proto/google/protobuf/timestamp.d.ts.map +1 -0
  74. package/package.json +47 -0
  75. package/src/clients/download.test.ts +45 -0
  76. package/src/clients/download.ts +106 -0
  77. package/src/clients/helpers.ts +84 -0
  78. package/src/clients/logs.ts +68 -0
  79. package/src/clients/ls_api.ts +34 -0
  80. package/src/clients/progress.ts +86 -0
  81. package/src/clients/upload.test.ts +30 -0
  82. package/src/clients/upload.ts +199 -0
  83. package/src/drivers/download_and_logs_blob.ts +801 -0
  84. package/src/drivers/download_blob.test.ts +223 -0
  85. package/src/drivers/download_url.test.ts +90 -0
  86. package/src/drivers/download_url.ts +314 -0
  87. package/src/drivers/helpers/files_cache.test.ts +79 -0
  88. package/src/drivers/helpers/files_cache.ts +74 -0
  89. package/src/drivers/helpers/helpers.ts +136 -0
  90. package/src/drivers/helpers/ls_list_entry.test.ts +57 -0
  91. package/src/drivers/helpers/ls_list_entry.ts +152 -0
  92. package/src/drivers/helpers/ls_storage_entry.ts +135 -0
  93. package/src/drivers/helpers/polling_ops.ts +7 -0
  94. package/src/drivers/helpers/test_helpers.ts +5 -0
  95. package/src/drivers/logs.test.ts +337 -0
  96. package/src/drivers/logs.ts +214 -0
  97. package/src/drivers/logs_stream.ts +399 -0
  98. package/src/drivers/ls.test.ts +90 -0
  99. package/src/drivers/ls.ts +147 -0
  100. package/src/drivers/upload.test.ts +454 -0
  101. package/src/drivers/upload.ts +499 -0
  102. package/src/helpers/download.ts +43 -0
  103. package/src/index.ts +15 -0
  104. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.ts +60 -0
  105. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.ts +442 -0
  106. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.ts +63 -0
  107. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.ts +503 -0
  108. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.ts +84 -0
  109. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.ts +697 -0
  110. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.ts +212 -0
  111. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.ts +1036 -0
  112. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.ts +170 -0
  113. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.ts +1201 -0
  114. package/src/proto/google/api/http.ts +838 -0
  115. package/src/proto/google/protobuf/descriptor.ts +5173 -0
  116. package/src/proto/google/protobuf/duration.ts +272 -0
  117. package/src/proto/google/protobuf/timestamp.ts +354 -0
@@ -0,0 +1,74 @@
1
+ import { CallersCounter, mapEntries, mapGet } from '@milaboratories/ts-helpers';
2
+
3
+ type PathLike = string;
4
+
5
+ export interface CachedFile {
6
+ sizeBytes: number;
7
+ path: PathLike;
8
+ counter: CallersCounter;
9
+ }
10
+
11
+ /** Holds counters of how many callers need the file.
12
+ * If some counters become zero and a cache size exceeds a soft limit,
13
+ * remove not needed blobs one by one.
14
+ * If all the files are needed, do nothing. */
15
+ export class FilesCache<T extends CachedFile> {
16
+ private cache: Map<PathLike, T> = new Map();
17
+ private totalSizeBytes: number = 0;
18
+
19
+ constructor(private readonly softSizeBytes: number) {}
20
+
21
+ existsFile(path: PathLike): boolean {
22
+ return this.cache.get(path) != undefined;
23
+ }
24
+
25
+ getFile(path: PathLike, callerId: string): T | undefined {
26
+ const file = this.cache.get(path);
27
+ if (file != undefined) {
28
+ file.counter.inc(callerId);
29
+ }
30
+
31
+ return file;
32
+ }
33
+
34
+ /** Decrements a counter in a cache and if we exceeds
35
+ * a soft limit, removes files with zero counters. */
36
+ removeFile(path: PathLike, callerId: string): T[] {
37
+ mapGet(this.cache, path).counter.dec(callerId);
38
+ return this.toDelete();
39
+ }
40
+
41
+ /** Returns what results should be deleted to comply with the soft limit. */
42
+ toDelete(): T[] {
43
+ if (this.totalSizeBytes <= this.softSizeBytes) return [];
44
+
45
+ const result: T[] = [];
46
+ let freedBytes = 0;
47
+
48
+ mapEntries(this.cache)
49
+ .filter(([_, file]: [string, T]) => file.counter.isZero())
50
+ .forEach(([path, _]) => {
51
+ if (this.totalSizeBytes - freedBytes <= this.softSizeBytes) return;
52
+ const file = mapGet(this.cache, path);
53
+ freedBytes += file.sizeBytes;
54
+ result.push(file);
55
+ });
56
+
57
+ return result;
58
+ }
59
+
60
+ addCache(file: T, callerId: string) {
61
+ const created = this.cache.get(file.path) == undefined;
62
+ this.cache.set(file.path, file);
63
+ file.counter.inc(callerId);
64
+
65
+ if (file.sizeBytes < 0) throw new Error(`empty sizeBytes: ${file}`);
66
+
67
+ if (created) this.totalSizeBytes += file.sizeBytes;
68
+ }
69
+
70
+ removeCache(file: T) {
71
+ this.cache.delete(file.path);
72
+ this.totalSizeBytes -= file.sizeBytes;
73
+ }
74
+ }
@@ -0,0 +1,136 @@
1
+ import type { RpcOptions } from '@protobuf-ts/runtime-rpc';
2
+ import { ClientLogs } from '../../clients/logs';
3
+ import {
4
+ PlClient,
5
+ ResourceId,
6
+ BasicResourceData,
7
+ isNullResourceId,
8
+ valErr,
9
+ getField
10
+ } from '@milaboratories/pl-client';
11
+ import { scheduler } from 'node:timers/promises';
12
+ import { ResourceInfo } from '@milaboratories/pl-tree';
13
+
14
+ // TODO: remove this when we switch to refreshState.
15
+
16
+ /** It's an Updater but for tasks that happens in a while loop with sleeping between. */
17
+ export class LongUpdater {
18
+ private updater: Updater;
19
+
20
+ constructor(
21
+ private readonly onUpdate: () => Promise<boolean>,
22
+ private readonly sleepMs: number
23
+ ) {
24
+ this.updater = new Updater(async () => {
25
+ while (true) {
26
+ const done = await this.onUpdate();
27
+ if (done) return;
28
+ await scheduler.wait(this.sleepMs);
29
+ }
30
+ });
31
+ }
32
+
33
+ schedule = () => this.updater.schedule();
34
+ }
35
+
36
+ /** Updater incorporates a pattern when someone wants to run a callback
37
+ * that updates something only when it's not already running. */
38
+ export class Updater {
39
+ private updating: Promise<void> | undefined;
40
+
41
+ constructor(private readonly onUpdate: () => Promise<void>) {}
42
+
43
+ schedule() {
44
+ if (this.updating == undefined) {
45
+ this.updating = (async () => {
46
+ try {
47
+ await this.onUpdate();
48
+ } catch (e) {
49
+ console.log(`error while updating in Updater: ${e}`);
50
+ } finally {
51
+ this.updating = undefined;
52
+ }
53
+ })();
54
+ }
55
+ }
56
+ }
57
+
58
+ // TODO: remove all the code below to the computable that calculates Mixcr logs.
59
+
60
+ export async function getStream(
61
+ client: PlClient,
62
+ streamManagerId: ResourceId
63
+ ): Promise<BasicResourceData | undefined> {
64
+ return client.withReadTx('LogsDriverGetStream', async (tx) => {
65
+ const sm = await tx.getResourceData(streamManagerId, true);
66
+ const stream = await valErr(tx, getField(sm, 'stream'));
67
+ if (stream.error != '') {
68
+ throw new Error(`while getting stream: ${stream.error}`);
69
+ }
70
+ if (isNullResourceId(stream.valueId)) return undefined;
71
+
72
+ return await tx.getResourceData(stream.valueId, false);
73
+ });
74
+ }
75
+
76
+ export type MixcrProgressResponse =
77
+ | { found: false }
78
+ | ({ found: true } & MixcrProgressLine);
79
+
80
+ export type MixcrProgressLine = {
81
+ stage: string; // Building pre-clones from tag groups
82
+ progress: string; // 35.3%
83
+ eta: string; // ETA: 00:00:07
84
+ };
85
+
86
+ /** Is set by a template code.
87
+ * Mixcr adds this prefix to every log line that contains a progress. */
88
+ const mixcrProgressPrefix = '8C7#F1328%9E089B3D22';
89
+ const mixcrProgressRegex =
90
+ /(?<stage>.*):\s*(?<progress>[\d.]+%)\s.*(?<eta>ETA:.*)/g;
91
+
92
+ export function lineToProgress(line: string): MixcrProgressLine | undefined {
93
+ const noPrefix = line.replace(mixcrProgressPrefix, '');
94
+ const parsed = noPrefix.match(mixcrProgressRegex);
95
+
96
+ if (parsed == null || parsed.length != 4) {
97
+ return undefined;
98
+ }
99
+
100
+ const [_, stage, progress, eta] = parsed;
101
+
102
+ return {
103
+ stage, // For example, 'Building pre-clones from tag groups'
104
+ progress, // 35.3%
105
+ eta // ETA: 00:00:07
106
+ };
107
+ }
108
+
109
+ export async function mixcrProgressFromLogs(
110
+ rInfo: ResourceInfo,
111
+ client: ClientLogs,
112
+ options?: RpcOptions
113
+ ): Promise<MixcrProgressResponse> {
114
+ const lastLines = await client.lastLines(
115
+ rInfo,
116
+ 1,
117
+ 0n,
118
+ mixcrProgressPrefix,
119
+ options
120
+ );
121
+ if (lastLines.data == null || lastLines.data.length == 0) {
122
+ return { found: false };
123
+ }
124
+
125
+ const line = lastLines.data.toString().split(/\r?\n/)[0];
126
+ if (line == undefined) {
127
+ return { found: false };
128
+ }
129
+
130
+ const progress = lineToProgress(line);
131
+ if (progress === undefined) {
132
+ return { found: false };
133
+ }
134
+
135
+ return { found: true, ...progress };
136
+ }
@@ -0,0 +1,57 @@
1
+ import {
2
+ ConsoleLoggerAdapter,
3
+ HmacSha256Signer
4
+ } from '@milaboratories/ts-helpers';
5
+ import { fromFileHandle, toFileHandle, toListItem } from './ls_list_entry';
6
+ import type { Dirent, Stats } from 'node:fs';
7
+
8
+ test('toFileHandle should ok when encode data for UploadBlob', () => {
9
+ const signer = new HmacSha256Signer('abc');
10
+
11
+ const handle = toFileHandle({
12
+ storageName: 'library',
13
+ signer,
14
+ remote: false,
15
+ item: {
16
+ directory: 'C:\\programFiles\\',
17
+ name: 'file.txt',
18
+ size: 20n,
19
+ isDir: false,
20
+ fullName: 'C:\\programFiles\\file.txt',
21
+ lastModified: { seconds: 150n, nanos: 260 }
22
+ }
23
+ });
24
+ const got = fromFileHandle(handle);
25
+
26
+ expect(got.modificationTime).toEqual('150');
27
+ expect(got.localPath).toEqual('C:\\programFiles\\file.txt');
28
+ expect(got.pathSignature).not.toHaveLength(0);
29
+ expect(got.sizeBytes).toEqual('20');
30
+ });
31
+
32
+ test('toListItem should ok', () => {
33
+ const got = toListItem(new ConsoleLoggerAdapter(), {
34
+ fullName: 'C:\\programFiles\\file.txt',
35
+ dirent: {
36
+ isFile: () => true,
37
+ isDirectory: () => false,
38
+ name: 'file.txt'
39
+ } as Dirent,
40
+ directory: 'C:\\programFiles\\',
41
+ stat: {
42
+ mtimeMs: 150000,
43
+ size: 20
44
+ } as Stats
45
+ });
46
+
47
+ expect(got).toMatchObject({
48
+ isDir: false,
49
+ name: 'file.txt',
50
+ fullName: 'C:\\programFiles\\file.txt',
51
+ lastModified: {
52
+ seconds: 150n,
53
+ nanos: 0
54
+ },
55
+ size: 20n
56
+ });
57
+ });
@@ -0,0 +1,152 @@
1
+ import { MiLogger, notEmpty, Signer } from '@milaboratories/ts-helpers';
2
+ import * as sdk from '@milaboratories/pl-model-common';
3
+ import { Timestamp } from '../../proto/google/protobuf/timestamp';
4
+ import { Dirent, Stats } from 'node:fs';
5
+
6
+ /** A duck-typing interface for grpc results. */
7
+ export interface ListResponse {
8
+ items: ListItem[];
9
+ delimiter: string;
10
+ }
11
+
12
+ export interface ListItem {
13
+ isDir: boolean;
14
+ name: string;
15
+ fullName: string;
16
+ lastModified?: Timestamp;
17
+ size: bigint;
18
+ directory: string;
19
+ }
20
+
21
+ /** */
22
+ export function toLsEntries(info: {
23
+ storageName: string;
24
+ list: ListResponse;
25
+ signer: Signer;
26
+ remote: boolean;
27
+ }): sdk.ListFilesResult {
28
+ const parent =
29
+ info.list.items.length > 0 ? info.list.items[0]?.directory : undefined;
30
+
31
+ return {
32
+ parent: parent,
33
+ entries: info.list.items.map((item) => toLsEntry(item, info))
34
+ };
35
+ }
36
+
37
+ function toLsEntry(
38
+ item: ListItem,
39
+ info: {
40
+ storageName: string;
41
+ list: ListResponse;
42
+ signer: Signer;
43
+ remote: boolean;
44
+ }
45
+ ): sdk.LsEntry {
46
+ if (item.isDir)
47
+ return {
48
+ type: 'dir',
49
+ name: item.name,
50
+ fullPath: item.fullName
51
+ };
52
+
53
+ return {
54
+ type: 'file',
55
+ name: item.name,
56
+ fullPath: item.fullName,
57
+ handle: toFileHandle({ item: item, ...info })
58
+ };
59
+ }
60
+
61
+ export function toFileHandle(info: {
62
+ storageName: string;
63
+ item: ListItem;
64
+ signer: Signer;
65
+ remote: boolean;
66
+ }): sdk.ImportFileHandle {
67
+ if (info.remote) {
68
+ return createIndexHandle(info);
69
+ }
70
+
71
+ return createUploadHandle(
72
+ info.item.fullName,
73
+ info.signer,
74
+ info.item.size,
75
+ notEmpty(info.item.lastModified).seconds
76
+ );
77
+ }
78
+
79
+ export type UploadHandleData = {
80
+ /** Local file path, to take data for upload */
81
+ localPath: string;
82
+ /** Path signature, to check this data was generated by us */
83
+ pathSignature: string;
84
+ /** File size in bytes */
85
+ sizeBytes: string;
86
+ /** Modification time unix timestamp in seconds */
87
+ modificationTime: string;
88
+ };
89
+
90
+ function createIndexHandle(info: {
91
+ storageName: string;
92
+ item: ListItem;
93
+ }): sdk.ImportFileHandleIndex {
94
+ const data = encodeURIComponent(
95
+ JSON.stringify({
96
+ storageId: info.storageName,
97
+ path: info.item.fullName
98
+ })
99
+ );
100
+
101
+ return `index://index/${data}`;
102
+ }
103
+
104
+ export function createUploadHandle(
105
+ localPath: string,
106
+ signer: Signer,
107
+ sizeBytes: bigint,
108
+ modificationTimeSeconds: bigint
109
+ ): sdk.ImportFileHandleUpload {
110
+ const data: UploadHandleData = {
111
+ localPath,
112
+ pathSignature: signer.sign(localPath),
113
+ sizeBytes: String(sizeBytes),
114
+ modificationTime: String(modificationTimeSeconds)
115
+ };
116
+
117
+ return `upload://upload/${encodeURIComponent(JSON.stringify(data))}` as sdk.ImportFileHandleUpload;
118
+ }
119
+
120
+ export function fromFileHandle(handle: sdk.ImportFileHandle) {
121
+ const url = new URL(handle);
122
+ return JSON.parse(decodeURIComponent(url.pathname.substring(1)));
123
+ }
124
+
125
+ export function toListItem(
126
+ logger: MiLogger,
127
+ info: {
128
+ directory: string;
129
+ fullName: string;
130
+ dirent: Dirent;
131
+ stat: Stats;
132
+ }
133
+ ): ListItem | undefined {
134
+ if (!(info.dirent.isFile() || info.dirent.isDirectory())) {
135
+ logger.warn(
136
+ `tried to get non-dir and non-file ${info.dirent.name}, skip it`
137
+ );
138
+ return;
139
+ }
140
+
141
+ return {
142
+ directory: info.directory,
143
+ isDir: info.dirent.isDirectory(),
144
+ name: info.dirent.name,
145
+ fullName: info.fullName,
146
+ lastModified: {
147
+ seconds: BigInt(Math.floor(info.stat.mtimeMs / 1000)),
148
+ nanos: 0
149
+ },
150
+ size: BigInt(info.stat.size)
151
+ };
152
+ }
@@ -0,0 +1,135 @@
1
+ import * as sdk from '@milaboratories/pl-model-common';
2
+ import {
3
+ bigintToResourceId,
4
+ ResourceId,
5
+ ResourceType
6
+ } from '@milaboratories/pl-client';
7
+ import { assertNever } from '@milaboratories/ts-helpers';
8
+
9
+ /**
10
+ * Converts local and remote storages to StorageEntries.
11
+ */
12
+ export function toStorageEntry(
13
+ locals: Record<string, string>,
14
+ remotes: Record<string, ResourceId>
15
+ ): sdk.StorageEntry[] {
16
+ const localEntries = Object.entries(locals).map(localToEntry);
17
+ const remoteEntries = Object.entries(remotes).map(remoteToEntry);
18
+
19
+ return localEntries.concat(remoteEntries);
20
+ }
21
+
22
+ export type StorageHandleData =
23
+ | RemoteStorageHandleData
24
+ | LocalStorageHandleData;
25
+
26
+ /**
27
+ * Gets a storage handle and gives an underlying data from it.
28
+ */
29
+ export function fromStorageHandle(
30
+ handle: sdk.StorageHandle
31
+ ): StorageHandleData {
32
+ if (isRemoteStorageHandle(handle)) {
33
+ return fromRemoteHandle(handle);
34
+ } else if (isLocalStorageHandle(handle)) {
35
+ return fromLocalHandle(handle);
36
+ }
37
+
38
+ assertNever(handle);
39
+ }
40
+
41
+ //
42
+ // Local storage:
43
+ //
44
+
45
+ export type LocalStorageHandleData = {
46
+ remote: false;
47
+ name: string;
48
+ path: string;
49
+ };
50
+
51
+ function localToEntry([name, path]: [string, string]): sdk.StorageEntry {
52
+ return {
53
+ name: name,
54
+ handle: toLocalHandle(name, path),
55
+ initialFullPath: path
56
+ };
57
+ }
58
+
59
+ const localHandleRegex = /^local:\/\/(?<name>.*)\/(?<path>.*)$/;
60
+
61
+ export function isLocalStorageHandle(
62
+ handle: sdk.StorageHandle
63
+ ): handle is sdk.StorageHandleLocal {
64
+ return localHandleRegex.test(handle);
65
+ }
66
+
67
+ function toLocalHandle(name: string, path: string): sdk.StorageHandleLocal {
68
+ return `local://${name}/${encodeURIComponent(path)}`;
69
+ }
70
+
71
+ function fromLocalHandle(handle: string): LocalStorageHandleData {
72
+ const parsed = handle.match(localHandleRegex);
73
+ if (parsed == null)
74
+ throw new Error(`Local list handle wasn't parsed: ${handle}`);
75
+
76
+ const { name, path } = parsed.groups!;
77
+
78
+ return {
79
+ path: decodeURIComponent(path),
80
+ name,
81
+ remote: false
82
+ };
83
+ }
84
+
85
+ //
86
+ // Remote storage:
87
+ //
88
+
89
+ export type RemoteStorageHandleData = {
90
+ remote: true;
91
+ name: string;
92
+ id: ResourceId;
93
+ type: ResourceType;
94
+ };
95
+
96
+ function remoteToEntry([name, rId]: [string, ResourceId]): sdk.StorageEntry {
97
+ return {
98
+ name: name,
99
+ handle: toRemoteHandle(name, rId),
100
+ initialFullPath: ''
101
+ };
102
+ }
103
+
104
+ const remoteHandleRegex = /^remote:\/\/(?<name>.*)\/(?<resourceId>.*)$/;
105
+
106
+ export function isRemoteStorageHandle(
107
+ handle: sdk.StorageHandle
108
+ ): handle is sdk.StorageHandleRemote {
109
+ return remoteHandleRegex.test(handle);
110
+ }
111
+
112
+ function toRemoteHandle(
113
+ name: string,
114
+ rId: ResourceId
115
+ ): sdk.StorageHandleRemote {
116
+ return `remote://${name}/${BigInt(rId)}`;
117
+ }
118
+
119
+ function fromRemoteHandle(handle: string): RemoteStorageHandleData {
120
+ const parsed = handle.match(remoteHandleRegex);
121
+ if (parsed == null)
122
+ throw new Error(`Remote list handle wasn't parsed: ${handle}`);
123
+ const { name, resourceId } = parsed.groups!;
124
+
125
+ return {
126
+ id: bigintToResourceId(BigInt(resourceId)),
127
+ type: storageType(name),
128
+ name,
129
+ remote: true
130
+ };
131
+ }
132
+
133
+ function storageType(name: string): ResourceType {
134
+ return { name: `LS/${name}`, version: '1' };
135
+ }
@@ -0,0 +1,7 @@
1
+ /** Generic settings for drivers that perform polling */
2
+ export type PollingOps = {
3
+ /** How frequent the driver should update exposed states from the backend. */
4
+ pollingInterval: number;
5
+ /** For how long to continue polling after the last derived computable value access. */
6
+ stopPollingDelay: number;
7
+ };
@@ -0,0 +1,5 @@
1
+ export const PL_STORAGE_TO_PATH = process.env.PL_STORAGE_TO_PATH
2
+ ? Object.fromEntries(
3
+ process.env.PL_STORAGE_TO_PATH.split(';').map((kv) => kv.split(':'))
4
+ )
5
+ : {};