Setting the file. One moment.
Resolve Mapping · Rp Mapper · wix/skills · Skills Docs
ContentsBack to the top of the page scripts/resolve-mapping.js
scripts/ resolve-mapping.js
JavaScript · 148 lines · 7 KB
15 //
16 // Exit codes: 0 resolved (residue may be non-empty); 2 blocked (a required Wix input is unfilled,
17 // or an overlay names a canonical field that does not exist).
18
19 const fs = require ( 'fs' );
20 const path = require ( 'path' );
21 const { resolveMapping } = require ( '../lib/mapping-resolve.js' );
22
23 function parseArgs ( argv ) {
24 const args = { fileset: null , out: null , headers: null , vendor: null , json: false };
25 for ( let i = 0 ; i < argv. length ; i += 1 ) {
26 const next = () => argv[i + 1 ];
27 if (argv[i] === '--fileset' ) { args.fileset = next (); i += 1 ; }
28 else if (argv[i] === '--out' ) { args.out = next (); i += 1 ; }
29 else if (argv[i] === '--headers' ) { args.headers = next (); i += 1 ; }
30 else if (argv[i] === '--vendor' ) { args.vendor = next (); i += 1 ; }
31 else if (argv[i] === '--json' ) args.json = true ;
32 }
33 return args;
34 }
35
36 // The fileset is the discovery capture's machine output. Header and vendor are read from the
37 // primary product stream, which is where the capture records what it actually saw.
38 function fromFileset ( filesetPath ) {
39 const fileset = JSON . parse (fs. readFileSync (filesetPath, 'utf8' ));
40 const streams = fileset.streams || [];
41 const stream = streams. find (( s ) => s.role === 'product' ) || streams[ 0 ];
42 if ( ! stream) throw new Error ( `${ filesetPath }: no streams recorded` );
43 const vendor = (stream.vendor && stream.vendor.name) || 'custom' ;
44 let headers = stream.header || stream.columns || null ;
45 if ( ! headers) {
46 // Older captures record the header per source file rather than per stream.
47 const sourceFile = (fileset.sourceFiles || []). find (( f ) => f.role === stream.role);
48 headers = (sourceFile && (sourceFile.header || sourceFile.columns)) || null ;
49 }
50 if ( ! headers) {
51 // Captures written before fileset.json carried `header` still have it in the sibling
52 // raw-capture.json. Fall back rather than force a re-run of discovery.
53 const raw = path. join (path. dirname (filesetPath), 'raw-capture.json' );
54 if (fs. existsSync (raw)) {
55 const rawDoc = JSON . parse (fs. readFileSync (raw, 'utf8' ));
56 const rawStream = (rawDoc.streams || []). find (( s ) => s.role === stream.role) || (rawDoc.streams || [])[ 0 ];
57 if (rawStream) {
58 headers = rawStream.header
59 || (Array. isArray (rawStream.columns) ? rawStream.columns. map (( c ) => c.name). filter (Boolean) : null );
60 }
61 }
62 }
63 if ( ! headers) {
64 throw new Error (
65 `${ filesetPath }: the capture did not record a header list for role "${ stream . role }", and no ` +
66 'sibling raw-capture.json supplied one. Pass --headers explicitly, or re-run csv-discovery.' ,
67 );
68 }
69 return { headers, vendor };
70 }
71
72 function main () {
73 const args = parseArgs (process.argv. slice ( 2 ));
74 let headers;
75 let vendor;
76
77 if (args.fileset) ({ headers, vendor } = fromFileset (args.fileset));
78 else if (args.headers) { headers = args.headers. split ( ',' ). map (( h ) => h. trim ()); vendor = args.vendor || 'custom' ; }
79 else {
80 console. error ( 'resolve-mapping: pass --fileset <path> or --headers "A,B,C"' );
81 process. exit ( 64 );
82 }
83 if (args.vendor) vendor = args.vendor;
84
85 const resolved = resolveMapping ({ headers, vendor });
86 const blocked = resolved.residue.unfilledRequired. length > 0 || resolved.canonicalErrors. length > 0 ;
87
88 if (args.json) {
89 console. log ( JSON . stringify (resolved, null , 2 ));
90 } else {
91 const c = resolved.coverage;
92 console. log ( `vendor ${ resolved . vendor }${ resolved . overlayVersion ? ` (profile ${ resolved . overlayVersion })` : ''}` );
93 console. log ( ` columns : ${ c . totalColumns }` );
94 console. log ( ` mapped by overlay: ${ c . mappedColumns } (${ ( c . ratio * 100 ). toFixed ( 1 ) }%)` );
95 console. log ( ` landing in Wix : ${ c . landingInWix }` );
96 console. log ( ` residue for agent: ${ resolved . residue . unmappedColumns . length } unmapped, ${ resolved . residue . unsupportedTargets . length } with no Wix home` );
97 if (resolved.derivedEntities. length ) {
98 console. log ( ` derived entities : ${ resolved . derivedEntities . map (( d ) => `${ d . entity }${ d . present ? '' : ' (column absent)'}` ). join ( ', ' ) }` );
99 }
100 if (resolved.overlayDrift. length ) console. log ( ` overlay drift : ${ resolved . overlayDrift . length } declared field(s) not in this header` );
101 if (resolved.duplicateColumns. length ) console. log ( ` DUPLICATE columns: ${ resolved . duplicateColumns . join ( ', ' ) }` );
102 if (resolved.canonicalErrors. length ) {
103 console. log ( ' CANONICAL ERRORS (overlay bug — fix the overlay, do not work around it):' );
104 for ( const e of resolved.canonicalErrors) console. log ( ` - ${ e . wixTarget }: ${ e . reason }` );
105 }
106 if (resolved.residue.unfilledRequired. length ) {
107 console. log ( ' BLOCKED — required Wix inputs nothing feeds:' );
108 for ( const f of resolved.residue.unfilledRequired) console. log ( ` - ${ f }` );
109 }
110 if (resolved.residue.unmappedColumns. length ) {
111 console. log ( ' --- residue: decide each of these (the agentic step) ---' );
112 for ( const col of resolved.residue.unmappedColumns) console. log ( ` ? ${ col }` );
113 }
114 }
115
116 if (args.out) {
117 fs. mkdirSync (args.out, { recursive: true });
118 const now = new Date (). toISOString ();
119 fs. writeFileSync (
120 path. join (args.out, 'mapping-resolution.json' ),
121 `${ JSON . stringify ({ schemaVersion: 1 , generatedAt: now , generator: 'rp-mapper/resolve-mapping.js' , deterministic: true , ... resolved }, null , 2 ) } \n ` ,
122 );
123 // The residue is written as its own artifact so the agentic step has a single, explicit queue
124 // instead of having to re-derive "what is left" from the full resolution.
125 fs. writeFileSync (
126 path. join (args.out, 'mapping-residue.json' ),
127 `${ JSON . stringify (
128 {
129 schemaVersion: 1 ,
130 generatedAt: now ,
131 note: 'Every entry needs an explicit decision: map it to a canonical field, or record it as intentionally skipped. Leaving it here is how a stale overlay becomes silently dropped data.' ,
132 vendor: resolved.vendor ,
133 unmappedColumns: resolved.residue.unmappedColumns ,
134 unsupportedTargets: resolved.residue.unsupportedTargets ,
135 unfilledRequired: resolved.residue.unfilledRequired ,
136 overlayDrift: resolved.overlayDrift ,
137 },
138 null ,
139 2 ,
140 ) } \n ` ,
141 );
142 console. log ( ` \n wrote mapping-resolution.json + mapping-residue.json to ${ args . out }` );
143 }
144
145 process. exit (blocked ? 2 : 0 );
146 }
147
148 main ();