Context
After the 2.0 refactor, ff-cli is mostly TypeScript, but the core src/utilities/ layer is still CommonJS and is reached from TS via require() interop (each site carries an eslint-disable @typescript-eslint/no-require-imports). The biggest gap: the DP-1 playlist item is built as an untyped object literal in playlist-builder.js, so field-shape mistakes aren't caught at compile time — that's the layer where the recent mime/duration playback bugs lived.
This is the largest of the three refactors we scoped; #1 (drop legacy config migration) and #2 (shared castPlaylist) are done in #83. This one is deliberately split out because it's bigger and riskier, and best done incrementally.
Scope
Convert the remaining CommonJS utilities to TypeScript and remove the require() interop dance:
Approach
- Incremental, one file per PR.
playlist-builder.js first; it unblocks typing the item across the rest.
- Keep behavior identical — the suite (370 tests, incl. the
play delivery-signing and DP-1 conformance e2e tests) is the safety net. Don't change output or wire formats.
npm run verify green after each step.
Why not bundle it into the 2.0 work
The test coverage on the builder is decent but not a full safety net for a type migration, so this is intentionally a separate, careful effort rather than something rushed into the release.
Acceptance criteria
- No
.js files remain under src/utilities/ (or a documented exception).
- No
no-require-imports eslint-disables remain for these modules.
- DP-1 items are constructed through a typed interface.
npm run verify passes; no behavior/output changes.
Context
After the 2.0 refactor, ff-cli is mostly TypeScript, but the core
src/utilities/layer is still CommonJS and is reached from TS viarequire()interop (each site carries aneslint-disable @typescript-eslint/no-require-imports). The biggest gap: the DP-1 playlist item is built as an untyped object literal inplaylist-builder.js, so field-shape mistakes aren't caught at compile time — that's the layer where the recentmime/durationplayback bugs lived.This is the largest of the three refactors we scoped; #1 (drop legacy config migration) and #2 (shared
castPlaylist) are done in #83. This one is deliberately split out because it's bigger and riskier, and best done incrementally.Scope
Convert the remaining CommonJS utilities to TypeScript and remove the
require()interop dance:src/utilities/playlist-builder.js— start here (most central; freshly worked on). Introduce a typedDP1Item/ playlist builder so item construction is type-checked.src/utilities/nft-indexer.jssrc/utilities/index.jssrc/utilities/feed-fetcher.jssrc/utilities/functions.jssrc/utilities/playlist-signer.jssrc/utilities/playlist-signing-role.js// eslint-disable ... no-require-imports+require()interop in:src/commands/find.ts,src/commands/play.ts,src/commands/sign.ts,src/main.ts,src/utilities/playlist-source.ts,src/utilities/playlist-cast.ts.ff-playerconsumes (feral-file/ff-playersrc/models/dp1.model.ts) so the producer and the renderer agree on field names.Approach
playlist-builder.jsfirst; it unblocks typing the item across the rest.playdelivery-signing and DP-1 conformance e2e tests) is the safety net. Don't change output or wire formats.npm run verifygreen after each step.Why not bundle it into the 2.0 work
The test coverage on the builder is decent but not a full safety net for a type migration, so this is intentionally a separate, careful effort rather than something rushed into the release.
Acceptance criteria
.jsfiles remain undersrc/utilities/(or a documented exception).no-require-importseslint-disables remain for these modules.npm run verifypasses; no behavior/output changes.