diff --git a/README.md b/README.md index 4f05fc8..486166c 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,10 @@ A lightweight IPNI node mapping Filecoin PieceCID → payload block CID. - [Design doc](./docs/design.md) + +## Development + +```bash +docker run --name redis -p 6379:6379 -d redis +npm start -w indexer +``` diff --git a/indexer/bin/piece-indexer.js b/indexer/bin/piece-indexer.js new file mode 100644 index 0000000..c1fb86a --- /dev/null +++ b/indexer/bin/piece-indexer.js @@ -0,0 +1,17 @@ +import { Redis } from 'ioredis' + +const { + REDIS_URL: redisUrl = 'redis://localhost:6379' +} = process.env + +const redisUrlParsed = new URL(redisUrl) +const redis = new Redis({ + host: redisUrlParsed.hostname, + port: Number(redisUrlParsed.port), + username: redisUrlParsed.username, + password: redisUrlParsed.password, + lazyConnect: true, // call connect() explicitly so that we can exit on connection error + family: 6 // required for upstash +}) + +await redis.connect() diff --git a/indexer/lib/http-assertions.js b/indexer/lib/http-assertions.js new file mode 100644 index 0000000..4c4d8b6 --- /dev/null +++ b/indexer/lib/http-assertions.js @@ -0,0 +1,18 @@ +/** + * @param {Response} res + * @param {string} [errorMsg] + */ +export async function assertOkResponse (res, errorMsg) { + if (res.ok) return + + let body + try { + body = await res.text() + } catch {} + const err = new Error(`${errorMsg ?? `Cannot fetch ${res.url}`} (${res.status}): ${body?.trimEnd()}`) + Object.assign(err, { + statusCode: res.status, + serverMessage: body + }) + throw err +} diff --git a/indexer/lib/observer.js b/indexer/lib/observer.js new file mode 100644 index 0000000..57ae27a --- /dev/null +++ b/indexer/lib/observer.js @@ -0,0 +1,88 @@ +import createDebug from 'debug' +import { multiaddrToUri } from '@multiformats/multiaddr-to-uri' +import { assertOkResponse } from './http-assertions.js' + +const debug = createDebug('spark-piece-indexer:observer') + +/** @import { Repository, IpniProviderInfo } from './typings.js' */ + +/** + * @returns {Promise} + */ +export async function getProvidersWithMetadata () { + const res = await fetch('https://cid.contact/providers') + assertOkResponse(res) + + const providers = /** @type {{ + AddrInfo: { + ID: string; + Addrs: string[]; + }, + LastAdvertisement: { + "/": string; + }, + LastAdvertisementTime: string; + Publisher: { + ID: string; + Addrs: string[]; + }, + // Ignored: ExtendedProviders, FrozenAt + * }[]} + */(await res.json()) + + return providers.map(p => { + const providerId = p.Publisher.ID + const lastAdvertisementCID = p.LastAdvertisement['/'] + + // FIXME: handle empty Addrs[] + let providerAddress = p.Publisher.Addrs[0] + try { + providerAddress = multiaddrToUri(providerAddress) + } catch (err) { + debug('Cannot convert address to URI (provider: %s): %s', providerId, err) + } + + return { providerId, providerAddress, lastAdvertisementCID } + }) +} + +/** + * @param {Repository} repository + * @param {IpniProviderInfo[]} ipniProviders + */ +export async function updateProviderStateFromIPNI (repository, ipniProviders) { + const providersWithState = await repository.getProvidersWithState() + + for (const { providerId, providerAddress, lastAdvertisementCID } of ipniProviders) { + const status = providersWithState.get(providerId) + if (!status) { + const status = { + providerAddress, + lastHead: lastAdvertisementCID, + nextHead: lastAdvertisementCID, + head: lastAdvertisementCID, + tail: lastAdvertisementCID, + status: 'advertisement walk not started yet' + } + providersWithState.set(providerId, status) + debug('Initializing status for provider %s: %o', providerId, status) + continue + } + + let updated = false + if (providerAddress !== status.providerAddress) { + debug('Updating provider address from %s to %s', status.providerAddress, providerAddress) + status.providerAddress = providerAddress + updated = true + } + + // TODO: update the status + + if (!updated) { + debug('No changes for provider %s', providerId) + providersWithState.delete(providerId) + } + } + + await repository.updateProvidersWithState(providersWithState) +} diff --git a/indexer/lib/redis-repository.js b/indexer/lib/redis-repository.js new file mode 100644 index 0000000..eb668cd --- /dev/null +++ b/indexer/lib/redis-repository.js @@ -0,0 +1,40 @@ +/** @import { Repository, ProvidersWithState} from "./typings.js" */ + +/** @implements {Repository} */ +export class RedisRepository { + #redis + + /** + * @param {import('ioredis').Redis} redis + */ + constructor (redis) { + this.#redis = redis + } + + /** + * @returns {Promise} + */ + async getProvidersWithState () { + /** @type {string[]} */ + const providerIds = [] + const keyStream = this.#redis.scanStream({ + match: 'provider-state:*', + count: 1000 + }) + for await (const key of keyStream) { + const [, id] = key.split(':') + providerIds.push(id) + } + + const rawStates = this.#redis. + // TODO + return new Map() + } + + /** + * @param {ProvidersWithState} updates + */ + async updateProvidersWithState (updates) { + throw new Error('Method not implemented.') + } +} diff --git a/indexer/lib/typings.d.ts b/indexer/lib/typings.d.ts new file mode 100644 index 0000000..e4b468d --- /dev/null +++ b/indexer/lib/typings.d.ts @@ -0,0 +1,28 @@ +export interface ProviderIndexingState { + providerAddress: string; + lastHead: string; + nextHead: string; + head: string; + tail: string; + status: string; +} + +export type PiecePayloadCIDs = string[]; + +// Mapping providerIds to the indexing state +export type ProvidersWithState = Map + +export interface Repository { + getProvidersWithState(): Promise; + updateProvidersWithState(updates: ProvidersWithState): Promise; + + // addPiecePayloadCID(provider: string, pieceCid: string, payloadCid: string): Promise; + // getPiecePayloadCIDs(provider: string, pieceCid: string): Promise; +} + +export interface IpniProviderInfo { + providerId: string; + providerAddress: string; + lastAdvertisementCID: string; +} + diff --git a/indexer/package.json b/indexer/package.json index bc8bb92..136d195 100644 --- a/indexer/package.json +++ b/indexer/package.json @@ -3,8 +3,6 @@ "type": "module", "private": true, "scripts": { - "dry-run": "node bin/dry-run.js", - "migrate": "node bin/migrate.js", "start": "node bin/piece-indexer.js", "lint": "standard", "test": "node --test" @@ -13,6 +11,7 @@ "standard": "^17.1.0" }, "dependencies": { + "@multiformats/multiaddr-to-uri": "^10.1.0", "debug": "^4.3.6", "ioredis": "^5.4.1" } diff --git a/indexer/test/observer.test.js b/indexer/test/observer.test.js new file mode 100644 index 0000000..1e0f198 --- /dev/null +++ b/indexer/test/observer.test.js @@ -0,0 +1,80 @@ +import createDebug from 'debug' +import { Redis } from 'ioredis' +import assert from 'node:assert' +import { after, before, beforeEach, describe, it } from 'node:test' +import { getProvidersWithMetadata, updateProviderStateFromIPNI } from '../lib/observer.js' +import { RedisRepository } from '../lib/redis-repository.js' + +const debug = createDebug('test') + +/** @import { ProvidersWithState, ProviderIndexingState } from '../lib/typings.js' */ + +// See https://github.com/filecoin-station/frisbii-on-fly +const FRISBII_ID = '12D3KooWC8gXxg9LoJ9h3hy3jzBkEAxamyHEQJKtRmAuBuvoMzpr' +const FRISBII_ADDRESS = 'https://frisbii.fly.dev' + +describe('getProvidersWithMetadata', () => { + it('returns response including known providers', async () => { + const providers = await getProvidersWithMetadata() + debug(JSON.stringify(providers, null, 2)) + + const frisbiiOnFly = providers.find( + p => p.providerId === FRISBII_ID && p.providerAddress === FRISBII_ADDRESS + ) + + assert(frisbiiOnFly) + assert.match(frisbiiOnFly.lastAdvertisementCID, /^bagu/) + }) +}) + +describe('updateProviderStateFromIPNI', () => { + /** @type {Redis} */ + let redis + + before(async () => { + redis = new Redis({ db: 1 }) + }) + + beforeEach(async () => { + await redis.flushall() + }) + + after(async () => { + await redis?.disconnect() + }) + + it('creates an initial state for a new provider', async () => { + const repository = new RedisRepository(redis) + await updateProviderStateFromIPNI(repository, [ + { + providerId: 'peer1', + providerAddress: 'https://example.com', + lastAdvertisementCID: 'bagu1' + } + ]) + + const state = await repository.getProvidersWithState() + assertStateEqual(state, { + peer1: { + providerAddress: 'https://example.com', + lastHead: 'tbd', + nextHead: 'tbd', + head: 'tbd', + tail: 'tbd', + status: 'tbd' + } + }) + }) +}) + +/** + * + * @param {ProvidersWithState} actualMap + * @param {Record} expectedObject + */ +function assertStateEqual (actualMap, expectedObject) { + assert.deepStrictEqual( + Object.fromEntries(actualMap.entries()), + expectedObject + ) +} diff --git a/package-lock.json b/package-lock.json index f084ad4..3ff4783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "indexer" ], "devDependencies": { + "@types/debug": "^4.1.12", + "@types/node": "^22.5.3", "prettier": "^3.3.2", "standard": "^17.1.0", "typescript": "^5.5.4" @@ -20,6 +22,7 @@ "indexer": { "name": "@filecoin-station/spark-piece-indexer", "dependencies": { + "@multiformats/multiaddr-to-uri": "^10.1.0", "debug": "^4.3.6", "ioredis": "^5.4.1" }, @@ -27,6 +30,19 @@ "standard": "^17.1.0" } }, + "node_modules/@chainsafe/is-ip": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@chainsafe/is-ip/-/is-ip-2.0.2.tgz", + "integrity": "sha512-ndGqEMG1W5WkGagaqOZHpPU172AGdxr+LD15sv3WIUvT5oCFUrG1Y0CW/v2Egwj4JXEvSibaIIIqImsm98y1nA==" + }, + "node_modules/@chainsafe/netmask": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@chainsafe/netmask/-/netmask-2.0.0.tgz", + "integrity": "sha512-I3Z+6SWUoaljh3TBzCnCxjlUyN8tA+NAk5L6m9IxvCf1BENQTePzPMis97CoN/iMW1St3WN+AWCCRp+TTBRiDg==", + "dependencies": { + "@chainsafe/is-ip": "^2.0.1" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -127,6 +143,60 @@ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" + }, + "node_modules/@libp2p/interface": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@libp2p/interface/-/interface-1.7.0.tgz", + "integrity": "sha512-/zFyaIaIGW0aihhsH7/93vQdpWInUzFocxF11RO/029Y6h0SVjs24HHbils+DqaFDTqN+L7oNlBx2rM2MnmTjA==", + "dependencies": { + "@multiformats/multiaddr": "^12.2.3", + "it-pushable": "^3.2.3", + "it-stream-types": "^2.0.1", + "multiformats": "^13.1.0", + "progress-events": "^1.0.0", + "uint8arraylist": "^2.4.8" + } + }, + "node_modules/@multiformats/dns": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@multiformats/dns/-/dns-1.0.6.tgz", + "integrity": "sha512-nt/5UqjMPtyvkG9BQYdJ4GfLK3nMqGpFZOzf4hAmIa0sJh2LlS9YKXZ4FgwBDsaHvzZqR/rUFIywIc7pkHNNuw==", + "dependencies": { + "@types/dns-packet": "^5.6.5", + "buffer": "^6.0.3", + "dns-packet": "^5.6.1", + "hashlru": "^2.3.0", + "p-queue": "^8.0.1", + "progress-events": "^1.0.0", + "uint8arrays": "^5.0.2" + } + }, + "node_modules/@multiformats/multiaddr": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@multiformats/multiaddr/-/multiaddr-12.3.0.tgz", + "integrity": "sha512-JQ8Gc/jgucqqvEaDTFN/AvxlYDHEE7lgEWLMYW7hKZkWggER+GvG/tVxUgUxIP8M0vFpvEHKKHE0lKzyMsgi8Q==", + "dependencies": { + "@chainsafe/is-ip": "^2.0.1", + "@chainsafe/netmask": "^2.0.0", + "@libp2p/interface": "^1.0.0", + "@multiformats/dns": "^1.0.3", + "multiformats": "^13.0.0", + "uint8-varint": "^2.0.1", + "uint8arrays": "^5.0.0" + } + }, + "node_modules/@multiformats/multiaddr-to-uri": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@multiformats/multiaddr-to-uri/-/multiaddr-to-uri-10.1.0.tgz", + "integrity": "sha512-ZNwSAx3ssBWwd4y0LKrOsq9xG7LBHboQxnUdSduNc2fTh/NS1UjA2slgUy6KHxH5k9S2DSus0iU2CoyJyN0/pg==", + "dependencies": { + "@multiformats/multiaddr": "^12.3.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -168,12 +238,43 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dns-packet": { + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/@types/dns-packet/-/dns-packet-5.6.5.tgz", + "integrity": "sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.5.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.3.tgz", + "integrity": "sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -418,6 +519,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -428,6 +548,29 @@ "concat-map": "0.0.1" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/builtins": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", @@ -654,6 +797,17 @@ "node": ">=0.10" } }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1322,6 +1476,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1653,6 +1812,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hashlru": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", + "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1665,6 +1829,25 @@ "node": ">= 0.4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2111,6 +2294,23 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/it-pushable": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/it-pushable/-/it-pushable-3.2.3.tgz", + "integrity": "sha512-gzYnXYK8Y5t5b/BnJUr7glfQLO4U5vyb05gPx/TyTw+4Bv1zM9gFk4YsOrnulWefMewlphCjKkakFvj1y99Tcg==", + "dependencies": { + "p-defer": "^4.0.0" + } + }, + "node_modules/it-stream-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/it-stream-types/-/it-stream-types-2.0.1.tgz", + "integrity": "sha512-6DmOs5r7ERDbvS4q8yLKENcj6Yecr7QQTqWApbZdfAUTEC947d+PEha7PCqhm//9oxaLYL7TWRekwhoXl2s6fg==", + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -2309,6 +2509,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multiformats": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.2.2.tgz", + "integrity": "sha512-RWI+nyf0q64vyOxL8LbKtjJMki0sogRL/8axvklNtiTM0iFCVtHwME9w6+0P1/v4dQvsIg8A45oT3ka1t/M/+A==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2452,6 +2657,17 @@ "node": ">= 0.8.0" } }, + "node_modules/p-defer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-4.0.1.tgz", + "integrity": "sha512-Mr5KC5efvAK5VUptYEIopP1bakB85k2IWXaRC0rsh1uwn1L6M0LVml8OIQ4Gudg4oyZakf7FmeRLkMMtZW1i5A==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2482,6 +2698,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.0.1.tgz", + "integrity": "sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.2.tgz", + "integrity": "sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2665,6 +2907,11 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/progress-events": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/progress-events/-/progress-events-1.0.1.tgz", + "integrity": "sha512-MOzLIwhpt64KIVN64h1MwdKWiyKFNc/S6BoYKPIVUHFg0/eIEyBulhWCgn678v/4c0ri3FdGuzXymNCv02MUIw==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3312,6 +3559,31 @@ "node": ">=14.17" } }, + "node_modules/uint8-varint": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.4.tgz", + "integrity": "sha512-FwpTa7ZGA/f/EssWAb5/YV6pHgVF1fViKdW8cWaEarjB8t7NyofSWBdOTyFPaGuUG4gx3v1O3PQ8etsiOs3lcw==", + "dependencies": { + "uint8arraylist": "^2.0.0", + "uint8arrays": "^5.0.0" + } + }, + "node_modules/uint8arraylist": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/uint8arraylist/-/uint8arraylist-2.4.8.tgz", + "integrity": "sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==", + "dependencies": { + "uint8arrays": "^5.0.1" + } + }, + "node_modules/uint8arrays": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", + "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", + "dependencies": { + "multiformats": "^13.0.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -3327,6 +3599,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 03b5dcb..f803edd 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ }, "homepage": "https://github.com/filecoin-station/piece-indexer#readme", "devDependencies": { + "@types/debug": "^4.1.12", + "@types/node": "^22.5.3", "prettier": "^3.3.2", "standard": "^17.1.0", "typescript": "^5.5.4"