tangled
alpha
login
or
join now
atscan.net
/
plcbundle-ref
5
fork
atom
PLC Bundle V1 Example Implementations
5
fork
atom
overview
issues
pulls
pipelines
fist commit
tree.fail
4 months ago
6f4d1ee4
+643
4 changed files
expand all
collapse all
unified
split
.gitignore
package.json
plcbundle.ts
pnpm-lock.yaml
+3
.gitignore
···
1
1
+
node_modules
2
2
+
.DS_Store
3
3
+
plc_bundles
+17
package.json
···
1
1
+
{
2
2
+
"name": "plcbundle-js",
3
3
+
"version": "1.0.0",
4
4
+
"description": "",
5
5
+
"main": "index.js",
6
6
+
"type": "module",
7
7
+
"scripts": {
8
8
+
"test": "echo \"Error: no test specified\" && exit 1"
9
9
+
},
10
10
+
"keywords": [],
11
11
+
"author": "",
12
12
+
"license": "ISC",
13
13
+
"dependencies": {
14
14
+
"@bokuweb/zstd-wasm": "^0.0.27",
15
15
+
"axios": "^1.13.0"
16
16
+
}
17
17
+
}
+411
plcbundle.ts
···
1
1
+
#!/usr/bin/env node
2
2
+
3
3
+
/**
4
4
+
* plcbundle.ts - Fetch from PLC Directory and create verifiable bundles
5
5
+
*/
6
6
+
7
7
+
import fs from 'fs/promises';
8
8
+
import path from 'path';
9
9
+
import crypto from 'crypto';
10
10
+
import { fileURLToPath } from 'url';
11
11
+
import { init, compress, decompress } from '@bokuweb/zstd-wasm';
12
12
+
import axios from 'axios';
13
13
+
14
14
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
15
+
16
16
+
const BUNDLE_SIZE = 10000;
17
17
+
const INDEX_FILE = 'plc_bundles.json';
18
18
+
const PLC_URL = 'https://plc.directory';
19
19
+
20
20
+
// ============================================================================
21
21
+
// Types
22
22
+
// ============================================================================
23
23
+
24
24
+
interface PLCOperation {
25
25
+
did: string;
26
26
+
cid: string;
27
27
+
createdAt: string;
28
28
+
operation: Record<string, any>;
29
29
+
nullified?: boolean | string;
30
30
+
_raw?: string;
31
31
+
}
32
32
+
33
33
+
interface BundleMetadata {
34
34
+
bundle_number: number;
35
35
+
start_time: string;
36
36
+
end_time: string;
37
37
+
operation_count: number;
38
38
+
did_count: number;
39
39
+
hash: string;
40
40
+
content_hash: string;
41
41
+
parent: string;
42
42
+
compressed_hash: string;
43
43
+
compressed_size: number;
44
44
+
uncompressed_size: number;
45
45
+
created_at: string;
46
46
+
}
47
47
+
48
48
+
interface Index {
49
49
+
version: string;
50
50
+
last_bundle: number;
51
51
+
updated_at: string;
52
52
+
total_size_bytes: number;
53
53
+
bundles: BundleMetadata[];
54
54
+
}
55
55
+
56
56
+
// Initialize zstd
57
57
+
await init();
58
58
+
59
59
+
// ============================================================================
60
60
+
// Index Management
61
61
+
// ============================================================================
62
62
+
63
63
+
const loadIndex = async (dir: string): Promise<Index> => {
64
64
+
try {
65
65
+
const data = await fs.readFile(path.join(dir, INDEX_FILE), 'utf8');
66
66
+
return JSON.parse(data);
67
67
+
} catch (err) {
68
68
+
return {
69
69
+
version: '1.0',
70
70
+
last_bundle: 0,
71
71
+
updated_at: new Date().toISOString(),
72
72
+
total_size_bytes: 0,
73
73
+
bundles: []
74
74
+
};
75
75
+
}
76
76
+
};
77
77
+
78
78
+
const saveIndex = async (dir: string, index: Index): Promise<void> => {
79
79
+
index.updated_at = new Date().toISOString();
80
80
+
const indexPath = path.join(dir, INDEX_FILE);
81
81
+
const tempPath = indexPath + '.tmp';
82
82
+
await fs.writeFile(tempPath, JSON.stringify(index, null, 2));
83
83
+
await fs.rename(tempPath, indexPath);
84
84
+
};
85
85
+
86
86
+
// ============================================================================
87
87
+
// Bundle Loading
88
88
+
// ============================================================================
89
89
+
90
90
+
const loadBundle = async (dir: string, bundleNumber: number): Promise<PLCOperation[]> => {
91
91
+
const filename = `${String(bundleNumber).padStart(6, '0')}.jsonl.zst`;
92
92
+
const filepath = path.join(dir, filename);
93
93
+
94
94
+
const compressed = await fs.readFile(filepath);
95
95
+
const decompressed = decompress(compressed);
96
96
+
const jsonl = Buffer.from(decompressed).toString('utf8');
97
97
+
98
98
+
const lines = jsonl.trim().split('\n').filter(l => l);
99
99
+
return lines.map(line => {
100
100
+
const op = JSON.parse(line) as PLCOperation;
101
101
+
op._raw = line;
102
102
+
return op;
103
103
+
});
104
104
+
};
105
105
+
106
106
+
// ============================================================================
107
107
+
// Boundary Handling
108
108
+
// ============================================================================
109
109
+
110
110
+
const getBoundaryCIDs = (operations: PLCOperation[]): Set<string> => {
111
111
+
if (operations.length === 0) return new Set();
112
112
+
113
113
+
const lastOp = operations[operations.length - 1];
114
114
+
const boundaryTime = lastOp.createdAt;
115
115
+
const cidSet = new Set<string>();
116
116
+
117
117
+
// Walk backwards from the end to find all operations with the same timestamp
118
118
+
for (let i = operations.length - 1; i >= 0; i--) {
119
119
+
if (operations[i].createdAt === boundaryTime) {
120
120
+
cidSet.add(operations[i].cid);
121
121
+
} else {
122
122
+
break;
123
123
+
}
124
124
+
}
125
125
+
126
126
+
return cidSet;
127
127
+
};
128
128
+
129
129
+
const stripBoundaryDuplicates = (
130
130
+
operations: PLCOperation[],
131
131
+
prevBoundaryCIDs: Set<string>
132
132
+
): PLCOperation[] => {
133
133
+
if (prevBoundaryCIDs.size === 0) return operations;
134
134
+
if (operations.length === 0) return operations;
135
135
+
136
136
+
const boundaryTime = operations[0].createdAt;
137
137
+
let startIdx = 0;
138
138
+
139
139
+
// Skip operations that are in the previous bundle's boundary
140
140
+
for (let i = 0; i < operations.length; i++) {
141
141
+
const op = operations[i];
142
142
+
143
143
+
// Stop if we've moved past the boundary timestamp
144
144
+
if (op.createdAt > boundaryTime) {
145
145
+
break;
146
146
+
}
147
147
+
148
148
+
// Skip if this CID was in the previous boundary
149
149
+
if (op.createdAt === boundaryTime && prevBoundaryCIDs.has(op.cid)) {
150
150
+
startIdx = i + 1;
151
151
+
continue;
152
152
+
}
153
153
+
154
154
+
break;
155
155
+
}
156
156
+
157
157
+
const stripped = operations.slice(startIdx);
158
158
+
if (startIdx > 0) {
159
159
+
console.log(` Stripped ${startIdx} boundary duplicates`);
160
160
+
}
161
161
+
return stripped;
162
162
+
};
163
163
+
164
164
+
// ============================================================================
165
165
+
// PLC Directory Client
166
166
+
// ============================================================================
167
167
+
168
168
+
const fetchOperations = async (after: string | null, count: number = 1000): Promise<PLCOperation[]> => {
169
169
+
const params: Record<string, any> = { count };
170
170
+
if (after) {
171
171
+
params.after = after;
172
172
+
}
173
173
+
174
174
+
const response = await axios.get<string>(`${PLC_URL}/export`, {
175
175
+
params,
176
176
+
responseType: 'text'
177
177
+
});
178
178
+
179
179
+
const lines = response.data.trim().split('\n').filter(l => l);
180
180
+
181
181
+
return lines.map(line => {
182
182
+
const op = JSON.parse(line) as PLCOperation;
183
183
+
op._raw = line; // Preserve exact JSON
184
184
+
return op;
185
185
+
});
186
186
+
};
187
187
+
188
188
+
// ============================================================================
189
189
+
// Bundle Operations
190
190
+
// ============================================================================
191
191
+
192
192
+
const serializeJSONL = (operations: PLCOperation[]): string => {
193
193
+
const lines = operations.map(op => {
194
194
+
const json = op._raw || JSON.stringify(op);
195
195
+
return json + '\n';
196
196
+
});
197
197
+
return lines.join('');
198
198
+
};
199
199
+
200
200
+
const sha256 = (data: Buffer | string): string => {
201
201
+
return crypto.createHash('sha256').update(data).digest('hex');
202
202
+
};
203
203
+
204
204
+
const calculateChainHash = (parent: string, contentHash: string): string => {
205
205
+
let data: string;
206
206
+
if (!parent || parent === '') {
207
207
+
data = `plcbundle:genesis:${contentHash}`;
208
208
+
} else {
209
209
+
data = `${parent}:${contentHash}`;
210
210
+
}
211
211
+
return sha256(data);
212
212
+
};
213
213
+
214
214
+
const extractUniqueDIDs = (operations: PLCOperation[]): number => {
215
215
+
const dids = new Set<string>();
216
216
+
operations.forEach(op => dids.add(op.did));
217
217
+
return dids.size;
218
218
+
};
219
219
+
220
220
+
const saveBundle = async (
221
221
+
dir: string,
222
222
+
bundleNumber: number,
223
223
+
operations: PLCOperation[],
224
224
+
parentHash: string
225
225
+
): Promise<BundleMetadata> => {
226
226
+
const filename = `${String(bundleNumber).padStart(6, '0')}.jsonl.zst`;
227
227
+
const filepath = path.join(dir, filename);
228
228
+
229
229
+
const jsonl = serializeJSONL(operations);
230
230
+
const uncompressedBuffer = Buffer.from(jsonl, 'utf8');
231
231
+
232
232
+
const contentHash = sha256(uncompressedBuffer);
233
233
+
const uncompressedSize = uncompressedBuffer.length;
234
234
+
235
235
+
const chainHash = calculateChainHash(parentHash, contentHash);
236
236
+
237
237
+
const compressed = compress(uncompressedBuffer, 3);
238
238
+
const compressedBuffer = Buffer.from(compressed);
239
239
+
const compressedHash = sha256(compressedBuffer);
240
240
+
const compressedSize = compressedBuffer.length;
241
241
+
242
242
+
await fs.writeFile(filepath, compressedBuffer);
243
243
+
244
244
+
const startTime = operations[0].createdAt;
245
245
+
const endTime = operations[operations.length - 1].createdAt;
246
246
+
const didCount = extractUniqueDIDs(operations);
247
247
+
248
248
+
return {
249
249
+
bundle_number: bundleNumber,
250
250
+
start_time: startTime,
251
251
+
end_time: endTime,
252
252
+
operation_count: operations.length,
253
253
+
did_count: didCount,
254
254
+
hash: chainHash,
255
255
+
content_hash: contentHash,
256
256
+
parent: parentHash || '',
257
257
+
compressed_hash: compressedHash,
258
258
+
compressed_size: compressedSize,
259
259
+
uncompressed_size: uncompressedSize,
260
260
+
created_at: new Date().toISOString()
261
261
+
};
262
262
+
};
263
263
+
264
264
+
// ============================================================================
265
265
+
// Main Logic
266
266
+
// ============================================================================
267
267
+
268
268
+
const run = async (): Promise<void> => {
269
269
+
const dir = process.argv[2] || './plc_bundles';
270
270
+
271
271
+
console.log('PLC Bundle Fetcher');
272
272
+
console.log('==================');
273
273
+
console.log();
274
274
+
console.log(`Directory: ${dir}`);
275
275
+
console.log(`Source: ${PLC_URL}`);
276
276
+
console.log();
277
277
+
278
278
+
await fs.mkdir(dir, { recursive: true });
279
279
+
280
280
+
const index = await loadIndex(dir);
281
281
+
282
282
+
let currentBundle = index.last_bundle + 1;
283
283
+
let cursor: string | null = null;
284
284
+
let parentHash = '';
285
285
+
let prevBoundaryCIDs = new Set<string>();
286
286
+
287
287
+
if (index.bundles.length > 0) {
288
288
+
const lastBundle = index.bundles[index.bundles.length - 1];
289
289
+
cursor = lastBundle.end_time;
290
290
+
parentHash = lastBundle.hash;
291
291
+
292
292
+
try {
293
293
+
const prevOps = await loadBundle(dir, lastBundle.bundle_number);
294
294
+
prevBoundaryCIDs = getBoundaryCIDs(prevOps);
295
295
+
console.log(`Loaded previous bundle boundary: ${prevBoundaryCIDs.size} CIDs`);
296
296
+
} catch (err) {
297
297
+
console.log(`Could not load previous bundle for boundary detection`);
298
298
+
}
299
299
+
300
300
+
console.log(`Resuming from bundle ${currentBundle}`);
301
301
+
console.log(`Last operation: ${cursor}`);
302
302
+
} else {
303
303
+
console.log('Starting from the beginning (genesis)');
304
304
+
}
305
305
+
306
306
+
console.log();
307
307
+
308
308
+
let mempool: PLCOperation[] = [];
309
309
+
const seenCIDs = new Set<string>(prevBoundaryCIDs);
310
310
+
let totalFetched = 0;
311
311
+
let totalBundles = 0;
312
312
+
313
313
+
while (true) {
314
314
+
try {
315
315
+
console.log(`Fetching operations (cursor: ${cursor || 'start'})...`);
316
316
+
const operations = await fetchOperations(cursor, 1000);
317
317
+
318
318
+
if (operations.length === 0) {
319
319
+
console.log('No more operations available');
320
320
+
break;
321
321
+
}
322
322
+
323
323
+
// Deduplicate
324
324
+
const uniqueOps = operations.filter(op => {
325
325
+
if (seenCIDs.has(op.cid)) {
326
326
+
return false;
327
327
+
}
328
328
+
seenCIDs.add(op.cid);
329
329
+
return true;
330
330
+
});
331
331
+
332
332
+
console.log(` Fetched ${operations.length} operations (${uniqueOps.length} unique)`);
333
333
+
totalFetched += uniqueOps.length;
334
334
+
335
335
+
mempool.push(...uniqueOps);
336
336
+
cursor = operations[operations.length - 1].createdAt;
337
337
+
338
338
+
while (mempool.length >= BUNDLE_SIZE) {
339
339
+
const bundleOps = mempool.splice(0, BUNDLE_SIZE);
340
340
+
341
341
+
console.log(`\nCreating bundle ${String(currentBundle).padStart(6, '0')}...`);
342
342
+
343
343
+
const metadata = await saveBundle(dir, currentBundle, bundleOps, parentHash);
344
344
+
345
345
+
index.bundles.push(metadata);
346
346
+
index.last_bundle = currentBundle;
347
347
+
index.total_size_bytes += metadata.compressed_size;
348
348
+
349
349
+
await saveIndex(dir, index);
350
350
+
351
351
+
console.log(` ✓ Bundle ${String(currentBundle).padStart(6, '0')}: ${metadata.operation_count} ops, ${metadata.did_count} DIDs`);
352
352
+
console.log(` Chain Hash: ${metadata.hash}`);
353
353
+
console.log(` Content Hash: ${metadata.content_hash}`);
354
354
+
console.log(` Size: ${(metadata.compressed_size / 1024).toFixed(1)} KB`);
355
355
+
356
356
+
// Get boundary CIDs for next bundle
357
357
+
prevBoundaryCIDs = getBoundaryCIDs(bundleOps);
358
358
+
console.log(` Boundary CIDs: ${prevBoundaryCIDs.size}`);
359
359
+
console.log();
360
360
+
361
361
+
parentHash = metadata.hash;
362
362
+
currentBundle++;
363
363
+
totalBundles++;
364
364
+
}
365
365
+
366
366
+
await new Promise(resolve => setTimeout(resolve, 100));
367
367
+
368
368
+
} catch (err: any) {
369
369
+
console.error(`Error: ${err.message}`);
370
370
+
371
371
+
if (err.response) {
372
372
+
console.error(`HTTP Status: ${err.response.status}`);
373
373
+
}
374
374
+
375
375
+
if (err.code === 'ECONNRESET' || err.code === 'ECONNABORTED') {
376
376
+
console.log('Connection error, retrying in 5 seconds...');
377
377
+
await new Promise(resolve => setTimeout(resolve, 5000));
378
378
+
continue;
379
379
+
}
380
380
+
381
381
+
break;
382
382
+
}
383
383
+
}
384
384
+
385
385
+
await saveIndex(dir, index);
386
386
+
387
387
+
console.log();
388
388
+
console.log('================');
389
389
+
console.log('Complete!');
390
390
+
console.log('================');
391
391
+
console.log(`Total operations fetched: ${totalFetched}`);
392
392
+
console.log(`Bundles created: ${totalBundles}`);
393
393
+
console.log(`Total bundles: ${index.bundles.length}`);
394
394
+
console.log(`Mempool: ${mempool.length} operations`);
395
395
+
console.log(`Total size: ${(index.total_size_bytes / 1024 / 1024).toFixed(1)} MB`);
396
396
+
397
397
+
if (mempool.length > 0) {
398
398
+
console.log();
399
399
+
console.log(`Note: ${mempool.length} operations in mempool`);
400
400
+
}
401
401
+
};
402
402
+
403
403
+
// ============================================================================
404
404
+
// Entry Point
405
405
+
// ============================================================================
406
406
+
407
407
+
run().catch(err => {
408
408
+
console.error('Fatal error:', err.message);
409
409
+
console.error(err.stack);
410
410
+
process.exit(1);
411
411
+
});
+212
pnpm-lock.yaml
···
1
1
+
lockfileVersion: '9.0'
2
2
+
3
3
+
settings:
4
4
+
autoInstallPeers: true
5
5
+
excludeLinksFromLockfile: false
6
6
+
7
7
+
importers:
8
8
+
9
9
+
.:
10
10
+
dependencies:
11
11
+
'@bokuweb/zstd-wasm':
12
12
+
specifier: ^0.0.27
13
13
+
version: 0.0.27
14
14
+
axios:
15
15
+
specifier: ^1.13.0
16
16
+
version: 1.13.0
17
17
+
18
18
+
packages:
19
19
+
20
20
+
'@bokuweb/zstd-wasm@0.0.27':
21
21
+
resolution: {integrity: sha512-GDm2uOTK3ESjnYmSeLQifJnBsRCWajKLvN32D2ZcQaaCIJI/Hse9s74f7APXjHit95S10UImsRGkTsbwHmrtmg==}
22
22
+
23
23
+
asynckit@0.4.0:
24
24
+
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
25
25
+
26
26
+
axios@1.13.0:
27
27
+
resolution: {integrity: sha512-zt40Pz4zcRXra9CVV31KeyofwiNvAbJ5B6YPz9pMJ+yOSLikvPT4Yi5LjfgjRa9CawVYBaD1JQzIVcIvBejKeA==}
28
28
+
29
29
+
call-bind-apply-helpers@1.0.2:
30
30
+
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
31
31
+
engines: {node: '>= 0.4'}
32
32
+
33
33
+
combined-stream@1.0.8:
34
34
+
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
35
35
+
engines: {node: '>= 0.8'}
36
36
+
37
37
+
delayed-stream@1.0.0:
38
38
+
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
39
39
+
engines: {node: '>=0.4.0'}
40
40
+
41
41
+
dunder-proto@1.0.1:
42
42
+
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
43
43
+
engines: {node: '>= 0.4'}
44
44
+
45
45
+
es-define-property@1.0.1:
46
46
+
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
47
47
+
engines: {node: '>= 0.4'}
48
48
+
49
49
+
es-errors@1.3.0:
50
50
+
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
51
51
+
engines: {node: '>= 0.4'}
52
52
+
53
53
+
es-object-atoms@1.1.1:
54
54
+
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
55
55
+
engines: {node: '>= 0.4'}
56
56
+
57
57
+
es-set-tostringtag@2.1.0:
58
58
+
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
59
59
+
engines: {node: '>= 0.4'}
60
60
+
61
61
+
follow-redirects@1.15.11:
62
62
+
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
63
63
+
engines: {node: '>=4.0'}
64
64
+
peerDependencies:
65
65
+
debug: '*'
66
66
+
peerDependenciesMeta:
67
67
+
debug:
68
68
+
optional: true
69
69
+
70
70
+
form-data@4.0.4:
71
71
+
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
72
72
+
engines: {node: '>= 6'}
73
73
+
74
74
+
function-bind@1.1.2:
75
75
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
76
76
+
77
77
+
get-intrinsic@1.3.0:
78
78
+
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
79
79
+
engines: {node: '>= 0.4'}
80
80
+
81
81
+
get-proto@1.0.1:
82
82
+
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
83
83
+
engines: {node: '>= 0.4'}
84
84
+
85
85
+
gopd@1.2.0:
86
86
+
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
87
87
+
engines: {node: '>= 0.4'}
88
88
+
89
89
+
has-symbols@1.1.0:
90
90
+
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
91
91
+
engines: {node: '>= 0.4'}
92
92
+
93
93
+
has-tostringtag@1.0.2:
94
94
+
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
95
95
+
engines: {node: '>= 0.4'}
96
96
+
97
97
+
hasown@2.0.2:
98
98
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
99
99
+
engines: {node: '>= 0.4'}
100
100
+
101
101
+
math-intrinsics@1.1.0:
102
102
+
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
103
103
+
engines: {node: '>= 0.4'}
104
104
+
105
105
+
mime-db@1.52.0:
106
106
+
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
107
107
+
engines: {node: '>= 0.6'}
108
108
+
109
109
+
mime-types@2.1.35:
110
110
+
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
111
111
+
engines: {node: '>= 0.6'}
112
112
+
113
113
+
proxy-from-env@1.1.0:
114
114
+
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
115
115
+
116
116
+
snapshots:
117
117
+
118
118
+
'@bokuweb/zstd-wasm@0.0.27': {}
119
119
+
120
120
+
asynckit@0.4.0: {}
121
121
+
122
122
+
axios@1.13.0:
123
123
+
dependencies:
124
124
+
follow-redirects: 1.15.11
125
125
+
form-data: 4.0.4
126
126
+
proxy-from-env: 1.1.0
127
127
+
transitivePeerDependencies:
128
128
+
- debug
129
129
+
130
130
+
call-bind-apply-helpers@1.0.2:
131
131
+
dependencies:
132
132
+
es-errors: 1.3.0
133
133
+
function-bind: 1.1.2
134
134
+
135
135
+
combined-stream@1.0.8:
136
136
+
dependencies:
137
137
+
delayed-stream: 1.0.0
138
138
+
139
139
+
delayed-stream@1.0.0: {}
140
140
+
141
141
+
dunder-proto@1.0.1:
142
142
+
dependencies:
143
143
+
call-bind-apply-helpers: 1.0.2
144
144
+
es-errors: 1.3.0
145
145
+
gopd: 1.2.0
146
146
+
147
147
+
es-define-property@1.0.1: {}
148
148
+
149
149
+
es-errors@1.3.0: {}
150
150
+
151
151
+
es-object-atoms@1.1.1:
152
152
+
dependencies:
153
153
+
es-errors: 1.3.0
154
154
+
155
155
+
es-set-tostringtag@2.1.0:
156
156
+
dependencies:
157
157
+
es-errors: 1.3.0
158
158
+
get-intrinsic: 1.3.0
159
159
+
has-tostringtag: 1.0.2
160
160
+
hasown: 2.0.2
161
161
+
162
162
+
follow-redirects@1.15.11: {}
163
163
+
164
164
+
form-data@4.0.4:
165
165
+
dependencies:
166
166
+
asynckit: 0.4.0
167
167
+
combined-stream: 1.0.8
168
168
+
es-set-tostringtag: 2.1.0
169
169
+
hasown: 2.0.2
170
170
+
mime-types: 2.1.35
171
171
+
172
172
+
function-bind@1.1.2: {}
173
173
+
174
174
+
get-intrinsic@1.3.0:
175
175
+
dependencies:
176
176
+
call-bind-apply-helpers: 1.0.2
177
177
+
es-define-property: 1.0.1
178
178
+
es-errors: 1.3.0
179
179
+
es-object-atoms: 1.1.1
180
180
+
function-bind: 1.1.2
181
181
+
get-proto: 1.0.1
182
182
+
gopd: 1.2.0
183
183
+
has-symbols: 1.1.0
184
184
+
hasown: 2.0.2
185
185
+
math-intrinsics: 1.1.0
186
186
+
187
187
+
get-proto@1.0.1:
188
188
+
dependencies:
189
189
+
dunder-proto: 1.0.1
190
190
+
es-object-atoms: 1.1.1
191
191
+
192
192
+
gopd@1.2.0: {}
193
193
+
194
194
+
has-symbols@1.1.0: {}
195
195
+
196
196
+
has-tostringtag@1.0.2:
197
197
+
dependencies:
198
198
+
has-symbols: 1.1.0
199
199
+
200
200
+
hasown@2.0.2:
201
201
+
dependencies:
202
202
+
function-bind: 1.1.2
203
203
+
204
204
+
math-intrinsics@1.1.0: {}
205
205
+
206
206
+
mime-db@1.52.0: {}
207
207
+
208
208
+
mime-types@2.1.35:
209
209
+
dependencies:
210
210
+
mime-db: 1.52.0
211
211
+
212
212
+
proxy-from-env@1.1.0: {}