@lumeweb/pinner 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +690 -28
  3. package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
  4. package/dist/cjs/adapters/pinata/adapter.cjs +88 -0
  5. package/dist/cjs/adapters/pinata/adapter.cjs.map +1 -0
  6. package/dist/cjs/adapters/pinata/adapter.d.cts +35 -0
  7. package/dist/cjs/adapters/pinata/builder.cjs +194 -0
  8. package/dist/cjs/adapters/pinata/builder.cjs.map +1 -0
  9. package/dist/cjs/adapters/pinata/index.cjs +3 -0
  10. package/dist/cjs/adapters/pinata/list-builder.cjs +52 -0
  11. package/dist/cjs/adapters/pinata/list-builder.cjs.map +1 -0
  12. package/dist/cjs/blockstore/index.cjs +2 -0
  13. package/dist/cjs/blockstore/unstorage-base.cjs +240 -0
  14. package/dist/cjs/blockstore/unstorage-base.cjs.map +1 -0
  15. package/dist/cjs/blockstore/unstorage-base.d.cts +23 -0
  16. package/dist/cjs/blockstore/unstorage.cjs +39 -0
  17. package/dist/cjs/blockstore/unstorage.cjs.map +1 -0
  18. package/dist/cjs/blockstore/unstorage.d.cts +36 -0
  19. package/dist/cjs/config.d.cts +51 -0
  20. package/dist/cjs/encoder/base64.cjs +38 -0
  21. package/dist/cjs/encoder/base64.cjs.map +1 -0
  22. package/dist/cjs/encoder/csv/csv-formatter.cjs +81 -0
  23. package/dist/cjs/encoder/csv/csv-formatter.cjs.map +1 -0
  24. package/dist/cjs/encoder/csv/field-formatter.cjs +76 -0
  25. package/dist/cjs/encoder/csv/field-formatter.cjs.map +1 -0
  26. package/dist/cjs/encoder/csv/row-formatter.cjs +159 -0
  27. package/dist/cjs/encoder/csv/row-formatter.cjs.map +1 -0
  28. package/dist/cjs/encoder/csv.cjs +44 -0
  29. package/dist/cjs/encoder/csv.cjs.map +1 -0
  30. package/dist/cjs/encoder/error.cjs +19 -0
  31. package/dist/cjs/encoder/error.cjs.map +1 -0
  32. package/dist/cjs/encoder/index.cjs +6 -0
  33. package/dist/cjs/encoder/json.cjs +36 -0
  34. package/dist/cjs/encoder/json.cjs.map +1 -0
  35. package/dist/cjs/encoder/text.cjs +35 -0
  36. package/dist/cjs/encoder/text.cjs.map +1 -0
  37. package/dist/cjs/encoder/url.cjs +39 -0
  38. package/dist/cjs/encoder/url.cjs.map +1 -0
  39. package/dist/cjs/errors/index.cjs +104 -0
  40. package/dist/cjs/errors/index.cjs.map +1 -0
  41. package/dist/cjs/errors/index.d.cts +47 -0
  42. package/dist/cjs/index.cjs +42 -0
  43. package/dist/cjs/index.d.cts +14 -0
  44. package/dist/cjs/pin/client.cjs +96 -0
  45. package/dist/cjs/pin/client.cjs.map +1 -0
  46. package/dist/cjs/pin/index.cjs +1 -0
  47. package/dist/cjs/pinner.cjs +126 -0
  48. package/dist/cjs/pinner.cjs.map +1 -0
  49. package/dist/cjs/pinner.d.cts +77 -0
  50. package/dist/cjs/types/constants.cjs +34 -0
  51. package/dist/cjs/types/constants.cjs.map +1 -0
  52. package/dist/cjs/types/mime-types.cjs +11 -0
  53. package/dist/cjs/types/mime-types.cjs.map +1 -0
  54. package/dist/cjs/types/mime-types.d.cts +7 -0
  55. package/dist/cjs/types/pin.d.cts +74 -0
  56. package/dist/cjs/types/pinata.d.cts +99 -0
  57. package/dist/cjs/types/type-guards.cjs +20 -0
  58. package/dist/cjs/types/type-guards.cjs.map +1 -0
  59. package/dist/cjs/types/type-guards.d.cts +15 -0
  60. package/dist/cjs/types/upload.cjs +18 -0
  61. package/dist/cjs/types/upload.cjs.map +1 -0
  62. package/dist/cjs/types/upload.d.cts +189 -0
  63. package/dist/cjs/upload/base-upload.cjs +135 -0
  64. package/dist/cjs/upload/base-upload.cjs.map +1 -0
  65. package/dist/cjs/upload/builder.cjs +174 -0
  66. package/dist/cjs/upload/builder.cjs.map +1 -0
  67. package/dist/cjs/upload/builder.d.cts +60 -0
  68. package/dist/cjs/upload/car.cjs +129 -0
  69. package/dist/cjs/upload/car.cjs.map +1 -0
  70. package/dist/cjs/upload/car.d.cts +19 -0
  71. package/dist/cjs/upload/constants.cjs +9 -0
  72. package/dist/cjs/upload/constants.cjs.map +1 -0
  73. package/dist/cjs/upload/index.cjs +8 -0
  74. package/dist/cjs/upload/manager.cjs +249 -0
  75. package/dist/cjs/upload/manager.cjs.map +1 -0
  76. package/dist/cjs/upload/manager.d.cts +35 -0
  77. package/dist/cjs/upload/normalize.cjs +28 -0
  78. package/dist/cjs/upload/normalize.cjs.map +1 -0
  79. package/dist/cjs/upload/tus-upload.cjs +74 -0
  80. package/dist/cjs/upload/tus-upload.cjs.map +1 -0
  81. package/dist/cjs/upload/xhr-upload.cjs +41 -0
  82. package/dist/cjs/upload/xhr-upload.cjs.map +1 -0
  83. package/dist/cjs/utils/env.cjs +12 -0
  84. package/dist/cjs/utils/env.cjs.map +1 -0
  85. package/dist/cjs/utils/stream.cjs +141 -0
  86. package/dist/cjs/utils/stream.cjs.map +1 -0
  87. package/dist/cjs/utils/stream.d.cts +23 -0
  88. package/dist/cjs/utils/tus-patch.cjs +50 -0
  89. package/dist/cjs/utils/tus-patch.cjs.map +1 -0
  90. package/dist/cjs/utils/validation.cjs +62 -0
  91. package/dist/cjs/utils/validation.cjs.map +1 -0
  92. package/dist/esm/_virtual/rolldown_runtime.js +8 -0
  93. package/dist/esm/adapters/pinata/adapter.d.ts +35 -0
  94. package/dist/esm/adapters/pinata/adapter.js +87 -0
  95. package/dist/esm/adapters/pinata/adapter.js.map +1 -0
  96. package/dist/esm/adapters/pinata/builder.d.ts +1 -0
  97. package/dist/esm/adapters/pinata/builder.js +187 -0
  98. package/dist/esm/adapters/pinata/builder.js.map +1 -0
  99. package/dist/esm/adapters/pinata/index.d.ts +4 -0
  100. package/dist/esm/adapters/pinata/index.js +3 -0
  101. package/dist/esm/adapters/pinata/list-builder.d.ts +1 -0
  102. package/dist/esm/adapters/pinata/list-builder.js +51 -0
  103. package/dist/esm/adapters/pinata/list-builder.js.map +1 -0
  104. package/dist/esm/blockstore/index.d.ts +2 -0
  105. package/dist/esm/blockstore/index.js +2 -0
  106. package/dist/esm/blockstore/unstorage-base.d.ts +23 -0
  107. package/dist/esm/blockstore/unstorage-base.js +231 -0
  108. package/dist/esm/blockstore/unstorage-base.js.map +1 -0
  109. package/dist/esm/blockstore/unstorage.d.ts +36 -0
  110. package/dist/esm/blockstore/unstorage.js +38 -0
  111. package/dist/esm/blockstore/unstorage.js.map +1 -0
  112. package/dist/esm/config.d.ts +51 -0
  113. package/dist/esm/encoder/base64.js +37 -0
  114. package/dist/esm/encoder/base64.js.map +1 -0
  115. package/dist/esm/encoder/csv/csv-formatter.js +81 -0
  116. package/dist/esm/encoder/csv/csv-formatter.js.map +1 -0
  117. package/dist/esm/encoder/csv/field-formatter.js +75 -0
  118. package/dist/esm/encoder/csv/field-formatter.js.map +1 -0
  119. package/dist/esm/encoder/csv/row-formatter.js +159 -0
  120. package/dist/esm/encoder/csv/row-formatter.js.map +1 -0
  121. package/dist/esm/encoder/csv.js +43 -0
  122. package/dist/esm/encoder/csv.js.map +1 -0
  123. package/dist/esm/encoder/error.js +18 -0
  124. package/dist/esm/encoder/error.js.map +1 -0
  125. package/dist/esm/encoder/index.js +6 -0
  126. package/dist/esm/encoder/json.js +35 -0
  127. package/dist/esm/encoder/json.js.map +1 -0
  128. package/dist/esm/encoder/text.js +34 -0
  129. package/dist/esm/encoder/text.js.map +1 -0
  130. package/dist/esm/encoder/url.js +36 -0
  131. package/dist/esm/encoder/url.js.map +1 -0
  132. package/dist/esm/errors/index.d.ts +47 -0
  133. package/dist/esm/errors/index.js +93 -0
  134. package/dist/esm/errors/index.js.map +1 -0
  135. package/dist/esm/index.d.ts +16 -0
  136. package/dist/esm/index.js +14 -0
  137. package/dist/esm/pin/client.js +95 -0
  138. package/dist/esm/pin/client.js.map +1 -0
  139. package/dist/esm/pin/index.js +1 -0
  140. package/dist/esm/pinner.d.ts +77 -0
  141. package/dist/esm/pinner.js +125 -0
  142. package/dist/esm/pinner.js.map +1 -0
  143. package/dist/esm/types/constants.js +29 -0
  144. package/dist/esm/types/constants.js.map +1 -0
  145. package/dist/esm/types/mime-types.d.ts +7 -0
  146. package/dist/esm/types/mime-types.js +8 -0
  147. package/dist/esm/types/mime-types.js.map +1 -0
  148. package/dist/esm/types/pin.d.ts +74 -0
  149. package/dist/esm/types/pinata.d.ts +99 -0
  150. package/dist/esm/types/type-guards.d.ts +15 -0
  151. package/dist/esm/types/type-guards.js +19 -0
  152. package/dist/esm/types/type-guards.js.map +1 -0
  153. package/dist/esm/types/upload.d.ts +189 -0
  154. package/dist/esm/types/upload.js +16 -0
  155. package/dist/esm/types/upload.js.map +1 -0
  156. package/dist/esm/upload/base-upload.js +132 -0
  157. package/dist/esm/upload/base-upload.js.map +1 -0
  158. package/dist/esm/upload/builder.d.ts +60 -0
  159. package/dist/esm/upload/builder.js +173 -0
  160. package/dist/esm/upload/builder.js.map +1 -0
  161. package/dist/esm/upload/car.d.ts +19 -0
  162. package/dist/esm/upload/car.js +125 -0
  163. package/dist/esm/upload/car.js.map +1 -0
  164. package/dist/esm/upload/constants.js +7 -0
  165. package/dist/esm/upload/constants.js.map +1 -0
  166. package/dist/esm/upload/index.js +8 -0
  167. package/dist/esm/upload/manager.d.ts +35 -0
  168. package/dist/esm/upload/manager.js +248 -0
  169. package/dist/esm/upload/manager.js.map +1 -0
  170. package/dist/esm/upload/normalize.js +28 -0
  171. package/dist/esm/upload/normalize.js.map +1 -0
  172. package/dist/esm/upload/tus-upload.js +72 -0
  173. package/dist/esm/upload/tus-upload.js.map +1 -0
  174. package/dist/esm/upload/xhr-upload.js +39 -0
  175. package/dist/esm/upload/xhr-upload.js.map +1 -0
  176. package/dist/esm/utils/env.js +11 -0
  177. package/dist/esm/utils/env.js.map +1 -0
  178. package/dist/esm/utils/stream.d.ts +23 -0
  179. package/dist/esm/utils/stream.js +134 -0
  180. package/dist/esm/utils/stream.js.map +1 -0
  181. package/dist/esm/utils/tus-patch.js +51 -0
  182. package/dist/esm/utils/tus-patch.js.map +1 -0
  183. package/dist/esm/utils/validation.js +60 -0
  184. package/dist/esm/utils/validation.js.map +1 -0
  185. package/package.json +95 -8
  186. package/public/mockServiceWorker.js +349 -0
package/README.md CHANGED
@@ -1,45 +1,707 @@
1
1
  # @lumeweb/pinner
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ A TypeScript library for uploading files to IPFS and managing pinning operations with support for multiple upload protocols, custom storage backends, and flexible configuration.
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ ## Features
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
7
+ - **Multiple Upload Methods**: TUS resumable uploads, XHR uploads, and direct CAR file uploads
8
+ - **Directory Support**: Upload multiple files as a directory to IPFS
9
+ - **Pin Management**: Add, list, remove, and check status of pinned content
10
+ - **Custom Blockstore**: Flexible storage backend using unstorage (IndexedDB, filesystem, Redis, etc.)
11
+ - **Adapters**: Built-in Pinata adapter with extensible adapter pattern
12
+ - **Encoders**: Support for CSV, JSON, text, base64, and URL encoding
13
+ - **Progress Tracking**: Real-time upload progress and operation polling
14
+ - **Cross-Platform**: Works in both browser and Node.js environments
15
+ - **TypeScript**: Fully typed with comprehensive type definitions
8
16
 
9
- ## Purpose
17
+ ## Installation
10
18
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@lumeweb/pinner`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
19
+ ```bash
20
+ pnpm add @lumeweb/pinner
21
+ ```
15
22
 
16
- ## What is OIDC Trusted Publishing?
23
+ ## Quick Start
17
24
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
25
+ ```typescript
26
+ import { Pinner } from "@lumeweb/pinner";
19
27
 
20
- ## Setup Instructions
28
+ // Initialize with JWT token
29
+ const pinner = new Pinner({
30
+ jwt: "your-jwt-token",
31
+ endpoint: "https://ipfs.pinner.xyz",
32
+ gateway: "https://dweb.link"
33
+ });
21
34
 
22
- To properly configure OIDC trusted publishing for this package:
35
+ // Upload a file
36
+ const file = new File(["Hello, IPFS!"], "hello.txt", { type: "text/plain" });
37
+ const operation = await pinner.upload(file);
23
38
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
39
+ // Wait for completion
40
+ const result = await operation.result;
41
+ console.log("CID:", result.cid);
42
+ console.log("URL:", result.url);
28
43
 
29
- ## DO NOT USE THIS PACKAGE
44
+ // List pins
45
+ const pins = await pinner.listPins();
46
+ console.log("Pinned content:", pins);
47
+ ```
30
48
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
49
+ ## Configuration
36
50
 
37
- ## More Information
51
+ The `Pinner` class accepts a `PinnerConfig` object:
38
52
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
53
+ ```typescript
54
+ interface PinnerConfig {
55
+ // Required
56
+ jwt: string;
57
+
58
+ // Optional
59
+ endpoint?: string; // Default: "https://ipfs.pinner.xyz"
60
+ gateway?: string; // Default: "https://dweb.link"
61
+ allowedFileTypes?: string[]; // MIME types allowed for upload
62
+ fetch?: typeof fetch; // Custom fetch implementation
63
+ datastore?: Datastore; // Custom datastore for Helia
64
+ storage?: Storage; // Custom unstorage instance
65
+ datastoreName?: string; // Base name for storage (default: "pinner-helia-data")
66
+ }
67
+ ```
68
+
69
+ ## Upload Methods
70
+
71
+ ### Basic Upload
72
+
73
+ ```typescript
74
+ // Upload with default options
75
+ const operation = await pinner.upload(file);
76
+
77
+ // Upload with custom options
78
+ const operation = await pinner.upload(file, {
79
+ metadata: { name: "My File" },
80
+ timeout: 30000
81
+ });
82
+
83
+ // Wait for result
84
+ const result = await operation.result;
85
+ ```
86
+
87
+ ### Upload and Wait
88
+
89
+ Convenience method for simple use cases:
90
+
91
+ ```typescript
92
+ const result = await pinner.uploadAndWait(file);
93
+ console.log("CID:", result.cid);
94
+ ```
95
+
96
+ ### Directory Upload
97
+
98
+ ```typescript
99
+ const files = [
100
+ new File(["content1"], "file1.txt"),
101
+ new File(["content2"], "file2.txt")
102
+ ];
103
+
104
+ const operation = await pinner.uploadDirectory(files);
105
+ const result = await operation.result;
106
+ ```
107
+
108
+ ### CAR File Upload
109
+
110
+ Upload pre-generated CAR files without preprocessing:
111
+
112
+ ```typescript
113
+ const carFile = new File([carData], "content.car", { type: "application/vnd.ipld.car" });
114
+ const operation = await pinner.uploadCar(carFile);
115
+ const result = await operation.result;
116
+ ```
117
+
118
+ ### Upload Builder Pattern
119
+
120
+ Use the builder API for more control:
121
+
122
+ ```typescript
123
+ // Upload JSON
124
+ const operation = await pinner.upload.json({ foo: "bar" });
125
+
126
+ // Upload text
127
+ const operation = await pinner.upload.text("Hello, world!");
128
+
129
+ // Upload CSV
130
+ const operation = await pinner.upload.csv([
131
+ { name: "Alice", age: 30 },
132
+ { name: "Bob", age: 25 }
133
+ ]);
134
+ ```
135
+
136
+ ## Progress Tracking
137
+
138
+ Upload operations return an `UploadOperation` with progress tracking:
139
+
140
+ ```typescript
141
+ const operation = await pinner.upload(file);
142
+
143
+ // Listen to progress events
144
+ operation.on("progress", (progress) => {
145
+ console.log(`Progress: ${progress.progress}%`);
146
+ console.log(`Speed: ${progress.speed} bytes/sec`);
147
+ });
148
+
149
+ // Listen to completion
150
+ operation.on("complete", (result) => {
151
+ console.log("Upload complete:", result.cid);
152
+ });
153
+
154
+ // Listen to errors
155
+ operation.on("error", (error) => {
156
+ console.error("Upload failed:", error);
157
+ });
158
+
159
+ // Or await the result directly
160
+ const result = await operation.result;
161
+ ```
162
+
163
+ ## Pin Management
164
+
165
+ ### Pin by CID
166
+
167
+ ```typescript
168
+ import { CID } from "multiformats/cid";
169
+
170
+ // Pin existing content
171
+ const cid = CID.parse("Qm...");
172
+ await pinner.pinByHash(cid);
173
+
174
+ // With options
175
+ await pinner.pinByHash(cid, {
176
+ name: "My Pin",
177
+ metadata: { key: "value" }
178
+ });
179
+ ```
180
+
181
+ ### List Pins
182
+
183
+ ```typescript
184
+ // List all pins
185
+ const pins = await pinner.listPins();
186
+
187
+ // List with filters
188
+ const pins = await pinner.listPins({
189
+ status: "pinned",
190
+ limit: 10,
191
+ offset: 0
192
+ });
193
+ ```
194
+
195
+ ### Get Pin Status
196
+
197
+ ```typescript
198
+ const pin = await pinner.getPinStatus(cid);
199
+ console.log("Pin status:", pin.status);
200
+ console.log("Created:", pin.created);
201
+ ```
202
+
203
+ ### Check if Pinned
204
+
205
+ ```typescript
206
+ const isPinned = await pinner.isPinned(cid);
207
+ if (isPinned) {
208
+ console.log("Content is pinned");
209
+ }
210
+ ```
211
+
212
+ ### Update Metadata
213
+
214
+ ```typescript
215
+ await pinner.setPinMetadata(cid, {
216
+ name: "Updated Name",
217
+ description: "Updated description"
218
+ });
219
+ ```
220
+
221
+ ### Remove Pin
222
+
223
+ ```typescript
224
+ await pinner.unpin(cid);
225
+ ```
226
+
227
+ ## Operation Polling
228
+
229
+ Wait for operations to complete with custom polling options:
230
+
231
+ ```typescript
232
+ // Poll with default settings
233
+ const result = await pinner.waitForOperation(operationId);
234
+
235
+ // Poll with custom options
236
+ const result = await pinner.waitForOperation(operationId, {
237
+ interval: 1000, // Check every 1 second
238
+ timeout: 60000, // Timeout after 60 seconds
239
+ settledStates: ["completed", "failed"]
240
+ });
241
+ ```
242
+
243
+ ## Custom Blockstore
244
+
245
+ Configure a custom storage backend using unstorage:
246
+
247
+ ```typescript
248
+ import { Pinner, createBlockstore, setDriverFactory } from "@lumeweb/pinner";
249
+ import { createStorage } from "unstorage";
250
+ import redisDriver from "unstorage/drivers/redis";
251
+
252
+ // Create Redis storage
253
+ const storage = createStorage({
254
+ driver: redisDriver({
255
+ host: "localhost",
256
+ port: 6379,
257
+ base: "pinner:"
258
+ })
259
+ });
260
+
261
+ // Initialize with custom storage
262
+ const pinner = new Pinner({
263
+ jwt: "your-token",
264
+ storage
265
+ });
266
+ ```
267
+
268
+ ### Blockstore Options
269
+
270
+ ```typescript
271
+ import { createBlockstore } from "@lumeweb/pinner";
272
+
273
+ // Auto-configure (browser: IndexedDB, Node.js: filesystem)
274
+ const blockstore = createBlockstore();
275
+
276
+ // Custom driver
277
+ const blockstore = createBlockstore({
278
+ driver: localStorageDriver({ base: "my-app:" })
279
+ });
280
+
281
+ // Pre-configured storage
282
+ const blockstore = createBlockstore({
283
+ storage: myStorageInstance
284
+ });
285
+
286
+ // Disable auto-configuration (uses memory)
287
+ const blockstore = createBlockstore({
288
+ autoConfigure: false
289
+ });
290
+ ```
291
+
292
+ See [blockstore/README.md](./src/blockstore/README.md) for detailed blockstore documentation.
293
+
294
+ ## Adapters
295
+
296
+ ### Pinata Adapter
297
+
298
+ The Pinata adapter provides a Pinata SDK-compatible interface for the Pinner client, making it easy to migrate from Pinata SDK with minimal code changes.
299
+
300
+ #### Setup
301
+
302
+ ```typescript
303
+ import { Pinner, pinataAdapter } from "@lumeweb/pinner";
304
+
305
+ // Initialize Pinner
306
+ const pinner = new Pinner({
307
+ jwt: "your-jwt-token"
308
+ });
309
+
310
+ // Create Pinata adapter
311
+ const pinata = pinataAdapter(pinner);
312
+ ```
313
+
314
+ #### Upload Methods
315
+
316
+ The Pinata adapter provides multiple upload methods with a fluent builder pattern:
317
+
318
+ ##### Upload a File
319
+
320
+ ```typescript
321
+ // Simple file upload
322
+ const result = await pinata.upload.file(file).execute();
323
+ console.log("CID:", result.IpfsHash);
324
+ console.log("Size:", result.PinSize);
325
+
326
+ // Upload with metadata
327
+ const result = await pinata.upload.file(file)
328
+ .name("My File")
329
+ .keyvalues({ key: "value" })
330
+ .execute();
331
+ ```
332
+
333
+ ##### Upload Multiple Files (Directory)
334
+
335
+ ```typescript
336
+ const files = [
337
+ new File(["content1"], "file1.txt"),
338
+ new File(["content2"], "file2.txt")
339
+ ];
340
+
341
+ const result = await pinata.upload.fileArray(files)
342
+ .name("My Directory")
343
+ .keyvalues({ type: "directory" })
344
+ .execute();
345
+ ```
346
+
347
+ ##### Upload JSON Data
348
+
349
+ ```typescript
350
+ const data = { foo: "bar", number: 42 };
351
+
352
+ const result = await pinata.upload.json(data)
353
+ .name("data.json")
354
+ .keyvalues({ format: "json" })
355
+ .execute();
356
+ ```
357
+
358
+ ##### Upload Base64 String
359
+
360
+ ```typescript
361
+ const base64String = "SGVsbG8sIHdvcmxkIQ==";
362
+
363
+ const result = await pinata.upload.base64(base64String)
364
+ .name("base64-file.txt")
365
+ .execute();
366
+ ```
367
+
368
+ ##### Upload from URL
369
+
370
+ ```typescript
371
+ const result = await pinata.upload.url("https://example.com/data.json")
372
+ .name("downloaded-file.json")
373
+ .execute();
374
+ ```
375
+
376
+ ##### Pin by CID
377
+
378
+ ```typescript
379
+ // Pin existing content
380
+ await pinata.upload.cid("Qm...").execute();
381
+
382
+ // Pin with metadata
383
+ await pinata.upload.cid("Qm...")
384
+ .name("Existing Content")
385
+ .keyvalues({ source: "external" })
386
+ .execute();
387
+ ```
388
+
389
+ #### Pin Management
390
+
391
+ ##### Pin by Hash
392
+
393
+ ```typescript
394
+ await pinata.pinByHash("Qm...", {
395
+ name: "My Pin",
396
+ keyvalues: { key: "value" }
397
+ });
398
+ ```
399
+
400
+ ##### Unpin Content
401
+
402
+ ```typescript
403
+ await pinata.unpin("Qm...");
404
+ ```
405
+
406
+ ##### Get Pin Status
407
+
408
+ ```typescript
409
+ const pin = await pinata.getPinStatus("Qm...");
410
+ console.log("Pin ID:", pin.id);
411
+ console.log("IPFS Hash:", pin.ipfsPinHash);
412
+ console.log("Size:", pin.size);
413
+ console.log("Date Pinned:", pin.datePinned);
414
+ console.log("Metadata:", pin.metadata);
415
+ ```
416
+
417
+ ##### Check if Pinned
418
+
419
+ ```typescript
420
+ const isPinned = await pinata.isPinned("Qm...");
421
+ if (isPinned) {
422
+ console.log("Content is pinned");
423
+ }
424
+ ```
425
+
426
+ ##### Update Pin Metadata
427
+
428
+ ```typescript
429
+ await pinata.setPinMetadata("Qm...", {
430
+ name: "Updated Name",
431
+ key: "value"
432
+ });
433
+ ```
434
+
435
+ #### Files Management
436
+
437
+ ##### List Files
438
+
439
+ ```typescript
440
+ // List all files
441
+ const files = await pinata.files.list().execute();
442
+
443
+ // List with pagination
444
+ const files = await pinata.files.list()
445
+ .limit(10)
446
+ .pageToken("next-page-token")
447
+ .execute();
448
+
449
+ files.forEach(file => {
450
+ console.log("ID:", file.id);
451
+ console.log("CID:", file.cid);
452
+ console.log("Size:", file.size);
453
+ console.log("Name:", file.name);
454
+ console.log("Created:", file.createdAt);
455
+ });
456
+ ```
457
+
458
+ ##### Get File by ID
459
+
460
+ ```typescript
461
+ const file = await pinata.files.get("Qm...");
462
+ console.log("File details:", file);
463
+ ```
464
+
465
+ #### Pinata SDK Compatibility
466
+
467
+ The adapter follows the Pinata SDK's API conventions, making migration straightforward:
468
+
469
+ **Pinata SDK:**
470
+ ```typescript
471
+ import pinataSDK from '@pinata/sdk';
472
+ const pinata = pinataSDK('apiKey', 'apiSecret');
473
+
474
+ const result = await pinata.pinFileToIPFS(file, {
475
+ pinataMetadata: { name: 'My File' },
476
+ pinataOptions: { cidVersion: 1 }
477
+ });
478
+ ```
479
+
480
+ **Pinner with Pinata Adapter:**
481
+ ```typescript
482
+ import { Pinner, pinataAdapter } from '@lumeweb/pinner';
483
+
484
+ const pinner = new Pinner({ jwt: 'your-jwt-token' });
485
+ const pinata = pinataAdapter(pinner);
486
+
487
+ const result = await pinata.upload.file(file)
488
+ .name('My File')
489
+ .execute();
490
+ ```
491
+
492
+ #### Result Format
493
+
494
+ Upload operations return a `PinataUploadResult`:
495
+
496
+ ```typescript
497
+ interface PinataUploadResult {
498
+ IpfsHash: string; // IPFS CID
499
+ PinSize: number; // Size in bytes
500
+ Timestamp: string; // ISO timestamp
501
+ isDuplicate: boolean; // Whether content was already pinned
502
+ }
503
+ ```
504
+
505
+ #### Error Handling
506
+
507
+ The adapter provides specific errors:
508
+
509
+ ```typescript
510
+ import { PinataAdapterError } from "@lumeweb/pinner/adapters/pinata";
511
+
512
+ try {
513
+ const result = await pinata.upload.file(file).execute();
514
+ } catch (error) {
515
+ if (error instanceof PinataAdapterError) {
516
+ switch (error.code) {
517
+ case "UPLOAD_FAILED":
518
+ console.error("Upload failed:", error.message);
519
+ break;
520
+ case "EMPTY_FILE_ARRAY":
521
+ console.error("Cannot upload empty file array");
522
+ break;
523
+ case "INVALID_CID":
524
+ console.error("Invalid CID:", error.message);
525
+ break;
526
+ }
527
+ }
528
+ }
529
+ ```
530
+
531
+ ## Encoders
532
+
533
+ The library provides several encoders for different data formats:
534
+
535
+ ```typescript
536
+ // JSON encoder
537
+ const operation = await pinner.upload.json({ data: "value" });
538
+
539
+ // CSV encoder
540
+ const operation = await pinner.upload.csv([
541
+ { column1: "value1", column2: "value2" }
542
+ ]);
543
+
544
+ // Text encoder
545
+ const operation = await pinner.upload.text("Plain text content");
546
+
547
+ // Base64 encoder
548
+ const operation = await pinner.upload.base64("SGVsbG8sIHdvcmxkIQ==");
549
+
550
+ // URL encoder
551
+ const operation = await pinner.upload.url("https://example.com/data");
552
+ ```
553
+
554
+ ## Error Handling
555
+
556
+ The library provides specific error types:
557
+
558
+ ```typescript
559
+ import {
560
+ PinnerError,
561
+ ConfigurationError,
562
+ AuthenticationError,
563
+ UploadError,
564
+ NetworkError,
565
+ ValidationError,
566
+ EmptyFileError,
567
+ TimeoutError,
568
+ PinError,
569
+ NotFoundError,
570
+ RateLimitError
571
+ } from "@lumeweb/pinner";
572
+
573
+ try {
574
+ const result = await pinner.uploadAndWait(file);
575
+ } catch (error) {
576
+ if (error instanceof AuthenticationError) {
577
+ console.error("Authentication failed:", error.message);
578
+ } else if (error instanceof NetworkError) {
579
+ console.error("Network error:", error.message);
580
+ } else if (error instanceof ValidationError) {
581
+ console.error("Validation error:", error.message);
582
+ } else {
583
+ console.error("Unknown error:", error);
584
+ }
585
+ }
586
+ ```
587
+
588
+ ### Type Guards
589
+
590
+ ```typescript
591
+ import { isRetryable, isAuthenticationError } from "@lumeweb/pinner";
592
+
593
+ if (isRetryable(error)) {
594
+ // Retry the operation
595
+ }
596
+
597
+ if (isAuthenticationError(error)) {
598
+ // Re-authenticate
599
+ }
600
+ ```
601
+
602
+ ## Stream Utilities
603
+
604
+ ```typescript
605
+ import {
606
+ streamToBlob,
607
+ calculateStreamSize,
608
+ asyncGeneratorToReadableStream,
609
+ readableStreamToAsyncIterable
610
+ } from "@lumeweb/pinner";
611
+
612
+ // Convert stream to blob
613
+ const blob = await streamToBlob(stream);
614
+
615
+ // Calculate stream size
616
+ const size = await calculateStreamSize(stream);
617
+
618
+ // Convert async generator to readable stream
619
+ const readableStream = asyncGeneratorToReadableStream(asyncGenerator);
620
+
621
+ // Convert readable stream to async iterable
622
+ const asyncIterable = readableStreamToAsyncIterable(readableStream);
623
+ ```
624
+
625
+ ## Testing
626
+
627
+ The library includes comprehensive tests:
628
+
629
+ ```bash
630
+ # Run all tests
631
+ pnpm test
632
+
633
+ # Run tests with coverage
634
+ pnpm coverage
635
+
636
+ # Run specific test project
637
+ vitest run --project node-upload-unit
638
+ vitest run --project browser-upload-integration
639
+ ```
640
+
641
+ ### Test Structure
642
+
643
+ - **Unit Tests**: Test individual components with mocks
644
+ - **Integration Tests**: Test real-world scenarios without mocks
645
+ - **Browser Tests**: Run tests in Chromium using Playwright
646
+ - **Node Tests**: Run tests in Node.js environment
647
+
648
+ ## Build
649
+
650
+ ```bash
651
+ # Build the library
652
+ pnpm build
653
+
654
+ # Type checking
655
+ pnpm lint
656
+ ```
657
+
658
+ The library outputs:
659
+ - ESM: `dist/esm/`
660
+ - CJS: `dist/cjs/`
661
+ - Type definitions: `dist/esm/*.d.ts`
662
+
663
+ ## Package Exports
664
+
665
+ ```typescript
666
+ // Main exports
667
+ import { Pinner } from "@lumeweb/pinner";
668
+
669
+ // Upload types and utilities
670
+ import {
671
+ UploadManager,
672
+ UploadResult,
673
+ UploadOptions,
674
+ UploadProgress
675
+ } from "@lumeweb/pinner";
676
+
677
+ // Pin types
678
+ import type {
679
+ RemotePins,
680
+ RemotePin,
681
+ RemoteAddOptions,
682
+ RemoteLsOptions
683
+ } from "@lumeweb/pinner";
684
+
685
+ // Blockstore
686
+ import {
687
+ createBlockstore,
688
+ createDatastore,
689
+ setDriverFactory
690
+ } from "@lumeweb/pinner";
691
+
692
+ // Adapters
693
+ import { pinataAdapter } from "@lumeweb/pinner/adapters/pinata";
694
+
695
+ // Blockstore module
696
+ import { UnstorageBlockstore } from "@lumeweb/pinner/blockstore";
697
+ ```
698
+
699
+ ## License
700
+
701
+ MIT
702
+
703
+ ## Contributing
704
+
705
+ Contributions are welcome! Please ensure all tests pass and follow the existing code style.
42
706
 
43
- ---
44
707
 
45
- **Maintained for OIDC setup purposes only**