Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
daf4cb2
Merge master (v0.21.3 release) back into develop
jdogresorg Oct 3, 2026
b11e069
Update dispenser source path comment
jdogresorg Oct 4, 2026
dfe56a6
Regenerate CI tier timing block
jdogresorg Oct 4, 2026
d025dab
Merge: Update dispenser source path comment
jdogresorg Oct 4, 2026
1da2b00
sync trunk
jdogresorg Oct 4, 2026
39a1c84
test: cover migration query timeout helpers
jdogresorg Oct 4, 2026
9b9cf68
test: cover RPC configuration helpers
jdogresorg Oct 4, 2026
5b85feb
test RPC helper error handling
jdogresorg Oct 4, 2026
8c22158
test: use numeric custom minimum input
jdogresorg Oct 4, 2026
a75b38e
split RPC helper error suites
jdogresorg Oct 4, 2026
5b11963
test: use numeric zero with custom minimum
jdogresorg Oct 4, 2026
b0ed53a
report RPC helper cases separately
jdogresorg Oct 4, 2026
d6da593
Make migration status tests filesystem-independent
jdogresorg Oct 4, 2026
50eb564
Merge: Make migration status tests filesystem-independent
jdogresorg Oct 4, 2026
1902649
sync trunk
jdogresorg Oct 4, 2026
0d9c670
sync trunk
jdogresorg Oct 4, 2026
fe1932c
sync trunk
jdogresorg Oct 4, 2026
ed61521
test: cover negative default minimum
jdogresorg Oct 4, 2026
cd05450
sync trunk
jdogresorg Oct 4, 2026
6ce4394
Confirm CI tier timing block
jdogresorg Oct 5, 2026
e7f4206
Pin decoder base image digest
jdogresorg Oct 5, 2026
12e0d7a
Pin both Docker base image stages
jdogresorg Oct 5, 2026
8ff0220
Use pinned dependency stage in decoder image
jdogresorg Oct 5, 2026
12e9ead
Merge: Use pinned dependency stage in decoder image
jdogresorg Oct 5, 2026
bb016f8
Merge: Confirm CI tier timing block
jdogresorg Oct 5, 2026
65b2669
sync trunk
jdogresorg Oct 5, 2026
9833b4a
Treat suite title additions as growth
jdogresorg Oct 5, 2026
6df112a
Merge: Treat suite title additions as growth
jdogresorg Oct 5, 2026
0cc52fe
sync trunk
jdogresorg Oct 5, 2026
65f94ab
sync trunk
jdogresorg Oct 5, 2026
8c5eead
sync trunk
jdogresorg Oct 5, 2026
86b4c0f
Merge: sync trunk
jdogresorg Oct 5, 2026
d0fa093
Merge: sync trunk
jdogresorg Oct 5, 2026
1988ec3
Merge: sync trunk
jdogresorg Oct 5, 2026
7c51a7b
Split decoder protocol constants into named families under constants/
jdogresorg Oct 5, 2026
b994029
Merge decoder activation constant families into one module
jdogresorg Oct 5, 2026
a5f4f89
Route decoder api environment reads through config getters
jdogresorg Oct 5, 2026
e2799f8
Merge: Merge decoder activation constant families into one module
jdogresorg Oct 5, 2026
34fe761
Merge: Route decoder api environment reads through config getters
jdogresorg Oct 5, 2026
680be6f
Pin payload_helpers push-size classes, alias canonicalization and probes
jdogresorg Oct 6, 2026
1f78302
Pin payload_helpers push-size classes, alias canonicalization and probes
jdogresorg Oct 6, 2026
6ab6b88
Merge: Pin payload_helpers push-size classes, alias canonicalization …
jdogresorg Oct 6, 2026
a7b8db6
Add dispenser expiration extension undo queries
jdogresorg Oct 6, 2026
c183a93
Wire the dispenser expiration extension undo into extend, reorg and p…
jdogresorg Oct 6, 2026
6f284ae
Pin executable migration residue at commit time, ported from the inde…
jdogresorg Oct 6, 2026
bf1be41
Validate block hashes from the node, carry JSON-RPC error codes on HT…
jdogresorg Oct 6, 2026
e7d3d7e
Pin VALID_ACTION_NAMES membership as a consensus flag-day surface
jdogresorg Oct 6, 2026
e02981c
Guard the bufferutils patch twin against the utxo-tracker copy (revie…
jdogresorg Oct 6, 2026
bc5b30c
Fix extension undo test doubles: single mock method, restore ordering…
jdogresorg Oct 6, 2026
db41649
sync trunk
jdogresorg Oct 6, 2026
b52e51e
Bring extension undo tests under the structure limits: split two long…
jdogresorg Oct 6, 2026
7ed9c08
sync trunk
jdogresorg Oct 6, 2026
aec20e3
Prove the extension undo table is reconciled by the startup schema ch…
jdogresorg Oct 6, 2026
a9c0cf7
Declare the deleteBlockByIndex title rename from 4 to 5 DELETE querie…
jdogresorg Oct 6, 2026
27f12d8
Add the CLA Assistant step to the PR checklist in CONTRIBUTING.md
jdogresorg Oct 6, 2026
8ae0447
Merge: Declare the deleteBlockByIndex title rename from 4 to 5 DELETE…
jdogresorg Oct 6, 2026
5f7f3ee
Merge: Add the CLA Assistant step to the PR checklist in CONTRIBUTING.md
jdogresorg Oct 6, 2026
f1bab88
Merge: sync trunk
jdogresorg Oct 6, 2026
e406513
Review round 2026-10-06: 4 review fixes in xchain-decoder
jdogresorg Oct 7, 2026
5844478
Clarify AGPL attribution term
jdogresorg Oct 7, 2026
297e85a
Merge: Clarify AGPL attribution term
jdogresorg Oct 7, 2026
3e3eb52
Review round 2026-10-06, third pass: 1 review fix in xchain-decoder
jdogresorg Oct 7, 2026
16e8154
test(decoder): guard coin parity through sibling trust
jdogresorg Oct 7, 2026
58261be
Merge: test(decoder): guard coin parity through sibling trust
jdogresorg Oct 7, 2026
6e78693
Assert transaction ID columns are BIGINT at startup
jdogresorg Oct 7, 2026
a970132
test(decoder): refresh action manifest fixture
jdogresorg Oct 7, 2026
e093c05
Repair migration assertion gate compatibility
jdogresorg Oct 7, 2026
13ac5eb
Require BIGINT ID assertion during startup
jdogresorg Oct 7, 2026
edac802
test(decoder): verify published envelope chunk vectors
jdogresorg Oct 7, 2026
66cc1b4
Add dispenser purge grace activation map
jdogresorg Oct 7, 2026
3d5258c
Document protocol-time caller ordering
jdogresorg Oct 7, 2026
f4cf047
Restore dispenser purge safety invariant
jdogresorg Oct 7, 2026
7378a5c
Enforce protocol-time history bound
jdogresorg Oct 7, 2026
9e06b16
Merge: test(decoder): verify published envelope chunk vectors
jdogresorg Oct 8, 2026
22e947f
Merge: Enforce protocol-time history bound
jdogresorg Oct 8, 2026
4eae715
sync trunk
jdogresorg Oct 8, 2026
22b2875
Handle staged documentation constant rollout
jdogresorg Oct 8, 2026
7ff9b8c
Fix decoder mempool admission, reorg verification, migration guard an…
jdogresorg Oct 8, 2026
4e2b866
sync trunk
jdogresorg Oct 8, 2026
4f176bd
Split BIGINT migration guard tests
jdogresorg Oct 8, 2026
a081c8c
Re-sync the coin registry from the canonical xchain-hub/src/coins
jdogresorg Oct 8, 2026
734f188
Merge: Handle staged documentation constant rollout
jdogresorg Oct 8, 2026
94f0788
sync trunk
jdogresorg Oct 8, 2026
c3fba3d
Gate dispenser purge on cancellation grace
jdogresorg Oct 8, 2026
af1f562
Split cancellation grace test suite
jdogresorg Oct 8, 2026
51e3bd1
Pin BIGINT migration checksum rebaseline
jdogresorg Oct 8, 2026
7513f72
Preserve cancellation grace test titles
jdogresorg Oct 8, 2026
2576023
Require unsigned transaction BIGINT columns
jdogresorg Oct 8, 2026
13bdcce
Split BIGINT migration guard tests
jdogresorg Oct 8, 2026
1dfa037
Enforce newest-first protocol time windows
jdogresorg Oct 8, 2026
6c0c6b7
Merge: Enforce newest-first protocol time windows
jdogresorg Oct 8, 2026
5859ea1
Merge: Preserve cancellation grace test titles
jdogresorg Oct 8, 2026
fa4e795
sync trunk
jdogresorg Oct 8, 2026
de4ced0
sync trunk
jdogresorg Oct 8, 2026
8bf53fb
Declare startup registry title rename
jdogresorg Oct 8, 2026
caab0bf
Merge: Declare startup registry title rename
jdogresorg Oct 8, 2026
30c1792
Add dispenser address key helper
jdogresorg Oct 8, 2026
5a078be
Merge: Add dispenser address key helper
jdogresorg Oct 8, 2026
a56d5a9
Add keyed dispenser registration collapse
jdogresorg Oct 8, 2026
d94e90e
Merge: Add keyed dispenser registration collapse
jdogresorg Oct 8, 2026
e6da362
sync trunk
jdogresorg Oct 8, 2026
1c1dcee
Merge: sync trunk
jdogresorg Oct 8, 2026
a5cdf69
Add dispenser address ID collapse activation
jdogresorg Oct 8, 2026
df5e6b8
Gate dispenser registration collapse by address id
jdogresorg Oct 8, 2026
49ed46d
Split dispenser registration test declarations
jdogresorg Oct 8, 2026
1544a56
Merge: Add dispenser address ID collapse activation
jdogresorg Oct 8, 2026
21981e1
Merge: Split dispenser registration test declarations
jdogresorg Oct 8, 2026
047268d
Re-vendor the action manifest so BET format 4 matches the canonical c…
jdogresorg Oct 8, 2026
dc99588
Repin the vendored action manifest hash after the BET format 4 re-ven…
jdogresorg Oct 8, 2026
57c915c
fix: review round corrections
jdogresorg Oct 8, 2026
c0fe2c4
fix: review round corrections
jdogresorg Oct 8, 2026
bdf77d1
fix: preserve stale mempool read metadata
jdogresorg Oct 9, 2026
278209d
refactor: split mempool stale read helpers
jdogresorg Oct 9, 2026
d5ba2f6
test: prove stale mempool fallback behavior
jdogresorg Oct 9, 2026
cb29fbb
test: enforce mempool stale fallback in api suite
jdogresorg Oct 9, 2026
87d6dd8
Merge: test: enforce mempool stale fallback in api suite
jdogresorg Oct 9, 2026
040fca4
Fix 12 review findings in xchain-decoder
jdogresorg Oct 9, 2026
ede8029
Clamp wide dispenser expirations in decoder
jdogresorg Oct 9, 2026
89b2146
Pin declared sibling main checkouts
jdogresorg Oct 9, 2026
069fc43
Sync decoder action manifest identity
jdogresorg Oct 9, 2026
ed4a3f3
docs(decoder): explain dispatched batch admission
jdogresorg Oct 9, 2026
434b56c
Keep wide expiration gate out of the activation-map rule
jdogresorg Oct 9, 2026
96abf14
Merge: Pin declared sibling main checkouts
jdogresorg Oct 9, 2026
3ff58cf
Merge: docs(decoder): explain dispatched batch admission
jdogresorg Oct 10, 2026
f132e51
Gate exact dispenser expiration validation
jdogresorg Oct 9, 2026
b5a7f39
Clarify exact expiration validation comment
jdogresorg Oct 9, 2026
5dd6def
Split exact expiration test and trim registration comments to meet st…
jdogresorg Oct 9, 2026
2327779
Trim activation documentation below structure limit
jdogresorg Oct 9, 2026
53f986f
Merge: Trim activation documentation below structure limit
jdogresorg Oct 10, 2026
2bfbac0
sync trunk
jdogresorg Oct 10, 2026
b6ad513
Merge: sync trunk
jdogresorg Oct 10, 2026
f77b0d0
Re-take the suite-title pin against the current test tree
jdogresorg Oct 10, 2026
fd19b9b
Enforce migration residue pin sort order
jdogresorg Oct 10, 2026
de37d9a
Merge: Re-take the suite-title pin against the current test tree
jdogresorg Oct 10, 2026
40ccc72
Merge: Enforce migration residue pin sort order
jdogresorg Oct 10, 2026
e9bd652
Set the repo git identity at session start and turn attribution off
jdogresorg Oct 10, 2026
a8ecd7a
Read DECODER_DB_* in the decoder entry points through src/config.js
jdogresorg Oct 11, 2026
b35dae6
Merge: Read DECODER_DB_* in the decoder entry points through src/conf…
jdogresorg Oct 11, 2026
da334c1
Classify mempool payments against the open-dispenser set
jdogresorg Oct 11, 2026
f420f29
Merge: Classify mempool payments against the open-dispenser set
jdogresorg Oct 11, 2026
ae761b8
Release v0.23.0
jdogresorg Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"attribution": false,
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "git config user.name 'J-Dog' && git config user.email 'j-dog@j-dog.net' || true",
"timeout": 10,
"statusMessage": "Setting the repo git identity"
}
]
}
]
}
}
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.23.0] - 2026-10-11

### Added
- Added dispenser address collapse, purge grace and expiration extension undo, each behind an activation.
- Enforced newest-first protocol time windows and the protocol time history bound.

### Changed
- Required unsigned BIGINT transaction id columns and asserted them at startup.
- Pinned the BIGINT migration checksum rebaseline so the widened ids migration heals in place.

### Fixed
- Preserved stale mempool read metadata and kept a known reorg halt on a database ping failure.
- Validated block hashes from the node and carried JSON-RPC error codes on HTTP 200 errors.

## [0.21.0] - 2026-09-29

### Added
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ CI runs the no-external-services gate, the docker tiers, the cross-repo drift gu
2. Update `CHANGELOG.md` with a terse entry for your change.
3. Make sure `git status` is clean apart from intended changes (no `node_modules/`, no editor leftovers, no `.env`).
4. Open the PR with a clear title and a description of what changed and why.
5. Agree to the Contributor License Agreement when the CLA Assistant bot links it on your first PR. Its `license/cla` check must pass before merge, so a PR from a contributor who has not signed cannot be merged. One signature covers every XChain Platform repository.

For non-security bugs, open an issue at <https://github.com/XChain-Platform/xchain-decoder/issues/new>. For security bugs, see [`SECURITY.md`](./SECURITY.md).

Expand Down
13 changes: 8 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
FROM node:25.9.0

RUN mkdir /XChainDecoder/
FROM node:22-alpine@sha256:0a7108bf6c7bf5de370ffb1a3ed6be93d405b43ff159f681a8d18c0e2bc2e402 AS dependencies
WORKDIR /XChainDecoder
COPY ./package.json /XChainDecoder/package.json
COPY ./package-lock.json /XChainDecoder/package-lock.json
WORKDIR /XChainDecoder
RUN npm ci --omit=dev

FROM node:25.9.0@sha256:78839ac448c23517f8eab2e8f7943d9b4f73979eb7f8bed2c73dbf72ff869e7b AS decoder
WORKDIR /XChainDecoder
COPY ./package.json /XChainDecoder/package.json
COPY ./package-lock.json /XChainDecoder/package-lock.json
COPY --from=dependencies /XChainDecoder/node_modules /XChainDecoder/node_modules
COPY ./src /XChainDecoder/src
# Patch bitcoinjs-lib's bufferutils with a BigInt-aware 64-bit reader. The stock
# readUInt64 throws "RangeError: value out of range" for output values above
Expand All @@ -24,4 +27,4 @@ COPY ./src/chain/bufferutils.js /XChainDecoder/node_modules/bitcoinjs-lib/src/bu
# xchain-encoder/Dockerfile). This image registers real drain work on SIGTERM
# (src/api.js: flip decoderRunning, then decoder.stop()), which only runs when
# node is PID 1 and receives the signal itself.
CMD ["node", "./src/api.js"]
CMD ["node", "./src/api.js"]
6 changes: 4 additions & 2 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ For commercial licensing, contact **legal@dankest.llc**.

## Attribution

Any redistribution or modification of this software must retain the following
attribution, per the AGPL and the project's trademark policy:
As an additional term under section 7(b) of the GNU Affero General Public
License v3.0, any redistribution or modification of this software must preserve
the following author attribution in its source and in the Appropriate Legal
Notices displayed by any work containing it:

> Based on XChain Platform by Dankest, LLC - https://dankest.llc

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Transaction extraction service for the XChain Platform. Polls the coin nodes of
- **ACTION validation**: 34-name allowlist plus 5 short-form aliases (e.g. TRANSFER -> SEND) expanded before database writes
- **Parse-failure quarantine**: tx-level decode failures retry up to 3 times, then are quarantined as PARSE_ERROR events rather than halting the block
- **Source pubkey capture**: records the source address pubkey per transaction in a dedicated table for downstream use by the indexer
- **Native-coin fee tracking**: when FEE_DESTINATION is set, outputs paying that address are persisted to transaction_outputs for indexer fee validation
- **Native-coin fee tracking**: outputs paying the coin registry's FEE_DESTINATION are persisted to transaction_outputs for indexer fee validation; on regtest only, `XCHAIN_FEE_DESTINATION_<COIN>_REGTEST` redirects it for the decoder and indexer alike, and a bare `FEE_DESTINATION` env is ignored with a warning
- **Graceful shutdown**: SIGTERM/SIGINT handlers complete in-flight work
- **Node RPC failover**: rotates through `NODE_URL_FALLBACK` endpoints after `NODE_FAILOVER_THRESHOLD` consecutive connection failures, round-robin, so a recovered primary is retried again if the fallback also dies
- **1300+ tests**: unit, integration, e2e, security, fuzz, chaos, mutation, regression, benchmarks, smoke
Expand Down
1 change: 1 addition & 0 deletions bin/ci-full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ ci_tier_deferred() {
# >>> ci-tier timer (generated block; re-run the tier wirer to update) >>>
run_tier() {
ci_tier_deferred "$1" && return 0 # ci-tier guard (generated)
if [ "${CI_TIER:-full}" = "fast" ] && [ -n "${FAILED:-}" ]; then echo; echo "ci:full ===== $1 NOT RUN (a push stops at its first red tier, its verdict already red; the full sweep runs it) ====="; return 0; fi # ci-tier stop (generated)
local name="$1"; shift
local __ci_tier_t0=$SECONDS
echo; echo "ci:full ===== $name ====="
Expand Down
140 changes: 140 additions & 0 deletions bin/lib/migration_residue_pin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/env node
/*********************************************************************
*
* Copyright © 2025–2026 Dankest, LLC
* Based on XChain Platform by Dankest, LLC – https://dankest.llc
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This file is part of XChain Platform. Licensed under the GNU Affero
* General Public License v3.0 or later; see LICENSE.md. A commercial
* license (without AGPL source-disclosure terms) is available -
* contact legal@dankest.llc.
*
**********************************************************************
*
* The migration immutability pin: two sha256 values per committed migration in
* test/fixtures/migration-executable-residue.json, so an in-place edit to an
* applied file fails in CI instead of surfacing on a fleet as `content CHANGED`.
* Ported from the indexer's pin of the same name; the decoder's runner hashes
* migrations the same way (whole-file sha256 in schema_migrations).
*
* residue the file with every `--` line and blank line dropped (the basis the
* MIGRATION_CHECKSUM_REBASELINES entries are verified on). A change
* here is an executable edit and always fails.
* sha256 the whole file, exactly what schema_migrations records. A change here
* passes only when a reviewed rebaseline heals the pinned hash to the
* current one, since that is the only case a deployed DB survives.
*
* A file the pin does not hold yet passes when its name sorts after the newest pinned
* migration, so a new migration needs no fixture edit; `--add` pins it. A file that
* sorts before the newest pinned migration fails because it was missed or backdated.
* An existing entry moves only through `--accept <file>`, which is legitimate solely
* for a file no database has applied.
*
* USAGE (from the repo root)
* node bin/lib/migration_residue_pin.js check; exit 1 on a violation
* node bin/lib/migration_residue_pin.js --add pin every unpinned file
* node bin/lib/migration_residue_pin.js --accept <file> [--accept <file>]
*
********************************************************************/

'use strict';

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');

// Loads the rebaseline table only; requiring src/db opens no connection and reads no env.
const { MIGRATION_CHECKSUM_REBASELINES } = require('../../src/db');

const ROOT = path.join(__dirname, '..', '..');
const MIG_DIR = path.join(ROOT, 'src', 'sql', 'migrations');
const FIXTURE = path.join(ROOT, 'test', 'fixtures', 'migration-executable-residue.json');

const sha256 = (s) => crypto.createHash('sha256').update(s).digest('hex');

function executableResidue(raw){
return String(raw).split('\n').filter((l) => !/^\s*--/.test(l) && l.trim() !== '').join('\n');
}

function pinOf(raw){
return { residue: sha256(executableResidue(raw)), sha256: sha256(raw) };
}

// Every violation of the pin over `files` (name -> raw text), as { file, kind, message }.
function findViolations({ files, fixture, rebaselines }){
const out = [];
const newest = Object.keys(fixture).sort().pop() || '';
for(const file of Object.keys(files).filter((f) => !Object.hasOwn(fixture, f) && f < newest).sort()){
out.push({ file, kind: 'unpinned', message: file + ' sorts before the newest pinned migration ' + newest +
' but is not pinned: run node bin/lib/migration_residue_pin.js --add and commit the fixture.' });
}
for(const file of Object.keys(fixture).sort()){
const pinned = fixture[file];
// Deleting or renaming an applied migration is forbidden: the decoder has no ledger rename map.
if(!Object.hasOwn(files, file)){
out.push({ file, kind: 'missing', message: file + ' is pinned but gone: deleting or renaming an applied ' +
'migration breaks every ledger that recorded it.' });
continue;
}
const now = pinOf(files[file]);
// A changed statement is never healable; it needs a new dated migration.
if(now.residue !== pinned.residue){
out.push({ file, kind: 'residue', message: file + ': the executable statements of a pinned migration ' +
'changed. Revert and write a new dated migration; MIGRATION_CHECKSUM_REBASELINES heals the ledger only.' });
continue;
}
// A comment-only change passes only under a rebaseline healing exactly the pinned hash to the current one.
const heal = rebaselines && rebaselines[file];
const healed = heal && heal.to === now.sha256 && [].concat(heal.from).includes(pinned.sha256);
if(now.sha256 !== pinned.sha256 && !healed){
out.push({ file, kind: 'content', message: file + ': content changed with no rebaseline from ' +
pinned.sha256.slice(0, 12) + ' to ' + now.sha256.slice(0, 12) + ', so every DB that applied it logs ' +
'`content CHANGED` and migrate.js fails closed there. Revert, or add a reviewed comment-only rebaseline.' });
}
}
return out;
}

// Every committed migration (`.sql` only; the README beside them is not one), name -> raw text.
function readTree(){
const files = {};
for(const f of fs.readdirSync(MIG_DIR).filter((n) => n.endsWith('.sql'))) files[f] = fs.readFileSync(path.join(MIG_DIR, f), 'utf8');
return files;
}

function writeFixture(fixture){
const sorted = {};
for(const k of Object.keys(fixture).sort()) sorted[k] = fixture[k];
fs.writeFileSync(FIXTURE, JSON.stringify(sorted, null, 2) + '\n');
}

function main(argv){
const files = readTree();
const fixture = JSON.parse(fs.readFileSync(FIXTURE, 'utf8'));
const accept = argv.flatMap((a, i) => (a === '--accept' ? [argv[i + 1]] : []));
if(argv.includes('--add') || accept.length){
const added = Object.keys(files).filter((f) => !fixture[f]);
if(argv.includes('--add')) for(const f of added) fixture[f] = pinOf(files[f]);
for(const f of accept){
if(!files[f]) throw new Error('--accept ' + f + ': no such migration');
console.warn('ACCEPTED a new pin for ' + f + ': legitimate only if no database has applied it.');
fixture[f] = pinOf(files[f]);
}
writeFixture(fixture);
console.log('pinned ' + (argv.includes('--add') ? added.length : 0) + ' new, accepted ' + accept.length +
'. Next: node bin/lib/migration_residue_pin.js');
return 0;
}
const bad = findViolations({ files, fixture, rebaselines: MIGRATION_CHECKSUM_REBASELINES });
for(const v of bad) console.log(v.kind + ': ' + v.message);
const unpinned = Object.keys(files).filter((f) => !fixture[f]).length;
console.log(bad.length + ' violation(s), ' + unpinned + ' unpinned file(s).' +
(unpinned ? ' Next: node bin/lib/migration_residue_pin.js --add' : ''));
return bad.length ? 1 : 0;
}

module.exports = { executableResidue, pinOf, findViolations, readTree, FIXTURE };

if(require.main === module) process.exitCode = main(process.argv.slice(2));
Loading
Loading