v3.13.1: expanded-registry pages try every command format

The explorer's next-occurrence page (P2=03) is retried in the other
command formats (ramPageFormats: the expanded codings first - the serving
one first when it is expanded - and the compact command string last,
because a compact page attempt can consume the page without delivering
it) and the format that serves a page is kept for the following ones.
Live 2026-10-02 (test-eSIM card): a compact page 2 answered 61B6 and the
chained GET RESPONSE 6A86, after which the AA and AE 80 retries both saw
the occurrence already gone; the AA-wrapped page returns 182 B with the
remaining app instances' CE/CC/EA, while AE 80 is refused.  A page that
no format serves ends the listing.

Only an explicit 6310 promise counts for the "(first page only)" flag
(ramExpandedMore): a probe after a 61xx page that every format refuses is
the card's final answer, so that card's ELF listing is no longer flagged
(its expanded registry has three entries; the fourth package exists only
in the compact listing - the ELF tag-list forms and per-AID queries are
refused in every format).

In Auto the explorer also queries entries the bulk listing left without
expanded fields individually by AID (ramAppsMissingDetails, <= 6 packets,
the compact command string carries the GET RESPONSE for 61xx).

Docs: help EN/RU, repo AGENTS (v3.13.1 note).  sw cache simple-v371.
Tests: 667 Python + 794 frontend green.
This commit is contained in:
2026-10-02 12:29:48 +03:00
parent 4f0b2a743c
commit 5b8324ed02
7 changed files with 153 additions and 37 deletions
+27 -1
View File
@@ -26,7 +26,7 @@ const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamB
'ramFormatIsExpanded', 'ramFmtLadder',
'stkParamsBuild', 'ramRemoteSwOk', 'spPorAccepted', 'ramIncrementCntr', 'ramDeleteFromExplorer', 'ramListingSpi2', 'ramRemoveFromExplorer', 'ramHasInstance', 'ramExpandedQueryApdu',
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'ramParseModuleAids', 'parseTLV', '_parseE3Entry', '_parseMenuEntries',
'ramExpandedReport', 'ramExpandedMore', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate',
'ramExpandedReport', 'ramExpandedMore', 'ramPageFormats', 'ramAppsMissingDetails', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate',
'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow',
'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'spPresetIdx', 'tarPresetIdx',
@@ -1628,4 +1628,30 @@ test('ramExpandedMore marks only an unfulfilled 6310', () => {
assert.strictEqual(ramExpandedMore(true, '6310', 8), true);
// a transport failure (no remote status word) leaves it unfulfilled
assert.strictEqual(ramExpandedMore(true, 'index out of range', 2), true);
// a probe after a '61xx' page (nothing promised) never flags the listing
assert.strictEqual(ramExpandedMore(false, '6A86', 2), false);
});
test('ramPageFormats tries the expanded codings first, compact last', () => {
// the compact command string is last: a compact page attempt can consume
// the page without delivering it (the live card's 61xx + failed chain)
assert.deepStrictEqual(ramPageFormats('compact'), ['expanded', 'expanded-ae', 'compact']);
assert.deepStrictEqual(ramPageFormats('expanded'), ['expanded', 'expanded-ae', 'compact']);
assert.deepStrictEqual(ramPageFormats('expanded-ae'), ['expanded-ae', 'expanded', 'compact']);
// the run format ('auto') is never a page format: the server's detection
// would cost an extra packet per page
assert.deepStrictEqual(ramPageFormats('auto'), ['expanded', 'expanded-ae', 'compact']);
assert.deepStrictEqual(ramPageFormats(null), ['expanded', 'expanded-ae', 'compact']);
});
test('ramAppsMissingDetails picks only the entries without expanded fields', () => {
assert.deepStrictEqual(ramAppsMissingDetails([
{ aid: 'A1', version: '1.0' },
{ aid: 'A2' },
{ aid: 'A3', sdAid: 'AA' },
{ aid: 'A4', menuEntries: [{ position: 1 }] },
{ aid: 'A5', rawTlvs: ['8F'] },
{ aid: '' },
]).map(e => e.aid), ['A2']);
assert.deepStrictEqual(ramAppsMissingDetails(null), []);
});