@willwade/aac-processors 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -360,8 +360,26 @@ function getHighContrastNeutralColor(backgroundColor) {
360
360
  }
361
361
  return calculateLuminance(normalized) < 0.5 ? '#f5f5f5' : '#808080';
362
362
  }
363
+ function isRecord(value) {
364
+ return typeof value === 'object' && value !== null;
365
+ }
366
+ function normalizeStringRecord(input) {
367
+ if (!isRecord(input)) {
368
+ return undefined;
369
+ }
370
+ const entries = [];
371
+ Object.entries(input).forEach(([key, value]) => {
372
+ if (typeof value === 'string') {
373
+ entries.push([key, value]);
374
+ }
375
+ });
376
+ if (entries.length === 0) {
377
+ return undefined;
378
+ }
379
+ return Object.fromEntries(entries);
380
+ }
363
381
  function normalizeColorScheme(raw) {
364
- if (!raw || typeof raw !== 'object')
382
+ if (!isRecord(raw))
365
383
  return null;
366
384
  const scheme = raw;
367
385
  const nameCandidate = [scheme.name, scheme.key, scheme.id].find((value) => typeof value === 'string' && value.length > 0);
@@ -373,21 +391,22 @@ function normalizeColorScheme(raw) {
373
391
  categories = scheme.categories.filter((value) => typeof value === 'string');
374
392
  colors = scheme.colors.filter((value) => typeof value === 'string');
375
393
  }
376
- else if (scheme.colorMap && typeof scheme.colorMap === 'object') {
377
- categories = Object.keys(scheme.colorMap);
394
+ else if (isRecord(scheme.colorMap)) {
395
+ const colorMap = scheme.colorMap;
396
+ categories = Object.keys(colorMap);
378
397
  colors = categories.map((category) => {
379
- const colorValue = scheme.colorMap[category];
398
+ const colorValue = colorMap[category];
380
399
  return typeof colorValue === 'string' ? colorValue : '#ffffff';
381
400
  });
382
401
  }
383
402
  if (!categories.length || !colors.length) {
384
403
  return null;
385
404
  }
386
- const mappingsCandidate = (typeof scheme.mappings === 'object' && scheme.mappings) ||
387
- (typeof scheme.categoryMappings === 'object' && scheme.categoryMappings) ||
388
- (typeof scheme.categoryMapping === 'object' && scheme.categoryMapping) ||
405
+ const mappingsCandidate = normalizeStringRecord(scheme.mappings) ||
406
+ normalizeStringRecord(scheme.categoryMappings) ||
407
+ normalizeStringRecord(scheme.categoryMapping) ||
389
408
  undefined;
390
- const customBordersCandidate = typeof scheme.customBorders === 'object' && scheme.customBorders ? scheme.customBorders : undefined;
409
+ const customBordersCandidate = normalizeStringRecord(scheme.customBorders);
391
410
  return {
392
411
  name: nameCandidate,
393
412
  categories,
@@ -397,11 +416,12 @@ function normalizeColorScheme(raw) {
397
416
  };
398
417
  }
399
418
  function getAllColorSchemeDefinitions(colorConfig) {
400
- const additional = Array.isArray(colorConfig?.additionalColorSchemes)
419
+ const rawAdditional = Array.isArray(colorConfig?.additionalColorSchemes)
401
420
  ? colorConfig.additionalColorSchemes
402
- .map((scheme) => normalizeColorScheme(scheme))
403
- .filter((value) => Boolean(value))
404
421
  : [];
422
+ const additional = rawAdditional
423
+ .map((scheme) => normalizeColorScheme(scheme))
424
+ .filter((value) => Boolean(value));
405
425
  return [...DEFAULT_COLOR_SCHEME_DEFINITIONS, ...additional];
406
426
  }
407
427
  function getActiveColorSchemeDefinition(colorConfig) {
@@ -412,7 +432,8 @@ function getActiveColorSchemeDefinition(colorConfig) {
412
432
  if (!schemes.length) {
413
433
  return null;
414
434
  }
415
- const activeName = (typeof colorConfig.activeColorScheme === 'string' && colorConfig.activeColorScheme) || undefined;
435
+ const activeName = (typeof colorConfig.activeColorScheme === 'string' && colorConfig.activeColorScheme) ||
436
+ undefined;
416
437
  const normalizedName = activeName ? COLOR_SCHEME_ALIASES[activeName] || activeName : undefined;
417
438
  if (normalizedName) {
418
439
  const match = schemes.find((scheme) => scheme.name === normalizedName);
@@ -435,11 +456,13 @@ function getSchemeColorForCategory(category, scheme, fallback) {
435
456
  const color = scheme.colors[index];
436
457
  return typeof color === 'string' ? color : fallback;
437
458
  }
438
- function resolveBorderColor(element, colorConfig, scheme, backgroundColor, schemeColor, fallbackBorder) {
459
+ function resolveBorderColor(element, colorConfig = {}, scheme, backgroundColor, schemeColor, fallbackBorder) {
439
460
  const defaultBorderColor = (fallbackBorder || '#808080').toLowerCase();
440
- const colorMode = colorConfig?.colorMode || 'COLOR_MODE_BACKGROUND';
461
+ const colorMode = typeof colorConfig.colorMode === 'string' ? colorConfig.colorMode : 'COLOR_MODE_BACKGROUND';
441
462
  if (colorMode === 'COLOR_MODE_BORDER') {
442
- return (getSchemeColorForCategory(element.colorCategory, scheme, fallbackBorder || '#808080') || fallbackBorder || '#808080');
463
+ return (getSchemeColorForCategory(element.colorCategory, scheme, fallbackBorder || '#808080') ||
464
+ fallbackBorder ||
465
+ '#808080');
443
466
  }
444
467
  if (colorMode === 'COLOR_MODE_BOTH') {
445
468
  if (!element.colorCategory) {
@@ -459,22 +482,24 @@ function resolveBorderColor(element, colorConfig, scheme, backgroundColor, schem
459
482
  if (defaultBorderColor !== '#808080') {
460
483
  return fallbackBorder || '#808080';
461
484
  }
462
- const gridBackground = colorConfig?.gridBackgroundColor || '#ffffff';
485
+ const gridBackground = typeof colorConfig.gridBackgroundColor === 'string'
486
+ ? colorConfig.gridBackgroundColor
487
+ : '#ffffff';
463
488
  return getHighContrastNeutralColor(gridBackground);
464
489
  }
465
- function resolveButtonColors(element, colorConfig, scheme) {
466
- const fallbackBackground = colorConfig?.elementBackgroundColor || '#FFFFFF';
467
- const fallbackBorder = colorConfig?.elementBorderColor || '#808080';
468
- const colorMode = colorConfig?.colorMode || 'COLOR_MODE_BACKGROUND';
490
+ function resolveButtonColors(element, colorConfig = {}, scheme) {
491
+ const fallbackBackground = typeof colorConfig.elementBackgroundColor === 'string'
492
+ ? colorConfig.elementBackgroundColor
493
+ : '#FFFFFF';
494
+ const fallbackBorder = typeof colorConfig.elementBorderColor === 'string' ? colorConfig.elementBorderColor : '#808080';
495
+ const colorMode = typeof colorConfig.colorMode === 'string' ? colorConfig.colorMode : 'COLOR_MODE_BACKGROUND';
469
496
  const isSchemeActive = colorConfig?.colorSchemesActivated !== false;
470
497
  const schemeColor = isSchemeActive && colorMode !== 'COLOR_MODE_BORDER'
471
498
  ? getSchemeColorForCategory(element.colorCategory, scheme || null)
472
499
  : undefined;
473
500
  const backgroundColor = element.backgroundColor || schemeColor || fallbackBackground || '#FFFFFF';
474
- const borderColor = resolveBorderColor(element, colorConfig || {}, scheme || null, backgroundColor, schemeColor, fallbackBorder);
475
- const fontColor = element.fontColor ||
476
- colorConfig?.fontColor ||
477
- getContrastingTextColor(backgroundColor);
501
+ const borderColor = resolveBorderColor(element, colorConfig, scheme || null, backgroundColor, schemeColor, fallbackBorder);
502
+ const fontColor = element.fontColor || colorConfig?.fontColor || getContrastingTextColor(backgroundColor);
478
503
  return {
479
504
  backgroundColor,
480
505
  borderColor,
@@ -584,9 +609,11 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
584
609
  switch (action.modelName) {
585
610
  case 'GridActionSpeakCustom':
586
611
  if (action.speakText && typeof action.speakText === 'object') {
587
- Object.values(action.speakText).forEach((text) => {
588
- if (text && typeof text === 'string')
589
- texts.push(text);
612
+ const speakTextMap = action.speakText;
613
+ Object.values(speakTextMap).forEach((textValue) => {
614
+ if (typeof textValue === 'string' && textValue.length > 0) {
615
+ texts.push(textValue);
616
+ }
590
617
  });
591
618
  }
592
619
  break;
@@ -632,7 +659,10 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
632
659
  if (!grdFile.grids) {
633
660
  return tree;
634
661
  }
635
- const colorConfig = grdFile.metadata?.colorConfig;
662
+ const rawColorConfig = grdFile.metadata?.colorConfig;
663
+ const colorConfig = isRecord(rawColorConfig)
664
+ ? rawColorConfig
665
+ : undefined;
636
666
  const activeColorSchemeDefinition = getActiveColorSchemeDefinition(colorConfig);
637
667
  // First pass: create all pages
638
668
  grdFile.grids.forEach((grid) => {
@@ -683,8 +713,9 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
683
713
  }
684
714
  // Handle navigation relationships
685
715
  const navAction = element.actions.find((a) => a.modelName === 'GridActionNavigate');
686
- if (navAction && navAction.toGridId) {
687
- const targetPage = tree.getPage(navAction.toGridId);
716
+ const targetGridId = navAction && typeof navAction.toGridId === 'string' ? navAction.toGridId : undefined;
717
+ if (targetGridId) {
718
+ const targetPage = tree.getPage(targetGridId);
688
719
  if (targetPage) {
689
720
  targetPage.parentId = page.id;
690
721
  }
@@ -708,8 +739,18 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
708
739
  getLocalizedText(text) {
709
740
  if (typeof text === 'string')
710
741
  return text;
711
- if (typeof text === 'object' && text) {
712
- return text.en || text.de || text.es || Object.values(text)[0] || '';
742
+ if (isRecord(text)) {
743
+ const preferred = ['en', 'de', 'es'];
744
+ for (const lang of preferred) {
745
+ const value = text[lang];
746
+ if (typeof value === 'string' && value.length > 0) {
747
+ return value;
748
+ }
749
+ }
750
+ const fallback = Object.values(text).find((value) => typeof value === 'string' && value.length > 0);
751
+ if (fallback) {
752
+ return fallback;
753
+ }
713
754
  }
714
755
  return '';
715
756
  }
@@ -717,32 +758,29 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
717
758
  let audioRecording;
718
759
  if (this.loadAudio) {
719
760
  const audioAction = element.actions.find((a) => a.modelName === 'GridActionAudio');
720
- if (audioAction && audioAction.dataBase64) {
761
+ if (audioAction && typeof audioAction.dataBase64 === 'string') {
762
+ const parsedId = Number.parseInt(String(audioAction.id), 10);
763
+ const metadata = {};
764
+ if (typeof audioAction.mimeType === 'string') {
765
+ metadata.mimeType = audioAction.mimeType;
766
+ }
767
+ if (typeof audioAction.durationMs === 'number') {
768
+ metadata.durationMs = audioAction.durationMs;
769
+ }
721
770
  audioRecording = {
722
- id: parseInt(audioAction.id) || undefined,
771
+ id: Number.isNaN(parsedId) ? undefined : parsedId,
723
772
  data: Buffer.from(audioAction.dataBase64, 'base64'),
724
- identifier: audioAction.filename,
725
- metadata: JSON.stringify({
726
- mimeType: audioAction.mimeType,
727
- durationMs: audioAction.durationMs,
728
- }),
773
+ identifier: typeof audioAction.filename === 'string' ? audioAction.filename : undefined,
774
+ metadata: JSON.stringify(metadata),
729
775
  };
730
776
  }
731
777
  }
732
778
  const colorStyles = resolveButtonColors(element, colorConfig, activeColorScheme);
733
779
  const navAction = element.actions.find((a) => a.modelName === 'GridActionNavigate');
734
- const targetPageId = navAction ? navAction.toGridId : null;
735
- // Determine button type based on actions
736
- let buttonType = 'SPEAK';
737
- if (targetPageId) {
738
- buttonType = 'NAVIGATE';
739
- }
780
+ const targetPageId = navAction && typeof navAction.toGridId === 'string' ? navAction.toGridId : null;
740
781
  const label = this.getLocalizedLabel(element.label);
741
782
  // Create semantic action from AstericsGrid element
742
783
  let semanticAction;
743
- let legacyAction = null;
744
- // Find the primary action
745
- const primaryAction = element.actions[0]; // AstericsGrid typically has one primary action
746
784
  if (navAction && targetPageId) {
747
785
  semanticAction = {
748
786
  category: treeStructure_1.AACSemanticCategory.NAVIGATION,
@@ -759,10 +797,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
759
797
  targetPageId: targetPageId,
760
798
  },
761
799
  };
762
- legacyAction = {
763
- type: 'NAVIGATE',
764
- targetPageId: targetPageId,
765
- };
766
800
  }
767
801
  else {
768
802
  // Check for other action types
@@ -785,9 +819,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
785
819
  message: 'Delete word',
786
820
  },
787
821
  };
788
- legacyAction = {
789
- type: 'DELETE_WORD',
790
- };
791
822
  break;
792
823
  case 'COLLECT_ACTION_REMOVE_CHAR':
793
824
  semanticAction = {
@@ -804,9 +835,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
804
835
  message: 'Delete character',
805
836
  },
806
837
  };
807
- legacyAction = {
808
- type: 'DELETE_CHARACTER',
809
- };
810
838
  break;
811
839
  case 'COLLECT_ACTION_CLEAR':
812
840
  semanticAction = {
@@ -823,9 +851,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
823
851
  message: 'Clear text',
824
852
  },
825
853
  };
826
- legacyAction = {
827
- type: 'CLEAR_TEXT',
828
- };
829
854
  break;
830
855
  }
831
856
  }
@@ -847,9 +872,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
847
872
  message: 'Go back',
848
873
  },
849
874
  };
850
- legacyAction = {
851
- type: 'GO_BACK',
852
- };
853
875
  break;
854
876
  case 'TO_HOME':
855
877
  semanticAction = {
@@ -866,9 +888,6 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
866
888
  message: 'Go home',
867
889
  },
868
890
  };
869
- legacyAction = {
870
- type: 'GO_HOME',
871
- };
872
891
  break;
873
892
  }
874
893
  }
@@ -916,7 +935,10 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
916
935
  }
917
936
  }
918
937
  // Determine the final background color
919
- const finalBackgroundColor = element.backgroundColor || colorStyles.backgroundColor || colorConfig?.elementBackgroundColor || '#FFFFFF';
938
+ const finalBackgroundColor = element.backgroundColor ||
939
+ colorStyles.backgroundColor ||
940
+ colorConfig?.elementBackgroundColor ||
941
+ '#FFFFFF';
920
942
  // Determine font color with priority:
921
943
  // 1. Explicit element.fontColor (highest priority)
922
944
  // 2. Resolved color from color category
@@ -954,18 +976,18 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
954
976
  id: element.id,
955
977
  label: label,
956
978
  message: label,
957
- targetPageId: targetPageId,
979
+ targetPageId: targetPageId || undefined,
958
980
  semanticAction: semanticAction,
959
981
  audioRecording: audioRecording,
960
982
  image: imageName, // Store image filename/reference
961
- parameters: imageData ? {
962
- ...{ imageData: imageData } // Store actual image data in parameters for conversion
963
- } : undefined,
983
+ parameters: imageData
984
+ ? {
985
+ ...{ imageData: imageData }, // Store actual image data in parameters for conversion
986
+ }
987
+ : undefined,
964
988
  style: {
965
989
  backgroundColor: finalBackgroundColor,
966
- borderColor: colorStyles.borderColor ||
967
- colorConfig?.elementBorderColor ||
968
- '#CCCCCC',
990
+ borderColor: colorStyles.borderColor || colorConfig?.elementBorderColor || '#CCCCCC',
969
991
  borderWidth: colorConfig?.borderWidth || 1,
970
992
  fontFamily: colorConfig?.fontFamily || 'Arial',
971
993
  fontSize: colorConfig?.fontSizePct ? colorConfig.fontSizePct * 16 : 16, // Default to 16px
@@ -996,7 +1018,10 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
996
1018
  Object.keys(grid.label).forEach((lang) => {
997
1019
  const originalText = grid.label[lang];
998
1020
  if (originalText && translations.has(originalText)) {
999
- grid.label[lang] = translations.get(originalText);
1021
+ const translation = translations.get(originalText);
1022
+ if (translation !== undefined) {
1023
+ grid.label[lang] = translation;
1024
+ }
1000
1025
  }
1001
1026
  });
1002
1027
  }
@@ -1007,7 +1032,10 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1007
1032
  Object.keys(element.label).forEach((lang) => {
1008
1033
  const originalText = element.label[lang];
1009
1034
  if (originalText && translations.has(originalText)) {
1010
- element.label[lang] = translations.get(originalText);
1035
+ const translation = translations.get(originalText);
1036
+ if (translation !== undefined) {
1037
+ element.label[lang] = translation;
1038
+ }
1011
1039
  }
1012
1040
  });
1013
1041
  }
@@ -1015,7 +1043,10 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1015
1043
  if (element.wordForms) {
1016
1044
  element.wordForms.forEach((wordForm) => {
1017
1045
  if (wordForm.value && translations.has(wordForm.value)) {
1018
- wordForm.value = translations.get(wordForm.value);
1046
+ const translation = translations.get(wordForm.value);
1047
+ if (translation !== undefined) {
1048
+ wordForm.value = translation;
1049
+ }
1019
1050
  }
1020
1051
  });
1021
1052
  }
@@ -1030,12 +1061,13 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1030
1061
  switch (action.modelName) {
1031
1062
  case 'GridActionSpeakCustom':
1032
1063
  if (action.speakText && typeof action.speakText === 'object') {
1033
- Object.keys(action.speakText).forEach((lang) => {
1034
- const originalText = action.speakText[lang];
1064
+ const speakTextMap = action.speakText;
1065
+ Object.keys(speakTextMap).forEach((lang) => {
1066
+ const originalText = speakTextMap[lang];
1035
1067
  if (typeof originalText === 'string' && translations.has(originalText)) {
1036
1068
  const translation = translations.get(originalText);
1037
- if (translation) {
1038
- action.speakText[lang] = translation;
1069
+ if (translation !== undefined) {
1070
+ speakTextMap[lang] = translation;
1039
1071
  }
1040
1072
  }
1041
1073
  });
@@ -1044,13 +1076,13 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1044
1076
  case 'GridActionChangeLang':
1045
1077
  if (typeof action.language === 'string' && translations.has(action.language)) {
1046
1078
  const translation = translations.get(action.language);
1047
- if (translation) {
1079
+ if (translation !== undefined) {
1048
1080
  action.language = translation;
1049
1081
  }
1050
1082
  }
1051
1083
  if (typeof action.voice === 'string' && translations.has(action.voice)) {
1052
1084
  const translation = translations.get(action.voice);
1053
- if (translation) {
1085
+ if (translation !== undefined) {
1054
1086
  action.voice = translation;
1055
1087
  }
1056
1088
  }
@@ -1058,13 +1090,13 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1058
1090
  case 'GridActionHTTP':
1059
1091
  if (typeof action.restUrl === 'string' && translations.has(action.restUrl)) {
1060
1092
  const translation = translations.get(action.restUrl);
1061
- if (translation) {
1093
+ if (translation !== undefined) {
1062
1094
  action.restUrl = translation;
1063
1095
  }
1064
1096
  }
1065
1097
  if (typeof action.body === 'string' && translations.has(action.body)) {
1066
1098
  const translation = translations.get(action.body);
1067
- if (translation) {
1099
+ if (translation !== undefined) {
1068
1100
  action.body = translation;
1069
1101
  }
1070
1102
  }
@@ -1072,7 +1104,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1072
1104
  case 'GridActionOpenWebpage':
1073
1105
  if (typeof action.openURL === 'string' && translations.has(action.openURL)) {
1074
1106
  const translation = translations.get(action.openURL);
1075
- if (translation) {
1107
+ if (translation !== undefined) {
1076
1108
  action.openURL = translation;
1077
1109
  }
1078
1110
  }
@@ -1080,7 +1112,7 @@ class AstericsGridProcessor extends baseProcessor_1.BaseProcessor {
1080
1112
  case 'GridActionMatrix':
1081
1113
  if (typeof action.sendText === 'string' && translations.has(action.sendText)) {
1082
1114
  const translation = translations.get(action.sendText);
1083
- if (translation) {
1115
+ if (translation !== undefined) {
1084
1116
  action.sendText = translation;
1085
1117
  }
1086
1118
  }
@@ -12,13 +12,13 @@ export declare class ExcelProcessor extends BaseProcessor {
12
12
  * @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
13
13
  * @returns Array of all text content found in the Excel file
14
14
  */
15
- extractTexts(filePathOrBuffer: string | Buffer): string[];
15
+ extractTexts(_filePathOrBuffer: string | Buffer): string[];
16
16
  /**
17
17
  * Load Excel file into AACTree structure
18
18
  * @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
19
19
  * @returns AACTree representation of the Excel file
20
20
  */
21
- loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
21
+ loadIntoTree(_filePathOrBuffer: string | Buffer): AACTree;
22
22
  /**
23
23
  * Process texts in Excel file (apply translations)
24
24
  * @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
@@ -26,7 +26,7 @@ export declare class ExcelProcessor extends BaseProcessor {
26
26
  * @param outputPath - Path where translated Excel file should be saved
27
27
  * @returns Buffer containing the translated Excel file
28
28
  */
29
- processTexts(filePathOrBuffer: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer;
29
+ processTexts(_filePathOrBuffer: string | Buffer, _translations: Map<string, string>, outputPath: string): Buffer;
30
30
  /**
31
31
  * Convert an AAC page to an Excel worksheet
32
32
  * @param workbook - Excel workbook to add worksheet to