@dungarees/gcs-bucket 0.11.4

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/fake.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { GcsClient, GcsWriteOptions } from './type.ts';
2
+ export type StoredObject = {
3
+ data: Buffer;
4
+ options?: GcsWriteOptions;
5
+ };
6
+ export type FakeGcsClient = {
7
+ client: GcsClient;
8
+ objects: Map<string, StoredObject>;
9
+ };
10
+ export declare const createFakeGcsClient: ({ now }?: {
11
+ now?: number;
12
+ }) => FakeGcsClient;
package/fake.js ADDED
@@ -0,0 +1,59 @@
1
+ import { toBuffer, toSignedUrlConfig } from './request.js';
2
+ import { PassThrough, Readable } from 'node:stream';
3
+ const toKey = ({ bucket, objectName }) => `${bucket}/${objectName}`;
4
+ export const createFakeGcsClient = ({ now = 0 } = {}) => {
5
+ const objects = new Map();
6
+ const readOrThrow = (ref) => {
7
+ const stored = objects.get(toKey(ref));
8
+ if (stored === undefined) {
9
+ throw new Error(`No such object: ${toKey(ref)}`);
10
+ }
11
+ return stored;
12
+ };
13
+ return {
14
+ client: {
15
+ writeObject: async ({ bucket, objectName, data, options }) => {
16
+ objects.set(toKey({ bucket, objectName }), {
17
+ data: toBuffer(data),
18
+ ...(options !== undefined && { options }),
19
+ });
20
+ await Promise.resolve();
21
+ },
22
+ readObject: async ({ bucket, objectName }) => await Promise.resolve(readOrThrow({ bucket, objectName }).data),
23
+ deleteObject: async ({ bucket, objectName }) => {
24
+ objects.delete(toKey({ bucket, objectName }));
25
+ await Promise.resolve();
26
+ },
27
+ existsObject: async ({ bucket, objectName }) => await Promise.resolve(objects.has(toKey({ bucket, objectName }))),
28
+ listObjects: async ({ bucket, options }) => {
29
+ const prefix = `${bucket}/`;
30
+ const names = [...objects.keys()]
31
+ .filter((key) => key.startsWith(prefix))
32
+ .map((key) => key.slice(prefix.length))
33
+ .filter((name) => options?.prefix === undefined || name.startsWith(options.prefix))
34
+ .sort();
35
+ return await Promise.resolve({
36
+ objects: options?.maxResults === undefined ? names : names.slice(0, options.maxResults),
37
+ });
38
+ },
39
+ getSignedUrl: async ({ bucket, objectName, options }) => {
40
+ const { action, expires } = toSignedUrlConfig({ options, now });
41
+ return await Promise.resolve(`https://fake-storage.invalid/${bucket}/${objectName}?action=${action}&expires=${expires}`);
42
+ },
43
+ writeStream: ({ bucket, objectName, options }) => {
44
+ const passThrough = new PassThrough();
45
+ const chunks = [];
46
+ passThrough.on('data', (chunk) => chunks.push(chunk));
47
+ passThrough.on('end', () => {
48
+ objects.set(toKey({ bucket, objectName }), {
49
+ data: Buffer.concat(chunks),
50
+ ...(options !== undefined && { options }),
51
+ });
52
+ });
53
+ return passThrough;
54
+ },
55
+ readStream: ({ bucket, objectName }) => Readable.from(readOrThrow({ bucket, objectName }).data),
56
+ },
57
+ objects,
58
+ };
59
+ };
package/package.json ADDED
@@ -0,0 +1,450 @@
1
+ {
2
+ "name": "@dungarees/gcs-bucket",
3
+ "engines": {
4
+ "node": ">=25.0.0"
5
+ },
6
+ "scripts": {
7
+ "type-check": "tsc --noEmit"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@google-cloud/storage": "^7.14.0"
12
+ },
13
+ "devDependencies": {
14
+ "typescript": "^5.9.3",
15
+ "vitest": "^3.0.2"
16
+ },
17
+ "author": "info@productkind.com",
18
+ "license": "MIT",
19
+ "version": "0.11.4",
20
+ "exports": {
21
+ "./fake.ts": {
22
+ "import": "./fake.js",
23
+ "types": "./fake.d.ts"
24
+ },
25
+ "./node_modules/typescript/lib/lib.d.ts": {
26
+ "import": "./node_modules/typescript/lib/lib.d.js",
27
+ "types": "./node_modules/typescript/lib/lib.d.d.ts"
28
+ },
29
+ "./node_modules/typescript/lib/lib.decorators.d.ts": {
30
+ "import": "./node_modules/typescript/lib/lib.decorators.d.js",
31
+ "types": "./node_modules/typescript/lib/lib.decorators.d.d.ts"
32
+ },
33
+ "./node_modules/typescript/lib/lib.decorators.legacy.d.ts": {
34
+ "import": "./node_modules/typescript/lib/lib.decorators.legacy.d.js",
35
+ "types": "./node_modules/typescript/lib/lib.decorators.legacy.d.d.ts"
36
+ },
37
+ "./node_modules/typescript/lib/lib.dom.asynciterable.d.ts": {
38
+ "import": "./node_modules/typescript/lib/lib.dom.asynciterable.d.js",
39
+ "types": "./node_modules/typescript/lib/lib.dom.asynciterable.d.d.ts"
40
+ },
41
+ "./node_modules/typescript/lib/lib.dom.d.ts": {
42
+ "import": "./node_modules/typescript/lib/lib.dom.d.js",
43
+ "types": "./node_modules/typescript/lib/lib.dom.d.d.ts"
44
+ },
45
+ "./node_modules/typescript/lib/lib.dom.iterable.d.ts": {
46
+ "import": "./node_modules/typescript/lib/lib.dom.iterable.d.js",
47
+ "types": "./node_modules/typescript/lib/lib.dom.iterable.d.d.ts"
48
+ },
49
+ "./node_modules/typescript/lib/lib.es2015.collection.d.ts": {
50
+ "import": "./node_modules/typescript/lib/lib.es2015.collection.d.js",
51
+ "types": "./node_modules/typescript/lib/lib.es2015.collection.d.d.ts"
52
+ },
53
+ "./node_modules/typescript/lib/lib.es2015.core.d.ts": {
54
+ "import": "./node_modules/typescript/lib/lib.es2015.core.d.js",
55
+ "types": "./node_modules/typescript/lib/lib.es2015.core.d.d.ts"
56
+ },
57
+ "./node_modules/typescript/lib/lib.es2015.d.ts": {
58
+ "import": "./node_modules/typescript/lib/lib.es2015.d.js",
59
+ "types": "./node_modules/typescript/lib/lib.es2015.d.d.ts"
60
+ },
61
+ "./node_modules/typescript/lib/lib.es2015.generator.d.ts": {
62
+ "import": "./node_modules/typescript/lib/lib.es2015.generator.d.js",
63
+ "types": "./node_modules/typescript/lib/lib.es2015.generator.d.d.ts"
64
+ },
65
+ "./node_modules/typescript/lib/lib.es2015.iterable.d.ts": {
66
+ "import": "./node_modules/typescript/lib/lib.es2015.iterable.d.js",
67
+ "types": "./node_modules/typescript/lib/lib.es2015.iterable.d.d.ts"
68
+ },
69
+ "./node_modules/typescript/lib/lib.es2015.promise.d.ts": {
70
+ "import": "./node_modules/typescript/lib/lib.es2015.promise.d.js",
71
+ "types": "./node_modules/typescript/lib/lib.es2015.promise.d.d.ts"
72
+ },
73
+ "./node_modules/typescript/lib/lib.es2015.proxy.d.ts": {
74
+ "import": "./node_modules/typescript/lib/lib.es2015.proxy.d.js",
75
+ "types": "./node_modules/typescript/lib/lib.es2015.proxy.d.d.ts"
76
+ },
77
+ "./node_modules/typescript/lib/lib.es2015.reflect.d.ts": {
78
+ "import": "./node_modules/typescript/lib/lib.es2015.reflect.d.js",
79
+ "types": "./node_modules/typescript/lib/lib.es2015.reflect.d.d.ts"
80
+ },
81
+ "./node_modules/typescript/lib/lib.es2015.symbol.d.ts": {
82
+ "import": "./node_modules/typescript/lib/lib.es2015.symbol.d.js",
83
+ "types": "./node_modules/typescript/lib/lib.es2015.symbol.d.d.ts"
84
+ },
85
+ "./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts": {
86
+ "import": "./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.js",
87
+ "types": "./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.d.ts"
88
+ },
89
+ "./node_modules/typescript/lib/lib.es2016.array.include.d.ts": {
90
+ "import": "./node_modules/typescript/lib/lib.es2016.array.include.d.js",
91
+ "types": "./node_modules/typescript/lib/lib.es2016.array.include.d.d.ts"
92
+ },
93
+ "./node_modules/typescript/lib/lib.es2016.d.ts": {
94
+ "import": "./node_modules/typescript/lib/lib.es2016.d.js",
95
+ "types": "./node_modules/typescript/lib/lib.es2016.d.d.ts"
96
+ },
97
+ "./node_modules/typescript/lib/lib.es2016.full.d.ts": {
98
+ "import": "./node_modules/typescript/lib/lib.es2016.full.d.js",
99
+ "types": "./node_modules/typescript/lib/lib.es2016.full.d.d.ts"
100
+ },
101
+ "./node_modules/typescript/lib/lib.es2016.intl.d.ts": {
102
+ "import": "./node_modules/typescript/lib/lib.es2016.intl.d.js",
103
+ "types": "./node_modules/typescript/lib/lib.es2016.intl.d.d.ts"
104
+ },
105
+ "./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts": {
106
+ "import": "./node_modules/typescript/lib/lib.es2017.arraybuffer.d.js",
107
+ "types": "./node_modules/typescript/lib/lib.es2017.arraybuffer.d.d.ts"
108
+ },
109
+ "./node_modules/typescript/lib/lib.es2017.d.ts": {
110
+ "import": "./node_modules/typescript/lib/lib.es2017.d.js",
111
+ "types": "./node_modules/typescript/lib/lib.es2017.d.d.ts"
112
+ },
113
+ "./node_modules/typescript/lib/lib.es2017.date.d.ts": {
114
+ "import": "./node_modules/typescript/lib/lib.es2017.date.d.js",
115
+ "types": "./node_modules/typescript/lib/lib.es2017.date.d.d.ts"
116
+ },
117
+ "./node_modules/typescript/lib/lib.es2017.full.d.ts": {
118
+ "import": "./node_modules/typescript/lib/lib.es2017.full.d.js",
119
+ "types": "./node_modules/typescript/lib/lib.es2017.full.d.d.ts"
120
+ },
121
+ "./node_modules/typescript/lib/lib.es2017.intl.d.ts": {
122
+ "import": "./node_modules/typescript/lib/lib.es2017.intl.d.js",
123
+ "types": "./node_modules/typescript/lib/lib.es2017.intl.d.d.ts"
124
+ },
125
+ "./node_modules/typescript/lib/lib.es2017.object.d.ts": {
126
+ "import": "./node_modules/typescript/lib/lib.es2017.object.d.js",
127
+ "types": "./node_modules/typescript/lib/lib.es2017.object.d.d.ts"
128
+ },
129
+ "./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts": {
130
+ "import": "./node_modules/typescript/lib/lib.es2017.sharedmemory.d.js",
131
+ "types": "./node_modules/typescript/lib/lib.es2017.sharedmemory.d.d.ts"
132
+ },
133
+ "./node_modules/typescript/lib/lib.es2017.string.d.ts": {
134
+ "import": "./node_modules/typescript/lib/lib.es2017.string.d.js",
135
+ "types": "./node_modules/typescript/lib/lib.es2017.string.d.d.ts"
136
+ },
137
+ "./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts": {
138
+ "import": "./node_modules/typescript/lib/lib.es2017.typedarrays.d.js",
139
+ "types": "./node_modules/typescript/lib/lib.es2017.typedarrays.d.d.ts"
140
+ },
141
+ "./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts": {
142
+ "import": "./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.js",
143
+ "types": "./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.d.ts"
144
+ },
145
+ "./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts": {
146
+ "import": "./node_modules/typescript/lib/lib.es2018.asynciterable.d.js",
147
+ "types": "./node_modules/typescript/lib/lib.es2018.asynciterable.d.d.ts"
148
+ },
149
+ "./node_modules/typescript/lib/lib.es2018.d.ts": {
150
+ "import": "./node_modules/typescript/lib/lib.es2018.d.js",
151
+ "types": "./node_modules/typescript/lib/lib.es2018.d.d.ts"
152
+ },
153
+ "./node_modules/typescript/lib/lib.es2018.full.d.ts": {
154
+ "import": "./node_modules/typescript/lib/lib.es2018.full.d.js",
155
+ "types": "./node_modules/typescript/lib/lib.es2018.full.d.d.ts"
156
+ },
157
+ "./node_modules/typescript/lib/lib.es2018.intl.d.ts": {
158
+ "import": "./node_modules/typescript/lib/lib.es2018.intl.d.js",
159
+ "types": "./node_modules/typescript/lib/lib.es2018.intl.d.d.ts"
160
+ },
161
+ "./node_modules/typescript/lib/lib.es2018.promise.d.ts": {
162
+ "import": "./node_modules/typescript/lib/lib.es2018.promise.d.js",
163
+ "types": "./node_modules/typescript/lib/lib.es2018.promise.d.d.ts"
164
+ },
165
+ "./node_modules/typescript/lib/lib.es2018.regexp.d.ts": {
166
+ "import": "./node_modules/typescript/lib/lib.es2018.regexp.d.js",
167
+ "types": "./node_modules/typescript/lib/lib.es2018.regexp.d.d.ts"
168
+ },
169
+ "./node_modules/typescript/lib/lib.es2019.array.d.ts": {
170
+ "import": "./node_modules/typescript/lib/lib.es2019.array.d.js",
171
+ "types": "./node_modules/typescript/lib/lib.es2019.array.d.d.ts"
172
+ },
173
+ "./node_modules/typescript/lib/lib.es2019.d.ts": {
174
+ "import": "./node_modules/typescript/lib/lib.es2019.d.js",
175
+ "types": "./node_modules/typescript/lib/lib.es2019.d.d.ts"
176
+ },
177
+ "./node_modules/typescript/lib/lib.es2019.full.d.ts": {
178
+ "import": "./node_modules/typescript/lib/lib.es2019.full.d.js",
179
+ "types": "./node_modules/typescript/lib/lib.es2019.full.d.d.ts"
180
+ },
181
+ "./node_modules/typescript/lib/lib.es2019.intl.d.ts": {
182
+ "import": "./node_modules/typescript/lib/lib.es2019.intl.d.js",
183
+ "types": "./node_modules/typescript/lib/lib.es2019.intl.d.d.ts"
184
+ },
185
+ "./node_modules/typescript/lib/lib.es2019.object.d.ts": {
186
+ "import": "./node_modules/typescript/lib/lib.es2019.object.d.js",
187
+ "types": "./node_modules/typescript/lib/lib.es2019.object.d.d.ts"
188
+ },
189
+ "./node_modules/typescript/lib/lib.es2019.string.d.ts": {
190
+ "import": "./node_modules/typescript/lib/lib.es2019.string.d.js",
191
+ "types": "./node_modules/typescript/lib/lib.es2019.string.d.d.ts"
192
+ },
193
+ "./node_modules/typescript/lib/lib.es2019.symbol.d.ts": {
194
+ "import": "./node_modules/typescript/lib/lib.es2019.symbol.d.js",
195
+ "types": "./node_modules/typescript/lib/lib.es2019.symbol.d.d.ts"
196
+ },
197
+ "./node_modules/typescript/lib/lib.es2020.bigint.d.ts": {
198
+ "import": "./node_modules/typescript/lib/lib.es2020.bigint.d.js",
199
+ "types": "./node_modules/typescript/lib/lib.es2020.bigint.d.d.ts"
200
+ },
201
+ "./node_modules/typescript/lib/lib.es2020.d.ts": {
202
+ "import": "./node_modules/typescript/lib/lib.es2020.d.js",
203
+ "types": "./node_modules/typescript/lib/lib.es2020.d.d.ts"
204
+ },
205
+ "./node_modules/typescript/lib/lib.es2020.date.d.ts": {
206
+ "import": "./node_modules/typescript/lib/lib.es2020.date.d.js",
207
+ "types": "./node_modules/typescript/lib/lib.es2020.date.d.d.ts"
208
+ },
209
+ "./node_modules/typescript/lib/lib.es2020.full.d.ts": {
210
+ "import": "./node_modules/typescript/lib/lib.es2020.full.d.js",
211
+ "types": "./node_modules/typescript/lib/lib.es2020.full.d.d.ts"
212
+ },
213
+ "./node_modules/typescript/lib/lib.es2020.intl.d.ts": {
214
+ "import": "./node_modules/typescript/lib/lib.es2020.intl.d.js",
215
+ "types": "./node_modules/typescript/lib/lib.es2020.intl.d.d.ts"
216
+ },
217
+ "./node_modules/typescript/lib/lib.es2020.number.d.ts": {
218
+ "import": "./node_modules/typescript/lib/lib.es2020.number.d.js",
219
+ "types": "./node_modules/typescript/lib/lib.es2020.number.d.d.ts"
220
+ },
221
+ "./node_modules/typescript/lib/lib.es2020.promise.d.ts": {
222
+ "import": "./node_modules/typescript/lib/lib.es2020.promise.d.js",
223
+ "types": "./node_modules/typescript/lib/lib.es2020.promise.d.d.ts"
224
+ },
225
+ "./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts": {
226
+ "import": "./node_modules/typescript/lib/lib.es2020.sharedmemory.d.js",
227
+ "types": "./node_modules/typescript/lib/lib.es2020.sharedmemory.d.d.ts"
228
+ },
229
+ "./node_modules/typescript/lib/lib.es2020.string.d.ts": {
230
+ "import": "./node_modules/typescript/lib/lib.es2020.string.d.js",
231
+ "types": "./node_modules/typescript/lib/lib.es2020.string.d.d.ts"
232
+ },
233
+ "./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts": {
234
+ "import": "./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.js",
235
+ "types": "./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.d.ts"
236
+ },
237
+ "./node_modules/typescript/lib/lib.es2021.d.ts": {
238
+ "import": "./node_modules/typescript/lib/lib.es2021.d.js",
239
+ "types": "./node_modules/typescript/lib/lib.es2021.d.d.ts"
240
+ },
241
+ "./node_modules/typescript/lib/lib.es2021.full.d.ts": {
242
+ "import": "./node_modules/typescript/lib/lib.es2021.full.d.js",
243
+ "types": "./node_modules/typescript/lib/lib.es2021.full.d.d.ts"
244
+ },
245
+ "./node_modules/typescript/lib/lib.es2021.intl.d.ts": {
246
+ "import": "./node_modules/typescript/lib/lib.es2021.intl.d.js",
247
+ "types": "./node_modules/typescript/lib/lib.es2021.intl.d.d.ts"
248
+ },
249
+ "./node_modules/typescript/lib/lib.es2021.promise.d.ts": {
250
+ "import": "./node_modules/typescript/lib/lib.es2021.promise.d.js",
251
+ "types": "./node_modules/typescript/lib/lib.es2021.promise.d.d.ts"
252
+ },
253
+ "./node_modules/typescript/lib/lib.es2021.string.d.ts": {
254
+ "import": "./node_modules/typescript/lib/lib.es2021.string.d.js",
255
+ "types": "./node_modules/typescript/lib/lib.es2021.string.d.d.ts"
256
+ },
257
+ "./node_modules/typescript/lib/lib.es2021.weakref.d.ts": {
258
+ "import": "./node_modules/typescript/lib/lib.es2021.weakref.d.js",
259
+ "types": "./node_modules/typescript/lib/lib.es2021.weakref.d.d.ts"
260
+ },
261
+ "./node_modules/typescript/lib/lib.es2022.array.d.ts": {
262
+ "import": "./node_modules/typescript/lib/lib.es2022.array.d.js",
263
+ "types": "./node_modules/typescript/lib/lib.es2022.array.d.d.ts"
264
+ },
265
+ "./node_modules/typescript/lib/lib.es2022.d.ts": {
266
+ "import": "./node_modules/typescript/lib/lib.es2022.d.js",
267
+ "types": "./node_modules/typescript/lib/lib.es2022.d.d.ts"
268
+ },
269
+ "./node_modules/typescript/lib/lib.es2022.error.d.ts": {
270
+ "import": "./node_modules/typescript/lib/lib.es2022.error.d.js",
271
+ "types": "./node_modules/typescript/lib/lib.es2022.error.d.d.ts"
272
+ },
273
+ "./node_modules/typescript/lib/lib.es2022.full.d.ts": {
274
+ "import": "./node_modules/typescript/lib/lib.es2022.full.d.js",
275
+ "types": "./node_modules/typescript/lib/lib.es2022.full.d.d.ts"
276
+ },
277
+ "./node_modules/typescript/lib/lib.es2022.intl.d.ts": {
278
+ "import": "./node_modules/typescript/lib/lib.es2022.intl.d.js",
279
+ "types": "./node_modules/typescript/lib/lib.es2022.intl.d.d.ts"
280
+ },
281
+ "./node_modules/typescript/lib/lib.es2022.object.d.ts": {
282
+ "import": "./node_modules/typescript/lib/lib.es2022.object.d.js",
283
+ "types": "./node_modules/typescript/lib/lib.es2022.object.d.d.ts"
284
+ },
285
+ "./node_modules/typescript/lib/lib.es2022.regexp.d.ts": {
286
+ "import": "./node_modules/typescript/lib/lib.es2022.regexp.d.js",
287
+ "types": "./node_modules/typescript/lib/lib.es2022.regexp.d.d.ts"
288
+ },
289
+ "./node_modules/typescript/lib/lib.es2022.string.d.ts": {
290
+ "import": "./node_modules/typescript/lib/lib.es2022.string.d.js",
291
+ "types": "./node_modules/typescript/lib/lib.es2022.string.d.d.ts"
292
+ },
293
+ "./node_modules/typescript/lib/lib.es2023.array.d.ts": {
294
+ "import": "./node_modules/typescript/lib/lib.es2023.array.d.js",
295
+ "types": "./node_modules/typescript/lib/lib.es2023.array.d.d.ts"
296
+ },
297
+ "./node_modules/typescript/lib/lib.es2023.collection.d.ts": {
298
+ "import": "./node_modules/typescript/lib/lib.es2023.collection.d.js",
299
+ "types": "./node_modules/typescript/lib/lib.es2023.collection.d.d.ts"
300
+ },
301
+ "./node_modules/typescript/lib/lib.es2023.d.ts": {
302
+ "import": "./node_modules/typescript/lib/lib.es2023.d.js",
303
+ "types": "./node_modules/typescript/lib/lib.es2023.d.d.ts"
304
+ },
305
+ "./node_modules/typescript/lib/lib.es2023.full.d.ts": {
306
+ "import": "./node_modules/typescript/lib/lib.es2023.full.d.js",
307
+ "types": "./node_modules/typescript/lib/lib.es2023.full.d.d.ts"
308
+ },
309
+ "./node_modules/typescript/lib/lib.es2023.intl.d.ts": {
310
+ "import": "./node_modules/typescript/lib/lib.es2023.intl.d.js",
311
+ "types": "./node_modules/typescript/lib/lib.es2023.intl.d.d.ts"
312
+ },
313
+ "./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts": {
314
+ "import": "./node_modules/typescript/lib/lib.es2024.arraybuffer.d.js",
315
+ "types": "./node_modules/typescript/lib/lib.es2024.arraybuffer.d.d.ts"
316
+ },
317
+ "./node_modules/typescript/lib/lib.es2024.collection.d.ts": {
318
+ "import": "./node_modules/typescript/lib/lib.es2024.collection.d.js",
319
+ "types": "./node_modules/typescript/lib/lib.es2024.collection.d.d.ts"
320
+ },
321
+ "./node_modules/typescript/lib/lib.es2024.d.ts": {
322
+ "import": "./node_modules/typescript/lib/lib.es2024.d.js",
323
+ "types": "./node_modules/typescript/lib/lib.es2024.d.d.ts"
324
+ },
325
+ "./node_modules/typescript/lib/lib.es2024.full.d.ts": {
326
+ "import": "./node_modules/typescript/lib/lib.es2024.full.d.js",
327
+ "types": "./node_modules/typescript/lib/lib.es2024.full.d.d.ts"
328
+ },
329
+ "./node_modules/typescript/lib/lib.es2024.object.d.ts": {
330
+ "import": "./node_modules/typescript/lib/lib.es2024.object.d.js",
331
+ "types": "./node_modules/typescript/lib/lib.es2024.object.d.d.ts"
332
+ },
333
+ "./node_modules/typescript/lib/lib.es2024.promise.d.ts": {
334
+ "import": "./node_modules/typescript/lib/lib.es2024.promise.d.js",
335
+ "types": "./node_modules/typescript/lib/lib.es2024.promise.d.d.ts"
336
+ },
337
+ "./node_modules/typescript/lib/lib.es2024.regexp.d.ts": {
338
+ "import": "./node_modules/typescript/lib/lib.es2024.regexp.d.js",
339
+ "types": "./node_modules/typescript/lib/lib.es2024.regexp.d.d.ts"
340
+ },
341
+ "./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts": {
342
+ "import": "./node_modules/typescript/lib/lib.es2024.sharedmemory.d.js",
343
+ "types": "./node_modules/typescript/lib/lib.es2024.sharedmemory.d.d.ts"
344
+ },
345
+ "./node_modules/typescript/lib/lib.es2024.string.d.ts": {
346
+ "import": "./node_modules/typescript/lib/lib.es2024.string.d.js",
347
+ "types": "./node_modules/typescript/lib/lib.es2024.string.d.d.ts"
348
+ },
349
+ "./node_modules/typescript/lib/lib.es5.d.ts": {
350
+ "import": "./node_modules/typescript/lib/lib.es5.d.js",
351
+ "types": "./node_modules/typescript/lib/lib.es5.d.d.ts"
352
+ },
353
+ "./node_modules/typescript/lib/lib.es6.d.ts": {
354
+ "import": "./node_modules/typescript/lib/lib.es6.d.js",
355
+ "types": "./node_modules/typescript/lib/lib.es6.d.d.ts"
356
+ },
357
+ "./node_modules/typescript/lib/lib.esnext.array.d.ts": {
358
+ "import": "./node_modules/typescript/lib/lib.esnext.array.d.js",
359
+ "types": "./node_modules/typescript/lib/lib.esnext.array.d.d.ts"
360
+ },
361
+ "./node_modules/typescript/lib/lib.esnext.collection.d.ts": {
362
+ "import": "./node_modules/typescript/lib/lib.esnext.collection.d.js",
363
+ "types": "./node_modules/typescript/lib/lib.esnext.collection.d.d.ts"
364
+ },
365
+ "./node_modules/typescript/lib/lib.esnext.d.ts": {
366
+ "import": "./node_modules/typescript/lib/lib.esnext.d.js",
367
+ "types": "./node_modules/typescript/lib/lib.esnext.d.d.ts"
368
+ },
369
+ "./node_modules/typescript/lib/lib.esnext.decorators.d.ts": {
370
+ "import": "./node_modules/typescript/lib/lib.esnext.decorators.d.js",
371
+ "types": "./node_modules/typescript/lib/lib.esnext.decorators.d.d.ts"
372
+ },
373
+ "./node_modules/typescript/lib/lib.esnext.disposable.d.ts": {
374
+ "import": "./node_modules/typescript/lib/lib.esnext.disposable.d.js",
375
+ "types": "./node_modules/typescript/lib/lib.esnext.disposable.d.d.ts"
376
+ },
377
+ "./node_modules/typescript/lib/lib.esnext.error.d.ts": {
378
+ "import": "./node_modules/typescript/lib/lib.esnext.error.d.js",
379
+ "types": "./node_modules/typescript/lib/lib.esnext.error.d.d.ts"
380
+ },
381
+ "./node_modules/typescript/lib/lib.esnext.float16.d.ts": {
382
+ "import": "./node_modules/typescript/lib/lib.esnext.float16.d.js",
383
+ "types": "./node_modules/typescript/lib/lib.esnext.float16.d.d.ts"
384
+ },
385
+ "./node_modules/typescript/lib/lib.esnext.full.d.ts": {
386
+ "import": "./node_modules/typescript/lib/lib.esnext.full.d.js",
387
+ "types": "./node_modules/typescript/lib/lib.esnext.full.d.d.ts"
388
+ },
389
+ "./node_modules/typescript/lib/lib.esnext.intl.d.ts": {
390
+ "import": "./node_modules/typescript/lib/lib.esnext.intl.d.js",
391
+ "types": "./node_modules/typescript/lib/lib.esnext.intl.d.d.ts"
392
+ },
393
+ "./node_modules/typescript/lib/lib.esnext.iterator.d.ts": {
394
+ "import": "./node_modules/typescript/lib/lib.esnext.iterator.d.js",
395
+ "types": "./node_modules/typescript/lib/lib.esnext.iterator.d.d.ts"
396
+ },
397
+ "./node_modules/typescript/lib/lib.esnext.promise.d.ts": {
398
+ "import": "./node_modules/typescript/lib/lib.esnext.promise.d.js",
399
+ "types": "./node_modules/typescript/lib/lib.esnext.promise.d.d.ts"
400
+ },
401
+ "./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts": {
402
+ "import": "./node_modules/typescript/lib/lib.esnext.sharedmemory.d.js",
403
+ "types": "./node_modules/typescript/lib/lib.esnext.sharedmemory.d.d.ts"
404
+ },
405
+ "./node_modules/typescript/lib/lib.scripthost.d.ts": {
406
+ "import": "./node_modules/typescript/lib/lib.scripthost.d.js",
407
+ "types": "./node_modules/typescript/lib/lib.scripthost.d.d.ts"
408
+ },
409
+ "./node_modules/typescript/lib/lib.webworker.asynciterable.d.ts": {
410
+ "import": "./node_modules/typescript/lib/lib.webworker.asynciterable.d.js",
411
+ "types": "./node_modules/typescript/lib/lib.webworker.asynciterable.d.d.ts"
412
+ },
413
+ "./node_modules/typescript/lib/lib.webworker.d.ts": {
414
+ "import": "./node_modules/typescript/lib/lib.webworker.d.js",
415
+ "types": "./node_modules/typescript/lib/lib.webworker.d.d.ts"
416
+ },
417
+ "./node_modules/typescript/lib/lib.webworker.importscripts.d.ts": {
418
+ "import": "./node_modules/typescript/lib/lib.webworker.importscripts.d.js",
419
+ "types": "./node_modules/typescript/lib/lib.webworker.importscripts.d.d.ts"
420
+ },
421
+ "./node_modules/typescript/lib/lib.webworker.iterable.d.ts": {
422
+ "import": "./node_modules/typescript/lib/lib.webworker.iterable.d.js",
423
+ "types": "./node_modules/typescript/lib/lib.webworker.iterable.d.d.ts"
424
+ },
425
+ "./node_modules/typescript/lib/tsserverlibrary.d.ts": {
426
+ "import": "./node_modules/typescript/lib/tsserverlibrary.d.js",
427
+ "types": "./node_modules/typescript/lib/tsserverlibrary.d.d.ts"
428
+ },
429
+ "./node_modules/typescript/lib/typescript.d.ts": {
430
+ "import": "./node_modules/typescript/lib/typescript.d.js",
431
+ "types": "./node_modules/typescript/lib/typescript.d.d.ts"
432
+ },
433
+ "./request.test.ts": {
434
+ "import": "./request.test.js",
435
+ "types": "./request.test.d.ts"
436
+ },
437
+ "./request.ts": {
438
+ "import": "./request.js",
439
+ "types": "./request.d.ts"
440
+ },
441
+ "./service.ts": {
442
+ "import": "./service.js",
443
+ "types": "./service.d.ts"
444
+ },
445
+ "./type.ts": {
446
+ "import": "./type.js",
447
+ "types": "./type.d.ts"
448
+ }
449
+ }
450
+ }
package/request.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { GcsListOptions, GcsSignedUrlOptions, GcsWriteOptions } from './type.ts';
2
+ export declare const MINIMUM_SIGNED_URL_SECONDS = 1;
3
+ export declare const toSaveOptions: (options?: GcsWriteOptions) => {
4
+ gzip?: boolean;
5
+ contentType?: string;
6
+ metadata?: Record<string, string>;
7
+ };
8
+ export declare const toListQuery: (options?: GcsListOptions) => {
9
+ prefix?: string;
10
+ maxResults?: number;
11
+ delimiter?: string;
12
+ pageToken?: string;
13
+ };
14
+ export declare const toSignedUrlConfig: ({ options, now, }: {
15
+ options: GcsSignedUrlOptions;
16
+ now: number;
17
+ }) => {
18
+ action: "read" | "write";
19
+ expires: number;
20
+ contentType?: string;
21
+ };
22
+ export declare const toBuffer: (data: Buffer | string) => Buffer;
package/request.js ADDED
@@ -0,0 +1,22 @@
1
+ export const MINIMUM_SIGNED_URL_SECONDS = 1;
2
+ // Each optional field is omitted rather than set to undefined, because the storage SDK inspects
3
+ // key presence and treats an explicit undefined gzip or contentType as a value.
4
+ export const toSaveOptions = (options) => ({
5
+ ...(options?.gzip !== undefined && { gzip: options.gzip }),
6
+ ...(options?.contentType !== undefined && { contentType: options.contentType }),
7
+ ...(options?.metadata !== undefined && { metadata: options.metadata }),
8
+ });
9
+ export const toListQuery = (options) => ({
10
+ ...(options?.prefix !== undefined && { prefix: options.prefix }),
11
+ ...(options?.maxResults !== undefined && { maxResults: options.maxResults }),
12
+ ...(options?.delimiter !== undefined && { delimiter: options.delimiter }),
13
+ ...(options?.pageToken !== undefined && { pageToken: options.pageToken }),
14
+ });
15
+ // The SDK wants an absolute expiry instant, so the caller's duration is added to the current time.
16
+ // The floor exists because an expiry at or before now is rejected outright.
17
+ export const toSignedUrlConfig = ({ options, now, }) => ({
18
+ action: options.action,
19
+ expires: now + Math.max(MINIMUM_SIGNED_URL_SECONDS, options.expiresInSeconds) * 1000,
20
+ ...(options.contentType !== undefined && { contentType: options.contentType }),
21
+ });
22
+ export const toBuffer = (data) => typeof data === 'string' ? Buffer.from(data) : data;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ import { createFakeGcsClient } from './fake.js';
2
+ import { MINIMUM_SIGNED_URL_SECONDS, toBuffer, toListQuery, toSaveOptions, toSignedUrlConfig, } from './request.js';
3
+ import { expect, test } from 'vitest';
4
+ test('a string is written as its own bytes', () => {
5
+ expect(toBuffer('hello').toString('utf-8')).toBe('hello');
6
+ });
7
+ test('a buffer is written as it stands', () => {
8
+ const buffer = Buffer.from('hello');
9
+ expect(toBuffer(buffer)).toBe(buffer);
10
+ });
11
+ test('no write options means nothing is sent to the storage SDK', () => {
12
+ expect(toSaveOptions()).toEqual({});
13
+ });
14
+ test('write options that were given are carried through', () => {
15
+ expect(toSaveOptions({ gzip: true, contentType: 'text/csv', metadata: { owner: 'test' } })).toEqual({ gzip: true, contentType: 'text/csv', metadata: { owner: 'test' } });
16
+ });
17
+ test('gzip set to false is carried through rather than dropped as falsy', () => {
18
+ expect(toSaveOptions({ gzip: false })).toEqual({ gzip: false });
19
+ });
20
+ test('a write option that was not given is left off entirely', () => {
21
+ const saveOptions = toSaveOptions({ gzip: true });
22
+ expect('contentType' in saveOptions).toBe(false);
23
+ expect('metadata' in saveOptions).toBe(false);
24
+ });
25
+ test('no list options means an empty query', () => {
26
+ expect(toListQuery()).toEqual({});
27
+ });
28
+ test('list options that were given are carried through', () => {
29
+ expect(toListQuery({ prefix: 'a/', maxResults: 10, delimiter: '/', pageToken: 'token' })).toEqual({ prefix: 'a/', maxResults: 10, delimiter: '/', pageToken: 'token' });
30
+ });
31
+ test('maxResults of zero is carried through rather than dropped as falsy', () => {
32
+ expect(toListQuery({ maxResults: 0 })).toEqual({ maxResults: 0 });
33
+ });
34
+ test('a signed url expires the requested number of seconds from now', () => {
35
+ expect(toSignedUrlConfig({ options: { action: 'read', expiresInSeconds: 60 }, now: 1000 })).toEqual({ action: 'read', expires: 61000 });
36
+ });
37
+ test('a signed url keeps the action it was asked for', () => {
38
+ expect(toSignedUrlConfig({ options: { action: 'write', expiresInSeconds: 60 }, now: 0 })).toHaveProperty('action', 'write');
39
+ });
40
+ test('a signed url carries a content type when one is given', () => {
41
+ expect(toSignedUrlConfig({
42
+ options: { action: 'write', expiresInSeconds: 60, contentType: 'text/csv' },
43
+ now: 0,
44
+ })).toHaveProperty('contentType', 'text/csv');
45
+ });
46
+ test('a signed url that would expire immediately is pushed to the minimum', () => {
47
+ expect(toSignedUrlConfig({ options: { action: 'read', expiresInSeconds: 0 }, now: 0 })).toHaveProperty('expires', MINIMUM_SIGNED_URL_SECONDS * 1000);
48
+ });
49
+ test('a signed url with a negative duration is pushed to the minimum', () => {
50
+ expect(toSignedUrlConfig({ options: { action: 'read', expiresInSeconds: -60 }, now: 0 })).toHaveProperty('expires', MINIMUM_SIGNED_URL_SECONDS * 1000);
51
+ });
52
+ test('an object written to the fake bucket reads back', async () => {
53
+ const { client } = createFakeGcsClient();
54
+ await client.writeObject({ bucket: 'b', objectName: 'o', data: 'contents' });
55
+ expect((await client.readObject({ bucket: 'b', objectName: 'o' })).toString('utf-8')).toBe('contents');
56
+ });
57
+ test('reading an object that was never written fails', async () => {
58
+ const { client } = createFakeGcsClient();
59
+ await expect(client.readObject({ bucket: 'b', objectName: 'missing' })).rejects.toThrow('No such object: b/missing');
60
+ });
61
+ test('existsObject reports whether an object is there', async () => {
62
+ const { client } = createFakeGcsClient();
63
+ await client.writeObject({ bucket: 'b', objectName: 'o', data: 'contents' });
64
+ expect(await client.existsObject({ bucket: 'b', objectName: 'o' })).toBe(true);
65
+ expect(await client.existsObject({ bucket: 'b', objectName: 'other' })).toBe(false);
66
+ });
67
+ test('a deleted object is no longer there', async () => {
68
+ const { client } = createFakeGcsClient();
69
+ await client.writeObject({ bucket: 'b', objectName: 'o', data: 'contents' });
70
+ await client.deleteObject({ bucket: 'b', objectName: 'o' });
71
+ expect(await client.existsObject({ bucket: 'b', objectName: 'o' })).toBe(false);
72
+ });
73
+ test('deleting an object that is not there is not an error', async () => {
74
+ const { client } = createFakeGcsClient();
75
+ await expect(client.deleteObject({ bucket: 'b', objectName: 'missing' })).resolves.toBeUndefined();
76
+ });
77
+ test('listObjects names the objects in one bucket only', async () => {
78
+ const { client } = createFakeGcsClient();
79
+ await client.writeObject({ bucket: 'b', objectName: 'one', data: '1' });
80
+ await client.writeObject({ bucket: 'b', objectName: 'two', data: '2' });
81
+ await client.writeObject({ bucket: 'other', objectName: 'three', data: '3' });
82
+ expect(await client.listObjects({ bucket: 'b' })).toEqual({ objects: ['one', 'two'] });
83
+ });
84
+ test('listObjects narrows to a prefix when one is given', async () => {
85
+ const { client } = createFakeGcsClient();
86
+ await client.writeObject({ bucket: 'b', objectName: 'logs/one', data: '1' });
87
+ await client.writeObject({ bucket: 'b', objectName: 'data/two', data: '2' });
88
+ expect(await client.listObjects({ bucket: 'b', options: { prefix: 'logs/' } })).toEqual({
89
+ objects: ['logs/one'],
90
+ });
91
+ });
92
+ test('listObjects stops at maxResults', async () => {
93
+ const { client } = createFakeGcsClient();
94
+ await client.writeObject({ bucket: 'b', objectName: 'one', data: '1' });
95
+ await client.writeObject({ bucket: 'b', objectName: 'two', data: '2' });
96
+ expect(await client.listObjects({ bucket: 'b', options: { maxResults: 1 } })).toEqual({
97
+ objects: ['one'],
98
+ });
99
+ });
100
+ test('the fake signed url names the bucket, object, action and expiry', async () => {
101
+ const { client } = createFakeGcsClient({ now: 1000 });
102
+ expect(await client.getSignedUrl({
103
+ bucket: 'b',
104
+ objectName: 'o',
105
+ options: { action: 'read', expiresInSeconds: 60 },
106
+ })).toBe('https://fake-storage.invalid/b/o?action=read&expires=61000');
107
+ });
108
+ test('a stream written to the fake bucket reads back', async () => {
109
+ const { client } = createFakeGcsClient();
110
+ const stream = client.writeStream({ bucket: 'b', objectName: 'o' });
111
+ await new Promise((resolve) => {
112
+ stream.on('finish', resolve);
113
+ stream.end('streamed');
114
+ });
115
+ expect((await client.readObject({ bucket: 'b', objectName: 'o' })).toString('utf-8')).toBe('streamed');
116
+ });
117
+ test('an object can be read back through a stream', async () => {
118
+ const { client } = createFakeGcsClient();
119
+ await client.writeObject({ bucket: 'b', objectName: 'o', data: 'contents' });
120
+ const chunks = [];
121
+ for await (const chunk of client.readStream({ bucket: 'b', objectName: 'o' })) {
122
+ chunks.push(Buffer.from(chunk));
123
+ }
124
+ expect(Buffer.concat(chunks).toString('utf-8')).toBe('contents');
125
+ });
package/service.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { GcsBucketConfig, GcsClient } from './type.ts';
2
+ export declare const createGcsClient: (config?: GcsBucketConfig) => GcsClient;
package/service.js ADDED
@@ -0,0 +1,48 @@
1
+ import { toBuffer, toListQuery, toSaveOptions, toSignedUrlConfig } from './request.js';
2
+ import { Storage } from '@google-cloud/storage';
3
+ import { PassThrough } from 'node:stream';
4
+ export const createGcsClient = (config) => {
5
+ const storage = new Storage(config?.projectId !== undefined ? { projectId: config.projectId } : undefined);
6
+ const getFile = ({ bucket, objectName }) => storage.bucket(bucket).file(objectName);
7
+ return {
8
+ writeObject: async ({ bucket, objectName, data, options }) => {
9
+ await getFile({ bucket, objectName }).save(toBuffer(data), toSaveOptions(options));
10
+ },
11
+ readObject: async ({ bucket, objectName }) => {
12
+ const [contents] = await getFile({ bucket, objectName }).download();
13
+ return contents;
14
+ },
15
+ deleteObject: async ({ bucket, objectName }) => {
16
+ await getFile({ bucket, objectName }).delete({ ignoreNotFound: true });
17
+ },
18
+ existsObject: async ({ bucket, objectName }) => {
19
+ const [exists] = await getFile({ bucket, objectName }).exists();
20
+ return exists;
21
+ },
22
+ listObjects: async ({ bucket, options }) => {
23
+ const query = toListQuery(options);
24
+ const [files, , response] = await storage
25
+ .bucket(bucket)
26
+ .getFiles(Object.keys(query).length > 0 ? query : undefined);
27
+ const nextPageToken = typeof response === 'object' && response !== null && 'nextPageToken' in response
28
+ ? response.nextPageToken
29
+ : undefined;
30
+ return {
31
+ objects: files.map(({ name }) => name),
32
+ ...(typeof nextPageToken === 'string' && { nextPageToken }),
33
+ };
34
+ },
35
+ getSignedUrl: async ({ bucket, objectName, options }) => {
36
+ const [url] = await getFile({ bucket, objectName }).getSignedUrl(toSignedUrlConfig({ options, now: Date.now() }));
37
+ return url;
38
+ },
39
+ // Written through a PassThrough so the caller gets a stream it can write to immediately,
40
+ // rather than one whose errors surface only once the upload starts.
41
+ writeStream: ({ bucket, objectName, options }) => {
42
+ const passThrough = new PassThrough();
43
+ passThrough.pipe(getFile({ bucket, objectName }).createWriteStream(toSaveOptions(options)));
44
+ return passThrough;
45
+ },
46
+ readStream: ({ bucket, objectName }) => getFile({ bucket, objectName }).createReadStream(),
47
+ };
48
+ };
package/type.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type { Readable, Writable } from 'node:stream';
2
+ export type GcsBucketConfig = {
3
+ projectId?: string;
4
+ };
5
+ export type GcsWriteOptions = {
6
+ contentType?: string;
7
+ gzip?: boolean;
8
+ metadata?: Record<string, string>;
9
+ };
10
+ export type GcsListOptions = {
11
+ prefix?: string;
12
+ maxResults?: number;
13
+ delimiter?: string;
14
+ pageToken?: string;
15
+ };
16
+ export type GcsSignedUrlAction = 'read' | 'write';
17
+ export type GcsSignedUrlOptions = {
18
+ action: GcsSignedUrlAction;
19
+ expiresInSeconds: number;
20
+ contentType?: string;
21
+ };
22
+ export type GcsListResult = {
23
+ objects: string[];
24
+ nextPageToken?: string;
25
+ };
26
+ export type GcsObjectRef = {
27
+ bucket: string;
28
+ objectName: string;
29
+ };
30
+ export type GcsClient = {
31
+ writeObject: (args: GcsObjectRef & {
32
+ data: Buffer | string;
33
+ options?: GcsWriteOptions;
34
+ }) => Promise<void>;
35
+ readObject: (args: GcsObjectRef) => Promise<Buffer>;
36
+ deleteObject: (args: GcsObjectRef) => Promise<void>;
37
+ existsObject: (args: GcsObjectRef) => Promise<boolean>;
38
+ listObjects: (args: {
39
+ bucket: string;
40
+ options?: GcsListOptions;
41
+ }) => Promise<GcsListResult>;
42
+ getSignedUrl: (args: GcsObjectRef & {
43
+ options: GcsSignedUrlOptions;
44
+ }) => Promise<string>;
45
+ writeStream: (args: GcsObjectRef & {
46
+ options?: GcsWriteOptions;
47
+ }) => Writable;
48
+ readStream: (args: GcsObjectRef) => Readable;
49
+ };
package/type.js ADDED
@@ -0,0 +1 @@
1
+ export {};