@naturalcycles/cloud-storage-lib 1.13.3 → 2.0.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/dist/cloudStorage.d.ts +5 -5
- package/dist/cloudStorage.js +24 -26
- package/dist/commonStorage.js +1 -2
- package/dist/commonStorageBucket.d.ts +3 -3
- package/dist/commonStorageBucket.js +10 -13
- package/dist/commonStorageKeyValueDB.d.ts +8 -9
- package/dist/commonStorageKeyValueDB.js +12 -15
- package/dist/inMemoryCommonStorage.d.ts +3 -3
- package/dist/inMemoryCommonStorage.js +21 -27
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -10
- package/dist/model.js +1 -2
- package/dist/testing/commonStorageTest.d.ts +1 -1
- package/dist/testing/commonStorageTest.js +12 -14
- package/package.json +13 -10
- package/src/cloudStorage.ts +12 -16
- package/src/commonStorageBucket.ts +3 -3
- package/src/commonStorageKeyValueDB.ts +16 -14
- package/src/inMemoryCommonStorage.ts +4 -11
- package/src/index.ts +7 -7
- package/src/testing/commonStorageTest.ts +4 -2
package/dist/cloudStorage.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Storage, StorageOptions } from '@google-cloud/storage';
|
|
2
|
-
import { CommonLogger, LocalTimeInput } from '@naturalcycles/js-lib';
|
|
2
|
+
import type { CommonLogger, LocalTimeInput } from '@naturalcycles/js-lib';
|
|
3
3
|
import type { ReadableBinary, ReadableTyped, WritableBinary } from '@naturalcycles/nodejs-lib';
|
|
4
|
-
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage';
|
|
5
|
-
import type { GCPServiceAccount } from './model';
|
|
4
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js';
|
|
5
|
+
import type { GCPServiceAccount } from './model.js';
|
|
6
6
|
export type { Storage, StorageOptions, };
|
|
7
7
|
/**
|
|
8
8
|
* This object is intentionally made to NOT extend StorageOptions,
|
|
@@ -34,8 +34,8 @@ export declare class CloudStorage implements CommonStorage {
|
|
|
34
34
|
cfg: CloudStorageCfg & {
|
|
35
35
|
logger: CommonLogger;
|
|
36
36
|
};
|
|
37
|
-
static createFromGCPServiceAccount(credentials?: GCPServiceAccount, cfg?: CloudStorageCfg): CloudStorage
|
|
38
|
-
static createFromStorageOptions(storageOptions?: StorageOptions, cfg?: CloudStorageCfg): CloudStorage
|
|
37
|
+
static createFromGCPServiceAccount(credentials?: GCPServiceAccount, cfg?: CloudStorageCfg): Promise<CloudStorage>;
|
|
38
|
+
static createFromStorageOptions(storageOptions?: StorageOptions, cfg?: CloudStorageCfg): Promise<CloudStorage>;
|
|
39
39
|
/**
|
|
40
40
|
* Passing the pre-created Storage allows to instantiate it from both
|
|
41
41
|
* GCP Storage and FirebaseStorage.
|
package/dist/cloudStorage.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CloudStorage = void 0;
|
|
4
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
1
|
+
import { _assert, _chunk, _since, _substringAfterLast, localTime, pMap, SKIP, } from '@naturalcycles/js-lib';
|
|
5
2
|
const MAX_RECURSION_DEPTH = 10;
|
|
6
3
|
const BATCH_SIZE = 32;
|
|
7
4
|
/**
|
|
@@ -9,7 +6,8 @@ const BATCH_SIZE = 32;
|
|
|
9
6
|
*
|
|
10
7
|
* API: https://googleapis.dev/nodejs/storage/latest/index.html
|
|
11
8
|
*/
|
|
12
|
-
class CloudStorage {
|
|
9
|
+
export class CloudStorage {
|
|
10
|
+
storage;
|
|
13
11
|
constructor(storage, cfg = {}) {
|
|
14
12
|
this.storage = storage;
|
|
15
13
|
this.cfg = {
|
|
@@ -17,9 +15,10 @@ class CloudStorage {
|
|
|
17
15
|
...cfg,
|
|
18
16
|
};
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
18
|
+
cfg;
|
|
19
|
+
static async createFromGCPServiceAccount(credentials, cfg) {
|
|
20
|
+
const { Storage } = await import('@google-cloud/storage');
|
|
21
|
+
const storage = new Storage({
|
|
23
22
|
credentials,
|
|
24
23
|
// Explicitly passing it here to fix this error:
|
|
25
24
|
// Error: Unable to detect a Project Id in the current environment.
|
|
@@ -30,9 +29,9 @@ class CloudStorage {
|
|
|
30
29
|
});
|
|
31
30
|
return new CloudStorage(storage, cfg);
|
|
32
31
|
}
|
|
33
|
-
static createFromStorageOptions(storageOptions, cfg) {
|
|
34
|
-
const
|
|
35
|
-
const storage = new
|
|
32
|
+
static async createFromStorageOptions(storageOptions, cfg) {
|
|
33
|
+
const { Storage } = await import('@google-cloud/storage');
|
|
34
|
+
const storage = new Storage(storageOptions);
|
|
36
35
|
return new CloudStorage(storage, cfg);
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
@@ -50,7 +49,7 @@ class CloudStorage {
|
|
|
50
49
|
}
|
|
51
50
|
async deletePaths(bucketName, prefixes) {
|
|
52
51
|
const bucket = this.storage.bucket(bucketName);
|
|
53
|
-
await
|
|
52
|
+
await pMap(prefixes, async (prefix) => {
|
|
54
53
|
await bucket.deleteFiles({
|
|
55
54
|
prefix,
|
|
56
55
|
// to keep going in case error occurs, similar to THROW_AGGREGATED
|
|
@@ -72,7 +71,7 @@ class CloudStorage {
|
|
|
72
71
|
// It doesn't make sense to return or do anything with them
|
|
73
72
|
return files.map(f => f.name).filter(s => !s.endsWith('/'));
|
|
74
73
|
}
|
|
75
|
-
return files.map(f =>
|
|
74
|
+
return files.map(f => _substringAfterLast(f.name, '/')).filter(Boolean);
|
|
76
75
|
}
|
|
77
76
|
getFileNamesStream(bucketName, opt = {}) {
|
|
78
77
|
const { prefix, fullPaths = true } = opt;
|
|
@@ -81,7 +80,7 @@ class CloudStorage {
|
|
|
81
80
|
maxResults: opt.limit || undefined,
|
|
82
81
|
}).flatMap(f => {
|
|
83
82
|
const r = this.normalizeFilename(f.name, fullPaths);
|
|
84
|
-
if (r ===
|
|
83
|
+
if (r === SKIP)
|
|
85
84
|
return [];
|
|
86
85
|
return [r];
|
|
87
86
|
});
|
|
@@ -93,7 +92,7 @@ class CloudStorage {
|
|
|
93
92
|
maxResults: opt.limit || undefined,
|
|
94
93
|
}).flatMap(async (f) => {
|
|
95
94
|
const filePath = this.normalizeFilename(f.name, fullPaths);
|
|
96
|
-
if (filePath ===
|
|
95
|
+
if (filePath === SKIP)
|
|
97
96
|
return [];
|
|
98
97
|
const [content] = await f.download();
|
|
99
98
|
return [{ filePath, content }];
|
|
@@ -151,8 +150,8 @@ class CloudStorage {
|
|
|
151
150
|
.move(this.storage.bucket(toBucket || fromBucket).file(toPath));
|
|
152
151
|
}
|
|
153
152
|
async movePath(fromBucket, fromPrefix, toPrefix, toBucket) {
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
_assert(fromPrefix.endsWith('/'), 'fromPrefix should end with `/`');
|
|
154
|
+
_assert(toPrefix.endsWith('/'), 'toPrefix should end with `/`');
|
|
156
155
|
await this.storage
|
|
157
156
|
.bucket(fromBucket)
|
|
158
157
|
.getFilesStream({
|
|
@@ -165,12 +164,12 @@ class CloudStorage {
|
|
|
165
164
|
});
|
|
166
165
|
}
|
|
167
166
|
async deleteFiles(bucketName, filePaths) {
|
|
168
|
-
await
|
|
167
|
+
await pMap(filePaths, async (filePath) => {
|
|
169
168
|
await this.storage.bucket(bucketName).file(filePath).delete();
|
|
170
169
|
});
|
|
171
170
|
}
|
|
172
171
|
async combineFiles(bucketName, filePaths, toPath, toBucket, currentRecursionDepth = 0) {
|
|
173
|
-
|
|
172
|
+
_assert(currentRecursionDepth <= MAX_RECURSION_DEPTH, `combineFiles reached max recursion depth of ${MAX_RECURSION_DEPTH}`);
|
|
174
173
|
const { logger, debug } = this.cfg;
|
|
175
174
|
if (filePaths.length === 0) {
|
|
176
175
|
if (debug) {
|
|
@@ -193,7 +192,7 @@ class CloudStorage {
|
|
|
193
192
|
return;
|
|
194
193
|
}
|
|
195
194
|
const started = Date.now();
|
|
196
|
-
await
|
|
195
|
+
await pMap(_chunk(filePaths, BATCH_SIZE), async (fileBatch, i) => {
|
|
197
196
|
if (debug) {
|
|
198
197
|
logger.log(`[${currentRecursionDepth}] Composing batch ${i + 1}...`);
|
|
199
198
|
}
|
|
@@ -205,7 +204,7 @@ class CloudStorage {
|
|
|
205
204
|
await this.deleteFiles(bucketName, fileBatch);
|
|
206
205
|
});
|
|
207
206
|
if (debug) {
|
|
208
|
-
logger.log(`[${currentRecursionDepth}] Batch composed into ${intermediateFiles.length} files, in ${
|
|
207
|
+
logger.log(`[${currentRecursionDepth}] Batch composed into ${intermediateFiles.length} files, in ${_since(started)}`);
|
|
209
208
|
}
|
|
210
209
|
await this.combineFiles(toBucket || bucketName, intermediateFiles, toPath, toBucket, currentRecursionDepth + 1);
|
|
211
210
|
}
|
|
@@ -227,7 +226,7 @@ class CloudStorage {
|
|
|
227
226
|
.getSignedUrl({
|
|
228
227
|
action: 'read',
|
|
229
228
|
version: 'v4',
|
|
230
|
-
expires:
|
|
229
|
+
expires: localTime(expires).unixMillis,
|
|
231
230
|
});
|
|
232
231
|
return url;
|
|
233
232
|
}
|
|
@@ -238,11 +237,10 @@ class CloudStorage {
|
|
|
238
237
|
normalizeFilename(fileName, fullPaths) {
|
|
239
238
|
if (fullPaths) {
|
|
240
239
|
if (fileName.endsWith('/'))
|
|
241
|
-
return
|
|
240
|
+
return SKIP; // skip folders
|
|
242
241
|
return fileName;
|
|
243
242
|
}
|
|
244
|
-
fileName =
|
|
245
|
-
return fileName ||
|
|
243
|
+
fileName = _substringAfterLast(fileName, '/');
|
|
244
|
+
return fileName || SKIP; // skip folders
|
|
246
245
|
}
|
|
247
246
|
}
|
|
248
|
-
exports.CloudStorage = CloudStorage;
|
package/dist/commonStorage.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Readable, Writable } from 'node:stream';
|
|
2
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage';
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js';
|
|
4
4
|
export interface CommonStorageBucketCfg {
|
|
5
5
|
storage: CommonStorage;
|
|
6
6
|
bucketName: string;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CommonStorageBucket = void 0;
|
|
4
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
1
|
+
import { AppError, pMap } from '@naturalcycles/js-lib';
|
|
5
2
|
/**
|
|
6
3
|
* Convenience wrapper around CommonStorage for a given Bucket.
|
|
7
4
|
*
|
|
8
5
|
* Similar to what CommonDao is to CommonDB.
|
|
9
6
|
*/
|
|
10
|
-
class CommonStorageBucket {
|
|
7
|
+
export class CommonStorageBucket {
|
|
8
|
+
cfg;
|
|
11
9
|
constructor(cfg) {
|
|
12
10
|
this.cfg = cfg;
|
|
13
11
|
}
|
|
@@ -45,27 +43,27 @@ class CommonStorageBucket {
|
|
|
45
43
|
return v ?? this.throwRequiredError(filePath);
|
|
46
44
|
}
|
|
47
45
|
throwRequiredError(filePath) {
|
|
48
|
-
throw new
|
|
46
|
+
throw new AppError(`File required, but not found: ${this.cfg.bucketName}/${filePath}`, {
|
|
49
47
|
code: 'FILE_REQUIRED',
|
|
50
48
|
});
|
|
51
49
|
}
|
|
52
50
|
async getFileContents(paths) {
|
|
53
|
-
return (await
|
|
51
|
+
return (await pMap(paths, async (filePath) => (await this.cfg.storage.getFile(this.cfg.bucketName, filePath)))).filter(Boolean);
|
|
54
52
|
}
|
|
55
53
|
async getFileContentsAsJson(paths) {
|
|
56
|
-
return (await
|
|
54
|
+
return (await pMap(paths, async (filePath) => {
|
|
57
55
|
const buf = await this.cfg.storage.getFile(this.cfg.bucketName, filePath);
|
|
58
56
|
return buf ? JSON.parse(buf.toString()) : null;
|
|
59
57
|
})).filter(Boolean);
|
|
60
58
|
}
|
|
61
59
|
async getFileEntries(paths) {
|
|
62
|
-
return (await
|
|
60
|
+
return (await pMap(paths, async (filePath) => {
|
|
63
61
|
const content = await this.cfg.storage.getFile(this.cfg.bucketName, filePath);
|
|
64
62
|
return { filePath, content: content };
|
|
65
63
|
})).filter(f => f.content);
|
|
66
64
|
}
|
|
67
65
|
async getFileEntriesAsJson(paths) {
|
|
68
|
-
return (await
|
|
66
|
+
return (await pMap(paths, async (filePath) => {
|
|
69
67
|
const buf = await this.cfg.storage.getFile(this.cfg.bucketName, filePath);
|
|
70
68
|
return buf ? { filePath, content: JSON.parse(buf.toString()) } : null;
|
|
71
69
|
})).filter(Boolean);
|
|
@@ -89,7 +87,7 @@ class CommonStorageBucket {
|
|
|
89
87
|
await this.cfg.storage.saveFile(this.cfg.bucketName, filePath, Buffer.from(JSON.stringify(content)));
|
|
90
88
|
}
|
|
91
89
|
async saveFiles(entries) {
|
|
92
|
-
await
|
|
90
|
+
await pMap(entries, async (f) => {
|
|
93
91
|
await this.cfg.storage.saveFile(this.cfg.bucketName, f.filePath, f.content);
|
|
94
92
|
});
|
|
95
93
|
}
|
|
@@ -100,7 +98,7 @@ class CommonStorageBucket {
|
|
|
100
98
|
return await this.cfg.storage.deletePath(this.cfg.bucketName, prefix);
|
|
101
99
|
}
|
|
102
100
|
async deletePaths(prefixes) {
|
|
103
|
-
await
|
|
101
|
+
await pMap(prefixes, async (prefix) => {
|
|
104
102
|
return await this.cfg.storage.deletePath(this.cfg.bucketName, prefix);
|
|
105
103
|
});
|
|
106
104
|
}
|
|
@@ -143,4 +141,3 @@ class CommonStorageBucket {
|
|
|
143
141
|
await this.cfg.storage.moveFile(this.cfg.bucketName, fromPath, toPath, toBucket);
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
|
-
exports.CommonStorageBucket = CommonStorageBucket;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { CommonDBCreateOptions, CommonKeyValueDB } from '@naturalcycles/db-lib';
|
|
2
|
-
import { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { CommonStorage } from './commonStorage';
|
|
1
|
+
import type { CommonDBCreateOptions, CommonKeyValueDB, KeyValueDBTuple } from '@naturalcycles/db-lib';
|
|
2
|
+
import type { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB.js';
|
|
3
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
4
|
+
import type { CommonStorage } from './commonStorage.js';
|
|
6
5
|
export interface CommonStorageKeyValueDBCfg {
|
|
7
6
|
storage: CommonStorage;
|
|
8
7
|
bucketName: string;
|
|
@@ -29,11 +28,11 @@ export declare class CommonStorageKeyValueDB implements CommonKeyValueDB {
|
|
|
29
28
|
*/
|
|
30
29
|
private getBucketAndPrefix;
|
|
31
30
|
deleteByIds(table: string, ids: string[]): Promise<void>;
|
|
32
|
-
getByIds
|
|
33
|
-
saveBatch
|
|
31
|
+
getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]>;
|
|
32
|
+
saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void>;
|
|
34
33
|
streamIds(table: string, limit?: number): ReadableTyped<string>;
|
|
35
|
-
streamValues
|
|
36
|
-
streamEntries
|
|
34
|
+
streamValues(table: string, limit?: number): ReadableTyped<Buffer>;
|
|
35
|
+
streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple>;
|
|
37
36
|
count(table: string): Promise<number>;
|
|
38
37
|
incrementBatch(_table: string, _entries: IncrementTuple[]): Promise<IncrementTuple[]>;
|
|
39
38
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.CommonStorageKeyValueDB = void 0;
|
|
4
|
-
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
5
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
1
|
+
import { commonKeyValueDBFullSupport } from '@naturalcycles/db-lib';
|
|
2
|
+
import { AppError, pMap } from '@naturalcycles/js-lib';
|
|
6
3
|
/**
|
|
7
4
|
* CommonKeyValueDB, backed up by a CommonStorage implementation.
|
|
8
5
|
*
|
|
@@ -11,14 +8,15 @@ const js_lib_1 = require("@naturalcycles/js-lib");
|
|
|
11
8
|
* fileName is ${id} (without extension)
|
|
12
9
|
* file contents is ${v} (Buffer)
|
|
13
10
|
*/
|
|
14
|
-
class CommonStorageKeyValueDB {
|
|
11
|
+
export class CommonStorageKeyValueDB {
|
|
12
|
+
cfg;
|
|
15
13
|
constructor(cfg) {
|
|
16
14
|
this.cfg = cfg;
|
|
17
|
-
this.support = {
|
|
18
|
-
...db_lib_1.commonKeyValueDBFullSupport,
|
|
19
|
-
increment: false,
|
|
20
|
-
};
|
|
21
15
|
}
|
|
16
|
+
support = {
|
|
17
|
+
...commonKeyValueDBFullSupport,
|
|
18
|
+
increment: false,
|
|
19
|
+
};
|
|
22
20
|
async ping() {
|
|
23
21
|
await this.cfg.storage.ping(this.cfg.bucketName);
|
|
24
22
|
}
|
|
@@ -44,14 +42,14 @@ class CommonStorageKeyValueDB {
|
|
|
44
42
|
}
|
|
45
43
|
async deleteByIds(table, ids) {
|
|
46
44
|
const { bucketName, prefix } = this.getBucketAndPrefix(table);
|
|
47
|
-
await
|
|
45
|
+
await pMap(ids, async (id) => {
|
|
48
46
|
await this.cfg.storage.deletePath(bucketName, [prefix, id].join('/'));
|
|
49
47
|
});
|
|
50
48
|
}
|
|
51
49
|
async getByIds(table, ids) {
|
|
52
50
|
const { bucketName, prefix } = this.getBucketAndPrefix(table);
|
|
53
51
|
const map = {};
|
|
54
|
-
await
|
|
52
|
+
await pMap(ids, async (id) => {
|
|
55
53
|
const buf = await this.cfg.storage.getFile(bucketName, [prefix, id].join('/'));
|
|
56
54
|
if (buf)
|
|
57
55
|
map[id] = buf;
|
|
@@ -60,7 +58,7 @@ class CommonStorageKeyValueDB {
|
|
|
60
58
|
}
|
|
61
59
|
async saveBatch(table, entries) {
|
|
62
60
|
const { bucketName, prefix } = this.getBucketAndPrefix(table);
|
|
63
|
-
await
|
|
61
|
+
await pMap(entries, async ([id, content]) => {
|
|
64
62
|
await this.cfg.storage.saveFile(bucketName, [prefix, id].join('/'), content);
|
|
65
63
|
});
|
|
66
64
|
}
|
|
@@ -83,7 +81,6 @@ class CommonStorageKeyValueDB {
|
|
|
83
81
|
return (await this.cfg.storage.getFileNames(bucketName, { prefix })).length;
|
|
84
82
|
}
|
|
85
83
|
async incrementBatch(_table, _entries) {
|
|
86
|
-
throw new
|
|
84
|
+
throw new AppError('CommonStorageKeyValueDB.incrementBatch() is not implemented');
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
|
-
exports.CommonStorageKeyValueDB = CommonStorageKeyValueDB;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LocalTimeInput, StringMap } from '@naturalcycles/js-lib';
|
|
2
|
-
import { ReadableBinary, ReadableTyped, WritableBinary } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage';
|
|
1
|
+
import type { LocalTimeInput, StringMap } from '@naturalcycles/js-lib';
|
|
2
|
+
import type { ReadableBinary, ReadableTyped, WritableBinary } from '@naturalcycles/nodejs-lib';
|
|
3
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js';
|
|
4
4
|
export declare class InMemoryCommonStorage implements CommonStorage {
|
|
5
5
|
/**
|
|
6
6
|
* data[bucketName][filePath] = Buffer
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* data[bucketName][filePath] = Buffer
|
|
10
|
-
*/
|
|
11
|
-
this.data = {};
|
|
12
|
-
this.publicMap = {};
|
|
13
|
-
}
|
|
1
|
+
import { _assert, _isTruthy, _stringMapEntries, _substringAfterLast, localTime, } from '@naturalcycles/js-lib';
|
|
2
|
+
import { fs2, md5, readableFrom } from '@naturalcycles/nodejs-lib';
|
|
3
|
+
export class InMemoryCommonStorage {
|
|
4
|
+
/**
|
|
5
|
+
* data[bucketName][filePath] = Buffer
|
|
6
|
+
*/
|
|
7
|
+
data = {};
|
|
8
|
+
publicMap = {};
|
|
14
9
|
async ping() { }
|
|
15
10
|
async getBucketNames() {
|
|
16
11
|
return Object.keys(this.data);
|
|
17
12
|
}
|
|
18
13
|
getBucketNamesStream() {
|
|
19
|
-
return
|
|
14
|
+
return readableFrom(Object.keys(this.data));
|
|
20
15
|
}
|
|
21
16
|
async fileExists(bucketName, filePath) {
|
|
22
17
|
return !!this.data[bucketName]?.[filePath];
|
|
@@ -47,35 +42,35 @@ class InMemoryCommonStorage {
|
|
|
47
42
|
const { prefix = '', fullPaths = true } = opt;
|
|
48
43
|
return Object.keys(this.data[bucketName] || {})
|
|
49
44
|
.filter(filePath => filePath.startsWith(prefix))
|
|
50
|
-
.map(f => (fullPaths ? f :
|
|
45
|
+
.map(f => (fullPaths ? f : _substringAfterLast(f, '/')));
|
|
51
46
|
}
|
|
52
47
|
getFileNamesStream(bucketName, opt = {}) {
|
|
53
48
|
const { prefix = '', fullPaths = true } = opt;
|
|
54
|
-
return
|
|
49
|
+
return readableFrom(Object.keys(this.data[bucketName] || {})
|
|
55
50
|
.filter(filePath => filePath.startsWith(prefix))
|
|
56
51
|
.slice(0, opt.limit)
|
|
57
|
-
.map(n => (fullPaths ? n :
|
|
52
|
+
.map(n => (fullPaths ? n : _substringAfterLast(n, '/'))));
|
|
58
53
|
}
|
|
59
54
|
getFilesStream(bucketName, opt = {}) {
|
|
60
55
|
const { prefix = '', fullPaths = true } = opt;
|
|
61
|
-
return
|
|
56
|
+
return readableFrom(_stringMapEntries(this.data[bucketName] || {})
|
|
62
57
|
.map(([filePath, content]) => ({
|
|
63
58
|
filePath,
|
|
64
59
|
content,
|
|
65
60
|
}))
|
|
66
61
|
.filter(f => f.filePath.startsWith(prefix))
|
|
67
62
|
.slice(0, opt.limit)
|
|
68
|
-
.map(f => (fullPaths ? f : { ...f, filePath:
|
|
63
|
+
.map(f => (fullPaths ? f : { ...f, filePath: _substringAfterLast(f.filePath, '/') })));
|
|
69
64
|
}
|
|
70
65
|
getFileReadStream(bucketName, filePath) {
|
|
71
|
-
return
|
|
66
|
+
return readableFrom(this.data[bucketName][filePath]);
|
|
72
67
|
}
|
|
73
68
|
getFileWriteStream(_bucketName, _filePath) {
|
|
74
69
|
throw new Error('Method not implemented.');
|
|
75
70
|
}
|
|
76
71
|
async uploadFile(localFilePath, bucketName, bucketFilePath) {
|
|
77
72
|
this.data[bucketName] ||= {};
|
|
78
|
-
this.data[bucketName][bucketFilePath] = await
|
|
73
|
+
this.data[bucketName][bucketFilePath] = await fs2.readBufferAsync(localFilePath);
|
|
79
74
|
}
|
|
80
75
|
async setFileVisibility(bucketName, filePath, isPublic) {
|
|
81
76
|
this.publicMap[bucketName] ||= {};
|
|
@@ -101,7 +96,7 @@ class InMemoryCommonStorage {
|
|
|
101
96
|
const tob = toBucket || fromBucket;
|
|
102
97
|
this.data[fromBucket] ||= {};
|
|
103
98
|
this.data[tob] ||= {};
|
|
104
|
-
|
|
99
|
+
_stringMapEntries(this.data[fromBucket]).forEach(([filePath, v]) => {
|
|
105
100
|
if (!filePath.startsWith(fromPrefix))
|
|
106
101
|
return;
|
|
107
102
|
this.data[tob][toPrefix + filePath.slice(fromPrefix.length)] = v;
|
|
@@ -117,15 +112,14 @@ class InMemoryCommonStorage {
|
|
|
117
112
|
return;
|
|
118
113
|
const tob = toBucket || bucketName;
|
|
119
114
|
this.data[tob] ||= {};
|
|
120
|
-
this.data[tob][toPath] = Buffer.concat(filePaths.map(p => this.data[bucketName][p]).filter(
|
|
115
|
+
this.data[tob][toPath] = Buffer.concat(filePaths.map(p => this.data[bucketName][p]).filter(_isTruthy));
|
|
121
116
|
// delete source files
|
|
122
117
|
filePaths.forEach(p => delete this.data[bucketName][p]);
|
|
123
118
|
}
|
|
124
119
|
async getSignedUrl(bucketName, filePath, expires) {
|
|
125
120
|
const buf = this.data[bucketName]?.[filePath];
|
|
126
|
-
|
|
127
|
-
const signature =
|
|
128
|
-
return `https://testurl.com/${bucketName}/${filePath}?expires=${
|
|
121
|
+
_assert(buf, `getSignedUrl file not found: ${bucketName}/${filePath}`);
|
|
122
|
+
const signature = md5(buf);
|
|
123
|
+
return `https://testurl.com/${bucketName}/${filePath}?expires=${localTime(expires).unix}&signature=${signature}`;
|
|
129
124
|
}
|
|
130
125
|
}
|
|
131
|
-
exports.InMemoryCommonStorage = InMemoryCommonStorage;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from './cloudStorage';
|
|
2
|
-
export * from './commonStorage';
|
|
3
|
-
export * from './commonStorageBucket';
|
|
4
|
-
export * from './commonStorageKeyValueDB';
|
|
5
|
-
export * from './inMemoryCommonStorage';
|
|
6
|
-
export * from './model';
|
|
7
|
-
export * from './testing/commonStorageTest';
|
|
1
|
+
export * from './cloudStorage.js';
|
|
2
|
+
export * from './commonStorage.js';
|
|
3
|
+
export * from './commonStorageBucket.js';
|
|
4
|
+
export * from './commonStorageKeyValueDB.js';
|
|
5
|
+
export * from './inMemoryCommonStorage.js';
|
|
6
|
+
export * from './model.js';
|
|
7
|
+
export * from './testing/commonStorageTest.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
tslib_1.__exportStar(require("./inMemoryCommonStorage"), exports);
|
|
9
|
-
tslib_1.__exportStar(require("./model"), exports);
|
|
10
|
-
tslib_1.__exportStar(require("./testing/commonStorageTest"), exports);
|
|
1
|
+
export * from './cloudStorage.js';
|
|
2
|
+
export * from './commonStorage.js';
|
|
3
|
+
export * from './commonStorageBucket.js';
|
|
4
|
+
export * from './commonStorageKeyValueDB.js';
|
|
5
|
+
export * from './inMemoryCommonStorage.js';
|
|
6
|
+
export * from './model.js';
|
|
7
|
+
export * from './testing/commonStorageTest.js';
|
package/dist/model.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.runCommonStorageTest = runCommonStorageTest;
|
|
4
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
1
|
+
import { _range, _substringAfterLast, pMap } from '@naturalcycles/js-lib';
|
|
2
|
+
import { expect, test } from 'vitest';
|
|
5
3
|
const TEST_FOLDER = 'test/subdir';
|
|
6
|
-
const TEST_ITEMS =
|
|
4
|
+
const TEST_ITEMS = _range(10).map(n => ({
|
|
7
5
|
id: `id_${n + 1}`,
|
|
8
6
|
n,
|
|
9
7
|
even: n % 2 === 0,
|
|
10
8
|
}));
|
|
11
|
-
const TEST_ITEMS2 =
|
|
9
|
+
const TEST_ITEMS2 = _range(10).map(n => ({
|
|
12
10
|
fileType: 2,
|
|
13
11
|
id: `id_${n + 1}`,
|
|
14
12
|
n,
|
|
15
13
|
even: n % 2 === 0,
|
|
16
14
|
}));
|
|
17
|
-
const TEST_ITEMS3 =
|
|
15
|
+
const TEST_ITEMS3 = _range(10).map(n => ({
|
|
18
16
|
fileType: 3,
|
|
19
17
|
id: `id_${n + 1}`,
|
|
20
18
|
n,
|
|
@@ -27,7 +25,7 @@ const TEST_FILES = [TEST_ITEMS, TEST_ITEMS2, TEST_ITEMS3].map((obj, i) => ({
|
|
|
27
25
|
/**
|
|
28
26
|
* This test suite must be idempotent.
|
|
29
27
|
*/
|
|
30
|
-
function runCommonStorageTest(storage, bucketName) {
|
|
28
|
+
export function runCommonStorageTest(storage, bucketName) {
|
|
31
29
|
// test('createBucket', async () => {
|
|
32
30
|
// await storage.createBucket(bucketName)
|
|
33
31
|
// })
|
|
@@ -44,7 +42,7 @@ function runCommonStorageTest(storage, bucketName) {
|
|
|
44
42
|
// console.log(buckets)
|
|
45
43
|
// })
|
|
46
44
|
test('prepare: clear bucket', async () => {
|
|
47
|
-
await
|
|
45
|
+
await pMap(TEST_FILES.map(f => f.filePath), async (filePath) => await storage.deletePath(bucketName, filePath));
|
|
48
46
|
});
|
|
49
47
|
// test('listFileNames on root should return empty', async () => {
|
|
50
48
|
// const fileNames = await storage.getFileNames(bucketName)
|
|
@@ -61,7 +59,7 @@ function runCommonStorageTest(storage, bucketName) {
|
|
|
61
59
|
expect(fileNames).toEqual([]);
|
|
62
60
|
});
|
|
63
61
|
test(`exists should return empty array`, async () => {
|
|
64
|
-
await
|
|
62
|
+
await pMap(TEST_FILES, async (f) => {
|
|
65
63
|
const exists = await storage.fileExists(bucketName, f.filePath);
|
|
66
64
|
expect(exists).toBe(false);
|
|
67
65
|
});
|
|
@@ -69,12 +67,12 @@ function runCommonStorageTest(storage, bucketName) {
|
|
|
69
67
|
test(`saveFiles, then listFileNames, streamFileNames and getFiles should return just saved files`, async () => {
|
|
70
68
|
const testFilesMap = Object.fromEntries(TEST_FILES.map(f => [f.filePath, f.content]));
|
|
71
69
|
// It's done in the same test to ensure "strong consistency"
|
|
72
|
-
await
|
|
70
|
+
await pMap(TEST_FILES, async (f) => await storage.saveFile(bucketName, f.filePath, f.content));
|
|
73
71
|
const fileNamesShort = await storage.getFileNames(bucketName, {
|
|
74
72
|
prefix: TEST_FOLDER,
|
|
75
73
|
fullPaths: false,
|
|
76
74
|
});
|
|
77
|
-
expect(fileNamesShort.sort()).toEqual(TEST_FILES.map(f =>
|
|
75
|
+
expect(fileNamesShort.sort()).toEqual(TEST_FILES.map(f => _substringAfterLast(f.filePath, '/')).sort());
|
|
78
76
|
const fileNames = await storage.getFileNames(bucketName, { prefix: TEST_FOLDER });
|
|
79
77
|
expect(fileNames.sort()).toEqual(TEST_FILES.map(f => f.filePath).sort());
|
|
80
78
|
const streamedFileNames = await storage
|
|
@@ -82,11 +80,11 @@ function runCommonStorageTest(storage, bucketName) {
|
|
|
82
80
|
.toArray();
|
|
83
81
|
expect(streamedFileNames.sort()).toEqual(TEST_FILES.map(f => f.filePath).sort());
|
|
84
82
|
const filesMap = {};
|
|
85
|
-
await
|
|
83
|
+
await pMap(fileNames, async (filePath) => {
|
|
86
84
|
filesMap[filePath] = (await storage.getFile(bucketName, filePath));
|
|
87
85
|
});
|
|
88
86
|
expect(filesMap).toEqual(testFilesMap);
|
|
89
|
-
await
|
|
87
|
+
await pMap(fileNames, async (filePath) => {
|
|
90
88
|
const exists = await storage.fileExists(bucketName, filePath);
|
|
91
89
|
expect(exists).toBe(true);
|
|
92
90
|
});
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/cloud-storage-lib",
|
|
3
|
+
"type": "module",
|
|
3
4
|
"scripts": {
|
|
4
5
|
"prepare": "husky",
|
|
5
6
|
"build": "dev-lib build",
|
|
@@ -9,16 +10,18 @@
|
|
|
9
10
|
"lbt": "dev-lib lbt"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
|
-
"@google-cloud/storage": "^7
|
|
13
|
-
"@naturalcycles/db-lib": "^
|
|
14
|
-
"@naturalcycles/js-lib": "^14
|
|
15
|
-
"@naturalcycles/nodejs-lib": "^13
|
|
13
|
+
"@google-cloud/storage": "^7",
|
|
14
|
+
"@naturalcycles/db-lib": "^10",
|
|
15
|
+
"@naturalcycles/js-lib": "^14",
|
|
16
|
+
"@naturalcycles/nodejs-lib": "^13"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
|
-
"@naturalcycles/dev-lib": "^
|
|
19
|
-
"@types/node": "^22
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"@naturalcycles/dev-lib": "^17",
|
|
20
|
+
"@types/node": "^22",
|
|
21
|
+
"@vitest/coverage-v8": "^3",
|
|
22
|
+
"firebase-admin": "^13",
|
|
23
|
+
"tsx": "^4",
|
|
24
|
+
"vitest": "^3"
|
|
22
25
|
},
|
|
23
26
|
"files": [
|
|
24
27
|
"dist",
|
|
@@ -38,9 +41,9 @@
|
|
|
38
41
|
"url": "https://github.com/NaturalCycles/cloud-storage-lib"
|
|
39
42
|
},
|
|
40
43
|
"engines": {
|
|
41
|
-
"node": ">=
|
|
44
|
+
"node": ">=22.12.0"
|
|
42
45
|
},
|
|
43
|
-
"version": "
|
|
46
|
+
"version": "2.0.0",
|
|
44
47
|
"description": "CommonStorage implementation based on Google Cloud Storage",
|
|
45
48
|
"author": "Natural Cycles Team",
|
|
46
49
|
"license": "MIT"
|
package/src/cloudStorage.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
// eslint-disable-next-line import-x/no-duplicates
|
|
2
1
|
import type { File, Storage, StorageOptions } from '@google-cloud/storage'
|
|
3
|
-
|
|
4
|
-
import type * as StorageLib from '@google-cloud/storage'
|
|
2
|
+
import type { CommonLogger, LocalTimeInput, UnixTimestampMillis } from '@naturalcycles/js-lib'
|
|
5
3
|
import {
|
|
6
4
|
_assert,
|
|
7
5
|
_chunk,
|
|
8
6
|
_since,
|
|
9
7
|
_substringAfterLast,
|
|
10
|
-
CommonLogger,
|
|
11
8
|
localTime,
|
|
12
|
-
LocalTimeInput,
|
|
13
9
|
pMap,
|
|
14
10
|
SKIP,
|
|
15
11
|
} from '@naturalcycles/js-lib'
|
|
16
12
|
import type { ReadableBinary, ReadableTyped, WritableBinary } from '@naturalcycles/nodejs-lib'
|
|
17
|
-
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage'
|
|
18
|
-
import type { GCPServiceAccount } from './model'
|
|
13
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js'
|
|
14
|
+
import type { GCPServiceAccount } from './model.js'
|
|
19
15
|
|
|
20
16
|
export type {
|
|
21
17
|
// This is the latest version, to be imported by consumers
|
|
@@ -67,13 +63,13 @@ export class CloudStorage implements CommonStorage {
|
|
|
67
63
|
logger: CommonLogger
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
static createFromGCPServiceAccount(
|
|
66
|
+
static async createFromGCPServiceAccount(
|
|
71
67
|
credentials?: GCPServiceAccount,
|
|
72
68
|
cfg?: CloudStorageCfg,
|
|
73
|
-
): CloudStorage {
|
|
74
|
-
const
|
|
69
|
+
): Promise<CloudStorage> {
|
|
70
|
+
const { Storage } = await import('@google-cloud/storage')
|
|
75
71
|
|
|
76
|
-
const storage = new
|
|
72
|
+
const storage = new Storage({
|
|
77
73
|
credentials,
|
|
78
74
|
// Explicitly passing it here to fix this error:
|
|
79
75
|
// Error: Unable to detect a Project Id in the current environment.
|
|
@@ -86,12 +82,12 @@ export class CloudStorage implements CommonStorage {
|
|
|
86
82
|
return new CloudStorage(storage, cfg)
|
|
87
83
|
}
|
|
88
84
|
|
|
89
|
-
static createFromStorageOptions(
|
|
85
|
+
static async createFromStorageOptions(
|
|
90
86
|
storageOptions?: StorageOptions,
|
|
91
87
|
cfg?: CloudStorageCfg,
|
|
92
|
-
): CloudStorage {
|
|
93
|
-
const
|
|
94
|
-
const storage = new
|
|
88
|
+
): Promise<CloudStorage> {
|
|
89
|
+
const { Storage } = await import('@google-cloud/storage')
|
|
90
|
+
const storage = new Storage(storageOptions)
|
|
95
91
|
return new CloudStorage(storage, cfg)
|
|
96
92
|
}
|
|
97
93
|
|
|
@@ -319,7 +315,7 @@ export class CloudStorage implements CommonStorage {
|
|
|
319
315
|
return
|
|
320
316
|
}
|
|
321
317
|
|
|
322
|
-
const started = Date.now()
|
|
318
|
+
const started = Date.now() as UnixTimestampMillis
|
|
323
319
|
await pMap(_chunk(filePaths, BATCH_SIZE), async (fileBatch, i) => {
|
|
324
320
|
if (debug) {
|
|
325
321
|
logger.log(`[${currentRecursionDepth}] Composing batch ${i + 1}...`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Readable, Writable } from 'node:stream'
|
|
1
|
+
import type { Readable, Writable } from 'node:stream'
|
|
2
2
|
import { AppError, pMap } from '@naturalcycles/js-lib'
|
|
3
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
4
|
-
import { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage'
|
|
3
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
4
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js'
|
|
5
5
|
|
|
6
6
|
export interface CommonStorageBucketCfg {
|
|
7
7
|
storage: CommonStorage
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
CommonDBCreateOptions,
|
|
3
3
|
CommonKeyValueDB,
|
|
4
|
-
|
|
4
|
+
KeyValueDBTuple,
|
|
5
5
|
} from '@naturalcycles/db-lib'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { commonKeyValueDBFullSupport } from '@naturalcycles/db-lib'
|
|
7
|
+
import type { IncrementTuple } from '@naturalcycles/db-lib/dist/kv/commonKeyValueDB.js'
|
|
8
|
+
import type { StringMap } from '@naturalcycles/js-lib'
|
|
9
|
+
import { AppError, pMap } from '@naturalcycles/js-lib'
|
|
10
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
11
|
+
import type { CommonStorage } from './commonStorage.js'
|
|
10
12
|
|
|
11
13
|
export interface CommonStorageKeyValueDBCfg {
|
|
12
14
|
storage: CommonStorage
|
|
@@ -64,7 +66,7 @@ export class CommonStorageKeyValueDB implements CommonKeyValueDB {
|
|
|
64
66
|
})
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
async getByIds
|
|
69
|
+
async getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]> {
|
|
68
70
|
const { bucketName, prefix } = this.getBucketAndPrefix(table)
|
|
69
71
|
|
|
70
72
|
const map: StringMap<Buffer> = {}
|
|
@@ -74,14 +76,14 @@ export class CommonStorageKeyValueDB implements CommonKeyValueDB {
|
|
|
74
76
|
if (buf) map[id] = buf
|
|
75
77
|
})
|
|
76
78
|
|
|
77
|
-
return ids.map(id => [id, map[id]] as
|
|
79
|
+
return ids.map(id => [id, map[id]] as KeyValueDBTuple).filter(t => t[1])
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
async saveBatch
|
|
82
|
+
async saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void> {
|
|
81
83
|
const { bucketName, prefix } = this.getBucketAndPrefix(table)
|
|
82
84
|
|
|
83
85
|
await pMap(entries, async ([id, content]) => {
|
|
84
|
-
await this.cfg.storage.saveFile(bucketName, [prefix, id].join('/'), content
|
|
86
|
+
await this.cfg.storage.saveFile(bucketName, [prefix, id].join('/'), content)
|
|
85
87
|
})
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -91,18 +93,18 @@ export class CommonStorageKeyValueDB implements CommonKeyValueDB {
|
|
|
91
93
|
return this.cfg.storage.getFileNamesStream(bucketName, { prefix, limit, fullPaths: false })
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
streamValues
|
|
96
|
+
streamValues(table: string, limit?: number): ReadableTyped<Buffer> {
|
|
95
97
|
const { bucketName, prefix } = this.getBucketAndPrefix(table)
|
|
96
98
|
|
|
97
|
-
return this.cfg.storage.getFilesStream(bucketName, { prefix, limit }).map(f => f.content
|
|
99
|
+
return this.cfg.storage.getFilesStream(bucketName, { prefix, limit }).map(f => f.content)
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
streamEntries
|
|
102
|
+
streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple> {
|
|
101
103
|
const { bucketName, prefix } = this.getBucketAndPrefix(table)
|
|
102
104
|
|
|
103
105
|
return this.cfg.storage
|
|
104
106
|
.getFilesStream(bucketName, { prefix, limit, fullPaths: false })
|
|
105
|
-
.map(f => [f.filePath, f.content
|
|
107
|
+
.map(f => [f.filePath, f.content])
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
async count(table: string): Promise<number> {
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
+
import type { LocalTimeInput, StringMap } from '@naturalcycles/js-lib'
|
|
1
2
|
import {
|
|
2
3
|
_assert,
|
|
3
4
|
_isTruthy,
|
|
4
5
|
_stringMapEntries,
|
|
5
6
|
_substringAfterLast,
|
|
6
7
|
localTime,
|
|
7
|
-
LocalTimeInput,
|
|
8
|
-
StringMap,
|
|
9
8
|
} from '@naturalcycles/js-lib'
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ReadableBinary,
|
|
14
|
-
readableFrom,
|
|
15
|
-
ReadableTyped,
|
|
16
|
-
WritableBinary,
|
|
17
|
-
} from '@naturalcycles/nodejs-lib'
|
|
18
|
-
import { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage'
|
|
9
|
+
import type { ReadableBinary, ReadableTyped, WritableBinary } from '@naturalcycles/nodejs-lib'
|
|
10
|
+
import { fs2, md5, readableFrom } from '@naturalcycles/nodejs-lib'
|
|
11
|
+
import type { CommonStorage, CommonStorageGetOptions, FileEntry } from './commonStorage.js'
|
|
19
12
|
|
|
20
13
|
export class InMemoryCommonStorage implements CommonStorage {
|
|
21
14
|
/**
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from './cloudStorage'
|
|
2
|
-
export * from './commonStorage'
|
|
3
|
-
export * from './commonStorageBucket'
|
|
4
|
-
export * from './commonStorageKeyValueDB'
|
|
5
|
-
export * from './inMemoryCommonStorage'
|
|
6
|
-
export * from './model'
|
|
7
|
-
export * from './testing/commonStorageTest'
|
|
1
|
+
export * from './cloudStorage.js'
|
|
2
|
+
export * from './commonStorage.js'
|
|
3
|
+
export * from './commonStorageBucket.js'
|
|
4
|
+
export * from './commonStorageKeyValueDB.js'
|
|
5
|
+
export * from './inMemoryCommonStorage.js'
|
|
6
|
+
export * from './model.js'
|
|
7
|
+
export * from './testing/commonStorageTest.js'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { StringMap } from '@naturalcycles/js-lib'
|
|
2
|
+
import { _range, _substringAfterLast, pMap } from '@naturalcycles/js-lib'
|
|
3
|
+
import { expect, test } from 'vitest'
|
|
4
|
+
import type { CommonStorage, FileEntry } from '../commonStorage.js'
|
|
3
5
|
|
|
4
6
|
const TEST_FOLDER = 'test/subdir'
|
|
5
7
|
|