Setting the file. One moment.
Read Back Summary · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page Bundled file
lib/ read-back-summary.js
JavaScript · 83 lines · 7 KB
Object.
fromEntries
(keys.
filter
(
key
=>
typeof
object?.[key]
===
'number'
&&
Number.
isFinite
(object[key])).
map
(
key
=>
[key, Math.
max
(
0
, object[key])]));
6 function identity ( value ) {
7 if ( ! value) return null ;
8 return { userId: value.userId || null , displayName: value.displayName || null , source: value.source || null , observedAt: value.observedAt || null };
9 }
10 function acknowledgement ( value ) {
11 if ( ! value) return { status: 'pending' };
12 return { status: value.status, requestId: value.requestId || null , answeredAt: value.answeredAt || null , recordedBy: identity (value.recordedBy) };
13 }
14 function currentEntries ( gate ) {
15 return Object. entries (gate.runs || {}). flatMap (([ runId , run ]) => Object. entries (run.entities || {}). filter (([ entity , item ]) => ! (item.supersededBy && gate.runs[item.supersededBy]?.entities?.[entity])). map (([ entity , item ]) => ({ runId, entity, item })));
16 }
17 function projectReadBackSummary ( gate , { entity : filter } = {}) {
18 const runs = new Map ();
19 let observedLossCount = 0 ;
20 for ( const { runId , entity , item } of currentEntries (gate)) {
21 if (filter && filter !== entity) continue ;
22 if ( ! runs. has (runId)) runs. set (runId, []);
23 if (item.gated === false ) {
24 const declared = require ( './read-back-gate' ). declaration (entity, item);
25 if (declared.gated) throw new Error ( 'Gated entity cannot be reported as a non-gated declaration' );
26 runs. get (runId). push ({ ... declared, status: 'not-covered' });
27 continue ;
28 }
29 const guardDropped = item.guardDropped || [];
30 const guardPaths = [ ...new Set (guardDropped. map ( row => row.path). filter (Boolean))]. sort ();
31 const fieldCounts = Object. fromEntries ( FIELD_CATEGORIES . map ( kind => [kind, kind === 'guardDropped' ? guardPaths. length : new Set (item.paths?.[kind] || []).size]));
32 const losses = (gate.observedLosses || []). filter ( row => row.entity === entity);
33 observedLossCount += losses. length ;
34 runs. get (runId). push ({
35 entity, gated: true , status: item.status, stopReason: item.stopReason || null ,
36 canaryCount: item.canaries?. length || 0 , endOfRunVerified: item.endOfRunVerified === true ,
37 fieldCounts, observedPathCounts: fieldCounts,
38 guardDropped: { recordCount: new Set (guardDropped. map ( row => row.sourceId)).size, paths: guardPaths },
39 observedLossCount: losses. length ,
40 ... (item.heldRecords?.count ? { heldRecords: { count: item.heldRecords.count, reasons: { ... item.heldRecords.reasons } } } : {}),
41 approvedOmissions: (item.approvedOmissions || []). map ( row => ({ path: row.path, kind: row.kind, recordCount: row.recordCount, approvedBy: identity (row.approvedBy), merchantAcknowledgement: acknowledgement (row.merchantAcknowledgement) })),
42 pendingSettlementCount: (item.settlements || []). filter ( row => row.status !== 'passed' ). length ,
43 timing: numeric (item.timing, [ 'elapsedMs' , 'preparationMs' , 'writeMs' , 'activeReadMs' ]),
44 retryStats: numeric (item.retryStats, [ 'attempts' , 'timeouts' , 'readMs' , 'pacingMs' , 'retryMs' , 'settlementMs' , 'remainingMs' ])
45 });
46 }
47 return { schemaVersion: 1 , status: runs.size ? 'recorded' : 'not-run' , observedLossCount, runs: [ ... runs]. map (([ runId , entities ]) => ({ runId, entities: entities. sort (( a , b ) => a.entity. localeCompare (b.entity)) })) };
48 }
49 async function recordDeclaration ( projectDir , runId , entity , options ) {
50 const declared = require ( './read-back-gate' ). declaration (entity, options);
51 if (declared.gated) throw new Error ( 'Gated entity requires ReadBackGate; a declaration cannot bypass it' );
52 if ( typeof runId !== 'string' || ! runId. trim () || typeof entity !== 'string' || ! entity. trim ()) throw new Error ( 'Declaration requires run and entity' );
53 return state. update (projectDir, gate => {
54 // Never overwrite earlier gate evidence, including a stopped or superseded row.
55 const history = Object. values (gate.runs). flatMap ( run => Object. entries (run.entities || {})). filter (([ name ]) => name === entity);
56 if (history. some (([, item ]) => item.gated !== false || item.status !== 'not-covered' )) throw new Error ( 'Declaration cannot replace existing gate history' );
57 for ( const [ previousId , previous ] of Object. entries (gate.runs)) {
58 if (previousId !== runId && Object. hasOwn (previous.entities || {}, entity)) previous.entities[entity].supersededBy = runId;
59 }
60 const run = Object. hasOwn (gate.runs, runId) ? gate.runs[runId] : { entities: {} };
61 gate.runs = { ... gate.runs, [runId]: { ... run, entities: { ... run.entities, [entity]: { ... declared, status: 'not-covered' } } } };
62 return declared;
63 });
64 }
65 function renderReadBackSummary ( summary ) {
66 const lines = [ '' , '## Read-Back Verification' , '' , `- Status: ${ summary . status }.` ];
67 for ( const run of summary.runs) for ( const item of run.entities) {
68 lines. push ( '' , `### ${ item . entity } (${ run . runId })` , '' );
69 if ( ! item.gated) {
70 lines. push ( `- Outside the four-entity field gate; verification: ${ item . verificationKind }; reason: ${ item . reason }.` );
71 continue ;
72 }
73 lines. push ( `- Gate: ${ item . status }; final record verified: ${ item . endOfRunVerified ? 'yes' : 'no'}${ item . stopReason ? `; reason: ${ item . stopReason }` : ''}.` );
74 lines. push ( `- Field counts: ${ Object . entries ( item . fieldCounts ). map (([ key , value ]) => `${ key }: ${ value }` ). join ( '; ' ) }.` );
75 if (item.heldRecords) lines. push ( `- Held by the builder (not attempted): ${ item . heldRecords . count } records; ${ Object . entries ( item . heldRecords . reasons ). map (([ reason , n ]) => `${ reason }: ${ n }` ). join ( '; ' ) }.` );
76 lines. push ( `- Guard-dropped: ${ item . guardDropped . recordCount } records; ${ item . guardDropped . paths . length } paths. Pending settlements: ${ item . pendingSettlementCount }.` );
77 lines. push ( `- Timing: ${ Object . entries ( item . timing ). map (([ key , value ]) => `${ key }: ${ value } ms` ). join ( '; ' ) || 'not recorded'}.` );
78 lines. push ( `- Read activity: ${ Object . entries ( item . retryStats ). map (([ key , value ]) => `${ key }: ${ value }` ). join ( '; ' ) || 'not recorded'}.` );
79 for ( const omission of item.approvedOmissions) lines. push ( `- Approved omission: ${ omission . path } (${ omission . kind }, ${ omission . recordCount == null ? 'unknown' : omission . recordCount } records); approved by ${ omission . approvedBy ?. displayName || 'not recorded'} (${ omission . approvedBy ?. userId || 'no stable id'}); merchant acknowledgement: ${ omission . merchantAcknowledgement . status }.` );
80 }
81 return lines;
82 }
83 module . exports = { FIELD_CATEGORIES, currentEntries, projectReadBackSummary, recordDeclaration, renderReadBackSummary };