@bicharts/shape-core 0.1.12 → 0.1.14

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 (40) hide show
  1. package/dist/chunk-2LR73DNS.mjs +2 -0
  2. package/dist/chunk-3UOCZ2D6.mjs +2 -0
  3. package/dist/chunk-GY54KQLP.mjs +2 -0
  4. package/dist/chunk-NWDPA7DG.mjs +2 -0
  5. package/dist/chunk-QEH7WTLB.mjs +2 -0
  6. package/dist/chunk-WBEROMBG.mjs +2 -0
  7. package/dist/chunk-XWCQ7BJB.mjs +2 -0
  8. package/dist/formatDetector.mjs +1 -7
  9. package/dist/geoDetector.mjs +1 -14
  10. package/dist/geoPoint.mjs +1 -23
  11. package/dist/index.mjs +1 -250
  12. package/dist/indexedText.mjs +1 -23
  13. package/dist/models.mjs +0 -1
  14. package/dist/monthNames.mjs +1 -9
  15. package/dist/ordinalDetector.mjs +1 -11
  16. package/dist/util.mjs +1 -11
  17. package/package.json +2 -2
  18. package/dist/chunk-ICDJLWEM.mjs +0 -83
  19. package/dist/chunk-ICDJLWEM.mjs.map +0 -7
  20. package/dist/chunk-NGV56PGT.mjs +0 -787
  21. package/dist/chunk-NGV56PGT.mjs.map +0 -7
  22. package/dist/chunk-QTEE53OQ.mjs +0 -205
  23. package/dist/chunk-QTEE53OQ.mjs.map +0 -7
  24. package/dist/chunk-RZAZ7PZK.mjs +0 -31
  25. package/dist/chunk-RZAZ7PZK.mjs.map +0 -7
  26. package/dist/chunk-VHWC3THS.mjs +0 -572
  27. package/dist/chunk-VHWC3THS.mjs.map +0 -7
  28. package/dist/chunk-VLMGGCN4.mjs +0 -36
  29. package/dist/chunk-VLMGGCN4.mjs.map +0 -7
  30. package/dist/chunk-YJVU7G63.mjs +0 -559
  31. package/dist/chunk-YJVU7G63.mjs.map +0 -7
  32. package/dist/formatDetector.mjs.map +0 -7
  33. package/dist/geoDetector.mjs.map +0 -7
  34. package/dist/geoPoint.mjs.map +0 -7
  35. package/dist/index.mjs.map +0 -7
  36. package/dist/indexedText.mjs.map +0 -7
  37. package/dist/models.mjs.map +0 -7
  38. package/dist/monthNames.mjs.map +0 -7
  39. package/dist/ordinalDetector.mjs.map +0 -7
  40. package/dist/util.mjs.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/ordinalDetector.ts"],
4
- "sourcesContent": ["// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Client-side ordinal-scale detector\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Server-side detection is impossible: by the time the request reaches the\n// server, the actual category values have been obfuscated for privacy in\n// IndexedText.obfuscateString. The LLM sees random-looking strings instead\n// of \"Strongly Disagree\" / \"Agree\" / \"Neutral\".\n//\n// Detection MUST happen client-side, before obfuscation. We then ship two\n// new column-level fields to the server (orderedDomain + ordinalPattern)\n// that name a KNOWN ordinal scale (Likert, frequency, intensity, severity,\n// quality, etc.) and provide the canonical order using the USER'S OWN\n// value strings \u2014 preserving their capitalization / spacing / pluralization\n// so the LLM can pass the array directly into a `domain(...)` call at\n// render time without a re-mapping step.\n//\n// Privacy posture (2026-05-26): Likert-class category names are\n// public-domain terminology and not user secrets \u2014 they're the same in\n// every survey-research textbook on Earth. Shipping the matched values is\n// equivalent to shipping the chart's eventual axis tick labels (which the\n// LLM-emitted code reads from data.rows at render time anyway). We only\n// ship values when they MATCH a known ordinal catalog pattern; non-matches\n// pass through to the existing obfuscation flow unchanged.\n//\n// The detector is conservative \u2014 it requires enough anchors of a known\n// pattern to be confident (default \u22653 distinct matched values for 5+ pt\n// scales, \u22652 for 3-4 pt scales) AND the user's set must be a subset of the\n// pattern (so a column with \"Strongly Disagree, Disagree, Pineapple\" won't\n// trip \u2014 Pineapple isn't in the Likert catalog).\n//\n// Returns null when no pattern matches \u2192 caller falls through to the\n// existing obfuscation pipeline (orderedDomain stays undefined on the\n// shipped column).\n\ntype OrdinalPattern = {\n name: string;\n // Canonical pattern values in their inherent order, lowercased + space-\n // normalized for matching. The user's actual values may use different\n // capitalization / hyphens / extra whitespace; we normalize before\n // comparing.\n canonical: string[];\n // Minimum distinct user values that must match a pattern entry.\n // Higher for shorter patterns to avoid false-positive on coincidences\n // (e.g. a 3-pt pattern needs 2 of 3 matched to fire, not just 1).\n minMatches: number;\n};\n\n// Catalog ordered most-specific \u2192 most-generic. First match wins, so 5/7-pt\n// scales beat 3-pt scales when both could match a user's value set.\nconst PATTERNS: OrdinalPattern[] = [\n // Likert agreement \u2014 the case that motivated this whole feature\n { name: \"likert7_agreement\", minMatches: 5,\n canonical: [\"strongly disagree\", \"disagree\", \"slightly disagree\", \"neutral\", \"slightly agree\", \"agree\", \"strongly agree\"] },\n { name: \"likert5_agreement\", minMatches: 3,\n canonical: [\"strongly disagree\", \"disagree\", \"neutral\", \"agree\", \"strongly agree\"] },\n { name: \"likert3_agreement\", minMatches: 2,\n canonical: [\"disagree\", \"neutral\", \"agree\"] },\n\n // Satisfaction\n { name: \"likert5_satisfaction\", minMatches: 3,\n canonical: [\"very dissatisfied\", \"dissatisfied\", \"neutral\", \"satisfied\", \"very satisfied\"] },\n { name: \"likert3_satisfaction\", minMatches: 2,\n canonical: [\"dissatisfied\", \"neutral\", \"satisfied\"] },\n\n // Quality\n { name: \"likert5_quality\", minMatches: 3,\n canonical: [\"poor\", \"fair\", \"good\", \"very good\", \"excellent\"] },\n { name: \"likert3_quality\", minMatches: 2,\n canonical: [\"poor\", \"fair\", \"good\"] },\n\n // Frequency\n { name: \"frequency5\", minMatches: 3,\n canonical: [\"never\", \"rarely\", \"sometimes\", \"often\", \"always\"] },\n\n // Intensity \u2014 common in psychology / wellness surveys\n { name: \"intensity5\", minMatches: 3,\n canonical: [\"not at all\", \"slightly\", \"moderately\", \"very\", \"extremely\"] },\n\n // Importance\n { name: \"importance5\", minMatches: 3,\n canonical: [\"not important\", \"slightly important\", \"moderately important\", \"very important\", \"extremely important\"] },\n\n // Severity / priority / urgency \u2014 common in IT / clinical / ops data\n { name: \"severity_low_critical\", minMatches: 3,\n canonical: [\"low\", \"medium\", \"high\", \"critical\"] },\n // Support/ops priority where the top band is \"Urgent\" (not \"Critical\").\n // Distinct catalog entry so the subset check passes for {Low,Medium,High,\n // Urgent} (the city_311 case where the LLM otherwise invented a generic\n // Critical/High/Normal/Low taxonomy because no orderedDomain shipped).\n { name: \"severity_low_urgent\", minMatches: 3,\n canonical: [\"low\", \"medium\", \"high\", \"urgent\"] },\n { name: \"severity_none_critical\", minMatches: 3,\n canonical: [\"none\", \"mild\", \"moderate\", \"severe\", \"critical\"] },\n { name: \"priority_p4_p1\", minMatches: 3,\n canonical: [\"p4\", \"p3\", \"p2\", \"p1\", \"p0\"] },\n // Jira-style issue priority (Blocker highest).\n { name: \"severity_trivial_blocker\", minMatches: 3,\n canonical: [\"trivial\", \"minor\", \"major\", \"critical\", \"blocker\"] },\n { name: \"severity_minor_severe\", minMatches: 3,\n canonical: [\"minor\", \"moderate\", \"major\", \"severe\"] },\n\n // Generic magnitude / confidence bands (two spellings of the mid value:\n // 'medium' vs 'moderate' \u2014 separate patterns so the subset check is exact).\n { name: \"magnitude5_medium\", minMatches: 3,\n canonical: [\"very low\", \"low\", \"medium\", \"high\", \"very high\"] },\n { name: \"magnitude5_moderate\", minMatches: 3,\n canonical: [\"very low\", \"low\", \"moderate\", \"high\", \"very high\"] },\n\n // Speed\n { name: \"speed5\", minMatches: 3,\n canonical: [\"very slow\", \"slow\", \"moderate\", \"fast\", \"very fast\"] },\n\n // Temperature (qualitative)\n { name: \"temperature6\", minMatches: 3,\n canonical: [\"freezing\", \"cold\", \"cool\", \"mild\", \"warm\", \"hot\"] },\n\n // Skill / experience ladder\n { name: \"experience6\", minMatches: 3,\n canonical: [\"novice\", \"beginner\", \"intermediate\", \"advanced\", \"expert\", \"master\"] },\n\n // Membership / loyalty tiers (metal ladder)\n { name: \"tier_metal\", minMatches: 3,\n canonical: [\"bronze\", \"silver\", \"gold\", \"platinum\", \"diamond\"] },\n\n // T-shirt sizes \u2014 letter form. Single letters are short/ambiguous, so\n // require 4 anchors to fire (keeps {M,F} gender / {A,B} groups from\n // tripping it).\n { name: \"tshirt_letter_sizes\", minMatches: 4,\n canonical: [\"xxs\", \"xs\", \"s\", \"m\", \"l\", \"xl\", \"xxl\", \"xxxl\"] },\n // T-shirt sizes \u2014 word form.\n { name: \"tshirt_word_sizes\", minMatches: 3,\n canonical: [\"extra small\", \"small\", \"medium\", \"large\", \"extra large\"] },\n\n // NPS respondent bands\n { name: \"nps_bands\", minMatches: 2,\n canonical: [\"detractor\", \"passive\", \"promoter\"] },\n\n // Likelihood / probability bands\n { name: \"likelihood5\", minMatches: 3,\n canonical: [\"very unlikely\", \"unlikely\", \"neutral\", \"likely\", \"very likely\"] },\n\n // Educational levels (one common ladder)\n { name: \"education\", minMatches: 3,\n canonical: [\"none\", \"primary\", \"secondary\", \"bachelors\", \"masters\", \"doctoral\"] },\n\n // Grade letters \u2014 A best \u2192 F worst. Single letters are short; we require\n // 3 of 5 to fire to keep false-positive risk low.\n { name: \"grades_a_to_f\", minMatches: 3,\n canonical: [\"a\", \"b\", \"c\", \"d\", \"f\"] },\n\n { name: \"grades_a_to_f_plusminus\", minMatches: 4,\n canonical: [\"a+\", \"a\", \"a-\", \"b+\", \"b\", \"b-\", \"c+\", \"c\", \"c-\", \"d+\", \"d\", \"d-\", \"f\"] },\n ];\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Synonym dictionary (2026-06-22)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// PATTERNS above is the CATALOG (scales + canonical order). This maps the many\n// ways a rung is SPELLED in real data \u2192 the canonical term used in PATTERNS, so a\n// column of \"Frequently / Occasionally / Always\" still resolves to the frequency\n// scale. KEY = the lookup word as it might appear in data; VALUE = the exact\n// PATTERNS canonical. Both are compared after normalize() (lowercase, -/_ \u2192 space).\n// A key that IS already a canonical is ignored (never shadow a real rung). The\n// orderedDomain still ships the USER'S actual strings \u2014 only the MATCH is\n// synonym-aware \u2014 and subset + minMatches discipline is unchanged, so this widens\n// recall WITHOUT widening false positives. Extend freely; values must be exact\n// PATTERNS canonicals.\nconst SYNONYMS: Record<string, string> = {\n // frequency5: never / rarely / sometimes / often / always\n \"seldom\": \"rarely\", \"infrequently\": \"rarely\", \"hardly ever\": \"rarely\",\n \"occasionally\": \"sometimes\", \"periodically\": \"sometimes\", \"now and then\": \"sometimes\",\n \"frequently\": \"often\", \"frequent\": \"often\", \"regularly\": \"often\", \"commonly\": \"often\", \"usually\": \"often\",\n \"constantly\": \"always\", \"every time\": \"always\", \"all the time\": \"always\", \"consistently\": \"always\", \"continually\": \"always\",\n // likert5_quality: poor / fair / good / very good / excellent\n \"bad\": \"poor\", \"weak\": \"poor\", \"subpar\": \"poor\",\n \"mediocre\": \"fair\", \"passable\": \"fair\", \"so so\": \"fair\",\n \"decent\": \"good\", \"solid\": \"good\",\n \"outstanding\": \"excellent\", \"superb\": \"excellent\", \"exceptional\": \"excellent\", \"stellar\": \"excellent\",\n // likert5_satisfaction\n \"unsatisfied\": \"dissatisfied\", \"unhappy\": \"dissatisfied\", \"displeased\": \"dissatisfied\",\n \"happy\": \"satisfied\", \"content\": \"satisfied\", \"pleased\": \"satisfied\",\n \"delighted\": \"very satisfied\", \"highly satisfied\": \"very satisfied\", \"extremely satisfied\": \"very satisfied\",\n \"highly dissatisfied\": \"very dissatisfied\", \"extremely dissatisfied\": \"very dissatisfied\",\n // agreement\n \"neither\": \"neutral\", \"undecided\": \"neutral\", \"no opinion\": \"neutral\", \"neither agree nor disagree\": \"neutral\",\n \"completely agree\": \"strongly agree\", \"totally agree\": \"strongly agree\",\n \"completely disagree\": \"strongly disagree\", \"totally disagree\": \"strongly disagree\",\n // likelihood5\n \"highly unlikely\": \"very unlikely\", \"extremely unlikely\": \"very unlikely\",\n \"highly likely\": \"very likely\", \"extremely likely\": \"very likely\", \"almost certain\": \"very likely\",\n // education ladder\n \"bachelor\": \"bachelors\", \"bachelor's\": \"bachelors\", \"undergraduate\": \"bachelors\", \"bachelor degree\": \"bachelors\",\n // bare \"master\" is intentionally OMITTED \u2014 it is the experience6 canonical\n // (skill level), so it must not be remapped to the education \"masters\".\n \"master's\": \"masters\", \"graduate\": \"masters\", \"master degree\": \"masters\",\n \"doctorate\": \"doctoral\", \"phd\": \"doctoral\",\n};\n\n// Normalized synonym \u2192 canonical map, built once at load. Skips any key that is\n// itself a canonical (a real rung never resolves to something else) and first-wins\n// on duplicate keys.\nconst CANONICAL_SET: Set<string> = (() => {\n const s = new Set<string>();\n for (const p of PATTERNS) for (const c of p.canonical) s.add(normalize(c));\n return s;\n})();\nconst SYNONYM_TO_CANON: Map<string, string> = (() => {\n const m = new Map<string, string>();\n for (const [syn, canon] of Object.entries(SYNONYMS)) {\n const ns = normalize(syn);\n if (CANONICAL_SET.has(ns) || m.has(ns)) continue; // never shadow a canonical; first wins\n m.set(ns, normalize(canon));\n }\n return m;\n})();\n// Resolve a normalized user value to its canonical term (or itself when it is\n// already a canonical / has no synonym entry).\nfunction resolveCanon(norm: string): string {\n return SYNONYM_TO_CANON.get(norm) ?? norm;\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Numeric-ordinal NAME test (2026-05-31)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// A low-card, CONTIGUOUS, non-measure integer (Hour 0-23, day-of-month 1-31,\n// rating 1-5) is a discrete AXIS dimension, not a continuous measure. We only\n// promote it to ORDINAL (an ordered numeric axis, usable by line/scatter as\n// well as heatmap) when its column NAME reads like a temporal or rank/level\n// dimension \u2014 'Hour' yes, 'Sales' no. Non-matching (or gappy) low-card\n// integers fall back to Categorical (still a valid discrete axis). Matching is\n// WHOLE-TOKEN (camelCase + separators split) so 'threshold' doesn't match 'hr'\n// and 'average' doesn't match 'age'.\nconst ORDINAL_NAME_TOKENS: Set<string> = new Set([\n // temporal\n \"hour\", \"hr\", \"day\", \"dow\", \"dom\", \"weekday\", \"week\", \"wk\", \"woy\",\n \"month\", \"mth\", \"quarter\", \"qtr\", \"year\", \"yr\", \"semester\", \"trimester\",\n \"decade\", \"period\", \"fiscal\", \"fy\",\n // rank / level / scale\n \"rank\", \"level\", \"lvl\", \"tier\", \"grade\", \"band\", \"decile\", \"percentile\",\n \"quartile\", \"quintile\", \"score\", \"rating\", \"stars\", \"step\", \"stage\",\n \"phase\", \"round\", \"wave\", \"episode\", \"chapter\", \"age\", \"cohort\",\n \"generation\", \"seniority\", \"position\", \"sequence\", \"seq\", \"priority\",\n]);\n\nfunction tokenizeName(name: string): string[] {\n return name\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\") // camelCase \u2192 words\n .replace(/[_\\-./]+/g, \" \") // separators \u2192 space\n .toLowerCase()\n .split(/\\s+/)\n .filter(t => t.length > 0);\n}\n\nexport function isOrdinalFriendlyName(name: string): boolean {\n if (!name) return false;\n for (const tok of tokenizeName(name)) {\n if (ORDINAL_NAME_TOKENS.has(tok)) return true;\n }\n return false;\n}\n\n// Normalize one value string for matching: lowercase, replace -/_ with\n// spaces, collapse multiple whitespace runs into one, trim.\nfunction normalize(s: string): string {\n return s.toLowerCase()\n .replace(/[-_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Calendar / cyclic ordinals (2026-05-31)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Days, months, and quarters have MANY surface forms (Jan/January,\n// Thu/Thr/Thurs/Thursday, Q1/Qtr 1/Quarter 1). Unlike the fixed Likert phrases\n// above, we match via a synonym DICTIONARY: each known surface form (normalized)\n// maps to its ordinal POSITION. A column qualifies only when EVERY distinct\n// value is in the family's dictionary (so one 'Pineapple' outlier disqualifies\n// it \u2014 same subset discipline as the scale patterns) AND at least `minMatches`\n// distinct positions are present. We then ship the user's ACTUAL strings\n// (whichever form they used \u2014 'Mon' or 'Monday') ordered by position.\n//\n// Privacy posture is identical to the scales above: public-domain calendar\n// terminology, safe to send unobfuscated. Genesis (a production incident): a freemium\n// Weekday heatmap had its category values stripped; the LLM guessed FULL names\n// 'Monday'..'Sunday' while the data was 'Mon'..'Sun', hardcoded that list, and\n// filtered the data to empty \u2192 \"INVALID: no data after filtering\".\ntype CalendarFamily = { name: string; minMatches: number; dict: Map<string, number> };\n\nfunction buildCalendarDict(entries: [string[], number][]): Map<string, number> {\n const m = new Map<string, number>();\n for (const [forms, pos] of entries) {\n for (const f of forms) m.set(normalize(f), pos);\n }\n return m;\n}\n\nconst CALENDAR_FAMILIES: CalendarFamily[] = [\n // Weekday, Mon\u2192Sun (ISO order, matches the server's DataShapeAdvisor R9).\n { name: \"weekday_mon_sun\", minMatches: 4, dict: buildCalendarDict([\n [[\"monday\", \"mon\"], 0],\n [[\"tuesday\", \"tue\", \"tues\"], 1],\n [[\"wednesday\", \"wed\", \"weds\"], 2],\n [[\"thursday\", \"thu\", \"thur\", \"thurs\", \"thr\"], 3],\n [[\"friday\", \"fri\"], 4],\n [[\"saturday\", \"sat\"], 5],\n [[\"sunday\", \"sun\"], 6],\n ]) },\n // Month, Jan\u2192Dec.\n { name: \"month_jan_dec\", minMatches: 4, dict: buildCalendarDict([\n [[\"january\", \"jan\"], 0],\n [[\"february\", \"feb\"], 1],\n [[\"march\", \"mar\"], 2],\n [[\"april\", \"apr\"], 3],\n [[\"may\"], 4],\n [[\"june\", \"jun\"], 5],\n [[\"july\", \"jul\"], 6],\n [[\"august\", \"aug\"], 7],\n [[\"september\", \"sep\", \"sept\"], 8],\n [[\"october\", \"oct\"], 9],\n [[\"november\", \"nov\"], 10],\n [[\"december\", \"dec\"], 11],\n ]) },\n // Quarter, Q1\u2192Q4. Power BI emits 'Qtr 1'/'Qtr 2'/... (the Marimekko genesis).\n { name: \"quarter_q1_q4\", minMatches: 3, dict: buildCalendarDict([\n [[\"q1\", \"qtr 1\", \"qtr1\", \"quarter 1\", \"q 1\"], 0],\n [[\"q2\", \"qtr 2\", \"qtr2\", \"quarter 2\", \"q 2\"], 1],\n [[\"q3\", \"qtr 3\", \"qtr3\", \"quarter 3\", \"q 3\"], 2],\n [[\"q4\", \"qtr 4\", \"qtr4\", \"quarter 4\", \"q 4\"], 3],\n ]) },\n];\n\n// LOCALE-AWARE calendar surface forms (2026-07-18, Phase 7). The static\n// CALENDAR_FAMILIES above carry ENGLISH forms only, so a non-English STANDALONE weekday /\n// month TEXT column (\"Montag\", \"Janvier\", \"Monat\"-valued) was missed \u2192 OrdinalPattern unset\n// \u2192 the server's Phase-6 cyclicality gate over-rejected Circular heatmap / Polar for that\n// data. We build the locale's weekday + month names via Intl (the SAME mechanism dateUnshred\n// uses for date-hierarchy month parsing), keyed with THIS module's normalize(), and UNION\n// them with the English dicts at match time \u2014 so an English column in a non-English report\n// STILL resolves (English wins on any collision). Cached per locale.\nconst _localeCalCache: Record<string, { weekday: Map<string, number>; month: Map<string, number> }> = {};\n\nfunction localeCalendarDicts(locale?: string): { weekday: Map<string, number>; month: Map<string, number> } {\n const key = locale || \"\";\n if (!key) return { weekday: new Map(), month: new Map() };\n const cached = _localeCalCache[key];\n if (cached) return cached;\n const weekday = new Map<string, number>();\n const month = new Map<string, number>();\n // Add both the raw normalized form and a dot-stripped variant (some locales' short forms\n // carry a trailing '.', e.g. \"lun.\" \u2014 normalize() keeps dots, so index both spellings).\n const add = (map: Map<string, number>, s: string, pos: number) => {\n const n = normalize(s); if (n) map.set(n, pos);\n const nd = normalize(s.replace(/\\./g, \"\")); if (nd && nd !== n) map.set(nd, pos);\n };\n try {\n const wLong = new Intl.DateTimeFormat(key, { weekday: \"long\" });\n const wShort = new Intl.DateTimeFormat(key, { weekday: \"short\" });\n for (let i = 0; i < 7; ++i) { // 2024-01-01 is Monday \u2192 ISO Mon..Sun = 0..6\n const d = new Date(Date.UTC(2024, 0, 1 + i));\n add(weekday, wLong.format(d), i);\n add(weekday, wShort.format(d), i);\n }\n const mLong = new Intl.DateTimeFormat(key, { month: \"long\" });\n const mShort = new Intl.DateTimeFormat(key, { month: \"short\" });\n for (let m = 0; m < 12; ++m) { // mid-month \u2192 no tz day-shift\n const d = new Date(Date.UTC(2020, m, 15));\n add(month, mLong.format(d), m);\n add(month, mShort.format(d), m);\n }\n } catch {\n // Unknown locale tag \u2014 leave the maps empty (English-only fallback).\n }\n const result = { weekday, month };\n _localeCalCache[key] = result;\n return result;\n}\n\n// English base UNION locale extras; the English base wins on any key collision.\nfunction mergeDicts(base: Map<string, number>, extra: Map<string, number>): Map<string, number> {\n if (extra.size === 0) return base;\n const m = new Map(base);\n for (const [k, v] of extra) if (!m.has(k)) m.set(k, v);\n return m;\n}\n\n// Try each calendar family against the user's normalized\u2192original value index.\n// Returns the user's actual strings in calendar order, or null when no family\n// fully contains the value set. `locale` augments the weekday/month families with the\n// report locale's own surface forms (see localeCalendarDicts).\nfunction detectCalendarOrdinal(normIndex: Map<string, string>, locale?: string): OrdinalDetectionResult | null {\n const loc = localeCalendarDicts(locale);\n for (const fam of CALENDAR_FAMILIES) {\n const dict =\n fam.name === \"weekday_mon_sun\" ? mergeDicts(fam.dict, loc.weekday) :\n fam.name === \"month_jan_dec\" ? mergeDicts(fam.dict, loc.month) :\n fam.dict; // quarter stays English (Q1..Q4 near-universal)\n let allInDict = true;\n const byPos = new Map<number, string>(); // position \u2192 first original at that position\n for (const [norm, original] of normIndex) {\n const pos = dict.get(norm);\n if (pos === undefined) { allInDict = false; break; }\n if (!byPos.has(pos)) byPos.set(pos, original);\n }\n if (!allInDict) continue;\n if (byPos.size < fam.minMatches) continue;\n const orderedDomain = [...byPos.entries()].sort((a, b) => a[0] - b[0]).map(e => e[1]);\n return { pattern: fam.name, orderedDomain };\n }\n return null;\n}\n\n// Build a lookup: normalized form \u2192 original user string. When two raw\n// values normalize to the same key (e.g. \"Strongly Disagree\" and\n// \"STRONGLY DISAGREE\"), the first one wins \u2014 that's fine for ordering\n// purposes since they'd render as the same axis category anyway after\n// the LLM's downstream `groupby`.\nfunction buildNormalizedIndex(rawValues: string[]): Map<string, string> {\n const out = new Map<string, string>();\n for (const v of rawValues) {\n if (v === null || v === undefined) continue;\n const s = String(v);\n if (s.length === 0) continue;\n const n = normalize(s);\n if (!out.has(n)) out.set(n, s);\n }\n return out;\n}\n\n// Result of a successful detection.\nexport type OrdinalDetectionResult = {\n // Catalog pattern name (debug + telemetry).\n pattern: string;\n // The USER'S original value strings in canonical pattern order. May be\n // a SUBSET of the pattern's canonical list (when the user's data is\n // missing some pattern entries \u2014 e.g., a 4-pt subset of 5-pt Likert).\n // Ordering is always the pattern's ordering.\n orderedDomain: string[];\n};\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Safe short-code values (Tier B, 2026-05-31)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// For categoricals that are NOT a recognized ordinal but are clearly NON-PII\n// short codes, ship the ACTUAL distinct values (unobfuscated) so the LLM can\n// derive the axis domain from real data instead of guessing labels it can't\n// see (the freemium failure mode). This is a PRIVACY decision (expose the\n// values), NOT an ordering one \u2014 no order is asserted; the server tells the\n// LLM \"derive the domain from these, order not significant.\"\n//\n// A column ships only when EVERY distinct value is \"safe\" AND there are \u2264 15\n// distinct values. A value is safe if EITHER:\n// (1) ALLOWLIST \u2014 it's a known generic, non-PII token (sizes, days, months,\n// quarters, common flags like yes/no/true/false/na). Always OK; these\n// carry no user-specific information by construction.\n// (2) CODE STRUCTURE \u2014 at most ONE letter, at least ONE digit, length \u2264 10\n// (J-01, Q1, '2024-3', pure-numeric '01'). The digit requirement is what\n// excludes 2-letter INITIALS (JC, MB) \u2014 the residual PII risk we\n// flagged \u2014 since alpha-only tokens must instead come from the allowlist.\nconst MAX_SAFE_DISTINCT = 15;\nconst MAX_CODE_LEN = 10;\n\n// Generic, non-PII category tokens that are always safe to ship verbatim.\n// Calendar tokens are folded in from CALENDAR_FAMILIES so a column with FEWER\n// values than Tier A's minMatches (e.g. just {Sat, Sun}) is still covered.\nconst ALWAYS_SAFE_TOKENS: Set<string> = (() => {\n const literal = [\n // sizes\n \"xxs\", \"xs\", \"s\", \"sm\", \"small\", \"m\", \"med\", \"medium\", \"l\", \"lg\", \"large\",\n \"xl\", \"xlarge\", \"xxl\", \"xxxl\", \"2xl\", \"3xl\", \"4xl\",\n // boolean / flags / fillers\n \"y\", \"n\", \"yes\", \"no\", \"t\", \"f\", \"true\", \"false\",\n \"na\", \"n/a\", \"n a\", \"none\", \"null\", \"unknown\", \"other\", \"all\", \"both\",\n // magnitude / severity words (safe standalone even when Tier A doesn't fire)\n \"low\", \"medium\", \"med\", \"high\", \"very low\", \"very high\", \"minimal\", \"critical\", \"urgent\",\n ];\n const set = new Set<string>(literal.map(normalize));\n for (const fam of CALENDAR_FAMILIES) {\n for (const k of fam.dict.keys()) set.add(k); // already normalized\n }\n return set;\n})();\n\nfunction isCodeStructured(raw: string): boolean {\n const s = raw.trim();\n if (s.length === 0 || s.length > MAX_CODE_LEN) return false;\n const letters = (s.match(/\\p{L}/gu) || []).length;\n if (letters > 1) return false; // \u2264 1 letter\n return /\\p{Nd}/u.test(s); // \u2265 1 digit\n}\n\nexport function safeDistinctValuesToShip(rawDistinct: string[]): string[] | null {\n if (!rawDistinct || rawDistinct.length === 0) return null;\n const seen = new Set<string>();\n const out: string[] = [];\n for (const v of rawDistinct) {\n if (v === null || v === undefined) continue;\n const s = String(v);\n if (seen.has(s)) continue;\n const norm = normalize(s);\n const safe = norm === \"\" || ALWAYS_SAFE_TOKENS.has(norm) || isCodeStructured(s);\n if (!safe) return null; // one unsafe value disqualifies the whole column\n seen.add(s);\n out.push(s);\n if (out.length > MAX_SAFE_DISTINCT) return null; // too high cardinality \u2192 not a bounded code set\n }\n return out.length > 0 ? out : null;\n}\n\n// Run all patterns against the user's distinct values; return the\n// highest-scoring match, or null if none qualify.\nexport function detectOrdinalDomain(rawValues: string[], locale?: string): OrdinalDetectionResult | null {\n if (!rawValues || rawValues.length < 2) return null;\n const normIndex = buildNormalizedIndex(rawValues);\n if (normIndex.size < 2) return null;\n\n // Resolve each user value to its canonical term (synonym-aware). canonIndex\n // maps canonical \u2192 the user's ORIGINAL string, so orderedDomain preserves the\n // user's own spelling while matching tolerates synonyms.\n const canonIndex = new Map<string, string>();\n for (const [norm, original] of normIndex) {\n const canon = resolveCanon(norm);\n if (!canonIndex.has(canon)) canonIndex.set(canon, original);\n }\n\n let best: { pattern: OrdinalPattern; matched: number; orderedDomain: string[] } | null = null;\n for (const pattern of PATTERNS) {\n const orderedDomain: string[] = [];\n for (const canonForm of pattern.canonical) {\n const raw = canonIndex.get(canonForm);\n if (raw !== undefined) orderedDomain.push(raw);\n }\n if (orderedDomain.length < pattern.minMatches) continue;\n\n // Subset check: every user value must RESOLVE (directly or via synonym)\n // to a canonical in the pattern. Reject patterns where the user has extra\n // values outside the catalog (e.g. \"Strongly Disagree, Disagree, Refused\").\n const patternSet = new Set(pattern.canonical);\n let allUserValuesInPattern = true;\n for (const normUserValue of normIndex.keys()) {\n if (!patternSet.has(resolveCanon(normUserValue))) { allUserValuesInPattern = false; break; }\n }\n if (!allUserValuesInPattern) continue;\n\n // Score preference (best first):\n // 1. Higher matched count wins.\n // 2. On ties, the SHORTER canonical pattern wins \u2014 represents a\n // tighter / more-specific fit. Example: a column with exactly\n // {strongly disagree, disagree, neutral, agree, strongly agree}\n // matches BOTH likert5_agreement (5 matched / 5 canonical) AND\n // likert7_agreement (5 matched / 7 canonical). The 5pt pattern\n // is the right call \u2014 the user's data IS a 5pt Likert, not a\n // 7pt scale that happens to have 2 categories empty.\n let isBetter: boolean;\n if (best === null) isBetter = true;\n else if (orderedDomain.length > best.matched) isBetter = true;\n else if (orderedDomain.length === best.matched\n && pattern.canonical.length < best.pattern.canonical.length) isBetter = true;\n else isBetter = false;\n if (isBetter) {\n best = { pattern, matched: orderedDomain.length, orderedDomain };\n }\n }\n if (best) return { pattern: best.pattern.name, orderedDomain: best.orderedDomain };\n // No survey/scale pattern \u2014 try the calendar dictionaries (weekday / month /\n // quarter), augmented with the report locale's own surface forms. Disjoint\n // vocabularies, so a column matches at most one family.\n return detectCalendarOrdinal(normIndex, locale);\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,36 +0,0 @@
1
- /*! @bicharts/shape-core — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; US Census/TIGER (public domain). Full text: NOTICE in this package. */
2
-
3
- // src/util.ts
4
- function STR(v) {
5
- if (v !== void 0 && v !== null) {
6
- return v.toString();
7
- }
8
- return "";
9
- }
10
- function SIMPLE_STRING_HASH(str) {
11
- if (!str) return 0;
12
- const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
13
- const FNV_PRIME = 0x100000001b3n;
14
- let hash = FNV_OFFSET_BASIS;
15
- for (let i = 0; i < str.length; i++) {
16
- hash ^= BigInt(str.charCodeAt(i));
17
- hash = hash * FNV_PRIME & 0xFFFFFFFFFFFFFFFFn;
18
- }
19
- return Number(hash & 0x1FFFFFFFFFFFFFn);
20
- }
21
- function GET_RANDOM() {
22
- const g = typeof globalThis !== "undefined" ? globalThis : {};
23
- if (g.crypto && typeof g.crypto.getRandomValues === "function") {
24
- const array = new Uint32Array(1);
25
- g.crypto.getRandomValues(array);
26
- return array[0] / (4294967295 + 1);
27
- }
28
- return Math.floor(Date.now() % 4294967295) / (4294967295 + 1);
29
- }
30
-
31
- export {
32
- STR,
33
- SIMPLE_STRING_HASH,
34
- GET_RANDOM
35
- };
36
- //# sourceMappingURL=chunk-VLMGGCN4.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/util.ts"],
4
- "sourcesContent": ["// Package-owned pure utilities. These were previously reached via the visual's\n// `lib/shared/Shared` (a large, PBI/DOM-coupled grab-bag); shape-core copies only\n// the three pure functions the profiler needs so the package has NO dependency\n// back into the visual. Keep these byte-equivalent to Shared's versions \u2014 the\n// visual and the MCP client must profile identically. (If Shared's versions ever\n// change, mirror the change here; a divergence would make the two hosts disagree.)\n\n/** Null/undefined-safe stringify. Mirrors Shared.STR. */\nexport function STR(v: any): string {\n if (v !== undefined && v !== null) {\n return v.toString();\n }\n return \"\";\n}\n\n/** FNV-1a string hash masked to a JS-safe 53-bit integer. Mirrors\n * Shared.SIMPLE_STRING_HASH. Deterministic \u2014 safe for stable keys. */\nexport function SIMPLE_STRING_HASH(str: string): number {\n if (!str) return 0;\n\n const FNV_OFFSET_BASIS = 0xcbf29ce484222325n;\n const FNV_PRIME = 0x100000001b3n;\n\n let hash = FNV_OFFSET_BASIS;\n\n for (let i = 0; i < str.length; i++) {\n hash ^= BigInt(str.charCodeAt(i));\n hash = (hash * FNV_PRIME) & 0xFFFFFFFFFFFFFFFFn; // Keep it within 64 bits\n }\n\n return Number(hash & 0x1FFFFFFFFFFFFFn); // Mask to JS safe integer (53 bits)\n}\n\n/** Uniform [0,1). Mirrors Shared.GET_RANDOM. Used ONLY by the sample-obfuscation\n * path, never by stat/shape derivation \u2014 so the emitted SHAPE stats stay\n * deterministic across runs and across hosts even though the obfuscated sample\n * text does not. Prefers Web Crypto (browser + Node >=19 expose globalThis.crypto);\n * falls back so the package never throws in a bare runtime. */\nexport function GET_RANDOM(): number {\n const g: any = (typeof globalThis !== \"undefined\") ? globalThis : {};\n if (g.crypto && typeof g.crypto.getRandomValues === \"function\") {\n const array = new Uint32Array(1);\n g.crypto.getRandomValues(array);\n return array[0] / (0xFFFFFFFF + 1);\n }\n // Last-resort fallback (no Web Crypto present). Obfuscation-only, so a weaker\n // source is acceptable here; stat derivation never reaches this.\n return Math.floor(Date.now() % 0xFFFFFFFF) / (0xFFFFFFFF + 1);\n}\n"],
5
- "mappings": ";;;AAQO,SAAS,IAAI,GAAgB;AAChC,MAAI,MAAM,UAAa,MAAM,MAAM;AAC/B,WAAO,EAAE,SAAS;AAAA,EACtB;AACA,SAAO;AACX;AAIO,SAAS,mBAAmB,KAAqB;AACpD,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,mBAAmB;AACzB,QAAM,YAAY;AAElB,MAAI,OAAO;AAEX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,YAAQ,OAAO,IAAI,WAAW,CAAC,CAAC;AAChC,WAAQ,OAAO,YAAa;AAAA,EAChC;AAEA,SAAO,OAAO,OAAO,iBAAiB;AAC1C;AAOO,SAAS,aAAqB;AACjC,QAAM,IAAU,OAAO,eAAe,cAAe,aAAa,CAAC;AACnE,MAAI,EAAE,UAAU,OAAO,EAAE,OAAO,oBAAoB,YAAY;AAC5D,UAAM,QAAQ,IAAI,YAAY,CAAC;AAC/B,MAAE,OAAO,gBAAgB,KAAK;AAC9B,WAAO,MAAM,CAAC,KAAK,aAAa;AAAA,EACpC;AAGA,SAAO,KAAK,MAAM,KAAK,IAAI,IAAI,UAAU,KAAK,aAAa;AAC/D;",
6
- "names": []
7
- }