@@ -304,3 +304,96 @@ export function buildPageVariablesExport(
304304 } ) ) ;
305305}
306306
307+ export interface ImportCandidate {
308+ name : string ;
309+ sourceValue : string ;
310+ tagType : string ;
311+ }
312+
313+ export interface ImportSkip {
314+ name : string ;
315+ sourceValue : string ;
316+ reason : string ;
317+ }
318+
319+ export interface PageVariablesImportPlan {
320+ importable : ImportCandidate [ ] ;
321+ skipped : ImportSkip [ ] ;
322+ }
323+
324+ /**
325+ * Whether `value` can structurally apply to `tag`'s type — a hard yes/no, unlike
326+ * `validateValueAgainstTag()`'s always-a-warning checks. Deliberately skips select/multiselect's
327+ * declared-option check: two pages can legitimately point a same-named select at different
328+ * datasets/sources, so an option-list mismatch doesn't mean the value can't apply.
329+ */
330+ function typeCompatibilityIssue ( tag : TemplateVariableTag , value : string ) : string | null {
331+ switch ( tag . tag_type ) {
332+ case 'boolean' :
333+ return BOOLEAN_ALLOWED_VALUES . includes ( value )
334+ ? null
335+ : `Not a recognized boolean value ("${ value } ").` ;
336+
337+ case 'color' :
338+ return value === '' || COLOR_PATTERN . test ( value ) ? null : `Not a recognized color value ("${ value } ").` ;
339+
340+ case 'list' : {
341+ if ( value === '' ) {
342+ return null ;
343+ }
344+
345+ try {
346+ const parsed = JSON . parse ( value ) ;
347+
348+ return parsed === null || Array . isArray ( parsed ) ? null : 'Value is valid JSON but not an array.' ;
349+ } catch {
350+ return 'Value is not valid JSON.' ;
351+ }
352+ }
353+
354+ default :
355+ return null ;
356+ }
357+ }
358+
359+ /**
360+ * Plans an import of `sourceValues` (another page's live variable values) into the current
361+ * page's schema: matches by variable name first (anything unmatched is skipped outright), then
362+ * checks the matched value against the target tag's type. Nothing here writes anything — the
363+ * caller decides what to actually apply.
364+ */
365+ export function planPageVariablesImport (
366+ schema : TemplateVariablesSchema | null ,
367+ sourceValues : PageVariableEntry [ ] ,
368+ ) : PageVariablesImportPlan {
369+ const schemaMap = new Map < string , TemplateVariableTag > ( ( schema ?. tags ?? [ ] ) . map ( ( tag ) => [ tag . name , tag ] ) ) ;
370+ const importable : ImportCandidate [ ] = [ ] ;
371+ const skipped : ImportSkip [ ] = [ ] ;
372+
373+ for ( const entry of sourceValues ) {
374+ const tag = schemaMap . get ( entry . name ) ;
375+
376+ if ( ! tag ) {
377+ skipped . push ( {
378+ name : entry . name ,
379+ sourceValue : entry . value ,
380+ reason : "Not in the current page's schema." ,
381+ } ) ;
382+
383+ continue ;
384+ }
385+
386+ const issue = typeCompatibilityIssue ( tag , entry . value ) ;
387+
388+ if ( issue ) {
389+ skipped . push ( { name : entry . name , sourceValue : entry . value , reason : issue } ) ;
390+
391+ continue ;
392+ }
393+
394+ importable . push ( { name : entry . name , sourceValue : entry . value , tagType : tag . tag_type || 'text' } ) ;
395+ }
396+
397+ return { importable, skipped } ;
398+ }
399+
0 commit comments