@milaboratories/pl-drivers 1.13.0 → 1.14.0

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 (141) hide show
  1. package/dist/clients/download.cjs +5 -3
  2. package/dist/clients/download.cjs.map +1 -1
  3. package/dist/clients/download.d.ts.map +1 -1
  4. package/dist/clients/download.js +6 -4
  5. package/dist/clients/download.js.map +1 -1
  6. package/dist/clients/logs.cjs +10 -6
  7. package/dist/clients/logs.cjs.map +1 -1
  8. package/dist/clients/logs.d.ts.map +1 -1
  9. package/dist/clients/logs.js +11 -7
  10. package/dist/clients/logs.js.map +1 -1
  11. package/dist/clients/ls_api.cjs +5 -3
  12. package/dist/clients/ls_api.cjs.map +1 -1
  13. package/dist/clients/ls_api.js +6 -4
  14. package/dist/clients/ls_api.js.map +1 -1
  15. package/dist/clients/progress.cjs +7 -3
  16. package/dist/clients/progress.cjs.map +1 -1
  17. package/dist/clients/progress.d.ts.map +1 -1
  18. package/dist/clients/progress.js +8 -4
  19. package/dist/clients/progress.js.map +1 -1
  20. package/dist/clients/upload.cjs +22 -12
  21. package/dist/clients/upload.cjs.map +1 -1
  22. package/dist/clients/upload.d.ts.map +1 -1
  23. package/dist/clients/upload.js +23 -13
  24. package/dist/clients/upload.js.map +1 -1
  25. package/dist/drivers/download_blob/blob_key.cjs +3 -2
  26. package/dist/drivers/download_blob/blob_key.cjs.map +1 -1
  27. package/dist/drivers/download_blob/blob_key.js +3 -2
  28. package/dist/drivers/download_blob/blob_key.js.map +1 -1
  29. package/dist/drivers/download_blob/download_blob.cjs.map +1 -1
  30. package/dist/drivers/download_blob/download_blob.js.map +1 -1
  31. package/dist/drivers/download_blob_url/driver.cjs +2 -1
  32. package/dist/drivers/download_blob_url/driver.cjs.map +1 -1
  33. package/dist/drivers/download_blob_url/driver.d.ts +2 -2
  34. package/dist/drivers/download_blob_url/driver.d.ts.map +1 -1
  35. package/dist/drivers/download_blob_url/driver.js +3 -2
  36. package/dist/drivers/download_blob_url/driver.js.map +1 -1
  37. package/dist/drivers/download_blob_url/driver_id.cjs +4 -1
  38. package/dist/drivers/download_blob_url/driver_id.cjs.map +1 -1
  39. package/dist/drivers/download_blob_url/driver_id.js +3 -1
  40. package/dist/drivers/download_blob_url/driver_id.js.map +1 -1
  41. package/dist/drivers/download_url/task.cjs +1 -1
  42. package/dist/drivers/download_url/task.cjs.map +1 -1
  43. package/dist/drivers/download_url/task.d.ts.map +1 -1
  44. package/dist/drivers/download_url/task.js +1 -1
  45. package/dist/drivers/download_url/task.js.map +1 -1
  46. package/dist/drivers/helpers/download_remote_handle.cjs +7 -4
  47. package/dist/drivers/helpers/download_remote_handle.cjs.map +1 -1
  48. package/dist/drivers/helpers/download_remote_handle.js +8 -5
  49. package/dist/drivers/helpers/download_remote_handle.js.map +1 -1
  50. package/dist/drivers/helpers/logs_handle.cjs +9 -6
  51. package/dist/drivers/helpers/logs_handle.cjs.map +1 -1
  52. package/dist/drivers/helpers/logs_handle.js +10 -7
  53. package/dist/drivers/helpers/logs_handle.js.map +1 -1
  54. package/dist/drivers/helpers/ls_remote_import_handle.cjs +2 -2
  55. package/dist/drivers/helpers/ls_remote_import_handle.cjs.map +1 -1
  56. package/dist/drivers/helpers/ls_remote_import_handle.js +2 -2
  57. package/dist/drivers/helpers/ls_remote_import_handle.js.map +1 -1
  58. package/dist/drivers/helpers/ls_storage_entry.cjs +10 -16
  59. package/dist/drivers/helpers/ls_storage_entry.cjs.map +1 -1
  60. package/dist/drivers/helpers/ls_storage_entry.js +11 -17
  61. package/dist/drivers/helpers/ls_storage_entry.js.map +1 -1
  62. package/dist/drivers/logs_stream.cjs.map +1 -1
  63. package/dist/drivers/logs_stream.js.map +1 -1
  64. package/dist/drivers/ls.cjs +31 -29
  65. package/dist/drivers/ls.cjs.map +1 -1
  66. package/dist/drivers/ls.d.ts +10 -10
  67. package/dist/drivers/ls.d.ts.map +1 -1
  68. package/dist/drivers/ls.js +31 -29
  69. package/dist/drivers/ls.js.map +1 -1
  70. package/dist/drivers/types.cjs.map +1 -1
  71. package/dist/drivers/types.d.ts +2 -1
  72. package/dist/drivers/types.d.ts.map +1 -1
  73. package/dist/drivers/types.js.map +1 -1
  74. package/dist/drivers/upload.cjs.map +1 -1
  75. package/dist/drivers/upload.js.map +1 -1
  76. package/dist/drivers/virtual_storages.cjs +3 -0
  77. package/dist/drivers/virtual_storages.cjs.map +1 -1
  78. package/dist/drivers/virtual_storages.js +3 -0
  79. package/dist/drivers/virtual_storages.js.map +1 -1
  80. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs +2 -2
  81. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs.map +1 -1
  82. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts +2 -2
  83. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts.map +1 -1
  84. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js +2 -2
  85. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js.map +1 -1
  86. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs +2 -2
  87. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs.map +1 -1
  88. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts.map +1 -1
  89. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js +2 -2
  90. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js.map +1 -1
  91. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs +4 -4
  92. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs.map +1 -1
  93. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts +4 -4
  94. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts.map +1 -1
  95. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js +4 -4
  96. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js.map +1 -1
  97. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs +6 -6
  98. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs.map +1 -1
  99. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts +6 -6
  100. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts.map +1 -1
  101. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js +6 -6
  102. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js.map +1 -1
  103. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs +10 -10
  104. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs.map +1 -1
  105. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js +10 -10
  106. package/dist/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js.map +1 -1
  107. package/dist/proto-rest/downloadapi.d.ts +6 -6
  108. package/dist/proto-rest/downloadapi.d.ts.map +1 -1
  109. package/dist/proto-rest/progressapi.d.ts +19 -19
  110. package/dist/proto-rest/progressapi.d.ts.map +1 -1
  111. package/package.json +5 -5
  112. package/src/clients/download.ts +6 -3
  113. package/src/clients/logs.ts +22 -7
  114. package/src/clients/ls_api.ts +6 -4
  115. package/src/clients/progress.ts +18 -4
  116. package/src/clients/upload.ts +31 -14
  117. package/src/drivers/download_blob/blob_key.ts +4 -8
  118. package/src/drivers/download_blob/download_blob.ts +2 -2
  119. package/src/drivers/download_blob_url/driver.ts +10 -5
  120. package/src/drivers/download_blob_url/driver_id.ts +5 -3
  121. package/src/drivers/download_url/task.ts +5 -1
  122. package/src/drivers/helpers/download_remote_handle.ts +16 -5
  123. package/src/drivers/helpers/logs_handle.ts +16 -7
  124. package/src/drivers/helpers/ls_remote_import_handle.ts +2 -2
  125. package/src/drivers/helpers/ls_storage_entry.ts +15 -17
  126. package/src/drivers/logs_stream.ts +5 -5
  127. package/src/drivers/ls.test.ts +4 -4
  128. package/src/drivers/ls.ts +44 -58
  129. package/src/drivers/types.ts +3 -1
  130. package/src/drivers/upload.ts +3 -3
  131. package/src/drivers/virtual_storages.ts +3 -0
  132. package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.ts +7 -6
  133. package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.ts +7 -6
  134. package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.ts +14 -12
  135. package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.ts +21 -18
  136. package/src/proto-grpc/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.ts +35 -30
  137. package/src/proto-grpc/google/protobuf/descriptor.ts +5 -2
  138. package/src/proto-rest/downloadapi.ts +6 -6
  139. package/src/proto-rest/lsapi.ts +30 -30
  140. package/src/proto-rest/progressapi.ts +21 -21
  141. package/src/proto-rest/uploadapi.ts +41 -41
package/src/drivers/ls.ts CHANGED
@@ -1,14 +1,5 @@
1
- import type { PlClient, ResourceData, ResourceId } from "@milaboratories/pl-client";
2
- import { isNotNullResourceId } from "@milaboratories/pl-client";
1
+ import type { PlClient, UserResources } from "@milaboratories/pl-client";
3
2
  import type * as sdk from "@milaboratories/pl-model-common";
4
- import type {
5
- LocalImportFileHandle,
6
- LsEntry,
7
- OpenDialogOps,
8
- OpenMultipleFilesResponse,
9
- OpenSingleFileResponse,
10
- TableRange,
11
- } from "@milaboratories/pl-model-common";
12
3
  import { isImportFileHandleIndex } from "@milaboratories/pl-model-common";
13
4
  import type { MiLogger, Signer } from "@milaboratories/ts-helpers";
14
5
  import * as fsp from "node:fs/promises";
@@ -16,6 +7,7 @@ import * as path from "node:path";
16
7
  import { createLsFilesClient } from "../clients/constructors";
17
8
  import type { ClientLs } from "../clients/ls_api";
18
9
  import { validateAbsolute } from "../helpers/validate";
10
+ import type { ResourceInfo } from "@milaboratories/pl-tree";
19
11
  import {
20
12
  createIndexImportHandle,
21
13
  createUploadImportHandle,
@@ -26,6 +18,7 @@ import {
26
18
  createLocalStorageHandle,
27
19
  createRemoteStorageHandle,
28
20
  parseStorageHandle,
21
+ RemoteStorageHandleData,
29
22
  } from "./helpers/ls_storage_entry";
30
23
  import type { LocalStorageProjection, VirtualLocalStorageSpec } from "./types";
31
24
  import { DefaultVirtualLocalStorages } from "./virtual_storages";
@@ -52,21 +45,20 @@ export type ListRemoteFilesResultWithAdditionalInfo = {
52
45
  entries: LsEntryWithAdditionalInfo[];
53
46
  };
54
47
 
55
- export type LsEntryWithAdditionalInfo = LsEntry & {
48
+ export type LsEntryWithAdditionalInfo = sdk.LsEntry & {
56
49
  size: number;
57
50
  };
58
51
 
59
52
  export type OpenFileDialogCallback = (
60
53
  multipleFiles: boolean,
61
- ops?: OpenDialogOps,
54
+ ops?: sdk.OpenDialogOps,
62
55
  ) => Promise<undefined | string[]>;
63
56
 
64
57
  export class LsDriver implements InternalLsDriver {
65
58
  private constructor(
66
59
  private readonly logger: MiLogger,
67
60
  private readonly lsClient: ClientLs,
68
- /** Pl storage id, to resource id. The resource id can be used to make LS GRPC calls to. */
69
- private readonly storageIdToResourceId: Record<string, ResourceId>,
61
+ private readonly userResources: UserResources,
70
62
  private readonly signer: Signer,
71
63
  /** Virtual storages by name */
72
64
  private readonly virtualStoragesMap: Map<string, VirtualLocalStorageSpec>,
@@ -76,8 +68,8 @@ export class LsDriver implements InternalLsDriver {
76
68
  ) {}
77
69
 
78
70
  public async getLocalFileContent(
79
- file: LocalImportFileHandle,
80
- range?: TableRange,
71
+ file: sdk.LocalImportFileHandle,
72
+ range?: sdk.TableRange,
81
73
  ): Promise<Uint8Array> {
82
74
  const localPath = await this.tryResolveLocalFileHandle(file);
83
75
 
@@ -95,15 +87,15 @@ export class LsDriver implements InternalLsDriver {
95
87
  return await fsp.readFile(localPath);
96
88
  }
97
89
 
98
- public async getLocalFileSize(file: LocalImportFileHandle): Promise<number> {
90
+ public async getLocalFileSize(file: sdk.LocalImportFileHandle): Promise<number> {
99
91
  const localPath = await this.tryResolveLocalFileHandle(file);
100
92
  const stat = await fsp.stat(localPath);
101
93
  return stat.size;
102
94
  }
103
95
 
104
96
  public async showOpenMultipleFilesDialog(
105
- ops?: OpenDialogOps,
106
- ): Promise<OpenMultipleFilesResponse> {
97
+ ops?: sdk.OpenDialogOps,
98
+ ): Promise<sdk.OpenMultipleFilesResponse> {
107
99
  const result = await this.openFileDialogCallback(true, ops);
108
100
  if (result === undefined) return {};
109
101
  return {
@@ -111,7 +103,9 @@ export class LsDriver implements InternalLsDriver {
111
103
  };
112
104
  }
113
105
 
114
- public async showOpenSingleFileDialog(ops?: OpenDialogOps): Promise<OpenSingleFileResponse> {
106
+ public async showOpenSingleFileDialog(
107
+ ops?: sdk.OpenDialogOps,
108
+ ): Promise<sdk.OpenSingleFileResponse> {
115
109
  const result = await this.openFileDialogCallback(false, ops);
116
110
  if (result === undefined) return {};
117
111
  return {
@@ -125,7 +119,7 @@ export class LsDriver implements InternalLsDriver {
125
119
  * @param handle handle to be resolved
126
120
  * @private
127
121
  */
128
- private async tryResolveLocalFileHandle(handle: LocalImportFileHandle): Promise<string> {
122
+ private async tryResolveLocalFileHandle(handle: sdk.LocalImportFileHandle): Promise<string> {
129
123
  if (isImportFileHandleIndex(handle)) {
130
124
  const handleData = parseIndexHandle(handle);
131
125
  const localProjection = this.localProjectionsMap.get(handleData.storageId);
@@ -153,7 +147,7 @@ export class LsDriver implements InternalLsDriver {
153
147
 
154
148
  public async getLocalFileHandle(
155
149
  localPath: string,
156
- ): Promise<sdk.ImportFileHandle & LocalImportFileHandle> {
150
+ ): Promise<sdk.ImportFileHandle & sdk.LocalImportFileHandle> {
157
151
  validateAbsolute(localPath);
158
152
 
159
153
  // Checking if local path is directly reachable by pl, because it is in one of the
@@ -169,7 +163,7 @@ export class LsDriver implements InternalLsDriver {
169
163
  return createIndexImportHandle(
170
164
  lp.storageId,
171
165
  pathWithinStorage,
172
- ) as sdk.ImportFileHandleIndex & LocalImportFileHandle;
166
+ ) as sdk.ImportFileHandleIndex & sdk.LocalImportFileHandle;
173
167
  }
174
168
  }
175
169
 
@@ -181,30 +175,30 @@ export class LsDriver implements InternalLsDriver {
181
175
  this.signer,
182
176
  stat.size,
183
177
  stat.mtimeMs / 1000n, // integer division
184
- ) as sdk.ImportFileHandleUpload & LocalImportFileHandle;
178
+ ) as sdk.ImportFileHandleUpload & sdk.LocalImportFileHandle;
185
179
  }
186
180
 
187
181
  public async getStorageList(): Promise<sdk.StorageEntry[]> {
188
182
  const virtualStorages = [...this.virtualStoragesMap.values()].map((s) => ({
183
+ id: s.id,
189
184
  name: s.name,
190
- handle: createLocalStorageHandle(s.name, s.root),
185
+ handle: createLocalStorageHandle(s.id, s.root),
191
186
  initialFullPath: s.initialPath,
192
187
  }));
193
188
 
194
- const otherStorages = Object.entries(this.storageIdToResourceId!).map(
195
- ([storageId, resourceId]) => ({
196
- name: storageId,
197
- handle: createRemoteStorageHandle(storageId, resourceId),
198
- initialFullPath: "", // we don't have any additional information from where to start browsing remote storages
199
- isInitialPathHome: false,
200
- }),
201
- );
189
+ const dataLibraries = await this.userResources.getDataLibraries();
190
+ const remoteStorages = [...dataLibraries.values()].map((info) => ({
191
+ id: info.storageId,
192
+ name: info.storageName,
193
+ handle: createRemoteStorageHandle(info),
194
+ initialFullPath: "",
195
+ }));
202
196
 
203
197
  // root must be a storage so we can index any file,
204
198
  // but for UI it's enough
205
199
  // to have local virtual storage on *nix,
206
200
  // and local_disk_${drive} on Windows.
207
- const noRoot = otherStorages.filter((it) => it.name !== "root");
201
+ const noRoot = remoteStorages.filter((it) => it.id !== "root");
208
202
 
209
203
  return [...virtualStorages, ...noRoot];
210
204
  }
@@ -216,13 +210,14 @@ export class LsDriver implements InternalLsDriver {
216
210
  const storageData = parseStorageHandle(storageHandle);
217
211
 
218
212
  if (storageData.isRemote) {
219
- const response = await this.lsClient.list(storageData, fullPath);
213
+ const rInfo = await this.resolveRemoteStorageResourceInfo(storageData);
214
+ const response = await this.lsClient.list(rInfo, fullPath);
220
215
  return {
221
216
  entries: response.items.map((e) => ({
222
217
  type: e.isDir ? "dir" : "file",
223
218
  name: e.name,
224
219
  fullPath: e.fullName,
225
- handle: createIndexImportHandle(storageData.name, e.fullName),
220
+ handle: createIndexImportHandle(storageData.storageId, e.fullName),
226
221
  })),
227
222
  };
228
223
  }
@@ -234,7 +229,7 @@ export class LsDriver implements InternalLsDriver {
234
229
  }
235
230
  const lsRoot = path.isAbsolute(fullPath) ? fullPath : path.join(storageData.rootPath, fullPath);
236
231
 
237
- const entries: LsEntry[] = [];
232
+ const entries: sdk.LsEntry[] = [];
238
233
  for await (const dirent of await fsp.opendir(lsRoot)) {
239
234
  if (!dirent.isFile() && !dirent.isDirectory()) continue;
240
235
 
@@ -263,19 +258,27 @@ export class LsDriver implements InternalLsDriver {
263
258
  throw new Error(`Storage ${storageData.name} is not remote`);
264
259
  }
265
260
 
266
- const response = await this.lsClient.list(storageData, fullPath);
261
+ const rInfo = await this.resolveRemoteStorageResourceInfo(storageData);
262
+ const response = await this.lsClient.list(rInfo, fullPath);
267
263
 
268
264
  return {
269
265
  entries: response.items.map((e) => ({
270
266
  type: e.isDir ? "dir" : "file",
271
267
  name: e.name,
272
268
  fullPath: e.fullName,
273
- handle: createIndexImportHandle(storageData.name, e.fullName),
269
+ handle: createIndexImportHandle(storageData.storageId, e.fullName),
274
270
  size: Number(e.size),
275
271
  })),
276
272
  };
277
273
  }
278
274
 
275
+ /** Looks up ResourceType for a remote storage from the data libraries index. */
276
+ private async resolveRemoteStorageResourceInfo(
277
+ storageData: RemoteStorageHandleData,
278
+ ): Promise<ResourceInfo> {
279
+ return { id: storageData.resourceId, type: storageData.resourceType };
280
+ }
281
+
279
282
  public async fileToImportHandle(_file: sdk.FileLike): Promise<sdk.ImportFileHandle> {
280
283
  throw new Error(
281
284
  "Not implemented. This method must be implemented and intercepted in desktop preload script.",
@@ -300,7 +303,7 @@ export class LsDriver implements InternalLsDriver {
300
303
  for (const lp of localProjections) if (lp.localPath !== "") validateAbsolute(lp.localPath);
301
304
 
302
305
  // creating indexed maps for quick access
303
- const virtualStoragesMap = new Map(virtualStorages.map((s) => [s.name, s]));
306
+ const virtualStoragesMap = new Map(virtualStorages.map((s) => [s.id, s]));
304
307
  const localProjectionsMap = new Map(localProjections.map((s) => [s.storageId, s]));
305
308
 
306
309
  // validating there is no intersection
@@ -315,7 +318,7 @@ export class LsDriver implements InternalLsDriver {
315
318
  return new LsDriver(
316
319
  logger,
317
320
  lsClient,
318
- await doGetAvailableStorageIds(client),
321
+ client.userResources,
319
322
  signer,
320
323
  virtualStoragesMap,
321
324
  localProjectionsMap,
@@ -323,20 +326,3 @@ export class LsDriver implements InternalLsDriver {
323
326
  );
324
327
  }
325
328
  }
326
-
327
- async function doGetAvailableStorageIds(client: PlClient): Promise<Record<string, ResourceId>> {
328
- return client.withReadTx("GetAvailableStorageIds", async (tx) => {
329
- const lsProviderId = await tx.getResourceByName("LSProvider");
330
- const provider = await tx.getResourceData(lsProviderId, true);
331
-
332
- return providerToStorageIds(provider);
333
- });
334
- }
335
-
336
- function providerToStorageIds(provider: ResourceData) {
337
- return Object.fromEntries(
338
- provider.fields
339
- .filter((f) => f.type == "Dynamic" && isNotNullResourceId(f.value))
340
- .map((f) => [f.name.substring("storage/".length), f.value as ResourceId]),
341
- );
342
- }
@@ -104,7 +104,9 @@ export type LocalStorageProjection = {
104
104
 
105
105
  /** Allows to add parts of local FS as virtual storages, presenting homogeneous API to UI */
106
106
  export type VirtualLocalStorageSpec = {
107
- /** Virtual storage ID, must not intersect with other storage ids */
107
+ /** Stable machine identifier, must not intersect with other storage ids */
108
+ readonly id: string;
109
+ /** Human-readable display name */
108
110
  readonly name: string;
109
111
 
110
112
  /** Local path to "chroot" the API in */
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import type { ResourceId, ResourceType } from "@milaboratories/pl-client";
2
+ import type { SignedResourceId, ResourceType } from "@milaboratories/pl-client";
3
3
  import type { Watcher, ComputableCtx } from "@milaboratories/computable";
4
4
  import { Computable, PollingComputableHooks } from "@milaboratories/computable";
5
5
  import type { MiLogger, Signer } from "@milaboratories/ts-helpers";
@@ -50,7 +50,7 @@ export type UploadDriverOps = PollingOps & {
50
50
  * Handles both Index and Upload blobs,
51
51
  * the client needs to pass concrete blobs from `handle` field. */
52
52
  export class UploadDriver {
53
- private readonly idToProgress: Map<ResourceId, UploadTask> = new Map();
53
+ private readonly idToProgress: Map<SignedResourceId, UploadTask> = new Map();
54
54
 
55
55
  /** Holds a queue that upload blobs. */
56
56
  private readonly uploadQueue: TaskProcessor;
@@ -148,7 +148,7 @@ export class UploadDriver {
148
148
  }
149
149
 
150
150
  /** Decrement counters for the file and remove an uploading if counter == 0. */
151
- private async release(id: ResourceId, callerId: string) {
151
+ private async release(id: SignedResourceId, callerId: string) {
152
152
  const task = this.idToProgress.get(id);
153
153
  if (task === undefined) return;
154
154
 
@@ -9,6 +9,7 @@ export async function DefaultVirtualLocalStorages(): Promise<VirtualLocalStorage
9
9
  if (path.sep == "/")
10
10
  return [
11
11
  {
12
+ id: "local",
12
13
  name: "local",
13
14
  root: "/",
14
15
  initialPath: home,
@@ -33,6 +34,7 @@ export async function DefaultVirtualLocalStorages(): Promise<VirtualLocalStorage
33
34
  return drives.map((drive) => {
34
35
  const isHomeDrive = drive == homeDrive;
35
36
  return {
37
+ id: `local_disk_${drive}`,
36
38
  name: `local_disk_${drive}`,
37
39
  root: `${drive}:\\`,
38
40
  initialPath: isHomeDrive ? home : `${drive}:\\`,
@@ -41,6 +43,7 @@ export async function DefaultVirtualLocalStorages(): Promise<VirtualLocalStorage
41
43
  } catch {
42
44
  return [
43
45
  {
46
+ id: `local_disk_${homeDrive}`,
44
47
  name: `local_disk_${homeDrive}`,
45
48
  root: `${homeDrive}:\\`,
46
49
  initialPath: home,
@@ -32,9 +32,9 @@ export interface DownloadAPI_GetDownloadURL_Request {
32
32
  /**
33
33
  * Signature proving the caller is authorized to access this resource.
34
34
  *
35
- * @generated from protobuf field: optional bytes resource_signature = 3
35
+ * @generated from protobuf field: bytes resource_signature = 3
36
36
  */
37
- resourceSignature?: Uint8Array;
37
+ resourceSignature: Uint8Array;
38
38
  /**
39
39
  * Pass `true` here if the blob will be downloaded from the internal network,
40
40
  * e.g. controllers could use this if they are trying to download something from the internal network.
@@ -151,13 +151,14 @@ class DownloadAPI_GetDownloadURL_Request$Type extends MessageType<DownloadAPI_Ge
151
151
  constructor() {
152
152
  super("MiLaboratories.Controller.Shared.DownloadAPI.GetDownloadURL.Request", [
153
153
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
154
- { no: 3, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
154
+ { no: 3, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
155
155
  { no: 2, name: "is_internal_use", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
156
156
  ]);
157
157
  }
158
158
  create(value?: PartialMessage<DownloadAPI_GetDownloadURL_Request>): DownloadAPI_GetDownloadURL_Request {
159
159
  const message = globalThis.Object.create((this.messagePrototype!));
160
160
  message.resourceId = 0n;
161
+ message.resourceSignature = new Uint8Array(0);
161
162
  message.isInternalUse = false;
162
163
  if (value !== undefined)
163
164
  reflectionMergePartial<DownloadAPI_GetDownloadURL_Request>(this, message, value);
@@ -171,7 +172,7 @@ class DownloadAPI_GetDownloadURL_Request$Type extends MessageType<DownloadAPI_Ge
171
172
  case /* uint64 resource_id */ 1:
172
173
  message.resourceId = reader.uint64().toBigInt();
173
174
  break;
174
- case /* optional bytes resource_signature */ 3:
175
+ case /* bytes resource_signature */ 3:
175
176
  message.resourceSignature = reader.bytes();
176
177
  break;
177
178
  case /* bool is_internal_use */ 2:
@@ -195,8 +196,8 @@ class DownloadAPI_GetDownloadURL_Request$Type extends MessageType<DownloadAPI_Ge
195
196
  /* bool is_internal_use = 2; */
196
197
  if (message.isInternalUse !== false)
197
198
  writer.tag(2, WireType.Varint).bool(message.isInternalUse);
198
- /* optional bytes resource_signature = 3; */
199
- if (message.resourceSignature !== undefined)
199
+ /* bytes resource_signature = 3; */
200
+ if (message.resourceSignature.length)
200
201
  writer.tag(3, WireType.LengthDelimited).bytes(message.resourceSignature);
201
202
  let u = options.writeUnknownFields;
202
203
  if (u !== false)
@@ -92,9 +92,9 @@ export interface LsAPI_List_Request {
92
92
  /**
93
93
  * Signature proving the caller is authorized to access this resource.
94
94
  *
95
- * @generated from protobuf field: optional bytes resource_signature = 3
95
+ * @generated from protobuf field: bytes resource_signature = 3
96
96
  */
97
- resourceSignature?: Uint8Array;
97
+ resourceSignature: Uint8Array;
98
98
  /**
99
99
  * Location to list, relative to the storage root. Only items that have <full_name> starting
100
100
  * with <location> are included in the list response.
@@ -299,13 +299,14 @@ class LsAPI_List_Request$Type extends MessageType<LsAPI_List_Request> {
299
299
  constructor() {
300
300
  super("MiLaboratories.Controller.Shared.LsAPI.List.Request", [
301
301
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
302
- { no: 3, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
302
+ { no: 3, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
303
303
  { no: 2, name: "location", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
304
304
  ]);
305
305
  }
306
306
  create(value?: PartialMessage<LsAPI_List_Request>): LsAPI_List_Request {
307
307
  const message = globalThis.Object.create((this.messagePrototype!));
308
308
  message.resourceId = 0n;
309
+ message.resourceSignature = new Uint8Array(0);
309
310
  message.location = "";
310
311
  if (value !== undefined)
311
312
  reflectionMergePartial<LsAPI_List_Request>(this, message, value);
@@ -319,7 +320,7 @@ class LsAPI_List_Request$Type extends MessageType<LsAPI_List_Request> {
319
320
  case /* uint64 resource_id */ 1:
320
321
  message.resourceId = reader.uint64().toBigInt();
321
322
  break;
322
- case /* optional bytes resource_signature */ 3:
323
+ case /* bytes resource_signature */ 3:
323
324
  message.resourceSignature = reader.bytes();
324
325
  break;
325
326
  case /* string location */ 2:
@@ -343,8 +344,8 @@ class LsAPI_List_Request$Type extends MessageType<LsAPI_List_Request> {
343
344
  /* string location = 2; */
344
345
  if (message.location !== "")
345
346
  writer.tag(2, WireType.LengthDelimited).string(message.location);
346
- /* optional bytes resource_signature = 3; */
347
- if (message.resourceSignature !== undefined)
347
+ /* bytes resource_signature = 3; */
348
+ if (message.resourceSignature.length)
348
349
  writer.tag(3, WireType.LengthDelimited).bytes(message.resourceSignature);
349
350
  let u = options.writeUnknownFields;
350
351
  if (u !== false)
@@ -58,9 +58,9 @@ export interface ProgressAPI_GetStatus_Request {
58
58
  */
59
59
  resourceId: bigint;
60
60
  /**
61
- * @generated from protobuf field: optional bytes resource_signature = 2
61
+ * @generated from protobuf field: bytes resource_signature = 2
62
62
  */
63
- resourceSignature?: Uint8Array;
63
+ resourceSignature: Uint8Array;
64
64
  }
65
65
  /**
66
66
  * @generated from protobuf message MiLaboratories.Controller.Shared.ProgressAPI.GetStatus.Response
@@ -85,9 +85,9 @@ export interface ProgressAPI_RealtimeStatus_Request {
85
85
  */
86
86
  resourceId: bigint;
87
87
  /**
88
- * @generated from protobuf field: optional bytes resource_signature = 3
88
+ * @generated from protobuf field: bytes resource_signature = 3
89
89
  */
90
- resourceSignature?: Uint8Array;
90
+ resourceSignature: Uint8Array;
91
91
  /**
92
92
  * @generated from protobuf field: google.protobuf.Duration update_interval = 2
93
93
  */
@@ -262,12 +262,13 @@ class ProgressAPI_GetStatus_Request$Type extends MessageType<ProgressAPI_GetStat
262
262
  constructor() {
263
263
  super("MiLaboratories.Controller.Shared.ProgressAPI.GetStatus.Request", [
264
264
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
265
- { no: 2, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ }
265
+ { no: 2, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ }
266
266
  ]);
267
267
  }
268
268
  create(value?: PartialMessage<ProgressAPI_GetStatus_Request>): ProgressAPI_GetStatus_Request {
269
269
  const message = globalThis.Object.create((this.messagePrototype!));
270
270
  message.resourceId = 0n;
271
+ message.resourceSignature = new Uint8Array(0);
271
272
  if (value !== undefined)
272
273
  reflectionMergePartial<ProgressAPI_GetStatus_Request>(this, message, value);
273
274
  return message;
@@ -280,7 +281,7 @@ class ProgressAPI_GetStatus_Request$Type extends MessageType<ProgressAPI_GetStat
280
281
  case /* uint64 resource_id */ 1:
281
282
  message.resourceId = reader.uint64().toBigInt();
282
283
  break;
283
- case /* optional bytes resource_signature */ 2:
284
+ case /* bytes resource_signature */ 2:
284
285
  message.resourceSignature = reader.bytes();
285
286
  break;
286
287
  default:
@@ -298,8 +299,8 @@ class ProgressAPI_GetStatus_Request$Type extends MessageType<ProgressAPI_GetStat
298
299
  /* uint64 resource_id = 1; */
299
300
  if (message.resourceId !== 0n)
300
301
  writer.tag(1, WireType.Varint).uint64(message.resourceId);
301
- /* optional bytes resource_signature = 2; */
302
- if (message.resourceSignature !== undefined)
302
+ /* bytes resource_signature = 2; */
303
+ if (message.resourceSignature.length)
303
304
  writer.tag(2, WireType.LengthDelimited).bytes(message.resourceSignature);
304
305
  let u = options.writeUnknownFields;
305
306
  if (u !== false)
@@ -400,13 +401,14 @@ class ProgressAPI_RealtimeStatus_Request$Type extends MessageType<ProgressAPI_Re
400
401
  constructor() {
401
402
  super("MiLaboratories.Controller.Shared.ProgressAPI.RealtimeStatus.Request", [
402
403
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
403
- { no: 3, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
404
+ { no: 3, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
404
405
  { no: 2, name: "update_interval", kind: "message", T: () => Duration }
405
406
  ]);
406
407
  }
407
408
  create(value?: PartialMessage<ProgressAPI_RealtimeStatus_Request>): ProgressAPI_RealtimeStatus_Request {
408
409
  const message = globalThis.Object.create((this.messagePrototype!));
409
410
  message.resourceId = 0n;
411
+ message.resourceSignature = new Uint8Array(0);
410
412
  if (value !== undefined)
411
413
  reflectionMergePartial<ProgressAPI_RealtimeStatus_Request>(this, message, value);
412
414
  return message;
@@ -419,7 +421,7 @@ class ProgressAPI_RealtimeStatus_Request$Type extends MessageType<ProgressAPI_Re
419
421
  case /* uint64 resource_id */ 1:
420
422
  message.resourceId = reader.uint64().toBigInt();
421
423
  break;
422
- case /* optional bytes resource_signature */ 3:
424
+ case /* bytes resource_signature */ 3:
423
425
  message.resourceSignature = reader.bytes();
424
426
  break;
425
427
  case /* google.protobuf.Duration update_interval */ 2:
@@ -443,8 +445,8 @@ class ProgressAPI_RealtimeStatus_Request$Type extends MessageType<ProgressAPI_Re
443
445
  /* google.protobuf.Duration update_interval = 2; */
444
446
  if (message.updateInterval)
445
447
  Duration.internalBinaryWrite(message.updateInterval, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
446
- /* optional bytes resource_signature = 3; */
447
- if (message.resourceSignature !== undefined)
448
+ /* bytes resource_signature = 3; */
449
+ if (message.resourceSignature.length)
448
450
  writer.tag(3, WireType.LengthDelimited).bytes(message.resourceSignature);
449
451
  let u = options.writeUnknownFields;
450
452
  if (u !== false)
@@ -29,9 +29,9 @@ export interface StreamingAPI_ReadBinary {
29
29
  /**
30
30
  * Signature proving the caller is authorized to access this resource.
31
31
  *
32
- * @generated from protobuf field: optional bytes resource_signature = 3
32
+ * @generated from protobuf field: bytes resource_signature = 3
33
33
  */
34
- resourceSignature?: Uint8Array;
34
+ resourceSignature: Uint8Array;
35
35
  /**
36
36
  * <offset> makes the streamer perform a seek operation to the given offset before sending the data.
37
37
  *
@@ -61,9 +61,9 @@ export interface StreamingAPI_ReadText {
61
61
  /**
62
62
  * Signature proving the caller is authorized to access this resource.
63
63
  *
64
- * @generated from protobuf field: optional bytes resource_signature = 3
64
+ * @generated from protobuf field: bytes resource_signature = 3
65
65
  */
66
- resourceSignature?: Uint8Array;
66
+ resourceSignature: Uint8Array;
67
67
  /**
68
68
  * <offset> makes the streamer perform a seek operation to the given offset before sending the contents.
69
69
  * This offset is taken in BYTES, as it eases streaming recovery after a client reconnection or controller restart.
@@ -115,9 +115,9 @@ export interface StreamingAPI_LastLines {
115
115
  /**
116
116
  * Signature proving the caller is authorized to access this resource.
117
117
  *
118
- * @generated from protobuf field: optional bytes resource_signature = 4
118
+ * @generated from protobuf field: bytes resource_signature = 4
119
119
  */
120
- resourceSignature?: Uint8Array;
120
+ resourceSignature: Uint8Array;
121
121
  /**
122
122
  * <offset> makes the streamer perform a seek operation to the given offset before sending the contents.
123
123
  * This offset is taken in BYTES, as it eases streaming recovery after a client reconnection or controller restart.
@@ -226,7 +226,7 @@ class StreamingAPI_ReadBinary$Type extends MessageType<StreamingAPI_ReadBinary>
226
226
  constructor() {
227
227
  super("MiLaboratories.Controller.Shared.StreamingAPI.ReadBinary", [
228
228
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
229
- { no: 3, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
229
+ { no: 3, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
230
230
  { no: 2, name: "offset", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
231
231
  { no: 11, name: "chunk_size", kind: "scalar", opt: true, T: 13 /*ScalarType.UINT32*/ }
232
232
  ]);
@@ -234,6 +234,7 @@ class StreamingAPI_ReadBinary$Type extends MessageType<StreamingAPI_ReadBinary>
234
234
  create(value?: PartialMessage<StreamingAPI_ReadBinary>): StreamingAPI_ReadBinary {
235
235
  const message = globalThis.Object.create((this.messagePrototype!));
236
236
  message.resourceId = 0n;
237
+ message.resourceSignature = new Uint8Array(0);
237
238
  message.offset = 0n;
238
239
  if (value !== undefined)
239
240
  reflectionMergePartial<StreamingAPI_ReadBinary>(this, message, value);
@@ -247,7 +248,7 @@ class StreamingAPI_ReadBinary$Type extends MessageType<StreamingAPI_ReadBinary>
247
248
  case /* uint64 resource_id */ 1:
248
249
  message.resourceId = reader.uint64().toBigInt();
249
250
  break;
250
- case /* optional bytes resource_signature */ 3:
251
+ case /* bytes resource_signature */ 3:
251
252
  message.resourceSignature = reader.bytes();
252
253
  break;
253
254
  case /* int64 offset */ 2:
@@ -274,8 +275,8 @@ class StreamingAPI_ReadBinary$Type extends MessageType<StreamingAPI_ReadBinary>
274
275
  /* int64 offset = 2; */
275
276
  if (message.offset !== 0n)
276
277
  writer.tag(2, WireType.Varint).int64(message.offset);
277
- /* optional bytes resource_signature = 3; */
278
- if (message.resourceSignature !== undefined)
278
+ /* bytes resource_signature = 3; */
279
+ if (message.resourceSignature.length)
279
280
  writer.tag(3, WireType.LengthDelimited).bytes(message.resourceSignature);
280
281
  /* optional uint32 chunk_size = 11; */
281
282
  if (message.chunkSize !== undefined)
@@ -295,7 +296,7 @@ class StreamingAPI_ReadText$Type extends MessageType<StreamingAPI_ReadText> {
295
296
  constructor() {
296
297
  super("MiLaboratories.Controller.Shared.StreamingAPI.ReadText", [
297
298
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
298
- { no: 3, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
299
+ { no: 3, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
299
300
  { no: 2, name: "offset", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
300
301
  { no: 20, name: "read_limit", kind: "scalar", opt: true, T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
301
302
  { no: 21, name: "search", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
@@ -305,6 +306,7 @@ class StreamingAPI_ReadText$Type extends MessageType<StreamingAPI_ReadText> {
305
306
  create(value?: PartialMessage<StreamingAPI_ReadText>): StreamingAPI_ReadText {
306
307
  const message = globalThis.Object.create((this.messagePrototype!));
307
308
  message.resourceId = 0n;
309
+ message.resourceSignature = new Uint8Array(0);
308
310
  message.offset = 0n;
309
311
  if (value !== undefined)
310
312
  reflectionMergePartial<StreamingAPI_ReadText>(this, message, value);
@@ -318,7 +320,7 @@ class StreamingAPI_ReadText$Type extends MessageType<StreamingAPI_ReadText> {
318
320
  case /* uint64 resource_id */ 1:
319
321
  message.resourceId = reader.uint64().toBigInt();
320
322
  break;
321
- case /* optional bytes resource_signature */ 3:
323
+ case /* bytes resource_signature */ 3:
322
324
  message.resourceSignature = reader.bytes();
323
325
  break;
324
326
  case /* int64 offset */ 2:
@@ -351,8 +353,8 @@ class StreamingAPI_ReadText$Type extends MessageType<StreamingAPI_ReadText> {
351
353
  /* int64 offset = 2; */
352
354
  if (message.offset !== 0n)
353
355
  writer.tag(2, WireType.Varint).int64(message.offset);
354
- /* optional bytes resource_signature = 3; */
355
- if (message.resourceSignature !== undefined)
356
+ /* bytes resource_signature = 3; */
357
+ if (message.resourceSignature.length)
356
358
  writer.tag(3, WireType.LengthDelimited).bytes(message.resourceSignature);
357
359
  /* optional int64 read_limit = 20; */
358
360
  if (message.readLimit !== undefined)
@@ -378,7 +380,7 @@ class StreamingAPI_LastLines$Type extends MessageType<StreamingAPI_LastLines> {
378
380
  constructor() {
379
381
  super("MiLaboratories.Controller.Shared.StreamingAPI.LastLines", [
380
382
  { no: 1, name: "resource_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
381
- { no: 4, name: "resource_signature", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
383
+ { no: 4, name: "resource_signature", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
382
384
  { no: 2, name: "offset", kind: "scalar", opt: true, T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
383
385
  { no: 3, name: "line_count", kind: "scalar", opt: true, T: 5 /*ScalarType.INT32*/ },
384
386
  { no: 21, name: "search", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
@@ -388,6 +390,7 @@ class StreamingAPI_LastLines$Type extends MessageType<StreamingAPI_LastLines> {
388
390
  create(value?: PartialMessage<StreamingAPI_LastLines>): StreamingAPI_LastLines {
389
391
  const message = globalThis.Object.create((this.messagePrototype!));
390
392
  message.resourceId = 0n;
393
+ message.resourceSignature = new Uint8Array(0);
391
394
  if (value !== undefined)
392
395
  reflectionMergePartial<StreamingAPI_LastLines>(this, message, value);
393
396
  return message;
@@ -400,7 +403,7 @@ class StreamingAPI_LastLines$Type extends MessageType<StreamingAPI_LastLines> {
400
403
  case /* uint64 resource_id */ 1:
401
404
  message.resourceId = reader.uint64().toBigInt();
402
405
  break;
403
- case /* optional bytes resource_signature */ 4:
406
+ case /* bytes resource_signature */ 4:
404
407
  message.resourceSignature = reader.bytes();
405
408
  break;
406
409
  case /* optional int64 offset */ 2:
@@ -436,8 +439,8 @@ class StreamingAPI_LastLines$Type extends MessageType<StreamingAPI_LastLines> {
436
439
  /* optional int32 line_count = 3; */
437
440
  if (message.lineCount !== undefined)
438
441
  writer.tag(3, WireType.Varint).int32(message.lineCount);
439
- /* optional bytes resource_signature = 4; */
440
- if (message.resourceSignature !== undefined)
442
+ /* bytes resource_signature = 4; */
443
+ if (message.resourceSignature.length)
441
444
  writer.tag(4, WireType.LengthDelimited).bytes(message.resourceSignature);
442
445
  /* optional string search = 21; */
443
446
  if (message.search !== undefined)