Setting the file. One moment.
Crosswalk Adoption · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page lib/ crosswalk-adoption.js
JavaScript · 100 lines · 6 KB
const
fs
=
require
(
'node:fs'
);
15 const path = require ( 'node:path' );
16 const local = require ( './local-state' );
17 const { readEntityMappings } = require ( './website-handoff' );
18
19 const ADOPT_ONLY = 'adopt-only-no-writes' ;
20 const SECTION_SUFFIX = '_candidate' ;
21 const readJson = ( file , fallback ) => {
22 try { return JSON . parse (fs. readFileSync (file, 'utf8' )); } catch (error) { if (error.code === 'ENOENT' && fallback !== undefined ) return fallback; throw error; }
23 };
24
25 // `gid://<app>/<Type>/<id>[?query]` -> `<id>`; any other id is kept as a string.
26 function normalizeSourceId ( id ) {
27 const text = String (id);
28 const match = / ^ gid: \/\/ [ ^ /] + \/ [ ^ /] + \/ ( [ ^ /?] + )(?: \? . * ) ?$ / . exec (text);
29 return match ? match[ 1 ] : text;
30 }
31
32 // One binding per adopted ref: the quick sections that hold its rows and the exact key the standard
33 // importer uses for it (`keyPrefix + id`, as the readiness gate builds it from the task's source binding).
34 function deriveBindings ( projectDir , { contract , sourceSystem } = {}) {
35 const plan = readJson (path. join (projectDir, 'mapping' , 'mapping-plan.json' ));
36 const manifest = readJson (path. join (projectDir, 'execution' , 'execution-manifest.json' ), { tasks: [] });
37 const bound = new Map ();
38 for ( const task of manifest.tasks || []) if (task.adoptOnly === true ) for ( const source of task.sourceBindings || []) bound. set (source.ref, source);
39 const platform = readJson (path. join (projectDir, 'source-schema.json' ), {}).platform;
40 const bindings = [];
41 for ( const row of readEntityMappings (plan)) {
42 if (row.writeMode !== ADOPT_ONLY || typeof row.targetRef !== 'string' || typeof row.sourceEntity !== 'string' ) continue ;
43 const entity = (contract?.entities || []). find ( candidate => candidate.target === row.targetRef);
44 if ( ! entity) continue ;
45 // The manifest's binding wins when codegen has run; before that the convention `<sourceEntity>:` applies.
46 const source = bound. get (row.targetRef);
47 bindings. push ({
48 ref: row.targetRef, sections: [entity.id, entity.id + SECTION_SUFFIX ],
49 sourceSystem: sourceSystem || platform || 'source' , sourceEntityType: source?.sourceEntity || row.sourceEntity,
50 targetEntityType: row.targetRef. split ( '/' )[ 1 ], keyPrefix: source?.keyPrefix || `${ row . sourceEntity }:` ,
51 });
52 }
53 return bindings;
54 }
55
56 function entries ( crosswalk , binding ) {
57 const found = new Map (), candidateOnly = new Set (); let skippedEmpty = 0 ;
58 for ( const [ index , section ] of (binding.sections || []). entries ()) {
59 for ( const [ rawId , targetId ] of Object. entries (crosswalk[section] || {})) {
60 if ( typeof targetId !== 'string' || ! targetId) { skippedEmpty ++ ; continue ; }
61 const id = normalizeSourceId (rawId);
62 if (found. has (id)) { if (found. get (id) !== targetId) found. set (id, null ); continue ; }
63 found. set (id, targetId); if (index > 0 ) candidateOnly. add (id);
64 }
65 }
66 return { found, candidateOnly, skippedEmpty };
67 }
68
69 async function adoptCrosswalk ({ projectDir , sourceFile , bindings , siteId , dryRun = false , adoptedFrom = 'quick-mode' , now = new Date () }) {
70 if ( ! projectDir || ! sourceFile || ! Array. isArray (bindings) || ! bindings. length ) throw new Error ( 'crosswalk_adoption_input_invalid' );
71 if ( ! dryRun && ! siteId) throw new Error ( 'crosswalk_adoption_site_required' );
72 const crosswalk = readJson (sourceFile, undefined );
73 const existing = await local. loadCrosswalk (projectDir);
74 const claimed = new Map (existing.rows. map ( row => [row.targetId, row.sourceStableKey]));
75 const rows = [], summary = { dryRun, adoptedFrom, bindings: [], conflicts: [] };
76 for ( const binding of bindings) {
77 for ( const field of [ 'ref' , 'sourceSystem' , 'sourceEntityType' , 'targetEntityType' , 'keyPrefix' ]) if ( typeof binding[field] !== 'string' || ! binding[field]) throw new Error ( `crosswalk_adoption_binding_invalid:${ field }` );
78 const { found , candidateOnly , skippedEmpty } = entries (crosswalk, binding);
79 const sharedTarget = new Map (); for ( const targetId of found. values ()) if (targetId) sharedTarget. set (targetId, (sharedTarget. get (targetId) || 0 ) + 1 );
80 const count = { ref: binding.ref, keyPrefix: binding.keyPrefix, seeded: 0 , alreadyAdopted: 0 , candidateOnly: 0 , skippedEmpty, conflicts: 0 };
81 for ( const [ id , targetId ] of [ ... found]. sort (([ a ], [ b ]) => a. localeCompare (b))) {
82 const sourceStableKey = binding.keyPrefix + id, present = existing.bySource[sourceStableKey];
83 const conflict = ! targetId ? 'source_section_disagree' : sharedTarget. get (targetId) > 1 ? 'target_shared_by_sources'
84 : present && present.targetId !== targetId ? 'existing_row_differs' : ! present && claimed. has (targetId) ? 'target_claimed_by_other_row' : null ;
85 if (conflict) { count.conflicts ++ ; summary.conflicts. push ({ ref: binding.ref, sourceStableKey, reason: conflict }); continue ; }
86 if (present) { count.alreadyAdopted ++ ; continue ; }
87 count.seeded ++ ; if (candidateOnly. has (id)) count.candidateOnly ++ ;
88 claimed. set (targetId, sourceStableKey);
89 rows. push ({ schemaVersion: 1 , sourceSystem: binding.sourceSystem, sourceEntityType: binding.sourceEntityType, sourceId: id, sourceStableKey,
90 targetSystem: 'wix' , targetEntityType: binding.targetEntityType, targetId, status: 'imported' , ... (siteId ? { siteId } : {}),
91 updatedAt: now. toISOString (), adoptedFrom, adoptedSection: candidateOnly. has (id) ? binding.sections[ 1 ] : binding.sections[ 0 ] });
92 }
93 summary.bindings. push (count);
94 }
95 if ( ! dryRun && rows. length ) await local. updateCrosswalkRows (projectDir, rows);
96 summary.seeded = rows. length ; summary.wrote = ! dryRun && rows. length > 0 ; summary.ok = summary.conflicts. length === 0 ;
97 return summary;
98 }
99
100 module . exports = { adoptCrosswalk, deriveBindings, normalizeSourceId };