@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
package/README.md CHANGED
@@ -1,45 +1,724 @@
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 Adapters
297
+
298
+ The Pinata adapters provide Pinata SDK API compatibility for the Pinner client, allowing applications written for the Pinata SDK to work with Lume's IPFS pinning infrastructure with minimal code changes.
299
+
300
+ **Attribution**: These adapters include TypeScript type definitions and API interfaces adapted from the Pinata SDK for compatibility purposes. The original Pinata SDK is available at:
301
+ - **Pinata SDK 2.x**: https://github.com/PinataCloud/pinata/commit/cdc0c06116aaadaf7c4b287a2673cd23b6ba1125
302
+ - **Pinata SDK 1.x**: https://github.com/PinataCloud/pinata/commit/c141177ff3036e46fa7b95fcc68c159b58817836
303
+
304
+ The adapters provide Pinata SDK API compatibility but route all operations through Lume's IPFS pinning infrastructure. They do NOT use Pinata's servers or services.
305
+
306
+ #### Available Adapters
307
+
308
+ - **V2 Adapter** (`pinataAdapter`): Compatible with Pinata SDK 2.x API (recommended, latest)
309
+ - **Legacy Adapter** (`pinataLegacyAdapter`): Compatible with Pinata SDK 1.x API
310
+
311
+ See [adapters/README.md](./src/adapters/README.md) for comprehensive documentation including migration guides, feature support tables, and detailed examples.
312
+
313
+ #### Setup
314
+
315
+ ```typescript
316
+ import { Pinner, pinataAdapter, pinataLegacyAdapter } from "@lumeweb/pinner";
317
+
318
+ // Initialize Pinner
319
+ const pinner = new Pinner({
320
+ jwt: "your-jwt-token",
321
+ endpoint: "https://your-pinning-service-endpoint.com"
322
+ });
323
+
324
+ // Create Pinata V2 adapter (recommended)
325
+ const pinata = pinataAdapter(pinner);
326
+
327
+ // Or use the legacy adapter
328
+ const pinataLegacy = pinataLegacyAdapter(pinner);
329
+ ```
330
+
331
+ #### Upload Methods
332
+
333
+ The Pinata adapter provides multiple upload methods with a fluent builder pattern:
334
+
335
+ ##### Upload a File
336
+
337
+ ```typescript
338
+ // Simple file upload
339
+ const result = await pinata.upload.public.file(file).execute();
340
+ console.log("CID:", result.IpfsHash);
341
+ console.log("Size:", result.PinSize);
342
+
343
+ // Upload with metadata
344
+ const result = await pinata.upload.public.file(file)
345
+ .name("My File")
346
+ .keyvalues({ key: "value" })
347
+ .execute();
348
+ ```
349
+
350
+ ##### Upload Multiple Files (Directory)
351
+
352
+ ```typescript
353
+ const files = [
354
+ new File(["content1"], "file1.txt"),
355
+ new File(["content2"], "file2.txt")
356
+ ];
357
+
358
+ const result = await pinata.upload.public.fileArray(files)
359
+ .name("My Directory")
360
+ .keyvalues({ type: "directory" })
361
+ .execute();
362
+ ```
363
+
364
+ ##### Upload JSON Data
365
+
366
+ ```typescript
367
+ const data = { foo: "bar", number: 42 };
368
+
369
+ const result = await pinata.upload.public.json(data)
370
+ .name("data.json")
371
+ .keyvalues({ format: "json" })
372
+ .execute();
373
+ ```
374
+
375
+ ##### Upload Base64 String
376
+
377
+ ```typescript
378
+ const base64String = "SGVsbG8sIHdvcmxkIQ==";
379
+
380
+ const result = await pinata.upload.public.base64(base64String)
381
+ .name("base64-file.txt")
382
+ .execute();
383
+ ```
384
+
385
+ ##### Upload from URL
386
+
387
+ ```typescript
388
+ const result = await pinata.upload.public.url("https://example.com/data.json")
389
+ .name("downloaded-file.json")
390
+ .execute();
391
+ ```
392
+
393
+ ##### Pin by CID
394
+
395
+ ```typescript
396
+ // Pin existing content
397
+ await pinata.upload.public.cid("Qm...").execute();
398
+
399
+ // Pin with metadata
400
+ await pinata.upload.public.cid("Qm...")
401
+ .name("Existing Content")
402
+ .keyvalues({ source: "external" })
403
+ .execute();
404
+ ```
405
+
406
+ #### Pin Management
407
+
408
+ ##### Pin by Hash
409
+
410
+ ```typescript
411
+ await pinata.pinByHash("Qm...", {
412
+ name: "My Pin",
413
+ keyvalues: { key: "value" }
414
+ });
415
+ ```
416
+
417
+ ##### Unpin Content
418
+
419
+ ```typescript
420
+ await pinata.unpin("Qm...");
421
+ ```
422
+
423
+ ##### Get Pin Status
424
+
425
+ ```typescript
426
+ const pin = await pinata.getPinStatus("Qm...");
427
+ console.log("Pin ID:", pin.id);
428
+ console.log("IPFS Hash:", pin.ipfsPinHash);
429
+ console.log("Size:", pin.size);
430
+ console.log("Date Pinned:", pin.datePinned);
431
+ console.log("Metadata:", pin.metadata);
432
+ ```
433
+
434
+ ##### Check if Pinned
435
+
436
+ ```typescript
437
+ const isPinned = await pinata.isPinned("Qm...");
438
+ if (isPinned) {
439
+ console.log("Content is pinned");
440
+ }
441
+ ```
442
+
443
+ ##### Update Pin Metadata
444
+
445
+ ```typescript
446
+ await pinata.setPinMetadata("Qm...", {
447
+ name: "Updated Name",
448
+ key: "value"
449
+ });
450
+ ```
451
+
452
+ #### Files Management
453
+
454
+ ##### List Files
455
+
456
+ ```typescript
457
+ // List all files
458
+ const files = await pinata.files.public.list().execute();
459
+
460
+ // List with pagination
461
+ const files = await pinata.files.public.list()
462
+ .limit(10)
463
+ .pageToken("next-page-token")
464
+ .execute();
465
+
466
+ files.forEach(file => {
467
+ console.log("ID:", file.id);
468
+ console.log("CID:", file.cid);
469
+ console.log("Size:", file.size);
470
+ console.log("Name:", file.name);
471
+ console.log("Created:", file.createdAt);
472
+ });
473
+ ```
474
+
475
+ ##### Get File by ID
476
+
477
+ ```typescript
478
+ const file = await pinata.files.public.get("Qm...");
479
+ console.log("File details:", file);
480
+ ```
481
+
482
+ #### Pinata SDK Compatibility
483
+
484
+ The adapter follows the Pinata SDK's API conventions, making migration straightforward:
485
+
486
+ **Pinata SDK:**
487
+ ```typescript
488
+ import pinataSDK from '@pinata/sdk';
489
+ const pinata = pinataSDK('apiKey', 'apiSecret');
490
+
491
+ const result = await pinata.pinFileToIPFS(file, {
492
+ pinataMetadata: { name: 'My File' },
493
+ pinataOptions: { cidVersion: 1 }
494
+ });
495
+ ```
496
+
497
+ **Pinner with Pinata Adapter:**
498
+ ```typescript
499
+ import { Pinner, pinataAdapter } from '@lumeweb/pinner';
500
+
501
+ const pinner = new Pinner({ jwt: 'your-jwt-token' });
502
+ const pinata = pinataAdapter(pinner);
503
+
504
+ const result = await pinata.upload.public.file(file)
505
+ .name('My File')
506
+ .execute();
507
+ ```
508
+
509
+ #### Result Format
510
+
511
+ Upload operations return a `PinataUploadResult`:
512
+
513
+ ```typescript
514
+ interface PinataUploadResult {
515
+ IpfsHash: string; // IPFS CID
516
+ PinSize: number; // Size in bytes
517
+ Timestamp: string; // ISO timestamp
518
+ isDuplicate: boolean; // Whether content was already pinned
519
+ }
520
+ ```
521
+
522
+ #### Error Handling
523
+
524
+ The adapter provides specific errors:
525
+
526
+ ```typescript
527
+ import { PinataAdapterError } from "@lumeweb/pinner/adapters/pinata";
528
+
529
+ try {
530
+ const result = await pinata.upload.file(file).execute();
531
+ } catch (error) {
532
+ if (error instanceof PinataAdapterError) {
533
+ switch (error.code) {
534
+ case "UPLOAD_FAILED":
535
+ console.error("Upload failed:", error.message);
536
+ break;
537
+ case "EMPTY_FILE_ARRAY":
538
+ console.error("Cannot upload empty file array");
539
+ break;
540
+ case "INVALID_CID":
541
+ console.error("Invalid CID:", error.message);
542
+ break;
543
+ }
544
+ }
545
+ }
546
+ ```
547
+
548
+ ## Encoders
549
+
550
+ The library provides several encoders for different data formats:
551
+
552
+ ```typescript
553
+ // JSON encoder
554
+ const operation = await pinner.upload.json({ data: "value" });
555
+
556
+ // CSV encoder
557
+ const operation = await pinner.upload.csv([
558
+ { column1: "value1", column2: "value2" }
559
+ ]);
560
+
561
+ // Text encoder
562
+ const operation = await pinner.upload.text("Plain text content");
563
+
564
+ // Base64 encoder
565
+ const operation = await pinner.upload.base64("SGVsbG8sIHdvcmxkIQ==");
566
+
567
+ // URL encoder
568
+ const operation = await pinner.upload.url("https://example.com/data");
569
+ ```
570
+
571
+ ## Error Handling
572
+
573
+ The library provides specific error types:
574
+
575
+ ```typescript
576
+ import {
577
+ PinnerError,
578
+ ConfigurationError,
579
+ AuthenticationError,
580
+ UploadError,
581
+ NetworkError,
582
+ ValidationError,
583
+ EmptyFileError,
584
+ TimeoutError,
585
+ PinError,
586
+ NotFoundError,
587
+ RateLimitError
588
+ } from "@lumeweb/pinner";
589
+
590
+ try {
591
+ const result = await pinner.uploadAndWait(file);
592
+ } catch (error) {
593
+ if (error instanceof AuthenticationError) {
594
+ console.error("Authentication failed:", error.message);
595
+ } else if (error instanceof NetworkError) {
596
+ console.error("Network error:", error.message);
597
+ } else if (error instanceof ValidationError) {
598
+ console.error("Validation error:", error.message);
599
+ } else {
600
+ console.error("Unknown error:", error);
601
+ }
602
+ }
603
+ ```
604
+
605
+ ### Type Guards
606
+
607
+ ```typescript
608
+ import { isRetryable, isAuthenticationError } from "@lumeweb/pinner";
609
+
610
+ if (isRetryable(error)) {
611
+ // Retry the operation
612
+ }
613
+
614
+ if (isAuthenticationError(error)) {
615
+ // Re-authenticate
616
+ }
617
+ ```
618
+
619
+ ## Stream Utilities
620
+
621
+ ```typescript
622
+ import {
623
+ streamToBlob,
624
+ calculateStreamSize,
625
+ asyncGeneratorToReadableStream,
626
+ readableStreamToAsyncIterable
627
+ } from "@lumeweb/pinner";
628
+
629
+ // Convert stream to blob
630
+ const blob = await streamToBlob(stream);
631
+
632
+ // Calculate stream size
633
+ const size = await calculateStreamSize(stream);
634
+
635
+ // Convert async generator to readable stream
636
+ const readableStream = asyncGeneratorToReadableStream(asyncGenerator);
637
+
638
+ // Convert readable stream to async iterable
639
+ const asyncIterable = readableStreamToAsyncIterable(readableStream);
640
+ ```
641
+
642
+ ## Testing
643
+
644
+ The library includes comprehensive tests:
645
+
646
+ ```bash
647
+ # Run all tests
648
+ pnpm test
649
+
650
+ # Run tests with coverage
651
+ pnpm coverage
652
+
653
+ # Run specific test project
654
+ vitest run --project node-upload-unit
655
+ vitest run --project browser-upload-integration
656
+ ```
657
+
658
+ ### Test Structure
659
+
660
+ - **Unit Tests**: Test individual components with mocks
661
+ - **Integration Tests**: Test real-world scenarios without mocks
662
+ - **Browser Tests**: Run tests in Chromium using Playwright
663
+ - **Node Tests**: Run tests in Node.js environment
664
+
665
+ ## Build
666
+
667
+ ```bash
668
+ # Build the library
669
+ pnpm build
670
+
671
+ # Type checking
672
+ pnpm lint
673
+ ```
674
+
675
+ The library outputs:
676
+ - ESM: `dist/esm/`
677
+ - CJS: `dist/cjs/`
678
+ - Type definitions: `dist/esm/*.d.ts`
679
+
680
+ ## Package Exports
681
+
682
+ ```typescript
683
+ // Main exports
684
+ import { Pinner } from "@lumeweb/pinner";
685
+
686
+ // Upload types and utilities
687
+ import {
688
+ UploadManager,
689
+ UploadResult,
690
+ UploadOptions,
691
+ UploadProgress
692
+ } from "@lumeweb/pinner";
693
+
694
+ // Pin types
695
+ import type {
696
+ RemotePins,
697
+ RemotePin,
698
+ RemoteAddOptions,
699
+ RemoteLsOptions
700
+ } from "@lumeweb/pinner";
701
+
702
+ // Blockstore
703
+ import {
704
+ createBlockstore,
705
+ createDatastore,
706
+ setDriverFactory
707
+ } from "@lumeweb/pinner";
708
+
709
+ // Adapters
710
+ import { pinataAdapter, pinataLegacyAdapter } from "@lumeweb/pinner";
711
+
712
+ // Blockstore module
713
+ import { UnstorageBlockstore } from "@lumeweb/pinner/blockstore";
714
+ ```
715
+
716
+ ## License
717
+
718
+ MIT
719
+
720
+ ## Contributing
721
+
722
+ Contributions are welcome! Please ensure all tests pass and follow the existing code style.
42
723
 
43
- ---
44
724
 
45
- **Maintained for OIDC setup purposes only**