@cirrobio/sdk 0.4.2 → 0.9.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.
- package/README.md +7 -0
- package/dist/index.esm.js +759 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +815 -19
- package/dist/index.js.map +1 -0
- package/dist/types/api/config.d.ts +6 -0
- package/dist/{api → types/api}/error-handler.d.ts +0 -4
- package/dist/types/api/error.d.ts +4 -0
- package/dist/types/api.d.ts +2 -0
- package/dist/types/auth/authentication-provider.d.ts +24 -0
- package/dist/types/auth/current-user.d.ts +16 -0
- package/dist/types/auth/static-token-auth.d.ts +11 -0
- package/dist/types/auth.d.ts +3 -0
- package/dist/types/data/data.service.d.ts +44 -0
- package/dist/types/data.d.ts +1 -0
- package/dist/{file → types/file}/calculate-size.d.ts +1 -1
- package/dist/{file → types/file}/file.service.d.ts +9 -6
- package/dist/types/file/manifest-parser.d.ts +14 -0
- package/dist/types/file/models/assets.d.ts +8 -0
- package/dist/{file → types/file/models}/file-object.model.d.ts +1 -1
- package/dist/types/file/models/file.d.ts +14 -0
- package/dist/types/file/models/folder.d.ts +13 -0
- package/dist/{file → types/file}/shared.d.ts +1 -1
- package/dist/types/file/util/get-parent.fn.d.ts +4 -0
- package/dist/{file.d.ts → types/file.d.ts} +5 -2
- package/dist/{index.d.ts → types/index.d.ts} +2 -0
- package/dist/types/util/get-resource-name.d.ts +1 -0
- package/dist/{util.d.ts → types/util.d.ts} +1 -0
- package/package.json +20 -15
- package/dist/api/error-handler.js +0 -37
- package/dist/api.d.ts +0 -1
- package/dist/api.js +0 -6
- package/dist/file/__test__/s3-utils.test.d.ts +0 -1
- package/dist/file/__test__/s3-utils.test.js +0 -16
- package/dist/file/actions/delete.fn.js +0 -15
- package/dist/file/actions/sign-url.fn.js +0 -27
- package/dist/file/actions/upload.fn.js +0 -23
- package/dist/file/calculate-size.js +0 -16
- package/dist/file/extensions.fn.js +0 -77
- package/dist/file/file-object.model.js +0 -8
- package/dist/file/file.service.js +0 -81
- package/dist/file/project-access-context.js +0 -24
- package/dist/file/shared.js +0 -11
- package/dist/file/util/credentials-mutex.so.js +0 -32
- package/dist/file/util/s3-client.js +0 -19
- package/dist/file/util/s3-utils.js +0 -17
- package/dist/file.js +0 -43
- package/dist/formatters/bytes-to-string.js +0 -30
- package/dist/formatters/json-pretty-print.js +0 -12
- package/dist/formatters/normalize-date.js +0 -16
- package/dist/formatters/normalize-string.js +0 -13
- package/dist/formatters/slash.js +0 -19
- package/dist/formatters/to-date-format.js +0 -16
- package/dist/formatters/to-friendly-name.js +0 -17
- package/dist/formatters/to-money.js +0 -16
- package/dist/formatters/to-pascal-case.js +0 -12
- package/dist/formatters/to-title-case.js +0 -14
- package/dist/formatters.js +0 -24
- package/dist/util/download.js +0 -22
- package/dist/util/extract-from-object.js +0 -15
- package/dist/util/handle-promise.js +0 -11
- package/dist/util.js +0 -10
- package/src/api/error-handler.ts +0 -36
- package/src/api.ts +0 -1
- package/src/file/__test__/s3-utils.test.ts +0 -17
- package/src/file/actions/delete.fn.ts +0 -18
- package/src/file/actions/sign-url.fn.ts +0 -36
- package/src/file/actions/upload.fn.ts +0 -30
- package/src/file/calculate-size.ts +0 -14
- package/src/file/extensions.fn.ts +0 -88
- package/src/file/file-object.model.ts +0 -57
- package/src/file/file.service.ts +0 -86
- package/src/file/project-access-context.ts +0 -26
- package/src/file/shared.ts +0 -7
- package/src/file/util/credentials-mutex.so.ts +0 -33
- package/src/file/util/s3-client.ts +0 -17
- package/src/file/util/s3-utils.ts +0 -14
- package/src/file.ts +0 -13
- package/src/formatters/__tests__/formatters.spec.ts +0 -65
- package/src/formatters/bytes-to-string.ts +0 -32
- package/src/formatters/json-pretty-print.ts +0 -8
- package/src/formatters/normalize-date.ts +0 -10
- package/src/formatters/normalize-string.ts +0 -8
- package/src/formatters/slash.ts +0 -15
- package/src/formatters/to-date-format.ts +0 -12
- package/src/formatters/to-friendly-name.ts +0 -14
- package/src/formatters/to-money.ts +0 -13
- package/src/formatters/to-pascal-case.ts +0 -8
- package/src/formatters/to-title-case.ts +0 -9
- package/src/formatters.ts +0 -10
- package/src/index.ts +0 -4
- package/src/util/download.ts +0 -18
- package/src/util/extract-from-object.ts +0 -11
- package/src/util/handle-promise.ts +0 -7
- package/src/util.ts +0 -3
- package/tsconfig.json +0 -21
- /package/dist/{file → types/file}/actions/delete.fn.d.ts +0 -0
- /package/dist/{file → types/file}/actions/sign-url.fn.d.ts +0 -0
- /package/dist/{file → types/file}/actions/upload.fn.d.ts +0 -0
- /package/dist/{file → types/file}/extensions.fn.d.ts +0 -0
- /package/dist/{file → types/file}/project-access-context.d.ts +0 -0
- /package/dist/{file → types/file}/util/credentials-mutex.so.d.ts +0 -0
- /package/dist/{file → types/file}/util/s3-client.d.ts +0 -0
- /package/dist/{file → types/file}/util/s3-utils.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/bytes-to-string.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/json-pretty-print.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/normalize-date.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/normalize-string.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/slash.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/to-date-format.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/to-friendly-name.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/to-money.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/to-pascal-case.d.ts +0 -0
- /package/dist/{formatters → types/formatters}/to-title-case.d.ts +0 -0
- /package/dist/{formatters.d.ts → types/formatters.d.ts} +0 -0
- /package/dist/{util → types/util}/download.d.ts +0 -0
- /package/dist/{util → types/util}/extract-from-object.d.ts +0 -0
- /package/dist/{util → types/util}/handle-promise.d.ts +0 -0
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import { ProjectAccessType, Configuration, AuditApi, BillingApi, ComputeEnvironmentApi, DatasetsApi, ExecutionApi, FileApi, GovernanceApi, MetadataApi, NotebooksApi, ProcessesApi, ProjectRequestsApi, ProjectsApi, ReferencesApi, SharingApi, SystemApi, ToolsApi, UsersApi } from '@cirrobio/api-client';
|
|
2
|
+
import { S3Client, GetObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
|
3
|
+
import { getSignedUrl as getSignedUrl$1 } from '@aws-sdk/s3-request-presigner';
|
|
4
|
+
import { Upload } from '@aws-sdk/lib-storage';
|
|
5
|
+
export { Upload } from '@aws-sdk/lib-storage';
|
|
6
|
+
import { v4 } from 'uuid';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* An array of file extensions that can be rendered in a genome viewer
|
|
10
|
+
*/
|
|
11
|
+
const FILE_TRACK_ANNOTATION = ['bed', 'bed.gz', 'gtf', 'gtf.gz'];
|
|
12
|
+
const FILE_TRACK_ALIGNMENTS = ['cram', 'cram.gz']; // TODO: put back bam
|
|
13
|
+
const FILE_TRACK_VARIANT = ['vcf', 'vcf.gz'];
|
|
14
|
+
const FILE_TRACK_WIG = ['wig', 'wig.gz', 'bw', 'bw.gz', 'bigwig', 'bigwig.gz'];
|
|
15
|
+
const FILE_TRACK_SEG = ['seg', 'seg.gz'];
|
|
16
|
+
const FILE_TRACK_INDEX_EXTENSIONS = ['tbi', 'bai', 'crai', 'csi'];
|
|
17
|
+
const FILE_VITESSCE_EXTENSIONS = ['hdf5', 'h5ad', 'loom'];
|
|
18
|
+
const FILE_TRACK_EXTENSIONS_NO_INDEX = [
|
|
19
|
+
...FILE_TRACK_ANNOTATION,
|
|
20
|
+
...FILE_TRACK_ALIGNMENTS,
|
|
21
|
+
...FILE_TRACK_VARIANT,
|
|
22
|
+
...FILE_TRACK_WIG,
|
|
23
|
+
...FILE_TRACK_SEG
|
|
24
|
+
];
|
|
25
|
+
const FILE_TRACK_EXTENSIONS = [
|
|
26
|
+
...FILE_TRACK_EXTENSIONS_NO_INDEX,
|
|
27
|
+
...FILE_TRACK_INDEX_EXTENSIONS
|
|
28
|
+
];
|
|
29
|
+
const FILE_BROWSER_EXTENSIONS = ['html', 'pdf'];
|
|
30
|
+
/**
|
|
31
|
+
* An array of file extensions that are protein structure files.
|
|
32
|
+
*/
|
|
33
|
+
const FILE_PROTEIN_STRUCTURE_EXTENSIONS = ['pdb', 'pdb.gz', 'cif', 'cif.gz', 'ent', 'ent.gz', 'mmtf', 'mmtf.gz'];
|
|
34
|
+
/**
|
|
35
|
+
* An array of file extensions that are considered tabular files.
|
|
36
|
+
*/
|
|
37
|
+
const FILE_DSV_EXTENSIONS = ['tab', 'csv', 'tsv', 'dsv'];
|
|
38
|
+
/**
|
|
39
|
+
* An array of file extensions that contain genomic sequence content.
|
|
40
|
+
*/
|
|
41
|
+
const FILE_TXT_GENOMICS_EXTENSIONS = ['fasta', 'fna', 'fsa', 'fa', 'fastp', 'fastq', 'faa', 'gbk', 'gff', 'vcf', 'seq'];
|
|
42
|
+
/**
|
|
43
|
+
* An array of file extensions that are considered TXT files.
|
|
44
|
+
*/
|
|
45
|
+
const FILE_TXT_EXTENSIONS = ['txt', 'log', 'yml', 'cfg', 'config', 'xml', 'yaml', ...FILE_DSV_EXTENSIONS, ...FILE_TXT_GENOMICS_EXTENSIONS];
|
|
46
|
+
/**
|
|
47
|
+
* An array of file extensions that are considered image files.
|
|
48
|
+
*/
|
|
49
|
+
const FILE_IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'svg'];
|
|
50
|
+
/**
|
|
51
|
+
* An array of file extensions that are considered OME (Open Microscopy Environment) files.
|
|
52
|
+
*/
|
|
53
|
+
const FILE_OME_EXTENSIONS = ['tif', 'ome.tif', 'ome.tiff', 'ome.tif.gz', 'ome.tiff.gz'];
|
|
54
|
+
/**
|
|
55
|
+
* An array of file extensions that can be opened in the browser.
|
|
56
|
+
* Includes common document formats such as HTML, PDF, and JSON, as well as image, DSV, TXT, and OME file formats.
|
|
57
|
+
*/
|
|
58
|
+
const FILE_EXTENSIONS_TO_OPEN = ['html', 'pdf', 'json', 'fcs',
|
|
59
|
+
...FILE_IMAGE_EXTENSIONS,
|
|
60
|
+
...FILE_TXT_EXTENSIONS,
|
|
61
|
+
...FILE_TRACK_EXTENSIONS,
|
|
62
|
+
...FILE_PROTEIN_STRUCTURE_EXTENSIONS
|
|
63
|
+
];
|
|
64
|
+
/**
|
|
65
|
+
* Checks if a file has an extension that matches one in the provided list.
|
|
66
|
+
* @param filePath The file to check the extension of.
|
|
67
|
+
* @param extensions A list of file endings to check the file against.
|
|
68
|
+
* @returns True or false if the file has an ending in the extensions list.
|
|
69
|
+
*/
|
|
70
|
+
function matchesExtension(filePath, extensions) {
|
|
71
|
+
filePath = filePath.toLowerCase();
|
|
72
|
+
// Decompress the filePath if it ends with .gz
|
|
73
|
+
if (filePath.endsWith('.gz')) {
|
|
74
|
+
filePath = filePath.slice(0, -3); // remove the .gz extension
|
|
75
|
+
}
|
|
76
|
+
// Now, get the extension of the file
|
|
77
|
+
const fileExtension = filePath.slice(filePath.lastIndexOf('.') + 1);
|
|
78
|
+
// Check if the fileExtension is in the list of extensions
|
|
79
|
+
return extensions.includes(fileExtension);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class Mutex {
|
|
83
|
+
constructor() {
|
|
84
|
+
this.mutex = Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
lock() {
|
|
87
|
+
let begin = () => { };
|
|
88
|
+
this.mutex = this.mutex.then(() => new Promise(begin));
|
|
89
|
+
return new Promise((res) => {
|
|
90
|
+
begin = res;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async dispatch(fn) {
|
|
94
|
+
const unlock = await this.lock();
|
|
95
|
+
try {
|
|
96
|
+
return await Promise.resolve(fn());
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
unlock();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A mutex to ensure that only one request for credentials is made at a time.
|
|
105
|
+
*/
|
|
106
|
+
const credentialsMutex = new Mutex();
|
|
107
|
+
/**
|
|
108
|
+
* A cache of credentials to avoid making multiple requests for the same credentials.
|
|
109
|
+
*/
|
|
110
|
+
const credentialsCache = new Map();
|
|
111
|
+
|
|
112
|
+
const clientCache = new Map();
|
|
113
|
+
/**
|
|
114
|
+
* Creates an S3 client using the provided credentials.
|
|
115
|
+
*/
|
|
116
|
+
function createS3Client(credentials) {
|
|
117
|
+
const cacheKey = `${credentials.accessKeyId}-${credentials.region}`;
|
|
118
|
+
const cachedClient = clientCache.get(cacheKey);
|
|
119
|
+
if (cachedClient) {
|
|
120
|
+
return cachedClient;
|
|
121
|
+
}
|
|
122
|
+
const newClient = new S3Client({
|
|
123
|
+
credentials: {
|
|
124
|
+
accessKeyId: credentials.accessKeyId,
|
|
125
|
+
secretAccessKey: credentials.secretAccessKey,
|
|
126
|
+
sessionToken: credentials.sessionToken,
|
|
127
|
+
},
|
|
128
|
+
region: credentials.region,
|
|
129
|
+
useDualstackEndpoint: true,
|
|
130
|
+
});
|
|
131
|
+
clientCache.set(cacheKey, newClient);
|
|
132
|
+
return newClient;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Converts an S3 URI to a bucket and key
|
|
137
|
+
*/
|
|
138
|
+
function s3UriToParams(uri) {
|
|
139
|
+
const matches = /^s3:\/\/([^/]+)\/(.+)$/.exec(uri);
|
|
140
|
+
if (!matches) {
|
|
141
|
+
throw new Error(`Received invalid uri: '${uri}'`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
Bucket: matches[1],
|
|
145
|
+
Key: matches[2],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get a signed URL for a file in S3 given its S3 URI.
|
|
151
|
+
* Defaults to a 5 minute timeout.
|
|
152
|
+
*/
|
|
153
|
+
function getSignedUrl({ url, credentials, ...params }) {
|
|
154
|
+
var _a, _b;
|
|
155
|
+
const client = createS3Client(credentials);
|
|
156
|
+
const { Bucket, Key } = s3UriToParams(url);
|
|
157
|
+
const fileName = (_a = params.filename) !== null && _a !== void 0 ? _a : Key.split('/').pop();
|
|
158
|
+
const args = { Bucket, Key };
|
|
159
|
+
if (params === null || params === void 0 ? void 0 : params.download) {
|
|
160
|
+
args.ResponseContentDisposition = `attachment; filename="${fileName}"`;
|
|
161
|
+
}
|
|
162
|
+
if (params === null || params === void 0 ? void 0 : params.gzip) {
|
|
163
|
+
args.ResponseContentEncoding = 'gzip';
|
|
164
|
+
}
|
|
165
|
+
const command = new GetObjectCommand(args);
|
|
166
|
+
return getSignedUrl$1(client, command, { expiresIn: 60 * ((_b = params === null || params === void 0 ? void 0 : params.timeout) !== null && _b !== void 0 ? _b : 5) });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const resourcePrefix = 'project-';
|
|
170
|
+
function getResourceName(projectId) {
|
|
171
|
+
return `${resourcePrefix}${projectId}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get the S3 bucket name for a project.
|
|
176
|
+
* Will be deprecated in the future, use domain from manifest instead.
|
|
177
|
+
*/
|
|
178
|
+
function getProjectS3Bucket(projectId) {
|
|
179
|
+
return getResourceName(projectId);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Service for viewing files in Cirro
|
|
184
|
+
* currently this only operates on files within a project
|
|
185
|
+
*/
|
|
186
|
+
class FileService {
|
|
187
|
+
constructor(fileCredsApi) {
|
|
188
|
+
this.fileCredsApi = fileCredsApi;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get contents of a file
|
|
192
|
+
*/
|
|
193
|
+
async getProjectFile(file) {
|
|
194
|
+
const url = await this.getSignedUrlFromProjectFile(file);
|
|
195
|
+
return fetch(url);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get a signed URL for a file
|
|
199
|
+
*/
|
|
200
|
+
async getSignedUrlFromProjectFile(file, params) {
|
|
201
|
+
const credentials = await this.getProjectAccessCredentials(file.fileAccessContext);
|
|
202
|
+
const _params = {
|
|
203
|
+
...params,
|
|
204
|
+
filename: file.name,
|
|
205
|
+
url: file.url,
|
|
206
|
+
credentials,
|
|
207
|
+
};
|
|
208
|
+
return getSignedUrl(_params);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get a signed URL for a file given a path
|
|
212
|
+
*/
|
|
213
|
+
async getSignedUrlFromProjectPath(fileAccessContext, path, params) {
|
|
214
|
+
const credentials = await this.getProjectAccessCredentials(fileAccessContext);
|
|
215
|
+
const _params = {
|
|
216
|
+
...params,
|
|
217
|
+
filename: path.split('/').pop(),
|
|
218
|
+
url: `s3://${getProjectS3Bucket(fileAccessContext.project.id)}/${path}`,
|
|
219
|
+
credentials,
|
|
220
|
+
};
|
|
221
|
+
return getSignedUrl(_params);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get credentials for accessing a project file
|
|
225
|
+
*/
|
|
226
|
+
async getProjectAccessCredentials(fileAccessContext) {
|
|
227
|
+
const accessType = fileAccessContext.fileAccessRequest.accessType;
|
|
228
|
+
// Special case for project download, since we can cache the credentials
|
|
229
|
+
if (accessType === ProjectAccessType.ProjectDownload || accessType === ProjectAccessType.SharedDatasetDownload) {
|
|
230
|
+
return this.getProjectReadCredentials(fileAccessContext);
|
|
231
|
+
}
|
|
232
|
+
return this.fileCredsApi.generateProjectFileAccessToken({ projectId: fileAccessContext.project.id, projectFileAccessRequest: fileAccessContext.fileAccessRequest });
|
|
233
|
+
}
|
|
234
|
+
async getProjectReadCredentials(fileAccessContext) {
|
|
235
|
+
const projectId = fileAccessContext.project.id;
|
|
236
|
+
// Append datasetId to cache key for shared dataset downloads since we need to generate a new token for each dataset
|
|
237
|
+
let cacheKey = projectId;
|
|
238
|
+
if (fileAccessContext.fileAccessRequest.accessType === ProjectAccessType.SharedDatasetDownload) {
|
|
239
|
+
cacheKey = `${projectId}-${fileAccessContext.fileAccessRequest.datasetId}`;
|
|
240
|
+
}
|
|
241
|
+
return credentialsMutex.dispatch(async () => {
|
|
242
|
+
const cachedCredentials = credentialsCache.get(cacheKey);
|
|
243
|
+
const expirationTime = cachedCredentials ? cachedCredentials === null || cachedCredentials === void 0 ? void 0 : cachedCredentials.expiration : null;
|
|
244
|
+
const fetchNewCredentials = !expirationTime || expirationTime < new Date();
|
|
245
|
+
if (fetchNewCredentials) {
|
|
246
|
+
const projectFileAccessRequest = fileAccessContext.fileAccessRequest;
|
|
247
|
+
const credentials = await this.fileCredsApi.generateProjectFileAccessToken({ projectId, projectFileAccessRequest });
|
|
248
|
+
credentialsCache.set(cacheKey, credentials);
|
|
249
|
+
return credentials;
|
|
250
|
+
}
|
|
251
|
+
return cachedCredentials;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Delete a file from S3 given its URL and credentials.
|
|
258
|
+
*/
|
|
259
|
+
async function deleteFile({ url, credentials }) {
|
|
260
|
+
const { Bucket, Key } = s3UriToParams(url);
|
|
261
|
+
const cmd = new DeleteObjectCommand({ Bucket, Key });
|
|
262
|
+
await createS3Client(credentials).send(cmd);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Upload a file to S3
|
|
267
|
+
*/
|
|
268
|
+
function uploadFile({ bucket, path, file, credentials, metadata }) {
|
|
269
|
+
const params = {
|
|
270
|
+
Bucket: bucket,
|
|
271
|
+
Key: path,
|
|
272
|
+
Body: file,
|
|
273
|
+
ContentType: file.type,
|
|
274
|
+
Metadata: metadata,
|
|
275
|
+
};
|
|
276
|
+
return new Upload({
|
|
277
|
+
client: createS3Client(credentials),
|
|
278
|
+
queueSize: 4,
|
|
279
|
+
params,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Helper class to encapsulate the file access for a project.
|
|
285
|
+
*/
|
|
286
|
+
class ProjectFileAccessContext {
|
|
287
|
+
constructor(project, dataset, fileAccessRequest) {
|
|
288
|
+
this.project = project;
|
|
289
|
+
this.dataset = dataset;
|
|
290
|
+
this.fileAccessRequest = fileAccessRequest;
|
|
291
|
+
}
|
|
292
|
+
static projectDownload(project) {
|
|
293
|
+
const request = { accessType: ProjectAccessType.ProjectDownload };
|
|
294
|
+
return new ProjectFileAccessContext(project, null, request);
|
|
295
|
+
}
|
|
296
|
+
static datasetDownload(project, dataset) {
|
|
297
|
+
const accessType = dataset.share ? ProjectAccessType.SharedDatasetDownload : ProjectAccessType.ProjectDownload;
|
|
298
|
+
const request = { accessType, datasetId: dataset.id };
|
|
299
|
+
return new ProjectFileAccessContext(project, dataset, request);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
var FileSystemObjectType;
|
|
304
|
+
(function (FileSystemObjectType) {
|
|
305
|
+
FileSystemObjectType["FILE"] = "file";
|
|
306
|
+
FileSystemObjectType["FOLDER"] = "folder";
|
|
307
|
+
})(FileSystemObjectType || (FileSystemObjectType = {}));
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Calculate the total size of all files in a directory.
|
|
311
|
+
* @param files - An array of FileSystemObject representing the files in the directory.
|
|
312
|
+
* @return The total size of all files in bytes.
|
|
313
|
+
*/
|
|
314
|
+
function calculateTotalSize(files) {
|
|
315
|
+
let totalSize = 0;
|
|
316
|
+
for (const file of files) {
|
|
317
|
+
totalSize += file.size;
|
|
318
|
+
}
|
|
319
|
+
return totalSize;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Pretty prints a JSON object with the specified indentation level.
|
|
324
|
+
* @param obj JSON object to be pretty printed.
|
|
325
|
+
* @param indentLevel Number of spaces to use for indentation.
|
|
326
|
+
*/
|
|
327
|
+
function jsonPrettyPrint(obj, indentLevel = 2) {
|
|
328
|
+
return JSON.stringify(obj, null, indentLevel);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Format bytes as human-readable text.
|
|
333
|
+
* @param bytes Number of bytes.
|
|
334
|
+
* @param si True to use metric(SI) units, aka powers of 1000. False to use
|
|
335
|
+
* binary(IEC), aka powers of 1024.
|
|
336
|
+
* @param dp Number of decimal places to display.
|
|
337
|
+
*
|
|
338
|
+
* @return Formatted string.
|
|
339
|
+
*/
|
|
340
|
+
function bytesToString(bytes, si = false, dp = 1) {
|
|
341
|
+
const thresh = si ? 1000 : 1024;
|
|
342
|
+
if (Math.abs(bytes) < thresh) {
|
|
343
|
+
return `${bytes} B`;
|
|
344
|
+
}
|
|
345
|
+
const units = si
|
|
346
|
+
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
347
|
+
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
348
|
+
let u = -1;
|
|
349
|
+
const r = 10 ** dp;
|
|
350
|
+
do {
|
|
351
|
+
bytes /= thresh;
|
|
352
|
+
++u;
|
|
353
|
+
} while (Math.round(Math.abs(bytes) * r) / r >= thresh
|
|
354
|
+
&& u < units.length - 1);
|
|
355
|
+
return `${bytes.toFixed(dp)} ${units[u]}`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Normalizes a date to UTC by adjusting for the timezone offset.
|
|
360
|
+
* Useful when you are working with dates that may not include time information.
|
|
361
|
+
* @param date
|
|
362
|
+
*/
|
|
363
|
+
function normalizeDate(date) {
|
|
364
|
+
if (!date)
|
|
365
|
+
throw new Error("Attempt to normalize undefined");
|
|
366
|
+
if (!(date instanceof Date))
|
|
367
|
+
date = new Date(date);
|
|
368
|
+
return new Date(date.getTime() - date.getTimezoneOffset() * -6e4);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Normalize a string by trimming it and returning null if it is empty.
|
|
373
|
+
* @param input The string to normalize.
|
|
374
|
+
*/
|
|
375
|
+
function normalizeString(input) {
|
|
376
|
+
if (!(input === null || input === void 0 ? void 0 : input.trim()))
|
|
377
|
+
return null;
|
|
378
|
+
return input.trim();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Removes the starting slash from a path.
|
|
383
|
+
* @param path Input path
|
|
384
|
+
*/
|
|
385
|
+
function removeStartingSlash(path) {
|
|
386
|
+
return (path === null || path === void 0 ? void 0 : path.startsWith('/')) ? path.substring(1) : path;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Removes the ending slash from a path.
|
|
390
|
+
* @param path Input path
|
|
391
|
+
*/
|
|
392
|
+
function removeEndingSlash(path) {
|
|
393
|
+
return (path === null || path === void 0 ? void 0 : path.endsWith('/')) ? path.slice(0, -1) : path;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const dateFormat = new Intl.DateTimeFormat('en-US', { month: '2-digit', day: '2-digit', year: '2-digit' });
|
|
397
|
+
/**
|
|
398
|
+
* Converts a date string or Date object to MM/DD/YY date string.
|
|
399
|
+
* Ex., "2023-10-01" -> "10/01/23"
|
|
400
|
+
* @param date Input date string or Date object.
|
|
401
|
+
*/
|
|
402
|
+
function toDateFormat(date) {
|
|
403
|
+
if (!date)
|
|
404
|
+
return null;
|
|
405
|
+
const d = (typeof date === 'string') ? new Date(date) : date;
|
|
406
|
+
return dateFormat.format(d);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Converts a string to Title case.
|
|
411
|
+
* Ex., "hello world" -> "Hello world"
|
|
412
|
+
* @param value
|
|
413
|
+
*/
|
|
414
|
+
function toTitleCase(value) {
|
|
415
|
+
if (!value)
|
|
416
|
+
return null;
|
|
417
|
+
return value[0].toUpperCase() + value.slice(1).toLowerCase();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Converts a field name to a more human-readable format.
|
|
422
|
+
* Ex., "helloWorld" -> "Hello World"
|
|
423
|
+
* @param value The value to convert.
|
|
424
|
+
*/
|
|
425
|
+
function toFriendlyName(value) {
|
|
426
|
+
let _value = value.split(/(?=[A-Z])/).join(' ');
|
|
427
|
+
_value = _value.split('_').join(' ');
|
|
428
|
+
_value = _value.split('|').join(' ');
|
|
429
|
+
_value = _value.replace(/ +/g, ' ');
|
|
430
|
+
return toTitleCase(_value);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const money = new Intl.NumberFormat('en-US', {
|
|
434
|
+
style: 'currency',
|
|
435
|
+
currency: 'USD',
|
|
436
|
+
});
|
|
437
|
+
/**
|
|
438
|
+
* Converts a number or bigint to a formatted money string.
|
|
439
|
+
* Ex., 1234.56 -> $1,234.56
|
|
440
|
+
* @param value Input number or bigint value.
|
|
441
|
+
*/
|
|
442
|
+
function toMoney(value) {
|
|
443
|
+
return money.format(value);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Converts a string to PascalCase.
|
|
448
|
+
* Ex., "hello world" -> "HelloWorld"
|
|
449
|
+
* @param value The string to convert.
|
|
450
|
+
*/
|
|
451
|
+
function toPascalCase(value) {
|
|
452
|
+
return value.replace(/(\w)(\w*)/g, (g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase());
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
class Assets extends Array {
|
|
456
|
+
// Total size of all files in human-readable format
|
|
457
|
+
get totalSize() {
|
|
458
|
+
if (!this._totalSize) {
|
|
459
|
+
this.calculateSize();
|
|
460
|
+
}
|
|
461
|
+
return this._totalSize;
|
|
462
|
+
}
|
|
463
|
+
get totalSizeBytes() {
|
|
464
|
+
if (!this._totalSizeBytes) {
|
|
465
|
+
this.calculateSize();
|
|
466
|
+
}
|
|
467
|
+
return this._totalSizeBytes;
|
|
468
|
+
}
|
|
469
|
+
calculateSize() {
|
|
470
|
+
this._totalSizeBytes = calculateTotalSize(this);
|
|
471
|
+
this._totalSize = bytesToString(this._totalSizeBytes);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
class Folder {
|
|
476
|
+
constructor(url, name, path, domain) {
|
|
477
|
+
this.kind = 'Folder';
|
|
478
|
+
this.fileAccessContext = null;
|
|
479
|
+
this.size = 0;
|
|
480
|
+
this.type = FileSystemObjectType.FOLDER;
|
|
481
|
+
this.id = v4();
|
|
482
|
+
this.url = url;
|
|
483
|
+
this.name = name;
|
|
484
|
+
this.path = path;
|
|
485
|
+
this.domain = domain;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
class File {
|
|
490
|
+
get type() {
|
|
491
|
+
return FileSystemObjectType.FILE;
|
|
492
|
+
}
|
|
493
|
+
get kind() {
|
|
494
|
+
return this.path.slice(this.
|
|
495
|
+
path.lastIndexOf('.') + 1);
|
|
496
|
+
}
|
|
497
|
+
constructor(domain, file, size, metadata, accessContext) {
|
|
498
|
+
this.id = v4();
|
|
499
|
+
this.url = `${domain}/${file}`;
|
|
500
|
+
this.size = size;
|
|
501
|
+
this.metadata = metadata;
|
|
502
|
+
this.fileAccessContext = accessContext;
|
|
503
|
+
const fileFolderIdx = file.lastIndexOf('/');
|
|
504
|
+
if (fileFolderIdx === -1) {
|
|
505
|
+
this.path = '';
|
|
506
|
+
this.name = file;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
this.path = file.substring(0, fileFolderIdx);
|
|
510
|
+
this.name = file.substring(fileFolderIdx + 1);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
class ManifestParser {
|
|
516
|
+
constructor(manifest, accessContext) {
|
|
517
|
+
this.manifest = manifest;
|
|
518
|
+
this.accessContext = accessContext;
|
|
519
|
+
}
|
|
520
|
+
get files() {
|
|
521
|
+
var _a, _b;
|
|
522
|
+
return (_b = (_a = this.manifest) === null || _a === void 0 ? void 0 : _a.files) !== null && _b !== void 0 ? _b : [];
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Generates asset objects from the manifest, optionally including folder entries.
|
|
526
|
+
*/
|
|
527
|
+
generateAssets(generateFolders = false) {
|
|
528
|
+
var _a;
|
|
529
|
+
const domain = removeEndingSlash((_a = this.manifest) === null || _a === void 0 ? void 0 : _a.domain);
|
|
530
|
+
const assets = new Assets();
|
|
531
|
+
for (const file of this.files) {
|
|
532
|
+
assets.push(new File(domain, file.path, file.size, file.metadata, this.accessContext));
|
|
533
|
+
}
|
|
534
|
+
// Optionally add folder entries
|
|
535
|
+
if (generateFolders) {
|
|
536
|
+
assets.push(...this.generateFolderAssets(assets, domain));
|
|
537
|
+
}
|
|
538
|
+
return assets;
|
|
539
|
+
}
|
|
540
|
+
// Create folder entries, useful for visualizing folders as items in a file browser
|
|
541
|
+
generateFolderAssets(assets, domain) {
|
|
542
|
+
const folderSet = new Set();
|
|
543
|
+
// Collect all unique folder paths
|
|
544
|
+
for (const asset of assets) {
|
|
545
|
+
const parts = asset.path.split('/').filter(p => p.length > 0);
|
|
546
|
+
let currentPath = '';
|
|
547
|
+
for (const part of parts) {
|
|
548
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
549
|
+
folderSet.add(currentPath);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Create Folder objects from unique paths
|
|
553
|
+
return Array.from(folderSet)
|
|
554
|
+
.map(folderPath => {
|
|
555
|
+
const name = folderPath.substring(folderPath.lastIndexOf('/') + 1);
|
|
556
|
+
const parentPath = folderPath.includes('/') ? folderPath.substring(0, folderPath.lastIndexOf('/')) : '';
|
|
557
|
+
return new Folder(folderPath, name, parentPath, domain);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Returns the parent directory of a given file path.
|
|
564
|
+
*/
|
|
565
|
+
function getParentPath(path) {
|
|
566
|
+
var _a;
|
|
567
|
+
return (_a = path.split("/").slice(0, -1).join("/")) !== null && _a !== void 0 ? _a : "";
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
class ApiError extends Error {
|
|
571
|
+
constructor(message, errors) {
|
|
572
|
+
super(message);
|
|
573
|
+
this.errors = errors;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
class PortalErrorHandler {
|
|
578
|
+
async post(context) {
|
|
579
|
+
const { response } = context;
|
|
580
|
+
if (response && (response.status >= 200 && response.status < 300)) {
|
|
581
|
+
return response;
|
|
582
|
+
}
|
|
583
|
+
// Handle Error
|
|
584
|
+
let errorMessage;
|
|
585
|
+
const errors = [];
|
|
586
|
+
try {
|
|
587
|
+
const err = await response.json();
|
|
588
|
+
console.warn(err);
|
|
589
|
+
if ('errorDetail' in err) {
|
|
590
|
+
errorMessage = err.errorDetail;
|
|
591
|
+
errors.push(err.errors.map((e) => e.message));
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
errorMessage = err.message;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
catch (ignore) {
|
|
598
|
+
errorMessage = "Unknown Error";
|
|
599
|
+
}
|
|
600
|
+
throw new ApiError(errorMessage, errors);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const generateApiConfig = ({ basePath = "/api", tokenGetter }) => {
|
|
605
|
+
return new Configuration({
|
|
606
|
+
basePath,
|
|
607
|
+
accessToken: tokenGetter,
|
|
608
|
+
middleware: [
|
|
609
|
+
new PortalErrorHandler()
|
|
610
|
+
],
|
|
611
|
+
});
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
class CurrentUser {
|
|
615
|
+
static fromCognitoUser(idTokenPayload) {
|
|
616
|
+
var _a, _b;
|
|
617
|
+
return {
|
|
618
|
+
username: (_a = idTokenPayload["cognito:username"]) !== null && _a !== void 0 ? _a : idTokenPayload.username,
|
|
619
|
+
sub: idTokenPayload.sub,
|
|
620
|
+
authTime: new Date(idTokenPayload.auth_time * 1000),
|
|
621
|
+
groups: (_b = idTokenPayload["cognito:groups"]) !== null && _b !== void 0 ? _b : [],
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
class StaticTokenAuthProvider {
|
|
627
|
+
constructor(token) {
|
|
628
|
+
this.token = token;
|
|
629
|
+
}
|
|
630
|
+
configure(_ignore) {
|
|
631
|
+
console.log('StaticTokenAuthProvider loaded');
|
|
632
|
+
}
|
|
633
|
+
async getAccessToken() {
|
|
634
|
+
return this.token;
|
|
635
|
+
}
|
|
636
|
+
async getCurrentUser() {
|
|
637
|
+
const accessToken = await this.getAccessToken();
|
|
638
|
+
const parsedToken = JSON.parse(atob(accessToken.split('.')[1]));
|
|
639
|
+
return CurrentUser.fromCognitoUser(parsedToken);
|
|
640
|
+
}
|
|
641
|
+
forceRefresh() {
|
|
642
|
+
return Promise.resolve();
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
class DataService {
|
|
647
|
+
constructor({ tokenGetter, basePath = "/api" }) {
|
|
648
|
+
this.apiConfig = generateApiConfig({ basePath, tokenGetter });
|
|
649
|
+
this._auditApi = new AuditApi(this.apiConfig);
|
|
650
|
+
this._billingApi = new BillingApi(this.apiConfig);
|
|
651
|
+
this._computeEnvironmentApi = new ComputeEnvironmentApi(this.apiConfig);
|
|
652
|
+
this._datasetsApi = new DatasetsApi(this.apiConfig);
|
|
653
|
+
this._executionApi = new ExecutionApi(this.apiConfig);
|
|
654
|
+
this._fileApi = new FileApi(this.apiConfig);
|
|
655
|
+
this._governanceApi = new GovernanceApi(this.apiConfig);
|
|
656
|
+
this._metadataApi = new MetadataApi(this.apiConfig);
|
|
657
|
+
this._notebooksApi = new NotebooksApi(this.apiConfig);
|
|
658
|
+
this._processesApi = new ProcessesApi(this.apiConfig);
|
|
659
|
+
this._projectRequestsApi = new ProjectRequestsApi(this.apiConfig);
|
|
660
|
+
this._projectsApi = new ProjectsApi(this.apiConfig);
|
|
661
|
+
this._referencesApi = new ReferencesApi(this.apiConfig);
|
|
662
|
+
this._sharingApi = new SharingApi(this.apiConfig);
|
|
663
|
+
this._systemApi = new SystemApi(this.apiConfig);
|
|
664
|
+
this._toolsApi = new ToolsApi(this.apiConfig);
|
|
665
|
+
this._usersApi = new UsersApi(this.apiConfig);
|
|
666
|
+
}
|
|
667
|
+
get audit() {
|
|
668
|
+
return this._auditApi;
|
|
669
|
+
}
|
|
670
|
+
get billing() {
|
|
671
|
+
return this._billingApi;
|
|
672
|
+
}
|
|
673
|
+
get computeEnvironments() {
|
|
674
|
+
return this._computeEnvironmentApi;
|
|
675
|
+
}
|
|
676
|
+
get datasets() {
|
|
677
|
+
return this._datasetsApi;
|
|
678
|
+
}
|
|
679
|
+
get execution() {
|
|
680
|
+
return this._executionApi;
|
|
681
|
+
}
|
|
682
|
+
get file() {
|
|
683
|
+
return this._fileApi;
|
|
684
|
+
}
|
|
685
|
+
get governance() {
|
|
686
|
+
return this._governanceApi;
|
|
687
|
+
}
|
|
688
|
+
get metadata() {
|
|
689
|
+
return this._metadataApi;
|
|
690
|
+
}
|
|
691
|
+
get notebooks() {
|
|
692
|
+
return this._notebooksApi;
|
|
693
|
+
}
|
|
694
|
+
get processes() {
|
|
695
|
+
return this._processesApi;
|
|
696
|
+
}
|
|
697
|
+
get projectRequests() {
|
|
698
|
+
return this._projectRequestsApi;
|
|
699
|
+
}
|
|
700
|
+
get projects() {
|
|
701
|
+
return this._projectsApi;
|
|
702
|
+
}
|
|
703
|
+
get references() {
|
|
704
|
+
return this._referencesApi;
|
|
705
|
+
}
|
|
706
|
+
get sharing() {
|
|
707
|
+
return this._sharingApi;
|
|
708
|
+
}
|
|
709
|
+
get system() {
|
|
710
|
+
return this._systemApi;
|
|
711
|
+
}
|
|
712
|
+
get tools() {
|
|
713
|
+
return this._toolsApi;
|
|
714
|
+
}
|
|
715
|
+
get users() {
|
|
716
|
+
return this._usersApi;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Extracts a value from an object using the json path notation, i.e. $.test.id
|
|
722
|
+
* @param path JSON path to the value
|
|
723
|
+
* @param obj Object to extract the value from
|
|
724
|
+
*/
|
|
725
|
+
function extractFromObject(path, obj) {
|
|
726
|
+
const pathParts = path.slice(2).split('.');
|
|
727
|
+
return pathParts.reduce(function (o, k) {
|
|
728
|
+
return o && o[k];
|
|
729
|
+
}, obj);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Ignores dangling promise rejections.
|
|
734
|
+
* @param err The error to ignore.
|
|
735
|
+
*/
|
|
736
|
+
function handlePromiseError(err) {
|
|
737
|
+
console.warn(err);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Download a blob as a file with the specified file name.
|
|
742
|
+
*/
|
|
743
|
+
function downloadBlob(blob, fileName) {
|
|
744
|
+
const url = window.URL.createObjectURL(blob);
|
|
745
|
+
const a = document.createElement('a');
|
|
746
|
+
a.href = url;
|
|
747
|
+
a.download = fileName;
|
|
748
|
+
a.click();
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Downloads the specified content as a file with the specified file name and type.
|
|
752
|
+
*/
|
|
753
|
+
function downloadContent(content, fileName, fileType = 'text/plain') {
|
|
754
|
+
const blob = new Blob([content], { type: fileType });
|
|
755
|
+
downloadBlob(blob, fileName);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export { ApiError, Assets, CurrentUser, DataService, FILE_BROWSER_EXTENSIONS, FILE_DSV_EXTENSIONS, FILE_EXTENSIONS_TO_OPEN, FILE_IMAGE_EXTENSIONS, FILE_OME_EXTENSIONS, FILE_PROTEIN_STRUCTURE_EXTENSIONS, FILE_TRACK_ALIGNMENTS, FILE_TRACK_ANNOTATION, FILE_TRACK_EXTENSIONS, FILE_TRACK_EXTENSIONS_NO_INDEX, FILE_TRACK_INDEX_EXTENSIONS, FILE_TRACK_SEG, FILE_TRACK_VARIANT, FILE_TRACK_WIG, FILE_TXT_EXTENSIONS, FILE_TXT_GENOMICS_EXTENSIONS, FILE_VITESSCE_EXTENSIONS, FileService, FileSystemObjectType, ManifestParser, ProjectFileAccessContext, StaticTokenAuthProvider, bytesToString, calculateTotalSize, createS3Client, credentialsCache, credentialsMutex, deleteFile, downloadBlob, downloadContent, extractFromObject, generateApiConfig, getParentPath, getProjectS3Bucket, getResourceName, getSignedUrl, handlePromiseError, jsonPrettyPrint, matchesExtension, normalizeDate, normalizeString, removeEndingSlash, removeStartingSlash, s3UriToParams, toDateFormat, toFriendlyName, toMoney, toPascalCase, toTitleCase, uploadFile };
|
|
759
|
+
//# sourceMappingURL=index.esm.js.map
|