Skip to content

Finish the match key rename in the 51Did constants and make the linter runnable - #188

Merged
jwrosewell merged 3 commits into
mainfrom
feature/51did-match-key-constants
Sep 2, 2026
Merged

Finish the match key rename in the 51Did constants and make the linter runnable#188
jwrosewell merged 3 commits into
mainfrom
feature/51did-match-key-constants

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

This pull request does three things. It finishes the match key rename in the
51Did constants, it makes the linter runnable from a clone and clears every
error the linter reports, and it fixes the one documentation gap among the
warnings that reaches customers.

Part one, the match key rename

The problem

The rename of the 51Did hash to the match key
(#186) was only half
done. FodId gained a matchKey getter with a deprecated hash alias kept
beside it, but two of the static constants in
fiftyone.pipeline.did/fodId.js
still carried the old name even though the comments written above those two
constants already said "match key".

/** Byte offset of the match key field within the payload. */
static HASH_OFFSET = 5;
/**
 * Byte length of the match key field for Probabilistic and HashedEmail
 * identifiers, being a SHA-256.
 */
static HASH_LENGTH = 32;
static HEADER_LENGTH = 5;
...
static PAYLOAD_LENGTH = 37;

A reader coming to this file has to work out that a constant called
HASH_OFFSET is where the match key starts, and that HEADER_LENGTH and
PAYLOAD_LENGTH are just the same two numbers written out again rather than
values worked out from them.

What changed

  1. MATCH_KEY_OFFSET and MATCH_KEY_LENGTH are now the real constants. The
    values (5 and 32) and the comments above them are unchanged.
  2. HASH_OFFSET and HASH_LENGTH remain as deprecated aliases pointing at
    the new names.
  3. HEADER_LENGTH is now FodId.MATCH_KEY_OFFSET and PAYLOAD_LENGTH is now
    FodId.MATCH_KEY_OFFSET + FodId.MATCH_KEY_LENGTH, so the numbers are no
    longer repeated. Neither of those two names changes, because neither is
    about the match key.
  4. The TypeScript definitions in fiftyone.pipeline.did/types/fodId.d.ts were
    regenerated with tsc from the same tsconfig.json the build uses, so the
    definitions and the JavaScript agree, aliases and deprecation notes
    included.
  5. Twenty-five references across five files now read the new names, being
    fodId.js (3), didClient.js (1), examples/fodIdExample.js (2),
    tests/envelope.js (3) and tests/fodId.test.js (16).
  6. Nothing to do with SHA-256 hashing itself was renamed. The comment saying
    the match key of a Probabilistic or HashedEmail identifier is a SHA-256
    stays exactly as it was.

Two lines that the longer names pushed past 80 columns were wrapped, one in
the example and one in the constants test.

Why the aliases are kept

The package is published to npm as
fiftyone.pipeline.did,
at 4.5.35 at the time of writing, and these two constants are part of its
public surface, so callers outside this repository may already read
FodId.HASH_OFFSET and FodId.HASH_LENGTH to build or slice a payload.
Removing the old names in this change would break that code with no warning.
The aliases hold the same values, so old and new callers see the same numbers,
and the @deprecated note tells anyone still on the old names to move across.
The wording of the note copies the deprecated hash getter that already sits
next to matchKey in the same file, so the whole rename reads the same way
wherever a reader meets it.

Part two, the linter

The problem

The repository has an ESLint setup and the build has been running it all
along, but nobody could run it from a clone, so the errors it prints have gone
unread.

.eslintrc sits at the repository root and covers every package. The lint
tooling it needs was declared only inside a PowerShell here string in
ci/setup-environment.ps1, which wrote a root package.json from scratch on
every CI run. Nothing of that file was in the repository, so a clone had no
npm install to run and no npm run lint to run either, and the only way to
lint was to install ESLint out of tree and point it at the config by hand.

The build does run npm run lint, through
common-ci
node/build-project.ps1, and it has been printing errors for a long time. In
the nightly run on main of 2 September 2026
(run 33583980519,
job Build and Test - 0 - Ubuntu_Node_24) the Build Project step printed this.

✖ 86 problems (15 errors, 71 warnings)
  1 error and 68 warnings potentially fixable with the `--fix` option.

The step then moved straight on to Run Unit Tests and the job reported
success, so nothing was stopping those 15 errors accumulating.

The 15 errors were these.

fiftyone.pipeline.cloudrequestengine/tests/integration.test.js
  41:35  error  ["_51DEGREES_RESOURCE_KEY"] is better written in dot notation  dot-notation

fiftyone.pipeline.did/fodId.js
  87:23  error  Parsing error: Unexpected token =

fiftyone.pipeline.did/tests/creatorContextServer.test.js
  70:1  error  'describe' is not defined  no-undef

fiftyone.pipeline.did/tests/didClient.integration.test.js
  38:28  error  'describe' is not defined   no-undef
  38:39  error  'describe' is not defined   no-undef
  46:3   error  'beforeAll' is not defined  no-undef

fiftyone.pipeline.did/tests/didClient.test.js
  140:1  error  'describe' is not defined   no-undef
  142:3  error  'afterEach' is not defined  no-undef
  183:1  error  'describe' is not defined   no-undef
  246:1  error  'describe' is not defined   no-undef
  330:1  error  'describe' is not defined   no-undef
  558:1  error  'describe' is not defined   no-undef
  648:1  error  'describe' is not defined   no-undef
  894:1  error  'describe' is not defined   no-undef

fiftyone.pipeline.did/tests/fodId.test.js
  244:26  error  Parsing error: Identifier directly after number

A correction to what this pull request said earlier

An earlier version of this description said ESLint was not declared as a
dependency anywhere in the repository, and listed eight errors including five
reports of a missing jsdoc/newline-after-description rule. Both statements
were wrong and are corrected here.

ESLint was declared, in ci/setup-environment.ps1, and the accurate statement
is that the declaration was not in a file a clone could install from. The five
missing rule reports were an artefact of the out of tree install, which pulled
a current eslint-plugin-jsdoc in which that rule no longer exists. The
version CI pins, 38.1.6, still defines it, and it is a warning there rather
than an error. The no-multi-spaces error was real but was hidden on main,
because the file it sits in could not be parsed at all, so no other rule in
that file ever ran.

What changed

The root package.json is now in the repository. It carries the same
unit-test, integration-test, lint and tsc scripts the CI script used
to generate, and the same tooling versions, so nothing about how the build
behaves changes. The tooling moved into devDependencies, which is how every
package in this repository already declares jest and TypeScript. ESLint stays
pinned exactly at 8.57.0 and the plugins keep the caret ranges CI has been
proving on every run, which matters because a caret on
eslint-plugin-jsdoc: ^38.1.6 is what keeps the major version that removed
newline-after-description out. The package is marked private because it is
a harness for running jest, tsc and ESLint against the local source and is
never published.

ci/setup-environment.ps1 no longer writes that file. Leaving the
generation in place would silently overwrite the checked in copy on every CI
run and let the two drift apart, which is the fault being fixed. The script
now just calls ./node/setup-environment.ps1, which runs npm install. The
notes the here string carried, about why jest has to be 28 or later and why
the test patterns use escaped double quotes rather than single quotes on
Windows, are kept as comments in the script so that knowledge survives.

.eslintrc ecmaVersion goes from 2020 to 2022. Class static fields are
ES2022 and numeric separators such as 0b0000_0101 are ES2021, and the source
has used both since before this change, so ESLint could not parse
fiftyone.pipeline.did/fodId.js or fiftyone.pipeline.did/tests/fodId.test.js
at all. Everything in those two files went unchecked. Raising the setting
parses both.

.eslintrc gains the jest environment. The globals list named test,
jest and expect by hand and stopped there, so describe, beforeAll and
afterEach read as undefined variables in the test files. The three hand
listed names are replaced by "env": { "jest": true }, which declares the
whole set the runner provides. It is applied through an overrides block
matching **/tests/**/*.js, **/*.test.js and setup.js, so the jest names
reach the test files, the test helpers and the root setup file and no further.

Two faults in the source are fixed. In
fiftyone.pipeline.cloudrequestengine/tests/integration.test.js a resource key
was read as process.env['_51DEGREES_RESOURCE_KEY'] on a line whose own next
line already used process.env.RESOURCE_KEY, so it now reads
process.env._51DEGREES_RESOURCE_KEY and matches its neighbour. In
fiftyone.pipeline.did/tests/fodId.test.js a trailing comment kept the padding
from an alignment that the match key rename in
#186 had already broken,
so the padding is gone.

junit.xml and test-results/ are added to .gitignore, because now that
npm run unit-test can be run from a clone it drops a reporter file at the
root.

No rule was turned off and no eslint-disable comment was added anywhere.

Which fixes are config changes, and why

Two of the four are config changes rather than source changes, and in both
cases the config was wrong and the source was right.

ecmaVersion describes the language the source is written in. The source is
valid modern JavaScript that runs on every Node version this repository
supports, being 20, 22 and 24. Editing the source to suit a 2020 setting would
mean giving up class static fields and numeric separators for no reason, so
the setting is what had to move.

describe, beforeAll and afterEach are real globals that jest injects into
a test file. They are not undefined, they were undeclared. The config was
already trying to say this by hand and had simply missed most of the names, so
naming the environment is both the supported way to say it and the way that
cannot go stale again.

How it was verified

Values read back from the built class, which confirms the aliases and the
worked out lengths still give the numbers the format needs.

$ node -e "const F=require('./fodId'); console.log(...)"
{"MATCH_KEY_OFFSET":5,"MATCH_KEY_LENGTH":32,"HASH_OFFSET":5,"HASH_LENGTH":32,
 "HEADER_LENGTH":5,"PAYLOAD_LENGTH":37,"RANDOM_PAYLOAD_LENGTH":21,
 "GUID_LENGTH":16}

The 51Did suite, run from fiftyone.pipeline.did on Node v24.13.0.

$ npx jest
PASS tests/creatorContextServer.test.js
PASS tests/didClient.test.js
PASS tests/fodId.test.js

Test Suites: 1 skipped, 3 passed, 3 of 4 total
Tests:       2 skipped, 139 passed, 141 total
Snapshots:   0 total
Time:        4.238 s
Ran all test suites.

The same command on main gives the same totals, being 139 passed and 2
skipped in 3 passed suites with 1 suite skipped, so this change neither adds
nor loses a test and the skips are not related to it.

The whole repository suite, run from the newly checked in root harness the way
the build runs it.

$ npm run unit-test
Test Suites: 23 passed, 23 total
Tests:       269 passed, 269 total
Snapshots:   0 total
Time:        46.184 s
Ran all test suites.

The linter, run the normal way with no out of tree install. This is a clean
clone of this branch, then npm install, then npm run lint, which is the
whole point of part two.

$ git clone --branch feature/51did-match-key-constants ... pn-freshcheck
$ cd pn-freshcheck
$ npm install
added 506 packages in 42s
$ npm run lint

> pipeline-node@1.0.0 lint
> eslint . --ext .js

✖ 104 problems (0 errors, 104 warnings)
  0 errors and 100 warnings potentially fixable with the `--fix` option.

$ echo $?
0

So the count goes from 15 errors to 0, and npm run lint now exits 0. The
104 is after part three below removes one warning. Part two alone leaves 105.

The warnings rise from 71 to 105 and every one of the 34 extra warnings comes
from the two files that could not be parsed before, being fodId.js with 28
and fodId.test.js with 6. No other file gained a warning. What they are, and
what was done about them, is part three.

The generated TypeScript definitions still build, checked with npm run tsc
from the root, which exits 0. That command rewrites the .d.ts files for every
package, and only the ones this change is about,
fiftyone.pipeline.did/types/fodId.d.ts and
fiftyone.pipeline.did/types/idType.d.ts, are committed here. The churn it
produced in the other packages was reverted, because it comes from the root
harness resolving a different TypeScript patch version from the one that
generated the committed files and has nothing to do with this change. That
drift is worth a look on its own.

Part three, the one warning worth fixing

Clearing the parse errors in part two made 105 warnings readable for the first
time. They were then read, one by one, rather than counted. All 105 come from
the jsdoc plugin and not one reports a defect in behaviour, but one of them
is a real documentation gap that customers can see.

The problem

In fiftyone.pipeline.did/idType.js, the name member declared its return
with no description at all, whilst fromFlags four lines above it says what
its return means.

  /**
   * Decodes the identifier type from the top two bits (6-7) of a flags byte.
   * @param {number} flags the 1-byte flags value (0-255)
   * @returns {number} the IdType value
   */
  fromFlags (flags) { ... },

  /**
   * The human-readable name of an IdType value.
   * @param {number} type an IdType value
   * @returns {string}
   */
  name (type) {
    return NAMES[type];
  }

This is not only an internal comment. tsc copies it straight through into
the published fiftyone.pipeline.did/types/idType.d.ts, so a customer reading
the type definitions in their editor sees a documented fromFlags next to an
undocumented name.

What changed

The comment now says what a valid input is and what comes back, in both the
source and the definition file, which carry identical text.

  /**
   * The human-readable name of an IdType value.
   * @param {number} type an IdType value, being 0 to 3 as fromFlags returns
   * @returns {string} the name of that type, for example "Probabilistic"
   */

No code changed, no fallback was added and nothing throws.

Why the declared return type was not widened

NAMES holds exactly four entries, so name(type) hands back undefined for
any value outside 0 to 3, and a reader could argue the honest declaration is
string | undefined. It is deliberately left as string.

The package is published on npm as
fiftyone.pipeline.did
at 4.5.35, and name(type: number): string is in the type definitions
customers already compile against. Widening it to include undefined would
break every TypeScript caller that assigns the result to a string, at compile
time, in a patch release, over a comment. Naming the precondition on the
parameter instead makes the existing string declaration true, because for
every input the documentation now admits, a string is exactly what comes back.
If the type should be widened, that belongs in a release where a breaking
change is expected and announced, not here.

Why the other 104 are left alone

They were grouped by rule and read.

Count Rule What it is
67 jsdoc/newline-after-description Wants a blank line between the prose and the first tag. No package in this repository has ever written JSDoc that way.
32 jsdoc/require-jsdoc All 32 are local helper functions inside test files and examples. None are on the published API, which is fully documented already.
4 jsdoc/require-description Blocks whose @param or @returns already describes the member in full, wanting separate prose above the tags as well.
1 jsdoc/check-types @returns {function} preferring {Function}, in a test helper comment that is compiled into nothing.

None of the 104 reports a defect in behaviour. Two of them sit on the
deprecation blocks written for the aliases in part one, which are written the
same way as the deprecated hash getter they copy, so fixing only those two
would make them the only JSDoc blocks in the repository written differently
from every other.

eslint --fix is not the answer either. Asked with --fix-dry-run, which
writes nothing, it clears 100 of the 105 and then creates 165 new ones, taking
the total to 170, because the autofix for jsdoc/require-jsdoc inserts empty
stubs like this one, which then trip require-param-type,
require-param-description and require-description in turn.

+/**
+ *
+ * @param v
+ */
 function uint32LE (v) {

For completeness, --fix here is safe even though it is unhelpful. Every file
it would rewrite was tokenized before and after with the espree parser ESLint
itself uses, and all 11 token streams are identical, so every change it
proposes sits inside a comment and none can alter behaviour.

The two noisy rules are worth revisiting separately.
jsdoc/newline-after-description and jsdoc/require-jsdoc produce 99 of the
104, against a house style the repository does not follow and against test and
example helpers where a doc comment adds nothing. Turning them down, or at
least not applying require-jsdoc to tests and examples, would leave a
short list somebody will actually read. A warning list nobody reads is how 15
real errors sat unnoticed in the build log for months, which is the fault part
two fixes. That change is not made here because it is a judgement about house
style rather than part of this rename.

How part three was verified

The linter, from the repository root.

$ npm run lint

> pipeline-node@1.0.0 lint
> eslint . --ext .js

✖ 104 problems (0 errors, 104 warnings)
  0 errors and 100 warnings potentially fixable with the `--fix` option.

$ echo $?
0

Down from 105, and jsdoc/require-returns-description no longer appears
anywhere in the output. The remaining rules are
jsdoc/newline-after-description 67, jsdoc/require-jsdoc 32,
jsdoc/require-description 4 and jsdoc/check-types 1, which is the same set
as before minus the one fixed here.

The 51Did suite is unchanged.

$ npx jest
Test Suites: 1 skipped, 3 passed, 3 of 4 total
Tests:       2 skipped, 139 passed, 141 total

npm run tsc exits 0. The definition file was hand edited rather than taken
from a full generation run, to avoid the cross package churn described above,
and the generation was then run once anyway to check the hand edited file is
byte for byte what tsc produces from the new source comment. It is. The churn
that run created in the other packages was reverted, so the only definition
file committed in this part is fiftyone.pipeline.did/types/idType.d.ts.

Produced with AI assistance under James Rosewell's direction and needs human review.

The earlier rename left two static constants on FodId still carrying the
old hash name, HASH_OFFSET and HASH_LENGTH, even though the comments on
those two constants already described a match key. HEADER_LENGTH and
PAYLOAD_LENGTH were plain numbers repeating the same values, so the
relationship between them was not visible in the code.

MATCH_KEY_OFFSET and MATCH_KEY_LENGTH are now the real constants, with
the same values and the same comments. HASH_OFFSET and HASH_LENGTH stay
as deprecated aliases pointing at the new names, worded the same way as
the deprecated hash getter that already sits alongside matchKey, so code
outside this repository keeps working while it moves over. HEADER_LENGTH
and PAYLOAD_LENGTH are now worked out from the new names instead of
repeating the numbers, and their own names do not change.

Every use inside the repository now reads the new names, being the
client, the example, the test helper and the tests. Nothing to do with
SHA-256 hashing itself was renamed. The TypeScript definitions in
fiftyone.pipeline.did/types/fodId.d.ts were regenerated from the
JavaScript so the two agree.

Verified by running the 51Did jest suite, which gave 139 passed and 2
skipped, and by running the 51Did example, which prints the same match
key as before.
The repository lints with ESLint, and the nightly build runs npm run lint
from the repository root, but the root package.json that carries the lint
tooling was written out by ci/setup-environment.ps1 on every CI run and was
never in the repository. Anyone with a clone had nothing to install and
nothing to run, so the errors below went unnoticed for a long time even
though the build printed them.

The root package.json is now checked in, holding the same scripts and the
same tooling versions the CI script used to generate, with the tooling moved
into devDependencies to match how every package in this repository declares
its own build tools. ESLint stays pinned at 8.57.0 and the plugins keep the
caret ranges that CI has been proving for months. The package is marked
private because it is only a harness and is never published. The generation
step in ci/setup-environment.ps1 is gone, because leaving it would silently
overwrite the checked-in file, and the notes it carried about the jest
version and the quoting of the test patterns are kept as comments so that
knowledge is not lost.

Two changes to .eslintrc then clear thirteen of the fifteen errors.

The first is ecmaVersion, which was 2020. The source has used class static
fields since before this change and the tests use numeric separators, both
of which arrived after 2020, so ESLint could not parse fiftyone.pipeline.did
fodId.js or its test file at all and skipped every other rule in them.
Raising it to 2022 parses both.

The second is the jest globals. The globals list named test, jest and expect
by hand but not describe, beforeAll or afterEach, so twelve uses of those
three read as undefined variables. The three hand-listed names are replaced
by the jest environment, applied through an overrides block so it reaches
only the test files, the test helpers and setup.js rather than the whole
repository.

That leaves two real faults in the source, both now fixed rather than
silenced. A resource key was read with bracket notation on a line whose
neighbour already used a dot, and a trailing comment in the 51Did test file
kept the padding from an earlier alignment that the match key rename had
already broken.

npm run lint now reports no errors, where it reported fifteen before. The
105 remaining warnings are the repository's existing JSDoc house style. The
count rose from 71 only because the two files that could not be parsed now
can be, and every one of the extra warnings comes from those two files.

The 51Did suite still gives 139 passed and 2 skipped, and the whole
repository suite run from the new root harness gives 269 passed across 23
suites.
@jwrosewell jwrosewell changed the title Finish the match key rename in the 51Did constants Finish the match key rename in the 51Did constants and make the linter runnable Sep 2, 2026
The name member of IdType documented its return as a bare string type with
no description at all, whilst fromFlags four lines above it says the number
it returns is the IdType value. The gap stayed invisible until the parse
error in this package was fixed, because the linter could not read the file
to report it, and it reaches customers, as tsc copies the comment straight
through into the published fiftyone.pipeline.did/types/idType.d.ts.

The comment now states what a valid input is and what comes back. NAMES
holds four entries, so name returns undefined for anything outside 0 to 3,
and saying the input is 0 to 3 as fromFlags returns makes the declared
string return true for every valid call.

The declared type is deliberately left as string and not widened to allow
undefined. The package is published on npm at 4.5.35, so widening it would
stop TypeScript callers compiling against a version they already use. No
behaviour changes, and no fallback or throw was added.

The published type definition carries the same comment and is updated to
match. Running the type generation from the root harness rewrites definition
files across every package, because the harness resolves a different
TypeScript patch version from the one that produced the committed files, so
that churn was reverted and only idType.d.ts is committed here. The
generation was run to confirm the hand edited definition is byte for byte
what tsc produces from the new source comment.

The linter goes from 105 warnings to 104 and no longer reports a missing
returns description anywhere. The remaining 104 are left alone on purpose,
because they are JSDoc formatting and documentation rules that catch no
defect. The 51Did suite still gives 139 passed and 2 skipped.
@jwrosewell
jwrosewell merged commit 1415785 into main Sep 2, 2026
1 check passed
@jwrosewell
jwrosewell deleted the feature/51did-match-key-constants branch September 2, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant