@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.
- package/LICENSE +9 -0
- package/README.md +690 -28
- package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/cjs/adapters/pinata/adapter.cjs +88 -0
- package/dist/cjs/adapters/pinata/adapter.cjs.map +1 -0
- package/dist/cjs/adapters/pinata/adapter.d.cts +35 -0
- package/dist/cjs/adapters/pinata/builder.cjs +194 -0
- package/dist/cjs/adapters/pinata/builder.cjs.map +1 -0
- package/dist/cjs/adapters/pinata/index.cjs +3 -0
- package/dist/cjs/adapters/pinata/list-builder.cjs +52 -0
- package/dist/cjs/adapters/pinata/list-builder.cjs.map +1 -0
- package/dist/cjs/blockstore/index.cjs +2 -0
- package/dist/cjs/blockstore/unstorage-base.cjs +240 -0
- package/dist/cjs/blockstore/unstorage-base.cjs.map +1 -0
- package/dist/cjs/blockstore/unstorage-base.d.cts +23 -0
- package/dist/cjs/blockstore/unstorage.cjs +39 -0
- package/dist/cjs/blockstore/unstorage.cjs.map +1 -0
- package/dist/cjs/blockstore/unstorage.d.cts +36 -0
- package/dist/cjs/config.d.cts +51 -0
- package/dist/cjs/encoder/base64.cjs +38 -0
- package/dist/cjs/encoder/base64.cjs.map +1 -0
- package/dist/cjs/encoder/csv/csv-formatter.cjs +81 -0
- package/dist/cjs/encoder/csv/csv-formatter.cjs.map +1 -0
- package/dist/cjs/encoder/csv/field-formatter.cjs +76 -0
- package/dist/cjs/encoder/csv/field-formatter.cjs.map +1 -0
- package/dist/cjs/encoder/csv/row-formatter.cjs +159 -0
- package/dist/cjs/encoder/csv/row-formatter.cjs.map +1 -0
- package/dist/cjs/encoder/csv.cjs +44 -0
- package/dist/cjs/encoder/csv.cjs.map +1 -0
- package/dist/cjs/encoder/error.cjs +19 -0
- package/dist/cjs/encoder/error.cjs.map +1 -0
- package/dist/cjs/encoder/index.cjs +6 -0
- package/dist/cjs/encoder/json.cjs +36 -0
- package/dist/cjs/encoder/json.cjs.map +1 -0
- package/dist/cjs/encoder/text.cjs +35 -0
- package/dist/cjs/encoder/text.cjs.map +1 -0
- package/dist/cjs/encoder/url.cjs +39 -0
- package/dist/cjs/encoder/url.cjs.map +1 -0
- package/dist/cjs/errors/index.cjs +104 -0
- package/dist/cjs/errors/index.cjs.map +1 -0
- package/dist/cjs/errors/index.d.cts +47 -0
- package/dist/cjs/index.cjs +42 -0
- package/dist/cjs/index.d.cts +14 -0
- package/dist/cjs/pin/client.cjs +96 -0
- package/dist/cjs/pin/client.cjs.map +1 -0
- package/dist/cjs/pin/index.cjs +1 -0
- package/dist/cjs/pinner.cjs +126 -0
- package/dist/cjs/pinner.cjs.map +1 -0
- package/dist/cjs/pinner.d.cts +77 -0
- package/dist/cjs/types/constants.cjs +34 -0
- package/dist/cjs/types/constants.cjs.map +1 -0
- package/dist/cjs/types/mime-types.cjs +11 -0
- package/dist/cjs/types/mime-types.cjs.map +1 -0
- package/dist/cjs/types/mime-types.d.cts +7 -0
- package/dist/cjs/types/pin.d.cts +74 -0
- package/dist/cjs/types/pinata.d.cts +99 -0
- package/dist/cjs/types/type-guards.cjs +20 -0
- package/dist/cjs/types/type-guards.cjs.map +1 -0
- package/dist/cjs/types/type-guards.d.cts +15 -0
- package/dist/cjs/types/upload.cjs +18 -0
- package/dist/cjs/types/upload.cjs.map +1 -0
- package/dist/cjs/types/upload.d.cts +189 -0
- package/dist/cjs/upload/base-upload.cjs +135 -0
- package/dist/cjs/upload/base-upload.cjs.map +1 -0
- package/dist/cjs/upload/builder.cjs +174 -0
- package/dist/cjs/upload/builder.cjs.map +1 -0
- package/dist/cjs/upload/builder.d.cts +60 -0
- package/dist/cjs/upload/car.cjs +129 -0
- package/dist/cjs/upload/car.cjs.map +1 -0
- package/dist/cjs/upload/car.d.cts +19 -0
- package/dist/cjs/upload/constants.cjs +9 -0
- package/dist/cjs/upload/constants.cjs.map +1 -0
- package/dist/cjs/upload/index.cjs +8 -0
- package/dist/cjs/upload/manager.cjs +249 -0
- package/dist/cjs/upload/manager.cjs.map +1 -0
- package/dist/cjs/upload/manager.d.cts +35 -0
- package/dist/cjs/upload/normalize.cjs +28 -0
- package/dist/cjs/upload/normalize.cjs.map +1 -0
- package/dist/cjs/upload/tus-upload.cjs +74 -0
- package/dist/cjs/upload/tus-upload.cjs.map +1 -0
- package/dist/cjs/upload/xhr-upload.cjs +41 -0
- package/dist/cjs/upload/xhr-upload.cjs.map +1 -0
- package/dist/cjs/utils/env.cjs +12 -0
- package/dist/cjs/utils/env.cjs.map +1 -0
- package/dist/cjs/utils/stream.cjs +141 -0
- package/dist/cjs/utils/stream.cjs.map +1 -0
- package/dist/cjs/utils/stream.d.cts +23 -0
- package/dist/cjs/utils/tus-patch.cjs +50 -0
- package/dist/cjs/utils/tus-patch.cjs.map +1 -0
- package/dist/cjs/utils/validation.cjs +62 -0
- package/dist/cjs/utils/validation.cjs.map +1 -0
- package/dist/esm/_virtual/rolldown_runtime.js +8 -0
- package/dist/esm/adapters/pinata/adapter.d.ts +35 -0
- package/dist/esm/adapters/pinata/adapter.js +87 -0
- package/dist/esm/adapters/pinata/adapter.js.map +1 -0
- package/dist/esm/adapters/pinata/builder.d.ts +1 -0
- package/dist/esm/adapters/pinata/builder.js +187 -0
- package/dist/esm/adapters/pinata/builder.js.map +1 -0
- package/dist/esm/adapters/pinata/index.d.ts +4 -0
- package/dist/esm/adapters/pinata/index.js +3 -0
- package/dist/esm/adapters/pinata/list-builder.d.ts +1 -0
- package/dist/esm/adapters/pinata/list-builder.js +51 -0
- package/dist/esm/adapters/pinata/list-builder.js.map +1 -0
- package/dist/esm/blockstore/index.d.ts +2 -0
- package/dist/esm/blockstore/index.js +2 -0
- package/dist/esm/blockstore/unstorage-base.d.ts +23 -0
- package/dist/esm/blockstore/unstorage-base.js +231 -0
- package/dist/esm/blockstore/unstorage-base.js.map +1 -0
- package/dist/esm/blockstore/unstorage.d.ts +36 -0
- package/dist/esm/blockstore/unstorage.js +38 -0
- package/dist/esm/blockstore/unstorage.js.map +1 -0
- package/dist/esm/config.d.ts +51 -0
- package/dist/esm/encoder/base64.js +37 -0
- package/dist/esm/encoder/base64.js.map +1 -0
- package/dist/esm/encoder/csv/csv-formatter.js +81 -0
- package/dist/esm/encoder/csv/csv-formatter.js.map +1 -0
- package/dist/esm/encoder/csv/field-formatter.js +75 -0
- package/dist/esm/encoder/csv/field-formatter.js.map +1 -0
- package/dist/esm/encoder/csv/row-formatter.js +159 -0
- package/dist/esm/encoder/csv/row-formatter.js.map +1 -0
- package/dist/esm/encoder/csv.js +43 -0
- package/dist/esm/encoder/csv.js.map +1 -0
- package/dist/esm/encoder/error.js +18 -0
- package/dist/esm/encoder/error.js.map +1 -0
- package/dist/esm/encoder/index.js +6 -0
- package/dist/esm/encoder/json.js +35 -0
- package/dist/esm/encoder/json.js.map +1 -0
- package/dist/esm/encoder/text.js +34 -0
- package/dist/esm/encoder/text.js.map +1 -0
- package/dist/esm/encoder/url.js +36 -0
- package/dist/esm/encoder/url.js.map +1 -0
- package/dist/esm/errors/index.d.ts +47 -0
- package/dist/esm/errors/index.js +93 -0
- package/dist/esm/errors/index.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/pin/client.js +95 -0
- package/dist/esm/pin/client.js.map +1 -0
- package/dist/esm/pin/index.js +1 -0
- package/dist/esm/pinner.d.ts +77 -0
- package/dist/esm/pinner.js +125 -0
- package/dist/esm/pinner.js.map +1 -0
- package/dist/esm/types/constants.js +29 -0
- package/dist/esm/types/constants.js.map +1 -0
- package/dist/esm/types/mime-types.d.ts +7 -0
- package/dist/esm/types/mime-types.js +8 -0
- package/dist/esm/types/mime-types.js.map +1 -0
- package/dist/esm/types/pin.d.ts +74 -0
- package/dist/esm/types/pinata.d.ts +99 -0
- package/dist/esm/types/type-guards.d.ts +15 -0
- package/dist/esm/types/type-guards.js +19 -0
- package/dist/esm/types/type-guards.js.map +1 -0
- package/dist/esm/types/upload.d.ts +189 -0
- package/dist/esm/types/upload.js +16 -0
- package/dist/esm/types/upload.js.map +1 -0
- package/dist/esm/upload/base-upload.js +132 -0
- package/dist/esm/upload/base-upload.js.map +1 -0
- package/dist/esm/upload/builder.d.ts +60 -0
- package/dist/esm/upload/builder.js +173 -0
- package/dist/esm/upload/builder.js.map +1 -0
- package/dist/esm/upload/car.d.ts +19 -0
- package/dist/esm/upload/car.js +125 -0
- package/dist/esm/upload/car.js.map +1 -0
- package/dist/esm/upload/constants.js +7 -0
- package/dist/esm/upload/constants.js.map +1 -0
- package/dist/esm/upload/index.js +8 -0
- package/dist/esm/upload/manager.d.ts +35 -0
- package/dist/esm/upload/manager.js +248 -0
- package/dist/esm/upload/manager.js.map +1 -0
- package/dist/esm/upload/normalize.js +28 -0
- package/dist/esm/upload/normalize.js.map +1 -0
- package/dist/esm/upload/tus-upload.js +72 -0
- package/dist/esm/upload/tus-upload.js.map +1 -0
- package/dist/esm/upload/xhr-upload.js +39 -0
- package/dist/esm/upload/xhr-upload.js.map +1 -0
- package/dist/esm/utils/env.js +11 -0
- package/dist/esm/utils/env.js.map +1 -0
- package/dist/esm/utils/stream.d.ts +23 -0
- package/dist/esm/utils/stream.js +134 -0
- package/dist/esm/utils/stream.js.map +1 -0
- package/dist/esm/utils/tus-patch.js +51 -0
- package/dist/esm/utils/tus-patch.js.map +1 -0
- package/dist/esm/utils/validation.js +60 -0
- package/dist/esm/utils/validation.js.map +1 -0
- package/package.json +95 -8
- package/public/mockServiceWorker.js +349 -0
package/README.md
CHANGED
|
@@ -1,45 +1,707 @@
|
|
|
1
1
|
# @lumeweb/pinner
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
##
|
|
17
|
+
## Installation
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @lumeweb/pinner
|
|
21
|
+
```
|
|
15
22
|
|
|
16
|
-
##
|
|
23
|
+
## Quick Start
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
```typescript
|
|
26
|
+
import { Pinner } from "@lumeweb/pinner";
|
|
19
27
|
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
44
|
+
// List pins
|
|
45
|
+
const pins = await pinner.listPins();
|
|
46
|
+
console.log("Pinned content:", pins);
|
|
47
|
+
```
|
|
30
48
|
|
|
31
|
-
|
|
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
|
-
|
|
51
|
+
The `Pinner` class accepts a `PinnerConfig` object:
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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**
|