@lumeweb/pinner 0.0.1 → 0.1.1

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 (200) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +707 -28
  3. package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
  4. package/dist/cjs/adapters/pinata/index.cjs +6 -0
  5. package/dist/cjs/adapters/pinata/legacy/adapter.cjs +83 -0
  6. package/dist/cjs/adapters/pinata/legacy/adapter.cjs.map +1 -0
  7. package/dist/cjs/adapters/pinata/legacy/adapter.d.cts +74 -0
  8. package/dist/cjs/adapters/pinata/legacy/index.cjs +1 -0
  9. package/dist/cjs/adapters/pinata/shared/index.cjs +1 -0
  10. package/dist/cjs/adapters/pinata/shared/types.d.cts +218 -0
  11. package/dist/cjs/adapters/pinata/shared/utils.cjs +83 -0
  12. package/dist/cjs/adapters/pinata/shared/utils.cjs.map +1 -0
  13. package/dist/cjs/adapters/pinata/v2/adapter-interface.d.cts +198 -0
  14. package/dist/cjs/adapters/pinata/v2/adapter.cjs +636 -0
  15. package/dist/cjs/adapters/pinata/v2/adapter.cjs.map +1 -0
  16. package/dist/cjs/adapters/pinata/v2/adapter.d.cts +17 -0
  17. package/dist/cjs/adapters/pinata/v2/index.cjs +1 -0
  18. package/dist/cjs/adapters/pinata/v2/types.d.cts +308 -0
  19. package/dist/cjs/blockstore/index.cjs +2 -0
  20. package/dist/cjs/blockstore/unstorage-base.cjs +240 -0
  21. package/dist/cjs/blockstore/unstorage-base.cjs.map +1 -0
  22. package/dist/cjs/blockstore/unstorage-base.d.cts +23 -0
  23. package/dist/cjs/blockstore/unstorage.cjs +39 -0
  24. package/dist/cjs/blockstore/unstorage.cjs.map +1 -0
  25. package/dist/cjs/blockstore/unstorage.d.cts +36 -0
  26. package/dist/cjs/config.d.cts +51 -0
  27. package/dist/cjs/encoder/base64.cjs +38 -0
  28. package/dist/cjs/encoder/base64.cjs.map +1 -0
  29. package/dist/cjs/encoder/csv/csv-formatter.cjs +81 -0
  30. package/dist/cjs/encoder/csv/csv-formatter.cjs.map +1 -0
  31. package/dist/cjs/encoder/csv/field-formatter.cjs +76 -0
  32. package/dist/cjs/encoder/csv/field-formatter.cjs.map +1 -0
  33. package/dist/cjs/encoder/csv/row-formatter.cjs +159 -0
  34. package/dist/cjs/encoder/csv/row-formatter.cjs.map +1 -0
  35. package/dist/cjs/encoder/csv.cjs +44 -0
  36. package/dist/cjs/encoder/csv.cjs.map +1 -0
  37. package/dist/cjs/encoder/error.cjs +19 -0
  38. package/dist/cjs/encoder/error.cjs.map +1 -0
  39. package/dist/cjs/encoder/index.cjs +6 -0
  40. package/dist/cjs/encoder/json.cjs +36 -0
  41. package/dist/cjs/encoder/json.cjs.map +1 -0
  42. package/dist/cjs/encoder/text.cjs +35 -0
  43. package/dist/cjs/encoder/text.cjs.map +1 -0
  44. package/dist/cjs/encoder/url.cjs +39 -0
  45. package/dist/cjs/encoder/url.cjs.map +1 -0
  46. package/dist/cjs/errors/index.cjs +104 -0
  47. package/dist/cjs/errors/index.cjs.map +1 -0
  48. package/dist/cjs/errors/index.d.cts +47 -0
  49. package/dist/cjs/index.cjs +44 -0
  50. package/dist/cjs/index.d.cts +16 -0
  51. package/dist/cjs/pin/client.cjs +98 -0
  52. package/dist/cjs/pin/client.cjs.map +1 -0
  53. package/dist/cjs/pin/index.cjs +1 -0
  54. package/dist/cjs/pinner.cjs +132 -0
  55. package/dist/cjs/pinner.cjs.map +1 -0
  56. package/dist/cjs/pinner.d.cts +81 -0
  57. package/dist/cjs/types/constants.cjs +39 -0
  58. package/dist/cjs/types/constants.cjs.map +1 -0
  59. package/dist/cjs/types/mime-types.cjs +11 -0
  60. package/dist/cjs/types/mime-types.cjs.map +1 -0
  61. package/dist/cjs/types/mime-types.d.cts +7 -0
  62. package/dist/cjs/types/pin.d.cts +78 -0
  63. package/dist/cjs/types/type-guards.cjs +20 -0
  64. package/dist/cjs/types/type-guards.cjs.map +1 -0
  65. package/dist/cjs/types/type-guards.d.cts +15 -0
  66. package/dist/cjs/types/upload.cjs +18 -0
  67. package/dist/cjs/types/upload.cjs.map +1 -0
  68. package/dist/cjs/types/upload.d.cts +189 -0
  69. package/dist/cjs/upload/base-upload.cjs +135 -0
  70. package/dist/cjs/upload/base-upload.cjs.map +1 -0
  71. package/dist/cjs/upload/builder.cjs +174 -0
  72. package/dist/cjs/upload/builder.cjs.map +1 -0
  73. package/dist/cjs/upload/builder.d.cts +60 -0
  74. package/dist/cjs/upload/car.cjs +129 -0
  75. package/dist/cjs/upload/car.cjs.map +1 -0
  76. package/dist/cjs/upload/car.d.cts +19 -0
  77. package/dist/cjs/upload/constants.cjs +9 -0
  78. package/dist/cjs/upload/constants.cjs.map +1 -0
  79. package/dist/cjs/upload/index.cjs +8 -0
  80. package/dist/cjs/upload/manager.cjs +249 -0
  81. package/dist/cjs/upload/manager.cjs.map +1 -0
  82. package/dist/cjs/upload/manager.d.cts +35 -0
  83. package/dist/cjs/upload/normalize.cjs +28 -0
  84. package/dist/cjs/upload/normalize.cjs.map +1 -0
  85. package/dist/cjs/upload/tus-upload.cjs +74 -0
  86. package/dist/cjs/upload/tus-upload.cjs.map +1 -0
  87. package/dist/cjs/upload/xhr-upload.cjs +41 -0
  88. package/dist/cjs/upload/xhr-upload.cjs.map +1 -0
  89. package/dist/cjs/utils/env.cjs +12 -0
  90. package/dist/cjs/utils/env.cjs.map +1 -0
  91. package/dist/cjs/utils/stream.cjs +141 -0
  92. package/dist/cjs/utils/stream.cjs.map +1 -0
  93. package/dist/cjs/utils/stream.d.cts +23 -0
  94. package/dist/cjs/utils/tus-patch.cjs +50 -0
  95. package/dist/cjs/utils/tus-patch.cjs.map +1 -0
  96. package/dist/cjs/utils/validation.cjs +62 -0
  97. package/dist/cjs/utils/validation.cjs.map +1 -0
  98. package/dist/esm/_virtual/rolldown_runtime.js +8 -0
  99. package/dist/esm/adapters/pinata/index.d.ts +7 -0
  100. package/dist/esm/adapters/pinata/index.js +6 -0
  101. package/dist/esm/adapters/pinata/legacy/adapter.d.ts +74 -0
  102. package/dist/esm/adapters/pinata/legacy/adapter.js +83 -0
  103. package/dist/esm/adapters/pinata/legacy/adapter.js.map +1 -0
  104. package/dist/esm/adapters/pinata/legacy/index.d.ts +1 -0
  105. package/dist/esm/adapters/pinata/legacy/index.js +1 -0
  106. package/dist/esm/adapters/pinata/shared/index.d.ts +2 -0
  107. package/dist/esm/adapters/pinata/shared/index.js +1 -0
  108. package/dist/esm/adapters/pinata/shared/types.d.ts +218 -0
  109. package/dist/esm/adapters/pinata/shared/utils.d.ts +1 -0
  110. package/dist/esm/adapters/pinata/shared/utils.js +78 -0
  111. package/dist/esm/adapters/pinata/shared/utils.js.map +1 -0
  112. package/dist/esm/adapters/pinata/v2/adapter-interface.d.ts +198 -0
  113. package/dist/esm/adapters/pinata/v2/adapter.d.ts +17 -0
  114. package/dist/esm/adapters/pinata/v2/adapter.js +636 -0
  115. package/dist/esm/adapters/pinata/v2/adapter.js.map +1 -0
  116. package/dist/esm/adapters/pinata/v2/index.d.ts +3 -0
  117. package/dist/esm/adapters/pinata/v2/index.js +1 -0
  118. package/dist/esm/adapters/pinata/v2/types.d.ts +308 -0
  119. package/dist/esm/blockstore/index.d.ts +2 -0
  120. package/dist/esm/blockstore/index.js +2 -0
  121. package/dist/esm/blockstore/unstorage-base.d.ts +23 -0
  122. package/dist/esm/blockstore/unstorage-base.js +231 -0
  123. package/dist/esm/blockstore/unstorage-base.js.map +1 -0
  124. package/dist/esm/blockstore/unstorage.d.ts +36 -0
  125. package/dist/esm/blockstore/unstorage.js +38 -0
  126. package/dist/esm/blockstore/unstorage.js.map +1 -0
  127. package/dist/esm/config.d.ts +51 -0
  128. package/dist/esm/encoder/base64.js +37 -0
  129. package/dist/esm/encoder/base64.js.map +1 -0
  130. package/dist/esm/encoder/csv/csv-formatter.js +81 -0
  131. package/dist/esm/encoder/csv/csv-formatter.js.map +1 -0
  132. package/dist/esm/encoder/csv/field-formatter.js +75 -0
  133. package/dist/esm/encoder/csv/field-formatter.js.map +1 -0
  134. package/dist/esm/encoder/csv/row-formatter.js +159 -0
  135. package/dist/esm/encoder/csv/row-formatter.js.map +1 -0
  136. package/dist/esm/encoder/csv.js +43 -0
  137. package/dist/esm/encoder/csv.js.map +1 -0
  138. package/dist/esm/encoder/error.js +18 -0
  139. package/dist/esm/encoder/error.js.map +1 -0
  140. package/dist/esm/encoder/index.js +6 -0
  141. package/dist/esm/encoder/json.js +35 -0
  142. package/dist/esm/encoder/json.js.map +1 -0
  143. package/dist/esm/encoder/text.js +34 -0
  144. package/dist/esm/encoder/text.js.map +1 -0
  145. package/dist/esm/encoder/url.js +36 -0
  146. package/dist/esm/encoder/url.js.map +1 -0
  147. package/dist/esm/errors/index.d.ts +47 -0
  148. package/dist/esm/errors/index.js +93 -0
  149. package/dist/esm/errors/index.js.map +1 -0
  150. package/dist/esm/index.d.ts +18 -0
  151. package/dist/esm/index.js +15 -0
  152. package/dist/esm/pin/client.js +97 -0
  153. package/dist/esm/pin/client.js.map +1 -0
  154. package/dist/esm/pin/index.js +1 -0
  155. package/dist/esm/pinner.d.ts +81 -0
  156. package/dist/esm/pinner.js +131 -0
  157. package/dist/esm/pinner.js.map +1 -0
  158. package/dist/esm/types/constants.js +33 -0
  159. package/dist/esm/types/constants.js.map +1 -0
  160. package/dist/esm/types/mime-types.d.ts +7 -0
  161. package/dist/esm/types/mime-types.js +8 -0
  162. package/dist/esm/types/mime-types.js.map +1 -0
  163. package/dist/esm/types/pin.d.ts +78 -0
  164. package/dist/esm/types/type-guards.d.ts +15 -0
  165. package/dist/esm/types/type-guards.js +19 -0
  166. package/dist/esm/types/type-guards.js.map +1 -0
  167. package/dist/esm/types/upload.d.ts +189 -0
  168. package/dist/esm/types/upload.js +16 -0
  169. package/dist/esm/types/upload.js.map +1 -0
  170. package/dist/esm/upload/base-upload.js +132 -0
  171. package/dist/esm/upload/base-upload.js.map +1 -0
  172. package/dist/esm/upload/builder.d.ts +60 -0
  173. package/dist/esm/upload/builder.js +173 -0
  174. package/dist/esm/upload/builder.js.map +1 -0
  175. package/dist/esm/upload/car.d.ts +19 -0
  176. package/dist/esm/upload/car.js +125 -0
  177. package/dist/esm/upload/car.js.map +1 -0
  178. package/dist/esm/upload/constants.js +7 -0
  179. package/dist/esm/upload/constants.js.map +1 -0
  180. package/dist/esm/upload/index.js +8 -0
  181. package/dist/esm/upload/manager.d.ts +35 -0
  182. package/dist/esm/upload/manager.js +248 -0
  183. package/dist/esm/upload/manager.js.map +1 -0
  184. package/dist/esm/upload/normalize.js +28 -0
  185. package/dist/esm/upload/normalize.js.map +1 -0
  186. package/dist/esm/upload/tus-upload.js +72 -0
  187. package/dist/esm/upload/tus-upload.js.map +1 -0
  188. package/dist/esm/upload/xhr-upload.js +39 -0
  189. package/dist/esm/upload/xhr-upload.js.map +1 -0
  190. package/dist/esm/utils/env.js +11 -0
  191. package/dist/esm/utils/env.js.map +1 -0
  192. package/dist/esm/utils/stream.d.ts +23 -0
  193. package/dist/esm/utils/stream.js +134 -0
  194. package/dist/esm/utils/stream.js.map +1 -0
  195. package/dist/esm/utils/tus-patch.js +51 -0
  196. package/dist/esm/utils/tus-patch.js.map +1 -0
  197. package/dist/esm/utils/validation.js +60 -0
  198. package/dist/esm/utils/validation.js.map +1 -0
  199. package/package.json +95 -8
  200. package/public/mockServiceWorker.js +349 -0
@@ -0,0 +1,249 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_upload = require('../types/upload.cjs');
3
+ const require_mime_types = require('../types/mime-types.cjs');
4
+ const require_stream = require('../utils/stream.cjs');
5
+ const require_xhr_upload = require('./xhr-upload.cjs');
6
+ const require_tus_upload = require('./tus-upload.cjs');
7
+ const require_constants = require('../types/constants.cjs');
8
+ const require_car = require('./car.cjs');
9
+ const require_index = require('../errors/index.cjs');
10
+ let _lumeweb_portal_sdk = require("@lumeweb/portal-sdk");
11
+ let _lumeweb_query_builder = require("@lumeweb/query-builder");
12
+
13
+ //#region src/upload/manager.ts
14
+ var UploadManager = class {
15
+ xhrHandler;
16
+ tusHandler;
17
+ portalSdk;
18
+ uploadLimit = require_constants.TUS_SIZE_THRESHOLD;
19
+ limitFetched = false;
20
+ constructor(config) {
21
+ this.xhrHandler = new require_xhr_upload.XHRUploadHandler(config);
22
+ this.tusHandler = new require_tus_upload.TUSUploadHandler(config);
23
+ this.portalSdk = new _lumeweb_portal_sdk.Sdk(config.endpoint || require_constants.DEFAULT_ENDPOINT);
24
+ require_car.configureCar({
25
+ datastoreName: config.datastoreName,
26
+ datastore: config.datastore
27
+ });
28
+ }
29
+ async fetchUploadLimit() {
30
+ if (this.limitFetched) return this.uploadLimit;
31
+ try {
32
+ const result = await this.portalSdk.account().uploadLimit();
33
+ if (result.success && result.data?.limit) this.uploadLimit = result.data.limit;
34
+ } catch {
35
+ this.uploadLimit = require_constants.TUS_SIZE_THRESHOLD;
36
+ }
37
+ this.limitFetched = true;
38
+ return this.uploadLimit;
39
+ }
40
+ getUploadLimit() {
41
+ return this.uploadLimit;
42
+ }
43
+ async upload(input, options) {
44
+ this.#validateInput(input, options);
45
+ return this.#uploadInput(input, options);
46
+ }
47
+ async uploadCar(input, options) {
48
+ this.#validateInput(input, options);
49
+ return this.#uploadCarFile(input, options);
50
+ }
51
+ /**
52
+ * Wait for an operation to complete or reach a settled state.
53
+ *
54
+ * Handles two scenarios:
55
+ * 1. If an operationId is provided (in UploadResult), uses it directly
56
+ * 2. If only CID is available, lists operations filtered by CID and polls the first result
57
+ *
58
+ * @param input Either an operation ID (number) or an UploadResult
59
+ * @param options Polling options (interval, timeout, settledStates)
60
+ * @returns UploadResult with operation status merged in
61
+ */
62
+ async waitForOperation(input, options) {
63
+ if (require_upload.isUploadResult(input) && input.operationId) {
64
+ const result = await this.portalSdk.account().waitForOperation(input.operationId, options);
65
+ if (!result.success) throw new Error(result.error?.message || `Operation ${input.operationId} failed`);
66
+ const operation = result.data;
67
+ if (!operation.cid) throw new Error(`Operation ${input.operationId} completed without CID`);
68
+ if (operation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.FAILED || operation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.ERROR) throw new Error(`Operation ${input.operationId} failed: ${operation.error || operation.status_message || "Unknown error"}`);
69
+ return {
70
+ ...input,
71
+ cid: operation.cid,
72
+ operationId: operation.id
73
+ };
74
+ }
75
+ if (require_upload.isUploadResult(input) && input.cid) return await this.#waitForOperationByCid(input, options);
76
+ const operationId = typeof input === "number" ? input : void 0;
77
+ if (operationId) {
78
+ const result = await this.portalSdk.account().waitForOperation(operationId, options);
79
+ if (!result.success) throw new Error(result.error?.message || `Operation ${operationId} failed`);
80
+ const operation = result.data;
81
+ if (!operation.cid) throw new Error(`Operation ${operationId} completed without CID`);
82
+ if (operation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.FAILED || operation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.ERROR) throw new Error(`Operation ${operationId} failed: ${operation.error || operation.status_message || "Unknown error"}`);
83
+ return {
84
+ id: operationId.toString(),
85
+ cid: operation.cid,
86
+ name: operation.operation_display_name || "Unknown",
87
+ size: 0,
88
+ mimeType: "",
89
+ createdAt: new Date(operation.started_at),
90
+ numberOfFiles: 1,
91
+ operationId: operation.id
92
+ };
93
+ }
94
+ throw new Error("No operation ID or CID provided, cannot wait for operation");
95
+ }
96
+ /**
97
+ * Wait for an operation by CID.
98
+ *
99
+ * This is used when we have a CID from an upload but no operation ID.
100
+ * We list operations filtered by CID to find the operation ID,
101
+ * then use the SDK's waitForOperation for polling.
102
+ *
103
+ * @param uploadResult UploadResult with CID
104
+ * @param options Polling options (interval, timeout, settledStates)
105
+ * @returns UploadResult with operation status merged in
106
+ */
107
+ async #waitForOperationByCid(uploadResult, options) {
108
+ const params = {
109
+ filters: [(0, _lumeweb_query_builder.createEqFilter)("cid", uploadResult.cid)],
110
+ pagination: {
111
+ start: 0,
112
+ end: 1
113
+ }
114
+ };
115
+ const result = await this.portalSdk.account().listOperations(params);
116
+ if (!result.success) throw new Error(`Failed to find operation with CID ${uploadResult.cid}`);
117
+ const operation = result.data.data?.[0];
118
+ if (!operation) throw new Error(`Failed to find operation with CID ${uploadResult.cid}`);
119
+ const operationId = operation.id;
120
+ const waitResult = await this.portalSdk.account().waitForOperation(operationId, options);
121
+ if (!waitResult.success) throw new Error(waitResult.error?.message || `Operation ${operationId} failed`);
122
+ const finalOperation = waitResult.data;
123
+ if (!finalOperation.cid) throw new Error(`Operation ${operationId} completed without CID`);
124
+ if (finalOperation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.FAILED || finalOperation.status?.toLowerCase() === _lumeweb_portal_sdk.OPERATION_STATUS.ERROR) throw new Error(`Operation ${operationId} failed: ${finalOperation.error || finalOperation.status_message || "Unknown error"}`);
125
+ return {
126
+ ...uploadResult,
127
+ cid: finalOperation.cid,
128
+ operationId: finalOperation.id
129
+ };
130
+ }
131
+ #validateInput(input, options) {
132
+ if (input instanceof File) {
133
+ if (input.size === 0) throw new require_index.EmptyFileError(`Cannot upload empty file: ${input.name}`);
134
+ } else if (input instanceof ReadableStream) {
135
+ if (options?.size !== void 0 && options.size === 0) throw new require_index.EmptyFileError("Cannot upload empty stream");
136
+ }
137
+ }
138
+ async uploadDirectory(files, options) {
139
+ const carResult = await require_car.preprocessToCar(files, {
140
+ onProgress: options?.onProgress ? (p) => options.onProgress({
141
+ percentage: p,
142
+ bytesUploaded: 0,
143
+ bytesTotal: 0
144
+ }) : void 0,
145
+ signal: options?.signal
146
+ });
147
+ const operation = await this.#uploadCarResult(carResult, options?.name || "directory", options);
148
+ if (options?.waitForOperation && operation.result) {
149
+ const uploadResult = await operation.result;
150
+ operation.result = this.waitForOperation({
151
+ ...uploadResult,
152
+ isDirectory: true,
153
+ numberOfFiles: files.length
154
+ }, options.operationPollingOptions);
155
+ }
156
+ return operation;
157
+ }
158
+ async #uploadInput(input, options) {
159
+ if (await this.#isCarFileUpload(input, options)) return this.#uploadCarFile(input, options);
160
+ const limit = await this.fetchUploadLimit();
161
+ if (input instanceof ReadableStream) {
162
+ const [streamForSize, streamForUpload] = input.tee();
163
+ let size;
164
+ if (options?.size !== void 0) size = BigInt(options.size);
165
+ else size = await require_stream.calculateStreamSize(streamForSize, options?.signal);
166
+ if (size >= BigInt(limit)) return this.#uploadFile({
167
+ data: streamForUpload,
168
+ name: options?.name || "upload",
169
+ type: options?.name?.endsWith(require_mime_types.FILE_EXTENSION_CAR) || options?.isDirectory ? require_mime_types.MIME_TYPE_CAR : require_mime_types.MIME_TYPE_OCTET_STREAM,
170
+ size: Number(size)
171
+ }, options);
172
+ else {
173
+ const blob = await require_stream.streamToBlob(streamForUpload, "application/octet-stream");
174
+ const file = new File([blob], options?.name || "upload", { type: blob.type });
175
+ return this.#uploadFile(file, options);
176
+ }
177
+ }
178
+ return this.#uploadFile(input, options);
179
+ }
180
+ async #isCarFileUpload(input, options) {
181
+ if (options?.isCarFile === true) return true;
182
+ if (options?.isCarFile === false) return false;
183
+ if (input instanceof File) {
184
+ if (input.type === require_mime_types.MIME_TYPE_CAR || input.name.endsWith(require_mime_types.FILE_EXTENSION_CAR)) return await require_car.isCarFile(input);
185
+ }
186
+ if (input instanceof ReadableStream && options?.name?.endsWith(require_mime_types.FILE_EXTENSION_CAR)) return options?.isCarFile !== false;
187
+ return false;
188
+ }
189
+ async #uploadCarResult(carResult, name, options) {
190
+ const limit = await this.fetchUploadLimit();
191
+ if (carResult.size >= BigInt(limit)) return this.#uploadFile({
192
+ data: carResult.carStream,
193
+ name: `${name}${require_mime_types.FILE_EXTENSION_CAR}`,
194
+ type: require_mime_types.MIME_TYPE_CAR,
195
+ size: Number(carResult.size)
196
+ }, options);
197
+ else {
198
+ const blob = await require_stream.streamToBlob(carResult.carStream, require_mime_types.MIME_TYPE_CAR);
199
+ const file = new File([blob], `${name}${require_mime_types.FILE_EXTENSION_CAR}`, { type: require_mime_types.MIME_TYPE_CAR });
200
+ return this.#uploadFile(file, options);
201
+ }
202
+ }
203
+ async #uploadCarFile(input, options) {
204
+ const limit = await this.fetchUploadLimit();
205
+ if (input instanceof ReadableStream) {
206
+ const [streamForSize, streamForUpload] = input.tee();
207
+ let size;
208
+ if (options?.size !== void 0) size = BigInt(options.size);
209
+ else size = await require_stream.calculateStreamSize(streamForSize, options?.signal);
210
+ if (size >= BigInt(limit)) return this.#uploadFile({
211
+ data: streamForUpload,
212
+ name: options?.name || "upload.car",
213
+ type: require_mime_types.MIME_TYPE_CAR,
214
+ size: Number(size)
215
+ }, options);
216
+ else {
217
+ const blob = await require_stream.streamToBlob(streamForUpload, require_mime_types.MIME_TYPE_CAR);
218
+ const file = new File([blob], options?.name || "upload.car", { type: require_mime_types.MIME_TYPE_CAR });
219
+ return this.#uploadFile(file, options);
220
+ }
221
+ }
222
+ if (input.type !== require_mime_types.MIME_TYPE_CAR) input = new File([input], input.name, {
223
+ type: require_mime_types.MIME_TYPE_CAR,
224
+ lastModified: input.lastModified
225
+ });
226
+ return this.#uploadFile(input, options);
227
+ }
228
+ async #uploadFile(input, options) {
229
+ const limit = await this.fetchUploadLimit();
230
+ let isLargeFile = false;
231
+ if (input instanceof File) isLargeFile = input.size > limit;
232
+ else isLargeFile = true;
233
+ const operation = await (isLargeFile ? this.tusHandler.upload(input, options) : this.xhrHandler.upload(input, options));
234
+ if (options?.waitForOperation && operation.result) {
235
+ const uploadResult = await operation.result;
236
+ operation.result = this.waitForOperation(uploadResult, options.operationPollingOptions);
237
+ }
238
+ return operation;
239
+ }
240
+ destroy() {
241
+ this.xhrHandler.destroy();
242
+ this.tusHandler.destroy();
243
+ require_car.destroyCarPreprocessor();
244
+ }
245
+ };
246
+
247
+ //#endregion
248
+ exports.UploadManager = UploadManager;
249
+ //# sourceMappingURL=manager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.cjs","names":["TUS_SIZE_THRESHOLD","XHRUploadHandler","TUSUploadHandler","Sdk","DEFAULT_ENDPOINT","#validateInput","#uploadInput","#uploadCarFile","isUploadResult","OPERATION_STATUS","#waitForOperationByCid","EmptyFileError","preprocessToCar","#uploadCarResult","#isCarFileUpload","calculateStreamSize","#uploadFile","FILE_EXTENSION_CAR","MIME_TYPE_CAR","MIME_TYPE_OCTET_STREAM","streamToBlob","isCarFile"],"sources":["../../../src/upload/manager.ts"],"sourcesContent":["import type { OperationPollingOptions } from \"@lumeweb/portal-sdk\";\nimport {\n OPERATION_STATUS,\n type OperationsQueryParams,\n Sdk,\n} from \"@lumeweb/portal-sdk\";\nimport { createEqFilter } from \"@lumeweb/query-builder\";\n\nimport type { PinnerConfig } from \"../config\";\nimport type {\n UploadInput,\n UploadOperation,\n UploadOptions,\n UploadResult,\n} from \"@/types/upload\";\nimport { isUploadResult } from \"@/types/upload\";\nimport { XHRUploadHandler } from \"./xhr-upload\";\nimport { TUSUploadHandler } from \"./tus-upload\";\nimport { DEFAULT_ENDPOINT, TUS_SIZE_THRESHOLD } from \"@/types/constants\";\nimport {\n FILE_EXTENSION_CAR,\n MIME_TYPE_CAR,\n MIME_TYPE_OCTET_STREAM,\n} from \"@/types/mime-types\";\nimport {\n type CarPreprocessResult,\n configureCar,\n destroyCarPreprocessor,\n isCarFile,\n preprocessToCar,\n} from \"./car\";\nimport { calculateStreamSize, streamToBlob } from \"../utils/stream\";\nimport { EmptyFileError } from \"../errors\";\nimport { type UploadInputObject } from \"./normalize\";\n\nexport class UploadManager {\n private xhrHandler: XHRUploadHandler;\n private tusHandler: TUSUploadHandler;\n private portalSdk: Sdk;\n private uploadLimit: number = TUS_SIZE_THRESHOLD; // Default to 100 MB\n private limitFetched: boolean = false;\n\n constructor(config: PinnerConfig) {\n this.xhrHandler = new XHRUploadHandler(config);\n this.tusHandler = new TUSUploadHandler(config);\n this.portalSdk = new Sdk(config.endpoint || DEFAULT_ENDPOINT);\n configureCar({\n datastoreName: config.datastoreName,\n datastore: config.datastore,\n });\n }\n\n async fetchUploadLimit(): Promise<number> {\n if (this.limitFetched) {\n return this.uploadLimit;\n }\n\n try {\n const result = await this.portalSdk.account().uploadLimit();\n if (result.success && result.data?.limit) {\n this.uploadLimit = result.data.limit;\n }\n } catch {\n // Fallback to default 100 MB if API fails\n this.uploadLimit = TUS_SIZE_THRESHOLD;\n }\n\n this.limitFetched = true;\n return this.uploadLimit;\n }\n\n getUploadLimit(): number {\n return this.uploadLimit;\n }\n\n async upload(\n input: UploadInput,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n this.#validateInput(input, options);\n return this.#uploadInput(input, options);\n }\n\n async uploadCar(\n input: File | ReadableStream<Uint8Array>,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n this.#validateInput(input, options);\n return this.#uploadCarFile(input, options);\n }\n\n /**\n * Wait for an operation to complete or reach a settled state.\n *\n * Handles two scenarios:\n * 1. If an operationId is provided (in UploadResult), uses it directly\n * 2. If only CID is available, lists operations filtered by CID and polls the first result\n *\n * @param input Either an operation ID (number) or an UploadResult\n * @param options Polling options (interval, timeout, settledStates)\n * @returns UploadResult with operation status merged in\n */\n async waitForOperation(\n input: number | UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\n // Scenario 1: UploadResult with operationId - use it directly\n if (isUploadResult(input) && input.operationId) {\n const result = await this.portalSdk\n .account()\n .waitForOperation(input.operationId, options);\n\n if (!result.success) {\n throw new Error(\n result.error?.message || `Operation ${input.operationId} failed`,\n );\n }\n\n const operation = result.data;\n if (!operation.cid) {\n throw new Error(`Operation ${input.operationId} completed without CID`);\n }\n\n if (\n operation.status?.toLowerCase() === OPERATION_STATUS.FAILED ||\n operation.status?.toLowerCase() === OPERATION_STATUS.ERROR\n ) {\n throw new Error(\n `Operation ${input.operationId} failed: ${operation.error || operation.status_message || \"Unknown error\"}`,\n );\n }\n\n // Merge operation data into the original upload result\n return {\n ...input,\n cid: operation.cid,\n operationId: operation.id,\n };\n }\n\n // Scenario 2: UploadResult with CID but no operationId - find by CID\n if (isUploadResult(input) && input.cid) {\n return await this.#waitForOperationByCid(input, options);\n }\n\n // Scenario 3: Only operation ID provided\n const operationId = typeof input === \"number\" ? input : undefined;\n if (operationId) {\n const result = await this.portalSdk\n .account()\n .waitForOperation(operationId, options);\n\n if (!result.success) {\n throw new Error(\n result.error?.message || `Operation ${operationId} failed`,\n );\n }\n\n const operation = result.data;\n if (!operation.cid) {\n throw new Error(`Operation ${operationId} completed without CID`);\n }\n\n if (\n operation.status?.toLowerCase() === OPERATION_STATUS.FAILED ||\n operation.status?.toLowerCase() === OPERATION_STATUS.ERROR\n ) {\n throw new Error(\n `Operation ${operationId} failed: ${operation.error || operation.status_message || \"Unknown error\"}`,\n );\n }\n\n return {\n id: operationId.toString(),\n cid: operation.cid,\n name: operation.operation_display_name || \"Unknown\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(operation.started_at),\n numberOfFiles: 1,\n operationId: operation.id,\n };\n }\n\n throw new Error(\n \"No operation ID or CID provided, cannot wait for operation\",\n );\n }\n\n /**\n * Wait for an operation by CID.\n *\n * This is used when we have a CID from an upload but no operation ID.\n * We list operations filtered by CID to find the operation ID,\n * then use the SDK's waitForOperation for polling.\n *\n * @param uploadResult UploadResult with CID\n * @param options Polling options (interval, timeout, settledStates)\n * @returns UploadResult with operation status merged in\n */\n async #waitForOperationByCid(\n uploadResult: UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\n // List operations filtered by CID\n const params: OperationsQueryParams = {\n filters: [createEqFilter(\"cid\", uploadResult.cid)],\n pagination: { start: 0, end: 1 },\n };\n\n const result = await this.portalSdk.account().listOperations(params);\n\n if (!result.success) {\n throw new Error(`Failed to find operation with CID ${uploadResult.cid}`);\n }\n\n const operation = result.data.data?.[0];\n if (!operation) {\n throw new Error(`Failed to find operation with CID ${uploadResult.cid}`);\n }\n\n const operationId = operation.id;\n\n // Use SDK's waitForOperation for polling\n const waitResult = await this.portalSdk\n .account()\n .waitForOperation(operationId, options);\n\n if (!waitResult.success) {\n throw new Error(\n waitResult.error?.message || `Operation ${operationId} failed`,\n );\n }\n\n const finalOperation = waitResult.data;\n if (!finalOperation.cid) {\n throw new Error(`Operation ${operationId} completed without CID`);\n }\n\n if (\n finalOperation.status?.toLowerCase() === OPERATION_STATUS.FAILED ||\n finalOperation.status?.toLowerCase() === OPERATION_STATUS.ERROR\n ) {\n throw new Error(\n `Operation ${operationId} failed: ${finalOperation.error || finalOperation.status_message || \"Unknown error\"}`,\n );\n }\n\n // Return merged result\n return {\n ...uploadResult,\n cid: finalOperation.cid,\n operationId: finalOperation.id,\n };\n }\n\n #validateInput(input: UploadInput, options?: UploadOptions): void {\n if (input instanceof File) {\n if (input.size === 0) {\n throw new EmptyFileError(`Cannot upload empty file: ${input.name}`);\n }\n } else if (input instanceof ReadableStream) {\n // For ReadableStream, we can only validate if size is provided\n // Otherwise we need to calculate the size which consumes the stream\n if (options?.size !== undefined && options.size === 0) {\n throw new EmptyFileError(\"Cannot upload empty stream\");\n }\n }\n }\n\n async uploadDirectory(\n files: File[],\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const carResult = await preprocessToCar(files, {\n onProgress: options?.onProgress\n ? (p) =>\n options.onProgress!({\n percentage: p,\n bytesUploaded: 0,\n bytesTotal: 0,\n })\n : undefined,\n signal: options?.signal,\n });\n\n const operation = await this.#uploadCarResult(\n carResult,\n options?.name || \"directory\",\n options,\n );\n\n if (options?.waitForOperation && operation.result) {\n const uploadResult = await operation.result;\n operation.result = this.waitForOperation(\n { ...uploadResult, isDirectory: true, numberOfFiles: files.length },\n options.operationPollingOptions,\n );\n }\n\n return operation;\n }\n\n async #uploadInput(\n input: UploadInput,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n // Check if this is a CAR file that should be uploaded without preprocessing\n const isCarUpload = await this.#isCarFileUpload(input, options);\n if (isCarUpload) {\n return this.#uploadCarFile(input, options);\n }\n\n const limit = await this.fetchUploadLimit();\n\n if (input instanceof ReadableStream) {\n const [streamForSize, streamForUpload] = input.tee();\n let size: bigint;\n if (options?.size !== undefined) {\n size = BigInt(options.size);\n } else {\n size = await calculateStreamSize(streamForSize, options?.signal);\n }\n\n if (size >= BigInt(limit)) {\n return this.#uploadFile(\n {\n data: streamForUpload,\n name: options?.name || \"upload\",\n type:\n options?.name?.endsWith(FILE_EXTENSION_CAR) ||\n options?.isDirectory\n ? MIME_TYPE_CAR\n : MIME_TYPE_OCTET_STREAM,\n size: Number(size),\n },\n options,\n );\n } else {\n const blob = await streamToBlob(\n streamForUpload,\n \"application/octet-stream\",\n );\n const file = new File([blob], options?.name || \"upload\", {\n type: blob.type,\n });\n return this.#uploadFile(file, options);\n }\n }\n\n return this.#uploadFile(input, options);\n }\n\n async #isCarFileUpload(\n input: UploadInput,\n options?: UploadOptions,\n ): Promise<boolean> {\n // Explicit option takes precedence\n if (options?.isCarFile === true) {\n return true;\n }\n if (options?.isCarFile === false) {\n return false;\n }\n\n // Check if File input is a valid CAR file\n if (input instanceof File) {\n // Quick check: MIME type or extension\n if (\n input.type === MIME_TYPE_CAR ||\n input.name.endsWith(FILE_EXTENSION_CAR)\n ) {\n // Verify it's actually a valid CAR file\n return await isCarFile(input);\n }\n }\n\n // For ReadableStream, rely on explicit isCarFile option or name extension\n if (\n input instanceof ReadableStream &&\n options?.name?.endsWith(FILE_EXTENSION_CAR)\n ) {\n // We can't verify stream content without consuming it,\n // so we trust the explicit isCarFile option or extension\n return options?.isCarFile !== false;\n }\n\n return false;\n }\n\n async #uploadCarResult(\n carResult: CarPreprocessResult,\n name: string,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const limit = await this.fetchUploadLimit();\n\n if (carResult.size >= BigInt(limit)) {\n return this.#uploadFile(\n {\n data: carResult.carStream,\n name: `${name}${FILE_EXTENSION_CAR}`,\n type: MIME_TYPE_CAR,\n size: Number(carResult.size),\n },\n options,\n );\n } else {\n const blob = await streamToBlob(carResult.carStream, MIME_TYPE_CAR);\n const file = new File([blob], `${name}${FILE_EXTENSION_CAR}`, {\n type: MIME_TYPE_CAR,\n });\n return this.#uploadFile(file, options);\n }\n }\n\n async #uploadCarFile(\n input: File | ReadableStream<Uint8Array>,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const limit = await this.fetchUploadLimit();\n\n if (input instanceof ReadableStream) {\n const [streamForSize, streamForUpload] = input.tee();\n let size: bigint;\n if (options?.size !== undefined) {\n size = BigInt(options.size);\n } else {\n size = await calculateStreamSize(streamForSize, options?.signal);\n }\n\n if (size >= BigInt(limit)) {\n return this.#uploadFile(\n {\n data: streamForUpload,\n name: options?.name || \"upload.car\",\n type: MIME_TYPE_CAR,\n size: Number(size),\n },\n options,\n );\n } else {\n const blob = await streamToBlob(streamForUpload, MIME_TYPE_CAR);\n const file = new File([blob], options?.name || \"upload.car\", {\n type: MIME_TYPE_CAR,\n });\n return this.#uploadFile(file, options);\n }\n }\n\n // File input - ensure it has correct CAR MIME type\n if (input.type !== MIME_TYPE_CAR) {\n // Create a new File with correct CAR MIME type\n input = new File([input], input.name, {\n type: MIME_TYPE_CAR,\n lastModified: input.lastModified,\n });\n }\n\n return this.#uploadFile(input, options);\n }\n\n async #uploadFile(\n input: UploadInput | UploadInputObject,\n options?: UploadOptions,\n ): Promise<UploadOperation> {\n const limit = await this.fetchUploadLimit();\n\n let isLargeFile = false;\n\n if (input instanceof File) {\n isLargeFile = input.size > limit;\n } else {\n isLargeFile = true;\n }\n\n const operation = await (isLargeFile\n ? this.tusHandler.upload(input, options)\n : this.xhrHandler.upload(input, options));\n\n if (options?.waitForOperation && operation.result) {\n const uploadResult = await operation.result;\n operation.result = this.waitForOperation(\n uploadResult,\n options.operationPollingOptions,\n );\n }\n\n return operation;\n }\n\n destroy(): void {\n this.xhrHandler.destroy();\n this.tusHandler.destroy();\n destroyCarPreprocessor();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { PinnerConfig } from "../config.cjs";
2
+ import { UploadInput, UploadOperation, UploadOptions, UploadResult } from "../types/upload.cjs";
3
+ import { OperationPollingOptions } from "@lumeweb/portal-sdk";
4
+
5
+ //#region src/upload/manager.d.ts
6
+ declare class UploadManager {
7
+ #private;
8
+ private xhrHandler;
9
+ private tusHandler;
10
+ private portalSdk;
11
+ private uploadLimit;
12
+ private limitFetched;
13
+ constructor(config: PinnerConfig);
14
+ fetchUploadLimit(): Promise<number>;
15
+ getUploadLimit(): number;
16
+ upload(input: UploadInput, options?: UploadOptions): Promise<UploadOperation>;
17
+ uploadCar(input: File | ReadableStream<Uint8Array>, options?: UploadOptions): Promise<UploadOperation>;
18
+ /**
19
+ * Wait for an operation to complete or reach a settled state.
20
+ *
21
+ * Handles two scenarios:
22
+ * 1. If an operationId is provided (in UploadResult), uses it directly
23
+ * 2. If only CID is available, lists operations filtered by CID and polls the first result
24
+ *
25
+ * @param input Either an operation ID (number) or an UploadResult
26
+ * @param options Polling options (interval, timeout, settledStates)
27
+ * @returns UploadResult with operation status merged in
28
+ */
29
+ waitForOperation(input: number | UploadResult, options?: OperationPollingOptions): Promise<UploadResult>;
30
+ uploadDirectory(files: File[], options?: UploadOptions): Promise<UploadOperation>;
31
+ destroy(): void;
32
+ }
33
+ //#endregion
34
+ export { UploadManager };
35
+ //# sourceMappingURL=manager.d.cts.map
@@ -0,0 +1,28 @@
1
+ const require_mime_types = require('../types/mime-types.cjs');
2
+
3
+ //#region src/upload/normalize.ts
4
+ function normalizeUploadInput(input, options) {
5
+ if (input instanceof File) return {
6
+ data: input,
7
+ name: input.name,
8
+ type: input.type,
9
+ size: input.size
10
+ };
11
+ if (input instanceof ReadableStream) return {
12
+ data: input,
13
+ name: options?.name || "upload",
14
+ type: options?.name?.endsWith(require_mime_types.FILE_EXTENSION_CAR) ? require_mime_types.MIME_TYPE_CAR : require_mime_types.MIME_TYPE_OCTET_STREAM,
15
+ size: 0
16
+ };
17
+ const objectInput = input;
18
+ return {
19
+ data: objectInput.data,
20
+ name: objectInput.name,
21
+ type: objectInput.type,
22
+ size: objectInput.size || 0
23
+ };
24
+ }
25
+
26
+ //#endregion
27
+ exports.normalizeUploadInput = normalizeUploadInput;
28
+ //# sourceMappingURL=normalize.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.cjs","names":["FILE_EXTENSION_CAR","MIME_TYPE_CAR","MIME_TYPE_OCTET_STREAM"],"sources":["../../../src/upload/normalize.ts"],"sourcesContent":["import type { UploadInput, UploadOptions } from \"@/types/upload\";\nimport {\n FILE_EXTENSION_CAR,\n MIME_TYPE_CAR,\n MIME_TYPE_OCTET_STREAM,\n} from \"@/types/mime-types\";\n\nexport interface NormalizedUploadInput {\n data: File | ReadableStream<Uint8Array>;\n name: string;\n type: string;\n size: number;\n}\n\nexport interface UploadInputObject {\n data: ReadableStream<Uint8Array>;\n name: string;\n type: string;\n size?: number;\n}\n\nexport function normalizeUploadInput(\n input: UploadInput | UploadInputObject,\n options?: UploadOptions,\n): NormalizedUploadInput {\n if (input instanceof File) {\n return {\n data: input,\n name: input.name,\n type: input.type,\n size: input.size,\n };\n }\n\n if (input instanceof ReadableStream) {\n return {\n data: input,\n name: options?.name || \"upload\",\n type: options?.name?.endsWith(FILE_EXTENSION_CAR)\n ? MIME_TYPE_CAR\n : MIME_TYPE_OCTET_STREAM,\n size: 0,\n };\n }\n\n const objectInput = input as UploadInputObject;\n return {\n data: objectInput.data,\n name: objectInput.name,\n type: objectInput.type,\n size: objectInput.size || 0,\n };\n}\n"],"mappings":";;;AAqBA,SAAgB,qBACd,OACA,SACuB;AACvB,KAAI,iBAAiB,KACnB,QAAO;EACL,MAAM;EACN,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,MAAM,MAAM;EACb;AAGH,KAAI,iBAAiB,eACnB,QAAO;EACL,MAAM;EACN,MAAM,SAAS,QAAQ;EACvB,MAAM,SAAS,MAAM,SAASA,sCAAmB,GAC7CC,mCACAC;EACJ,MAAM;EACP;CAGH,MAAM,cAAc;AACpB,QAAO;EACL,MAAM,YAAY;EAClB,MAAM,YAAY;EAClB,MAAM,YAAY;EAClB,MAAM,YAAY,QAAQ;EAC3B"}
@@ -0,0 +1,74 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_upload = require('../types/upload.cjs');
3
+ const require_constants = require('./constants.cjs');
4
+ const require_base_upload = require('./base-upload.cjs');
5
+ const require_tus_patch = require('../utils/tus-patch.cjs');
6
+ let _uppy_tus = require("@uppy/tus");
7
+ _uppy_tus = require_rolldown_runtime.__toESM(_uppy_tus);
8
+
9
+ //#region src/upload/tus-upload.ts
10
+ var TUSUploadHandler = class extends require_base_upload.BaseUploadHandler {
11
+ constructor(config) {
12
+ require_tus_patch.patchTusNodeHttpStack();
13
+ super(config);
14
+ }
15
+ configurePlugin(uppy) {
16
+ uppy.use(_uppy_tus.default, {
17
+ endpoint: `${this.config.endpoint}/api/upload/tus`,
18
+ headers: { Authorization: `Bearer ${this.config.jwt}` },
19
+ chunkSize: 10 * 1024 * 1024,
20
+ retryDelays: [
21
+ 0,
22
+ 1e3,
23
+ 3e3,
24
+ 5e3
25
+ ]
26
+ });
27
+ }
28
+ parseResult(result) {
29
+ const uppyResponse = result;
30
+ if (!uppyResponse) return {
31
+ id: "",
32
+ cid: "",
33
+ name: "",
34
+ size: 0,
35
+ mimeType: "",
36
+ createdAt: /* @__PURE__ */ new Date(),
37
+ numberOfFiles: 1,
38
+ isDirectory: false,
39
+ [require_upload.UploadResultSymbol]: true
40
+ };
41
+ const response = uppyResponse.body;
42
+ if (response && response.cid) return {
43
+ id: response.id,
44
+ cid: response.cid,
45
+ name: response.name,
46
+ size: response.size,
47
+ mimeType: response.mimeType,
48
+ createdAt: new Date(response.createdAt),
49
+ numberOfFiles: response.numberOfFiles,
50
+ isDirectory: response.isDirectory ?? false,
51
+ keyvalues: response.keyvalues,
52
+ operationId: response.operationId,
53
+ [require_upload.UploadResultSymbol]: true
54
+ };
55
+ return {
56
+ id: uppyResponse.uploadURL?.split("/").pop() || "",
57
+ cid: "",
58
+ name: "",
59
+ size: 0,
60
+ mimeType: "",
61
+ createdAt: /* @__PURE__ */ new Date(),
62
+ numberOfFiles: 1,
63
+ isDirectory: false,
64
+ [require_upload.UploadResultSymbol]: true
65
+ };
66
+ }
67
+ getUploadSource() {
68
+ return require_constants.UPLOAD_SOURCE_TUS;
69
+ }
70
+ };
71
+
72
+ //#endregion
73
+ exports.TUSUploadHandler = TUSUploadHandler;
74
+ //# sourceMappingURL=tus-upload.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tus-upload.cjs","names":["BaseUploadHandler","TusPlugin","UploadResultSymbol","UPLOAD_SOURCE_TUS"],"sources":["../../../src/upload/tus-upload.ts"],"sourcesContent":["import Uppy from \"@uppy/core\";\nimport TusPlugin from \"@uppy/tus\";\nimport type { UploadResult } from \"@/types/upload\";\nimport { UploadResultSymbol } from \"@/types/upload\";\nimport { BaseUploadHandler } from \"./base-upload\";\nimport { patchTusNodeHttpStack } from \"@/utils/tus-patch\";\nimport type { PinnerConfig } from \"@/config\";\nimport { UPLOAD_SOURCE_TUS } from \"./constants\";\n\nexport class TUSUploadHandler extends BaseUploadHandler {\n constructor(config: PinnerConfig) {\n // Apply runtime patch for tus-js-client to handle abort() before send()\n patchTusNodeHttpStack();\n super(config);\n }\n protected configurePlugin(uppy: Uppy): void {\n uppy.use(TusPlugin, {\n endpoint: `${this.config.endpoint}/api/upload/tus`,\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n chunkSize: 10 * 1024 * 1024, // 10MB chunks\n retryDelays: [0, 1000, 3000, 5000],\n });\n }\n\n protected parseResult(result: unknown): UploadResult {\n const uppyResponse = result as\n | {\n uploadURL: string;\n body?: UploadResult;\n }\n | undefined;\n\n if (!uppyResponse) {\n return {\n id: \"\",\n cid: \"\",\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n isDirectory: false,\n [UploadResultSymbol]: true,\n };\n }\n\n const response = uppyResponse.body;\n\n if (response && response.cid) {\n return {\n id: response.id,\n cid: response.cid,\n name: response.name,\n size: response.size,\n mimeType: response.mimeType,\n createdAt: new Date(response.createdAt),\n numberOfFiles: response.numberOfFiles,\n isDirectory: response.isDirectory ?? false,\n keyvalues: response.keyvalues,\n operationId: response.operationId,\n [UploadResultSymbol]: true,\n };\n }\n\n const uploadId = uppyResponse.uploadURL?.split(\"/\").pop() || \"\";\n\n return {\n id: uploadId,\n cid: \"\",\n name: \"\",\n size: 0,\n mimeType: \"\",\n createdAt: new Date(),\n numberOfFiles: 1,\n isDirectory: false,\n [UploadResultSymbol]: true,\n };\n }\n\n protected getUploadSource(): string {\n return UPLOAD_SOURCE_TUS;\n }\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,mBAAb,cAAsCA,sCAAkB;CACtD,YAAY,QAAsB;AAEhC,2CAAuB;AACvB,QAAM,OAAO;;CAEf,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAIC,mBAAW;GAClB,UAAU,GAAG,KAAK,OAAO,SAAS;GAClC,SAAS,EACP,eAAe,UAAU,KAAK,OAAO,OACtC;GACD,WAAW,KAAK,OAAO;GACvB,aAAa;IAAC;IAAG;IAAM;IAAM;IAAK;GACnC,CAAC;;CAGJ,AAAU,YAAY,QAA+B;EACnD,MAAM,eAAe;AAOrB,MAAI,CAAC,aACH,QAAO;GACL,IAAI;GACJ,KAAK;GACL,MAAM;GACN,MAAM;GACN,UAAU;GACV,2BAAW,IAAI,MAAM;GACrB,eAAe;GACf,aAAa;IACZC,oCAAqB;GACvB;EAGH,MAAM,WAAW,aAAa;AAE9B,MAAI,YAAY,SAAS,IACvB,QAAO;GACL,IAAI,SAAS;GACb,KAAK,SAAS;GACd,MAAM,SAAS;GACf,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,WAAW,IAAI,KAAK,SAAS,UAAU;GACvC,eAAe,SAAS;GACxB,aAAa,SAAS,eAAe;GACrC,WAAW,SAAS;GACpB,aAAa,SAAS;IACrBA,oCAAqB;GACvB;AAKH,SAAO;GACL,IAHe,aAAa,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI;GAI3D,KAAK;GACL,MAAM;GACN,MAAM;GACN,UAAU;GACV,2BAAW,IAAI,MAAM;GACrB,eAAe;GACf,aAAa;IACZA,oCAAqB;GACvB;;CAGH,AAAU,kBAA0B;AAClC,SAAOC"}
@@ -0,0 +1,41 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_upload = require('../types/upload.cjs');
3
+ const require_constants = require('./constants.cjs');
4
+ const require_base_upload = require('./base-upload.cjs');
5
+ let _lumeweb_uppy_post_upload = require("@lumeweb/uppy-post-upload");
6
+ _lumeweb_uppy_post_upload = require_rolldown_runtime.__toESM(_lumeweb_uppy_post_upload);
7
+
8
+ //#region src/upload/xhr-upload.ts
9
+ var XHRUploadHandler = class extends require_base_upload.BaseUploadHandler {
10
+ configurePlugin(uppy) {
11
+ uppy.use(_lumeweb_uppy_post_upload.default, {
12
+ endpoint: `${this.config.endpoint}/api/upload`,
13
+ fieldName: "file",
14
+ formData: true,
15
+ headers: { Authorization: `Bearer ${this.config.jwt}` }
16
+ });
17
+ }
18
+ parseResult(result) {
19
+ const uppyResponse = result;
20
+ const response = uppyResponse.body || uppyResponse;
21
+ return {
22
+ id: response.id,
23
+ cid: response.cid,
24
+ name: response.name,
25
+ size: response.size,
26
+ mimeType: response.mimeType,
27
+ createdAt: new Date(response.createdAt),
28
+ numberOfFiles: response.numberOfFiles,
29
+ keyvalues: response.keyvalues,
30
+ operationId: response.operationId,
31
+ [require_upload.UploadResultSymbol]: true
32
+ };
33
+ }
34
+ getUploadSource() {
35
+ return require_constants.UPLOAD_SOURCE_XHR;
36
+ }
37
+ };
38
+
39
+ //#endregion
40
+ exports.XHRUploadHandler = XHRUploadHandler;
41
+ //# sourceMappingURL=xhr-upload.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xhr-upload.cjs","names":["BaseUploadHandler","XHRUpload","UploadResultSymbol","UPLOAD_SOURCE_XHR"],"sources":["../../../src/upload/xhr-upload.ts"],"sourcesContent":["import Uppy from \"@uppy/core\";\nimport XHRUpload from \"@lumeweb/uppy-post-upload\";\nimport type { UploadResult } from \"@/types/upload\";\nimport { UploadResultSymbol } from \"@/types/upload\";\nimport { BaseUploadHandler } from \"./base-upload\";\nimport { UPLOAD_SOURCE_XHR } from \"./constants\";\n\nexport class XHRUploadHandler extends BaseUploadHandler {\n protected configurePlugin(uppy: Uppy): void {\n uppy.use(XHRUpload, {\n endpoint: `${this.config.endpoint}/api/upload`,\n fieldName: \"file\",\n formData: true,\n headers: {\n Authorization: `Bearer ${this.config.jwt}`,\n },\n });\n }\n\n protected parseResult(result: unknown): UploadResult {\n const uppyResponse = result as {\n uploadURL: string;\n body?: {\n id: string;\n cid: string;\n name: string;\n size: number;\n mimeType: string;\n createdAt: string;\n numberOfFiles: number;\n keyvalues?: Record<string, string>;\n operationId?: number;\n };\n };\n\n const response = uppyResponse.body || (uppyResponse as any);\n\n return {\n id: response.id,\n cid: response.cid,\n name: response.name,\n size: response.size,\n mimeType: response.mimeType,\n createdAt: new Date(response.createdAt),\n numberOfFiles: response.numberOfFiles,\n keyvalues: response.keyvalues,\n operationId: response.operationId,\n [UploadResultSymbol]: true,\n };\n }\n\n protected getUploadSource(): string {\n return UPLOAD_SOURCE_XHR;\n }\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,mBAAb,cAAsCA,sCAAkB;CACtD,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAIC,mCAAW;GAClB,UAAU,GAAG,KAAK,OAAO,SAAS;GAClC,WAAW;GACX,UAAU;GACV,SAAS,EACP,eAAe,UAAU,KAAK,OAAO,OACtC;GACF,CAAC;;CAGJ,AAAU,YAAY,QAA+B;EACnD,MAAM,eAAe;EAerB,MAAM,WAAW,aAAa,QAAS;AAEvC,SAAO;GACL,IAAI,SAAS;GACb,KAAK,SAAS;GACd,MAAM,SAAS;GACf,MAAM,SAAS;GACf,UAAU,SAAS;GACnB,WAAW,IAAI,KAAK,SAAS,UAAU;GACvC,eAAe,SAAS;GACxB,WAAW,SAAS;GACpB,aAAa,SAAS;IACrBC,oCAAqB;GACvB;;CAGH,AAAU,kBAA0B;AAClC,SAAOC"}
@@ -0,0 +1,12 @@
1
+
2
+ //#region src/utils/env.ts
3
+ /**
4
+ * Check if the current environment is Node.js.
5
+ */
6
+ function isNodeEnvironment() {
7
+ return typeof process !== "undefined" && process?.versions?.node !== void 0;
8
+ }
9
+
10
+ //#endregion
11
+ exports.isNodeEnvironment = isNodeEnvironment;
12
+ //# sourceMappingURL=env.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.cjs","names":[],"sources":["../../../src/utils/env.ts"],"sourcesContent":["/**\n * Check if the current environment is Node.js.\n */\nexport function isNodeEnvironment(): boolean {\n return typeof process !== 'undefined' && process?.versions?.node !== undefined;\n}\n"],"mappings":";;;;;AAGA,SAAgB,oBAA6B;AAC3C,QAAO,OAAO,YAAY,eAAe,SAAS,UAAU,SAAS"}
@@ -0,0 +1,141 @@
1
+ const require_env = require('./env.cjs');
2
+
3
+ //#region src/utils/stream.ts
4
+ /**
5
+ * Convert a ReadableStream to a Blob.
6
+ */
7
+ async function streamToBlob(stream, mimeType) {
8
+ const chunks = [];
9
+ const reader = stream.getReader();
10
+ while (true) {
11
+ const { done, value } = await reader.read();
12
+ if (done) break;
13
+ chunks.push(value);
14
+ }
15
+ return new Blob(chunks, { type: mimeType });
16
+ }
17
+ /**
18
+ * Calculate the total size of a ReadableStream by consuming it.
19
+ */
20
+ async function calculateStreamSize(stream, signal) {
21
+ let size = 0n;
22
+ const reader = stream.getReader();
23
+ while (true) {
24
+ if (signal?.aborted) throw new Error("Aborted");
25
+ const { done, value } = await reader.read();
26
+ if (done) break;
27
+ size += BigInt(value.length);
28
+ }
29
+ return size;
30
+ }
31
+ /**
32
+ * Convert an async generator to a ReadableStream.
33
+ */
34
+ function asyncGeneratorToReadableStream(generator) {
35
+ return new ReadableStream({ async start(controller) {
36
+ try {
37
+ for await (const item of generator) controller.enqueue(item);
38
+ controller.close();
39
+ } catch (error) {
40
+ controller.error(error);
41
+ }
42
+ } });
43
+ }
44
+ /**
45
+ * Convert a ReadableStream to an async iterable.
46
+ */
47
+ async function* readableStreamToAsyncIterable(stream) {
48
+ const reader = stream.getReader();
49
+ try {
50
+ while (true) {
51
+ const { done, value } = await reader.read();
52
+ if (done) break;
53
+ yield value;
54
+ }
55
+ } finally {
56
+ reader.releaseLock();
57
+ }
58
+ }
59
+ /**
60
+ * Convert a web ReadableStream to a Node.js stream.Readable
61
+ * This is needed for Node.js environments where tus-js-client expects Node streams.
62
+ */
63
+ async function readableStreamToNodeStream(stream) {
64
+ if (!require_env.isNodeEnvironment()) throw new Error("readableStreamToNodeStream can only be used in Node.js environment");
65
+ const { Readable } = await import("stream");
66
+ const reader = stream.getReader();
67
+ return new Readable({
68
+ async read() {
69
+ try {
70
+ const { done, value } = await reader.read();
71
+ if (done) {
72
+ reader.releaseLock();
73
+ this.push(null);
74
+ } else this.push(Buffer.from(value));
75
+ } catch (error) {
76
+ reader.releaseLock();
77
+ this.destroy(error);
78
+ }
79
+ },
80
+ destroy(error, callback) {
81
+ try {
82
+ reader.releaseLock();
83
+ } catch (e) {}
84
+ if (callback) callback(error);
85
+ }
86
+ });
87
+ }
88
+ /**
89
+ * Convert a ReadableStream to a Blob using the Response API.
90
+ * This is the preferred method in browser environments as it's built-in and efficient.
91
+ */
92
+ async function streamToBlobViaResponse(stream) {
93
+ return new Response(stream).blob();
94
+ }
95
+ /**
96
+ * Convert a File to a ReadableStream of Uint8Array without loading entire blob into memory.
97
+ * This streams the file content chunk by chunk.
98
+ */
99
+ function fileToReadableStream(file) {
100
+ return new ReadableStream({ async start(controller) {
101
+ try {
102
+ const reader = file.stream().getReader();
103
+ while (true) {
104
+ const { done, value } = await reader.read();
105
+ if (done) {
106
+ controller.close();
107
+ break;
108
+ }
109
+ controller.enqueue(value);
110
+ }
111
+ } catch (error) {
112
+ controller.error(error);
113
+ }
114
+ } });
115
+ }
116
+ /**
117
+ * Collect all chunks from an async iterable or iterable into a single Uint8Array.
118
+ */
119
+ async function collectAsyncIterable(iterable) {
120
+ const chunks = [];
121
+ for await (const chunk of iterable) chunks.push(chunk);
122
+ const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
123
+ const result = new Uint8Array(totalLength);
124
+ let offset = 0;
125
+ for (const chunk of chunks) {
126
+ result.set(chunk, offset);
127
+ offset += chunk.length;
128
+ }
129
+ return result;
130
+ }
131
+
132
+ //#endregion
133
+ exports.asyncGeneratorToReadableStream = asyncGeneratorToReadableStream;
134
+ exports.calculateStreamSize = calculateStreamSize;
135
+ exports.collectAsyncIterable = collectAsyncIterable;
136
+ exports.fileToReadableStream = fileToReadableStream;
137
+ exports.readableStreamToAsyncIterable = readableStreamToAsyncIterable;
138
+ exports.readableStreamToNodeStream = readableStreamToNodeStream;
139
+ exports.streamToBlob = streamToBlob;
140
+ exports.streamToBlobViaResponse = streamToBlobViaResponse;
141
+ //# sourceMappingURL=stream.cjs.map