s3mini 0.4.0 → 0.6.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 +12 -4
- package/dist/s3mini.d.ts +179 -51
- package/dist/s3mini.js +439 -229
- package/dist/s3mini.js.map +1 -1
- package/dist/s3mini.min.js +1 -1
- package/dist/s3mini.min.js.map +1 -1
- package/package.json +22 -19
- package/src/S3.ts +491 -256
- package/src/consts.ts +1 -1
- package/src/index.ts +2 -2
- package/src/types.ts +62 -13
- package/src/utils.ts +67 -22
package/src/consts.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const DEFAULT_REQUEST_SIZE_IN_BYTES = 8 * 1024 * 1024;
|
|
|
13
13
|
|
|
14
14
|
// Headers
|
|
15
15
|
export const HEADER_AMZ_CONTENT_SHA256 = 'x-amz-content-sha256';
|
|
16
|
+
export const HEADER_AMZ_CHECKSUM_SHA256 = 'x-amz-checksum-sha256';
|
|
16
17
|
export const HEADER_AMZ_DATE = 'x-amz-date';
|
|
17
18
|
export const HEADER_HOST = 'host';
|
|
18
19
|
export const HEADER_AUTHORIZATION = 'authorization';
|
|
@@ -32,7 +33,6 @@ export const ERROR_UPLOAD_ID_REQUIRED = `${ERROR_PREFIX}uploadId must be a non-e
|
|
|
32
33
|
export const ERROR_PARTS_REQUIRED = `${ERROR_PREFIX}parts must be a non-empty array`;
|
|
33
34
|
export const ERROR_INVALID_PART = `${ERROR_PREFIX}Each part must have a partNumber (number) and ETag (string)`;
|
|
34
35
|
export const ERROR_DATA_BUFFER_REQUIRED = `${ERROR_PREFIX}data must be a Buffer or string`;
|
|
35
|
-
// const ERROR_PATH_REQUIRED = `${ERROR_PREFIX}path must be a string`;
|
|
36
36
|
export const ERROR_PREFIX_TYPE = `${ERROR_PREFIX}prefix must be a string`;
|
|
37
37
|
export const ERROR_MAX_KEYS_TYPE = `${ERROR_PREFIX}maxKeys must be a positive integer`;
|
|
38
38
|
export const ERROR_DELIMITER_REQUIRED = `${ERROR_PREFIX}delimiter must be a string`;
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import { S3mini
|
|
3
|
+
import { S3mini } from './S3.js';
|
|
4
4
|
import { sanitizeETag, runInBatches } from './utils.js';
|
|
5
5
|
|
|
6
6
|
// Export the S3 class as default export and named export
|
|
7
|
-
export { S3mini,
|
|
7
|
+
export { S3mini, sanitizeETag, runInBatches };
|
|
8
8
|
export default S3mini;
|
|
9
9
|
|
|
10
10
|
// Re-export types
|
package/src/types.ts
CHANGED
|
@@ -14,18 +14,8 @@ export interface SSECHeaders {
|
|
|
14
14
|
'x-amz-server-side-encryption-customer-key-md5': string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export interface
|
|
18
|
-
|
|
19
|
-
algorithm: string,
|
|
20
|
-
key: Buffer | string,
|
|
21
|
-
) => {
|
|
22
|
-
update: (data: Buffer | string) => { digest: (encoding?: string) => string | Buffer };
|
|
23
|
-
digest: (encoding?: string) => string | Buffer;
|
|
24
|
-
};
|
|
25
|
-
createHash: (algorithm: string) => {
|
|
26
|
-
update: (data: Buffer | string) => { digest: (encoding?: string) => string | Buffer };
|
|
27
|
-
digest: (encoding?: string) => string | Buffer;
|
|
28
|
-
};
|
|
17
|
+
export interface AWSHeaders {
|
|
18
|
+
[k: `x-amz-${string}`]: string;
|
|
29
19
|
}
|
|
30
20
|
|
|
31
21
|
export interface Logger {
|
|
@@ -72,7 +62,7 @@ export interface ListMultipartUploadSuccess {
|
|
|
72
62
|
key: string;
|
|
73
63
|
uploadId: string;
|
|
74
64
|
size?: number;
|
|
75
|
-
mtime?: Date
|
|
65
|
+
mtime?: Date;
|
|
76
66
|
etag?: string;
|
|
77
67
|
eTag?: string; // for backward compatibility
|
|
78
68
|
parts: UploadPart[];
|
|
@@ -107,3 +97,62 @@ export interface XmlMap {
|
|
|
107
97
|
[key: string]: XmlValue | XmlValue[]; // one or many children
|
|
108
98
|
[key: number]: XmlValue | XmlValue[]; // allow numeric keys
|
|
109
99
|
}
|
|
100
|
+
|
|
101
|
+
export interface CopyObjectOptions {
|
|
102
|
+
/**
|
|
103
|
+
* Specifies whether the metadata is copied from the source object or replaced with metadata provided in the request.
|
|
104
|
+
* Valid values: 'COPY' | 'REPLACE'
|
|
105
|
+
* Default: 'COPY'
|
|
106
|
+
*/
|
|
107
|
+
metadataDirective?: 'COPY' | 'REPLACE';
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Metadata to be set on the destination object when metadataDirective is 'REPLACE'.
|
|
111
|
+
* Keys can be provided with or without 'x-amz-meta-' prefix.
|
|
112
|
+
*/
|
|
113
|
+
metadata?: Record<string, string>;
|
|
114
|
+
contentType?: string;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Storage class for the destination object.
|
|
118
|
+
* Valid values: 'STANDARD' | 'REDUCED_REDUNDANCY' | 'STANDARD_IA' | 'ONEZONE_IA' | 'INTELLIGENT_TIERING' | 'GLACIER' | 'DEEP_ARCHIVE' | 'GLACIER_IR'
|
|
119
|
+
*/
|
|
120
|
+
storageClass?: string;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Specifies whether the object tag-set is copied from the source object or replaced with tag-set provided in the request.
|
|
124
|
+
* Valid values: 'COPY' | 'REPLACE'
|
|
125
|
+
*/
|
|
126
|
+
taggingDirective?: 'COPY' | 'REPLACE';
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* If the bucket is configured as a website, redirects requests for this object to another object or URL.
|
|
130
|
+
*/
|
|
131
|
+
websiteRedirectLocation?: string;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Server-Side Encryption with Customer-Provided Keys headers for the source object.
|
|
135
|
+
* Should include:
|
|
136
|
+
* - x-amz-copy-source-server-side-encryption-customer-algorithm
|
|
137
|
+
* - x-amz-copy-source-server-side-encryption-customer-key
|
|
138
|
+
* - x-amz-copy-source-server-side-encryption-customer-key-MD5
|
|
139
|
+
*/
|
|
140
|
+
sourceSSECHeaders?: Record<string, string | number>;
|
|
141
|
+
destinationSSECHeaders?: SSECHeaders;
|
|
142
|
+
additionalHeaders?: Record<string, string | number>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface CopyObjectResult {
|
|
146
|
+
etag: string;
|
|
147
|
+
lastModified?: Date;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Where Buffer is available, e.g. when @types/node is loaded, we want to use it.
|
|
152
|
+
* But it should be excluded in other environments (e.g. Cloudflare).
|
|
153
|
+
*/
|
|
154
|
+
export type MaybeBuffer = typeof globalThis extends { Buffer?: infer B }
|
|
155
|
+
? B extends new (...a: unknown[]) => unknown
|
|
156
|
+
? InstanceType<B>
|
|
157
|
+
: ArrayBuffer | Uint8Array
|
|
158
|
+
: ArrayBuffer | Uint8Array;
|
package/src/utils.ts
CHANGED
|
@@ -1,35 +1,80 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
import type {
|
|
3
|
-
declare const crypto: Crypto;
|
|
2
|
+
import type { XmlValue, XmlMap, ListBucketResponse, ErrorWithCode } from './types.js';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
4
|
+
const ENCODR = new TextEncoder();
|
|
5
|
+
const chunkSize = 0x8000; // 32KB chunks
|
|
6
|
+
const HEXS = '0123456789abcdef';
|
|
7
|
+
|
|
8
|
+
export const getByteSize = (data: unknown): number => {
|
|
9
|
+
if (typeof data === 'string') {
|
|
10
|
+
return ENCODR.encode(data).byteLength;
|
|
11
|
+
}
|
|
12
|
+
if (data instanceof ArrayBuffer || data instanceof Uint8Array) {
|
|
13
|
+
return data.byteLength;
|
|
14
|
+
}
|
|
15
|
+
if (data instanceof Blob) {
|
|
16
|
+
return data.size;
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Unsupported data type');
|
|
19
|
+
};
|
|
9
20
|
|
|
10
21
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @param {
|
|
13
|
-
* @returns {string}
|
|
22
|
+
* Turn a raw ArrayBuffer into its hexadecimal representation.
|
|
23
|
+
* @param {ArrayBuffer} buffer The raw bytes.
|
|
24
|
+
* @returns {string} Hexadecimal string
|
|
14
25
|
*/
|
|
15
|
-
export const
|
|
16
|
-
|
|
26
|
+
export const hexFromBuffer = (buffer: ArrayBuffer): string => {
|
|
27
|
+
const bytes = new Uint8Array(buffer);
|
|
28
|
+
let hex = '';
|
|
29
|
+
for (const byte of bytes) {
|
|
30
|
+
hex += HEXS[byte >> 4]! + HEXS[byte & 0x0f]!;
|
|
31
|
+
}
|
|
32
|
+
return hex;
|
|
17
33
|
};
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Turn a raw ArrayBuffer into its base64 representation.
|
|
37
|
+
* @param {ArrayBuffer} buffer The raw bytes.
|
|
38
|
+
* @returns {string} Base64 string
|
|
39
|
+
*/
|
|
40
|
+
export const base64FromBuffer = (buffer: ArrayBuffer): string => {
|
|
41
|
+
const bytes = new Uint8Array(buffer);
|
|
42
|
+
let result = '';
|
|
43
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
44
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
45
|
+
result += btoa(String.fromCharCode.apply(null, chunk as unknown as number[]));
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
21
48
|
};
|
|
22
49
|
|
|
23
50
|
/**
|
|
24
|
-
* Compute
|
|
25
|
-
* @param {string
|
|
26
|
-
* @
|
|
27
|
-
* @param {BufferEncoding} [encoding='hex'] – hex | base64 | …
|
|
28
|
-
* @returns {string | Buffer} hex encoded HMAC
|
|
51
|
+
* Compute SHA-256 hash of arbitrary string data.
|
|
52
|
+
* @param {string} content The content to be hashed.
|
|
53
|
+
* @returns {ArrayBuffer} The raw hash
|
|
29
54
|
*/
|
|
30
|
-
export const
|
|
31
|
-
const
|
|
32
|
-
|
|
55
|
+
export const sha256 = async (content: string): Promise<ArrayBuffer> => {
|
|
56
|
+
const data = ENCODR.encode(content);
|
|
57
|
+
|
|
58
|
+
return await globalThis.crypto.subtle.digest('SHA-256', data);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compute HMAC-SHA-256 of arbitrary data.
|
|
63
|
+
* @param {string|ArrayBuffer} key The key used to sign the content.
|
|
64
|
+
* @param {string} content The content to be signed.
|
|
65
|
+
* @returns {ArrayBuffer} The raw signature
|
|
66
|
+
*/
|
|
67
|
+
export const hmac = async (key: string | ArrayBuffer, content: string): Promise<ArrayBuffer> => {
|
|
68
|
+
const secret = await globalThis.crypto.subtle.importKey(
|
|
69
|
+
'raw',
|
|
70
|
+
typeof key === 'string' ? ENCODR.encode(key) : key,
|
|
71
|
+
{ name: 'HMAC', hash: 'SHA-256' },
|
|
72
|
+
false,
|
|
73
|
+
['sign'],
|
|
74
|
+
);
|
|
75
|
+
const data = ENCODR.encode(content);
|
|
76
|
+
|
|
77
|
+
return await globalThis.crypto.subtle.sign('HMAC', secret, data);
|
|
33
78
|
};
|
|
34
79
|
|
|
35
80
|
/**
|
|
@@ -45,7 +90,7 @@ export const sanitizeETag = (etag: string): string => {
|
|
|
45
90
|
'"': '',
|
|
46
91
|
'"': '',
|
|
47
92
|
};
|
|
48
|
-
return etag.replace(
|
|
93
|
+
return etag.replace(/(^("|"|"))|(("|"|")$)/g, m => replaceChars[m] as string);
|
|
49
94
|
};
|
|
50
95
|
|
|
51
96
|
const entityMap = {
|