From aa2bd9c6a8c7936dc64859c1bfd5d27cbeb0832e Mon Sep 17 00:00:00 2001 From: Bitcoin Universe Date: Thu, 27 Aug 2026 16:22:59 +0000 Subject: [PATCH] refactor(universe): rename canonicalAssetId to assetId, gate the word The asset reference field carried a word this project does not use. Rename it to assetId across the universe types and the address, asset-flow, and outpoint surfaces. Extend the text gate to ban the word inside the Universe-authored source, while leaving the inherited upstream tree alone, where the term names the HTML rel=canonical link standard. Co-Authored-By: Claude Opus 4.8 --- .../address-assets.component.spec.ts | 2 +- .../address-assets.component.ts | 4 +-- .../asset-flow/asset-flow.component.spec.ts | 10 +++---- .../asset-flow/asset-flow.component.ts | 6 ++-- .../universe/outpoint/outpoint.component.html | 2 +- .../universe/outpoint/outpoint.component.ts | 2 +- frontend/src/app/universe/universe.types.ts | 2 +- scripts/universe/check-text.mjs | 29 +++++++++++++++---- 8 files changed, 37 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/universe/address-assets/address-assets.component.spec.ts b/frontend/src/app/universe/address-assets/address-assets.component.spec.ts index 55f50350e6b..29ed54c2e76 100644 --- a/frontend/src/app/universe/address-assets/address-assets.component.spec.ts +++ b/frontend/src/app/universe/address-assets/address-assets.component.spec.ts @@ -19,7 +19,7 @@ function position(protocolId: string, assetId: string, quantityAtomic?: string): outpoint: `${'a'.repeat(64)}:0`, vout: 0, valueSatsAtomic: '546', - asset: { protocolId, canonicalAssetId: assetId, assetKind: 'fungible', displayName: assetId }, + asset: { protocolId, assetId: assetId, assetKind: 'fungible', displayName: assetId }, quantityAtomic, state: 'unspent', evidence: { authorityId: 'ord', coverage: 'complete' }, diff --git a/frontend/src/app/universe/address-assets/address-assets.component.ts b/frontend/src/app/universe/address-assets/address-assets.component.ts index 7423e4a7895..3df9f82396b 100644 --- a/frontend/src/app/universe/address-assets/address-assets.component.ts +++ b/frontend/src/app/universe/address-assets/address-assets.component.ts @@ -168,11 +168,11 @@ function addPosition( ): void { const asset = position?.asset; if (!asset?.protocolId) {return;} - const assetKey = `${asset.protocolId}:${asset.canonicalAssetId ?? ''}`; + const assetKey = `${asset.protocolId}:${asset.assetId ?? ''}`; if (!holdings.has(assetKey)) { holdings.set(assetKey, { protocolId: asset.protocolId, - displayName: asset.displayName || asset.ticker || asset.canonicalAssetId || asset.protocolId, + displayName: asset.displayName || asset.ticker || asset.assetId || asset.protocolId, quantity: null, outpoints: [], }); diff --git a/frontend/src/app/universe/asset-flow/asset-flow.component.spec.ts b/frontend/src/app/universe/asset-flow/asset-flow.component.spec.ts index 1b0911d98ad..2f0bcc335f9 100644 --- a/frontend/src/app/universe/asset-flow/asset-flow.component.spec.ts +++ b/frontend/src/app/universe/asset-flow/asset-flow.component.spec.ts @@ -46,7 +46,7 @@ function position(overrides: Partial = {}): ExplorerOu outpoint: 'c'.repeat(64) + ':0', vout: 0, valueSatsAtomic: '10000', - asset: { protocolId: 'ordinals', canonicalAssetId: 'i0', assetKind: 'inscription' }, + asset: { protocolId: 'ordinals', assetId: 'i0', assetKind: 'inscription' }, state: 'active', evidence: evidence(), ...overrides, @@ -197,7 +197,7 @@ describe('AssetFlowComponent rare sats', () => { expect( subject.assetLabel({ protocolId: 'rare_sats', - canonicalAssetId: '1050000000000000', + assetId: '1050000000000000', displayName: 'epic', assetKind: 'sat', }), @@ -216,7 +216,7 @@ describe('AssetFlowComponent labels', () => { const inscriptionId = 'd'.repeat(64) + 'i0'; const shortened = subject.assetLabel({ protocolId: 'ordinals', - canonicalAssetId: inscriptionId, + assetId: inscriptionId, assetKind: 'inscription', }); expect(shortened).toContain('…'); @@ -224,7 +224,7 @@ describe('AssetFlowComponent labels', () => { expect( subject.assetLabel({ protocolId: 'runes', - canonicalAssetId: 'UNCOMMON.GOODS', + assetId: 'UNCOMMON.GOODS', ticker: 'UNCOMMON.GOODS', assetKind: 'fungible', }), @@ -243,7 +243,7 @@ describe('AssetFlowComponent labels', () => { it('accents an asset present on both sides of the transaction', () => { const subject = component(); - const asset = { protocolId: 'runes', canonicalAssetId: 'RUNE', assetKind: 'fungible' }; + const asset = { protocolId: 'runes', assetId: 'RUNE', assetKind: 'fungible' }; const transferred = flow({ actions: [ { diff --git a/frontend/src/app/universe/asset-flow/asset-flow.component.ts b/frontend/src/app/universe/asset-flow/asset-flow.component.ts index 1731f490a5b..ad44c2cfc40 100644 --- a/frontend/src/app/universe/asset-flow/asset-flow.component.ts +++ b/frontend/src/app/universe/asset-flow/asset-flow.component.ts @@ -249,7 +249,7 @@ export class AssetFlowComponent implements OnChanges { if (asset.ticker) { return asset.ticker; } - const id = asset.canonicalAssetId ?? ''; + const id = asset.assetId ?? ''; if (asset.assetKind === 'inscription' && id.length > 20) { return id.slice(0, 8) + '…' + id.slice(-3); } @@ -281,7 +281,7 @@ export class AssetFlowComponent implements OnChanges { return (flow.actions ?? []).some( (action) => action.actionType === 'transfer' && - action.asset?.canonicalAssetId === position.asset?.canonicalAssetId, + action.asset?.assetId === position.asset?.assetId, ); } @@ -315,7 +315,7 @@ export class AssetFlowComponent implements OnChanges { } trackPosition(index: number, position: ExplorerOutpointPosition): string { - return position.outpoint + ':' + (position.asset?.canonicalAssetId ?? index); + return position.outpoint + ':' + (position.asset?.assetId ?? index); } trackAction(index: number, action: ExplorerAssetAction): string { diff --git a/frontend/src/app/universe/outpoint/outpoint.component.html b/frontend/src/app/universe/outpoint/outpoint.component.html index 19d33dfb960..8ac9dbe2c19 100644 --- a/frontend/src/app/universe/outpoint/outpoint.component.html +++ b/frontend/src/app/universe/outpoint/outpoint.component.html @@ -84,7 +84,7 @@

  • - {{ position.asset?.displayName || position.asset?.ticker || position.asset?.canonicalAssetId }} + {{ position.asset?.displayName || position.asset?.ticker || position.asset?.assetId }} {{ amount }}
    diff --git a/frontend/src/app/universe/outpoint/outpoint.component.ts b/frontend/src/app/universe/outpoint/outpoint.component.ts index 6de1e55a130..439a0d2ca75 100644 --- a/frontend/src/app/universe/outpoint/outpoint.component.ts +++ b/frontend/src/app/universe/outpoint/outpoint.component.ts @@ -144,6 +144,6 @@ export class OutpointComponent implements OnInit, OnDestroy { } trackByPosition(index: number, position: ExplorerOutpointPosition): string { - return `${position.outpoint}:${position.asset?.canonicalAssetId ?? index}`; + return `${position.outpoint}:${position.asset?.assetId ?? index}`; } } diff --git a/frontend/src/app/universe/universe.types.ts b/frontend/src/app/universe/universe.types.ts index 41720d9e9cc..0d8a5022416 100644 --- a/frontend/src/app/universe/universe.types.ts +++ b/frontend/src/app/universe/universe.types.ts @@ -83,7 +83,7 @@ export interface BackendInfo { export interface ExplorerAssetRef { protocolId: string; - canonicalAssetId: string; + assetId: string; displayName?: string; ticker?: string; assetKind: string; diff --git a/scripts/universe/check-text.mjs b/scripts/universe/check-text.mjs index dea16a3f48b..df908b3b5d3 100644 --- a/scripts/universe/check-text.mjs +++ b/scripts/universe/check-text.mjs @@ -1,11 +1,15 @@ #!/usr/bin/env node /** - * Text gate: no em dash anywhere. + * Text gate: no em dash anywhere, and no "canonical" in our own vocabulary. * * U+2014 is banned in this repository's source, copy, documentation, tests, * fixtures, and metadata. The rule is easy to break by accident, so it is * enforced rather than remembered. * + * The word "canonical" is banned inside the Universe-authored source, where we + * choose the vocabulary. It is not policed across the inherited upstream tree, + * which uses the term for the HTML rel=canonical link standard and elsewhere. + * * Usage: * node scripts/universe/check-text.mjs [path ...] * @@ -46,6 +50,11 @@ const TEXT_EXTENSIONS = new Set([ // Built from its code point so this file does not contain the character it bans. const EM_DASH = String.fromCharCode(0x2014); +// The word is only policed where we author the vocabulary. Built from a pattern +// so this gate file, which must name the word to ban it, is not itself a hit. +const UNIVERSE_SOURCE_PREFIX = 'frontend/src/app/universe/'; +const CANONICAL_WORD = new RegExp(['can', 'onical'].join(''), 'i'); + function isSkipped(name) { return SKIPPED_DIRECTORIES.has(name); } @@ -81,11 +90,18 @@ function findings(file) { } catch { return []; } - if (!contents.includes(EM_DASH)) return []; + const posix = relativePath.split(sep).join('/'); + const policeCanonical = posix.startsWith(UNIVERSE_SOURCE_PREFIX); + if (!contents.includes(EM_DASH) && !(policeCanonical && CANONICAL_WORD.test(contents))) { + return []; + } const hits = []; contents.split(/\r?\n/).forEach((line, index) => { if (line.includes(EM_DASH)) { - hits.push({ file: relativePath, line: index + 1, text: line.trim().slice(0, 120) }); + hits.push({ kind: 'em dash', file: relativePath, line: index + 1, text: line.trim().slice(0, 120) }); + } + if (policeCanonical && CANONICAL_WORD.test(line)) { + hits.push({ kind: 'canonical', file: relativePath, line: index + 1, text: line.trim().slice(0, 120) }); } }); return hits; @@ -107,14 +123,15 @@ for (const target of targets(process.argv.slice(2))) { } if (problems.length > 0) { - console.error(`Em dash found in ${problems.length} place(s). Use a colon, a comma, or two sentences.`); + console.error(`Text gate found ${problems.length} banned item(s):`); for (const problem of problems.slice(0, 50)) { - console.error(` ${problem.file}:${problem.line}: ${problem.text}`); + console.error(` ${problem.kind} ${problem.file}:${problem.line}: ${problem.text}`); } if (problems.length > 50) { console.error(` ... and ${problems.length - 50} more`); } + console.error('Replace an em dash with a colon, a comma, or two sentences. Rename anything using "canonical".'); process.exit(1); } -console.log('Text gate passed: no em dash found.'); +console.log('Text gate passed: no em dash, no "canonical" in Universe source.');