@hanzo/s3 0.6.3 → 8.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +202 -0
  2. package/MAINTAINERS.md +62 -0
  3. package/README.md +262 -0
  4. package/README_zh_CN.md +192 -0
  5. package/dist/esm/AssumeRoleProvider.d.mts +86 -0
  6. package/dist/esm/AssumeRoleProvider.mjs +183 -0
  7. package/dist/esm/CredentialProvider.d.mts +22 -0
  8. package/dist/esm/CredentialProvider.mjs +48 -0
  9. package/dist/esm/Credentials.d.mts +22 -0
  10. package/dist/esm/Credentials.mjs +38 -0
  11. package/dist/esm/IamAwsProvider.d.mts +27 -0
  12. package/dist/esm/IamAwsProvider.mjs +189 -0
  13. package/dist/esm/errors.d.mts +82 -0
  14. package/dist/esm/errors.mjs +117 -0
  15. package/dist/esm/helpers.d.mts +156 -0
  16. package/dist/esm/helpers.mjs +218 -0
  17. package/dist/esm/internal/async.d.mts +9 -0
  18. package/dist/esm/internal/async.mjs +14 -0
  19. package/dist/esm/internal/callbackify.d.mts +1 -0
  20. package/dist/esm/internal/callbackify.mjs +15 -0
  21. package/dist/esm/internal/client.d.mts +394 -0
  22. package/dist/esm/internal/client.mjs +3007 -0
  23. package/dist/esm/internal/copy-conditions.d.mts +10 -0
  24. package/dist/esm/internal/copy-conditions.mjs +25 -0
  25. package/dist/esm/internal/extensions.d.mts +18 -0
  26. package/dist/esm/internal/extensions.mjs +114 -0
  27. package/dist/esm/internal/helper.d.mts +177 -0
  28. package/dist/esm/internal/helper.mjs +552 -0
  29. package/dist/esm/internal/join-host-port.d.mts +11 -0
  30. package/dist/esm/internal/join-host-port.mjs +23 -0
  31. package/dist/esm/internal/post-policy.d.mts +17 -0
  32. package/dist/esm/internal/post-policy.mjs +98 -0
  33. package/dist/esm/internal/request.d.mts +11 -0
  34. package/dist/esm/internal/request.mjs +75 -0
  35. package/dist/esm/internal/response.d.mts +8 -0
  36. package/dist/esm/internal/response.mjs +16 -0
  37. package/dist/esm/internal/s3-endpoints.d.mts +38 -0
  38. package/dist/esm/internal/s3-endpoints.mjs +68 -0
  39. package/dist/esm/internal/type.d.mts +482 -0
  40. package/dist/esm/internal/type.mjs +30 -0
  41. package/dist/esm/internal/xml-parser.d.mts +93 -0
  42. package/dist/esm/internal/xml-parser.mjs +819 -0
  43. package/dist/esm/notification.d.mts +58 -0
  44. package/dist/esm/notification.mjs +209 -0
  45. package/dist/esm/s3.d.mts +40 -0
  46. package/dist/esm/s3.mjs +86 -0
  47. package/dist/esm/signing.d.mts +5 -0
  48. package/dist/esm/signing.mjs +258 -0
  49. package/dist/main/AssumeRoleProvider.d.ts +86 -0
  50. package/dist/main/AssumeRoleProvider.js +191 -0
  51. package/dist/main/CredentialProvider.d.ts +22 -0
  52. package/dist/main/CredentialProvider.js +55 -0
  53. package/dist/main/Credentials.d.ts +22 -0
  54. package/dist/main/Credentials.js +45 -0
  55. package/dist/main/IamAwsProvider.d.ts +27 -0
  56. package/dist/main/IamAwsProvider.js +198 -0
  57. package/dist/main/errors.d.ts +82 -0
  58. package/dist/main/errors.js +138 -0
  59. package/dist/main/helpers.d.ts +156 -0
  60. package/dist/main/helpers.js +233 -0
  61. package/dist/main/internal/async.d.ts +9 -0
  62. package/dist/main/internal/async.js +24 -0
  63. package/dist/main/internal/callbackify.d.ts +1 -0
  64. package/dist/main/internal/callbackify.js +21 -0
  65. package/dist/main/internal/client.d.ts +394 -0
  66. package/dist/main/internal/client.js +3014 -0
  67. package/dist/main/internal/copy-conditions.d.ts +10 -0
  68. package/dist/main/internal/copy-conditions.js +31 -0
  69. package/dist/main/internal/extensions.d.ts +18 -0
  70. package/dist/main/internal/extensions.js +122 -0
  71. package/dist/main/internal/helper.d.ts +177 -0
  72. package/dist/main/internal/helper.js +608 -0
  73. package/dist/main/internal/join-host-port.d.ts +11 -0
  74. package/dist/main/internal/join-host-port.js +29 -0
  75. package/dist/main/internal/post-policy.d.ts +17 -0
  76. package/dist/main/internal/post-policy.js +107 -0
  77. package/dist/main/internal/request.d.ts +11 -0
  78. package/dist/main/internal/request.js +83 -0
  79. package/dist/main/internal/response.d.ts +8 -0
  80. package/dist/main/internal/response.js +24 -0
  81. package/dist/main/internal/s3-endpoints.d.ts +38 -0
  82. package/dist/main/internal/s3-endpoints.js +73 -0
  83. package/dist/main/internal/type.d.ts +482 -0
  84. package/dist/main/internal/type.js +42 -0
  85. package/dist/main/internal/xml-parser.d.ts +93 -0
  86. package/dist/main/internal/xml-parser.js +849 -0
  87. package/dist/main/notification.d.ts +58 -0
  88. package/dist/main/notification.js +230 -0
  89. package/dist/main/s3.d.ts +40 -0
  90. package/dist/main/s3.js +117 -0
  91. package/dist/main/signing.d.ts +5 -0
  92. package/dist/main/signing.js +269 -0
  93. package/package.json +146 -39
  94. package/src/AssumeRoleProvider.ts +262 -0
  95. package/src/CredentialProvider.ts +54 -0
  96. package/src/Credentials.ts +44 -0
  97. package/src/IamAwsProvider.ts +234 -0
  98. package/src/errors.ts +120 -0
  99. package/src/helpers.ts +354 -0
  100. package/src/internal/async.ts +14 -0
  101. package/src/internal/callbackify.ts +19 -0
  102. package/src/internal/client.ts +3412 -0
  103. package/src/internal/copy-conditions.ts +30 -0
  104. package/src/internal/extensions.ts +140 -0
  105. package/src/internal/helper.ts +606 -0
  106. package/src/internal/join-host-port.ts +23 -0
  107. package/src/internal/post-policy.ts +99 -0
  108. package/src/internal/request.ts +102 -0
  109. package/src/internal/response.ts +26 -0
  110. package/src/internal/s3-endpoints.ts +70 -0
  111. package/src/internal/type.ts +577 -0
  112. package/src/internal/xml-parser.ts +871 -0
  113. package/src/notification.ts +254 -0
  114. package/src/s3.ts +155 -0
  115. package/src/signing.ts +325 -0
  116. package/lib/index.js +0 -450
  117. package/lib/index.js.map +0 -7
  118. package/lib/perfTest.js +0 -91
  119. package/lib/perfTest.js.map +0 -7
@@ -0,0 +1,99 @@
1
+ // Build PostPolicy object that can be signed by presignedPostPolicy
2
+ import * as errors from '../errors.ts'
3
+ import { isObject, isValidBucketName, isValidObjectName, isValidPrefix } from './helper.ts'
4
+ import type { ObjectMetaData } from './type.ts'
5
+
6
+ export class PostPolicy {
7
+ public policy: { conditions: (string | number)[][]; expiration?: string } = {
8
+ conditions: [],
9
+ }
10
+ public formData: Record<string, string> = {}
11
+
12
+ // set expiration date
13
+ setExpires(date: Date) {
14
+ if (!date) {
15
+ throw new errors.InvalidDateError('Invalid date: cannot be null')
16
+ }
17
+ this.policy.expiration = date.toISOString()
18
+ }
19
+
20
+ // set object name
21
+ setKey(objectName: string) {
22
+ if (!isValidObjectName(objectName)) {
23
+ throw new errors.InvalidObjectNameError(`Invalid object name : ${objectName}`)
24
+ }
25
+ this.policy.conditions.push(['eq', '$key', objectName])
26
+ this.formData.key = objectName
27
+ }
28
+
29
+ // set object name prefix, i.e policy allows any keys with this prefix
30
+ setKeyStartsWith(prefix: string) {
31
+ if (!isValidPrefix(prefix)) {
32
+ throw new errors.InvalidPrefixError(`Invalid prefix : ${prefix}`)
33
+ }
34
+ this.policy.conditions.push(['starts-with', '$key', prefix])
35
+ this.formData.key = prefix
36
+ }
37
+
38
+ // set bucket name
39
+ setBucket(bucketName: string) {
40
+ if (!isValidBucketName(bucketName)) {
41
+ throw new errors.InvalidBucketNameError(`Invalid bucket name : ${bucketName}`)
42
+ }
43
+ this.policy.conditions.push(['eq', '$bucket', bucketName])
44
+ this.formData.bucket = bucketName
45
+ }
46
+
47
+ // set Content-Type
48
+ setContentType(type: string) {
49
+ if (!type) {
50
+ throw new Error('content-type cannot be null')
51
+ }
52
+ this.policy.conditions.push(['eq', '$Content-Type', type])
53
+ this.formData['Content-Type'] = type
54
+ }
55
+
56
+ // set Content-Type prefix, i.e image/ allows any image
57
+ setContentTypeStartsWith(prefix: string) {
58
+ if (!prefix) {
59
+ throw new Error('content-type cannot be null')
60
+ }
61
+ this.policy.conditions.push(['starts-with', '$Content-Type', prefix])
62
+ this.formData['Content-Type'] = prefix
63
+ }
64
+
65
+ // set Content-Disposition
66
+ setContentDisposition(value: string) {
67
+ if (!value) {
68
+ throw new Error('content-disposition cannot be null')
69
+ }
70
+ this.policy.conditions.push(['eq', '$Content-Disposition', value])
71
+ this.formData['Content-Disposition'] = value
72
+ }
73
+
74
+ // set minimum/maximum length of what Content-Length can be.
75
+ setContentLengthRange(min: number, max: number) {
76
+ if (min > max) {
77
+ throw new Error('min cannot be more than max')
78
+ }
79
+ if (min < 0) {
80
+ throw new Error('min should be > 0')
81
+ }
82
+ if (max < 0) {
83
+ throw new Error('max should be > 0')
84
+ }
85
+ this.policy.conditions.push(['content-length-range', min, max])
86
+ }
87
+
88
+ // set user defined metadata
89
+ setUserMetaData(metaData: ObjectMetaData) {
90
+ if (!isObject(metaData)) {
91
+ throw new TypeError('metadata should be of type "object"')
92
+ }
93
+ Object.entries(metaData).forEach(([key, value]) => {
94
+ const amzMetaDataKey = `x-amz-meta-${key}`
95
+ this.policy.conditions.push(['eq', `$${amzMetaDataKey}`, value])
96
+ this.formData[amzMetaDataKey] = value.toString()
97
+ })
98
+ }
99
+ }
@@ -0,0 +1,102 @@
1
+ import type * as http from 'node:http'
2
+ import type * as https from 'node:https'
3
+ import type * as stream from 'node:stream'
4
+ import { pipeline } from 'node:stream'
5
+ import { promisify } from 'node:util'
6
+
7
+ import type { Transport } from './type.ts'
8
+
9
+ const pipelineAsync = promisify(pipeline)
10
+
11
+ export async function request(
12
+ transport: Transport,
13
+ opt: https.RequestOptions,
14
+ body: Buffer | string | stream.Readable | null = null,
15
+ ): Promise<http.IncomingMessage> {
16
+ return new Promise<http.IncomingMessage>((resolve, reject) => {
17
+ const requestObj = transport.request(opt, (response) => {
18
+ resolve(response)
19
+ })
20
+
21
+ requestObj.on('error', reject)
22
+
23
+ if (!body || Buffer.isBuffer(body) || typeof body === 'string') {
24
+ requestObj.end(body)
25
+ } else {
26
+ pipelineAsync(body, requestObj).catch(reject)
27
+ }
28
+ })
29
+ }
30
+
31
+ const MAX_RETRIES = 1
32
+ const BASE_DELAY_MS = 100 // Base delay for exponential backoff
33
+ const MAX_DELAY_MS = 60000 // Max delay for exponential backoff
34
+
35
+ // Retryable error codes for HTTP (ref: Go SDK)
36
+ export const retryHttpCodes: Record<string, boolean> = {
37
+ 408: true,
38
+ 429: true,
39
+ 499: true,
40
+ 500: true,
41
+ 502: true,
42
+ 503: true,
43
+ 504: true,
44
+ 520: true,
45
+ }
46
+
47
+ const isHttpRetryable = (httpResCode: number) => {
48
+ return retryHttpCodes[httpResCode] !== undefined
49
+ }
50
+
51
+ const sleep = (ms: number) => {
52
+ return new Promise((resolve) => setTimeout(resolve, ms))
53
+ }
54
+
55
+ const getExpBackOffDelay = (retryCount: number, baseDelayMs: number, maximumDelayMs: number) => {
56
+ const backOffBy = baseDelayMs * (1 << retryCount)
57
+ const additionalDelay = Math.random() * backOffBy
58
+ return Math.min(backOffBy + additionalDelay, maximumDelayMs)
59
+ }
60
+
61
+ export async function requestWithRetry(
62
+ transport: Transport,
63
+ opt: https.RequestOptions,
64
+ body: Buffer | string | stream.Readable | null = null,
65
+ maxRetries: number = MAX_RETRIES,
66
+ baseDelayMs: number = BASE_DELAY_MS,
67
+ maximumDelayMs: number = MAX_DELAY_MS,
68
+ ): Promise<http.IncomingMessage> {
69
+ let attempt = 0
70
+ let isRetryable = false
71
+ while (attempt <= maxRetries) {
72
+ try {
73
+ const response = await request(transport, opt, body)
74
+ // Check if the HTTP status code is retryable
75
+ if (isHttpRetryable(response.statusCode as number)) {
76
+ isRetryable = true
77
+ throw new Error(`Retryable HTTP status: ${response.statusCode}`) // trigger retry attempt with calculated delay
78
+ }
79
+
80
+ return response // Success, return the raw response
81
+ } catch (err: unknown) {
82
+ if (isRetryable) {
83
+ attempt++
84
+ isRetryable = false
85
+
86
+ if (attempt > maxRetries) {
87
+ throw new Error(`Request failed after ${maxRetries} retries: ${err}`)
88
+ }
89
+ const delay = getExpBackOffDelay(attempt, baseDelayMs, maximumDelayMs)
90
+ // eslint-disable-next-line no-console
91
+ console.warn(
92
+ `${new Date().toLocaleString()} Retrying request (attempt ${attempt}/${maxRetries}) after ${delay}ms due to: ${err}`,
93
+ )
94
+ await sleep(delay)
95
+ } else {
96
+ throw err // re-throw if any request, syntax errors
97
+ }
98
+ }
99
+ }
100
+
101
+ throw new Error(`${MAX_RETRIES} Retries exhausted, request failed.`)
102
+ }
@@ -0,0 +1,26 @@
1
+ import type http from 'node:http'
2
+ import type stream from 'node:stream'
3
+
4
+ export async function readAsBuffer(res: stream.Readable): Promise<Buffer> {
5
+ return new Promise((resolve, reject) => {
6
+ const body: Buffer[] = []
7
+ res
8
+ .on('data', (chunk: Buffer) => body.push(chunk))
9
+ .on('error', (e) => reject(e))
10
+ .on('end', () => resolve(Buffer.concat(body)))
11
+ })
12
+ }
13
+
14
+ export async function readAsString(res: http.IncomingMessage): Promise<string> {
15
+ const body = await readAsBuffer(res)
16
+ return body.toString()
17
+ }
18
+
19
+ export async function drainResponse(res: stream.Readable): Promise<void> {
20
+ return new Promise((resolve, reject) => {
21
+ res
22
+ .on('data', () => {})
23
+ .on('error', (e) => reject(e))
24
+ .on('end', () => resolve())
25
+ })
26
+ }
@@ -0,0 +1,70 @@
1
+ /*
2
+ * Hanzo S3 Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2015, 2016 Hanzo AI, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { isString } from './helper.ts'
18
+
19
+ // List of currently supported endpoints.
20
+ const awsS3Endpoint = {
21
+ 'af-south-1': 's3.af-south-1.amazonaws.com',
22
+ 'ap-east-1': 's3.ap-east-1.amazonaws.com',
23
+ 'ap-south-1': 's3.ap-south-1.amazonaws.com',
24
+ 'ap-south-2': 's3.ap-south-2.amazonaws.com',
25
+ 'ap-southeast-1': 's3.ap-southeast-1.amazonaws.com',
26
+ 'ap-southeast-2': 's3.ap-southeast-2.amazonaws.com',
27
+ 'ap-southeast-3': 's3.ap-southeast-3.amazonaws.com',
28
+ 'ap-southeast-4': 's3.ap-southeast-4.amazonaws.com',
29
+ 'ap-southeast-5': 's3.ap-southeast-5.amazonaws.com',
30
+ 'ap-northeast-1': 's3.ap-northeast-1.amazonaws.com',
31
+ 'ap-northeast-2': 's3.ap-northeast-2.amazonaws.com',
32
+ 'ap-northeast-3': 's3.ap-northeast-3.amazonaws.com',
33
+ 'ca-central-1': 's3.ca-central-1.amazonaws.com',
34
+ 'ca-west-1': 's3.ca-west-1.amazonaws.com',
35
+ 'cn-north-1': 's3.cn-north-1.amazonaws.com.cn',
36
+ 'eu-central-1': 's3.eu-central-1.amazonaws.com',
37
+ 'eu-central-2': 's3.eu-central-2.amazonaws.com',
38
+ 'eu-north-1': 's3.eu-north-1.amazonaws.com',
39
+ 'eu-south-1': 's3.eu-south-1.amazonaws.com',
40
+ 'eu-south-2': 's3.eu-south-2.amazonaws.com',
41
+ 'eu-west-1': 's3.eu-west-1.amazonaws.com',
42
+ 'eu-west-2': 's3.eu-west-2.amazonaws.com',
43
+ 'eu-west-3': 's3.eu-west-3.amazonaws.com',
44
+ 'il-central-1': 's3.il-central-1.amazonaws.com',
45
+ 'me-central-1': 's3.me-central-1.amazonaws.com',
46
+ 'me-south-1': 's3.me-south-1.amazonaws.com',
47
+ 'sa-east-1': 's3.sa-east-1.amazonaws.com',
48
+ 'us-east-1': 's3.us-east-1.amazonaws.com',
49
+ 'us-east-2': 's3.us-east-2.amazonaws.com',
50
+ 'us-west-1': 's3.us-west-1.amazonaws.com',
51
+ 'us-west-2': 's3.us-west-2.amazonaws.com',
52
+ 'us-gov-east-1': 's3.us-gov-east-1.amazonaws.com',
53
+ 'us-gov-west-1': 's3.us-gov-west-1.amazonaws.com',
54
+ // Add new endpoints here.
55
+ }
56
+
57
+ export type Region = keyof typeof awsS3Endpoint | string
58
+
59
+ // getS3Endpoint get relevant endpoint for the region.
60
+ export function getS3Endpoint(region: Region): string {
61
+ if (!isString(region)) {
62
+ throw new TypeError(`Invalid region: ${region}`)
63
+ }
64
+
65
+ const endpoint = (awsS3Endpoint as Record<string, string>)[region]
66
+ if (endpoint) {
67
+ return endpoint
68
+ }
69
+ return 's3.amazonaws.com'
70
+ }