@lumeweb/pinner 0.1.9 → 0.1.10

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 (33) hide show
  1. package/dist/esm/adapters/pinata/legacy/adapter.js +16 -4
  2. package/dist/esm/adapters/pinata/legacy/adapter.js.map +1 -1
  3. package/dist/esm/adapters/pinata/v2/adapter.js +4 -0
  4. package/dist/esm/adapters/pinata/v2/adapter.js.map +1 -1
  5. package/dist/esm/api/generated/content/content.msw.d.ts +7 -2
  6. package/dist/esm/api/generated/content/content.msw.js +31 -3
  7. package/dist/esm/api/generated/content/content.msw.js.map +1 -1
  8. package/dist/esm/api/generated/internal/internal.msw.js +4 -0
  9. package/dist/esm/api/generated/internal/internal.msw.js.map +1 -1
  10. package/dist/esm/api/generated/ipns/ipns.msw.js +16 -1
  11. package/dist/esm/api/generated/ipns/ipns.msw.js.map +1 -1
  12. package/dist/esm/api/generated/schemas/iPNSKeyListResponse.d.ts +2 -0
  13. package/dist/esm/api/generated/schemas/iPNSKeyResponse.d.ts +2 -0
  14. package/dist/esm/api/generated/schemas/postUploadResponse.d.ts +42 -0
  15. package/dist/esm/api/generated/schemas/uploadResultResponse.d.ts +44 -0
  16. package/dist/esm/api/generated/schemas/websiteItem.d.ts +1 -0
  17. package/dist/esm/api/generated/schemas/websiteResponse.d.ts +1 -0
  18. package/dist/esm/api/generated/websites/websites.msw.js +20 -0
  19. package/dist/esm/api/generated/websites/websites.msw.js.map +1 -1
  20. package/dist/esm/api/mocks.d.ts +2 -2
  21. package/dist/esm/api/mocks.js +2 -2
  22. package/dist/esm/types/upload.d.ts +3 -1
  23. package/dist/esm/types/upload.js.map +1 -1
  24. package/dist/esm/upload/base-upload.js +27 -0
  25. package/dist/esm/upload/base-upload.js.map +1 -1
  26. package/dist/esm/upload/manager.d.ts +1 -0
  27. package/dist/esm/upload/manager.js +52 -13
  28. package/dist/esm/upload/manager.js.map +1 -1
  29. package/dist/esm/upload/tus-upload.js +4 -37
  30. package/dist/esm/upload/tus-upload.js.map +1 -1
  31. package/dist/esm/upload/xhr-upload.js +3 -14
  32. package/dist/esm/upload/xhr-upload.js.map +1 -1
  33. package/package.json +4 -4
@@ -6,17 +6,19 @@ import { TUSUploadHandler } from "./tus-upload.js";
6
6
  import { DEFAULT_ENDPOINT, TUS_SIZE_THRESHOLD } from "../types/constants.js";
7
7
  import { configureCar, destroyCarPreprocessor, isCarFile, preprocessToCar } from "./car.js";
8
8
  import { EmptyFileError } from "../errors/index.js";
9
- import { OPERATION_STATUS, Sdk } from "@lumeweb/portal-sdk";
9
+ import { AccountError, OPERATION_STATUS, Sdk, poll } from "@lumeweb/portal-sdk";
10
10
  import { createEqFilter } from "@lumeweb/query-builder";
11
11
 
12
12
  //#region src/upload/manager.ts
13
13
  var UploadManager = class {
14
+ config;
14
15
  xhrHandler;
15
16
  tusHandler;
16
17
  portalSdk;
17
18
  uploadLimit = TUS_SIZE_THRESHOLD;
18
19
  limitFetched = false;
19
20
  constructor(config) {
21
+ this.config = config;
20
22
  this.xhrHandler = new XHRUploadHandler(config);
21
23
  this.tusHandler = new TUSUploadHandler(config);
22
24
  this.portalSdk = new Sdk(config.endpoint || "https://ipfs.pinner.xyz");
@@ -59,19 +61,13 @@ var UploadManager = class {
59
61
  * @returns UploadResult with operation status merged in
60
62
  */
61
63
  async waitForOperation(input, options) {
62
- if (isUploadResult(input) && input.operationId) {
63
- const result = await this.portalSdk.account().waitForOperation(input.operationId, options);
64
- if (!result.success) throw new Error(result.error?.message || `Operation ${input.operationId} failed`);
65
- const operation = result.data;
66
- if (!operation.cid) throw new Error(`Operation ${input.operationId} completed without CID`);
67
- if (operation.status?.toLowerCase() === OPERATION_STATUS.FAILED || operation.status?.toLowerCase() === OPERATION_STATUS.ERROR) throw new Error(`Operation ${input.operationId} failed: ${operation.error || operation.status_message || "Unknown error"}`);
68
- return {
69
- ...input,
70
- cid: operation.cid,
71
- operationId: operation.id
72
- };
73
- }
64
+ if (isUploadResult(input) && input.operationId) return await this.#waitForOperationById(input, options);
74
65
  if (isUploadResult(input) && input.cid) return await this.#waitForOperationByCid(input, options);
66
+ if (isUploadResult(input) && input.id) {
67
+ const resolved = await this.#waitForUploadResult(input, options);
68
+ if (resolved.cid) return await this.#waitForOperationByCid(resolved, options);
69
+ return resolved;
70
+ }
75
71
  const operationId = typeof input === "number" ? input : void 0;
76
72
  if (operationId) {
77
73
  const result = await this.portalSdk.account().waitForOperation(operationId, options);
@@ -104,6 +100,7 @@ var UploadManager = class {
104
100
  * @returns UploadResult with operation status merged in
105
101
  */
106
102
  async #waitForOperationByCid(uploadResult, options) {
103
+ if (!uploadResult.cid) throw new Error("Cannot wait for operation by CID — CID not available. Use upload result ID to poll GET /api/upload/result/{id} instead.");
107
104
  const params = {
108
105
  filters: [createEqFilter("cid", uploadResult.cid)],
109
106
  pagination: {
@@ -127,6 +124,48 @@ var UploadManager = class {
127
124
  operationId: finalOperation.id
128
125
  };
129
126
  }
127
+ async #waitForOperationById(input, options) {
128
+ const result = await this.portalSdk.account().waitForOperation(input.operationId, options);
129
+ if (!result.success) throw new Error(result.error?.message || `Operation ${input.operationId} failed`);
130
+ const operation = result.data;
131
+ if (!operation.cid) throw new Error(`Operation ${input.operationId} completed without CID`);
132
+ if (operation.status?.toLowerCase() === OPERATION_STATUS.FAILED || operation.status?.toLowerCase() === OPERATION_STATUS.ERROR) throw new Error(`Operation ${input.operationId} failed: ${operation.error || operation.status_message || "Unknown error"}`);
133
+ return {
134
+ ...input,
135
+ cid: operation.cid,
136
+ operationId: operation.id
137
+ };
138
+ }
139
+ async #waitForUploadResult(uploadResult, options) {
140
+ const uploadId = uploadResult.id;
141
+ if (!uploadId) throw new Error("No upload ID available to poll for upload result");
142
+ const fetchUrl = `${this.config.endpoint || "https://ipfs.pinner.xyz"}/api/upload/result/${encodeURIComponent(uploadId)}`;
143
+ const headers = { Authorization: `Bearer ${this.config.jwt}` };
144
+ const result = await poll(async () => {
145
+ const response = await fetch(fetchUrl, { headers });
146
+ if (response.status === 404) return {
147
+ success: false,
148
+ error: new AccountError(`Upload result not found for ID ${uploadId}`, 404)
149
+ };
150
+ const body = await response.json();
151
+ if (body.status === "failed") return {
152
+ success: false,
153
+ error: new AccountError(body.error || `Upload failed for ID ${uploadId}`, 500)
154
+ };
155
+ return {
156
+ success: true,
157
+ data: body
158
+ };
159
+ }, (data) => data.status === "completed" || data.status === "duplicate", {
160
+ interval: options?.interval,
161
+ timeout: options?.timeout
162
+ });
163
+ if (!result.success) throw result.error;
164
+ return {
165
+ ...uploadResult,
166
+ ...result.data.cid ? { cid: result.data.cid } : {}
167
+ };
168
+ }
130
169
  #validateInput(input, options) {
131
170
  if (input instanceof File) {
132
171
  if (input.size === 0) throw new EmptyFileError(`Cannot upload empty file: ${input.name}`);
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","names":["#validateInput","#uploadInput","#uploadCarFile","#waitForOperationByCid","#uploadCarResult","#isCarFileUpload","#uploadFile"],"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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"}
1
+ {"version":3,"file":"manager.js","names":["#validateInput","#uploadInput","#uploadCarFile","#waitForOperationById","#waitForOperationByCid","#waitForUploadResult","#uploadCarResult","#isCarFileUpload","#uploadFile"],"sources":["../../../src/upload/manager.ts"],"sourcesContent":["import type { OperationPollingOptions } from \"@lumeweb/portal-sdk\";\nimport {\n AccountError,\n OPERATION_STATUS,\n type OperationsQueryParams,\n Sdk,\n poll,\n} from \"@lumeweb/portal-sdk\";\nimport { createEqFilter } from \"@lumeweb/query-builder\";\nimport type { UploadResultResponse } from \"@/api/generated/schemas\";\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 config: PinnerConfig;\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.config = config;\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 return await this.#waitForOperationById(input, options);\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: UploadResult with no CID and no operationId — TUS upload.\n // Poll GET /api/upload/result/{id} to resolve the CID, then proceed via CID.\n if (isUploadResult(input) && input.id) {\n const resolved = await this.#waitForUploadResult(input, options);\n if (resolved.cid) {\n return await this.#waitForOperationByCid(resolved, options);\n }\n return resolved;\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 if (!uploadResult.cid) {\n throw new Error(\"Cannot wait for operation by CID — CID not available. Use upload result ID to poll GET /api/upload/result/{id} instead.\");\n }\n\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 async #waitForOperationById(\n input: UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\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 return {\n ...input,\n cid: operation.cid,\n operationId: operation.id,\n };\n }\n\n async #waitForUploadResult(\n uploadResult: UploadResult,\n options?: OperationPollingOptions,\n ): Promise<UploadResult> {\n const uploadId = uploadResult.id;\n if (!uploadId) {\n throw new Error(\"No upload ID available to poll for upload result\");\n }\n\n const baseUrl = this.config.endpoint || DEFAULT_ENDPOINT;\n const fetchUrl = `${baseUrl}/api/upload/result/${encodeURIComponent(uploadId)}`;\n const headers = { Authorization: `Bearer ${this.config.jwt}` };\n\n const result = await poll<UploadResultResponse>(\n async () => {\n const response = await fetch(fetchUrl, { headers });\n\n if (response.status === 404) {\n return {\n success: false,\n error: new AccountError(`Upload result not found for ID ${uploadId}`, 404),\n };\n }\n\n const body: UploadResultResponse = await response.json();\n\n if (body.status === \"failed\") {\n return {\n success: false,\n error: new AccountError(body.error || `Upload failed for ID ${uploadId}`, 500),\n };\n }\n\n return { success: true, data: body };\n },\n (data) => data.status === \"completed\" || data.status === \"duplicate\",\n { interval: options?.interval, timeout: options?.timeout },\n );\n\n if (!result.success) {\n throw result.error;\n }\n\n return {\n ...uploadResult,\n ...(result.data.cid ? { cid: result.data.cid } : {}),\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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"}
@@ -1,4 +1,3 @@
1
- import { UploadResultSymbol } from "../types/upload.js";
2
1
  import { UPLOAD_SOURCE_TUS } from "./constants.js";
3
2
  import { BaseUploadHandler } from "./base-upload.js";
4
3
  import TusPlugin from "@uppy/tus";
@@ -23,42 +22,10 @@ var TUSUploadHandler = class extends BaseUploadHandler {
23
22
  }
24
23
  parseResult(result) {
25
24
  const uppyResponse = result;
26
- if (!uppyResponse) return {
27
- id: "",
28
- cid: "",
29
- name: "",
30
- size: 0,
31
- mimeType: "",
32
- createdAt: /* @__PURE__ */ new Date(),
33
- numberOfFiles: 1,
34
- isDirectory: false,
35
- [UploadResultSymbol]: true
36
- };
37
- const response = uppyResponse.body;
38
- if (response && response.cid) return {
39
- id: response.id,
40
- cid: response.cid,
41
- name: response.name,
42
- size: response.size,
43
- mimeType: response.mimeType,
44
- createdAt: new Date(response.createdAt),
45
- numberOfFiles: response.numberOfFiles,
46
- isDirectory: response.isDirectory ?? false,
47
- keyvalues: response.keyvalues,
48
- operationId: response.operationId,
49
- [UploadResultSymbol]: true
50
- };
51
- return {
52
- id: uppyResponse.uploadURL?.split("/").pop() || "",
53
- cid: "",
54
- name: "",
55
- size: 0,
56
- mimeType: "",
57
- createdAt: /* @__PURE__ */ new Date(),
58
- numberOfFiles: 1,
59
- isDirectory: false,
60
- [UploadResultSymbol]: true
61
- };
25
+ if (!uppyResponse) return this.mapUploadResponse(void 0, "");
26
+ const uploadId = uppyResponse.uploadURL?.split("/").pop() || "";
27
+ const body = uppyResponse.body;
28
+ return this.mapUploadResponse(body, uploadId, { isDirectory: false });
62
29
  }
63
30
  getUploadSource() {
64
31
  return UPLOAD_SOURCE_TUS;
@@ -1 +1 @@
1
- {"version":3,"file":"tus-upload.js","names":[],"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 type { PinnerConfig } from \"@/config\";\nimport { UPLOAD_SOURCE_TUS } from \"./constants\";\n\nexport class TUSUploadHandler extends BaseUploadHandler {\n constructor(config: PinnerConfig) {\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":";;;;;;AAQA,IAAa,mBAAb,cAAsC,kBAAkB;CACtD,YAAY,QAAsB;AAChC,QAAM,OAAO;;CAEf,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAI,WAAW;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;IACZ,qBAAqB;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;IACrB,qBAAqB;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;IACZ,qBAAqB;GACvB;;CAGH,AAAU,kBAA0B;AAClC,SAAO"}
1
+ {"version":3,"file":"tus-upload.js","names":[],"sources":["../../../src/upload/tus-upload.ts"],"sourcesContent":["import Uppy from \"@uppy/core\";\nimport TusPlugin from \"@uppy/tus\";\nimport type { UploadResult } from \"@/types/upload\";\nimport { BaseUploadHandler } from \"./base-upload\";\nimport type { PinnerConfig } from \"@/config\";\nimport { UPLOAD_SOURCE_TUS } from \"./constants\";\n\nexport class TUSUploadHandler extends BaseUploadHandler {\n constructor(config: PinnerConfig) {\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,\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?: unknown;\n }\n | undefined;\n\n if (!uppyResponse) {\n return this.mapUploadResponse(undefined, \"\");\n }\n\n const uploadId = uppyResponse.uploadURL?.split(\"/\").pop() || \"\";\n const body = uppyResponse.body;\n\n return this.mapUploadResponse(body, uploadId, {\n isDirectory: false,\n });\n }\n\n protected getUploadSource(): string {\n return UPLOAD_SOURCE_TUS;\n }\n}\n"],"mappings":";;;;;AAOA,IAAa,mBAAb,cAAsC,kBAAkB;CACtD,YAAY,QAAsB;AAChC,QAAM,OAAO;;CAEf,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAI,WAAW;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,KAAK,kBAAkB,QAAW,GAAG;EAG9C,MAAM,WAAW,aAAa,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI;EAC7D,MAAM,OAAO,aAAa;AAE1B,SAAO,KAAK,kBAAkB,MAAM,UAAU,EAC5C,aAAa,OACd,CAAC;;CAGJ,AAAU,kBAA0B;AAClC,SAAO"}
@@ -1,4 +1,3 @@
1
- import { UploadResultSymbol } from "../types/upload.js";
2
1
  import { UPLOAD_SOURCE_XHR } from "./constants.js";
3
2
  import { BaseUploadHandler } from "./base-upload.js";
4
3
  import XHRUpload from "@lumeweb/uppy-post-upload";
@@ -15,19 +14,9 @@ var XHRUploadHandler = class extends BaseUploadHandler {
15
14
  }
16
15
  parseResult(result) {
17
16
  const uppyResponse = result;
18
- const response = uppyResponse.body || uppyResponse;
19
- return {
20
- id: response.id,
21
- cid: response.cid,
22
- name: response.name,
23
- size: response.size,
24
- mimeType: response.mimeType,
25
- createdAt: new Date(response.createdAt),
26
- numberOfFiles: response.numberOfFiles,
27
- keyvalues: response.keyvalues,
28
- operationId: response.operationId,
29
- [UploadResultSymbol]: true
30
- };
17
+ const body = uppyResponse.body;
18
+ const uploadId = uppyResponse.uploadURL?.split("/").pop() || "";
19
+ return this.mapUploadResponse(body, uploadId);
31
20
  }
32
21
  getUploadSource() {
33
22
  return UPLOAD_SOURCE_XHR;
@@ -1 +1 @@
1
- {"version":3,"file":"xhr-upload.js","names":[],"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,cAAsC,kBAAkB;CACtD,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAI,WAAW;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;IACrB,qBAAqB;GACvB;;CAGH,AAAU,kBAA0B;AAClC,SAAO"}
1
+ {"version":3,"file":"xhr-upload.js","names":[],"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 { 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?: unknown;\n };\n\n const body = uppyResponse.body;\n const uploadId = uppyResponse.uploadURL?.split(\"/\").pop() || \"\";\n\n return this.mapUploadResponse(body, uploadId);\n }\n\n protected getUploadSource(): string {\n return UPLOAD_SOURCE_XHR;\n }\n}\n"],"mappings":";;;;;AAMA,IAAa,mBAAb,cAAsC,kBAAkB;CACtD,AAAU,gBAAgB,MAAkB;AAC1C,OAAK,IAAI,WAAW;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;EAKrB,MAAM,OAAO,aAAa;EAC1B,MAAM,WAAW,aAAa,WAAW,MAAM,IAAI,CAAC,KAAK,IAAI;AAE7D,SAAO,KAAK,kBAAkB,MAAM,SAAS;;CAG/C,AAAU,kBAA0B;AAClC,SAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumeweb/pinner",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -63,9 +63,9 @@
63
63
  "progress-events": "^1.1.0",
64
64
  "tus-js-client": "4.3.1",
65
65
  "unstorage": "^1.17.5",
66
- "@lumeweb/uppy-post-upload": "0.1.1",
67
- "@lumeweb/portal-sdk": "0.1.4",
68
- "@lumeweb/query-builder": "0.1.1"
66
+ "@lumeweb/query-builder": "0.1.1",
67
+ "@lumeweb/portal-sdk": "0.1.5",
68
+ "@lumeweb/uppy-post-upload": "0.1.1"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@faker-js/faker": "^10.4.0",