Keyless FF tooling package for extracting artwork token coordinates from source inputs.
@feralfile/source-resolver resolves user-supplied source identifiers into
token information:
- chain:
ethereumortezos - contract address
- token id
resolveTokenInfo returns one token coordinate for token URLs or collection
pages where a representative token can be selected. resolveTokenInfos returns
an array of token coordinates for collection-like inputs when a keyless static
DOM, caller-provided rendered DOM, or public API path exposes multiple tokens.
Callers can pass { limit: number } to bound collection resolution; token
results include hasMore: true when the source exposed additional usable token
coordinates beyond the returned limit.
Artwork playback URLs are available as an opt-in enrichment:
const token = await resolveTokenInfo(url, { includeArtworkSource: true });
if (token.kind === 'token') {
console.log(token.source); // Marketplace identity, such as "artblocks".
console.log(token.artworkSource); // Browser-loadable artwork URL, when found.
}
const collection = await resolveTokenInfos(url, { includeArtworkSource: true });
if (collection.kind === 'tokens') {
console.log(collection.artworkSources); // Coordinate-paired artwork URLs.
}includeArtworkSource is disabled by default because resolving original or
playable media may require inspecting page content or calling a keyless public
API, even when token coordinates can be parsed entirely from URL components.
Collection enrichment can also request substantially more marketplace data.
Failure to resolve an artwork URL does not discard valid token coordinates;
artworkSource or the corresponding collection finding is simply omitted.
Raw token coordinates do not identify a marketplace adapter and therefore
cannot be enriched automatically.
It intentionally keeps secrets, API keys, playlist construction, DP-1 signing, and marketplace orchestration outside the package. Those belong in callers or server wrappers.
The parser currently recognizes:
- Objkt
- Art Blocks
- fxhash
- Feral File
- OpenSea
- SuperRare
- Neort
- Verse
- Raster
- raw
ethereum:{contract}:{tokenId}andtezos:{contract}:{tokenId}inputs - Ethereum and Tezos wallet addresses for caller-side catalog lookup
Collection-array resolution is supported where the source exposes keyless token data:
- Objkt collection pages through the public Objkt GraphQL API after resolving the collection slug, id, or KT1 contract.
- Art Blocks collection pages through the public Art Blocks GraphQL API after deriving the collection project id from the page.
- fxhash project pages through the public GraphQL API; direct FX1 tokens use keyless TzKT token metadata because fxhash internal object ids differ from on-chain token ids.
- Feral File show and series pages through public Feral File APIs.
- OpenSea collection pages through embedded item JSON or rendered item cards; the documented full collection API requires an API key, so it stays outside this keyless package.
- SuperRare collection pages through the public SuperRare GraphQL API.
- Verse series pages through the public Verse GraphQL API.
- Raster artwork collection pages through Raster's public kit API or rendered token cards.
Neort art pages expose off-chain art IDs rather than token coordinates, so they
remain outside resolveTokenInfos. Their keyless page state includes an
original resourceFileUrl, but includeArtworkSource intentionally returns
only coordinate-paired findings after token resolution and therefore cannot
attach that URL to a Neort art ID. Supporting it requires a separate off-chain
source-identity result shape rather than synthetic token coordinates.
The package exports helpers for callers that need to preflight source identity before resolving:
isValidChainisValidContractAddressisValidTokenIdisValidTokenCoordsisValidWalletAddressnormalizeContractAddressnormalizeTokenCoords
Validation is chain-specific. Ethereum contracts use 20-byte EVM address
construction and enforce EIP-55 checksum casing when mixed-case input is
provided. Tezos contracts must be valid KT1 Base58Check addresses. Ethereum
token ids must fit uint256; Tezos token ids are validated as decimal nats.
resolveTokenInfo follows the required fallback order:
- URL/input parsing.
- Static DOM lookup using
fetch. - Optional caller-provided headless browser rendering.
- Narrow public marketplace API lookup where site adapters expose one.
The browser path is an interface only. This package has no runtime browser dependency and does not own credentials or hosted infrastructure.
npm ci
npm run verifyRun known real-world URL fixtures:
npm run test:liveThe live fixture suite uses URLs confirmed in the in-app browser, follows redirects where marketplaces expose them over HTTP, checks for disappearing pages, and verifies resolver behavior. It does not require marketplace API keys.
Run the headless-browser resolver fixtures:
npx playwright install chromium
npm run test:headlessThe headless suite hooks Playwright into the HeadlessPageRenderer interface
and verifies pages whose token links appear only after client-side rendering.
CI runs both live suites on pull requests, scheduled runs, and manual dispatch
so supported-site URL changes are caught early.
Releases publish to npm as @feralfile/source-resolver via GitHub Actions OIDC trusted publishing (.github/workflows/release.yml).
- Set
package.jsonversionto the release version onmain. - Create a GitHub release whose tag is exactly that version (bare, no
vprefix — e.g.1.0.1), matching the ff-cli tag convention. - The Release workflow runs CI, verifies the tag matches
package.json, publishes with provenance, and verifies the registry version.
Bootstrap note: npm cannot configure a trusted publisher for a package that does not exist yet (npm/cli#8544), so the very first publish authenticates with an NPM_TOKEN repo secret (granular automation token with publish rights on the @feralfile scope). After the package exists, wire the trusted publisher on npmjs.com (org feral-file, repo ff-source-resolver, workflow release.yml) and delete the secret — the same token-era → OIDC-era arc ff-cli followed.