@willwade/aac-processors 0.1.7 → 0.1.8

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.
Files changed (32) hide show
  1. package/dist/analytics.d.ts +7 -0
  2. package/dist/analytics.js +23 -0
  3. package/dist/browser/index.browser.js +5 -0
  4. package/dist/browser/metrics.js +17 -0
  5. package/dist/browser/processors/gridset/helpers.js +390 -0
  6. package/dist/browser/processors/snap/helpers.js +252 -0
  7. package/dist/browser/utilities/analytics/history.js +116 -0
  8. package/dist/browser/utilities/analytics/metrics/comparison.js +477 -0
  9. package/dist/browser/utilities/analytics/metrics/core.js +775 -0
  10. package/dist/browser/utilities/analytics/metrics/effort.js +221 -0
  11. package/dist/browser/utilities/analytics/metrics/obl-types.js +6 -0
  12. package/dist/browser/utilities/analytics/metrics/obl.js +282 -0
  13. package/dist/browser/utilities/analytics/metrics/sentence.js +121 -0
  14. package/dist/browser/utilities/analytics/metrics/types.js +6 -0
  15. package/dist/browser/utilities/analytics/metrics/vocabulary.js +138 -0
  16. package/dist/browser/utilities/analytics/reference/browser.js +67 -0
  17. package/dist/browser/utilities/analytics/reference/index.js +129 -0
  18. package/dist/browser/utils/dotnetTicks.js +17 -0
  19. package/dist/index.browser.d.ts +1 -0
  20. package/dist/index.browser.js +18 -1
  21. package/dist/index.node.d.ts +2 -2
  22. package/dist/index.node.js +5 -5
  23. package/dist/metrics.d.ts +17 -0
  24. package/dist/metrics.js +44 -0
  25. package/dist/utilities/analytics/metrics/comparison.d.ts +2 -1
  26. package/dist/utilities/analytics/metrics/comparison.js +3 -3
  27. package/dist/utilities/analytics/metrics/vocabulary.d.ts +2 -2
  28. package/dist/utilities/analytics/reference/browser.d.ts +31 -0
  29. package/dist/utilities/analytics/reference/browser.js +73 -0
  30. package/dist/utilities/analytics/reference/index.d.ts +21 -0
  31. package/dist/utilities/analytics/reference/index.js +22 -46
  32. package/package.json +9 -1
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Vocabulary Coverage Analysis
3
+ *
4
+ * Analyzes how well an AAC board set covers core vocabulary
5
+ * and identifies missing/extra words compared to reference lists.
6
+ */
7
+ import { ReferenceLoader } from '../reference/index';
8
+ import { spellingEffort } from './effort';
9
+ export class VocabularyAnalyzer {
10
+ constructor(referenceLoader) {
11
+ this.referenceLoader = referenceLoader || new ReferenceLoader();
12
+ }
13
+ /**
14
+ * Analyze vocabulary coverage against core lists
15
+ */
16
+ analyze(metrics, options) {
17
+ // const locale = options?.locale || metrics.locale || 'en';
18
+ const highEffortThreshold = options?.highEffortThreshold || 5.0;
19
+ const lowEffortThreshold = options?.lowEffortThreshold || 2.0;
20
+ // Load reference data
21
+ const coreLists = this.referenceLoader.loadCoreLists();
22
+ // Create word to effort map (using lowercase keys for matching)
23
+ const wordEffortMap = new Map();
24
+ metrics.buttons.forEach((btn) => {
25
+ const word = btn.label.toLowerCase();
26
+ const existing = wordEffortMap.get(word);
27
+ if (!existing || btn.effort < existing) {
28
+ wordEffortMap.set(word, btn.effort);
29
+ }
30
+ });
31
+ // Analyze each core list
32
+ const core_coverage = {};
33
+ coreLists.forEach((list) => {
34
+ const analysis = this.analyzeCoreList(list, wordEffortMap);
35
+ core_coverage[list.id] = analysis;
36
+ });
37
+ // Find extra words (words not in any core list)
38
+ const allCoreWords = new Set();
39
+ coreLists.forEach((list) => {
40
+ list.words.forEach((word) => allCoreWords.add(word.toLowerCase()));
41
+ });
42
+ const extraWords = [];
43
+ wordEffortMap.forEach((effort, word) => {
44
+ if (!allCoreWords.has(word.toLowerCase())) {
45
+ extraWords.push(word);
46
+ }
47
+ });
48
+ extraWords.sort((a, b) => a.localeCompare(b));
49
+ // Find high/low effort words
50
+ const highEffortWords = [];
51
+ const lowEffortWords = [];
52
+ wordEffortMap.forEach((effort, word) => {
53
+ if (effort > highEffortThreshold) {
54
+ highEffortWords.push({ word, effort });
55
+ }
56
+ else if (effort < lowEffortThreshold) {
57
+ lowEffortWords.push({ word, effort });
58
+ }
59
+ });
60
+ highEffortWords.sort((a, b) => b.effort - a.effort);
61
+ lowEffortWords.sort((a, b) => a.effort - b.effort);
62
+ return {
63
+ core_coverage,
64
+ total_unique_words: wordEffortMap.size,
65
+ words_with_effort: wordEffortMap.size,
66
+ words_requiring_spelling: 0, // Calculated during sentence analysis
67
+ extra_words: extraWords,
68
+ high_effort_words: highEffortWords.slice(0, 50), // Top 50
69
+ low_effort_words: lowEffortWords.slice(0, 50), // Bottom 50
70
+ };
71
+ }
72
+ /**
73
+ * Analyze coverage for a single core list
74
+ */
75
+ analyzeCoreList(list, wordEffortMap) {
76
+ const covered = [];
77
+ const missing = [];
78
+ let totalEffort = 0;
79
+ list.words.forEach((word) => {
80
+ const lowerWord = word.toLowerCase();
81
+ const effort = wordEffortMap.get(lowerWord);
82
+ if (effort !== undefined) {
83
+ covered.push(word);
84
+ totalEffort += effort;
85
+ }
86
+ else {
87
+ missing.push(word);
88
+ }
89
+ });
90
+ const averageEffort = covered.length > 0 ? totalEffort / covered.length : 0;
91
+ return {
92
+ name: list.name,
93
+ total_words: list.words.length,
94
+ covered: covered.length,
95
+ missing: missing.length,
96
+ coverage_percent: (covered.length / list.words.length) * 100,
97
+ missing_words: missing,
98
+ average_effort: averageEffort,
99
+ };
100
+ }
101
+ /**
102
+ * Calculate coverage percentage for a specific word list
103
+ */
104
+ calculateCoverage(wordList, metrics) {
105
+ const wordSet = new Set(metrics.buttons.map((btn) => btn.label.toLowerCase()));
106
+ const covered = [];
107
+ const missing = [];
108
+ wordList.forEach((word) => {
109
+ if (wordSet.has(word.toLowerCase())) {
110
+ covered.push(word);
111
+ }
112
+ else {
113
+ missing.push(word);
114
+ }
115
+ });
116
+ return {
117
+ covered,
118
+ missing,
119
+ coverage_percent: (covered.length / wordList.length) * 100,
120
+ };
121
+ }
122
+ /**
123
+ * Get effort for a word, or calculate spelling effort if missing
124
+ */
125
+ getWordEffort(word, metrics) {
126
+ const btn = metrics.buttons.find((b) => b.label.toLowerCase() === word.toLowerCase());
127
+ if (btn) {
128
+ return btn.effort;
129
+ }
130
+ return spellingEffort(word, metrics.spelling_effort_base, metrics.spelling_effort_per_letter);
131
+ }
132
+ /**
133
+ * Check if a word is in the board set
134
+ */
135
+ hasWord(word, metrics) {
136
+ return metrics.buttons.some((b) => b.label.toLowerCase() === word.toLowerCase());
137
+ }
138
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Browser-friendly reference data loader using fetch.
3
+ */
4
+ export class InMemoryReferenceLoader {
5
+ constructor(data) {
6
+ this.data = data;
7
+ }
8
+ loadCoreLists() {
9
+ return this.data.coreLists;
10
+ }
11
+ loadCommonWords() {
12
+ return this.data.commonWords;
13
+ }
14
+ loadSynonyms() {
15
+ return this.data.synonyms;
16
+ }
17
+ loadSentences() {
18
+ return this.data.sentences;
19
+ }
20
+ loadFringe() {
21
+ return this.data.fringe;
22
+ }
23
+ loadBaseWords() {
24
+ return this.data.baseWords;
25
+ }
26
+ loadCommonFringe() {
27
+ const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
28
+ const coreWords = new Set();
29
+ this.data.coreLists.forEach((list) => {
30
+ list.words.forEach((word) => coreWords.add(word.toLowerCase()));
31
+ });
32
+ return Array.from(commonWords).filter((word) => !coreWords.has(word));
33
+ }
34
+ loadAll() {
35
+ return this.data;
36
+ }
37
+ }
38
+ export async function loadReferenceDataFromUrl(baseUrl, locale = 'en') {
39
+ const root = baseUrl.replace(/\/$/, '');
40
+ const fetchJson = async (name) => {
41
+ const res = await fetch(`${root}/${name}.${locale}.json`);
42
+ if (!res.ok) {
43
+ throw new Error(`Failed to load ${name}.${locale}.json`);
44
+ }
45
+ return (await res.json());
46
+ };
47
+ const [coreLists, commonWords, synonyms, sentences, fringe, baseWords] = await Promise.all([
48
+ fetchJson('core_lists'),
49
+ fetchJson('common_words'),
50
+ fetchJson('synonyms'),
51
+ fetchJson('sentences'),
52
+ fetchJson('fringe'),
53
+ fetchJson('base_words'),
54
+ ]);
55
+ return {
56
+ coreLists,
57
+ commonWords,
58
+ synonyms,
59
+ sentences,
60
+ fringe,
61
+ baseWords,
62
+ };
63
+ }
64
+ export async function createBrowserReferenceLoader(baseUrl, locale = 'en') {
65
+ const data = await loadReferenceDataFromUrl(baseUrl, locale);
66
+ return new InMemoryReferenceLoader(data);
67
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Reference Data Loader
3
+ *
4
+ * Loads reference vocabulary lists, core lists, and sentences
5
+ * for AAC metrics analysis.
6
+ */
7
+ import { getFs, getPath } from '../../../utils/io';
8
+ export class ReferenceLoader {
9
+ constructor(dataDir, locale = 'en') {
10
+ this.locale = locale;
11
+ if (dataDir) {
12
+ this.dataDir = dataDir;
13
+ }
14
+ else {
15
+ // Resolve the data directory relative to this file's location
16
+ // Use __dirname which works correctly after compilation
17
+ this.dataDir = getPath().join(__dirname, 'data');
18
+ }
19
+ }
20
+ /**
21
+ * Load core vocabulary lists
22
+ */
23
+ loadCoreLists() {
24
+ const filePath = getPath().join(this.dataDir, `core_lists.${this.locale}.json`);
25
+ const content = getFs().readFileSync(filePath, 'utf-8');
26
+ return JSON.parse(String(content));
27
+ }
28
+ /**
29
+ * Load common words with baseline effort scores
30
+ */
31
+ loadCommonWords() {
32
+ const filePath = getPath().join(this.dataDir, `common_words.${this.locale}.json`);
33
+ const content = getFs().readFileSync(filePath, 'utf-8');
34
+ return JSON.parse(String(content));
35
+ }
36
+ /**
37
+ * Load synonym mappings
38
+ */
39
+ loadSynonyms() {
40
+ const filePath = getPath().join(this.dataDir, `synonyms.${this.locale}.json`);
41
+ const content = getFs().readFileSync(filePath, 'utf-8');
42
+ return JSON.parse(String(content));
43
+ }
44
+ /**
45
+ * Load test sentences
46
+ */
47
+ loadSentences() {
48
+ const filePath = getPath().join(this.dataDir, `sentences.${this.locale}.json`);
49
+ const content = getFs().readFileSync(filePath, 'utf-8');
50
+ return JSON.parse(String(content));
51
+ }
52
+ /**
53
+ * Load fringe vocabulary
54
+ */
55
+ loadFringe() {
56
+ const filePath = getPath().join(this.dataDir, `fringe.${this.locale}.json`);
57
+ const content = getFs().readFileSync(filePath, 'utf-8');
58
+ const data = JSON.parse(String(content));
59
+ // Flatten nested category words if needed
60
+ if (Array.isArray(data) && data.length > 0 && data[0].categories) {
61
+ const flattened = [];
62
+ data.forEach((list) => {
63
+ list.categories.forEach((cat) => {
64
+ flattened.push(...cat.words);
65
+ });
66
+ });
67
+ return flattened;
68
+ }
69
+ return data;
70
+ }
71
+ /**
72
+ * Load base words hash map
73
+ */
74
+ loadBaseWords() {
75
+ const filePath = getPath().join(this.dataDir, `base_words.${this.locale}.json`);
76
+ const content = getFs().readFileSync(filePath, 'utf-8');
77
+ return JSON.parse(String(content));
78
+ }
79
+ /**
80
+ * Load common fringe vocabulary
81
+ * Common words that are NOT in core vocabulary lists
82
+ * (matching Ruby loader.rb:413-420)
83
+ */
84
+ loadCommonFringe() {
85
+ const commonWordsData = this.loadCommonWords();
86
+ const commonWords = new Set(commonWordsData.words.map((w) => w.toLowerCase()));
87
+ const coreLists = this.loadCoreLists();
88
+ const coreWords = new Set();
89
+ coreLists.forEach((list) => {
90
+ list.words.forEach((word) => coreWords.add(word.toLowerCase()));
91
+ });
92
+ // Common fringe = common words - core words
93
+ const commonFringe = Array.from(commonWords).filter((word) => !coreWords.has(word));
94
+ return commonFringe;
95
+ }
96
+ /**
97
+ * Get all reference data at once
98
+ */
99
+ loadAll() {
100
+ return {
101
+ coreLists: this.loadCoreLists(),
102
+ commonWords: this.loadCommonWords(),
103
+ synonyms: this.loadSynonyms(),
104
+ sentences: this.loadSentences(),
105
+ fringe: this.loadFringe(),
106
+ baseWords: this.loadBaseWords(),
107
+ };
108
+ }
109
+ }
110
+ /**
111
+ * Get the default reference data path
112
+ */
113
+ export function getReferenceDataPath() {
114
+ return String(getPath().join(__dirname, 'data'));
115
+ }
116
+ /**
117
+ * Check if reference data files exist
118
+ */
119
+ export function hasReferenceData() {
120
+ const dataPath = getReferenceDataPath();
121
+ const requiredFiles = [
122
+ 'core_lists.en.json',
123
+ 'common_words.en.json',
124
+ 'sentences.en.json',
125
+ 'synonyms.en.json',
126
+ 'fringe.en.json',
127
+ ];
128
+ return requiredFiles.every((file) => getFs().existsSync(getPath().join(dataPath, file)));
129
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Number of ticks (.NET 100ns units) between 0001-01-01 and Unix epoch.
3
+ */
4
+ export const DOTNET_EPOCH_TICKS = 621355968000000000n;
5
+ /**
6
+ * Number of ticks per millisecond.
7
+ */
8
+ export const TICKS_PER_MILLISECOND = 10000n;
9
+ /**
10
+ * Convert .NET ticks (100ns since 0001-01-01) to a JavaScript Date.
11
+ * Accepts bigint or number and rounds down to millisecond precision.
12
+ */
13
+ export function dotNetTicksToDate(ticks) {
14
+ const tickValue = BigInt(ticks);
15
+ const ms = Number((tickValue - DOTNET_EPOCH_TICKS) / TICKS_PER_MILLISECOND);
16
+ return new Date(ms);
17
+ }
@@ -22,6 +22,7 @@ export { SnapProcessor } from './processors/snapProcessor';
22
22
  export { TouchChatProcessor } from './processors/touchchatProcessor';
23
23
  export { ApplePanelsProcessor } from './processors/applePanelsProcessor';
24
24
  export { AstericsGridProcessor } from './processors/astericsGridProcessor';
25
+ export * as Metrics from './metrics';
25
26
  import { BaseProcessor } from './core/baseProcessor';
26
27
  export { configureSqlJs } from './utils/sqlite';
27
28
  /**
@@ -23,11 +23,23 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
23
23
  if (k2 === undefined) k2 = k;
24
24
  o[k2] = m[k];
25
25
  }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
26
31
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
32
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
28
33
  };
34
+ var __importStar = (this && this.__importStar) || function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
29
41
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.configureSqlJs = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
42
+ exports.configureSqlJs = exports.Metrics = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
31
43
  exports.getProcessor = getProcessor;
32
44
  exports.getSupportedExtensions = getSupportedExtensions;
33
45
  exports.isExtensionSupported = isExtensionSupported;
@@ -56,6 +68,11 @@ var applePanelsProcessor_1 = require("./processors/applePanelsProcessor");
56
68
  Object.defineProperty(exports, "ApplePanelsProcessor", { enumerable: true, get: function () { return applePanelsProcessor_1.ApplePanelsProcessor; } });
57
69
  var astericsGridProcessor_1 = require("./processors/astericsGridProcessor");
58
70
  Object.defineProperty(exports, "AstericsGridProcessor", { enumerable: true, get: function () { return astericsGridProcessor_1.AstericsGridProcessor; } });
71
+ // ===================================================================
72
+ // UTILITY FUNCTIONS
73
+ // ===================================================================
74
+ // Metrics namespace (pageset analytics)
75
+ exports.Metrics = __importStar(require("./metrics"));
59
76
  const dotProcessor_2 = require("./processors/dotProcessor");
60
77
  const opmlProcessor_2 = require("./processors/opmlProcessor");
61
78
  const obfProcessor_2 = require("./processors/obfProcessor");
@@ -9,9 +9,9 @@ export * from './core/treeStructure';
9
9
  export * from './core/baseProcessor';
10
10
  export * from './core/stringCasing';
11
11
  export * from './processors';
12
- export * as Analytics from './utilities/analytics';
13
- export * from './utilities/analytics';
12
+ export * as Analytics from './analytics';
14
13
  export * as Validation from './validation';
14
+ export * as Metrics from './metrics';
15
15
  export * as Gridset from './gridset';
16
16
  export * as Snap from './snap';
17
17
  export * as OBF from './obf';
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
33
33
  return result;
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.Validation = exports.Analytics = void 0;
36
+ exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.Metrics = exports.Validation = exports.Analytics = void 0;
37
37
  exports.getProcessor = getProcessor;
38
38
  exports.getSupportedExtensions = getSupportedExtensions;
39
39
  exports.isExtensionSupported = isExtensionSupported;
@@ -50,12 +50,12 @@ __exportStar(require("./processors"), exports);
50
50
  // ===================================================================
51
51
  // NAMESPACES
52
52
  // ===================================================================
53
- // Analytics namespace
54
- exports.Analytics = __importStar(require("./utilities/analytics"));
55
- // Also export analytics classes directly for convenience
56
- __exportStar(require("./utilities/analytics"), exports);
53
+ // Analytics namespace (usage/history)
54
+ exports.Analytics = __importStar(require("./analytics"));
57
55
  // Validation namespace
58
56
  exports.Validation = __importStar(require("./validation"));
57
+ // Metrics namespace (pageset analytics)
58
+ exports.Metrics = __importStar(require("./metrics"));
59
59
  // Processor namespaces (platform-specific utilities)
60
60
  exports.Gridset = __importStar(require("./gridset"));
61
61
  exports.Snap = __importStar(require("./snap"));
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Metrics Namespace
3
+ *
4
+ * Pageset-focused metrics and analytics (board structure, effort, vocabulary).
5
+ * Use this for analyzing AAC trees, not end-user usage logs.
6
+ */
7
+ export * from './utilities/analytics/metrics/types';
8
+ export * from './utilities/analytics/metrics/effort';
9
+ export * from './utilities/analytics/metrics/obl-types';
10
+ export { OblUtil, OblAnonymizer } from './utilities/analytics/metrics/obl';
11
+ export { MetricsCalculator } from './utilities/analytics/metrics/core';
12
+ export { VocabularyAnalyzer } from './utilities/analytics/metrics/vocabulary';
13
+ export { SentenceAnalyzer } from './utilities/analytics/metrics/sentence';
14
+ export { ComparisonAnalyzer } from './utilities/analytics/metrics/comparison';
15
+ export { ReferenceLoader } from './utilities/analytics/reference';
16
+ export { InMemoryReferenceLoader, createBrowserReferenceLoader, loadReferenceDataFromUrl, type ReferenceData, } from './utilities/analytics/reference/browser';
17
+ export * from './utilities/analytics/utils/idGenerator';
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Metrics Namespace
4
+ *
5
+ * Pageset-focused metrics and analytics (board structure, effort, vocabulary).
6
+ * Use this for analyzing AAC trees, not end-user usage logs.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.loadReferenceDataFromUrl = exports.createBrowserReferenceLoader = exports.InMemoryReferenceLoader = exports.ReferenceLoader = exports.ComparisonAnalyzer = exports.SentenceAnalyzer = exports.VocabularyAnalyzer = exports.MetricsCalculator = exports.OblAnonymizer = exports.OblUtil = void 0;
24
+ __exportStar(require("./utilities/analytics/metrics/types"), exports);
25
+ __exportStar(require("./utilities/analytics/metrics/effort"), exports);
26
+ __exportStar(require("./utilities/analytics/metrics/obl-types"), exports);
27
+ var obl_1 = require("./utilities/analytics/metrics/obl");
28
+ Object.defineProperty(exports, "OblUtil", { enumerable: true, get: function () { return obl_1.OblUtil; } });
29
+ Object.defineProperty(exports, "OblAnonymizer", { enumerable: true, get: function () { return obl_1.OblAnonymizer; } });
30
+ var core_1 = require("./utilities/analytics/metrics/core");
31
+ Object.defineProperty(exports, "MetricsCalculator", { enumerable: true, get: function () { return core_1.MetricsCalculator; } });
32
+ var vocabulary_1 = require("./utilities/analytics/metrics/vocabulary");
33
+ Object.defineProperty(exports, "VocabularyAnalyzer", { enumerable: true, get: function () { return vocabulary_1.VocabularyAnalyzer; } });
34
+ var sentence_1 = require("./utilities/analytics/metrics/sentence");
35
+ Object.defineProperty(exports, "SentenceAnalyzer", { enumerable: true, get: function () { return sentence_1.SentenceAnalyzer; } });
36
+ var comparison_1 = require("./utilities/analytics/metrics/comparison");
37
+ Object.defineProperty(exports, "ComparisonAnalyzer", { enumerable: true, get: function () { return comparison_1.ComparisonAnalyzer; } });
38
+ var reference_1 = require("./utilities/analytics/reference");
39
+ Object.defineProperty(exports, "ReferenceLoader", { enumerable: true, get: function () { return reference_1.ReferenceLoader; } });
40
+ var browser_1 = require("./utilities/analytics/reference/browser");
41
+ Object.defineProperty(exports, "InMemoryReferenceLoader", { enumerable: true, get: function () { return browser_1.InMemoryReferenceLoader; } });
42
+ Object.defineProperty(exports, "createBrowserReferenceLoader", { enumerable: true, get: function () { return browser_1.createBrowserReferenceLoader; } });
43
+ Object.defineProperty(exports, "loadReferenceDataFromUrl", { enumerable: true, get: function () { return browser_1.loadReferenceDataFromUrl; } });
44
+ __exportStar(require("./utilities/analytics/utils/idGenerator"), exports);
@@ -5,12 +5,13 @@
5
5
  * analyze vocabulary differences, and generate CARE component scores.
6
6
  */
7
7
  import { MetricsResult, ComparisonResult } from './types';
8
+ import { type ReferenceDataProvider } from '../reference/index';
8
9
  import { MetricsOptions } from './types';
9
10
  export declare class ComparisonAnalyzer {
10
11
  private vocabAnalyzer;
11
12
  private sentenceAnalyzer;
12
13
  private referenceLoader;
13
- constructor();
14
+ constructor(referenceLoader?: ReferenceDataProvider);
14
15
  private normalize;
15
16
  /**
16
17
  * Compare two board sets
@@ -12,10 +12,10 @@ const vocabulary_1 = require("./vocabulary");
12
12
  const index_1 = require("../reference/index");
13
13
  const effort_1 = require("./effort");
14
14
  class ComparisonAnalyzer {
15
- constructor() {
16
- this.vocabAnalyzer = new vocabulary_1.VocabularyAnalyzer();
15
+ constructor(referenceLoader) {
16
+ this.vocabAnalyzer = new vocabulary_1.VocabularyAnalyzer(referenceLoader);
17
17
  this.sentenceAnalyzer = new sentence_1.SentenceAnalyzer();
18
- this.referenceLoader = new index_1.ReferenceLoader();
18
+ this.referenceLoader = referenceLoader || new index_1.ReferenceLoader();
19
19
  }
20
20
  normalize(word) {
21
21
  return word
@@ -5,7 +5,7 @@
5
5
  * and identifies missing/extra words compared to reference lists.
6
6
  */
7
7
  import { MetricsResult } from './types';
8
- import { ReferenceLoader } from '../reference/index';
8
+ import { type ReferenceDataProvider } from '../reference/index';
9
9
  export interface VocabularyAnalysis {
10
10
  core_coverage: {
11
11
  [listId: string]: {
@@ -33,7 +33,7 @@ export interface VocabularyAnalysis {
33
33
  }
34
34
  export declare class VocabularyAnalyzer {
35
35
  private referenceLoader;
36
- constructor(referenceLoader?: ReferenceLoader);
36
+ constructor(referenceLoader?: ReferenceDataProvider);
37
37
  /**
38
38
  * Analyze vocabulary coverage against core lists
39
39
  */
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Browser-friendly reference data loader using fetch.
3
+ */
4
+ import type { CoreList, CommonWordsData, SynonymsData } from '../metrics/types';
5
+ import type { ReferenceDataProvider } from './index';
6
+ export interface ReferenceData {
7
+ coreLists: CoreList[];
8
+ commonWords: CommonWordsData;
9
+ synonyms: SynonymsData;
10
+ sentences: string[][];
11
+ fringe: string[];
12
+ baseWords: {
13
+ [word: string]: boolean;
14
+ };
15
+ }
16
+ export declare class InMemoryReferenceLoader implements ReferenceDataProvider {
17
+ private data;
18
+ constructor(data: ReferenceData);
19
+ loadCoreLists(): CoreList[];
20
+ loadCommonWords(): CommonWordsData;
21
+ loadSynonyms(): SynonymsData;
22
+ loadSentences(): string[][];
23
+ loadFringe(): string[];
24
+ loadBaseWords(): {
25
+ [word: string]: boolean;
26
+ };
27
+ loadCommonFringe(): string[];
28
+ loadAll(): ReferenceData;
29
+ }
30
+ export declare function loadReferenceDataFromUrl(baseUrl: string, locale?: string): Promise<ReferenceData>;
31
+ export declare function createBrowserReferenceLoader(baseUrl: string, locale?: string): Promise<InMemoryReferenceLoader>;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ /**
3
+ * Browser-friendly reference data loader using fetch.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InMemoryReferenceLoader = void 0;
7
+ exports.loadReferenceDataFromUrl = loadReferenceDataFromUrl;
8
+ exports.createBrowserReferenceLoader = createBrowserReferenceLoader;
9
+ class InMemoryReferenceLoader {
10
+ constructor(data) {
11
+ this.data = data;
12
+ }
13
+ loadCoreLists() {
14
+ return this.data.coreLists;
15
+ }
16
+ loadCommonWords() {
17
+ return this.data.commonWords;
18
+ }
19
+ loadSynonyms() {
20
+ return this.data.synonyms;
21
+ }
22
+ loadSentences() {
23
+ return this.data.sentences;
24
+ }
25
+ loadFringe() {
26
+ return this.data.fringe;
27
+ }
28
+ loadBaseWords() {
29
+ return this.data.baseWords;
30
+ }
31
+ loadCommonFringe() {
32
+ const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
33
+ const coreWords = new Set();
34
+ this.data.coreLists.forEach((list) => {
35
+ list.words.forEach((word) => coreWords.add(word.toLowerCase()));
36
+ });
37
+ return Array.from(commonWords).filter((word) => !coreWords.has(word));
38
+ }
39
+ loadAll() {
40
+ return this.data;
41
+ }
42
+ }
43
+ exports.InMemoryReferenceLoader = InMemoryReferenceLoader;
44
+ async function loadReferenceDataFromUrl(baseUrl, locale = 'en') {
45
+ const root = baseUrl.replace(/\/$/, '');
46
+ const fetchJson = async (name) => {
47
+ const res = await fetch(`${root}/${name}.${locale}.json`);
48
+ if (!res.ok) {
49
+ throw new Error(`Failed to load ${name}.${locale}.json`);
50
+ }
51
+ return (await res.json());
52
+ };
53
+ const [coreLists, commonWords, synonyms, sentences, fringe, baseWords] = await Promise.all([
54
+ fetchJson('core_lists'),
55
+ fetchJson('common_words'),
56
+ fetchJson('synonyms'),
57
+ fetchJson('sentences'),
58
+ fetchJson('fringe'),
59
+ fetchJson('base_words'),
60
+ ]);
61
+ return {
62
+ coreLists,
63
+ commonWords,
64
+ synonyms,
65
+ sentences,
66
+ fringe,
67
+ baseWords,
68
+ };
69
+ }
70
+ async function createBrowserReferenceLoader(baseUrl, locale = 'en') {
71
+ const data = await loadReferenceDataFromUrl(baseUrl, locale);
72
+ return new InMemoryReferenceLoader(data);
73
+ }