@intellectif/lk-core 0.14.0 → 0.15.0

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 (49) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -0
  3. package/dist/{activity-BVkg9VDo.d.cts → activity-C7cR8U9n.d.cts} +35 -2
  4. package/dist/{activity-BVkg9VDo.d.ts → activity-C7cR8U9n.d.ts} +35 -2
  5. package/dist/{chunk-R7PV3XIC.js → chunk-54XYQ3CS.js} +7 -4
  6. package/dist/chunk-54XYQ3CS.js.map +1 -0
  7. package/dist/{chunk-J5OIRIOW.cjs → chunk-5KWF34LJ.cjs} +31 -31
  8. package/dist/{chunk-J5OIRIOW.cjs.map → chunk-5KWF34LJ.cjs.map} +1 -1
  9. package/dist/{chunk-CZRUMHKW.cjs → chunk-7ETU6EXQ.cjs} +13 -10
  10. package/dist/chunk-7ETU6EXQ.cjs.map +1 -0
  11. package/dist/{chunk-6RND47QE.js → chunk-DPHX4E7X.js} +62 -3
  12. package/dist/chunk-DPHX4E7X.js.map +1 -0
  13. package/dist/{chunk-NBADQ3JU.js → chunk-EEMVWBPP.js} +195 -4
  14. package/dist/chunk-EEMVWBPP.js.map +1 -0
  15. package/dist/{chunk-RTIR6R2U.js → chunk-L6YBQXSB.js} +2 -2
  16. package/dist/{chunk-VCXIPLEW.cjs → chunk-QFWEF5ST.cjs} +63 -4
  17. package/dist/chunk-QFWEF5ST.cjs.map +1 -0
  18. package/dist/chunk-W7AQP6AV.cjs +418 -0
  19. package/dist/chunk-W7AQP6AV.cjs.map +1 -0
  20. package/dist/{index-v2_-Fyn3.d.ts → index-BWyMDlUS.d.cts} +367 -2
  21. package/dist/{index-BE1bM2Wi.d.cts → index-D10NaztZ.d.ts} +367 -2
  22. package/dist/index.cjs +397 -96
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +103 -7
  25. package/dist/index.d.ts +103 -7
  26. package/dist/index.js +310 -9
  27. package/dist/index.js.map +1 -1
  28. package/dist/schemas.cjs +11 -3
  29. package/dist/schemas.cjs.map +1 -1
  30. package/dist/schemas.d.cts +2 -2
  31. package/dist/schemas.d.ts +2 -2
  32. package/dist/schemas.js +10 -2
  33. package/dist/scoring.cjs +3 -3
  34. package/dist/scoring.d.cts +2 -2
  35. package/dist/scoring.d.ts +2 -2
  36. package/dist/scoring.js +2 -2
  37. package/dist/xapi.cjs +3 -3
  38. package/dist/xapi.d.cts +1 -1
  39. package/dist/xapi.d.ts +1 -1
  40. package/dist/xapi.js +2 -2
  41. package/package.json +1 -1
  42. package/dist/chunk-2NIALQEH.cjs +0 -227
  43. package/dist/chunk-2NIALQEH.cjs.map +0 -1
  44. package/dist/chunk-6RND47QE.js.map +0 -1
  45. package/dist/chunk-CZRUMHKW.cjs.map +0 -1
  46. package/dist/chunk-NBADQ3JU.js.map +0 -1
  47. package/dist/chunk-R7PV3XIC.js.map +0 -1
  48. package/dist/chunk-VCXIPLEW.cjs.map +0 -1
  49. /package/dist/{chunk-RTIR6R2U.js.map → chunk-L6YBQXSB.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/validation-scope.ts","../src/scoring/dictation/normalize.ts","../src/schemas/feedback.ts","../src/schemas/media.ts","../src/schemas/dictation.ts","../src/schemas/fill-in-the-blanks.ts","../src/schemas/multiple-choice.ts","../src/scoring/speech/limits.ts","../src/schemas/read-aloud.ts","../src/scoring/speech/locale.ts","../src/count-words.ts","../src/schemas/gap-select.ts","../src/schemas/written-response.ts","../src/schemas/redacted.ts","../src/scoring/dictation/edit-distance.ts","../src/scoring/dictation/align.ts","../src/scoring/text-match.ts","../src/registry/registry.ts","../src/authoring/issues.ts","../src/authoring/dictation.ts","../src/authoring/fill-in-the-blanks.ts","../src/authoring/gap-select.ts","../src/authoring/multiple-choice.ts","../src/authoring/read-aloud.ts","../src/authoring/written-response.ts","../src/scoring/activity-scorers/dictation.ts","../src/scoring/strategies/all-or-nothing.ts","../src/scoring/strategies/partial.ts","../src/scoring/activity-scorers/fill-in-the-blanks.ts","../src/scoring/activity-scorers/gap-select.ts","../src/scoring/activity-scorers/multiple-choice.ts","../src/registry/builtins.ts"],"sourcesContent":["import type { ValidationError } from './types/activity.js';\n\n/** Thrown when activity data fails schema validation at a component boundary. */\nexport class ActivitySchemaError extends Error {\n constructor(\n public readonly activityType: string,\n public readonly errors: ValidationError[],\n ) {\n super(`Invalid activity data for type \"${activityType}\"`);\n this.name = 'ActivitySchemaError';\n }\n}\n\n/** Thrown when an unrecognised activity type is passed to the scoring engine. */\nexport class UnknownActivityTypeError extends Error {\n constructor(public readonly activityType: string) {\n super(`Activity type \"${activityType}\" is not registered`);\n this.name = 'UnknownActivityTypeError';\n }\n}\n\n/**\n * Thrown when a grading entry point is asked to grade a `redact()` projection\n * (or any activity data whose answer key is missing, yielding a non-finite\n * score). Redacted data is learner-safe precisely because the key was removed,\n * so a score derived from it is meaningless — previously this produced a silent\n * `NaN` that serialized to `null` in a grade column. `evaluate()` answers the\n * same input without throwing, but it never grades it: a synchronously graded\n * type comes back `unscorable`, and a deferred one `deferred`, as it does for\n * any item of that type.\n */\nexport class RedactedScoringError extends Error {\n constructor(public readonly activityType: string) {\n super(\n `Activity data for \"${activityType}\" carries no answer key (it looks redacted), so it cannot be scored. ` +\n 'Score against the full activity data server-side. evaluate() answers this input without throwing, but it never grades it.',\n );\n this.name = 'RedactedScoringError';\n }\n}\n\n/**\n * Thrown when `score()` is called for an activity type whose grading is\n * deferred (asynchronous AI/human grading, e.g. `written-response`). A\n * deferred submission has no synchronous score — treating it as 0 would show\n * a learner a failing grade for work that simply has not been graded yet.\n * Call `evaluate()` instead, which returns `{ status: 'deferred', ... }`.\n */\nexport class DeferredScoringError extends Error {\n constructor(public readonly activityType: string) {\n super(\n `Activity type \"${activityType}\" is graded asynchronously and has no synchronous score. ` +\n `Use evaluate() — it returns { status: 'deferred' } for this type.`,\n );\n this.name = 'DeferredScoringError';\n }\n}\n","/**\n * Work shared for the length of one outermost validation call.\n *\n * A draft is checked twice over — by its type's draft checks, then by its\n * schema — and a group draft checks every item a third time when it validates\n * the group. The dictation normaliser is the expensive part of each, so it\n * memoises under the scope a validator opens here. Entries are keyed by the\n * content they were computed from, never by object identity, so data that\n * changes inside a scope can only miss the cache, never read a stale entry;\n * and the scope ends with the call that opened it, so nothing outlives it.\n *\n * Synchronous only. Outside a scope every cache is absent and the work is done\n * afresh, which is the behaviour of a direct `DictationDataSchema.safeParse`.\n */\nlet active: Map<symbol, unknown> | null = null;\n\n/** Runs `run` inside a validation scope, or inside the one already open. */\nexport function withValidationScope<T>(run: () => T): T {\n if (active !== null) {\n return run();\n }\n active = new Map();\n try {\n return run();\n } finally {\n active = null;\n }\n}\n\n/**\n * The open scope's cache under `key`, created by `create` on first use;\n * `undefined` outside a scope.\n */\nexport function scopeCache<V>(key: symbol, create: () => V): V | undefined {\n if (active === null) {\n return undefined;\n }\n let value = active.get(key) as V | undefined;\n if (value === undefined) {\n value = create();\n active.set(key, value);\n }\n return value;\n}\n","import { scopeCache } from '../../validation-scope.js';\n\n/**\n * The tolerance as the normaliser reads it — the wire `DictationTolerance`, the\n * shape zod infers for it (optional fields typed `| undefined`), or stale data\n * of any shape, which is read defensively rather than thrown on.\n */\nexport type ToleranceLike =\n | {\n readonly equivalences?: readonly { readonly from: string; readonly to: string }[] | undefined;\n }\n | undefined;\n\n/**\n * The longest transcript a dictation accepts, in code points, before and after\n * its equivalences are applied — and its rules may not grow it past twice\n * `DICTATION_MAX_TEXT_LENGTH` on the way. A dictation is a sentence or two; the\n * cap keeps the scorer's quadratic step bounded on the server that grades an\n * exam.\n */\nexport const DICTATION_MAX_TRANSCRIPT_LENGTH = 2000;\n/**\n * The longest learner text the scorer compares, in code points. Text beyond it\n * is cut off — before normalisation, and again after it, because a rewrite rule\n * or a folded character can lengthen text — and the alignment says so.\n */\nexport const DICTATION_MAX_TEXT_LENGTH = 8000;\n/** The longest `from` and `to` of an equivalence rule, in code points. */\nexport const DICTATION_MAX_EQUIVALENCE_LENGTH = 200;\n/**\n * The most equivalence rules a dictation carries. The scorer reads no rule\n * past this many, so stale data with more cannot slow a grading run.\n */\nexport const DICTATION_MAX_EQUIVALENCES = 100;\n/**\n * The most accepted transcripts a dictation carries. The scorer reads no more\n * than this many, for the same reason.\n */\nexport const DICTATION_MAX_ACCEPTED_TRANSCRIPTS = 10;\n\n/**\n * How long text may grow while equivalence rules run, in code points: twice\n * the attempt cap. Every rule rewrites every occurrence of a word, and each\n * runs on the output of the last, so a few rules could otherwise grow one\n * typed letter past what a process can hold. Text the rules grow past this is\n * cut here, as part of its normalisation: the schema refuses a transcript, a\n * title or a recording description that needs the cut, and `alignDictation`\n * reports an attempt that needs it as truncated. No claim is made that the cut\n * text normalises to a prefix of what unbounded rules would give — a later rule\n * that shrinks text can bring back into range what the cut removed, and no\n * bounded computation can reproduce that in general.\n */\nexport const WORKING_LENGTH = 2 * DICTATION_MAX_TEXT_LENGTH;\n\n/**\n * The fewest characters of scripts written without spaces, not counting\n * combining marks or digits, a transcript needs before the reveal check looks\n * for it where its edges cannot be seen. Two or three such characters often spell part\n * of another word — ตา (\"eye\") inside ตาม (\"follow\") — and a title may not be\n * refused for that; four in a row are the transcript wherever they stand.\n */\nconst UNSPACED_REVEAL_MIN_CHARACTERS = 4;\n\n/**\n * Code points are built from their numbers so the source stays ASCII: the\n * formatter rewrites a `\\u` escape in a string into the raw character, and a\n * hyphen and a Unicode hyphen are indistinguishable on screen.\n */\nconst cp = (...points: number[]): string => String.fromCodePoint(...points);\n\nconst NEXT_LINE = cp(0x85);\nconst MIDDLE_DOT = cp(0xb7);\nconst TIBETAN_TSHEG = cp(0x0f0b);\nconst COMBINING_KEYCAP = cp(0x20e3);\nconst WAVING_BLACK_FLAG = 0x1f3f4;\nconst ZWNJ = 0x200c;\nconst ZWJ = 0x200d;\nconst MONGOLIAN_VOWEL_SEPARATOR = 0x180e;\n/** ZERO WIDTH NON-JOINER, ZERO WIDTH JOINER, MONGOLIAN VOWEL SEPARATOR. */\nconst JOINERS = cp(ZWNJ, ZWJ, MONGOLIAN_VOWEL_SEPARATOR);\n/** The Mongolian free variation selectors. */\nconst MONGOLIAN_SELECTORS = cp(0x180b, 0x180c, 0x180d, 0x180f);\n/** The tag characters, U+E0020 to U+E007F, as a character-class range. */\nconst TAGS = `${cp(0xe0020)}-${cp(0xe007f)}`;\n/** ARABIC TATWEEL and NKO LAJANYALAN: letters that only stretch a word. */\nconst STRETCHERS = cp(0x0640, 0x07fa);\n\n/**\n * Compatibility characters, compared as the characters they stand for (their\n * NFKC form): fullwidth and halfwidth forms, which an input method for\n * Chinese, Japanese or Korean types by default; the Latin ligatures and\n * digraphs (ij, ŀ, ʼn, dž, lj, nj, dz) and the presentation forms and ligatures that\n * text copied from a PDF carries; Kangxi radicals, which look like the\n * ideographs they are drawn from; letterlike symbols such as ℃; Roman\n * numerals; vulgar fractions; enclosed, parenthesized, superscript and\n * subscript digits, letters and signs, the circled numbers to 50 included; the\n * micro sign; and the ordinal indicators º and ª. Not the spacing diacritics,\n * which decompose to a space and a combining mark, nor the fullwidth tilde,\n * which {@link FOLDED} reads as the wave dash it is typed for. A presentation\n * form of a combining mark is read by {@link foldCompatibility}.\n */\nconst COMPATIBILITY_RE = new RegExp(\n `[${[\n cp(0xaa),\n cp(0xb2, 0xb3, 0xb5, 0xb9, 0xba),\n `${cp(0xbc)}-${cp(0xbe)}`,\n `${cp(0x132)}-${cp(0x133)}`,\n `${cp(0x13f)}-${cp(0x140)}`,\n cp(0x149),\n `${cp(0x1c4)}-${cp(0x1cc)}`,\n `${cp(0x1f1)}-${cp(0x1f3)}`,\n `${cp(0x2070)}-${cp(0x209f)}`,\n `${cp(0x2100)}-${cp(0x218f)}`,\n `${cp(0x2460)}-${cp(0x24ff)}`,\n `${cp(0x2e80)}-${cp(0x2fdf)}`,\n `${cp(0x3251)}-${cp(0x325f)}`,\n `${cp(0x32b1)}-${cp(0x32bf)}`,\n `${cp(0xfb00)}-${cp(0xfdff)}`,\n `${cp(0xfe10)}-${cp(0xfe19)}`,\n `${cp(0xfe30)}-${cp(0xfe6f)}`,\n `${cp(0xfe70)}-${cp(0xfefe)}`,\n `${cp(0xff01)}-${cp(0xff5d)}`,\n `${cp(0xff5f)}-${cp(0xffe2)}`,\n `${cp(0xffe4)}-${cp(0xffee)}`,\n ].join('')}]`,\n 'gu',\n);\n\n/**\n * The compatibility jamo (U+3131–U+318E) a Korean keyboard types, by the\n * conjoining jamo they share an NFKC form with: a halfwidth jamo stands for the\n * compatibility jamo of the same name, and its NFKC form alone is a conjoining\n * jamo, which composes with the next into a syllable it was never typed as.\n */\nconst COMPATIBILITY_JAMO = new Map(\n Array.from({ length: 0x318e - 0x3131 + 1 }, (_, index) => {\n const jamo = cp(0x3131 + index);\n return [jamo.normalize('NFKC'), jamo] as const;\n }),\n);\n\n/**\n * Sequences written two ways that mean one thing, folded to the one Unicode\n * recommends: the Malayalam chillu letters in their pre-Unicode-5.1 spelling\n * (consonant, virama, joiner), the Bengali khanda ta likewise, the Malayalam\n * nta written with NA rather than chillu N, and the Marathi eyelash ra spelled\n * with RA rather than RRA, or with RRA and a joiner it does not need.\n */\nconst SEQUENCE_FOLDS = new Map<string, string>([\n [cp(0x0d23, 0x0d4d, ZWJ), cp(0x0d7a)],\n [cp(0x0d28, 0x0d4d, ZWJ), cp(0x0d7b)],\n [cp(0x0d30, 0x0d4d, ZWJ), cp(0x0d7c)],\n [cp(0x0d32, 0x0d4d, ZWJ), cp(0x0d7d)],\n [cp(0x0d33, 0x0d4d, ZWJ), cp(0x0d7e)],\n [cp(0x0d15, 0x0d4d, ZWJ), cp(0x0d7f)],\n [cp(0x0d2e, 0x0d4d, ZWJ), cp(0x0d54)],\n [cp(0x0d2f, 0x0d4d, ZWJ), cp(0x0d55)],\n [cp(0x0d34, 0x0d4d, ZWJ), cp(0x0d56)],\n [cp(0x09a4, 0x09cd, ZWJ), cp(0x09ce)],\n [cp(0x0d28, 0x0d4d, 0x0d31), cp(0x0d7b, 0x0d4d, 0x0d31)],\n [cp(0x0930, 0x094d, ZWJ), cp(0x0931, 0x094d)],\n [cp(0x0931, 0x094d, ZWJ), cp(0x0931, 0x094d)],\n]);\nconst SEQUENCE_FOLDS_RE = new RegExp([...SEQUENCE_FOLDS.keys()].join('|'), 'gu');\n/**\n * The Thai and Lao SARA AM typed as its two parts, NIKHAHIT and SARA AA, with\n * any tone marks typed between them — the order that draws like the vowel —\n * or before them: the tone marks, then the vowel.\n */\nconst THAI_SARA_AM_PARTS_RE = new RegExp(\n `${cp(0x0e4d)}([${cp(0x0e48)}-${cp(0x0e4b)}]*)${cp(0x0e32)}`,\n 'gu',\n);\nconst LAO_SARA_AM_PARTS_RE = new RegExp(\n `${cp(0x0ecd)}([${cp(0x0ec8)}-${cp(0x0ecb)}]*)${cp(0x0eb2)}`,\n 'gu',\n);\n\nfunction foldSequences(text: string): string {\n return text\n .replace(SEQUENCE_FOLDS_RE, (sequence) => SEQUENCE_FOLDS.get(sequence) as string)\n .replace(THAI_SARA_AM_PARTS_RE, `$1${cp(0x0e33)}`)\n .replace(LAO_SARA_AM_PARTS_RE, `$1${cp(0x0eb3)}`);\n}\n\n/**\n * Typographic quotation marks folded to the ASCII apostrophe or quote, and the\n * hyphens of other scripts folded to `-`. A phone keyboard types U+2019 for the\n * apostrophe in \"don't\"; without the fold that apostrophe costs an edit against\n * a transcript typed with U+0027. The acute and grave accents (U+00B4, U+0060)\n * are symbols, not punctuation, so they would survive the punctuation strip\n * unless folded here. The Hebrew geresh and maqaf are what an ASCII apostrophe\n * and hyphen stand in for on most keyboards, and the Tibetan non-breaking tsheg\n * is the tsheg.\n */\nconst FOLDED = new Map<string, string>(\n (\n [\n [0x2018, \"'\"], // LEFT SINGLE QUOTATION MARK\n [0x2019, \"'\"], // RIGHT SINGLE QUOTATION MARK (the smart apostrophe)\n [0x201a, \"'\"], // SINGLE LOW-9 QUOTATION MARK\n [0x201b, \"'\"], // SINGLE HIGH-REVERSED-9 QUOTATION MARK\n [0x2032, \"'\"], // PRIME\n [0x02bc, \"'\"], // MODIFIER LETTER APOSTROPHE\n [0x00b4, \"'\"], // ACUTE ACCENT, used as an apostrophe\n [0x0060, \"'\"], // GRAVE ACCENT, used as an apostrophe\n [0x05f3, \"'\"], // HEBREW PUNCTUATION GERESH\n [0x201c, '\"'], // LEFT DOUBLE QUOTATION MARK\n [0x201d, '\"'], // RIGHT DOUBLE QUOTATION MARK\n [0x201e, '\"'], // DOUBLE LOW-9 QUOTATION MARK\n [0x2033, '\"'], // DOUBLE PRIME\n [0x05f4, '\"'], // HEBREW PUNCTUATION GERSHAYIM\n [0x2010, '-'], // HYPHEN\n [0x2011, '-'], // NON-BREAKING HYPHEN\n [0x05be, '-'], // HEBREW PUNCTUATION MAQAF\n [0x058a, '-'], // ARMENIAN HYPHEN\n [0x0f0c, TIBETAN_TSHEG], // TIBETAN MARK DELIMITER TSHEG BSTAR\n [0xff5e, cp(0x301c)], // FULLWIDTH TILDE, which a Japanese keyboard types for the WAVE DASH\n ] as const\n ).map(([point, folded]) => [cp(point), folded]),\n);\nconst FOLDED_RE = new RegExp(`[${[...FOLDED.keys()].join('')}]`, 'gu');\n/**\n * Format characters that are drawn: the Arabic number signs and end of ayah,\n * the Syriac abbreviation mark and the Kaithi number signs (Unicode's\n * Prepended_Concatenation_Mark), each a visible sign over the digits after it.\n */\nconst DRAWN_FORMAT_CHARACTERS = `${cp(0x0600)}-${cp(0x0605)}${cp(0x06dd, 0x070f, 0x0890, 0x0891, 0x08e2, 0x110bd, 0x110cd)}`;\n/**\n * Characters nobody types as spelling: control characters other than the ones\n * that are spacing (tab, line feed, vertical tab, form feed, carriage return,\n * next line), format characters that are not drawn (zero-width space,\n * byte-order mark, soft hyphen), the rest of Unicode's default-ignorable code\n * points — variation selectors, so an emoji typed with or without U+FE0F is the\n * same emoji, and the Hangul fillers — and the Arabic tatweel and N'Ko\n * lajanyalan, which only stretch a word across the line.\n *\n * Left for {@link keepsInContext} to decide: the joiners, the Mongolian\n * selectors and the tag characters, each of which is spelling in one place.\n */\nconst INVISIBLE_RE = new RegExp(\n `(?![\\\\t\\\\n\\\\v\\\\f\\\\r${NEXT_LINE}${JOINERS}${MONGOLIAN_SELECTORS}${TAGS}${DRAWN_FORMAT_CHARACTERS}])[\\\\p{Cc}\\\\p{Cf}\\\\p{Default_Ignorable_Code_Point}${STRETCHERS}]`,\n 'gu',\n);\n/** Two or more of the same joiner in a row, which draw as one. */\nconst REPEATED_JOINER_RE = new RegExp(`([${JOINERS}])\\\\1+`, 'gu');\n/**\n * A lone surrogate, which is no character: read as U+FFFD, so that no removal\n * later in the normalisation can pair two of them into a character it would\n * have removed or lowercased.\n */\nconst LONE_SURROGATE_RE = /[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/g;\nconst REPLACEMENT_CHARACTER = cp(0xfffd);\nconst CONTEXTUAL_RE = new RegExp(`[${JOINERS}${MONGOLIAN_SELECTORS}${TAGS}]`, 'u');\nconst WHITESPACE_RE = new RegExp(`[\\\\s${NEXT_LINE}]+`, 'gu');\n/**\n * The scripts written without spaces between words, by script extension, so\n * the Japanese long-vowel mark and the kana voicing marks count as the kana\n * they belong to.\n */\nconst UNSPACED_CLASS =\n '\\\\p{Script_Extensions=Han}\\\\p{Script_Extensions=Hiragana}\\\\p{Script_Extensions=Katakana}\\\\p{Script_Extensions=Thai}\\\\p{Script_Extensions=Lao}\\\\p{Script_Extensions=Khmer}\\\\p{Script_Extensions=Myanmar}';\n/**\n * A character of a script written without spaces — but not a digit: a number is\n * a word in every script, so a Thai or a Burmese number has edges as a Latin\n * one has.\n */\nconst UNSPACED_CHARACTER = `(?!\\\\p{Nd})[${UNSPACED_CLASS}]`;\n/**\n * The punctuation that is spelling inside a word: the apostrophe (`cat's`), the\n * hyphen (`well-known`, and a hyphen after a Devanagari vowel sign), the middle\n * dot of the Catalan `l·l`, and the Tibetan tsheg between syllables.\n */\nconst IN_WORD_PUNCTUATION = `'\\\\-${MIDDLE_DOT}${TIBETAN_TSHEG}`;\n/**\n * Every punctuation code point, except in-word punctuation with a letter, a\n * combining mark or a digit on both sides, and the `#` or `*` of a keycap.\n * A middle dot beside a character of a script written without spaces is\n * punctuation — the dot between the parts of a name in Chinese, which Japanese\n * writes as U+30FB. Symbols (`\\p{S}`: `$`, `+`, the euro sign, the minus\n * sign) are never removed; `&`, `%`, `/`, `@` and `#` are punctuation and are.\n * Each alternative starts with the character it removes, so a look-behind is\n * read only where that character stands.\n */\nconst PUNCTUATION_RE = new RegExp(\n [\n `[${IN_WORD_PUNCTUATION}](?<![\\\\p{L}\\\\p{M}\\\\p{N}][${IN_WORD_PUNCTUATION}])`,\n `[${IN_WORD_PUNCTUATION}](?![\\\\p{L}\\\\p{M}\\\\p{N}])`,\n `${MIDDLE_DOT}(?<=${UNSPACED_CHARACTER}${MIDDLE_DOT})`,\n `${MIDDLE_DOT}(?=${UNSPACED_CHARACTER})`,\n `(?![#*]${COMBINING_KEYCAP})[^\\\\P{P}${IN_WORD_PUNCTUATION}]`,\n ].join('|'),\n 'gu',\n);\n/** Every punctuation mark but a keycap's `#` or `*`, in-word punctuation included. */\nconst ALL_PUNCTUATION_RE = new RegExp(`(?![#*]${COMBINING_KEYCAP})\\\\p{P}`, 'gu');\n/** A full stop, comma, apostrophe or Arabic separator inside a number: `1,100`, `3.5`. */\nconst DIGIT_SEPARATOR_RE = new RegExp(`(?<=\\\\p{N})[.,'${cp(0x066b, 0x066c)}](?=\\\\p{N})`, 'gu');\n\n/** The characters a candidate's length in scripts without spaces counts: not its marks, not its digits. */\nconst UNSPACED_LETTERS_RE = new RegExp(`(?![\\\\p{M}\\\\p{Nd}])[${UNSPACED_CLASS}]`, 'gu');\n/**\n * A space beside a character of a script written without spaces, with a letter,\n * mark or digit on its other side — `我 爱 北京`, and `我有 3 本书` for `我有3本书`.\n */\nconst SPACE_BESIDE_UNSPACED_RE = new RegExp(\n `(?<=${UNSPACED_CHARACTER}) (?=[\\\\p{L}\\\\p{M}\\\\p{N}])|(?<=[\\\\p{L}\\\\p{M}\\\\p{N}]) (?=${UNSPACED_CHARACTER})`,\n 'gu',\n);\n/**\n * A word is a run of letters, combining marks and digits, and the joiners kept\n * inside one: a mark belongs to the letter before it, so a rule for the Hindi\n * word for \"eight\" does not rewrite the start of the word for \"all eight\", and\n * a Persian half-space is part of its word.\n */\nconst WORD_RE = new RegExp(`^[\\\\p{L}\\\\p{M}\\\\p{N}${JOINERS}]$`, 'u');\nconst UNSPACED_CHARACTER_RE = new RegExp(`^${UNSPACED_CHARACTER}$`, 'u');\n/**\n * A letter of a script written without spaces, anywhere in a text. Letters\n * only: the ideographic comma and full stop carry the Han script extension\n * too, and a Korean sentence punctuated with them is still written with spaces\n * between its words.\n */\nconst UNSPACED_LETTER_RE = new RegExp(`(?=\\\\p{L})${UNSPACED_CHARACTER}`, 'u');\n\n/**\n * Whether `text` contains a letter of a script written without spaces between\n * words — Han, Hiragana, Katakana, Thai, Lao, Khmer or Myanmar, by script\n * extension. The tokeniser splits on spaces alone, so a run of such letters is\n * one word however many it holds, which is why a read-aloud reference refuses\n * one. A digit is a word in every script and does not count.\n */\nexport function containsUnspacedScript(text: string): boolean {\n return UNSPACED_LETTER_RE.test(text);\n}\n/** What is drawn onto the character before it: a match never ends just before one. */\nconst EXTENDING_RE = new RegExp(`^[\\\\p{M}${JOINERS}${TAGS}]$`, 'u');\n\nconst WORD = 1;\nconst UNSPACED = 2;\nconst EXTENDING = 4;\nconst KNOWN = 8;\n/**\n * What each character is, as the bits above, worked out once per character:\n * the rules ask it of the character beside every occurrence they test, and a\n * regular expression per question made a rule that fails its boundary at every\n * letter of a long text take seconds. One table of 64 KB per Unicode plane,\n * made the first time a character of that plane is asked about, so the cache\n * never holds more than the planes a process has read.\n */\nconst FLAGS: (Uint8Array | undefined)[] = [];\n\nfunction flagsOfCharacter(character: string): number {\n return (\n KNOWN |\n (WORD_RE.test(character) ? WORD : 0) |\n (UNSPACED_CHARACTER_RE.test(character) ? UNSPACED : 0) |\n (EXTENDING_RE.test(character) ? EXTENDING : 0)\n );\n}\n\n/** The flags of the code point that starts at UTF-16 index `index` of `text`. */\nfunction flagsAt(text: string, index: number): number {\n const point = text.codePointAt(index) as number;\n let plane = FLAGS[point >> 16];\n if (plane === undefined) {\n plane = new Uint8Array(0x10000);\n FLAGS[point >> 16] = plane;\n }\n const offset = point & 0xffff;\n let flags = plane[offset] as number;\n if (flags === 0) {\n flags = flagsOfCharacter(String.fromCodePoint(point));\n plane[offset] = flags;\n }\n return flags;\n}\n\n/**\n * The viramas — every character of Canonical_Combining_Class 9 in Unicode 16.0\n * (DerivedCombiningClass.txt): a joiner beside one chooses how a consonant\n * cluster is drawn.\n */\nconst VIRAMAS = new Set([\n 0x94d, 0x9cd, 0xa4d, 0xacd, 0xb4d, 0xbcd, 0xc4d, 0xccd, 0xd3b, 0xd3c, 0xd4d, 0xdca, 0xe3a, 0xeba,\n 0xf84, 0x1039, 0x103a, 0x1714, 0x1715, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2,\n 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x10a3f, 0x11046, 0x11070,\n 0x1107f, 0x110b9, 0x11133, 0x11134, 0x111c0, 0x11235, 0x112ea, 0x1134d, 0x113ce, 0x113cf, 0x113d0,\n 0x11442, 0x114c2, 0x115bf, 0x1163f, 0x116b6, 0x1172b, 0x11839, 0x1193d, 0x1193e, 0x119e0, 0x11a34,\n 0x11a47, 0x11a99, 0x11c3f, 0x11d44, 0x11d45, 0x11d97, 0x11f41, 0x11f42, 0x1612f,\n]);\n/**\n * The letters that join the letter after them — Joining_Type D or L in Unicode\n * 16.0 (DerivedJoiningType.txt) — as inclusive ranges: Arabic, Syriac, N'Ko,\n * Mandaic, Mongolian, Phags-pa, Manichaean, Psalter Pahlavi, Hanifi Rohingya,\n * Sogdian, Old Uyghur, Chorasmian and Adlam.\n */\nconst JOINS_FORWARD: readonly (readonly [number, number])[] = [\n [0x620, 0x620],\n [0x626, 0x626],\n [0x628, 0x628],\n [0x62a, 0x62e],\n [0x633, 0x63f],\n [0x641, 0x647],\n [0x649, 0x64a],\n [0x66e, 0x66f],\n [0x678, 0x687],\n [0x69a, 0x6bf],\n [0x6c1, 0x6c2],\n [0x6cc, 0x6cc],\n [0x6ce, 0x6ce],\n [0x6d0, 0x6d1],\n [0x6fa, 0x6fc],\n [0x6ff, 0x6ff],\n [0x712, 0x714],\n [0x71a, 0x71d],\n [0x71f, 0x727],\n [0x729, 0x729],\n [0x72b, 0x72b],\n [0x72d, 0x72e],\n [0x74e, 0x758],\n [0x75c, 0x76a],\n [0x76d, 0x770],\n [0x772, 0x772],\n [0x775, 0x777],\n [0x77a, 0x77f],\n [0x7ca, 0x7ea],\n [0x841, 0x845],\n [0x848, 0x848],\n [0x84a, 0x853],\n [0x855, 0x855],\n [0x860, 0x860],\n [0x862, 0x865],\n [0x868, 0x868],\n [0x886, 0x886],\n [0x889, 0x88d],\n [0x8a0, 0x8a9],\n [0x8af, 0x8b0],\n [0x8b3, 0x8b8],\n [0x8ba, 0x8c8],\n [0x1807, 0x1807],\n [0x1820, 0x1878],\n [0x1887, 0x18a8],\n [0x18aa, 0x18aa],\n [0xa840, 0xa872],\n [0x10ac0, 0x10ac4],\n [0x10acd, 0x10acd],\n [0x10ad3, 0x10adc],\n [0x10ade, 0x10ae0],\n [0x10aeb, 0x10aee],\n [0x10b80, 0x10b80],\n [0x10b82, 0x10b82],\n [0x10b86, 0x10b88],\n [0x10b8a, 0x10b8b],\n [0x10b8d, 0x10b8d],\n [0x10b90, 0x10b90],\n [0x10bad, 0x10bae],\n [0x10d00, 0x10d21],\n [0x10d23, 0x10d23],\n [0x10ec3, 0x10ec4],\n [0x10f30, 0x10f32],\n [0x10f34, 0x10f44],\n [0x10f51, 0x10f53],\n [0x10f70, 0x10f73],\n [0x10f76, 0x10f81],\n [0x10fb0, 0x10fb0],\n [0x10fb2, 0x10fb3],\n [0x10fb8, 0x10fb8],\n [0x10fbb, 0x10fbc],\n [0x10fbe, 0x10fbf],\n [0x10fc1, 0x10fc1],\n [0x10fc4, 0x10fc4],\n [0x10fca, 0x10fcb],\n [0x1e900, 0x1e943],\n];\n/** The letters that join the letter before them — Joining_Type D or R — as inclusive ranges. */\nconst JOINS_BACKWARD: readonly (readonly [number, number])[] = [\n [0x620, 0x620],\n [0x622, 0x63f],\n [0x641, 0x64a],\n [0x66e, 0x66f],\n [0x671, 0x673],\n [0x675, 0x6d3],\n [0x6d5, 0x6d5],\n [0x6ee, 0x6ef],\n [0x6fa, 0x6fc],\n [0x6ff, 0x6ff],\n [0x710, 0x710],\n [0x712, 0x72f],\n [0x74d, 0x77f],\n [0x7ca, 0x7ea],\n [0x840, 0x858],\n [0x860, 0x860],\n [0x862, 0x865],\n [0x867, 0x86a],\n [0x870, 0x882],\n [0x886, 0x886],\n [0x889, 0x88e],\n [0x8a0, 0x8ac],\n [0x8ae, 0x8c8],\n [0x1807, 0x1807],\n [0x1820, 0x1878],\n [0x1887, 0x18a8],\n [0x18aa, 0x18aa],\n [0xa840, 0xa871],\n [0x10ac0, 0x10ac5],\n [0x10ac7, 0x10ac7],\n [0x10ac9, 0x10aca],\n [0x10ace, 0x10ad6],\n [0x10ad8, 0x10ae1],\n [0x10ae4, 0x10ae4],\n [0x10aeb, 0x10aef],\n [0x10b80, 0x10b91],\n [0x10ba9, 0x10bae],\n [0x10d01, 0x10d23],\n [0x10ec2, 0x10ec4],\n [0x10f30, 0x10f44],\n [0x10f51, 0x10f54],\n [0x10f70, 0x10f81],\n [0x10fb0, 0x10fb0],\n [0x10fb2, 0x10fb6],\n [0x10fb8, 0x10fbf],\n [0x10fc1, 0x10fc4],\n [0x10fc9, 0x10fca],\n [0x1e900, 0x1e943],\n];\n/**\n * What a joining letter joins through (Joining_Type T): combining marks, the\n * Adlam nasalization mark, and the tag characters, which are still in the text\n * when a joiner's context is read.\n */\nconst TRANSPARENT_RE = new RegExp(`^[\\\\p{Mn}\\\\p{Me}${cp(0x1e94b)}${TAGS}]$`, 'u');\n\nfunction collapseWhitespace(text: string): string {\n return text.replace(WHITESPACE_RE, ' ').trim();\n}\n\n/** The number of code points in `text`. */\nexport function codePointLength(text: string): number {\n let length = 0;\n for (const _ of text) {\n length += 1;\n }\n return length;\n}\n\n/**\n * The first `max` code points of `text`; `text` itself when it is no longer\n * than that. Walks only as far as the cut, so a text of any size costs no more\n * than `max` code points.\n */\nexport function truncateCodePoints(text: string, max: number): string {\n if (text.length <= max) {\n return text;\n }\n let points = 0;\n let units = 0;\n for (const character of text) {\n if (points === max) {\n return text.slice(0, units);\n }\n points += 1;\n units += character.length;\n }\n return text;\n}\n\n/** Whether `text` is longer than `max` code points, without counting past the answer. */\nexport function isLongerThan(text: string, max: number): boolean {\n if (text.length <= max) {\n return false;\n }\n return text.length > 2 * max || truncateCodePoints(text, max).length < text.length;\n}\n\nfunction inRanges(point: number, ranges: readonly (readonly [number, number])[]): boolean {\n return ranges.some(([first, last]) => point >= first && point <= last);\n}\n\n/** A Mongolian letter or mark: the block's letters from U+1820 on (its few unassigned code points read the same). */\nfunction isMongolianLetterOrMark(point: number | undefined): boolean {\n return point !== undefined && point >= 0x1820 && point <= 0x18aa;\n}\n\n/** A Mongolian free variation selector. */\nfunction isMongolianSelector(point: number | undefined): boolean {\n return point !== undefined && ((point >= 0x180b && point <= 0x180d) || point === 0x180f);\n}\n\n/** The nearest code point before `index` in `points` that is not a nonspacing or enclosing mark. */\nfunction letterBefore(points: readonly string[], index: number): number | undefined {\n for (let at = index - 1; at >= 0; at -= 1) {\n const point = points[at] as string;\n if (!TRANSPARENT_RE.test(point)) {\n return point.codePointAt(0);\n }\n }\n return undefined;\n}\n\nfunction letterAfter(points: readonly string[], index: number): number | undefined {\n for (let at = index + 1; at < points.length; at += 1) {\n const point = points[at] as string;\n if (!TRANSPARENT_RE.test(point)) {\n return point.codePointAt(0);\n }\n }\n return undefined;\n}\n\nconst LETTER_OR_MARK_RE = /^[\\p{L}\\p{M}]$/u;\n\n/**\n * Whether the joiner or Mongolian selector at `index` is kept: where it changes\n * what is written, as the rules for joiners in internationalised domain names\n * (RFC 5892, CONTEXTJ) and Unicode's own script chapters and Mongolian\n * specification read.\n *\n * - A zero-width joiner directly after a virama — a half form, a Sinhala\n * conjunct, an eyelash ra — or between a letter and the virama after it, where\n * Unicode uses it for another written form of the cluster: the Bengali\n * ya-phalaa after RA, a Sinhala touching conjunct, a RA without its reph.\n * - A zero-width non-joiner directly after a virama; or between a letter that\n * joins the one after it and a letter that joins the one before it\n * (transparent characters between them skipped), where it breaks a join — the\n * Persian half-space of می‌خواهم, but not the invisible one after ز in روز‌ها.\n * - The Mongolian vowel separator between a Mongolian letter, mark or free\n * variation selector and a Mongolian letter or mark.\n * - A Mongolian free variation selector directly after a Mongolian letter or\n * mark.\n *\n * A repeated joiner has been read as one before this is asked, and the tag\n * characters are {@link settle}'s to decide.\n */\nfunction keepsInContext(points: readonly string[], index: number): boolean {\n const point = (points[index] as string).codePointAt(0) as number;\n const previous = points[index - 1];\n const afterVirama = previous !== undefined && VIRAMAS.has(previous.codePointAt(0) as number);\n if (point === ZWJ) {\n const next = points[index + 1];\n return (\n afterVirama ||\n (previous !== undefined &&\n LETTER_OR_MARK_RE.test(previous) &&\n next !== undefined &&\n VIRAMAS.has(next.codePointAt(0) as number))\n );\n }\n if (point === ZWNJ) {\n if (afterVirama) {\n return true;\n }\n const before = letterBefore(points, index);\n const after = letterAfter(points, index);\n return (\n before !== undefined &&\n after !== undefined &&\n inRanges(before, JOINS_FORWARD) &&\n inRanges(after, JOINS_BACKWARD)\n );\n }\n const previousPoint = previous?.codePointAt(0);\n if (point === MONGOLIAN_VOWEL_SEPARATOR) {\n return (\n (isMongolianLetterOrMark(previousPoint) || isMongolianSelector(previousPoint)) &&\n isMongolianLetterOrMark(points[index + 1]?.codePointAt(0))\n );\n }\n return isMongolianLetterOrMark(previousPoint);\n}\n\n/**\n * `text` with its sequences folded, a repeated joiner read as one, and its\n * joiners, Mongolian selectors and tag characters removed wherever they are not\n * in context — a tag character is kept in a run of them directly after U+1F3F4,\n * a subdivision flag — spacing collapsed and NFC applied; repeated until\n * nothing changes, because each removal can bring together characters that\n * compose, or take away the context of another.\n */\nfunction settle(text: string): string {\n let current = text;\n for (;;) {\n let next = foldSequences(current);\n if (CONTEXTUAL_RE.test(next)) {\n const points = Array.from(next.replace(REPEATED_JOINER_RE, '$1'));\n // Decided in one pass: whether the characters so far are U+1F3F4 and a run of tags after it.\n let inFlag = false;\n next = points\n .filter((character, index) => {\n const point = character.codePointAt(0) as number;\n if (point >= 0xe0020 && point <= 0xe007f) {\n return inFlag;\n }\n inFlag = point === WAVING_BLACK_FLAG;\n return !CONTEXTUAL_RE.test(character) || keepsInContext(points, index);\n })\n .join('');\n }\n next = collapseWhitespace(next).normalize('NFC');\n if (next === current) {\n return current;\n }\n current = next;\n }\n}\n\n/** A compatibility form that is a combining mark, alone or on the space or tatweel that carries it in isolation. */\nconst CARRIED_MARKS_RE = new RegExp(`^[ ${cp(0x0640)}]?(\\\\p{M}+)$`, 'u');\nconst SPACING_CHARACTER_RE = new RegExp(`[\\\\s${NEXT_LINE}]`, 'u');\nconst PUNCTUATION_CHARACTER_RE = /^\\p{P}$/u;\n\n/**\n * A compatibility character's NFKC form, read in its place in `text`:\n *\n * - A halfwidth jamo is the compatibility jamo it stands for, not the\n * conjoining jamo of its NFKC form.\n * - A presentation form of a combining mark — the halfwidth voiced and\n * semi-voiced sound marks, an Arabic vowel mark's isolated or medial form —\n * is that mark when a character precedes it to carry it, so `ガ` is `ガ`;\n * at the start of the text or after a space it keeps its own form rather\n * than put a mark on nothing.\n * - A punctuation mark whose NFKC form is a mark on a space, the dashed\n * overlines, keeps its own form, and is removed as punctuation.\n */\nfunction foldCompatibility(character: string, offset: number, text: string): string {\n const folded = character.normalize('NFKC');\n const carried = CARRIED_MARKS_RE.exec(folded);\n if (carried === null) {\n return COMPATIBILITY_JAMO.get(folded) ?? folded;\n }\n return PUNCTUATION_CHARACTER_RE.test(character) ||\n offset === 0 ||\n SPACING_CHARACTER_RE.test(text.charAt(offset - 1))\n ? character\n : (carried[1] as string);\n}\n\n/**\n * Steps 1–6 of the normalisation: lone surrogates read as U+FFFD, NFC,\n * compatibility characters folded, invisible characters removed, quotes and\n * hyphens folded, lowercased, whitespace collapsed, NFC again, then the\n * sequence folds and the joiner context settled. What an equivalence rule's `from` is matched as, and what it\n * is matched against: a symbol such as `&` survives, so a rule may name it\n * before the punctuation strip would remove it.\n */\nexport function preStripNormalize(text: string): string {\n return settle(\n collapseWhitespace(\n text\n .replace(LONE_SURROGATE_RE, REPLACEMENT_CHARACTER)\n .normalize('NFC')\n .replace(COMPATIBILITY_RE, foldCompatibility)\n .replace(INVISIBLE_RE, '')\n .replace(FOLDED_RE, (character) => FOLDED.get(character) as string)\n .toLowerCase(),\n ).normalize('NFC'),\n );\n}\n\n/** `text` with `pattern` replaced by `replacement` and settled, until nothing changes. */\nfunction settleWithout(text: string, pattern: RegExp, replacement: string): string {\n let current = text;\n for (;;) {\n const next = settle(current.replace(pattern, replacement));\n if (next === current) {\n return current;\n }\n current = next;\n }\n}\n\n/** Steps 8–10: punctuation removed except inside a word, then settled again. */\nfunction stripPunctuation(text: string): string {\n return settleWithout(text, PUNCTUATION_RE, '');\n}\n\n/**\n * The working text with every punctuation mark read as a space — in-word ones\n * too, but not a separator inside a number — for the reveal search, so a\n * transcript quoted as `Listen:hello` or `听写:你好` is found although the\n * strip joins it to the word before, and `rock n roll` inside `rock'n'roll`.\n */\nfunction spacePunctuation(text: string): string {\n return settleWithout(text.replace(DIGIT_SEPARATOR_RE, ''), ALL_PUNCTUATION_RE, ' ');\n}\n\nfunction isHighSurrogate(unit: number): boolean {\n return unit >= 0xd800 && unit <= 0xdbff;\n}\n\nfunction isLowSurrogate(unit: number): boolean {\n return unit >= 0xdc00 && unit <= 0xdfff;\n}\n\n/** The UTF-16 index where the code point ending at `index` (past the start of `text`) starts. */\nfunction startOfCodePointBefore(text: string, index: number): number {\n return index >= 2 &&\n isLowSurrogate(text.charCodeAt(index - 1)) &&\n isHighSurrogate(text.charCodeAt(index - 2))\n ? index - 2\n : index - 1;\n}\n\n/**\n * The last code point of a string that ended with `previous` (`''` when it was\n * empty) once `appended` is added. The text is well formed — a lone surrogate\n * was read as U+FFFD — so no pair is ever split between the two.\n */\nfunction lastCodePointAfter(previous: string, appended: string): string {\n return appended === ''\n ? previous\n : appended.slice(startOfCodePointBefore(appended, appended.length));\n}\n\n/**\n * An ASCII digit or lowercase letter: the ASCII word characters of text that\n * has been lowercased, as everything matched here has. Answered without a\n * regular expression because the rules ask once per occurrence they rewrite.\n */\nfunction isAsciiWordUnit(unit: number): boolean {\n return (unit >= 0x30 && unit <= 0x39) || (unit >= 0x61 && unit <= 0x7a);\n}\n\nfunction isWordAt(text: string, index: number): boolean {\n const unit = text.charCodeAt(index);\n return unit < 0x80 ? isAsciiWordUnit(unit) : (flagsAt(text, index) & WORD) !== 0;\n}\n\n/** A word character of a script that separates its words with spaces. */\nfunction isSpacedWordAt(text: string, index: number): boolean {\n const unit = text.charCodeAt(index);\n return unit < 0x80 ? isAsciiWordUnit(unit) : (flagsAt(text, index) & (WORD | UNSPACED)) === WORD;\n}\n\nfunction isExtendingAt(text: string, index: number): boolean {\n return text.charCodeAt(index) >= 0x80 && (flagsAt(text, index) & EXTENDING) !== 0;\n}\n\n/**\n * How a match's edge meets the text beside it.\n *\n * - `none` — no boundary: the edge is a symbol or a punctuation mark (`&` fires\n * in `rock&roll`), or a letter of a script written without spaces, where a\n * rule's word has no visible edge to require.\n * - `spaced` — the edge is a word character of a spaced script, and a word\n * character of a spaced script beside it means the match is inside a longer\n * word (`cat` never fires in `category`). A letter of another script beside\n * it is a visible edge.\n * - `visible` — the edge is a letter of a script written without spaces, and\n * any word character beside it hides where the match's word ends: the reveal\n * check's reading of a short transcript.\n */\ntype Edge = 'none' | 'spaced' | 'visible';\n\n/** Text searched for, with how each of its edges meets its neighbours. */\ninterface Phrase {\n text: string;\n before: Edge;\n after: Edge;\n}\n\nfunction edgeOf(text: string, index: number, unspaced: 'none' | 'visible'): Edge {\n if (!isWordAt(text, index)) {\n return 'none';\n }\n return isSpacedWordAt(text, index) ? 'spaced' : unspaced;\n}\n\nfunction phraseOf(text: string, unspaced: 'none' | 'visible'): Phrase {\n return {\n text,\n before: edgeOf(text, 0, unspaced),\n after: edgeOf(text, startOfCodePointBefore(text, text.length), unspaced),\n };\n}\n\nfunction blocks(edge: Edge, text: string, neighbour: number): boolean {\n if (edge === 'none') {\n return false;\n }\n return edge === 'spaced' ? isSpacedWordAt(text, neighbour) : isWordAt(text, neighbour);\n}\n\n/**\n * Whether an occurrence of `phrase` at UTF-16 index `at` of `text` is a match:\n * its edges meet their neighbours as {@link Edge} says, and nothing that draws\n * onto a character follows it. Both strings are well formed — a lone surrogate\n * was read as U+FFFD — so an occurrence always starts and ends on code-point\n * boundaries.\n */\nfunction isMatchAt(text: string, phrase: Phrase, at: number): boolean {\n const end = at + phrase.text.length;\n if (end < text.length && (isExtendingAt(text, end) || blocks(phrase.after, text, end))) {\n return false;\n }\n return !(at > 0 && blocks(phrase.before, text, startOfCodePointBefore(text, at)));\n}\n\n/**\n * The UTF-16 index of the first match of `phrase` in `text` at or after\n * `start`, or `-1`. An occurrence refused because a word character of a\n * spaced script comes before it is inside a word, and so is every occurrence\n * that starts later in that word: the search goes on after it, so a rule that\n * finds its letter at every position of a long word costs that word once.\n */\nfunction indexOfPhrase(text: string, phrase: Phrase, start: number): number {\n const needle = phrase.text;\n let at = text.indexOf(needle, start);\n while (at !== -1) {\n if (\n phrase.before === 'spaced' &&\n at > 0 &&\n isSpacedWordAt(text, startOfCodePointBefore(text, at))\n ) {\n let next = at;\n while (next < text.length && isSpacedWordAt(text, next)) {\n next +=\n isHighSurrogate(text.charCodeAt(next)) && isLowSurrogate(text.charCodeAt(next + 1))\n ? 2\n : 1;\n }\n at = text.indexOf(needle, next);\n continue;\n }\n if (isMatchAt(text, phrase, at)) {\n return at;\n }\n at = text.indexOf(needle, at + 1);\n }\n return -1;\n}\n\n/**\n * Whether `text` holds a match of `phrase`, found in time linear in both\n * lengths (Knuth–Morris–Pratt over UTF-16 units): a search that restarts after\n * each rejected occurrence costs the phrase's length per character on text\n * that repeats it.\n */\nfunction containsPhrase(text: string, phrase: Phrase): boolean {\n const needle = phrase.text;\n const length = needle.length;\n if (length === 0 || length > text.length) {\n return false;\n }\n const fallback = new Int32Array(length);\n for (let index = 1, matched = 0; index < length; index += 1) {\n while (matched > 0 && needle.charCodeAt(index) !== needle.charCodeAt(matched)) {\n matched = fallback[matched - 1] as number;\n }\n if (needle.charCodeAt(index) === needle.charCodeAt(matched)) {\n matched += 1;\n }\n fallback[index] = matched;\n }\n for (let index = 0, matched = 0; index < text.length; index += 1) {\n while (matched > 0 && text.charCodeAt(index) !== needle.charCodeAt(matched)) {\n matched = fallback[matched - 1] as number;\n }\n if (text.charCodeAt(index) === needle.charCodeAt(matched)) {\n matched += 1;\n }\n if (matched === length) {\n if (isMatchAt(text, phrase, index - length + 1)) {\n return true;\n }\n matched = fallback[matched - 1] as number;\n }\n }\n return false;\n}\n\ninterface Rule {\n from: Phrase;\n to: string;\n /**\n * Whether `to` starts, or ends, with a word character of a spaced script: a\n * space then sets it apart from such a character it would otherwise touch.\n */\n spaceBefore: boolean;\n spaceAfter: boolean;\n}\n\n/** A tolerance's rules, and the text that identifies them for a memo. */\ninterface CompiledRules {\n rules: readonly Rule[];\n signature: string;\n}\n\n/**\n * The rules of a tolerance, each as the pre-strip text it matches and the\n * words it inserts. `to` is inserted already normalised — its punctuation\n * removed — because the comparison ignores that punctuation anyway, and text\n * that only looks longer must not bring the working text closer to its cut.\n *\n * Only the first `DICTATION_MAX_EQUIVALENCES` entries are read, and each half of\n * a rule once. A rule that is not two strings, whose `from` or `to` is longer\n * than `DICTATION_MAX_EQUIVALENCE_LENGTH`, whose `from` folds to nothing, or\n * whose `to` normalises to nothing (it would delete a word) is skipped — stale\n * data only, since the schema refuses all of them — rather than thrown on.\n */\nfunction compileRules(tolerance: ToleranceLike): CompiledRules {\n const equivalences = tolerance?.equivalences;\n if (!Array.isArray(equivalences)) {\n return { rules: [], signature: '[]' };\n }\n const rules: Rule[] = [];\n for (const rule of equivalences.slice(0, DICTATION_MAX_EQUIVALENCES)) {\n const rawFrom: unknown = rule?.from;\n const rawTo: unknown = rule?.to;\n if (\n typeof rawFrom !== 'string' ||\n typeof rawTo !== 'string' ||\n isLongerThan(rawFrom, DICTATION_MAX_EQUIVALENCE_LENGTH) ||\n isLongerThan(rawTo, DICTATION_MAX_EQUIVALENCE_LENGTH)\n ) {\n continue;\n }\n const from = preStripNormalize(rawFrom);\n const to = stripPunctuation(preStripNormalize(rawTo));\n if (from === '' || to === '') {\n continue;\n }\n rules.push({\n from: phraseOf(from, 'none'),\n to,\n spaceBefore: isSpacedWordAt(to, 0),\n spaceAfter: isSpacedWordAt(to, startOfCodePointBefore(to, to.length)),\n });\n }\n return {\n rules,\n signature: JSON.stringify(rules.map((rule) => [rule.from.text, rule.to])),\n };\n}\n\n/** Normalised text, and whether the working text was cut at `WORKING_LENGTH` on the way. */\nexport interface NormalizedText {\n text: string;\n cut: boolean;\n}\n\n/** `text` cut at `WORKING_LENGTH` code points, when it is longer. */\nfunction bounded(text: string): NormalizedText {\n return isLongerThan(text, WORKING_LENGTH)\n ? { text: truncateCodePoints(text, WORKING_LENGTH), cut: true }\n : { text, cut: false };\n}\n\n/**\n * `text` with every match of `rule` rewritten, left to right and without\n * overlaps, except that it stops as soon as the output is longer than\n * `WORKING_LENGTH` code points and returns it cut there — the cut output is a\n * prefix of the full one, so nothing past the cut is computed.\n *\n * Where the rewrite would touch a word character of a spaced script in the\n * output, and its own edge there is one too (`50%` with `% → percent`), it is\n * set apart by a space. Both sides are decided on the output: after a rewrite,\n * by the text that follows it there — which, for a match right after it, is\n * that match's rewrite.\n */\nfunction rewrite(text: string, rule: Rule): NormalizedText {\n let output = '';\n let consumed = 0;\n // The last code point of `output`, carried along rather than read back:\n // reading a string that is being built by appending makes the engine copy it\n // whole, and every rewrite would cost the length of everything before it.\n let last = '';\n for (\n let at = indexOfPhrase(text, rule.from, 0);\n at !== -1;\n at = indexOfPhrase(text, rule.from, consumed)\n ) {\n const between = text.slice(consumed, at);\n // The space after the last rewrite, when text follows it before this one;\n // with none between, this rewrite's own space before decides.\n if (rule.spaceAfter && consumed > 0 && between !== '' && isSpacedWordAt(between, 0)) {\n output += ' ';\n }\n output += between;\n last = lastCodePointAfter(last, between);\n if (rule.spaceBefore && last !== '' && isSpacedWordAt(last, 0)) {\n output += ' ';\n last = ' ';\n }\n output += rule.to;\n last = lastCodePointAfter(last, rule.to);\n consumed = at + rule.from.text.length;\n // More UTF-16 units than twice the limit is more code points than the limit.\n if (output.length > 2 * WORKING_LENGTH) {\n return { text: truncateCodePoints(output, WORKING_LENGTH), cut: true };\n }\n }\n const rest = text.slice(consumed);\n const apart = rule.spaceAfter && consumed > 0 && rest !== '' && isSpacedWordAt(rest, 0);\n return bounded(apart ? `${output} ${rest}` : output + rest);\n}\n\nconst WORKING_TEXTS = Symbol('dictation working texts');\nconst MEASURED_TRANSCRIPTS = Symbol('dictation measured transcripts');\nconst SHOWN_TEXTS = Symbol('dictation shown texts');\nconst REVEALS = Symbol('dictation reveal answers');\n\n/** A map for one tolerance's results, kept for the open validation scope — or a fresh one outside it. */\nfunction scopedMap<V>(key: symbol, signature: string): Map<string, V> {\n const bySignature = scopeCache(key, () => new Map<string, Map<string, V>>());\n if (bySignature === undefined) {\n return new Map();\n }\n let map = bySignature.get(signature);\n if (map === undefined) {\n map = new Map();\n bySignature.set(signature, map);\n }\n return map;\n}\nconst PRE_STRIP_TEXTS = Symbol('dictation pre-strip texts');\n\n/** `bounded(preStripNormalize(text))`, remembered for the rest of an open validation scope. */\nfunction boundedPreStrip(text: string): NormalizedText {\n const memo = scopeCache(PRE_STRIP_TEXTS, () => new Map<string, NormalizedText>());\n let result = memo?.get(text);\n if (result === undefined) {\n result = bounded(preStripNormalize(text));\n memo?.set(text, result);\n }\n return result;\n}\n\n/**\n * Whether nothing of `text` is left once its invisible characters, spacing and\n * folds are read — before any rule or the punctuation strip — normalised through\n * the same memo as the working text, so a draft's \"not written yet\" check and\n * the rules read one normalisation.\n */\nexport function isBlankBeforeRules(text: string): boolean {\n return boundedPreStrip(text).text === '';\n}\n\n/**\n * The pre-strip working text after every rule, cut at `WORKING_LENGTH`\n * whenever it grows past it. Remembered for the rest of an open validation\n * scope, by the rules' text and the input's.\n */\nfunction workingText(\n text: string,\n compiled: CompiledRules,\n preStripped: NormalizedText = boundedPreStrip(text),\n): NormalizedText {\n if (compiled.rules.length === 0) {\n return preStripped;\n }\n const memo = scopeCache(WORKING_TEXTS, () => new Map<string, Map<string, NormalizedText>>());\n let known = memo?.get(compiled.signature);\n const remembered = known?.get(text);\n if (remembered !== undefined) {\n return remembered;\n }\n let working = preStripped;\n let cut = working.cut;\n for (const rule of compiled.rules) {\n working = rewrite(working.text, rule);\n cut ||= working.cut;\n }\n const result = { text: working.text, cut };\n if (memo !== undefined) {\n if (known === undefined) {\n known = new Map();\n memo.set(compiled.signature, known);\n }\n known.set(text, result);\n }\n return result;\n}\n\n/**\n * `normalizeDictationText`, with whether the working text was cut on the way:\n * what `alignDictation` reports as `truncated` even when the result is short.\n */\nexport function normalizeDictationTextBounded(\n text: string,\n tolerance?: ToleranceLike,\n): NormalizedText {\n const working = workingText(text, compileRules(tolerance));\n return { text: stripPunctuation(working.text), cut: working.cut };\n}\n\n/**\n * What a dictation compares: `text` with everything that is not spelling\n * removed, applied identically to the transcript and to the attempt.\n *\n * In order: a lone surrogate read as U+FFFD; NFC; compatibility characters\n * folded to what they stand for, in place (see `COMPATIBILITY_RE` and\n * `foldCompatibility`); invisible characters removed — control characters other\n * than spacing, format characters that are not drawn, default-ignorable code\n * points, the Arabic tatweel and N'Ko lajanyalan — except the joiners,\n * Mongolian selectors and tag characters left to the context check; quotes and\n * hyphens folded (see `FOLDED`); locale-insensitive lowercase; whitespace\n * (including NEXT LINE) collapsed to one space; NFC; sequences written two ways\n * folded (see `SEQUENCE_FOLDS`), a repeated joiner read as one, and each\n * joiner, selector or tag removed unless its context keeps it (see\n * `keepsInContext` and `settle`), until nothing changes; each equivalence rule\n * applied once, in listed order, to every match — a word being a run of\n * letters, combining marks, digits and kept joiners, so an apostrophe or a\n * hyphen ends one; a `from` edge that is a symbol, or a letter (not a digit) of\n * a script written without spaces, needs no boundary; and no match ends before\n * a combining mark — inserting `to` as its words, without its punctuation, and\n * set apart by a space from a word character of a spaced script it would touch\n * in the output; the\n * rules run after the collapse, so a multi-word `from` matches across a tab or\n * a newline, and before the punctuation strip, so a rule may name a symbol or a\n * punctuation mark; then punctuation removed except an apostrophe, hyphen,\n * middle dot or tsheg inside a word — a middle dot beside a character of a\n * script written without spaces is punctuation — and the `#` or `*` of a\n * keycap, and the folds and the context check applied again, until nothing\n * changes.\n *\n * Idempotent without a tolerance. With one, the rules are a fixed, ordered,\n * single pass, not a rewriting system: `[a → b, b → c]` sends `a` to `c`,\n * `[b → c, a → b]` sends `a` to `b`, and no rule can loop. `to` is inserted\n * literally — `$&` inserts a dollar sign, which is a symbol, and an ampersand,\n * which is punctuation and is removed — and no rule can turn a word into\n * nothing.\n *\n * Bounded for any input: while the rules run, the working text is cut at\n * twice `DICTATION_MAX_TEXT_LENGTH` code points, before the first rule and\n * after each one.\n */\nexport function normalizeDictationText(text: string, tolerance?: ToleranceLike): string {\n return normalizeDictationTextBounded(text, tolerance).text;\n}\n\nexport type { Edge };\n\n/** How the reveal search reads a candidate transcript. */\nexport interface RevealReading {\n /**\n * The texts looked for, as written and with its rules applied, each with its\n * punctuation removed and with every punctuation mark read as a space — a\n * separator inside a number removed in both — and, for a long candidate,\n * with each space beside a character of a script without spaces removed.\n */\n forms: readonly string[];\n /**\n * Four or more characters of scripts written without spaces, not counting\n * combining marks or digits, in the transcript as written.\n */\n long: boolean;\n /**\n * How its first and last characters, as written, meet the text beside them —\n * the same for every form, so a rule that changes the script at its edge\n * (`两 → 2`) does not change how it is found.\n */\n before: Edge;\n after: Edge;\n}\n\n/** A candidate transcript as the length, emptiness and reveal rules read it. */\nexport interface MeasuredTranscript {\n /**\n * Longer than `DICTATION_MAX_TRANSCRIPT_LENGTH` before or after its rules are\n * applied, or grown past the working bound while they ran.\n */\n tooLong: boolean;\n /** Its normalised text; `null` when it was already too long to normalise. */\n normalized: string | null;\n /** How the reveal search reads it: nothing to look for when it was too long to normalise. */\n reveal: RevealReading;\n}\n\n/** A title or a recording description as the reveal rule reads it. */\nexport interface ShownText {\n /**\n * Longer than the working bound raw, or grown past it by the rules: too long\n * to be searched for a transcript in full.\n */\n tooLong: boolean;\n /**\n * The forms searched: the text with its punctuation removed and with every\n * punctuation mark read as a space — a separator inside a number removed in\n * both — with its rules applied and without them. Empty when `tooLong`.\n */\n forms: readonly string[];\n}\n\nconst NOTHING_TO_REVEAL: RevealReading = { forms: [], long: false, before: 'none', after: 'none' };\n\n/** `text` with its punctuation removed, a separator inside a number included: `1'100` is one number. */\nfunction stripForReveal(text: string): string {\n return stripPunctuation(text.replace(DIGIT_SEPARATOR_RE, ''));\n}\n\n/**\n * The reveal reading of a candidate whose pre-strip text is `written` and whose\n * working text, its rules applied, is `working`. The transcript as written\n * decides whether it is long and how its edges are found; one that is only\n * punctuation until its rules rewrite it is read as rewritten.\n */\nfunction readingOf(written: string, working: string): RevealReading {\n const writtenStripped = stripForReveal(written);\n const workingStripped = stripForReveal(working);\n const source = writtenStripped === '' ? workingStripped : writtenStripped;\n if (source === '') {\n return NOTHING_TO_REVEAL;\n }\n const long = (source.match(UNSPACED_LETTERS_RE)?.length ?? 0) >= UNSPACED_REVEAL_MIN_CHARACTERS;\n const { before, after } = phraseOf(source, long ? 'none' : 'visible');\n const forms = [\n writtenStripped,\n spacePunctuation(written),\n workingStripped,\n spacePunctuation(working),\n ].map((form) => (long ? form.replace(SPACE_BESIDE_UNSPACED_RE, '') : form));\n return { forms: [...new Set(forms)].filter((form) => form !== ''), long, before, after };\n}\n\n/** The texts of one dictation, normalised under its tolerance. */\nexport interface DictationNormalizer {\n measure(text: string): MeasuredTranscript;\n shown(text: string): ShownText;\n}\n\n/**\n * A normaliser for one dictation's candidate transcripts and shown texts,\n * compiling its rules once and remembering each string it has read, so the\n * several rules that read a transcript — its emptiness, its length, its\n * duplicates, whether a title reveals it — normalise it once. A raw string over\n * its cap is not normalised at all.\n */\nexport function dictationNormalizer(tolerance: ToleranceLike): DictationNormalizer {\n const compiled = compileRules(tolerance);\n // Shared for the rest of an open validation scope, by the rules' text, so a\n // draft's checks and its schema read the very same results — which is what\n // lets the reveal search remember its answers too.\n const measured = scopedMap<MeasuredTranscript>(MEASURED_TRANSCRIPTS, compiled.signature);\n const shownTexts = scopedMap<ShownText>(SHOWN_TEXTS, compiled.signature);\n return {\n measure(text) {\n let result = measured.get(text);\n if (result === undefined) {\n if (isLongerThan(text, DICTATION_MAX_TRANSCRIPT_LENGTH)) {\n result = { tooLong: true, normalized: null, reveal: NOTHING_TO_REVEAL };\n } else {\n const written = boundedPreStrip(text);\n const working = workingText(text, compiled, written);\n const normalized = stripPunctuation(working.text);\n result = {\n tooLong: working.cut || isLongerThan(normalized, DICTATION_MAX_TRANSCRIPT_LENGTH),\n normalized,\n reveal: readingOf(written.text, working.text),\n };\n }\n measured.set(text, result);\n }\n return result;\n },\n shown(text) {\n let result = shownTexts.get(text);\n if (result === undefined) {\n // The text as written, without its rules, is searched too: a rule may\n // rewrite the title's copy of the transcript while leaving the\n // transcript alone.\n const plainText = isLongerThan(text, WORKING_LENGTH) ? null : boundedPreStrip(text);\n const working = plainText === null ? null : workingText(text, compiled, plainText);\n if (plainText === null || working === null || working.cut) {\n result = { tooLong: true, forms: [] };\n } else {\n const plain = plainText.text;\n result = {\n tooLong: false,\n forms: [\n ...new Set([\n stripForReveal(working.text),\n spacePunctuation(working.text),\n stripForReveal(plain),\n spacePunctuation(plain),\n ]),\n ],\n };\n }\n shownTexts.set(text, result);\n }\n return result;\n },\n };\n}\n\n/**\n * Whether `forms` (a {@link ShownText}'s) contain `candidate`, a measured\n * transcript, in any of its {@link RevealReading} forms.\n *\n * A candidate with fewer than `UNSPACED_REVEAL_MIN_CHARACTERS` characters of\n * scripts written without spaces is found only where its edges show: a letter\n * of such a script at its edge needs something other than a letter, mark or\n * digit beside it. One with at least that many is found wherever its\n * characters stand, with each space beside a character of such a script\n * ignored on both sides. A letter or digit of a spaced script at its edge needs\n * its boundary either way. A candidate too long for the schema, or one that\n * normalised to nothing, reveals nothing.\n */\nexport function revealsCandidate(forms: readonly string[], candidate: MeasuredTranscript): boolean {\n const memo = scopeCache(REVEALS, () => new WeakMap<object, WeakMap<object, boolean>>());\n const known = memo?.get(forms)?.get(candidate);\n if (known !== undefined) {\n return known;\n }\n const answer = searchReveal(forms, candidate);\n if (memo !== undefined) {\n const byCandidate = memo.get(forms) ?? new WeakMap<object, boolean>();\n byCandidate.set(candidate, answer);\n memo.set(forms, byCandidate);\n }\n return answer;\n}\n\n/** A shown text's forms with each space beside a character of a script without spaces removed, made once per forms. */\nconst COMPACTED_FORMS = new WeakMap<readonly string[], readonly string[]>();\n\nfunction searchReveal(forms: readonly string[], candidate: MeasuredTranscript): boolean {\n const { reveal } = candidate;\n if (candidate.tooLong) {\n return false;\n }\n let fields = forms;\n if (reveal.long) {\n fields =\n COMPACTED_FORMS.get(forms) ?? forms.map((form) => form.replace(SPACE_BESIDE_UNSPACED_RE, ''));\n COMPACTED_FORMS.set(forms, fields);\n }\n return reveal.forms.some((text) => {\n const phrase: Phrase = { text, before: reveal.before, after: reveal.after };\n return fields.some((field) => containsPhrase(field, phrase));\n });\n}\n","import { z } from 'zod/v4';\n\n/**\n * Optional authored \"overall feedback\" shown after submission, selected by\n * whether the learner passed (h5p-style overall feedback). Both fields are\n * optional; non-empty when present.\n */\nexport const FeedbackSchema = z.looseObject({\n correct: z.string().min(1).optional(),\n incorrect: z.string().min(1).optional(),\n});\n","import { z } from 'zod/v4';\n\n/**\n * Optional media attached to an activity, rendered above the question or\n * passage — e.g. a recording to listen to, or an embedded video to watch\n * before answering. URL-only by design: hosting/delivery (S3/CDN, or the\n * provider's own embed for `embed`) is the consuming app's responsibility\n * (see requirements \"Non-Goals and Shared Responsibility\").\n *\n * - `image`: rendered as `<img>` — `alt` is REQUIRED (WCAG 1.1.1 / Req 14.5).\n * - `audio` / `video`: rendered with native controls; `alt` is an optional\n * accessible label; `captionsUrl` points at a WebVTT `<track>`. `url` must\n * be a direct media file (NOT a YouTube/Vimeo page — use `embed` for those).\n * - `embed`: rendered as a sandboxed `<iframe>` for provider players\n * (YouTube/Vimeo/etc.). `url` MUST be the provider's *embeddable* URL\n * (e.g. `https://www.youtube.com/embed/<id>`). `alt` is REQUIRED and used\n * as the iframe's accessible `title` (WCAG 4.1.2 / 2.4.1).\n */\n/**\n * Media URL policy (security-reviewed): absolute URLs must use `https:`,\n * `http:`, `data:`, or `blob:`; root-relative paths (`/media/x.mp3`) are\n * allowed for same-origin hosting. Everything else — notably `javascript:`,\n * `file:`, `ftp:` — is rejected: these URLs land in `src` attributes\n * (including an iframe for `embed`), so an unvetted scheme is a stored-XSS\n * vector in every consuming app.\n */\nexport const MediaUrlSchema = z.union([\n z.url().refine((value) => /^(https?|data|blob):/i.test(value), {\n error: 'Absolute media URLs must use the https:, http:, data:, or blob: scheme.',\n }),\n z.string().regex(/^\\/(?!\\/)\\S*$/, {\n error: 'Relative media URLs must be root-relative (a single leading \"/\").',\n }),\n]);\n\n/**\n * Hints to the BROWSER'S OWN control bar, emitted as `controlsList` tokens.\n *\n * They remove a button, never a capability, and only in engines that implement\n * `controlsList` at all. `hide-download` does NOT prevent a download — the URL\n * is in the page and the bytes are in the network panel. If a recording must\n * not be kept, issue a short-lived signed URL; that is the consuming\n * application's control, not the SDK's.\n *\n * Meaningful only alongside the native bar: there is nothing to hint at once\n * the SDK owns the transport.\n */\nexport const NativeControlHintSchema = z.enum(['hide-download', 'hide-rate']);\n\n/**\n * How an audio recording may be played.\n *\n * STRICT on purpose: an unknown key here is an authoring error. {@link\n * MediaSchema} is loose, so without this a typo one level down (`maxPlay`,\n * `seeking`) would be silently accepted and the whole policy would quietly do\n * nothing on an exam that believed it was enforced.\n *\n * Nothing here is required together with anything else — `{ maxPlays: 2 }` is a\n * complete, valid policy, because `controls` and `seek` RESOLVE to the only\n * values that can keep that promise. The refinements below reject only a\n * combination an author wrote **explicitly** that the renderer cannot honour.\n */\nexport const MediaPlaybackSchema = z.strictObject({\n /**\n * `native` (the resolved default) renders today's `<audio controls>`\n * unchanged. `minimal` — resolved automatically whenever any enforcement\n * field is set — replaces the browser bar with the SDK transport:\n * play/pause, elapsed/total, mute, volume, optional speed, optional\n * scrubber, and a live plays-remaining status.\n */\n controls: z.enum(['native', 'minimal']).optional(),\n /**\n * How many times the recording may be STARTED. A play is consumed when\n * playback begins from anywhere other than where it last stopped, so\n * pausing, resuming, and paging between the questions of one listening\n * group are all free. Enforced in `practice` and `exam`; never in `review`.\n */\n maxPlays: z.number().int().min(1).max(20).optional(),\n /** `none` renders no scrubber and reverts an out-of-band seek to the high-water mark. */\n seek: z.enum(['allow', 'none']).optional(),\n /** `fixed` renders no speed control and snaps `playbackRate` back to 1. */\n rate: z.enum(['allow', 'fixed']).optional(),\n /** Advisory only. See {@link NativeControlHintSchema}. */\n nativeControlHints: z.array(NativeControlHintSchema).min(1).max(2).optional(),\n});\n\nexport const MediaSchema = z\n .looseObject({\n type: z.enum(['image', 'audio', 'video', 'embed']),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n captionsUrl: MediaUrlSchema.optional(),\n playback: MediaPlaybackSchema.optional(),\n })\n .refine(\n (m) =>\n (m.type !== 'image' && m.type !== 'embed') || (typeof m.alt === 'string' && m.alt.length > 0),\n {\n error: 'image and embed media require non-empty alt text (WCAG 1.1.1 / 4.1.2).',\n path: ['alt'],\n },\n )\n .refine((m) => m.type !== 'embed' || /^https?:\\/\\//i.test(m.url), {\n error:\n 'embed media requires an absolute http(s) provider URL. data:, blob:, and relative URLs are not allowed for embeds — the embed iframe runs with allow-scripts, and a data:/same-origin document there is an XSS vector.',\n path: ['url'],\n })\n .refine((m) => m.playback === undefined || m.type === 'audio', {\n error:\n 'playback policy is supported on audio media only. An embed is a provider iframe the SDK cannot control at all (it has no reliable JS API for a third-party player); an image has nothing to play; video is deliberately deferred, because a video transport must also own fullscreen and Picture-in-Picture and the SDK will not pretend to govern those yet.',\n path: ['playback'],\n })\n .refine(\n (m) =>\n m.playback?.controls !== 'native' ||\n (m.playback.maxPlays === undefined &&\n m.playback.seek !== 'none' &&\n m.playback.rate !== 'fixed'),\n {\n error:\n 'controls: \"native\" cannot carry maxPlays, seek: \"none\" or rate: \"fixed\". The browser\\'s own bar keeps its play button enabled after a budget is spent, and keeps a scrubber that would move and then silently snap back — a control that looks operable and does nothing (WCAG 3.2.2 / 4.1.3). Omit `controls` and the SDK transport is used automatically, or use nativeControlHints for a cosmetic hint.',\n path: ['playback', 'controls'],\n },\n )\n .refine((m) => m.playback?.maxPlays === undefined || m.playback.seek !== 'allow', {\n error:\n 'maxPlays cannot be combined with seek: \"allow\". A play is consumed when playback starts from somewhere other than where it stopped, so scrubbing back mid-play would replay the whole recording without spending anything. Omit `seek` — it resolves to \"none\" under a budget.',\n path: ['playback', 'seek'],\n })\n .refine(\n (m) =>\n m.playback?.controls !== 'minimal' ||\n m.playback.maxPlays !== undefined ||\n m.playback.seek === 'none' ||\n m.playback.rate === 'fixed',\n {\n error:\n 'controls: \"minimal\" with nothing to enforce trades the browser\\'s localized, familiar control bar for the SDK\\'s, and buys nothing. Set maxPlays, seek: \"none\" or rate: \"fixed\", or omit `controls`.',\n path: ['playback', 'controls'],\n },\n )\n .refine(\n (m) =>\n m.playback?.nativeControlHints === undefined ||\n (m.playback.controls !== 'minimal' &&\n m.playback.maxPlays === undefined &&\n m.playback.seek !== 'none' &&\n m.playback.rate !== 'fixed'),\n {\n error:\n \"nativeControlHints only affects the browser's own control bar, and an enforcing policy replaces that bar with the SDK transport — so the hints would be silently inert. Use them on an otherwise unrestricted recording, or drop them.\",\n path: ['playback', 'nativeControlHints'],\n },\n );\n\n/**\n * The strict counterpart of {@link MediaSchema}, for the redacted shapes.\n *\n * `RedactedActivityDataSchema` and `RedactedStimulusSchema` are `strictObject`\n * precisely so an unknown key is a validation failure rather than a\n * passthrough — but they embedded the LOOSE `MediaSchema`, so the strictness\n * stopped at the media boundary and `media.secretAnswerHint` passed\n * `assertRedacted`. This closes it.\n */\nexport const RedactedMediaSchema = z\n .strictObject({\n type: z.enum(['image', 'audio', 'video', 'embed']),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n captionsUrl: MediaUrlSchema.optional(),\n playback: MediaPlaybackSchema.optional(),\n })\n .refine(\n (m) =>\n (m.type !== 'image' && m.type !== 'embed') || (typeof m.alt === 'string' && m.alt.length > 0),\n {\n error: 'image and embed media require non-empty alt text (WCAG 1.1.1 / 4.1.2).',\n path: ['alt'],\n },\n )\n .refine((m) => m.type !== 'embed' || /^https?:\\/\\//i.test(m.url), {\n error:\n 'embed media requires an absolute http(s) provider URL. data:, blob:, and relative URLs are not allowed for embeds — the embed iframe runs with allow-scripts, and a data:/same-origin document there is an XSS vector.',\n path: ['url'],\n });\n","import { z } from 'zod/v4';\nimport {\n codePointLength,\n DICTATION_MAX_ACCEPTED_TRANSCRIPTS,\n DICTATION_MAX_EQUIVALENCE_LENGTH,\n DICTATION_MAX_EQUIVALENCES,\n DICTATION_MAX_TRANSCRIPT_LENGTH,\n type DictationNormalizer,\n dictationNormalizer,\n type MeasuredTranscript,\n normalizeDictationText,\n preStripNormalize,\n revealsCandidate,\n WORKING_LENGTH,\n} from '../scoring/dictation/normalize.js';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema, MediaUrlSchema } from './media.js';\n\n/**\n * Zod schema for the slower second recording of a dictation. Loose like every\n * content schema (a sidecar survives), audio only, and — deliberately — with no\n * `captionsUrl` and no `playback` of its own; the activity-level guards below\n * refuse both if an author writes them anyway.\n */\nexport const DictationSlowMediaSchema = z.looseObject({\n type: z.literal('audio'),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n});\n\n/**\n * A rule's `from` or `to`. The cap is enforced in code points by the rule's\n * refinements; the metadata states it in the JSON Schema export, whose\n * `maxLength` counts code points too.\n */\nconst RuleTextSchema = z.string().min(1).meta({ maxLength: DICTATION_MAX_EQUIVALENCE_LENGTH });\n\n/** A transcript or an accepted transcript: the raw cap, stated the same way. */\nconst TranscriptTextSchema = z.string().min(1).meta({ maxLength: DICTATION_MAX_TRANSCRIPT_LENGTH });\n\n/**\n * Zod schema for one whole-word rewrite rule. Both halves are bounded: `to`\n * because a rule expands text, `from` because it is compiled into the pattern\n * every attempt is searched with.\n */\nexport const DictationEquivalenceSchema = z\n .looseObject({\n from: RuleTextSchema,\n to: RuleTextSchema,\n })\n .refine((rule) => codePointLength(rule.from) <= DICTATION_MAX_EQUIVALENCE_LENGTH, {\n error: `A rule rewrites at most ${DICTATION_MAX_EQUIVALENCE_LENGTH} characters.`,\n path: ['from'],\n })\n .refine((rule) => codePointLength(rule.to) <= DICTATION_MAX_EQUIVALENCE_LENGTH, {\n error: `A rewrite is at most ${DICTATION_MAX_EQUIVALENCE_LENGTH} characters.`,\n path: ['to'],\n });\n\n/** Zod schema for a dictation's grading tolerances. At most `DICTATION_MAX_EQUIVALENCES` rules. */\nexport const DictationToleranceSchema = z.looseObject({\n equivalences: z\n .array(DictationEquivalenceSchema)\n .max(DICTATION_MAX_EQUIVALENCES, {\n error: `A dictation carries at most ${DICTATION_MAX_EQUIVALENCES} equivalence rules.`,\n })\n .optional(),\n});\n\n/** The fields every guard below reads. */\ninterface GuardedDictation {\n transcript: string;\n acceptedTranscripts?: string[] | undefined;\n tolerance?: z.infer<typeof DictationToleranceSchema> | undefined;\n}\n\n/**\n * One normaliser per parse. Zod hands every refinement of one parse the same\n * freshly built object, so keying by it shares the work between the guards and\n * can never return a result for data that has since changed.\n */\nconst normalizers = new WeakMap<object, DictationNormalizer>();\n\nfunction normalizerFor(data: GuardedDictation): DictationNormalizer {\n let normalizer = normalizers.get(data);\n if (normalizer === undefined) {\n normalizer = dictationNormalizer(data.tolerance);\n normalizers.set(data, normalizer);\n }\n return normalizer;\n}\n\nfunction measure(data: GuardedDictation, text: string): MeasuredTranscript {\n return normalizerFor(data).measure(text);\n}\n\n/** The transcript and every accepted alternative, in order. */\nfunction candidatesOf(data: GuardedDictation): string[] {\n return [data.transcript, ...(data.acceptedTranscripts ?? [])];\n}\n\n/** Written, within the cap, and nothing survives normalisation. */\nfunction isEmpty(measured: MeasuredTranscript): boolean {\n return measured.normalized === '';\n}\n\n/**\n * A check zod runs even when another field was refused. An ordinary check is\n * skipped once any issue has been reported, so an author fixing a recording's\n * address would only then learn that the title gives the answer away. The\n * check itself decides which of its guards the refused fields leave nothing to\n * read.\n */\nfunction checkEvenAfterIssues<T>(\n check: (payload: z.core.ParsePayload<T>) => void,\n): z.core.$ZodCheck<T> {\n const guard: z.core.$ZodCheck<T> = new z.core.$ZodCheck({ check: 'custom', when: () => true });\n guard._zod.check = check;\n return guard;\n}\n\n/**\n * Zod schema validating the full Dictation data contract.\n *\n * Twelve semantic guards, none expressible in JSON Schema, each an authoring\n * error that would otherwise reach a learner or a grade:\n *\n * 1. **The transcript survives normalisation** — the only division by zero the\n * scorer could meet, refused at authoring time.\n * 2. **The transcript is bounded**, raw and after its rules (a rewrite rule or\n * NFC composition can lengthen it), and its rules may not grow it past the\n * working bound on the way, so a valid transcript is never cut by the\n * normaliser.\n * 3. **So is every accepted transcript**, and each survives normalisation.\n * 4. **No two candidates normalise equal** — a second copy of the transcript\n * is dead weight that hides an authoring mistake.\n * 5. **The recording is audio.** A dictation is dictated.\n * 6. **The slow recording carries no playback policy**; it follows `media`'s.\n * 7. **A slow recording never sits beside a play budget** — a budget on one\n * file and a free second file of the same content is no budget.\n * 8. **The slow recording is a different file.**\n * 9. **A slow recording accompanies a recording** — never a stand-in for it,\n * which would also let a capped group stimulus be bypassed.\n * 10. **A rule rewrites something into something**: `from` must survive case,\n * quote and whitespace folding (a symbol such as `&` is fine — `from` is\n * checked BEFORE the punctuation strip, or the remedy for `&` could not\n * be authored), and `to` must survive full normalisation, so no rule can\n * empty a word.\n * 11. **No captions on either recording** — the captions are the answer.\n * 12. **The title and the recording descriptions do not contain the\n * transcript** — they are shown before the learner types — and each is\n * short enough, raw and once its rules are applied, to be searched in full.\n *\n * Every guard that reads a transcript shares one normalisation of it per\n * parse. Guards 11 and 12 report at the field that is wrong, since either of\n * two or three fields can be. The guards run even when an unrelated field was\n * refused, but a guard that reads a field zod refused waits until that field\n * parses: zod leaves a refused optional field — the rules, the accepted\n * transcripts, a recording — out of the value the guards read, and a refused\n * transcript or title is not text.\n */\nconst DictationDataShape = z.looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('dictation'),\n id: z.string().min(1),\n title: z.string().min(1),\n transcript: TranscriptTextSchema,\n acceptedTranscripts: z\n .array(TranscriptTextSchema)\n .max(DICTATION_MAX_ACCEPTED_TRANSCRIPTS)\n .optional(),\n media: MediaSchema.optional(),\n slowMedia: DictationSlowMediaSchema.optional(),\n hints: z.looseObject({ mode: z.literal('progressive-words') }).optional(),\n tolerance: DictationToleranceSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n});\n\nexport const DictationDataSchema = DictationDataShape.check(\n checkEvenAfterIssues<z.output<typeof DictationDataShape>>((ctx) => {\n // Refused at the root: the value is not an object, and there is nothing to guard.\n if (ctx.issues.some((issue) => (issue.path?.length ?? 0) === 0)) {\n return;\n }\n const data = ctx.value;\n // Zod leaves an optional key out of the parsed value when anything inside it\n // was refused, so a guard reading that key would read it as absent — rules\n // that rescue a transcript, a recording a slow one accompanies — and keeps a\n // refused required key as it was given, which need not be text. A guard\n // that depends on such a key waits until it parses.\n const refused = new Set(ctx.issues.map((issue) => String(issue.path?.[0])));\n const report = (path: readonly (string | number)[], input: unknown, message: string) => {\n ctx.issues.push({ code: 'custom', input, message, path: [...path] });\n };\n const rulesParse = !refused.has('tolerance');\n const transcriptParses = !refused.has('transcript');\n\n if (rulesParse && transcriptParses) {\n const transcript = measure(data, data.transcript);\n if (isEmpty(transcript)) {\n report(\n ['transcript'],\n data.transcript,\n 'The transcript must contain something to type: after ignoring case, punctuation and spacing nothing is left.',\n );\n }\n if (transcript.tooLong) {\n report(\n ['transcript'],\n data.transcript,\n `A transcript is at most ${DICTATION_MAX_TRANSCRIPT_LENGTH} characters, before and after its equivalences are applied, and its equivalences may not grow it past ${WORKING_LENGTH} characters on the way.`,\n );\n }\n if (!refused.has('acceptedTranscripts')) {\n const accepted = data.acceptedTranscripts ?? [];\n if (\n accepted.some((entry) => {\n const measured = measure(data, entry);\n return measured.tooLong || isEmpty(measured);\n })\n ) {\n report(\n ['acceptedTranscripts'],\n data.acceptedTranscripts,\n `An accepted transcript must contain something to type and be at most ${DICTATION_MAX_TRANSCRIPT_LENGTH} characters, before and after its equivalences are applied, and its equivalences may not grow it past ${WORKING_LENGTH} characters on the way.`,\n );\n }\n // A candidate too long to normalise is the length guard's failure, not a duplicate.\n const normalized = candidatesOf(data)\n .map((entry) => measure(data, entry).normalized)\n .filter((entry): entry is string => entry !== null);\n if (new Set(normalized).size !== normalized.length) {\n report(\n ['acceptedTranscripts'],\n data.acceptedTranscripts,\n 'An accepted transcript must differ from the transcript and from the other accepted transcripts once case, punctuation and spacing are ignored.',\n );\n }\n }\n }\n\n const mediaParses = !refused.has('media');\n const slowParses = !refused.has('slowMedia');\n if (mediaParses && data.media !== undefined && data.media.type !== 'audio') {\n report(\n ['media', 'type'],\n data.media.type,\n 'A dictation is dictated: its recording must be audio.',\n );\n }\n if (slowParses && data.slowMedia?.playback !== undefined) {\n report(\n ['slowMedia', 'playback'],\n data.slowMedia.playback,\n 'The slow recording carries no playback policy of its own; put the policy on `media` — the slow recording follows it (except the play budget).',\n );\n }\n if (mediaParses && slowParses && data.slowMedia !== undefined) {\n if (data.media?.playback?.maxPlays !== undefined) {\n report(\n ['slowMedia'],\n data.slowMedia,\n \"A play budget on `media` cannot coexist with an unbudgeted slow recording of the same content. Drop `slowMedia`, or drop `maxPlays` (a budgeted recording can still offer the transport's 0.75× speed when `rate` is `allow`).\",\n );\n }\n // Both addresses must be strings before they can be the same file: a\n // missing one is the address rule's failure, reported at its own path.\n if (\n typeof data.slowMedia.url === 'string' &&\n typeof data.media?.url === 'string' &&\n data.slowMedia.url === data.media.url\n ) {\n report(\n ['slowMedia', 'url'],\n data.slowMedia.url,\n 'The slow recording must be a different file from the recording.',\n );\n }\n if (data.media === undefined) {\n report(\n ['media'],\n data.media,\n 'A slow recording accompanies a recording: add `media` first.',\n );\n }\n }\n if (\n rulesParse &&\n !(data.tolerance?.equivalences ?? []).every(\n (rule) => preStripNormalize(rule.from) !== '' && normalizeDictationText(rule.to) !== '',\n )\n ) {\n report(\n ['tolerance', 'equivalences'],\n data.tolerance?.equivalences,\n 'An equivalence must name a word, phrase or symbol to rewrite, and rewrite it to something that survives normalisation (not only punctuation or spaces).',\n );\n }\n\n for (const field of ['media', 'slowMedia'] as const) {\n const recording = data[field] as { captionsUrl?: unknown } | undefined;\n if (recording?.captionsUrl !== undefined) {\n report(\n [field, 'captionsUrl'],\n recording.captionsUrl,\n 'A dictation recording cannot carry captions: the captions are the answer. Offer an accessible alternative as a different item.',\n );\n }\n }\n\n if (!rulesParse || !transcriptParses) {\n return;\n }\n const normalizer = normalizerFor(data);\n const candidates = candidatesOf(data).map((entry) => normalizer.measure(entry));\n const shown: [readonly (string | number)[], string | undefined][] = [\n [['title'], refused.has('title') ? undefined : data.title],\n [['media', 'alt'], data.media?.alt],\n [['slowMedia', 'alt'], data.slowMedia?.alt],\n ];\n for (const [path, value] of shown) {\n if (value === undefined) {\n continue;\n }\n const { tooLong, forms } = normalizer.shown(value);\n if (tooLong) {\n report(\n path,\n value,\n `The title and the recording descriptions are searched for the transcript before they are shown: each is at most ${WORKING_LENGTH} characters, before and after the equivalences are applied, and the equivalences may not grow it past that on the way.`,\n );\n } else if (candidates.some((candidate) => revealsCandidate(forms, candidate))) {\n report(\n path,\n value,\n 'The title and the recording descriptions are shown to the learner before they type: they must not contain the transcript. Describe the recording (\"Recording, normal speed\"), never transcribe it.',\n );\n }\n }\n }),\n);\n\n/**\n * Whether a group plays a captioned recording to a dictation: its stimulus\n * media carries captions while one of its items is a dictation with no\n * recording of its own, which therefore plays the stimulus recording. The\n * captions are then the answer, as they would be on the dictation's own\n * recording. `hasCaptions` decides what counts as captions written — the\n * schema counts any value, a draft only a written one.\n */\nexport function groupCaptionsRevealDictation(\n group: unknown,\n hasCaptions: (captionsUrl: unknown) => boolean = (captionsUrl) => captionsUrl !== undefined,\n): boolean {\n const isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n if (!isObject(group) || !isObject(group.stimulus) || !isObject(group.stimulus.media)) {\n return false;\n }\n if (!hasCaptions(group.stimulus.media.captionsUrl) || !Array.isArray(group.items)) {\n return false;\n }\n return group.items.some(\n (item) => isObject(item) && item.type === 'dictation' && item.media === undefined,\n );\n}\n\n/** The message every validator gives for {@link groupCaptionsRevealDictation}. */\nexport const GROUP_CAPTIONS_REVEAL_DICTATION =\n 'The stimulus recording carries captions, and a dictation in this group plays that recording: the captions are the answer. Remove the captions, or give the dictation its own recording.';\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema } from './media.js';\n\n/**\n * Matches `{{ blank_id }}` placeholders in a passage, capturing the trimmed id.\n * Exported for the draft checks in `authoring/`, which must read a passage\n * exactly as this schema does. It is not re-exported from the package.\n */\nexport const PLACEHOLDER_RE = /\\{\\{\\s*([^{}]+?)\\s*\\}\\}/g;\n\n/**\n * Zod schema for a `TextMatchPolicy` — the opt-in matching tolerances a blank\n * may declare. Every default reproduces the v1 trim + case-fold semantics.\n */\nexport const TextMatchPolicySchema = z.looseObject({\n caseSensitive: z.boolean().optional(),\n trim: z.boolean().optional(),\n normalize: z.enum(['none', 'NFC', 'NFKC']).optional(),\n foldDiacritics: z.boolean().optional(),\n collapseInnerWhitespace: z.boolean().optional(),\n ignorePunctuation: z.boolean().optional(),\n levenshtein: z.number().int().min(0).optional(),\n locale: z.string().optional(),\n});\n\n/**\n * Zod schema for a single fill-in-the-blank slot configuration. Loose:\n * unknown keys are preserved through validation. Accepted answers must\n * contain non-whitespace characters — a whitespace-only accepted answer\n * normalizes to the empty string and would mark an empty response correct.\n */\nexport const BlankConfigSchema = z.looseObject({\n id: z.string().min(1),\n acceptedAnswers: z\n .array(\n z\n .string()\n .min(1)\n .refine((answer) => answer.trim().length > 0, {\n error: 'Accepted answers must contain non-whitespace characters.',\n }),\n )\n .min(1),\n caseSensitive: z.boolean().optional(),\n trimWhitespace: z.boolean().optional(),\n match: TextMatchPolicySchema.optional(),\n hint: z.string().optional(),\n feedback: z.string().optional(),\n});\n\n/**\n * Zod schema validating the full Fill-in-the-Blanks activity data contract.\n * Loose at every level: unknown keys are preserved, never stripped (B7).\n *\n * The refinement enforces a true one-to-one correspondence between `{{id}}`\n * placeholders and `blanks[].id`: every blank id appears EXACTLY ONCE in the\n * passage and exactly once in `blanks[]`. (The previous set-based check let\n * duplicate placeholders and duplicate blank configs through, corrupting the\n * partial-score denominator and per-item details.)\n */\nexport const FillInTheBlanksDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('fill-in-the-blanks'),\n id: z.string().min(1),\n title: z.string().min(1),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n blanks: z.array(BlankConfigSchema).min(1),\n scoringStrategy: z.enum(['all-or-nothing', 'partial']),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine(\n (data) => {\n const placeholderCounts = new Map<string, number>();\n for (const match of data.passage.matchAll(PLACEHOLDER_RE)) {\n const id = match[1] as string;\n placeholderCounts.set(id, (placeholderCounts.get(id) ?? 0) + 1);\n }\n const blankIds = data.blanks.map((blank) => blank.id);\n if (new Set(blankIds).size !== blankIds.length) {\n return false;\n }\n if (placeholderCounts.size !== blankIds.length) {\n return false;\n }\n return blankIds.every((id) => placeholderCounts.get(id) === 1);\n },\n {\n error:\n 'Each blank id must appear exactly once in blanks[] and have exactly one matching {{id}} placeholder in the passage, and vice versa.',\n path: ['passage'],\n },\n );\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema, MediaUrlSchema } from './media.js';\n\n/**\n * Zod schema for a single Multiple Choice option. Loose: unknown keys are\n * preserved through validation (forward-compat / consumer sidecars — B7).\n */\n/**\n * A picture or recording carried by one option.\n *\n * LOOSE, like `MediaSchema` and every other content schema: a consumer's own\n * fields — an asset id, a CDN key — ride along and survive validation (B7).\n * Making it strict to catch a typo would reject exactly the sidecars the SDK\n * promises to preserve, and an invariant test guards that promise by counting\n * the closed objects in the exported JSON Schema.\n *\n * The two things that must NOT be swallowed are named explicitly instead. The\n * `type` enum refuses the kinds that cannot be an option, and a refinement\n * refuses `playback`, which would otherwise sit there looking like an enforced\n * budget while nothing read it.\n *\n * The URL goes through `MediaUrlSchema`, the same security-reviewed allow-list\n * activity media uses — `javascript:`, `file:` and `ftp:` are stored-XSS\n * vectors wherever the value lands in a `src`, and that list is not forked.\n */\nexport const MultipleChoiceOptionMediaSchema = z\n .looseObject({\n // No `embed`: an iframe swallows the click that selects the option, so the\n // learner could not choose it. No `video`: a native control bar inside the\n // option's label eats the same click.\n type: z.enum(['image', 'audio']),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n captionsUrl: MediaUrlSchema.optional(),\n })\n // The same rule MediaSchema applies, so there is one rule to learn: a picture\n // needs a text alternative (WCAG 1.1.1), a recording's label is optional.\n .refine((media) => media.type !== 'image' || (media.alt !== undefined && media.alt !== ''), {\n error: 'An image option requires a non-empty `alt`.',\n path: ['alt'],\n })\n // Named rather than ignored. `maxPlays` binds per slot through a\n // MediaBudgetBinding, and nothing decides yet whether four recordings in one\n // question share a budget or hold one each — so a policy written here would\n // be a promise no renderer keeps, on a listening exam that believed it had\n // one.\n .refine((media) => !('playback' in media), {\n error:\n 'An option carries no playback policy. maxPlays, seek and rate are enforced only on the media above the question.',\n path: ['playback'],\n });\n\nexport const MultipleChoiceOptionSchema = z.looseObject({\n id: z.string().min(1),\n text: z.string().min(1),\n isCorrect: z.boolean(),\n feedback: z.string().optional(),\n media: MultipleChoiceOptionMediaSchema.optional(),\n});\n\n/**\n * Zod schema validating the full Multiple Choice activity data contract.\n * Loose at every level: unknown keys are preserved, never stripped, so a\n * v0.3 runtime reading a future payload (or a consumer sidecar field) does\n * not silently delete data.\n *\n * Semantic guards: (1) at least one option must be correct, otherwise the\n * activity can never be answered correctly; (2) `mode: 'single'` must have\n * exactly one correct option — multiple correct options under single-select\n * make `showCorrectAnswers` and the xAPI correct-response ambiguous and mask\n * authoring errors; (3) option ids must be unique — the scorer looks options\n * up by id, so a duplicate id makes one option unscoreable. All are\n * unrepresentable in JSON Schema and are dropped from `toJSONSchema` output\n * by design.\n */\nexport const MultipleChoiceDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('multiple-choice'),\n id: z.string().min(1),\n title: z.string().min(1),\n question: z.string().min(1),\n questionHtml: z.string().optional(),\n mode: z.enum(['single', 'multi']),\n options: z.array(MultipleChoiceOptionSchema).min(2).max(26),\n scoringStrategy: z.enum(['all-or-nothing', 'partial']),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n shuffle: z.boolean().optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine((data) => data.options.some((option) => option.isCorrect), {\n error: 'At least one option must be marked correct.',\n path: ['options'],\n })\n .refine(\n (data) =>\n data.mode !== 'single' || data.options.filter((option) => option.isCorrect).length === 1,\n {\n error: 'Single-select activities (mode: \"single\") must have exactly one correct option.',\n path: ['options'],\n },\n )\n .refine((data) => new Set(data.options.map((option) => option.id)).size === data.options.length, {\n error: 'Option ids must be unique within the activity.',\n path: ['options'],\n });\n","import {\n DICTATION_MAX_TEXT_LENGTH,\n DICTATION_MAX_TRANSCRIPT_LENGTH,\n} from '../dictation/normalize.js';\n\n/**\n * The longest reference text a read-aloud item accepts, in code points, before\n * and after normalisation. It is the dictation transcript cap on purpose: the\n * reference is tokenised as a dictation transcript is, and the dictation\n * normaliser cuts a transcript at that many code points without saying so, so\n * a longer text would be graded against words it had silently lost.\n */\nexport const READ_ALOUD_MAX_REFERENCE_LENGTH = DICTATION_MAX_TRANSCRIPT_LENGTH;\n\n/** The longest take a read-aloud item may allow, in seconds: the most `recording.maxSeconds` may be. */\nexport const READ_ALOUD_MAX_SECONDS = 300;\n\n/** The most takes a read-aloud item may allow: the most `recording.maxTakes` may be. */\nexport const READ_ALOUD_MAX_TAKES = 20;\n\n/**\n * The largest weight a read-aloud dimension may carry. Weights are relative, so\n * no rubric needs more, and the cap keeps a mistyped weight from overflowing\n * the weighted total into a grade that cannot be computed.\n */\nexport const READ_ALOUD_MAX_DIMENSION_WEIGHT = 1000;\n\n/**\n * The most words a speech assessment may carry. It bounds one side of the\n * alignment, and one side alone bounds nothing: the aligner pairs TOKENS, and\n * one word normalises to as many as its text holds. What bounds the quadratic\n * work is this cap, the 200-unit cap on a single word's text, and the two rules\n * `validateSpeechAssessment` states over the total of them — what the words are\n * written with, and what they spell once normalised.\n */\nexport const SPEECH_ASSESSMENT_MAX_WORDS = 1000;\n\n/**\n * The most text the words of a speech assessment may carry between them: in\n * UTF-16 units as they are written, and in code points once they are normalised\n * into the tokens a mark is made from. One number for both, because they bound\n * the same work — a character that stands for eighteen under normalisation\n * would otherwise pass the first rule and overrun the second. Both count every\n * word, including one tagged `insertion`, which gives no token to pair but is\n * normalised and carried into the alignment all the same.\n *\n * It is the dictation attempt cap, because the words are the attempt: they are\n * normalised and aligned exactly as a learner's typed text is, and the aligner\n * reads no more of one than this. The aligner keeps the same number as its own\n * budget, a backstop for a public call whose evidence this build did not check.\n *\n * Internal on purpose. It is a bound on evidence an adapter writes, not a\n * number a caller composes anything from, so it is not exported from the\n * package: `validateSpeechAssessment` reports what it refuses and why.\n *\n * Four times the 2000-code-point reference cap, so no honest assessment of a\n * read-aloud item comes near it.\n */\nexport const SPEECH_ASSESSMENT_MAX_TEXT_LENGTH = DICTATION_MAX_TEXT_LENGTH;\n","import { z } from 'zod/v4';\nimport { containsUnspacedScript, dictationNormalizer } from '../scoring/dictation/normalize.js';\nimport {\n READ_ALOUD_MAX_DIMENSION_WEIGHT,\n READ_ALOUD_MAX_REFERENCE_LENGTH,\n READ_ALOUD_MAX_SECONDS,\n READ_ALOUD_MAX_TAKES,\n} from '../scoring/speech/limits.js';\nimport { CANONICAL_LOCALE_RE } from '../scoring/speech/locale.js';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema, MediaUrlSchema } from './media.js';\n\n/** The dimensions a read-aloud grade can be made of, in the order they are documented. */\nexport const READ_ALOUD_DIMENSIONS = ['accuracy', 'fluency', 'completeness', 'prosody'] as const;\n\n/** The most dimensions a read-aloud item can weigh: one of each name. */\nconst READ_ALOUD_MAX_DIMENSIONS = READ_ALOUD_DIMENSIONS.length;\n\n/**\n * Zod schema for the slower model recording of a read-aloud item.\n *\n * STRICT, where every other content schema is loose: these three fields are the\n * whole contract, so the two keys an author reaches for — a `playback` policy\n * and a `captionsUrl` — are refused here rather than by guards of their own. The\n * policy belongs on `media`, which this recording follows; a captions track\n * belongs on `media` too, where it is allowed, because the text being read is\n * public and captions of it give nothing away.\n */\nexport const ReadAloudSlowMediaSchema = z.strictObject({\n type: z.literal('audio'),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n});\n\n/**\n * The text the learner reads aloud. The cap is enforced in code points by guard\n * 1 below; the metadata states it in the JSON Schema export, whose `maxLength`\n * counts code points too, where zod's `.max()` counts UTF-16 units.\n */\nconst ReferenceTextSchema = z\n .string()\n .min(1)\n .refine((text) => text.trim() !== '', {\n error: 'The text to read aloud must contain something to read.',\n })\n .meta({ maxLength: READ_ALOUD_MAX_REFERENCE_LENGTH });\n\n/** The limits on a take. Loose, like every content object: a sidecar survives. */\nconst RecordingBoundsSchema = z.looseObject({\n maxSeconds: z.number().gt(0).max(READ_ALOUD_MAX_SECONDS),\n minSeconds: z.number().min(0).optional(),\n maxTakes: z.number().int().min(1).max(READ_ALOUD_MAX_TAKES).optional(),\n});\n\n/** One assessor dimension the grade counts, and its weight. */\nconst ReadAloudDimensionWeightSchema = z.looseObject({\n name: z.enum(READ_ALOUD_DIMENSIONS),\n weight: z.number().min(0).max(READ_ALOUD_MAX_DIMENSION_WEIGHT),\n});\n\n/** What the grade is made of: one to four weighted dimensions. */\nconst ReadAloudScoringSchema = z.looseObject({\n dimensions: z.array(ReadAloudDimensionWeightSchema).min(1).max(READ_ALOUD_MAX_DIMENSIONS),\n});\n\n/**\n * A check zod runs even when another field was refused. Copied from\n * `schemas/dictation.ts`, where the reasoning is written out: an ordinary check\n * is skipped once any issue has been reported, so an author fixing a recording's\n * address would only then learn that the same address is the slow recording's.\n * The check itself decides which of its guards the refused fields leave nothing\n * to read.\n */\nfunction checkEvenAfterIssues<T>(\n check: (payload: z.core.ParsePayload<T>) => void,\n): z.core.$ZodCheck<T> {\n const guard: z.core.$ZodCheck<T> = new z.core.$ZodCheck({ check: 'custom', when: () => true });\n guard._zod.check = check;\n return guard;\n}\n\nconst ReadAloudDataShape = z.looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('read-aloud'),\n id: z.string().min(1),\n title: z.string().min(1),\n instructions: z.string().optional(),\n referenceText: ReferenceTextSchema,\n locale: z.string().regex(CANONICAL_LOCALE_RE, {\n error:\n 'The locale must be a BCP 47 tag in canonical form, with a region: \"en-US\", \"es-419\", \"zh-Hant-TW\". Pronunciation is assessed against one locale\\'s speech, and \"en\" does not say whose.',\n }),\n media: MediaSchema.optional(),\n slowMedia: ReadAloudSlowMediaSchema.optional(),\n recording: RecordingBoundsSchema,\n scoring: ReadAloudScoringSchema,\n passThreshold: z.number().min(0).max(1).optional(),\n feedback: FeedbackSchema.optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n});\n\n/**\n * Zod schema validating the full Read Aloud data contract.\n *\n * Ten semantic guards, none expressible in JSON Schema, each an authoring\n * error that would otherwise reach a learner or a grade:\n *\n * 1. **The text is bounded**, before and after normalisation (NFC composition\n * can lengthen it), because it is tokenised as a dictation transcript is and\n * the normaliser cuts a longer one without saying so — so a grade would be\n * computed against words the item had silently lost.\n * 2. **The text is written in a script that separates its words with spaces.**\n * The tokeniser splits on spaces alone, so a run of Han, kana, Thai, Lao,\n * Khmer or Myanmar letters is one word however many it holds, and every\n * per-word mark for it would be one mark for the whole sentence.\n * 3. **The model recording is audio.** A read-aloud model is read aloud.\n * 4. **A slow model recording accompanies a recording** — never a stand-in for\n * it.\n * 5. **The slow recording is a different file.**\n * 6. **A slow recording never sits beside a play budget** — a budget on one\n * file and a free second file of the same content is no budget.\n * 7. **No dimension is weighed twice**: two entries for one name hide which\n * weight the grade used.\n * 8. **Some dimension carries a weight above 0**, or there is no weighted total\n * to compute and every take would be unscorable.\n * 9. **The shortest take is shorter than the longest.**\n * 10. **Something survives normalisation of the text**: a text of nothing but\n * punctuation is tokenised into no word at all, so a silent take would\n * record no omission and a perfect reading would align as one insertion\n * after another — which is what a feedback component renders.\n *\n * The guards run even when an unrelated field was refused, but a guard that\n * reads a field zod refused waits until that field parses: zod leaves a refused\n * optional field — a recording, a slow recording — out of the value the guards\n * read, and a refused required field is kept as it was given, which need not be\n * of the type the guard expects.\n *\n * Guard 4 reports at `media`, where the recording it asks for belongs and where\n * the dictation schema reports the same rule. Guard 10 is checked beside guards\n * 1 and 2, which read the same text, and numbered after them so that every\n * other guard keeps the number it was given.\n */\nexport const ReadAloudDataSchema = ReadAloudDataShape.check(\n checkEvenAfterIssues<z.output<typeof ReadAloudDataShape>>((ctx) => {\n // Refused at the root: the value is not an object, and there is nothing to guard.\n if (ctx.issues.some((issue) => (issue.path?.length ?? 0) === 0)) {\n return;\n }\n const data = ctx.value;\n const refused = new Set(ctx.issues.map((issue) => String(issue.path?.[0])));\n const report = (path: readonly (string | number)[], input: unknown, message: string) => {\n ctx.issues.push({ code: 'custom', input, message, path: [...path] });\n };\n\n if (!refused.has('referenceText')) {\n const text = data.referenceText;\n // One measurement answers both halves of guard 1, and guard 10 besides: a\n // raw text over the cap is reported without being normalised at all — its\n // `normalized` is then `null`, never `''`, so guard 10 cannot fire beside\n // it — and a shorter one is measured again once it is.\n const measured = dictationNormalizer(undefined).measure(text);\n if (measured.tooLong) {\n report(\n ['referenceText'],\n text,\n `The text to read aloud is at most ${READ_ALOUD_MAX_REFERENCE_LENGTH} characters, before and after normalisation.`,\n );\n }\n if (measured.normalized === '') {\n report(\n ['referenceText'],\n text,\n 'The text to read aloud must contain a word: after ignoring punctuation and spacing nothing is left, so no word would be marked and every reading would align as one insertion after another.',\n );\n }\n if (containsUnspacedScript(text)) {\n report(\n ['referenceText'],\n text,\n 'A read-aloud item needs a script written with spaces between its words. The text is compared word by word, and a run of Han, kana, Thai, Lao, Khmer or Myanmar letters is one word however many it holds.',\n );\n }\n }\n\n const mediaParses = !refused.has('media');\n const slowParses = !refused.has('slowMedia');\n if (mediaParses && data.media !== undefined && data.media.type !== 'audio') {\n report(\n ['media', 'type'],\n data.media.type,\n 'A read-aloud model is read aloud: its recording must be audio.',\n );\n }\n if (mediaParses && slowParses && data.slowMedia !== undefined) {\n if (data.media === undefined) {\n report(\n ['media'],\n data.media,\n 'A slow model recording accompanies a recording: add `media` first.',\n );\n }\n if (data.media?.playback?.maxPlays !== undefined) {\n report(\n ['slowMedia'],\n data.slowMedia,\n 'A play budget on `media` cannot coexist with an unbudgeted slow recording of the same content. Drop `slowMedia`, or drop `maxPlays`.',\n );\n }\n // Both addresses must be strings before they can be the same file: a\n // missing one is the address rule's failure, reported at its own path.\n if (\n typeof data.slowMedia.url === 'string' &&\n typeof data.media?.url === 'string' &&\n data.slowMedia.url === data.media.url\n ) {\n report(\n ['slowMedia', 'url'],\n data.slowMedia.url,\n 'The slow model recording must be a different file from the recording.',\n );\n }\n }\n\n if (!refused.has('scoring')) {\n const dimensions = data.scoring.dimensions;\n const seen = new Set<string>();\n for (const [index, dimension] of dimensions.entries()) {\n if (seen.has(dimension.name)) {\n report(\n ['scoring', 'dimensions', index, 'name'],\n dimension.name,\n `\"${dimension.name}\" is already weighed: a dimension counted twice hides which weight the grade used.`,\n );\n }\n seen.add(dimension.name);\n }\n if (dimensions.every((dimension) => dimension.weight === 0)) {\n report(\n ['scoring', 'dimensions'],\n dimensions,\n 'At least one dimension must carry a weight above 0: with every weight at 0 there is no weighted total, and every take would be unscorable.',\n );\n }\n }\n\n if (!refused.has('recording')) {\n const { minSeconds, maxSeconds } = data.recording;\n if (minSeconds !== undefined && minSeconds >= maxSeconds) {\n report(\n ['recording', 'minSeconds'],\n minSeconds,\n 'The shortest take must be shorter than the longest.',\n );\n }\n }\n }),\n);\n","/**\n * A BCP 47 tag in the one spelling read-aloud accepts: a lowercase language of\n * two or three letters, an optional title-case script, and a region of two\n * capitals or three digits — `en-US`, `es-419`, `zh-Hant-TW`. Nothing else: no\n * variants, extensions or private-use subtags.\n *\n * The region is required because pronunciation is assessed against one\n * locale's speech, and `en` does not say whose. The exact spelling is required\n * because a grade compares an item's locale with an assessment's as strings:\n * canonicalising at grade time would make a stored grade depend on the\n * runtime's `Intl` data.\n *\n * Internal: the content schema and the assessment validator share it.\n */\nexport const CANONICAL_LOCALE_RE = /^[a-z]{2,3}(?:-[A-Z][a-z]{3})?-(?:[A-Z]{2}|[0-9]{3})$/;\n","/**\n * Canonical word counter for written-response bounds (Req 22.9): tokens are\n * maximal runs of non-whitespace (split on `\\s+`), so hyphenated forms\n * (`well-known`) count as one word. The empty / whitespace-only string counts\n * 0. Consumers must use this helper rather than re-implementing the split so\n * client previews, SDK scoring, and stored `wordCount` values always agree.\n */\nexport function countWords(text: string): number {\n // Tolerate non-strings: this runs on client-supplied submission payloads,\n // where a malformed value must score 0 rather than throw mid-grading.\n if (typeof text !== 'string') {\n return 0;\n }\n const trimmed = text.trim();\n if (trimmed === '') {\n return 0;\n }\n return trimmed.split(/\\s+/).length;\n}\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { PLACEHOLDER_RE } from './fill-in-the-blanks.js';\nimport { MediaSchema } from './media.js';\n\n/**\n * Zod schema for one selectable choice. Loose, like every other schema here:\n * unknown keys are preserved so a consumer sidecar survives validation.\n */\nexport const GapSelectChoiceSchema = z.looseObject({\n id: z.string().min(1),\n text: z.string().min(1),\n});\n\n/** Zod schema for a shared word bank. */\nexport const GapSelectBankSchema = z.looseObject({\n id: z.string().min(1),\n choices: z.array(GapSelectChoiceSchema).min(2),\n});\n\n/** Zod schema for one gap. The `choices` / `bankId` exclusivity is checked on the activity. */\nexport const GapSelectGapSchema = z.looseObject({\n id: z.string().min(1),\n choices: z.array(GapSelectChoiceSchema).min(2).optional(),\n bankId: z.string().min(1).optional(),\n correctChoiceId: z.string().min(1),\n feedback: z.string().optional(),\n});\n\n/** Every choice a gap offers, or `undefined` when its source is missing or unresolvable. */\nfunction resolveChoices(\n gap: z.infer<typeof GapSelectGapSchema>,\n banks: readonly z.infer<typeof GapSelectBankSchema>[],\n): z.infer<typeof GapSelectChoiceSchema>[] | undefined {\n if (gap.choices !== undefined) {\n return gap.bankId === undefined ? gap.choices : undefined;\n }\n if (gap.bankId === undefined) {\n return undefined;\n }\n return banks.find((bank) => bank.id === gap.bankId)?.choices;\n}\n\n/** The distinct `{{id}}` placeholders in a passage, in the order they appear, with their counts. */\nfunction placeholderCounts(passage: string): Map<string, number> {\n const counts = new Map<string, number>();\n for (const match of passage.matchAll(PLACEHOLDER_RE)) {\n const id = match[1] as string;\n counts.set(id, (counts.get(id) ?? 0) + 1);\n }\n return counts;\n}\n\n/**\n * Zod schema validating the full Gap Select data contract.\n *\n * Six semantic guards, none of them expressible in JSON Schema and all of them\n * authoring errors that would otherwise reach a learner:\n *\n * 1. **Bank ids are unique** — two banks sharing an id makes which one a gap\n * draws from a matter of array order.\n * 2. **Gap ids are unique** — the scorer looks a gap's selection up by id, so a\n * duplicate makes one gap unscoreable (the same defect the Multiple Choice\n * option-id guard exists for).\n * 3. **Passage placeholders and gaps pair one to one**, each `{{id}}` appearing\n * exactly once — the Fill-in-the-Blanks bijection, for the same reason: a\n * gap with nowhere to render is a question the learner never sees, and a\n * placeholder with no gap renders as literal `{{id}}` text.\n * 4. **Exactly one choice source per gap** — `choices` or `bankId`, never both\n * and never neither.\n * 5. **A `bankId` names a bank that exists.**\n * 6. **`correctChoiceId` is one of the choices the gap actually offers**, and\n * choice ids within a resolved set are unique. An answer key pointing at a\n * choice nobody can pick is an item that cannot be passed.\n */\nexport const GapSelectDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('gap-select'),\n id: z.string().min(1),\n title: z.string().min(1),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n gaps: z.array(GapSelectGapSchema).min(1),\n banks: z.array(GapSelectBankSchema).optional(),\n scoringStrategy: z.enum(['all-or-nothing', 'partial']),\n presentation: z.literal('dropdown').optional(),\n shuffleChoices: z.boolean().optional(),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine(\n (data) => {\n const ids = (data.banks ?? []).map((bank) => bank.id);\n return new Set(ids).size === ids.length;\n },\n { error: 'Word bank ids must be unique.', path: ['banks'] },\n )\n .refine(\n (data) => {\n const ids = data.gaps.map((gap) => gap.id);\n return new Set(ids).size === ids.length;\n },\n { error: 'Gap ids must be unique.', path: ['gaps'] },\n )\n .refine(\n (data) => {\n const counts = placeholderCounts(data.passage);\n const ids = data.gaps.map((gap) => gap.id);\n return (\n counts.size === new Set(ids).size &&\n ids.every((id) => counts.get(id) === 1) &&\n [...counts.values()].every((count) => count === 1)\n );\n },\n {\n error:\n 'Every gap must appear exactly once in the passage as {{gap_id}}, and every {{gap_id}} must have a gap.',\n path: ['passage'],\n },\n )\n .refine(\n (data) => data.gaps.every((gap) => (gap.choices === undefined) !== (gap.bankId === undefined)),\n {\n error: 'Each gap needs exactly one choice source: its own `choices`, or a `bankId`.',\n path: ['gaps'],\n },\n )\n .refine(\n (data) => {\n const bankIds = new Set((data.banks ?? []).map((bank) => bank.id));\n return data.gaps.every((gap) => gap.bankId === undefined || bankIds.has(gap.bankId));\n },\n { error: 'A gap references a `bankId` that no word bank defines.', path: ['gaps'] },\n )\n .refine(\n (data) => {\n const banks = data.banks ?? [];\n return data.gaps.every((gap) => {\n const choices = resolveChoices(gap, banks);\n if (choices === undefined) {\n // Guards 4 and 5 already report this; do not fail twice for one cause.\n return true;\n }\n const ids = choices.map((choice) => choice.id);\n return new Set(ids).size === ids.length && ids.includes(gap.correctChoiceId);\n });\n },\n {\n error:\n 'Each gap needs unique choice ids and a `correctChoiceId` that is one of the choices it offers.',\n path: ['gaps'],\n },\n );\n","import { z } from 'zod/v4';\nimport { FeedbackSchema } from './feedback.js';\nimport { MediaSchema } from './media.js';\n\n/** Zod schema for a single rubric criterion. Loose: unknown keys preserved. */\nexport const WrittenResponseRubricCriterionSchema = z.looseObject({\n name: z.string().min(1),\n description: z.string().optional(),\n weight: z.number().min(0),\n});\n\n/** Zod schema for a written-response grading rubric. Loose: unknown keys preserved. */\nexport const WrittenResponseRubricSchema = z.looseObject({\n label: z.string().optional(),\n criteria: z.array(WrittenResponseRubricCriterionSchema).min(1),\n});\n\n/**\n * The strict counterparts of the rubric schemas, for the redacted shapes.\n *\n * The authoring schemas above are loose on purpose (Req 22.5: a consumer's\n * rubric sidecars must survive `validateActivity` verbatim). The REDACTED\n * shape must not be: `RedactedWrittenResponseDataSchema` is a `strictObject`\n * precisely so an unknown key is a validation failure, and embedding the loose\n * rubric there meant the strictness stopped at the rubric boundary — a\n * `rubric.modelAnswer` reached the learner and `assertRedacted` blessed it.\n */\nexport const RedactedWrittenResponseRubricCriterionSchema = z.strictObject({\n name: z.string().min(1),\n description: z.string().optional(),\n weight: z.number().min(0),\n});\n\nexport const RedactedWrittenResponseRubricSchema = z.strictObject({\n label: z.string().optional(),\n criteria: z.array(RedactedWrittenResponseRubricCriterionSchema).min(1),\n});\n\n/**\n * Zod schema validating the Written Response activity data contract (Req 22).\n *\n * Wire-format constraints (Req 22.9): field names are locked for\n * byte-compatibility with consumer-stored rows, and the schema is loose at\n * EVERY level (Req 22.5) — unknown top-level keys, `promptHtml`, `rubric`\n * sidecars and any future fields survive `validateActivity` verbatim.\n */\nexport const WrittenResponseDataSchema = z\n .looseObject({\n schemaVersion: z.literal('1.0'),\n type: z.literal('written-response'),\n id: z.string().min(1),\n title: z.string().min(1),\n prompt: z.string(),\n promptHtml: z.string().optional(),\n minWords: z.number().int().min(0),\n maxWords: z.number().int().min(1),\n rubric: WrittenResponseRubricSchema.optional(),\n languageTarget: z.string().optional(),\n media: MediaSchema.optional(),\n feedback: FeedbackSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n })\n .refine((data) => data.maxWords >= data.minWords, {\n error: 'maxWords must be greater than or equal to minWords.',\n path: ['maxWords'],\n });\n","import { z } from 'zod/v4';\nimport {\n READ_ALOUD_MAX_DIMENSION_WEIGHT,\n READ_ALOUD_MAX_SECONDS,\n READ_ALOUD_MAX_TAKES,\n} from '../scoring/speech/limits.js';\nimport { CANONICAL_LOCALE_RE } from '../scoring/speech/locale.js';\nimport { MediaUrlSchema, RedactedMediaSchema } from './media.js';\nimport { READ_ALOUD_DIMENSIONS, ReadAloudSlowMediaSchema } from './read-aloud.js';\nimport { RedactedWrittenResponseRubricSchema } from './written-response.js';\n\n/**\n * Schemas for REDACTED activity data — the learner-safe projection `redact()`\n * produces (R7). Deliberately STRICT (`z.strictObject`), the opposite of the\n * loose content schemas: a redacted payload must prove the ABSENCE of every\n * answer-key and author-only field, so any unknown key is a validation\n * failure, not a passthrough. `assertRedacted` validates against these.\n */\n\n/** Shared fields every redacted activity carries. */\nconst redactedBase = {\n /** Marker distinguishing a redacted projection from full activity data. */\n redacted: z.literal(true),\n /** Slot identity, carried through redaction so the client and the plan agree. */\n slotKey: z.string().min(1).optional(),\n schemaVersion: z.literal('1.0'),\n id: z.string().min(1),\n title: z.string().min(1),\n media: RedactedMediaSchema.optional(),\n passThreshold: z.number().min(0).max(1).optional(),\n locale: z.string().optional(),\n learningObjectives: z.array(z.string()).optional(),\n difficultyLevel: z.literal([1, 2, 3, 4, 5]).optional(),\n};\n\n/** A redacted Multiple Choice option: id and display text only — no `isCorrect`, no feedback. */\nexport const RedactedMultipleChoiceOptionMediaSchema = z.strictObject({\n type: z.enum(['image', 'audio']),\n url: z.string().min(1),\n alt: z.string().min(1).optional(),\n captionsUrl: z.string().min(1).optional(),\n});\n\n/**\n * A redacted Multiple Choice option: id, display text and its picture or\n * recording — no `isCorrect`, no feedback.\n *\n * The media survives intact for the reason a gap-select choice does: it is the\n * option the learner is being asked to pick, and an exam that stripped it would\n * show a row of blanks. Nothing in it is an answer key — the key is `isCorrect`,\n * which is gone.\n */\nexport const RedactedMultipleChoiceOptionSchema = z.strictObject({\n id: z.string().min(1),\n text: z.string().min(1),\n media: RedactedMultipleChoiceOptionMediaSchema.optional(),\n});\n\n/**\n * Redacted Multiple Choice data: renderable (question, mode, options to pick\n * from) with the answer key, per-option feedback, overall feedback, and the\n * scoring strategy removed. `scoringStrategy` is answer-key by design: MC\n * `partial` carries a wrong-selection penalty `all-or-nothing` does not, so\n * knowing the strategy tells a learner whether guessing is free.\n */\nexport const RedactedMultipleChoiceDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('multiple-choice'),\n question: z.string().min(1),\n questionHtml: z.string().optional(),\n mode: z.enum(['single', 'multi']),\n options: z.array(RedactedMultipleChoiceOptionSchema).min(2).max(26),\n shuffle: z.boolean().optional(),\n});\n\n/** A redacted blank: id and hint only — no accepted answers, no matching rules, no feedback. */\nexport const RedactedBlankConfigSchema = z.strictObject({\n id: z.string().min(1),\n hint: z.string().optional(),\n});\n\n/** Redacted Fill-in-the-Blanks data: passage and blank slots, key removed. */\nexport const RedactedFillInTheBlanksDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('fill-in-the-blanks'),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n blanks: z.array(RedactedBlankConfigSchema).min(1),\n});\n\n/** A redacted choice: exactly what it always was. Which one is correct was never stored here. */\nexport const RedactedGapSelectChoiceSchema = z.strictObject({\n id: z.string().min(1),\n text: z.string().min(1),\n});\n\n/** A redacted word bank: every choice survives, because the learner picks from them. */\nexport const RedactedGapSelectBankSchema = z.strictObject({\n id: z.string().min(1),\n choices: z.array(RedactedGapSelectChoiceSchema).min(2),\n});\n\n/** A redacted gap: its choice source survives; `correctChoiceId` and feedback do not. */\nexport const RedactedGapSelectGapSchema = z.strictObject({\n id: z.string().min(1),\n choices: z.array(RedactedGapSelectChoiceSchema).min(2).optional(),\n bankId: z.string().min(1).optional(),\n});\n\n/**\n * Redacted Gap Select data — the type where redaction **inverts** the\n * Fill-in-the-Blanks rule, and the reason this is a separate schema rather than\n * a variant of it.\n *\n * In Fill-in-the-Blanks the candidate answers ARE the key, so `acceptedAnswers`\n * is stripped. Here the learner cannot answer at all without seeing every\n * choice, so `choices` and `banks` must survive in full and `correctChoiceId`\n * is the only field withheld. A policy that treated \"the list of candidate\n * answers\" as answer-key for both types would ship an unanswerable exam.\n *\n * `scoringStrategy` stays answer-key for the same reason it does on the other\n * two: knowing whether marking is partial tells a learner whether guessing at a\n * gap is free.\n */\nexport const RedactedGapSelectDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('gap-select'),\n passage: z.string().min(1),\n passageHtml: z.string().optional(),\n gaps: z.array(RedactedGapSelectGapSchema).min(1),\n banks: z.array(RedactedGapSelectBankSchema).optional(),\n presentation: z.literal('dropdown').optional(),\n shuffleChoices: z.boolean().optional(),\n});\n\n/**\n * Redacted Written Response data: the prompt, word bounds and rubric are\n * learner-visible (a rubric tells the learner what they are graded on);\n * authored pass/fail feedback is removed until the grade exists.\n */\nexport const RedactedWrittenResponseDataSchema = z.strictObject({\n ...redactedBase,\n type: z.literal('written-response'),\n prompt: z.string(),\n promptHtml: z.string().optional(),\n minWords: z.number().int().min(0),\n maxWords: z.number().int().min(1),\n rubric: RedactedWrittenResponseRubricSchema.optional(),\n languageTarget: z.string().optional(),\n});\n\n/** A redacted slow recording: unchanged — it is public, and it never carried captions or a policy. */\nexport const RedactedDictationSlowMediaSchema = z.strictObject({\n type: z.literal('audio'),\n url: MediaUrlSchema,\n alt: z.string().min(1).optional(),\n});\n\n/**\n * Redacted Dictation data: the recording(s) and the hint mode survive; the\n * transcript, the accepted alternatives and the tolerance — the whole answer\n * key — do not. A `progressive-words` hint mode reveals nothing by itself: the\n * words it would reveal are the transcript, and that is gone.\n *\n * Three rules of the content schema are repeated here, because a projection\n * built by hand never passed through it: captions on the recording ARE the\n * answer, so a payload carrying them is not learner-safe; a slow recording\n * beside a play budget is an unbudgeted copy of the budgeted content; and so is\n * a slow recording with no recording beside it, which in a group sits beside a\n * stimulus recording that may be budgeted.\n */\nexport const RedactedDictationDataSchema = z\n .strictObject({\n ...redactedBase,\n type: z.literal('dictation'),\n slowMedia: RedactedDictationSlowMediaSchema.optional(),\n hints: z.strictObject({ mode: z.literal('progressive-words') }).optional(),\n })\n .check((ctx) => {\n const data = ctx.value;\n if (data.media?.captionsUrl !== undefined) {\n ctx.issues.push({\n code: 'custom',\n input: data.media.captionsUrl,\n message:\n 'A dictation recording cannot carry captions: the captions are the answer, so this payload is not learner-safe.',\n path: ['media', 'captionsUrl'],\n });\n }\n if (data.slowMedia !== undefined && data.media?.playback?.maxPlays !== undefined) {\n ctx.issues.push({\n code: 'custom',\n input: data.slowMedia,\n message:\n 'A play budget on `media` cannot coexist with an unbudgeted slow recording of the same content.',\n path: ['slowMedia'],\n });\n }\n if (data.slowMedia !== undefined && data.media === undefined) {\n ctx.issues.push({\n code: 'custom',\n input: data.slowMedia,\n message: 'A slow recording accompanies a recording: this payload has no `media`.',\n path: ['media'],\n });\n }\n });\n\n/**\n * Redacted Read Aloud data: everything survives, because a read-aloud item has\n * no answer key — the learner is shown the very text they are asked to read.\n * What redaction removes is the authored pass/fail feedback, which is written\n * about a grade that does not exist yet.\n *\n * `locale` is REQUIRED here, overriding the optional one on the shared base: it\n * decides the grade (an assessment made for another locale is unscorable), so a\n * projection that dropped it would send an exam client an item no assessor could\n * be asked for. Four rules of the content schema are repeated, because a\n * projection built by hand never passed through it: the recording is audio, a\n * slow recording accompanies a recording, is a different file, and never sits\n * beside a play budget — the last two being how an unbudgeted copy of budgeted\n * content would reach a learner.\n */\nexport const RedactedReadAloudDataSchema = z\n .strictObject({\n ...redactedBase,\n type: z.literal('read-aloud'),\n locale: z.string().regex(CANONICAL_LOCALE_RE),\n instructions: z.string().optional(),\n referenceText: z.string().min(1),\n slowMedia: ReadAloudSlowMediaSchema.optional(),\n recording: z.strictObject({\n maxSeconds: z.number().gt(0).max(READ_ALOUD_MAX_SECONDS),\n minSeconds: z.number().min(0).optional(),\n maxTakes: z.number().int().min(1).max(READ_ALOUD_MAX_TAKES).optional(),\n }),\n scoring: z.strictObject({\n dimensions: z\n .array(\n z.strictObject({\n name: z.enum(READ_ALOUD_DIMENSIONS),\n weight: z.number().min(0).max(READ_ALOUD_MAX_DIMENSION_WEIGHT),\n }),\n )\n .min(1)\n .max(READ_ALOUD_DIMENSIONS.length),\n }),\n })\n .check((ctx) => {\n const data = ctx.value;\n if (data.media !== undefined && data.media.type !== 'audio') {\n ctx.issues.push({\n code: 'custom',\n input: data.media.type,\n message: 'A read-aloud model recording must be audio, so this payload is not renderable.',\n path: ['media', 'type'],\n });\n }\n if (data.slowMedia !== undefined && data.media === undefined) {\n ctx.issues.push({\n code: 'custom',\n input: data.slowMedia,\n message: 'A slow model recording accompanies a recording: this payload has no `media`.',\n path: ['media'],\n });\n }\n if (data.slowMedia !== undefined && data.media?.playback?.maxPlays !== undefined) {\n ctx.issues.push({\n code: 'custom',\n input: data.slowMedia,\n message:\n 'A play budget on `media` cannot coexist with an unbudgeted slow recording of the same content.',\n path: ['slowMedia'],\n });\n }\n if (data.slowMedia !== undefined && data.slowMedia.url === data.media?.url) {\n ctx.issues.push({\n code: 'custom',\n input: data.slowMedia.url,\n message: 'The slow model recording must be a different file from the recording.',\n path: ['slowMedia', 'url'],\n });\n }\n });\n\n/**\n * The learner-safe SHAPE of each built-in type, derived from the strict schema\n * above rather than hand-written beside it.\n *\n * `redact()` returns {@link RedactedActivityData}, which proves a payload is\n * learner-safe but is index-signature typed — it deliberately says nothing\n * about what the payload still CONTAINS. That is right for the assertion and\n * useless for anything that has to render or transport the result, so every\n * integrator ends up re-declaring these interfaces by hand and they drift the\n * moment a schema changes. Deriving them with `z.infer` means the type and the\n * validator can never disagree.\n *\n * Use them for the payload a server sends an exam client, and for the props of\n * a renderer that must never see an answer key.\n */\nexport type RedactedMultipleChoiceOption = z.infer<typeof RedactedMultipleChoiceOptionSchema>;\n/** An option's picture or recording, unchanged by redaction — it is what the learner picks. */\nexport type RedactedMultipleChoiceOptionMedia = z.infer<\n typeof RedactedMultipleChoiceOptionMediaSchema\n>;\n/** A Multiple Choice item with the answer key, feedback and strategy removed. */\nexport type RedactedMultipleChoiceData = z.infer<typeof RedactedMultipleChoiceDataSchema>;\n/** A blank with its accepted answers and matching rules removed; the hint survives. */\nexport type RedactedBlankConfig = z.infer<typeof RedactedBlankConfigSchema>;\n/** A Fill-in-the-Blanks item with every accepted answer removed. */\nexport type RedactedFillInTheBlanksData = z.infer<typeof RedactedFillInTheBlanksDataSchema>;\n/** A Written Response item; the rubric survives, because it tells the learner what is assessed. */\nexport type RedactedWrittenResponseData = z.infer<typeof RedactedWrittenResponseDataSchema>;\n/** A selectable choice, unchanged by redaction. */\nexport type RedactedGapSelectChoice = z.infer<typeof RedactedGapSelectChoiceSchema>;\n/** A word bank, unchanged by redaction — the learner picks from it. */\nexport type RedactedGapSelectBank = z.infer<typeof RedactedGapSelectBankSchema>;\n/** A gap with its `correctChoiceId` removed; every choice it offers survives. */\nexport type RedactedGapSelectGap = z.infer<typeof RedactedGapSelectGapSchema>;\n/** A Gap Select item the learner can still answer: choices intact, answer key gone. */\nexport type RedactedGapSelectData = z.infer<typeof RedactedGapSelectDataSchema>;\n/** The slower recording of a dictation, unchanged by redaction. */\nexport type RedactedDictationSlowMedia = z.infer<typeof RedactedDictationSlowMediaSchema>;\n/** A Dictation item with its transcript, accepted alternatives and tolerances removed. */\nexport type RedactedDictationData = z.infer<typeof RedactedDictationDataSchema>;\n/**\n * A Read Aloud item, which keeps everything but the authored feedback: the text\n * to read is what the learner is asked to read. The slow recording needs no\n * redacted counterpart of its own — the content schema for it is already strict.\n */\nexport type RedactedReadAloudData = z.infer<typeof RedactedReadAloudDataSchema>;\n\n/**\n * Discriminated union of every built-in redacted activity. Narrow it on\n * `type`, exactly as you would {@link ActivityData}:\n *\n * ```ts\n * function render(item: RedactedActivity) {\n * if (item.type === 'multiple-choice') {\n * return item.options.map((option) => option.text); // no `isCorrect` to leak\n * }\n * }\n * ```\n */\nexport type RedactedActivity =\n | RedactedMultipleChoiceData\n | RedactedFillInTheBlanksData\n | RedactedWrittenResponseData\n | RedactedGapSelectData\n | RedactedDictationData\n | RedactedReadAloudData;\n","/**\n * The code points of `text`, as numbers. A surrogate pair is one code point; a\n * lone surrogate iterates as one code point too — deterministic, never thrown.\n */\nexport function codePoints(text: string): number[] {\n return Array.from(text, (character) => character.codePointAt(0) as number);\n}\n\n/**\n * Exact, unbounded Levenshtein distance between the code-point sequences of\n * `a` and `b`: insertions, deletions and substitutions cost 1 each, and a\n * transposition is two edits. Two-row Wagner–Fischer: O(|a|·|b|) time,\n * O(min(|a|, |b|)) memory.\n *\n * Deliberately NOT `levenshteinDistance` from `text-match.ts`: that one counts\n * UTF-16 code units, stops early once a bound is passed (returning `max + 1`,\n * not a distance), and is frozen by every fill-in-the-blanks grade vector. A\n * dictation grade needs the whole distance, over characters a learner can see.\n */\nexport function editDistance(a: string, b: string): number {\n if (a === b) {\n return 0;\n }\n let left = codePoints(a);\n let right = codePoints(b);\n if (left.length < right.length) {\n [left, right] = [right, left];\n }\n if (right.length === 0) {\n return left.length;\n }\n\n let previous = new Uint32Array(right.length + 1);\n let current = new Uint32Array(right.length + 1);\n for (let j = 0; j <= right.length; j += 1) {\n previous[j] = j;\n }\n for (let i = 1; i <= left.length; i += 1) {\n current[0] = i;\n const leftPoint = left[i - 1];\n for (let j = 1; j <= right.length; j += 1) {\n const deletion = (previous[j] as number) + 1;\n const insertion = (current[j - 1] as number) + 1;\n const substitution = (previous[j - 1] as number) + (leftPoint === right[j - 1] ? 0 : 1);\n current[j] = Math.min(deletion, insertion, substitution);\n }\n [previous, current] = [current, previous];\n }\n return previous[right.length] as number;\n}\n\n/**\n * How alike two strings are, in [0, 1]: `(max − distance) / max` over code\n * points, where `max` is the longer length. One division, correctly rounded,\n * so an exact rational tie at an authored threshold passes — `0.67` for 33\n * edits in 100 — where `1 − distance / max` evaluates to `0.6699999999999999`\n * and would fail it. Two empty strings are `0`, not `1`: nothing was dictated.\n */\nexport function similarity(a: string, b: string): number {\n const max = Math.max(codePoints(a).length, codePoints(b).length);\n return max === 0 ? 0 : (max - editDistance(a, b)) / max;\n}\n","import type { DictationData } from '../../types/activity.js';\nimport { similarity } from './edit-distance.js';\nimport {\n DICTATION_MAX_ACCEPTED_TRANSCRIPTS,\n DICTATION_MAX_TEXT_LENGTH,\n DICTATION_MAX_TRANSCRIPT_LENGTH,\n normalizeDictationText,\n normalizeDictationTextBounded,\n truncateCodePoints,\n} from './normalize.js';\n\n/** One transcript word, or one extra word the learner typed, after alignment. */\nexport interface DictationWordAlignment {\n /** `w<n>`, the 1-based position of a transcript word; absent for an extra word. */\n itemId?: string;\n /** The transcript word, normalised; `''` for an extra word. */\n reference: string;\n /** The word the learner typed for it, normalised; `''` for a missing word. */\n attempt: string;\n /** How alike the two words are, in [0, 1]; `0` for a missing or an extra word. */\n similarity: number;\n status: 'correct' | 'incorrect' | 'missing' | 'extra';\n}\n\n/**\n * The comparison a dictation grade is made of: the normalised strings, which\n * candidate transcript the attempt was scored against, and the word pairings.\n * The same data the scorer reads, so a marked display and the grade cannot\n * disagree.\n */\nexport interface DictationAlignment {\n /**\n * Which candidate the attempt was scored against, by ORIGINAL position:\n * `0` is `transcript`, `n` is `acceptedTranscripts[n − 1]`; `-1` when no\n * candidate survives normalisation (stale or transcript-less data).\n */\n candidateIndex: number;\n /** The chosen candidate, normalised; `''` when `candidateIndex` is `-1`. */\n reference: string;\n /** The attempt, normalised and possibly truncated. */\n attempt: string;\n /**\n * Whether the learner's text was cut: at `DICTATION_MAX_TEXT_LENGTH` before or\n * after normalisation, or while equivalence rules were growing it.\n */\n truncated: boolean;\n /** The similarity of `attempt` to `reference` — the score. */\n similarity: number;\n /** Transcript words and extra words, in the order they align. */\n words: DictationWordAlignment[];\n}\n\n/**\n * What `alignDictation` reads. Every field optional, and read defensively: full\n * data, a practice payload, a `redact(data, { reveal: 'after-submit' })`\n * projection, or a plain redacted projection with no transcript at all — which\n * yields `candidateIndex: -1`, never a throw.\n */\nexport type DictationReference = Partial<\n Pick<DictationData, 'transcript' | 'acceptedTranscripts' | 'tolerance'>\n>;\n\n/**\n * The words the scorer can emit a detail for, one list per candidate — index 0\n * is the transcript, index n is `acceptedTranscripts[n − 1]` — each word in the\n * normalised form `correctResponse` carries. A candidate that normalises to\n * nothing gives an empty list. Derivable from the activity alone, which is what\n * a validator of stored details joins against: by id AND by word, because\n * positional ids alone cannot tell an edited transcript of the same length.\n */\nexport function dictationReferenceWords(\n data: DictationReference,\n): readonly (readonly { itemId: string; word: string }[])[] {\n return rawCandidates(data).map((candidate) =>\n tokensOf(normalizeCandidate(candidate, data)).map((word, index) => ({\n itemId: `w${index + 1}`,\n word,\n })),\n );\n}\n\n/**\n * Compares a learner's text with a dictation's transcript(s). Pure and\n * deterministic: no locale, no `Intl`, integer alignment costs, one pinned\n * tie order.\n *\n * The attempt is cut at `DICTATION_MAX_TEXT_LENGTH` code points before and\n * after normalisation — a cut that ends on a space leaves no empty word after\n * it — and `truncated` is also set when the rules grew its\n * working text past the normaliser's bound (see `normalizeDictationText`).\n * Every candidate — the transcript and at most\n * `DICTATION_MAX_ACCEPTED_TRANSCRIPTS` accepted transcripts — is normalised\n * with the same tolerance and cut at `DICTATION_MAX_TRANSCRIPT_LENGTH`; the\n * attempt is scored against each surviving candidate and the first best wins.\n * Words are then paired by a word-level edit distance with exact-equality\n * costs, backtracked from the end preferring a pairing, then a missing\n * transcript word, then an extra typed word; a paired word's similarity is its\n * own character similarity.\n */\nexport function alignDictation(data: DictationReference, text: string): DictationAlignment {\n const raw = typeof text === 'string' ? text : '';\n const capped = truncateCodePoints(raw, DICTATION_MAX_TEXT_LENGTH);\n const { text: normalized, cut } = normalizeDictationTextBounded(capped, data.tolerance);\n const attempt = cutAt(normalized, DICTATION_MAX_TEXT_LENGTH);\n const truncated = cut || capped.length < raw.length || attempt.length < normalized.length;\n\n const candidates: { index: number; text: string }[] = [];\n rawCandidates(data).forEach((candidate, index) => {\n const candidateText = normalizeCandidate(candidate, data);\n if (candidateText !== '') {\n candidates.push({ index, text: candidateText });\n }\n });\n if (candidates.length === 0) {\n return { candidateIndex: -1, reference: '', attempt, truncated, similarity: 0, words: [] };\n }\n\n let chosen = candidates[0] as { index: number; text: string };\n let best = similarity(chosen.text, attempt);\n for (const candidate of candidates.slice(1)) {\n const candidateSimilarity = similarity(candidate.text, attempt);\n if (candidateSimilarity > best) {\n best = candidateSimilarity;\n chosen = candidate;\n }\n }\n\n return {\n candidateIndex: chosen.index,\n reference: chosen.text,\n attempt,\n truncated,\n similarity: best,\n words: alignWords(tokensOf(chosen.text), tokensOf(attempt)),\n };\n}\n\n/**\n * `transcript` first, then each accepted transcript, whatever type stale data\n * holds them as — at most `DICTATION_MAX_ACCEPTED_TRANSCRIPTS` of them, the\n * schema's own limit, so stale data with more cannot slow a grading run.\n */\nfunction rawCandidates(data: DictationReference): unknown[] {\n return [\n data.transcript,\n ...(Array.isArray(data.acceptedTranscripts)\n ? data.acceptedTranscripts.slice(0, DICTATION_MAX_ACCEPTED_TRANSCRIPTS)\n : []),\n ];\n}\n\n/**\n * A candidate normalised with the item's tolerance, cut at the transcript cap\n * before and after. The cut before normalising reaches only stale data — the\n * schema refuses a longer transcript — and keeps its cost bounded.\n */\nfunction normalizeCandidate(candidate: unknown, data: DictationReference): string {\n return typeof candidate === 'string'\n ? cutAt(\n normalizeDictationText(\n truncateCodePoints(candidate, DICTATION_MAX_TRANSCRIPT_LENGTH),\n data.tolerance,\n ),\n DICTATION_MAX_TRANSCRIPT_LENGTH,\n )\n : '';\n}\n\n/**\n * Normalised text cut at `max` code points, without the space the cut may end\n * on: normalised text is single-spaced, so that space is the only one that\n * could be left at an edge, and it would read as an empty word.\n */\nfunction cutAt(normalized: string, max: number): string {\n const cut = truncateCodePoints(normalized, max);\n return cut.endsWith(' ') ? cut.slice(0, -1) : cut;\n}\n\n/** The words of a normalised string: already single-spaced, so a plain split. */\nfunction tokensOf(normalized: string): string[] {\n return normalized === '' ? [] : normalized.split(' ');\n}\n\n/** The edit that produced a cell of the alignment matrix. */\nexport const STEP = {\n /** A reference element paired with an attempt element — equal or substituted. */\n pair: 0,\n /** A reference element the attempt lacks. */\n missing: 1,\n /** An attempt element the reference lacks. */\n extra: 2,\n} as const;\nexport type Step = (typeof STEP)[keyof typeof STEP];\n\n/**\n * Edit operations turning `reference` into `attempt`, as one step per cell of\n * a Wagner–Fischer matrix: pair (equal or substituted), missing (a reference\n * element the attempt lacks) or extra (an attempt element the reference\n * lacks). Ties are broken at fill time in that order, which is exactly the\n * order a backtrace from the end would test them in, so the result is the\n * pairing the reference implementation's backtrace produces. The matrix holds\n * one byte per cell; the distances need only two rows.\n */\nexport function alignSequences(\n reference: readonly string[],\n attempt: readonly string[],\n): { step: Step; referenceIndex: number; attemptIndex: number }[] {\n const rows = reference.length;\n const columns = attempt.length;\n const width = columns + 1;\n const steps = new Uint8Array((rows + 1) * width);\n let previous = new Uint32Array(width);\n let current = new Uint32Array(width);\n for (let j = 0; j <= columns; j += 1) {\n previous[j] = j;\n steps[j] = STEP.extra;\n }\n for (let i = 1; i <= rows; i += 1) {\n current[0] = i;\n steps[i * width] = STEP.missing;\n const referenceItem = reference[i - 1];\n for (let j = 1; j <= columns; j += 1) {\n const paired = (previous[j - 1] as number) + (referenceItem === attempt[j - 1] ? 0 : 1);\n const missing = (previous[j] as number) + 1;\n const extra = (current[j - 1] as number) + 1;\n const cost = Math.min(paired, missing, extra);\n current[j] = cost;\n steps[i * width + j] =\n paired === cost ? STEP.pair : missing === cost ? STEP.missing : STEP.extra;\n }\n [previous, current] = [current, previous];\n }\n\n const path: { step: Step; referenceIndex: number; attemptIndex: number }[] = [];\n let i = rows;\n let j = columns;\n while (i > 0 || j > 0) {\n const step = steps[i * width + j] as Step;\n if (step === STEP.pair) {\n i -= 1;\n j -= 1;\n } else if (step === STEP.missing) {\n i -= 1;\n } else {\n j -= 1;\n }\n path.push({ step, referenceIndex: i, attemptIndex: j });\n }\n return path.reverse();\n}\n\nfunction alignWords(\n reference: readonly string[],\n attempt: readonly string[],\n): DictationWordAlignment[] {\n const words: DictationWordAlignment[] = [];\n let ordinal = 0;\n for (const { step, referenceIndex, attemptIndex } of alignSequences(reference, attempt)) {\n if (step === STEP.extra) {\n words.push({\n reference: '',\n attempt: attempt[attemptIndex] as string,\n similarity: 0,\n status: 'extra',\n });\n continue;\n }\n ordinal += 1;\n const referenceWord = reference[referenceIndex] as string;\n if (step === STEP.missing) {\n words.push({\n itemId: `w${ordinal}`,\n reference: referenceWord,\n attempt: '',\n similarity: 0,\n status: 'missing',\n });\n continue;\n }\n const attemptWord = attempt[attemptIndex] as string;\n words.push({\n itemId: `w${ordinal}`,\n reference: referenceWord,\n attempt: attemptWord,\n similarity: similarity(referenceWord, attemptWord),\n status: referenceWord === attemptWord ? 'correct' : 'incorrect',\n });\n }\n return words;\n}\n","/**\n * Configurable free-text answer matching (R3.3).\n *\n * Grade-stability contract: the DEFAULTS reproduce the v1 fill-in-the-blanks\n * semantics exactly (trim, then locale-insensitive lowercase, strict `===`).\n * Every tolerance below is opt-in, because changing a default here changes\n * historical grades — and this SDK powers real summative exams.\n */\nexport interface TextMatchPolicy {\n /** Compare case-sensitively. Default `false` (v1 behaviour). */\n caseSensitive?: boolean;\n /** Strip leading/trailing whitespace before comparing. Default `true` (v1 behaviour). */\n trim?: boolean;\n /**\n * Unicode normalization applied to both sides before comparing. Default\n * `'none'` (v1 behaviour). `'NFC'` makes a decomposed `está` (combining\n * U+0301) equal its composed form — the fix for accent-input mismatches in\n * EN/ES/PT content.\n */\n normalize?: 'none' | 'NFC' | 'NFKC';\n /** Treat diacritics as equal to their base letters (`está` ≡ `esta`). Default `false`. */\n foldDiacritics?: boolean;\n /** Collapse runs of inner whitespace to a single space. Default `false`. */\n collapseInnerWhitespace?: boolean;\n /** Ignore Unicode punctuation on both sides. Default `false`. */\n ignorePunctuation?: boolean;\n /** Maximum Levenshtein edit distance still accepted as a match. Default `0`. */\n levenshtein?: number;\n /**\n * BCP 47 tag for locale-aware case folding (e.g. `'tr'` for Turkish dotted /\n * dotless I). Default: locale-insensitive `toLowerCase()` (v1 behaviour).\n */\n locale?: string;\n}\n\n/**\n * How a match was achieved. Lets a consumer award full credit for an `exact`\n * match and partial credit for a `folded` or `fuzzy` one (e.g. diacritic\n * tolerance in a listening gap-fill vs. a spelling test).\n * - `exact` — equal under the baseline trim/case rules alone.\n * - `normalized` — required Unicode normalization, whitespace collapse, or punctuation tolerance.\n * - `folded` — required diacritic folding.\n * - `fuzzy` — required Levenshtein tolerance.\n * - `none` — no accepted answer matched.\n */\nexport interface TextMatchResult {\n matched: boolean;\n via: 'exact' | 'normalized' | 'folded' | 'fuzzy' | 'none';\n}\n\n/** Combining marks (any script) removed for diacritic folding after NFD decomposition. */\nconst COMBINING_MARKS_RE = /\\p{M}/gu;\n/**\n * Unicode punctuation (`\\p{P}`) removed by `ignorePunctuation`. Deliberately\n * NOT `\\p{S}`: symbols like `$`, `+`, `%` can be the substance of an answer\n * (`$100` vs `100`), so a \"punctuation\" toggle must not erase them.\n */\nconst PUNCTUATION_RE = /\\p{P}/gu;\n\nfunction applyBaseline(value: string, policy: TextMatchPolicy): string {\n let result = value;\n if (policy.trim !== false) {\n result = result.trim();\n }\n if (policy.caseSensitive !== true) {\n result = policy.locale ? result.toLocaleLowerCase(policy.locale) : result.toLowerCase();\n }\n return result;\n}\n\nfunction applyNormalization(value: string, policy: TextMatchPolicy): string {\n let result = value;\n if (policy.normalize === 'NFC' || policy.normalize === 'NFKC') {\n result = result.normalize(policy.normalize);\n }\n if (policy.ignorePunctuation === true) {\n result = result.replace(PUNCTUATION_RE, '');\n }\n if (policy.collapseInnerWhitespace === true) {\n // \"Inner\" means between non-space runs. Only trim the ends when the\n // policy's trim is on (its default) — an explicit `trim: false` must not\n // be silently overridden by the collapse.\n result = result.replace(/(?<=\\S)\\s+(?=\\S)/g, ' ');\n if (policy.trim !== false) {\n result = result.trim();\n }\n }\n return result;\n}\n\nfunction applyDiacriticFold(value: string): string {\n return value.normalize('NFD').replace(COMBINING_MARKS_RE, '').normalize('NFC');\n}\n\n/**\n * Levenshtein distance with an early-exit bound: returns `max + 1` as soon as\n * the distance provably exceeds `max`.\n */\nexport function levenshteinDistance(a: string, b: string, max: number): number {\n if (a === b) return 0;\n if (Math.abs(a.length - b.length) > max) return max + 1;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n let previous = Array.from({ length: b.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i += 1) {\n const current = [i];\n let rowMin = i;\n for (let j = 1; j <= b.length; j += 1) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n const value = Math.min(\n (previous[j] as number) + 1,\n (current[j - 1] as number) + 1,\n (previous[j - 1] as number) + cost,\n );\n current.push(value);\n if (value < rowMin) rowMin = value;\n }\n if (rowMin > max) return max + 1;\n previous = current;\n }\n return previous[b.length] as number;\n}\n\n/**\n * Matches a learner's input against one or more accepted answers under a\n * {@link TextMatchPolicy}. Tolerances are evaluated in stages — baseline\n * (trim/case), then normalization, then diacritic folding, then Levenshtein —\n * and {@link TextMatchResult.via} reports the first stage that produced the\n * match, so graders can award credit by match quality.\n *\n * With no policy (or an empty one) this is byte-for-byte the v1 matching\n * semantics: `trim` + locale-insensitive `toLowerCase` + strict equality.\n */\nexport function matchText(\n input: string,\n accepted: string | readonly string[],\n policy: TextMatchPolicy = {},\n): TextMatchResult {\n const acceptedList = typeof accepted === 'string' ? [accepted] : accepted;\n const baselineInput = applyBaseline(input, policy);\n const baselineAccepted = acceptedList.map((answer) => applyBaseline(answer, policy));\n\n if (baselineAccepted.some((answer) => answer === baselineInput)) {\n return { matched: true, via: 'exact' };\n }\n\n const usesNormalization =\n policy.normalize === 'NFC' ||\n policy.normalize === 'NFKC' ||\n policy.ignorePunctuation === true ||\n policy.collapseInnerWhitespace === true;\n const normalizedInput = usesNormalization\n ? applyNormalization(baselineInput, policy)\n : baselineInput;\n const normalizedAccepted = usesNormalization\n ? baselineAccepted.map((answer) => applyNormalization(answer, policy))\n : baselineAccepted;\n\n if (usesNormalization && normalizedAccepted.some((answer) => answer === normalizedInput)) {\n return { matched: true, via: 'normalized' };\n }\n\n const foldedInput =\n policy.foldDiacritics === true ? applyDiacriticFold(normalizedInput) : normalizedInput;\n const foldedAccepted =\n policy.foldDiacritics === true\n ? normalizedAccepted.map((answer) => applyDiacriticFold(answer))\n : normalizedAccepted;\n\n if (policy.foldDiacritics === true && foldedAccepted.some((answer) => answer === foldedInput)) {\n return { matched: true, via: 'folded' };\n }\n\n // A learner who typed nothing made no typo. Levenshtein distance from an\n // empty string is just the answer's length, so `levenshtein: 1` on a\n // one-letter blank (\"a\", \"I\") marked an UNANSWERED blank correct — the\n // fuzzy stage rescuing a blank rather than a misspelling. No author enabling\n // typo tolerance intends that, so an empty input never reaches this stage.\n // Exact and normalized matching are untouched: an author who genuinely lists\n // \"\" as an accepted answer still gets it, deliberately, one stage earlier.\n const maxDistance = policy.levenshtein ?? 0;\n if (\n maxDistance > 0 &&\n foldedInput.trim().length > 0 &&\n foldedAccepted.some(\n (answer) => levenshteinDistance(foldedInput, answer, maxDistance) <= maxDistance,\n )\n ) {\n return { matched: true, via: 'fuzzy' };\n }\n\n return { matched: false, via: 'none' };\n}\n","import type { z } from 'zod/v4';\nimport type { DeferredScoringPartial, ScoringResult } from '../types/activity.js';\nimport type { DraftContext, DraftIssue } from '../types/authoring.js';\n\n/** ScoringResult without `passed` — the public `score()` / `evaluate()` fill that in. */\nexport type PartialScoringResult = Omit<ScoringResult, 'passed'>;\n\n/**\n * Sensitivity classification of a single activity-data field, driving\n * `redact()`:\n * - `public` — safe to send to a learner before they answer.\n * - `answer-key` — reveals (or helps infer) the correct answer or the scoring\n * rules; removed unless `reveal: 'after-submit'` is requested.\n * - `author-only` — never leaves the authoring/grading context (e.g. the\n * author transcript of a listening group's stimulus). A rubric is NOT one:\n * it tells the learner what they are assessed on, so it is `public`.\n */\nexport type Sensitivity = 'public' | 'answer-key' | 'author-only';\n\n/**\n * Per-field sensitivity map for an activity type. A `Sensitivity` value\n * classifies the whole field (objects and arrays included); a nested\n * `FieldPolicy` recurses into an object field — and, for an array field,\n * applies to every element. Fail-closed: any field a policy does not mention\n * is treated as `author-only` and removed by `redact()` — adding a field\n * without classifying it hides it, never leaks it.\n */\nexport interface FieldPolicy {\n readonly [field: string]: Sensitivity | FieldPolicy;\n}\n\n/** xAPI interaction types defined by xAPI 1.0.3 (cmi.interaction vocabulary). */\nexport type XAPIInteractionType =\n | 'choice'\n | 'fill-in'\n | 'long-fill-in'\n | 'matching'\n | 'sequencing'\n | 'performance'\n | 'true-false'\n | 'other';\n\n/** Interop facts a generic statement builder cannot infer from data alone. */\nexport interface ActivityTypeInterop<TData> {\n /** IRI for the xAPI Activity `definition.type`. */\n readonly xapiActivityTypeIri?: string;\n /** xAPI `definition.interactionType` for this activity type. */\n readonly xapiInteractionType?: XAPIInteractionType;\n /** Builds the xAPI `correctResponsesPattern` strings for an item. */\n readonly correctResponsesPattern?: (data: TData) => string[];\n}\n\n/**\n * How responses to an activity type are graded:\n * - `sync` — a pure function produces the grade at submit time.\n * - `deferred` — grading happens asynchronously (AI or human) after\n * submission; `partial` reports the facts computable synchronously\n * (e.g. word counts), surfaced in `ItemOutcome.partial`.\n */\nexport type ActivityTypeScoring<TData, TResponse> =\n | {\n readonly kind: 'sync';\n readonly score: (data: TData, response: TResponse) => PartialScoringResult;\n }\n | {\n readonly kind: 'deferred';\n readonly reason: 'requires_async_grading';\n readonly partial?: (data: TData, response: TResponse | undefined) => DeferredScoringPartial;\n };\n\n/**\n * Authoring support for an activity type: the empty draft an editor starts\n * from, and the rules that tell a draft that is unfinished from one that is\n * wrong. Both are optional. Without `checkDraft`, `validateDraft` still works\n * and reports every schema failure as `invalid`; without `createDraft`,\n * `createDraft()` throws for the type.\n */\nexport interface ActivityTypeAuthoring<TData> {\n /**\n * A new draft: every required field present, nothing authored yet. It must\n * come back from `validateDraft` as `incomplete` — never `invalid`, and never\n * `complete` — so a freshly added question is not reported as an error, and\n * cannot pass validation before anyone has written it.\n */\n readonly createDraft?: (context: DraftContext) => TData;\n /**\n * Reports what is missing (`incomplete`) and what is wrong (`invalid`) in a\n * draft. Receives any plain object and must not throw on one.\n *\n * Report each problem at the path the schema reports it. `validateDraft` adds\n * every schema failure as `invalid` unless an issue returned here sits at that\n * path or inside it — so an issue at the wrong path leaves the schema's\n * message standing beside yours, and makes an unfinished draft read as wrong.\n * An issue inside a path accounts for every failure at that path, a list's\n * length included, so report a rule about a whole list at the list's own path.\n * A failure at the root of the draft is accounted for only by an issue at the\n * root. A `null` the schema refuses, where nothing here reports it, comes back\n * as `null_not_allowed` — except inside a plain `z.union`, which reports a\n * failure once, at the union's own path, under its own code.\n *\n * A code documented in `docs/authoring.md` is reported with its documented\n * severity. For a code of your own, a severity other than `'incomplete'` is\n * reported as `invalid`.\n */\n readonly checkDraft?: (draft: Readonly<Record<string, unknown>>) => DraftIssue[];\n}\n\n/**\n * A value-level description of an activity type: its contract (schema), its\n * grading, its redaction policy, and its interop facts. Registering a\n * descriptor makes `validateActivity`, `validateDraft`, `score`, `evaluate`,\n * `redact`, and `jsonSchemaFor` work for the type — an activity type is a value,\n * not a hardcoded union member (R1).\n */\nexport interface ActivityTypeDescriptor<TData extends { type: string }, TResponse> {\n /** The `type` discriminator string (kebab-case by convention). */\n readonly type: TData['type'];\n /** Zod schema validating the activity's data contract. */\n readonly schema: z.ZodType<TData>;\n /** How responses are graded. */\n readonly scoring: ActivityTypeScoring<TData, TResponse>;\n /** Whether a response counts as an answer (vs. blank/untouched). */\n readonly isAnswered?: (response: TResponse | undefined) => boolean;\n /** Per-field sensitivity map driving `redact()`. Fail-closed. */\n readonly fieldPolicy?: FieldPolicy;\n /**\n * Schema the output of `redact(data)` (with default `reveal: 'none'`) must\n * satisfy. Strict by design: it proves the ABSENCE of answer-key fields,\n * so `assertRedacted` can guarantee a payload is safe to send to a learner.\n */\n readonly redactedSchema?: z.ZodType<unknown>;\n /** Interop facts for xAPI (and later QTI) statement building. */\n readonly interop?: ActivityTypeInterop<TData>;\n /** The interaction-event kinds components for this type emit. */\n readonly interactions?: readonly string[];\n /** Draft support for authoring tools. See {@link ActivityTypeAuthoring}. */\n readonly authoring?: ActivityTypeAuthoring<TData>;\n}\n\n/**\n * Internal type-erased descriptor shape stored in the registry. Dispatch call\n * sites cast payloads back; the public generic API preserves inference.\n */\nexport interface RegisteredActivityTypeDescriptor {\n readonly type: string;\n readonly schema: z.ZodType<unknown>;\n readonly scoring:\n | {\n readonly kind: 'sync';\n readonly score: (data: unknown, response: unknown) => PartialScoringResult;\n }\n | {\n readonly kind: 'deferred';\n readonly reason: 'requires_async_grading';\n readonly partial?: (data: unknown, response: unknown) => DeferredScoringPartial;\n };\n readonly isAnswered?: (response: unknown) => boolean;\n readonly fieldPolicy?: FieldPolicy;\n readonly redactedSchema?: z.ZodType<unknown>;\n readonly interop?: ActivityTypeInterop<unknown>;\n readonly interactions?: readonly string[];\n readonly authoring?: ActivityTypeAuthoring<unknown>;\n}\n\n/**\n * Module-scoped default registry (deliberate — no registry instances until a\n * second consumer exists; see roadmap §3.2 / audit §15.3).\n */\nconst registry = new Map<string, RegisteredActivityTypeDescriptor>();\n\n/**\n * Identity helper that gives full type inference when authoring a descriptor:\n *\n * ```ts\n * const myType = defineActivityType<MyTypeData, MyTypeLearnerResponse>({ ... });\n * registerActivityType(myType);\n * ```\n */\nexport function defineActivityType<TData extends { type: string }, TResponse>(\n descriptor: ActivityTypeDescriptor<TData, TResponse>,\n): ActivityTypeDescriptor<TData, TResponse> {\n return descriptor;\n}\n\n/**\n * Registers an activity type on the default registry, making it live for\n * `validateActivity`, `score`, `evaluate`, `redact`, and `jsonSchemaFor`.\n * Re-registering the same descriptor object is a no-op; registering a\n * DIFFERENT descriptor under an existing type throws — silently replacing a\n * type's contract or scoring is exactly the class of accident a summative\n * SDK must not allow.\n */\nexport function registerActivityType<TData extends { type: string }, TResponse>(\n descriptor: ActivityTypeDescriptor<TData, TResponse>,\n): void {\n if (descriptor.type === 'item-group') {\n // The container that holds several items around one stimulus. It has no\n // learner response and no score of its own, so it can never satisfy this\n // contract — and letting a consumer register one would make `isItemGroup`\n // and `flattenSequence` misread their own container.\n throw new Error(\n '\"item-group\" is reserved for the SDK\\'s item-group container (see ItemGroup) and cannot be registered as an activity type.',\n );\n }\n const existing = registry.get(descriptor.type);\n if (existing !== undefined) {\n if ((existing as unknown) === (descriptor as unknown)) {\n return;\n }\n throw new Error(\n `Activity type \"${descriptor.type}\" is already registered. ` +\n 'Registering a different descriptor for an existing type is not allowed.',\n );\n }\n registry.set(descriptor.type, descriptor as unknown as RegisteredActivityTypeDescriptor);\n}\n\n/** Returns the registered descriptor for `type`, or `undefined`. */\nexport function getActivityTypeDescriptor(\n type: string,\n): RegisteredActivityTypeDescriptor | undefined {\n return registry.get(type);\n}\n\n/** The type strings currently registered (built-ins plus consumer-registered). */\nexport function registeredActivityTypes(): string[] {\n return [...registry.keys()];\n}\n","import { MediaSchema } from '../schemas/media.js';\nimport type { DraftIssue, DraftSeverity } from '../types/authoring.js';\n\n/**\n * Every issue code the SDK's draft checks can report, with its severity.\n *\n * A code's severity is part of its contract, so it is fixed here, once: an\n * issue is built from its code, and the same code cannot be `incomplete` in one\n * place and `invalid` in another. `docs/authoring.md` documents this table, and\n * a test holds the two together.\n */\nexport const DRAFT_ISSUE_SEVERITY = {\n // Every registered type — reported by validateDraft itself\n null_not_allowed: 'invalid',\n // Every built-in activity\n schema_version_invalid: 'invalid',\n type_mismatch: 'invalid',\n id_required: 'invalid',\n title_required: 'incomplete',\n scoring_strategy_required: 'incomplete',\n pass_threshold_invalid: 'invalid',\n difficulty_level_invalid: 'invalid',\n feedback_empty: 'incomplete',\n redacted_data: 'invalid',\n media_type_required: 'incomplete',\n media_url_required: 'incomplete',\n media_url_invalid: 'invalid',\n media_alt_required: 'incomplete',\n media_playback_invalid: 'invalid',\n media_invalid: 'invalid',\n // multiple-choice\n mc_question_required: 'incomplete',\n mc_mode_required: 'incomplete',\n mc_options_too_few: 'incomplete',\n mc_options_too_many: 'invalid',\n mc_option_id_required: 'invalid',\n mc_option_id_duplicate: 'invalid',\n mc_option_text_required: 'incomplete',\n mc_option_correctness_required: 'incomplete',\n mc_option_media_kind: 'invalid',\n mc_correct_option_required: 'incomplete',\n mc_single_mode_one_correct: 'invalid',\n // fill-in-the-blanks\n fib_passage_required: 'incomplete',\n fib_blanks_required: 'incomplete',\n fib_blank_id_required: 'invalid',\n fib_blank_id_duplicate: 'invalid',\n fib_accepted_answers_required: 'incomplete',\n fib_accepted_answer_empty: 'incomplete',\n fib_levenshtein_invalid: 'invalid',\n fib_match_locale_invalid: 'invalid',\n fib_match_invalid: 'invalid',\n fib_blank_missing: 'incomplete',\n fib_placeholder_missing: 'incomplete',\n fib_placeholder_duplicate: 'invalid',\n fib_blanks_mismatch: 'invalid',\n // item-group (the testlet container, via validateItemGroupDraft)\n ig_not_an_object: 'invalid',\n ig_stimulus_required: 'incomplete',\n ig_stimulus_id_required: 'invalid',\n ig_stimulus_kind_required: 'incomplete',\n ig_stimulus_kind_invalid: 'invalid',\n ig_stimulus_body_required: 'incomplete',\n ig_stimulus_media_required: 'incomplete',\n ig_stimulus_media_kind: 'invalid',\n ig_items_required: 'incomplete',\n ig_item_id_required: 'invalid',\n ig_item_id_duplicate: 'invalid',\n ig_item_type_required: 'incomplete',\n ig_item_type_unknown: 'invalid',\n ig_item_nested_group: 'invalid',\n ig_shuffle_invalid: 'invalid',\n // gap-select\n gs_passage_required: 'incomplete',\n gs_gaps_required: 'incomplete',\n gs_gap_id_required: 'invalid',\n gs_gap_id_duplicate: 'invalid',\n gs_gap_missing: 'incomplete',\n gs_placeholder_missing: 'incomplete',\n gs_placeholder_duplicate: 'invalid',\n gs_gaps_mismatch: 'invalid',\n gs_presentation_invalid: 'invalid',\n gs_bank_id_required: 'invalid',\n gs_bank_id_duplicate: 'invalid',\n gs_bank_unknown: 'invalid',\n gs_choice_source_required: 'incomplete',\n gs_choice_source_conflict: 'invalid',\n gs_choices_too_few: 'incomplete',\n gs_choice_id_required: 'invalid',\n gs_choice_id_duplicate: 'invalid',\n gs_choice_text_required: 'incomplete',\n gs_correct_choice_required: 'incomplete',\n gs_correct_choice_unknown: 'invalid',\n // written-response\n wr_prompt_required: 'incomplete',\n wr_min_words_required: 'incomplete',\n wr_min_words_invalid: 'invalid',\n wr_max_words_required: 'incomplete',\n wr_max_words_invalid: 'invalid',\n wr_word_bounds_order: 'invalid',\n wr_rubric_criteria_required: 'incomplete',\n wr_criterion_name_required: 'incomplete',\n wr_criterion_weight_required: 'incomplete',\n wr_criterion_weight_invalid: 'invalid',\n wr_rubric_weights_zero: 'incomplete',\n wr_rubric_weights_too_large: 'invalid',\n // dictation\n dc_transcript_required: 'incomplete',\n dc_transcript_unscorable: 'invalid',\n dc_transcript_too_long: 'invalid',\n dc_accepted_transcript_empty: 'incomplete',\n dc_accepted_transcript_too_long: 'invalid',\n dc_accepted_transcript_duplicate: 'invalid',\n dc_accepted_transcripts_too_many: 'invalid',\n dc_media_kind: 'invalid',\n dc_captions_not_allowed: 'invalid',\n dc_slow_media_kind: 'invalid',\n dc_slow_media_playback: 'invalid',\n dc_slow_media_unbudgeted: 'invalid',\n dc_slow_media_same_url: 'invalid',\n dc_slow_media_without_media: 'incomplete',\n dc_transcript_revealed: 'invalid',\n dc_shown_text_too_long: 'invalid',\n dc_hints_mode_required: 'incomplete',\n dc_hints_mode_invalid: 'invalid',\n dc_equivalence_from_required: 'incomplete',\n dc_equivalence_to_required: 'incomplete',\n dc_tolerance_invalid: 'invalid',\n // read-aloud\n ra_reference_text_required: 'incomplete',\n ra_reference_text_too_long: 'invalid',\n ra_reference_text_unreadable: 'invalid',\n ra_reference_text_unspaced_script: 'invalid',\n ra_locale_required: 'incomplete',\n ra_locale_invalid: 'invalid',\n ra_max_seconds_required: 'incomplete',\n ra_max_seconds_out_of_range: 'invalid',\n ra_min_seconds_out_of_range: 'invalid',\n ra_max_takes_out_of_range: 'invalid',\n ra_dimensions_required: 'incomplete',\n ra_dimension_name_invalid: 'invalid',\n ra_dimension_duplicate: 'invalid',\n ra_dimension_weight_invalid: 'invalid',\n ra_dimension_weights_zero: 'invalid',\n ra_media_kind: 'invalid',\n ra_slow_media_without_media: 'incomplete',\n ra_slow_media_same_recording: 'invalid',\n ra_slow_media_beside_play_limit: 'invalid',\n} as const satisfies Readonly<Record<string, DraftSeverity>>;\n\nexport type DraftIssueCode = keyof typeof DRAFT_ISSUE_SEVERITY;\n\n/** A draft as a check receives it: any plain object. */\nexport type DraftFields = Readonly<Record<string, unknown>>;\n\n/** A schema library issue, as far as the draft checks read one. */\nexport interface SchemaIssue {\n readonly code: string;\n readonly path: readonly PropertyKey[];\n readonly input?: unknown;\n}\n\nexport function issue(\n code: DraftIssueCode,\n path: readonly (string | number)[],\n message: string,\n): DraftIssue {\n return { path: path.map(String), message, code, severity: DRAFT_ISSUE_SEVERITY[code] };\n}\n\nexport function isRecord(value: unknown): value is DraftFields {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * The value at `path` inside `root`, or `undefined` once the path leaves the\n * data. Used to tell a schema failure caused by `null` from any other.\n */\nexport function valueAt(root: unknown, path: readonly PropertyKey[]): unknown {\n let node: unknown = root;\n for (const segment of path) {\n if (typeof node !== 'object' || node === null) {\n return undefined;\n }\n node = (node as Record<PropertyKey, unknown>)[segment];\n }\n return node;\n}\n\n/**\n * Whether a schema issue is the schema refusing an empty value: a `null`, or an\n * `undefined` entry in a list, which JSON writes as `null`. `validateDraft`\n * reports each such refusal as `null_not_allowed`, so a check leaves them to it.\n *\n * It reads the issue's `input`, so parse with `{ reportInput: true }`. The input\n * is the value the failing check was given. A refinement on a whole object that\n * reports at one of its fields carries the object, so a rule that points at an\n * empty field keeps its own code and message. A discriminated union with no\n * member for its discriminator is the one refusal reported with the object as\n * its input.\n */\nexport function refusesEmpty(root: unknown, schemaIssue: SchemaIssue): boolean {\n const { path } = schemaIssue;\n const value = valueAt(root, path);\n const index = path.at(-1);\n const list = valueAt(root, path.slice(0, -1));\n const empty =\n value === null ||\n (value === undefined &&\n typeof index === 'number' &&\n Array.isArray(list) &&\n index < list.length);\n return empty && (schemaIssue.input === value || schemaIssue.code === 'invalid_union');\n}\n\n/** Absent or `null`: a field nobody has set. */\nexport function isUnset(value: unknown): boolean {\n return value === undefined || value === null;\n}\n\n/**\n * Not written yet: absent, `null`, or a string holding nothing but whitespace.\n *\n * Whitespace counts as unwritten even where the schema accepts it — a title of\n * `\" \"` is a valid string and an unfinished question. So does the empty string\n * a `<select>` placeholder hands over for a field that must be chosen. A value\n * of any other type is left to the schema, which reports it as `invalid`.\n */\nexport function isUnwritten(value: unknown): boolean {\n return isUnset(value) || (typeof value === 'string' && value.trim() === '');\n}\n\n/** An id with nothing in it. Whitespace is an id the schema accepts, so it is one here too. */\nexport function isMissingId(value: unknown): boolean {\n return isUnset(value) || value === '';\n}\n\n/**\n * A whole number JavaScript holds exactly — what the schemas' `.int()` accepts.\n * `2 ** 53` is a whole number, and the schemas refuse it.\n */\nexport function isWholeNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value);\n}\n\n/** A whole number above what JavaScript holds exactly, so that a message can say so. */\nexport function isTooLarge(value: unknown): boolean {\n return typeof value === 'number' && value > Number.MAX_SAFE_INTEGER && Number.isInteger(value);\n}\n\n/**\n * The envelope, `id` and `title`, which every built-in activity has, and which\n * lead its issues. `schemaVersion` and `type` identify the payload rather than\n * say anything an author wrote, so a wrong or missing one is `invalid`.\n */\nexport function checkIdentity(draft: DraftFields, type: string): DraftIssue[] {\n const issues: DraftIssue[] = [];\n if (draft.schemaVersion !== '1.0') {\n issues.push(\n issue(\n 'schema_version_invalid',\n ['schemaVersion'],\n 'The draft must have schemaVersion \"1.0\". A draft from createDraft has it.',\n ),\n );\n }\n if (draft.type !== type) {\n issues.push(issue('type_mismatch', ['type'], `The draft's type must be \"${type}\".`));\n }\n if (isMissingId(draft.id)) {\n issues.push(issue('id_required', ['id'], 'The activity has no id.'));\n }\n if (isUnwritten(draft.title)) {\n issues.push(issue('title_required', ['title'], 'Add a title.'));\n }\n return issues;\n}\n\n/** `scoringStrategy`, which both synchronously scored built-ins require. */\nexport function checkScoringStrategy(draft: DraftFields): DraftIssue[] {\n return isUnwritten(draft.scoringStrategy)\n ? [\n issue(\n 'scoring_strategy_required',\n ['scoringStrategy'],\n 'Choose how the question is scored.',\n ),\n ]\n : [];\n}\n\nconst DIFFICULTY_LEVELS: readonly unknown[] = [1, 2, 3, 4, 5];\n\n/**\n * The optional fields every built-in activity shares, checked after its own\n * fields. A `null` in any of them is left to `validateDraft`, which reports\n * `null_not_allowed` for every type alike.\n */\nexport function checkSharedOptional(draft: DraftFields): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const threshold = draft.passThreshold;\n if (!isUnset(threshold) && !(typeof threshold === 'number' && threshold >= 0 && threshold <= 1)) {\n issues.push(\n issue(\n 'pass_threshold_invalid',\n ['passThreshold'],\n 'The pass threshold must be a number from 0 to 1.',\n ),\n );\n }\n if (!isUnset(draft.difficultyLevel) && !DIFFICULTY_LEVELS.includes(draft.difficultyLevel)) {\n issues.push(\n issue(\n 'difficulty_level_invalid',\n ['difficultyLevel'],\n 'The difficulty level must be a whole number from 1 to 5.',\n ),\n );\n }\n const feedback = draft.feedback;\n if (isRecord(feedback)) {\n for (const key of ['correct', 'incorrect'] as const) {\n const message = feedback[key];\n if (typeof message === 'string' && message.trim() === '') {\n issues.push(\n issue('feedback_empty', ['feedback', key], 'Write this feedback message, or remove it.'),\n );\n }\n }\n }\n // What `redact()` produces for a learner. Its answer key is gone, and the\n // components refuse it outright in `practice`, so it is never a draft.\n if (draft.redacted === true) {\n issues.push(\n issue(\n 'redacted_data',\n ['redacted'],\n 'This is a copy prepared for learners, with the answer key removed. Edit the original activity instead.',\n ),\n );\n }\n if (isRecord(draft.media)) {\n issues.push(...checkMedia(draft.media));\n }\n return issues;\n}\n\nconst URL_POLICY =\n 'Use an https:, http:, data: or blob: address, or a path on the same site that starts with a single \"/\".';\nconst EMBED_URL =\n \"An embed needs the provider's full http(s) embed address, such as https://www.youtube.com/embed/VIDEO_ID.\";\n\n/**\n * Media, checked through `MediaSchema` itself, so the URL and playback rules\n * live in one place. What this adds is the split: a kind, an address or a\n * description nobody has given yet is `incomplete`; one that is given and\n * refused is `invalid`.\n */\nexport function checkMedia(\n media: DraftFields,\n prefix: readonly (string | number)[] = ['media'],\n schema: { safeParse: typeof MediaSchema.safeParse } = MediaSchema,\n): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const at = (...rest: (string | number)[]) => [...prefix, ...rest];\n const parsed = schema.safeParse(media, { reportInput: true });\n // A refused `null` inside the media block is `validateDraft`'s to report, as\n // `null_not_allowed` — unless a check below reads it as a field not set yet.\n const schemaIssues = parsed.success\n ? []\n : parsed.error.issues.filter((found) => !refusesEmpty(media, found));\n const refusal = (field: string) => schemaIssues.find((found) => found.path[0] === field);\n\n const typeUnset = isUnwritten(media.type);\n if (typeUnset) {\n issues.push(issue('media_type_required', at('type'), 'Choose what kind of media this is.'));\n }\n\n if (isUnwritten(media.url)) {\n issues.push(issue('media_url_required', at('url'), 'Add the address of the media file.'));\n } else {\n const refused = refusal('url');\n if (refused !== undefined) {\n // The only refinement on `url` is the embed rule; every other refusal is\n // the address policy itself, which zod reports only as \"Invalid input\".\n issues.push(\n issue('media_url_invalid', at('url'), refused.code === 'custom' ? EMBED_URL : URL_POLICY),\n );\n }\n }\n\n const needsAlt = media.type === 'image' || media.type === 'embed';\n const alt = media.alt;\n if ((needsAlt && isUnwritten(alt)) || (typeof alt === 'string' && alt.trim() === '')) {\n issues.push(issue('media_alt_required', at('alt'), 'Add a text description of the media.'));\n }\n\n const captions = media.captionsUrl;\n if (typeof captions === 'string' && captions.trim() === '') {\n // Optional, and left blank: unfinished, as a blank description is.\n issues.push(\n issue(\n 'media_url_required',\n at('captionsUrl'),\n 'Add the address of the captions file, or remove the captions.',\n ),\n );\n } else if (refusal('captionsUrl') !== undefined) {\n issues.push(issue('media_url_invalid', at('captionsUrl'), URL_POLICY));\n }\n\n for (const schemaIssue of schemaIssues) {\n const field = schemaIssue.path[0];\n if (\n field === 'url' ||\n field === 'alt' ||\n field === 'captionsUrl' ||\n (field === 'type' && typeUnset)\n ) {\n continue;\n }\n issues.push(\n issue(\n field === 'playback' ? 'media_playback_invalid' : 'media_invalid',\n at(...schemaIssue.path.map(String)),\n schemaIssue.message,\n ),\n );\n }\n return issues;\n}\n\n/**\n * Whether a check issue at `reported` accounts for a schema failure at `failed`:\n * at the same path, or inside it. The root is the exception. Every path is inside\n * the root, so a root-level failure is accounted for only by a root-level issue.\n */\nexport function covers(reported: readonly string[], failed: readonly string[]): boolean {\n if (failed.length === 0) {\n return reported.length === 0;\n }\n // Each segment of the failed path matches the reported path at the same\n // position. A failed path longer than the reported one cannot: the reported\n // path runs out, and no segment is ever undefined.\n return failed.every((segment, index) => reported[index] === segment);\n}\n\n/** A path as one comparable string; the root is `''`. */\nexport function pathKey(path: readonly unknown[]): string {\n return path.length === 0 ? '' : JSON.stringify(path.map(String));\n}\n\n/**\n * Every path a schema failure is covered at, given the issues already reported\n * — the lookup form of {@link covers}: each prefix of each reported path, and\n * the root only when an issue sits at the root. Built once, so matching a\n * schema's failures costs the depth of each path rather than a pass over every\n * reported issue, which made a draft with thousands of malformed entries take\n * seconds.\n */\nexport function coveredPathsOf(reported: readonly { path: readonly unknown[] }[]): Set<string> {\n const covered = new Set<string>();\n for (const known of reported) {\n if (known.path.length === 0) {\n covered.add('');\n }\n for (let length = 1; length <= known.path.length; length += 1) {\n covered.add(pathKey(known.path.slice(0, length)));\n }\n }\n return covered;\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport { DictationSlowMediaSchema, DictationToleranceSchema } from '../schemas/dictation.js';\nimport {\n DICTATION_MAX_ACCEPTED_TRANSCRIPTS,\n DICTATION_MAX_EQUIVALENCES,\n DICTATION_MAX_TRANSCRIPT_LENGTH,\n type DictationNormalizer,\n dictationNormalizer,\n isBlankBeforeRules,\n isLongerThan,\n normalizeDictationText,\n preStripNormalize,\n revealsCandidate,\n WORKING_LENGTH,\n} from '../scoring/dictation/normalize.js';\nimport type { DictationData, DictationTolerance } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkMedia,\n checkSharedOptional,\n coveredPathsOf,\n type DraftFields,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n pathKey,\n refusesEmpty,\n} from './issues.js';\n\n/**\n * Draft support for `dictation`.\n *\n * A new draft is an empty title and an empty transcript: no recording, no\n * hints, no tolerances, no accepted alternatives, and no `scoringStrategy`,\n * because the type has one strategy. The transcript is the answer key, so it\n * starts unwritten and the draft stays `incomplete` until somebody writes it.\n *\n * The rules that make a draft `invalid` are the ones writing more cannot fix:\n * a transcript nothing survives of once normalised, a recording that is not\n * audio, captions on a recording (the captions are the answer), a title that\n * gives the answer away or is too long to check, a slow recording beside a\n * play budget.\n */\nexport const dictationAuthoring: ActivityTypeAuthoring<DictationData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'dictation',\n id: newId(),\n title: '',\n transcript: '',\n }),\n checkDraft: checkDictationDraft,\n};\n\nfunction checkDictationDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'dictation');\n const tolerance = isRecord(draft.tolerance)\n ? (draft.tolerance as unknown as DictationTolerance)\n : undefined;\n // One normalisation per string for every check below.\n const normalizer = dictationNormalizer(tolerance);\n\n issues.push(...checkTranscript(draft.transcript, normalizer));\n issues.push(...checkAcceptedTranscripts(draft, normalizer));\n issues.push(...checkRecording(draft.media));\n issues.push(...checkSlowRecording(draft));\n issues.push(...checkRevealed(draft, normalizer));\n issues.push(...checkHints(draft.hints));\n issues.push(...checkTolerance(draft.tolerance));\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\n/**\n * Not written yet: absent, `null`, or nothing but what the normaliser removes\n * before it reads a word — spaces, line breaks, format and control characters,\n * variation selectors. A text over the cap is written, whatever it holds: 3,000\n * spaces are too long, as 3,000 zero-width spaces are.\n */\nfunction isUnwrittenTranscript(value: unknown): boolean {\n if (typeof value === 'string' && isLongerThan(value, DICTATION_MAX_TRANSCRIPT_LENGTH)) {\n return false;\n }\n return isUnwritten(value) || (typeof value === 'string' && isBlankBeforeRules(value));\n}\n\n/**\n * The accepted transcripts the checks normalise and search the shown text for:\n * the first `DICTATION_MAX_ACCEPTED_TRANSCRIPTS`, as the scorer reads them.\n * More is `dc_accepted_transcripts_too_many`, and measuring and searching for\n * every entry past the cap would let a pasted list take seconds to check.\n */\nfunction acceptedTranscriptsRead(accepted: readonly unknown[]): readonly unknown[] {\n return accepted.slice(0, DICTATION_MAX_ACCEPTED_TRANSCRIPTS);\n}\n\nfunction checkTranscript(transcript: unknown, normalizer: DictationNormalizer): DraftIssue[] {\n if (isUnwrittenTranscript(transcript)) {\n return [issue('dc_transcript_required', ['transcript'], 'Write the transcript.')];\n }\n if (typeof transcript !== 'string') {\n // A value of another type is the schema's to refuse.\n return [];\n }\n const measured = normalizer.measure(transcript);\n if (measured.tooLong) {\n return [\n issue(\n 'dc_transcript_too_long',\n ['transcript'],\n `The transcript is longer than ${DICTATION_MAX_TRANSCRIPT_LENGTH} characters before or after its equivalences are applied, or they grow it past ${WORKING_LENGTH} characters on the way. A dictation is a sentence or two.`,\n ),\n ];\n }\n if (measured.normalized === '') {\n return [\n issue(\n 'dc_transcript_unscorable',\n ['transcript'],\n 'The transcript has nothing to type: once case, punctuation and spacing are ignored, nothing is left.',\n ),\n ];\n }\n return [];\n}\n\nfunction checkAcceptedTranscripts(\n draft: DraftFields,\n normalizer: DictationNormalizer,\n): DraftIssue[] {\n const accepted = draft.acceptedTranscripts;\n if (!Array.isArray(accepted)) {\n return [];\n }\n const issues: DraftIssue[] = [];\n if (accepted.length > DICTATION_MAX_ACCEPTED_TRANSCRIPTS) {\n issues.push(\n issue(\n 'dc_accepted_transcripts_too_many',\n ['acceptedTranscripts'],\n `At most ${DICTATION_MAX_ACCEPTED_TRANSCRIPTS} accepted transcripts. Prefer an equivalence for a word that varies.`,\n ),\n );\n }\n for (const [index, text] of accepted.entries()) {\n // A `null` entry is `validateDraft`'s to report, as `null_not_allowed`.\n if (typeof text !== 'string') {\n continue;\n }\n if (index >= DICTATION_MAX_ACCEPTED_TRANSCRIPTS) {\n // Past the cap, only what the schema refuses of each entry is looked for — an empty one.\n if (text === '') {\n issues.push(acceptedTranscriptEmpty(index));\n }\n continue;\n }\n const measured = normalizer.measure(text);\n if (measured.tooLong) {\n issues.push(\n issue(\n 'dc_accepted_transcript_too_long',\n ['acceptedTranscripts', index],\n `Accepted transcript ${index + 1} is longer than ${DICTATION_MAX_TRANSCRIPT_LENGTH} characters before or after its equivalences are applied, or they grow it past ${WORKING_LENGTH} characters on the way.`,\n ),\n );\n } else if (measured.normalized === '') {\n issues.push(acceptedTranscriptEmpty(index));\n }\n }\n\n const candidates = [draft.transcript, ...acceptedTranscriptsRead(accepted)]\n .filter((text): text is string => typeof text === 'string')\n .map((text) => normalizer.measure(text).normalized)\n .filter((text): text is string => text !== null && text !== '');\n if (new Set(candidates).size !== candidates.length) {\n issues.push(\n issue(\n 'dc_accepted_transcript_duplicate',\n ['acceptedTranscripts'],\n 'An accepted transcript repeats the transcript, or another accepted transcript, once case, punctuation and spacing are ignored. Remove it.',\n ),\n );\n }\n return issues;\n}\n\nfunction acceptedTranscriptEmpty(index: number): DraftIssue {\n return issue(\n 'dc_accepted_transcript_empty',\n ['acceptedTranscripts', index],\n `Accepted transcript ${index + 1} has nothing to type. Fill it in or remove it.`,\n );\n}\n\n/** The recording. Its address, description and playback are `checkSharedOptional`'s; the kind and the captions are this type's. */\nfunction checkRecording(media: unknown): DraftIssue[] {\n if (!isRecord(media)) {\n return [];\n }\n const issues: DraftIssue[] = [];\n const kind = media.type;\n if (!isUnwritten(kind) && kind !== 'audio') {\n issues.push(\n issue(\n 'dc_media_kind',\n ['media', 'type'],\n 'A dictation is dictated: its recording must be audio.',\n ),\n );\n }\n if (!isUnset(media.captionsUrl)) {\n issues.push(\n issue('dc_captions_not_allowed', ['media', 'captionsUrl'], CAPTIONS_ARE_THE_ANSWER),\n );\n }\n return issues;\n}\n\nconst CAPTIONS_ARE_THE_ANSWER =\n 'A dictation recording cannot carry captions: the captions are the answer. Remove them, and offer an accessible alternative as a different item.';\n\n/**\n * The slow recording: the media rules under `slowMedia`, plus what it may not\n * have. A written kind that is not audio gets this type's own code, and the\n * address and description rules then run as for a recording — `checkMedia`\n * would otherwise report the schema's refusal of the kind a second time, as\n * `media_invalid`, and demand a description an image needs and a recording\n * does not. A slow recording of the wrong kind with no address reports both,\n * each under its own code.\n */\nfunction checkSlowRecording(draft: DraftFields): DraftIssue[] {\n const slow = draft.slowMedia;\n if (!isRecord(slow)) {\n return [];\n }\n const issues: DraftIssue[] = [];\n const kind = slow.type;\n const wrongKind = !isUnwritten(kind) && kind !== 'audio';\n if (wrongKind) {\n issues.push(\n issue('dc_slow_media_kind', ['slowMedia', 'type'], 'The slow recording must be audio.'),\n );\n }\n issues.push(\n ...checkMedia(\n wrongKind ? { ...slow, type: 'audio' } : slow,\n ['slowMedia'],\n DictationSlowMediaSchema,\n ),\n );\n if (!isUnset(slow.captionsUrl)) {\n issues.push(\n issue('dc_captions_not_allowed', ['slowMedia', 'captionsUrl'], CAPTIONS_ARE_THE_ANSWER),\n );\n }\n if (slow.playback !== undefined) {\n issues.push(\n issue(\n 'dc_slow_media_playback',\n ['slowMedia', 'playback'],\n 'The slow recording has no playback policy of its own. Put the policy on the recording; the slow recording follows it, except the play budget.',\n ),\n );\n }\n\n const media = draft.media;\n if (isUnset(media)) {\n issues.push(\n issue(\n 'dc_slow_media_without_media',\n ['media'],\n 'A slow recording accompanies a recording. Add the recording first.',\n ),\n );\n return issues;\n }\n if (!isRecord(media)) {\n return issues;\n }\n if (isRecord(media.playback) && media.playback.maxPlays !== undefined) {\n issues.push(\n issue(\n 'dc_slow_media_unbudgeted',\n ['slowMedia'],\n 'A play budget on the recording cannot coexist with a slow recording that has none. Remove the slow recording, or remove the play limit.',\n ),\n );\n }\n if (typeof slow.url === 'string' && slow.url !== '' && slow.url === media.url) {\n issues.push(\n issue(\n 'dc_slow_media_same_url',\n ['slowMedia', 'url'],\n 'The slow recording is the same file as the recording. Point it at the slower render.',\n ),\n );\n }\n return issues;\n}\n\n/** The title and the recording descriptions are shown before the learner types. */\nfunction checkRevealed(draft: DraftFields, normalizer: DictationNormalizer): DraftIssue[] {\n const candidates = [\n draft.transcript,\n ...(Array.isArray(draft.acceptedTranscripts)\n ? acceptedTranscriptsRead(draft.acceptedTranscripts)\n : []),\n ]\n .filter((text): text is string => typeof text === 'string')\n .map((text) => normalizer.measure(text));\n const shown: [readonly (string | number)[], unknown][] = [\n [['title'], draft.title],\n [['media', 'alt'], isRecord(draft.media) ? draft.media.alt : undefined],\n [['slowMedia', 'alt'], isRecord(draft.slowMedia) ? draft.slowMedia.alt : undefined],\n ];\n const issues: DraftIssue[] = [];\n for (const [path, text] of shown) {\n if (typeof text !== 'string') {\n continue;\n }\n const { tooLong, forms } = normalizer.shown(text);\n if (tooLong) {\n issues.push(\n issue(\n 'dc_shown_text_too_long',\n path,\n `${path[0] === 'title' ? 'The title' : 'The description'} is too long to be checked for the transcript: at most ${WORKING_LENGTH} characters before and after the equivalences are applied, and they may not grow it past that on the way.`,\n ),\n );\n } else if (candidates.some((candidate) => revealsCandidate(forms, candidate))) {\n issues.push(\n issue(\n 'dc_transcript_revealed',\n path,\n path[0] === 'title'\n ? 'The title gives the answer away: it contains the transcript. Name the exercise instead (\"Listen and type the sentence\").'\n : 'The description gives the answer away: it contains the transcript. Describe the recording (\"Recording, normal speed\"), never transcribe it.',\n ),\n );\n }\n }\n return issues;\n}\n\nfunction checkHints(hints: unknown): DraftIssue[] {\n if (!isRecord(hints)) {\n return [];\n }\n const mode = hints.mode;\n if (isUnwritten(mode)) {\n return [issue('dc_hints_mode_required', ['hints', 'mode'], 'Choose how hints are given.')];\n }\n if (mode !== 'progressive-words') {\n return [\n issue(\n 'dc_hints_mode_invalid',\n ['hints', 'mode'],\n 'The only hint mode is \"progressive-words\". Leave hints out unless you mean to offer them.',\n ),\n ];\n }\n return [];\n}\n\n/**\n * The tolerances: each rule's two halves, then whatever else the schema\n * refuses, under one code with the schema's message — the way\n * fill-in-the-blanks reports a match policy.\n */\nfunction checkTolerance(tolerance: unknown): DraftIssue[] {\n if (isUnset(tolerance)) {\n return [];\n }\n if (!isRecord(tolerance)) {\n return [\n issue(\n 'dc_tolerance_invalid',\n ['tolerance'],\n 'The tolerances are an object: `{ equivalences: [{ from, to }] }`.',\n ),\n ];\n }\n const issues: DraftIssue[] = [];\n const rules = tolerance.equivalences;\n if (Array.isArray(rules)) {\n // Counted here, not left to the schema: a rule with a half still to write\n // would otherwise cover the schema's count at its ancestor path, and a\n // draft over the cap would read as merely incomplete.\n if (rules.length > DICTATION_MAX_EQUIVALENCES) {\n issues.push(\n issue(\n 'dc_tolerance_invalid',\n ['tolerance', 'equivalences'],\n `A dictation carries at most ${DICTATION_MAX_EQUIVALENCES} equivalence rules.`,\n ),\n );\n }\n for (const [index, rule] of rules.entries()) {\n if (!isRecord(rule)) {\n continue;\n }\n const path = ['tolerance', 'equivalences', index];\n const from = rule.from;\n if (isUnwritten(from) || (typeof from === 'string' && preStripNormalize(from) === '')) {\n issues.push(\n issue(\n 'dc_equivalence_from_required',\n [...path, 'from'],\n `Say what rule ${index + 1} rewrites: a word, a phrase or a symbol.`,\n ),\n );\n }\n const to = rule.to;\n if (isUnwritten(to) || (typeof to === 'string' && normalizeDictationText(to) === '')) {\n issues.push(\n issue(\n 'dc_equivalence_to_required',\n [...path, 'to'],\n `Say what rule ${index + 1} rewrites to. A rule cannot delete a word or leave only punctuation.`,\n ),\n );\n }\n }\n }\n\n const parsed = DictationToleranceSchema.safeParse(tolerance, { reportInput: true });\n if (!parsed.success) {\n const covered = coveredPathsOf(issues);\n for (const schemaIssue of parsed.error.issues) {\n const path = ['tolerance', ...schemaIssue.path.map(String)];\n // A refused `null` is `validateDraft`'s to report, as `null_not_allowed`;\n // a half a rule already names, or the rule count, is reported once.\n if (refusesEmpty(tolerance, schemaIssue) || covered.has(pathKey(path))) {\n continue;\n }\n issues.push(issue('dc_tolerance_invalid', path, schemaIssue.message));\n for (const key of coveredPathsOf([{ path }])) {\n covered.add(key);\n }\n }\n }\n return issues;\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport { PLACEHOLDER_RE, TextMatchPolicySchema } from '../schemas/fill-in-the-blanks.js';\nimport type { FillInTheBlanksData } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkScoringStrategy,\n checkSharedOptional,\n type DraftFields,\n isMissingId,\n isRecord,\n issue,\n isTooLarge,\n isUnset,\n isUnwritten,\n isWholeNumber,\n refusesEmpty,\n} from './issues.js';\n\n/**\n * Draft support for `fill-in-the-blanks`.\n *\n * A new draft has an empty passage and no blanks, and `scoringStrategy` starts\n * at `all-or-nothing`, as for multiple choice: partial credit is the author's\n * call.\n *\n * The passage and the blanks must pair up one to one. When they do not, the\n * issue says which way. A placeholder with no blank, or a blank with no\n * placeholder, is something still to be written (`incomplete`). The same\n * placeholder twice, or two blanks sharing an id, cannot be fixed by writing\n * more (`invalid`). All of them are reported at `passage`, which is where the\n * schema reports the pairing.\n */\nexport const fillInTheBlanksAuthoring: ActivityTypeAuthoring<FillInTheBlanksData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'fill-in-the-blanks',\n id: newId(),\n title: '',\n passage: '',\n blanks: [],\n scoringStrategy: 'all-or-nothing',\n }),\n checkDraft: checkFillInTheBlanksDraft,\n};\n\nfunction checkFillInTheBlanksDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'fill-in-the-blanks');\n const passage = draft.passage;\n if (isUnwritten(passage)) {\n issues.push(issue('fib_passage_required', ['passage'], 'Write the passage.'));\n }\n issues.push(...checkScoringStrategy(draft));\n\n const blanks = isUnset(draft.blanks) ? [] : draft.blanks;\n if (Array.isArray(blanks)) {\n if (blanks.length === 0) {\n issues.push(\n issue(\n 'fib_blanks_required',\n ['blanks'],\n 'Add at least one blank, and mark where it goes in the passage with {{id}}.',\n ),\n );\n }\n const ids: string[] = [];\n for (const [index, blank] of blanks.entries()) {\n if (!isRecord(blank)) {\n continue;\n }\n const name = typeof blank.id === 'string' && blank.id !== '' ? `\"${blank.id}\"` : index + 1;\n if (isMissingId(blank.id)) {\n issues.push(\n issue('fib_blank_id_required', ['blanks', index, 'id'], `Blank ${index + 1} has no id.`),\n );\n } else if (typeof blank.id === 'string') {\n ids.push(blank.id);\n }\n\n const answers = isUnset(blank.acceptedAnswers) ? [] : blank.acceptedAnswers;\n if (Array.isArray(answers)) {\n if (answers.length === 0) {\n issues.push(\n issue(\n 'fib_accepted_answers_required',\n ['blanks', index, 'acceptedAnswers'],\n `Add an accepted answer for blank ${name}.`,\n ),\n );\n }\n for (const [position, answer] of answers.entries()) {\n // A `null` entry is `validateDraft`'s to report, as `null_not_allowed`.\n if (typeof answer === 'string' && answer.trim() === '') {\n issues.push(\n issue(\n 'fib_accepted_answer_empty',\n ['blanks', index, 'acceptedAnswers', position],\n `An accepted answer for blank ${name} is empty. Fill it in or remove it.`,\n ),\n );\n }\n }\n }\n\n if (isRecord(blank.match)) {\n issues.push(...checkMatch(blank.match, index));\n }\n }\n // An unwritten passage is already reported, and pairing blanks against it\n // would only restate that every blank is missing from it.\n if (typeof passage === 'string' && passage.trim() !== '') {\n issues.push(...checkPairing(passage, blanks, ids));\n }\n }\n\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\n/**\n * A blank's matching tolerances, checked through `TextMatchPolicySchema` so its\n * rules live in one place, plus the one thing the schema cannot see: a locale\n * the runtime refuses. `matchText` hands a non-empty `locale` to\n * `toLocaleLowerCase`, which throws on a tag that is not a language tag, so a\n * draft holding one would be \"complete\" and still crash the moment it was\n * scored.\n */\nfunction checkMatch(match: Readonly<Record<string, unknown>>, index: number): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const at = (...rest: (string | number)[]) => ['blanks', index, 'match', ...rest];\n\n const tolerance = match.levenshtein;\n if (!isUnset(tolerance) && !(isWholeNumber(tolerance) && tolerance >= 0)) {\n issues.push(\n issue(\n 'fib_levenshtein_invalid',\n at('levenshtein'),\n isTooLarge(tolerance)\n ? 'Typo tolerance is too large.'\n : 'Typo tolerance must be a whole number, 0 or more.',\n ),\n );\n }\n\n const locale = match.locale;\n if (typeof locale === 'string' && locale !== '' && !isLanguageTag(locale)) {\n issues.push(\n issue(\n 'fib_match_locale_invalid',\n at('locale'),\n `\"${locale}\" is not a language tag. Use one such as \"tr\" or \"en-US\", or leave the locale out.`,\n ),\n );\n }\n\n const parsed = TextMatchPolicySchema.safeParse(match, { reportInput: true });\n if (!parsed.success) {\n for (const schemaIssue of parsed.error.issues) {\n // `levenshtein` has its own code above, and a refused `null` is\n // `validateDraft`'s to report, as `null_not_allowed`.\n if (schemaIssue.path[0] === 'levenshtein' || refusesEmpty(match, schemaIssue)) {\n continue;\n }\n issues.push(\n issue('fib_match_invalid', at(...schemaIssue.path.map(String)), schemaIssue.message),\n );\n }\n }\n return issues;\n}\n\n/** Whether the runtime accepts `tag` as a language tag — the same test `toLocaleLowerCase` applies. */\nfunction isLanguageTag(tag: string): boolean {\n try {\n Intl.getCanonicalLocales(tag);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction checkPairing(passage: string, blanks: readonly unknown[], ids: readonly string[]) {\n const issues: DraftIssue[] = [];\n const placeholders = new Map<string, number>();\n for (const match of passage.matchAll(PLACEHOLDER_RE)) {\n const id = match[1] as string;\n placeholders.set(id, (placeholders.get(id) ?? 0) + 1);\n }\n const blankIds = new Map<string, number>();\n for (const id of ids) {\n blankIds.set(id, (blankIds.get(id) ?? 0) + 1);\n }\n\n for (const [id, count] of blankIds) {\n if (count > 1) {\n issues.push(\n issue('fib_blank_id_duplicate', ['passage'], `More than one blank has the id \"${id}\".`),\n );\n }\n }\n for (const [id, count] of placeholders) {\n if (count > 1) {\n issues.push(\n issue(\n 'fib_placeholder_duplicate',\n ['passage'],\n `{{${id}}} appears more than once in the passage. Each blank goes in one place.`,\n ),\n );\n }\n if (!blankIds.has(id)) {\n issues.push(\n issue(\n 'fib_blank_missing',\n ['passage'],\n `The passage has {{${id}}}, but there is no blank with that id.`,\n ),\n );\n }\n }\n for (const id of blankIds.keys()) {\n if (!placeholders.has(id)) {\n issues.push(\n issue(\n 'fib_placeholder_missing',\n ['passage'],\n `Blank \"${id}\" is not in the passage. Put {{${id}}} where it goes.`,\n ),\n );\n }\n }\n\n // The schema's own pairing rule, stated exactly as the schema states it. Each\n // code above is one specific way of breaking it. If it is broken in a way none\n // of them names — a blank with no usable id, for one — this says so, rather\n // than leaving the schema's message to be reported as a second failure.\n if (issues.length === 0 && !pairsOneToOne(placeholders, blanks)) {\n issues.push(\n issue(\n 'fib_blanks_mismatch',\n ['passage'],\n 'The blanks and the {{id}} placeholders in the passage do not pair up one to one.',\n ),\n );\n }\n return issues;\n}\n\n/**\n * `FillInTheBlanksDataSchema`'s pairing refinement, over a draft's raw blanks.\n * An entry that is not an object is reported on its own, and the schema never\n * reaches its refinement past one, so it takes no part in the pairing.\n */\nfunction pairsOneToOne(placeholders: ReadonlyMap<string, number>, blanks: readonly unknown[]) {\n const blankIds = blanks.filter(isRecord).map((blank) => blank.id);\n if (new Set(blankIds).size !== blankIds.length) {\n return false;\n }\n if (placeholders.size !== blankIds.length) {\n return false;\n }\n return blankIds.every((id) => typeof id === 'string' && placeholders.get(id) === 1);\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport { PLACEHOLDER_RE } from '../schemas/fill-in-the-blanks.js';\nimport type { GapSelectData } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkScoringStrategy,\n checkSharedOptional,\n type DraftFields,\n isMissingId,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n} from './issues.js';\n\n/** Mirrors `GapSelectChoiceSchema` arrays: `.min(2)` on both a gap's own list and a bank. */\nconst MIN_CHOICES = 2;\n\n/**\n * Draft support for `gap-select`.\n *\n * A new draft is an empty passage with no gaps, like fill-in-the-blanks, and\n * `scoringStrategy` starts at `all-or-nothing`: partial credit is the author's\n * to give.\n *\n * The one thing this type must never do is pre-select a correct choice. A gap\n * whose `correctChoiceId` already points at the first option lets an author\n * write four plausible choices, never open the correctness control, and hold a\n * `complete` question whose answer key is whatever happened to be listed first.\n * `correctChoiceId` therefore starts unwritten and the draft stays `incomplete`\n * until somebody says which choice is right — the same rule multiple-choice\n * follows for `isCorrect`.\n */\nexport const gapSelectAuthoring: ActivityTypeAuthoring<GapSelectData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'gap-select',\n id: newId(),\n title: '',\n passage: '',\n gaps: [],\n scoringStrategy: 'all-or-nothing',\n }),\n checkDraft: checkGapSelectDraft,\n};\n\nfunction checkGapSelectDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'gap-select');\n const passage = draft.passage;\n if (isUnwritten(passage)) {\n issues.push(issue('gs_passage_required', ['passage'], 'Write the passage.'));\n }\n issues.push(...checkScoringStrategy(draft));\n\n if (!isUnset(draft.presentation) && draft.presentation !== 'dropdown') {\n issues.push(\n issue(\n 'gs_presentation_invalid',\n ['presentation'],\n 'The only presentation is \"dropdown\". Leave it out unless you mean to set it.',\n ),\n );\n }\n\n const banks = isUnset(draft.banks) ? [] : draft.banks;\n const bankIds = new Set<string>();\n if (Array.isArray(banks)) {\n issues.push(...checkBanks(banks, bankIds));\n }\n\n const gaps = isUnset(draft.gaps) ? [] : draft.gaps;\n const gapIds: string[] = [];\n if (Array.isArray(gaps)) {\n if (gaps.length === 0) {\n issues.push(\n issue(\n 'gs_gaps_required',\n ['gaps'],\n 'Add at least one gap, and mark where it goes in the passage with {{id}}.',\n ),\n );\n }\n issues.push(...checkGaps(gaps, banks, bankIds, gapIds));\n // An unwritten passage is already reported; pairing every gap against it\n // would only restate that each one is missing.\n if (typeof passage === 'string' && passage.trim() !== '') {\n issues.push(...checkPairing(passage, gaps, gapIds));\n }\n }\n\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\n/** The shared word banks. Reported at `banks`, where the schema reports their uniqueness rule. */\nfunction checkBanks(banks: readonly unknown[], bankIds: Set<string>): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const repeated = new Set<string>();\n for (const [index, bank] of banks.entries()) {\n if (!isRecord(bank)) {\n continue;\n }\n if (isMissingId(bank.id)) {\n issues.push(\n issue('gs_bank_id_required', ['banks', index, 'id'], `Word bank ${index + 1} has no id.`),\n );\n } else if (typeof bank.id === 'string') {\n if (bankIds.has(bank.id)) {\n repeated.add(bank.id);\n }\n bankIds.add(bank.id);\n }\n const name = typeof bank.id === 'string' && bank.id !== '' ? `\"${bank.id}\"` : index + 1;\n issues.push(...checkChoiceList(bank.choices, ['banks', index, 'choices'], `word bank ${name}`));\n }\n for (const id of repeated) {\n issues.push(\n issue('gs_bank_id_duplicate', ['banks'], `More than one word bank has the id \"${id}\".`),\n );\n }\n return issues;\n}\n\n/**\n * Every gap. The rules the schema states over the whole array — a duplicate id,\n * a missing or doubled choice source, an unknown bank, an answer key naming a\n * choice the gap does not offer — are reported at `gaps`, because that is where\n * the schema reports them and an issue deeper inside would account for the\n * schema's failure while saying something narrower than it.\n */\nfunction checkGaps(\n gaps: readonly unknown[],\n banks: unknown,\n bankIds: ReadonlySet<string>,\n gapIds: string[],\n): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const atGaps: DraftIssue[] = [];\n const repeated = new Set<string>();\n const seen = new Set<string>();\n\n for (const [index, gap] of gaps.entries()) {\n if (!isRecord(gap)) {\n continue;\n }\n const ordinal = index + 1;\n const name = typeof gap.id === 'string' && gap.id !== '' ? `\"${gap.id}\"` : ordinal;\n\n if (isMissingId(gap.id)) {\n issues.push(issue('gs_gap_id_required', ['gaps', index, 'id'], `Gap ${ordinal} has no id.`));\n } else if (typeof gap.id === 'string') {\n if (seen.has(gap.id)) {\n repeated.add(gap.id);\n }\n seen.add(gap.id);\n gapIds.push(gap.id);\n }\n\n const hasOwn = !isUnset(gap.choices);\n const hasBank = !isUnwritten(gap.bankId);\n // A `bankId` left blank is the bank selector still on its placeholder. The\n // schema refuses an empty string AT the field, so this is reported there —\n // an issue at `gaps` would not account for it, and zod's own diagnostic\n // would stand beside ours and call an unfinished gap a broken one.\n const bankBlank = typeof gap.bankId === 'string' && gap.bankId.trim() === '';\n if (bankBlank) {\n issues.push(\n issue(\n 'gs_choice_source_required',\n ['gaps', index, 'bankId'],\n `Choose a word bank for gap ${name}, or remove the empty field.`,\n ),\n );\n } else if (hasOwn && hasBank) {\n atGaps.push(\n issue(\n 'gs_choice_source_conflict',\n ['gaps'],\n `Gap ${name} has both its own choices and a word bank. Use one or the other.`,\n ),\n );\n } else if (!hasOwn && !hasBank) {\n atGaps.push(\n issue(\n 'gs_choice_source_required',\n ['gaps'],\n `Give gap ${name} a list of choices, or point it at a word bank.`,\n ),\n );\n }\n\n if (hasBank && typeof gap.bankId === 'string' && !bankIds.has(gap.bankId)) {\n atGaps.push(\n issue(\n 'gs_bank_unknown',\n ['gaps'],\n `Gap ${name} uses the word bank \"${gap.bankId}\", which does not exist.`,\n ),\n );\n }\n\n // Checked whenever the list is PRESENT, even alongside a `bankId` the gap\n // should not also have: the schema validates the array either way, so\n // skipping it while the conflict is reported left the schema's own\n // complaint about a too-short list standing on its own.\n if (hasOwn) {\n issues.push(...checkChoiceList(gap.choices, ['gaps', index, 'choices'], `gap ${name}`));\n }\n\n issues.push(...checkAnswerKey(gap, index, name, banks, bankIds));\n }\n\n for (const id of repeated) {\n atGaps.push(issue('gs_gap_id_duplicate', ['gaps'], `More than one gap has the id \"${id}\".`));\n }\n return [...issues, ...atGaps];\n}\n\n/** A gap's answer key, against the choices it actually offers. */\nfunction checkAnswerKey(\n gap: Readonly<Record<string, unknown>>,\n index: number,\n name: string | number,\n banks: unknown,\n bankIds: ReadonlySet<string>,\n): DraftIssue[] {\n const key = gap.correctChoiceId;\n if (isUnwritten(key)) {\n // At the field, not at `gaps`: the schema requires `correctChoiceId` and\n // reports a missing one HERE, and an issue at `gaps` accounts only for\n // failures AT `gaps` — never for one deeper inside it. Reported at the\n // array, this left zod's raw `invalid_type` standing beside it and made an\n // answer key nobody had chosen yet read as a broken draft.\n return [\n issue(\n 'gs_correct_choice_required',\n ['gaps', index, 'correctChoiceId'],\n `Mark the correct choice for gap ${name}.`,\n ),\n ];\n }\n if (typeof key !== 'string') {\n // A value of another type is the schema's to refuse.\n return [];\n }\n const choices = resolveChoices(gap, banks, bankIds);\n if (choices === undefined) {\n // The source is missing, doubled or unknown — already reported, and there is\n // no list to check the key against.\n return [];\n }\n const offered = choices.some((choice) => isRecord(choice) && choice.id === key);\n return offered\n ? []\n : [\n issue(\n 'gs_correct_choice_unknown',\n ['gaps'],\n `Gap ${name} is marked correct on \"${key}\", which is not one of its choices.`,\n ),\n ];\n}\n\n/** The choices a gap resolves to, or `undefined` when its source is unusable. */\nfunction resolveChoices(\n gap: Readonly<Record<string, unknown>>,\n banks: unknown,\n bankIds: ReadonlySet<string>,\n): readonly unknown[] | undefined {\n const hasOwn = !isUnset(gap.choices);\n const hasBank = !isUnwritten(gap.bankId);\n if (hasOwn === hasBank) {\n return undefined;\n }\n if (hasOwn) {\n return Array.isArray(gap.choices) ? gap.choices : undefined;\n }\n if (typeof gap.bankId !== 'string' || !bankIds.has(gap.bankId) || !Array.isArray(banks)) {\n return undefined;\n }\n const bank = banks.find((entry) => isRecord(entry) && entry.id === gap.bankId);\n return isRecord(bank) && Array.isArray(bank.choices) ? bank.choices : undefined;\n}\n\n/** One list of choices — a gap's own or a bank's. Both carry the same rules. */\nfunction checkChoiceList(\n choices: unknown,\n path: readonly (string | number)[],\n owner: string,\n): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const list = isUnset(choices) ? [] : choices;\n if (!Array.isArray(list)) {\n return issues;\n }\n if (list.length < MIN_CHOICES) {\n issues.push(\n issue(\n 'gs_choices_too_few',\n path,\n `Give ${owner} at least ${MIN_CHOICES} choices to pick from.`,\n ),\n );\n }\n const seen = new Set<string>();\n const repeated = new Set<string>();\n for (const [index, choice] of list.entries()) {\n if (!isRecord(choice)) {\n continue;\n }\n if (isMissingId(choice.id)) {\n issues.push(\n issue('gs_choice_id_required', [...path, index, 'id'], `Choice ${index + 1} has no id.`),\n );\n } else if (typeof choice.id === 'string') {\n if (seen.has(choice.id)) {\n repeated.add(choice.id);\n }\n seen.add(choice.id);\n }\n if (isUnwritten(choice.text)) {\n issues.push(\n issue(\n 'gs_choice_text_required',\n [...path, index, 'text'],\n `Write the text of choice ${index + 1}.`,\n ),\n );\n }\n }\n for (const id of repeated) {\n issues.push(issue('gs_choice_id_duplicate', path, `More than one choice has the id \"${id}\".`));\n }\n return issues;\n}\n\n/**\n * The passage and the gaps, paired one to one. Reported at `passage`, which is\n * where the schema reports its pairing rule — the same split fill-in-the-blanks\n * uses: something still to write is `incomplete`, something writing more cannot\n * fix is `invalid`.\n */\nfunction checkPairing(\n passage: string,\n gaps: readonly unknown[],\n gapIds: readonly string[],\n): DraftIssue[] {\n const issues: DraftIssue[] = [];\n const placeholders = new Map<string, number>();\n for (const match of passage.matchAll(PLACEHOLDER_RE)) {\n const id = match[1] as string;\n placeholders.set(id, (placeholders.get(id) ?? 0) + 1);\n }\n const ids = new Set(gapIds);\n\n for (const [id, count] of placeholders) {\n if (count > 1) {\n issues.push(\n issue(\n 'gs_placeholder_duplicate',\n ['passage'],\n `{{${id}}} appears more than once in the passage. Each gap goes in one place.`,\n ),\n );\n }\n if (!ids.has(id)) {\n issues.push(\n issue(\n 'gs_gap_missing',\n ['passage'],\n `The passage has {{${id}}}, but there is no gap with that id.`,\n ),\n );\n }\n }\n for (const id of ids) {\n if (!placeholders.has(id)) {\n issues.push(\n issue(\n 'gs_placeholder_missing',\n ['passage'],\n `Gap \"${id}\" is not in the passage. Put {{${id}}} where it goes.`,\n ),\n );\n }\n }\n\n // The schema's pairing rule stated as the schema states it, for a break none\n // of the codes above names — a gap with no usable id beside correct ones.\n if (issues.length === 0 && !pairsOneToOne(placeholders, gaps)) {\n issues.push(\n issue(\n 'gs_gaps_mismatch',\n ['passage'],\n 'The gaps and the {{id}} placeholders in the passage do not pair up one to one.',\n ),\n );\n }\n return issues;\n}\n\nfunction pairsOneToOne(placeholders: ReadonlyMap<string, number>, gaps: readonly unknown[]) {\n const ids = gaps.filter(isRecord).map((gap) => gap.id);\n if (new Set(ids).size !== ids.length || placeholders.size !== ids.length) {\n return false;\n }\n return ids.every((id) => typeof id === 'string' && placeholders.get(id) === 1);\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport { MultipleChoiceOptionMediaSchema } from '../schemas/multiple-choice.js';\nimport type { MultipleChoiceData } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkMedia,\n checkScoringStrategy,\n checkSharedOptional,\n type DraftFields,\n isMissingId,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n} from './issues.js';\n\n/** Mirrors `MultipleChoiceDataSchema`: `options` is `.min(2).max(26)`. */\nconst MIN_OPTIONS = 2;\nconst MAX_OPTIONS = 26;\n\n/**\n * Draft support for `multiple-choice`.\n *\n * A new draft has two empty options and NO option marked correct. Pre-marking\n * one is the obvious convenience, and it is a trap: an author writes both\n * options, never touches the correctness control, and holds a `complete`\n * question whose answer key is whichever option happened to be marked by\n * default. Leaving it unmarked keeps the question `incomplete` until somebody\n * says which answer is right.\n *\n * `scoringStrategy` starts at `all-or-nothing`, the less generous strategy:\n * partial credit is something an author chooses to give.\n */\nexport const multipleChoiceAuthoring: ActivityTypeAuthoring<MultipleChoiceData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'multiple-choice',\n id: newId(),\n title: '',\n question: '',\n mode: 'single',\n scoringStrategy: 'all-or-nothing',\n options: [\n { id: newId(), text: '', isCorrect: false },\n { id: newId(), text: '', isCorrect: false },\n ],\n }),\n checkDraft: checkMultipleChoiceDraft,\n};\n\nfunction checkMultipleChoiceDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'multiple-choice');\n if (isUnwritten(draft.question)) {\n issues.push(issue('mc_question_required', ['question'], 'Write the question.'));\n }\n if (isUnwritten(draft.mode)) {\n issues.push(\n issue('mc_mode_required', ['mode'], 'Choose whether learners select one option or several.'),\n );\n }\n issues.push(...checkScoringStrategy(draft));\n\n const options = isUnset(draft.options) ? [] : draft.options;\n if (Array.isArray(options)) {\n // Every option-set rule is reported at `options`, where the schema reports\n // its refinements, so none of them is repeated as a second failure.\n if (options.length < MIN_OPTIONS) {\n issues.push(issue('mc_options_too_few', ['options'], 'Add at least two options.'));\n }\n if (options.length > MAX_OPTIONS) {\n issues.push(\n issue('mc_options_too_many', ['options'], `Use no more than ${MAX_OPTIONS} options.`),\n );\n }\n const seen = new Set<string>();\n const repeated = new Set<string>();\n let correct = 0;\n for (const [index, option] of options.entries()) {\n if (!isRecord(option)) {\n continue;\n }\n const ordinal = index + 1;\n if (isMissingId(option.id)) {\n issues.push(\n issue('mc_option_id_required', ['options', index, 'id'], `Option ${ordinal} has no id.`),\n );\n } else if (typeof option.id === 'string') {\n if (seen.has(option.id)) {\n repeated.add(option.id);\n }\n seen.add(option.id);\n }\n if (isUnwritten(option.text)) {\n issues.push(\n issue(\n 'mc_option_text_required',\n ['options', index, 'text'],\n `Write the text of option ${ordinal}.`,\n ),\n );\n }\n // The schema requires the flag on every option. An editor that has not\n // asked yet — or one that stores only the options marked correct — leaves\n // it unset, which is a decision still to make, not a wrong one.\n if (isUnset(option.isCorrect)) {\n issues.push(\n issue(\n 'mc_option_correctness_required',\n ['options', index, 'isCorrect'],\n `Say whether option ${ordinal} is correct.`,\n ),\n );\n }\n if (option.isCorrect === true) {\n correct += 1;\n }\n if (isRecord(option.media)) {\n issues.push(...checkOptionMedia(option.media, index, ordinal));\n }\n }\n for (const id of repeated) {\n issues.push(\n issue('mc_option_id_duplicate', ['options'], `More than one option has the id \"${id}\".`),\n );\n }\n if (correct === 0) {\n issues.push(\n issue(\n 'mc_correct_option_required',\n ['options'],\n draft.mode === 'multi'\n ? 'Mark at least one option as correct.'\n : 'Mark the correct option.',\n ),\n );\n } else if (draft.mode === 'single' && correct > 1) {\n issues.push(\n issue(\n 'mc_single_mode_one_correct',\n ['options'],\n 'Only one option can be marked correct when learners select one option.',\n ),\n );\n }\n }\n\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\n/**\n * An option's picture or recording, checked through the SAME rules as the\n * media above the question — one code per problem, wherever it appears, so a\n * consumer translating `media_alt_required` handles both.\n *\n * The one thing an option adds is the kind it refuses. `video` and `embed` are\n * valid above a question and impossible on an option: each renders a control\n * surface that swallows the click meant to select it, so an author who writes\n * one is told why rather than shown zod's enum message.\n */\nfunction checkOptionMedia(\n media: Readonly<Record<string, unknown>>,\n index: number,\n ordinal: number,\n): DraftIssue[] {\n const path = ['options', index, 'media'];\n const kind = media.type;\n if (kind === 'video' || kind === 'embed') {\n return [\n issue(\n 'mc_option_media_kind',\n [...path, 'type'],\n `Option ${ordinal} carries ${kind === 'embed' ? 'an embedded player' : 'a video'}, which cannot be an option: its controls swallow the click that selects the answer. Use a picture or a recording.`,\n ),\n ];\n }\n return checkMedia(media, path, MultipleChoiceOptionMediaSchema);\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport { READ_ALOUD_DIMENSIONS, ReadAloudSlowMediaSchema } from '../schemas/read-aloud.js';\nimport { containsUnspacedScript, dictationNormalizer } from '../scoring/dictation/normalize.js';\nimport {\n READ_ALOUD_MAX_DIMENSION_WEIGHT,\n READ_ALOUD_MAX_REFERENCE_LENGTH,\n READ_ALOUD_MAX_SECONDS,\n READ_ALOUD_MAX_TAKES,\n} from '../scoring/speech/limits.js';\nimport { CANONICAL_LOCALE_RE } from '../scoring/speech/locale.js';\nimport type { ReadAloudData } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkMedia,\n checkSharedOptional,\n type DraftFields,\n isRecord,\n issue,\n isUnset,\n isUnwritten,\n isWholeNumber,\n} from './issues.js';\n\n/**\n * Draft support for `read-aloud`.\n *\n * A new draft is an empty title, an empty text to read, no locale, no recording\n * limit and no weighted dimensions: `recording.maxSeconds: 0` reads as \"not set\n * yet\", the way `written-response` reads `maxWords: 0`, and the dimension list\n * starts empty because which of accuracy, fluency, completeness and prosody a\n * grade counts — and what each is worth — is a teaching decision the SDK must\n * not make. A defaulted set would let an author write the text, never open the\n * scoring control, and hold a `complete` item graded on weights nobody chose.\n *\n * The rules that make a draft `invalid` are the ones writing more cannot fix: a\n * text too long to be marked whole, a text nothing survives of once its\n * punctuation and spacing are ignored, a script whose words have no spaces\n * between them, a locale that is not canonical, bounds outside their range, a\n * dimension weighed twice or not at all, and the model-recording rules.\n */\nexport const readAloudAuthoring: ActivityTypeAuthoring<ReadAloudData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'read-aloud',\n id: newId(),\n title: '',\n referenceText: '',\n locale: '',\n recording: { maxSeconds: 0 },\n scoring: { dimensions: [] },\n }),\n checkDraft: checkReadAloudDraft,\n};\n\nfunction checkReadAloudDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'read-aloud');\n issues.push(...checkReferenceText(draft.referenceText));\n issues.push(...checkLocale(draft.locale));\n issues.push(...checkRecording(draft.recording));\n issues.push(...checkDimensions(draft.scoring));\n issues.push(...checkModelRecording(draft.media));\n issues.push(...checkSlowRecording(draft));\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\nfunction checkReferenceText(referenceText: unknown): DraftIssue[] {\n if (isUnwritten(referenceText)) {\n return [\n issue('ra_reference_text_required', ['referenceText'], 'Write the text to be read aloud.'),\n ];\n }\n if (typeof referenceText !== 'string') {\n // A value of another type is the schema's to refuse.\n return [];\n }\n const issues: DraftIssue[] = [];\n // One measurement for both rules; a text too long to normalise has no\n // normalised form to be empty, so only one of them can fire.\n const measured = dictationNormalizer(undefined).measure(referenceText);\n if (measured.tooLong) {\n issues.push(\n issue(\n 'ra_reference_text_too_long',\n ['referenceText'],\n `The text is longer than ${READ_ALOUD_MAX_REFERENCE_LENGTH} characters, before or after normalisation. A read-aloud item is a sentence or a short paragraph.`,\n ),\n );\n }\n if (measured.normalized === '') {\n issues.push(\n issue(\n 'ra_reference_text_unreadable',\n ['referenceText'],\n 'The text has no word to read: once punctuation and spacing are ignored, nothing is left. A reading of it would be marked against no word at all.',\n ),\n );\n }\n if (containsUnspacedScript(referenceText)) {\n issues.push(\n issue(\n 'ra_reference_text_unspaced_script',\n ['referenceText'],\n 'The text is written in a script that does not separate its words with spaces. Each word is marked on its own, and a run of Han, kana, Thai, Lao, Khmer or Myanmar letters would be marked as one word.',\n ),\n );\n }\n return issues;\n}\n\nfunction checkLocale(locale: unknown): DraftIssue[] {\n if (isUnwritten(locale)) {\n return [\n issue(\n 'ra_locale_required',\n ['locale'],\n 'Choose the language the learner reads in, with its region — \"en-US\", not \"en\".',\n ),\n ];\n }\n if (typeof locale !== 'string' || CANONICAL_LOCALE_RE.test(locale)) {\n return [];\n }\n return [\n issue(\n 'ra_locale_invalid',\n ['locale'],\n 'The language must be a BCP 47 tag in canonical form, with a region: \"en-US\", \"es-419\", \"zh-Hant-TW\". Pronunciation is assessed against one locale\\'s speech.',\n ),\n ];\n}\n\n/**\n * The limits on a take. Every bound is optional to the learner's experience and\n * none of them decides a grade, but the longest take has to be set before a\n * recorder can be shown at all.\n */\nfunction checkRecording(recording: unknown): DraftIssue[] {\n if (isUnset(recording)) {\n return [maxSecondsRequired()];\n }\n if (!isRecord(recording)) {\n return [];\n }\n const issues: DraftIssue[] = [];\n const max = recording.maxSeconds;\n if (isUnset(max) || max === 0) {\n issues.push(maxSecondsRequired());\n } else if (\n typeof max === 'number' &&\n !(Number.isFinite(max) && max > 0 && max <= READ_ALOUD_MAX_SECONDS)\n ) {\n issues.push(\n issue(\n 'ra_max_seconds_out_of_range',\n ['recording', 'maxSeconds'],\n `A take is at most ${READ_ALOUD_MAX_SECONDS} seconds long.`,\n ),\n );\n }\n const min = recording.minSeconds;\n if (typeof min === 'number' && !(Number.isFinite(min) && min >= 0)) {\n issues.push(minSecondsOutOfRange('The shortest take is a number of seconds, 0 or more.'));\n } else if (typeof min === 'number' && typeof max === 'number' && max > 0 && min >= max) {\n issues.push(minSecondsOutOfRange('The shortest take must be shorter than the longest.'));\n }\n const takes = recording.maxTakes;\n if (\n typeof takes === 'number' &&\n !(isWholeNumber(takes) && takes >= 1 && takes <= READ_ALOUD_MAX_TAKES)\n ) {\n issues.push(\n issue(\n 'ra_max_takes_out_of_range',\n ['recording', 'maxTakes'],\n `How many takes a learner may record is a whole number from 1 to ${READ_ALOUD_MAX_TAKES}.`,\n ),\n );\n }\n return issues;\n}\n\nfunction maxSecondsRequired(): DraftIssue {\n return issue(\n 'ra_max_seconds_required',\n ['recording', 'maxSeconds'],\n 'Set how long a take may be, in seconds.',\n );\n}\n\nfunction minSecondsOutOfRange(message: string): DraftIssue {\n return issue('ra_min_seconds_out_of_range', ['recording', 'minSeconds'], message);\n}\n\nconst DIMENSION_NAMES: readonly unknown[] = READ_ALOUD_DIMENSIONS;\n\n/** What the grade is made of. The schema's list rules, reported where it reports them. */\nfunction checkDimensions(scoring: unknown): DraftIssue[] {\n if (isUnset(scoring)) {\n return [dimensionsRequired()];\n }\n if (!isRecord(scoring)) {\n return [];\n }\n const dimensions = scoring.dimensions;\n if (isUnset(dimensions)) {\n return [dimensionsRequired()];\n }\n if (!Array.isArray(dimensions)) {\n return [];\n }\n if (dimensions.length === 0) {\n return [dimensionsRequired()];\n }\n\n const issues: DraftIssue[] = [];\n const seen = new Set<unknown>();\n // A weight nobody has given yet, or one the range refuses, leaves the total\n // undecided: the \"every weight is 0\" rule waits until every weight is usable,\n // as the written-response rubric's does.\n let everyWeightUsable = true;\n let totalWeight = 0;\n for (const [index, dimension] of dimensions.entries()) {\n if (!isRecord(dimension)) {\n everyWeightUsable = false;\n continue;\n }\n const path = ['scoring', 'dimensions', index];\n const name = dimension.name;\n if (!DIMENSION_NAMES.includes(name)) {\n issues.push(\n issue(\n 'ra_dimension_name_invalid',\n [...path, 'name'],\n `Choose what dimension ${index + 1} measures: ${READ_ALOUD_DIMENSIONS.join(', ')}.`,\n ),\n );\n } else if (seen.has(name)) {\n issues.push(\n issue(\n 'ra_dimension_duplicate',\n [...path, 'name'],\n `\"${String(name)}\" is already weighed. Remove this one, or measure something else.`,\n ),\n );\n } else {\n seen.add(name);\n }\n const weight = dimension.weight;\n if (\n typeof weight === 'number' &&\n Number.isFinite(weight) &&\n weight >= 0 &&\n weight <= READ_ALOUD_MAX_DIMENSION_WEIGHT\n ) {\n totalWeight += weight;\n } else {\n everyWeightUsable = false;\n issues.push(\n issue(\n 'ra_dimension_weight_invalid',\n [...path, 'weight'],\n `Give dimension ${index + 1} a weight: a number from 0 to ${READ_ALOUD_MAX_DIMENSION_WEIGHT}.`,\n ),\n );\n }\n }\n if (everyWeightUsable && totalWeight === 0) {\n issues.push(\n issue(\n 'ra_dimension_weights_zero',\n ['scoring', 'dimensions'],\n 'Give at least one dimension a weight above 0. With every weight at 0 there is no weighted total, and every take would be unscorable.',\n ),\n );\n }\n return issues;\n}\n\nfunction dimensionsRequired(): DraftIssue {\n return issue(\n 'ra_dimensions_required',\n ['scoring', 'dimensions'],\n 'Choose what the grade measures: at least one of accuracy, fluency, completeness or prosody, with a weight.',\n );\n}\n\n/** The model recording. Its address, description and playback are `checkSharedOptional`'s. */\nfunction checkModelRecording(media: unknown): DraftIssue[] {\n if (!isRecord(media)) {\n return [];\n }\n const kind = media.type;\n return isUnwritten(kind) || kind === 'audio'\n ? []\n : [\n issue(\n 'ra_media_kind',\n ['media', 'type'],\n 'A read-aloud model is read aloud: its recording must be audio.',\n ),\n ];\n}\n\n/**\n * The slow model recording: the media rules under `slowMedia`, plus what it may\n * not accompany. Its schema is strict, so a playback policy, a captions track or\n * any other key is refused there and reported under `media_invalid` — there is\n * no separate code, because the recording follows `media`'s policy and the text\n * being read is public, so captions of it withhold nothing.\n *\n * A written kind that is not `audio` is named here rather than left to that\n * refusal, and the rest of the rules then read the recording as the audio it\n * can only be: `checkMedia` decides from the kind whether a description is\n * required, and would otherwise demand of a slow recording the description a\n * picture needs, at a field that can hold nothing but a recording. Dictation\n * substitutes the kind before the same call, for the same reason.\n */\nfunction checkSlowRecording(draft: DraftFields): DraftIssue[] {\n const slow = draft.slowMedia;\n if (!isRecord(slow)) {\n return [];\n }\n const kind = slow.type;\n const wrongKind = !isUnwritten(kind) && kind !== 'audio';\n const issues: DraftIssue[] = wrongKind\n ? [issue('media_invalid', ['slowMedia', 'type'], 'The slow model recording must be audio.')]\n : [];\n issues.push(\n ...checkMedia(\n wrongKind ? { ...slow, type: 'audio' } : slow,\n ['slowMedia'],\n ReadAloudSlowMediaSchema,\n ),\n );\n\n const media = draft.media;\n if (isUnset(media)) {\n issues.push(\n issue(\n 'ra_slow_media_without_media',\n ['media'],\n 'A slow model recording accompanies a recording. Add the recording first.',\n ),\n );\n return issues;\n }\n if (!isRecord(media)) {\n return issues;\n }\n if (isRecord(media.playback) && media.playback.maxPlays !== undefined) {\n issues.push(\n issue(\n 'ra_slow_media_beside_play_limit',\n ['slowMedia'],\n 'A play budget on the recording cannot coexist with a slow recording that has none. Remove the slow recording, or remove the play limit.',\n ),\n );\n }\n if (typeof slow.url === 'string' && slow.url !== '' && slow.url === media.url) {\n issues.push(\n issue(\n 'ra_slow_media_same_recording',\n ['slowMedia', 'url'],\n 'The slow model recording is the same file as the recording. Point it at the slower reading.',\n ),\n );\n }\n return issues;\n}\n","import type { ActivityTypeAuthoring } from '../registry/registry.js';\nimport type { WrittenResponseData } from '../types/activity.js';\nimport type { DraftIssue } from '../types/authoring.js';\nimport {\n checkIdentity,\n checkSharedOptional,\n type DraftFields,\n isRecord,\n issue,\n isTooLarge,\n isUnset,\n isUnwritten,\n isWholeNumber,\n} from './issues.js';\n\n/**\n * Draft support for `written-response`.\n *\n * A new draft has no prompt, no rubric, `minWords: 0` and `maxWords: 0`. The\n * SDK picks no word limits and no rubric criteria: those are teaching decisions.\n *\n * - `minWords: 0` is a real setting — no lower limit, which `<WrittenResponse>`\n * renders as \"up to N words\" — so it is never reported.\n * - `maxWords: 0` is not. The schema requires at least 1, and 0 is what a\n * cleared number field produces, so it reads as \"not set yet\" (`incomplete`)\n * rather than as a wrong value.\n *\n * Stricter than the schema in four places:\n *\n * - A blank `prompt` is `incomplete` even when `promptHtml` is set. The plain\n * prompt is what `<WrittenResponse>` renders when no sanitiser is supplied, so\n * a rich-text-only prompt shows the learner nothing there.\n * - A rubric criterion whose name is only whitespace is `incomplete`.\n * - A rubric whose weights are all 0 is `incomplete`: `gradeFromRubric` cannot\n * compute a weighted total from them.\n * - A rubric whose weights add up to more than a number can hold is `invalid`,\n * for the same reason.\n */\nexport const writtenResponseAuthoring: ActivityTypeAuthoring<WrittenResponseData> = {\n createDraft: ({ newId }) => ({\n schemaVersion: '1.0',\n type: 'written-response',\n id: newId(),\n title: '',\n prompt: '',\n minWords: 0,\n maxWords: 0,\n }),\n checkDraft: checkWrittenResponseDraft,\n};\n\nfunction checkWrittenResponseDraft(draft: DraftFields): DraftIssue[] {\n const issues = checkIdentity(draft, 'written-response');\n if (isUnwritten(draft.prompt)) {\n issues.push(\n issue(\n 'wr_prompt_required',\n ['prompt'],\n isUnwritten(draft.promptHtml)\n ? 'Write the prompt.'\n : 'Write the prompt as plain text too. The rich-text prompt is shown only where a sanitiser is supplied; the plain text is shown everywhere else.',\n ),\n );\n }\n\n const min = draft.minWords;\n const max = draft.maxWords;\n if (isUnset(min)) {\n issues.push(\n issue(\n 'wr_min_words_required',\n ['minWords'],\n 'Set the minimum number of words. Use 0 for no minimum.',\n ),\n );\n } else if (!(isWholeNumber(min) && min >= 0)) {\n issues.push(\n issue(\n 'wr_min_words_invalid',\n ['minWords'],\n isTooLarge(min)\n ? 'The minimum word count is too large.'\n : 'The minimum word count must be a whole number, 0 or more.',\n ),\n );\n }\n if (isUnset(max) || max === 0) {\n issues.push(issue('wr_max_words_required', ['maxWords'], 'Set the maximum number of words.'));\n } else if (!(isWholeNumber(max) && max >= 1)) {\n issues.push(\n issue(\n 'wr_max_words_invalid',\n ['maxWords'],\n isTooLarge(max)\n ? 'The maximum word count is too large.'\n : 'The maximum word count must be a whole number, 1 or more.',\n ),\n );\n } else if (typeof min === 'number' && max < min) {\n // Reported at `maxWords`, where the schema reports the same rule.\n issues.push(\n issue(\n 'wr_word_bounds_order',\n ['maxWords'],\n 'The maximum word count cannot be lower than the minimum.',\n ),\n );\n }\n\n const rubric = draft.rubric;\n if (isRecord(rubric)) {\n const criteria = isUnset(rubric.criteria) ? [] : rubric.criteria;\n if (Array.isArray(criteria)) {\n issues.push(...checkCriteria(criteria));\n }\n }\n\n issues.push(...checkSharedOptional(draft));\n return issues;\n}\n\nfunction checkCriteria(criteria: readonly unknown[]): DraftIssue[] {\n const issues: DraftIssue[] = [];\n if (criteria.length === 0) {\n issues.push(\n issue(\n 'wr_rubric_criteria_required',\n ['rubric', 'criteria'],\n 'Add at least one rubric criterion, or remove the rubric.',\n ),\n );\n return issues;\n }\n let totalWeight = 0;\n let everyWeightUsable = true;\n for (const [index, criterion] of criteria.entries()) {\n if (!isRecord(criterion)) {\n everyWeightUsable = false;\n continue;\n }\n const ordinal = index + 1;\n const path = ['rubric', 'criteria', index];\n if (isUnwritten(criterion.name)) {\n issues.push(\n issue('wr_criterion_name_required', [...path, 'name'], `Name rubric criterion ${ordinal}.`),\n );\n }\n const weight = criterion.weight;\n if (isUnset(weight)) {\n everyWeightUsable = false;\n issues.push(\n issue(\n 'wr_criterion_weight_required',\n [...path, 'weight'],\n `Give rubric criterion ${ordinal} a weight.`,\n ),\n );\n } else if (!(typeof weight === 'number' && Number.isFinite(weight) && weight >= 0)) {\n everyWeightUsable = false;\n issues.push(\n issue(\n 'wr_criterion_weight_invalid',\n [...path, 'weight'],\n 'A rubric weight must be a number, 0 or more.',\n ),\n );\n } else {\n totalWeight += weight;\n }\n }\n if (everyWeightUsable && totalWeight === 0) {\n issues.push(\n issue(\n 'wr_rubric_weights_zero',\n ['rubric', 'criteria'],\n 'Give at least one rubric criterion a weight above 0. With every weight at 0, no weighted total can be computed.',\n ),\n );\n } else if (totalWeight === Number.POSITIVE_INFINITY) {\n // Each weight is finite, and their sum still overflows: `gradeFromRubric`\n // would divide by Infinity and produce no grade. No weight is negative, so\n // the sum stays overflowed whatever a weight not yet set turns out to be.\n issues.push(\n issue(\n 'wr_rubric_weights_too_large',\n ['rubric', 'criteria'],\n 'The rubric weights add up to more than can be calculated with. Use smaller weights.',\n ),\n );\n }\n return issues;\n}\n","import type { PartialScoringResult } from '../../registry/registry.js';\nimport type {\n DictationData,\n DictationLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { alignDictation } from '../dictation/align.js';\n\n/**\n * Scores a Dictation response: the character-level similarity of what the\n * learner typed to the transcript, over the whole sentence.\n *\n * Built on `alignDictation`, so the grade and a marked display read one\n * computation. One {@link ScoringDetail} per word of the chosen transcript, in\n * order — `w1`…`wN` — carrying the word the learner typed for it, an outcome\n * (`correct`, `incorrect`, or `incorrect-omission` when no typed word aligned)\n * and its own `score`, the pair's similarity. Extra typed words appear in the\n * alignment, never in `details`: ids must be derivable from the activity alone.\n *\n * Dictation is the first built-in whose details carry a continuous `score` of\n * their own, and its item score is not derived from them: the sentence is\n * graded as one string (`the cat sat` vs `the cat sit` → words 1, 1, 0.67;\n * score 0.909). A stored `details` array is the audit\n * record of the mark, and survives a future major that changes the algorithm.\n */\nexport function scoreDictation(\n data: DictationData,\n response: DictationLearnerResponse,\n): PartialScoringResult {\n const alignment = alignDictation(data, typeof response?.text === 'string' ? response.text : '');\n const details: ScoringDetail[] = [];\n for (const word of alignment.words) {\n if (word.status === 'extra') {\n continue;\n }\n details.push({\n itemId: word.itemId as string,\n correct: word.status === 'correct',\n outcome:\n word.status === 'correct'\n ? 'correct'\n : word.status === 'incorrect'\n ? 'incorrect'\n : 'incorrect-omission',\n learnerResponse: word.attempt,\n correctResponse: word.reference,\n score: word.similarity,\n weight: 1,\n });\n }\n return { score: alignment.similarity, maxScore: 1, feedback: null, details };\n}\n","/**\n * All-or-nothing scoring: full credit only when every item is correct.\n *\n * @param correctItems - per-item correctness flags for the relevant items\n * @returns `1` if every item is correct (or the list is empty), otherwise `0`\n */\nexport function allOrNothingStrategy(correctItems: boolean[]): number {\n return correctItems.every((isCorrect) => isCorrect) ? 1 : 0;\n}\n","/**\n * Partial-credit scoring for Multiple Choice — balanced/symmetric scheme.\n *\n * Reward and penalty are each normalised by their own pool: the fraction of\n * correct options found, minus the fraction of distractors wrongly chosen.\n * This is fairer than normalising the penalty by the correct-option count —\n * under that older form a single wrong pick could zero an otherwise-correct\n * response when there was only one correct answer.\n *\n * Result is always in [0, 1]: `reward ∈ [0,1]` and `penalty ∈ [0,1]`, so\n * `reward - penalty ∈ [-1,1]` and `max(0, …)` floors it at 0. Selecting every\n * option yields `1 - 1 = 0`; selecting exactly the correct set yields `1`.\n *\n * @param correctSelected - number of selected options that are correct\n * @param incorrectSelected - number of selected options that are incorrect\n * @param totalCorrect - total correct options; the `MultipleChoiceDataSchema`\n * \"≥1 correct\" guard guarantees this is ≥ 1, so the reward term cannot\n * divide by zero for schema-validated data\n * @param totalIncorrect - total incorrect options (distractors); when `0`\n * (every option is correct) the penalty term is defined as `0`\n */\nexport function partialStrategy(\n correctSelected: number,\n incorrectSelected: number,\n totalCorrect: number,\n totalIncorrect: number,\n): number {\n const reward = correctSelected / totalCorrect;\n const penalty = totalIncorrect === 0 ? 0 : incorrectSelected / totalIncorrect;\n return Math.max(0, reward - penalty);\n}\n\n/**\n * Partial-credit scoring for Fill-in-the-Blanks: fraction of blanks answered\n * correctly. Each blank is independently right or wrong, so a plain proportion\n * is already fair — no penalty term applies.\n *\n * @param correctBlanks - number of blanks answered correctly\n * @param totalBlanks - total number of blanks; the `FillInTheBlanksDataSchema`\n * `blanks.min(1)` constraint guarantees this is ≥ 1, so division by zero\n * cannot occur for schema-validated data\n */\nexport function partialBlankStrategy(correctBlanks: number, totalBlanks: number): number {\n return correctBlanks / totalBlanks;\n}\n","import type {\n BlankConfig,\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { allOrNothingStrategy } from '../strategies/all-or-nothing.js';\nimport { partialBlankStrategy } from '../strategies/partial.js';\nimport { matchText, type TextMatchPolicy } from '../text-match.js';\nimport type { PartialScoringResult } from './multiple-choice.js';\n\n/**\n * Resolves the effective match policy for a blank. The legacy\n * `caseSensitive` / `trimWhitespace` flags map onto the baseline policy\n * fields; an explicit `blank.match` policy takes precedence field-by-field.\n * With neither present, the result is the v1 semantics exactly.\n */\nfunction policyFor(blank: BlankConfig): TextMatchPolicy {\n return {\n ...(blank.caseSensitive !== undefined ? { caseSensitive: blank.caseSensitive } : {}),\n ...(blank.trimWhitespace !== undefined ? { trim: blank.trimWhitespace } : {}),\n ...blank.match,\n };\n}\n\n/**\n * Scores a Fill-in-the-Blanks response. Each blank is evaluated independently\n * via {@link matchText} under the blank's resolved policy; a missing answer\n * key is treated as empty input and scored incorrect.\n */\nexport function scoreFillInTheBlanks(\n data: FillInTheBlanksData,\n response: FillInTheBlanksLearnerResponse,\n): PartialScoringResult {\n const details: ScoringDetail[] = [];\n const perBlankCorrect: boolean[] = [];\n\n for (const blank of data.blanks) {\n const rawInput = response.answers[blank.id];\n const input = typeof rawInput === 'string' ? rawInput : '';\n const matched = matchText(input, blank.acceptedAnswers, policyFor(blank)).matched;\n\n perBlankCorrect.push(matched);\n details.push({\n itemId: blank.id,\n correct: matched,\n outcome: matched ? 'correct' : 'incorrect',\n learnerResponse: [input],\n correctResponse: [...blank.acceptedAnswers],\n weight: 1,\n });\n }\n\n const correctBlanks = perBlankCorrect.filter(Boolean).length;\n const scoreValue =\n data.scoringStrategy === 'all-or-nothing'\n ? allOrNothingStrategy(perBlankCorrect)\n : partialBlankStrategy(correctBlanks, data.blanks.length);\n\n return { score: scoreValue, maxScore: 1, feedback: null, details };\n}\n","import type {\n GapSelectChoice,\n GapSelectData,\n GapSelectGap,\n GapSelectLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { allOrNothingStrategy } from '../strategies/all-or-nothing.js';\nimport { partialBlankStrategy } from '../strategies/partial.js';\nimport type { PartialScoringResult } from './multiple-choice.js';\n\n/**\n * The choices a gap offers: its own list, or the bank it names.\n *\n * Returns an empty list when neither resolves. The schema rejects that, but the\n * scorer is reachable with data an older build stored, and an empty list scores\n * the gap incorrect rather than throwing — a paper that cannot be marked is\n * worse at an appeal than one gap nobody can pass.\n */\nfunction choicesFor(data: GapSelectData, gap: GapSelectGap): readonly GapSelectChoice[] {\n if (gap.choices !== undefined) {\n return gap.choices;\n }\n return data.banks?.find((bank) => bank.id === gap.bankId)?.choices ?? [];\n}\n\n/**\n * Scores a Gap Select response.\n *\n * Each gap is an identity comparison between the selected choice id and\n * `correctChoiceId` — no text matching of any kind, because a learner choosing\n * from a list cannot mistype, and tolerances that cannot apply must not be\n * configurable.\n *\n * A gap with no selection, or one whose selection is not a choice it offers\n * (a tampered payload, or content edited after the attempt), is scored\n * incorrect and reported as `incorrect-omission`, the outcome the SDK already\n * uses for \"the learner never answered this\". That keeps an unanswered gap\n * distinguishable from a wrong one everywhere downstream, which is the whole\n * reason the selector's first entry is blank.\n */\nexport function scoreGapSelect(\n data: GapSelectData,\n response: GapSelectLearnerResponse,\n): PartialScoringResult {\n const details: ScoringDetail[] = [];\n const perGapCorrect: boolean[] = [];\n\n for (const gap of data.gaps) {\n const raw = response.selections[gap.id];\n const selected = typeof raw === 'string' ? raw : '';\n const choices = choicesFor(data, gap);\n const offered = choices.some((choice) => choice.id === selected);\n const answered = selected !== '' && offered;\n const correct = answered && selected === gap.correctChoiceId;\n\n perGapCorrect.push(correct);\n details.push({\n itemId: gap.id,\n correct,\n outcome: correct ? 'correct' : answered ? 'incorrect' : 'incorrect-omission',\n learnerResponse: [selected],\n correctResponse: [gap.correctChoiceId],\n weight: 1,\n });\n }\n\n const correctGaps = perGapCorrect.filter(Boolean).length;\n const scoreValue =\n data.scoringStrategy === 'all-or-nothing'\n ? allOrNothingStrategy(perGapCorrect)\n : partialBlankStrategy(correctGaps, data.gaps.length);\n\n return { score: scoreValue, maxScore: 1, feedback: null, details };\n}\n","import type { PartialScoringResult } from '../../registry/registry.js';\nimport type {\n MultipleChoiceData,\n MultipleChoiceLearnerResponse,\n ScoringDetail,\n} from '../../types/activity.js';\nimport { partialStrategy } from '../strategies/partial.js';\n\nexport type { PartialScoringResult } from '../../registry/registry.js';\n\n/**\n * Scores a Multiple Choice response. Pure; trusts its typed inputs (validation\n * is the component boundary's job). Options are looked up by id; an unknown\n * selected id is treated as an incorrect selection.\n */\nexport function scoreMultipleChoice(\n data: MultipleChoiceData,\n response: MultipleChoiceLearnerResponse,\n): PartialScoringResult {\n const optionById = new Map(data.options.map((option) => [option.id, option]));\n const selected = new Set(response.selectedOptionIds);\n\n const totalCorrect = data.options.filter((option) => option.isCorrect).length;\n const totalIncorrect = data.options.length - totalCorrect;\n\n let scoreValue: number;\n\n if (data.scoringStrategy === 'all-or-nothing') {\n if (data.mode === 'single') {\n scoreValue =\n response.selectedOptionIds.length === 1 &&\n optionById.get(response.selectedOptionIds[0])?.isCorrect === true\n ? 1\n : 0;\n } else {\n const correctIds = data.options.filter((o) => o.isCorrect).map((o) => o.id);\n const allCorrectSelected = correctIds.every((id) => selected.has(id));\n scoreValue = selected.size === correctIds.length && allCorrectSelected ? 1 : 0;\n }\n } else {\n let correctSelected = 0;\n let incorrectSelected = 0;\n for (const id of selected) {\n const option = optionById.get(id);\n if (option?.isCorrect) {\n correctSelected += 1;\n } else {\n incorrectSelected += 1;\n }\n }\n scoreValue = partialStrategy(correctSelected, incorrectSelected, totalCorrect, totalIncorrect);\n }\n\n const details: ScoringDetail[] = data.options.map((option) => {\n const wasSelected = selected.has(option.id);\n const outcome = wasSelected\n ? option.isCorrect\n ? 'correct'\n : 'incorrect'\n : option.isCorrect\n ? 'incorrect-omission'\n : 'correct-omission';\n return {\n itemId: option.id,\n correct: wasSelected === option.isCorrect,\n outcome,\n learnerResponse: [wasSelected ? 'selected' : 'not-selected'],\n correctResponse: [option.isCorrect ? 'selected' : 'not-selected'],\n weight: 1,\n };\n });\n\n return { score: scoreValue, maxScore: 1, feedback: null, details };\n}\n","import type { z } from 'zod/v4';\nimport { dictationAuthoring } from '../authoring/dictation.js';\nimport { fillInTheBlanksAuthoring } from '../authoring/fill-in-the-blanks.js';\nimport { gapSelectAuthoring } from '../authoring/gap-select.js';\nimport { multipleChoiceAuthoring } from '../authoring/multiple-choice.js';\nimport { readAloudAuthoring } from '../authoring/read-aloud.js';\nimport { writtenResponseAuthoring } from '../authoring/written-response.js';\nimport { countWords } from '../count-words.js';\nimport { DictationDataSchema } from '../schemas/dictation.js';\nimport { FillInTheBlanksDataSchema } from '../schemas/fill-in-the-blanks.js';\nimport { GapSelectDataSchema } from '../schemas/gap-select.js';\nimport { MultipleChoiceDataSchema } from '../schemas/multiple-choice.js';\nimport { ReadAloudDataSchema } from '../schemas/read-aloud.js';\nimport {\n RedactedDictationDataSchema,\n RedactedFillInTheBlanksDataSchema,\n RedactedGapSelectDataSchema,\n RedactedMultipleChoiceDataSchema,\n RedactedReadAloudDataSchema,\n RedactedWrittenResponseDataSchema,\n} from '../schemas/redacted.js';\nimport { WrittenResponseDataSchema } from '../schemas/written-response.js';\nimport { scoreDictation } from '../scoring/activity-scorers/dictation.js';\nimport { scoreFillInTheBlanks } from '../scoring/activity-scorers/fill-in-the-blanks.js';\nimport { scoreGapSelect } from '../scoring/activity-scorers/gap-select.js';\nimport { scoreMultipleChoice } from '../scoring/activity-scorers/multiple-choice.js';\nimport {\n DICTATION_MAX_TEXT_LENGTH,\n normalizeDictationText,\n truncateCodePoints,\n} from '../scoring/dictation/normalize.js';\nimport type {\n DictationData,\n DictationLearnerResponse,\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse,\n GapSelectData,\n GapSelectLearnerResponse,\n MultipleChoiceData,\n MultipleChoiceLearnerResponse,\n ReadAloudData,\n ReadAloudLearnerResponse,\n WrittenResponseData,\n WrittenResponseLearnerResponse,\n} from '../types/activity.js';\nimport { defineActivityType, type FieldPolicy, registerActivityType } from './registry.js';\n\n/**\n * Field-sensitivity policies for the built-in types (R7). Fail-closed:\n * anything not listed here is dropped by `redact()`. `scoringStrategy` is\n * answer-key everywhere — MC `partial` penalises wrong selections while\n * `all-or-nothing` does not, so the strategy reveals whether guessing is\n * free. Authored feedback is answer-key (it may quote or hint the answer).\n * Rubrics are PUBLIC: a rubric tells the learner what they are assessed on,\n * and a deployment that wants it hidden tightens it per call — see the\n * `rubric` entry in the written-response policy below.\n */\n/**\n * `media` classified field by field, not as one opaque leaf.\n *\n * A scalar `Sensitivity` classifies the WHOLE field, so `media: 'public'`\n * returned the author's object by reference without recursing — and an\n * unclassified key nested under it (`media.secretAnswerHint`) survived\n * `redact()` AND passed `assertRedacted()`. The fail-closed contract the SDK\n * documents (\"a field the policy does not classify is removed\") stopped at the\n * media boundary. It no longer does.\n *\n * Everything here is public by necessity: the client is the thing that renders\n * and enforces the policy, and \"1 play remaining\" is text the learner has to\n * read. None of it is an answer key — a listening paper's answer key is\n * `stimulus.transcript`, which stays `author-only`.\n */\nexport const MEDIA_FIELD_POLICY: FieldPolicy = {\n type: 'public',\n url: 'public',\n alt: 'public',\n captionsUrl: 'public',\n playback: {\n controls: 'public',\n maxPlays: 'public',\n seek: 'public',\n rate: 'public',\n nativeControlHints: 'public',\n },\n};\n\nconst SHARED_PUBLIC_FIELDS: FieldPolicy = {\n schemaVersion: 'public',\n type: 'public',\n id: 'public',\n // Assembly metadata, not content: it names the slot this item occupies in a\n // paper. It has to survive redaction, or the exam client derives positional\n // slot ids while the server's stored plan holds keyed ones, and the\n // responses cannot be matched back to the attempt.\n slotKey: 'public',\n title: 'public',\n media: MEDIA_FIELD_POLICY,\n passThreshold: 'public',\n locale: 'public',\n learningObjectives: 'public',\n difficultyLevel: 'public',\n};\n\n/**\n * Activity-level feedback, classified field by field.\n *\n * The third object leaf to need this, after `media` and `rubric`, and the same\n * defect each time: a scalar classification assigns the author's object by\n * reference without recursing, so under `reveal: 'after-submit'` an\n * unclassified key nested inside it — a grader note, an internal cost — was\n * forwarded to the learner along with the answer key, and `redact()` handed\n * back an alias of the caller's object. `FeedbackSchema` is loose, so anything\n * can be parked there.\n *\n * Both fields stay `answer-key`: activity feedback IS the answer key's\n * commentary, so `reveal: 'none'` still drops the whole object.\n */\nexport const FEEDBACK_FIELD_POLICY: FieldPolicy = {\n correct: 'answer-key',\n incorrect: 'answer-key',\n};\n\n/**\n * A blank's matching tolerances, classified key by key.\n *\n * Same reasoning as {@link FEEDBACK_FIELD_POLICY}: `TextMatchPolicy` is a\n * documented set of knobs, and a consumer's private tuning parked beside them\n * is not part of what an after-submit reveal promises to show.\n */\nexport const TEXT_MATCH_FIELD_POLICY: FieldPolicy = {\n caseSensitive: 'answer-key',\n trim: 'answer-key',\n normalize: 'answer-key',\n foldDiacritics: 'answer-key',\n collapseInnerWhitespace: 'answer-key',\n ignorePunctuation: 'answer-key',\n levenshtein: 'answer-key',\n locale: 'answer-key',\n};\n\nconst MULTIPLE_CHOICE_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n question: 'public',\n questionHtml: 'public',\n mode: 'public',\n shuffle: 'public',\n scoringStrategy: 'answer-key',\n feedback: FEEDBACK_FIELD_POLICY,\n options: {\n id: 'public',\n text: 'public',\n isCorrect: 'answer-key',\n feedback: 'answer-key',\n // Public, and classified key by key rather than as one leaf — the lesson\n // `media`, `rubric` and `feedback` each taught: a scalar classification\n // assigns the author's object by reference without recursing, so an\n // unclassified key parked inside it survives redact() AND assertRedacted().\n // An option's picture or recording IS the thing the learner picks, so\n // stripping it would ship a row of blank options.\n media: {\n type: 'public',\n url: 'public',\n alt: 'public',\n captionsUrl: 'public',\n },\n },\n};\n\n/**\n * Gap Select, where the sensitivity of \"the candidate answers\" INVERTS.\n *\n * Fill-in-the-Blanks classifies `acceptedAnswers` as answer-key, because the\n * list of things that would be accepted IS the key. Here the same-shaped data\n * is the opposite: a learner who cannot see `choices` cannot answer at all, so\n * every choice — and every shared bank — is `public`, and `correctChoiceId` is\n * the single field withheld. Copying the Fill-in-the-Blanks policy across would\n * have produced an exam nobody could sit, which is why the two types do not\n * share one.\n */\nconst GAP_SELECT_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n passage: 'public',\n passageHtml: 'public',\n presentation: 'public',\n shuffleChoices: 'public',\n scoringStrategy: 'answer-key',\n feedback: FEEDBACK_FIELD_POLICY,\n banks: {\n id: 'public',\n choices: { id: 'public', text: 'public' },\n },\n gaps: {\n id: 'public',\n bankId: 'public',\n choices: { id: 'public', text: 'public' },\n correctChoiceId: 'answer-key',\n feedback: 'answer-key',\n },\n};\n\nconst FILL_IN_THE_BLANKS_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n passage: 'public',\n passageHtml: 'public',\n scoringStrategy: 'answer-key',\n feedback: FEEDBACK_FIELD_POLICY,\n blanks: {\n id: 'public',\n hint: 'public',\n acceptedAnswers: 'answer-key',\n caseSensitive: 'answer-key',\n trimWhitespace: 'answer-key',\n match: TEXT_MATCH_FIELD_POLICY,\n feedback: 'answer-key',\n },\n};\n\n/**\n * Dictation, where the answer key is the sentence itself.\n *\n * `transcript` and `acceptedTranscripts` are the key. So is `tolerance`, and it\n * is classified as ONE scalar leaf on purpose: `redact()` maps an array element\n * by element and omits only an empty OBJECT, so a nested policy over\n * `equivalences` would project two all-answer-key rules to `[{}, {}]` — not\n * empty, kept under `reveal: 'none'`, and refused by the strict redacted schema,\n * so every item carrying a rule set would fail to redact. The leaf projects to\n * nothing under `none` and to the whole object under `after-submit`.\n *\n * The slow recording is public, key by key, for the reason a gap-select choice\n * is: the learner must be able to play it in an exam. The schema refuses it\n * beside a play budget, so its being public cannot open one. The hint mode is\n * public because it is inert without the transcript.\n */\nconst DICTATION_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n transcript: 'answer-key',\n acceptedTranscripts: 'answer-key',\n slowMedia: { type: 'public', url: 'public', alt: 'public' },\n hints: { mode: 'public' },\n tolerance: 'answer-key',\n feedback: FEEDBACK_FIELD_POLICY,\n};\n\n/**\n * The rubric classified field by field, not as one opaque leaf.\n *\n * A scalar `Sensitivity` classifies the WHOLE field, so `rubric: 'public'`\n * returned the author's object by reference without recursing — and an\n * unclassified key nested under it survived `redact()` AND passed\n * `assertRedacted()`. A grader's `modelAnswer` or `aiModel` stashed on the\n * rubric went straight to the exam client. Same defect as the one `media` had,\n * one field along: \"the rubric is public\" has to mean its documented fields\n * are public, not that anything anyone parks under it is.\n */\nexport const RUBRIC_FIELD_POLICY: FieldPolicy = {\n label: 'public',\n // Applies to every element of the array.\n criteria: {\n name: 'public',\n description: 'public',\n weight: 'public',\n },\n};\n\nconst WRITTEN_RESPONSE_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n prompt: 'public',\n promptHtml: 'public',\n minWords: 'public',\n maxWords: 'public',\n languageTarget: 'public',\n feedback: FEEDBACK_FIELD_POLICY,\n // A rubric is a LEARNER affordance, not a grader secret: it tells the\n // learner what they are being graded on, which is pedagogically the point\n // of publishing one. (Classifying it author-only broke real deployments\n // that render a rubric panel during the attempt.) A deployment that wants\n // it hidden can tighten this per call via `redact(data, { policy })`.\n // Classified field by field so that stays true of the rubric's DOCUMENTED\n // fields only — see {@link RUBRIC_FIELD_POLICY}.\n rubric: RUBRIC_FIELD_POLICY,\n};\n\n/** Built-in Multiple Choice descriptor. */\nexport const multipleChoiceType = defineActivityType<\n MultipleChoiceData,\n MultipleChoiceLearnerResponse\n>({\n type: 'multiple-choice',\n // zod4 optional outputs are `T | undefined`; the hand-written wire types use\n // exact optionals. Structurally identical at runtime — cast is type-level only.\n schema: MultipleChoiceDataSchema as unknown as z.ZodType<MultipleChoiceData>,\n scoring: { kind: 'sync', score: scoreMultipleChoice },\n isAnswered: (response) => (response?.selectedOptionIds.length ?? 0) > 0,\n fieldPolicy: MULTIPLE_CHOICE_FIELD_POLICY,\n redactedSchema: RedactedMultipleChoiceDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'choice',\n correctResponsesPattern: (data) => [\n data.options\n .filter((option) => option.isCorrect)\n .map((option) => option.id)\n .join('[,]'),\n ],\n },\n interactions: ['option-selected', 'option-deselected', 'submitted'],\n authoring: multipleChoiceAuthoring,\n});\n\n/** Built-in Fill-in-the-Blanks descriptor. */\nexport const fillInTheBlanksType = defineActivityType<\n FillInTheBlanksData,\n FillInTheBlanksLearnerResponse\n>({\n type: 'fill-in-the-blanks',\n schema: FillInTheBlanksDataSchema as unknown as z.ZodType<FillInTheBlanksData>,\n scoring: { kind: 'sync', score: scoreFillInTheBlanks },\n isAnswered: (response) =>\n Object.values(response?.answers ?? {}).some((answer) => answer.trim().length > 0),\n fieldPolicy: FILL_IN_THE_BLANKS_FIELD_POLICY,\n redactedSchema: RedactedFillInTheBlanksDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'fill-in',\n // xAPI fill-in pattern: blank answers joined with \"[,]\". Only the first\n // accepted answer per blank is emitted (full alternates would explode\n // combinatorially); the complete key lives in the activity data.\n correctResponsesPattern: (data) => [\n data.blanks.map((blank) => blank.acceptedAnswers[0] ?? '').join('[,]'),\n ],\n },\n interactions: ['blank-filled', 'hint-requested', 'submitted'],\n authoring: fillInTheBlanksAuthoring,\n});\n\n/**\n * Built-in Written Response descriptor. Grading is DEFERRED: submissions are\n * graded asynchronously (AI or human) by the consumer; the synchronous\n * outcome reports only word-count facts. `wordCount` is recomputed from the\n * submitted text with the canonical `countWords()` — the client-supplied\n * count is informational, never trusted.\n */\nexport const writtenResponseType = defineActivityType<\n WrittenResponseData,\n WrittenResponseLearnerResponse\n>({\n type: 'written-response',\n schema: WrittenResponseDataSchema as unknown as z.ZodType<WrittenResponseData>,\n scoring: {\n kind: 'deferred',\n reason: 'requires_async_grading',\n partial: (data, response) => {\n const wordCount = countWords(response?.text ?? '');\n return {\n withinWordBounds:\n response !== undefined && wordCount >= data.minWords && wordCount <= data.maxWords,\n wordCount,\n };\n },\n },\n isAnswered: (response) => (response?.text.trim().length ?? 0) > 0,\n fieldPolicy: WRITTEN_RESPONSE_FIELD_POLICY,\n redactedSchema: RedactedWrittenResponseDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n xapiInteractionType: 'long-fill-in',\n correctResponsesPattern: () => [],\n },\n interactions: ['text-changed', 'submitted'],\n authoring: writtenResponseAuthoring,\n});\n\n/** Built-in Gap Select descriptor. */\nexport const gapSelectType = defineActivityType<GapSelectData, GapSelectLearnerResponse>({\n type: 'gap-select',\n schema: GapSelectDataSchema as unknown as z.ZodType<GapSelectData>,\n scoring: { kind: 'sync', score: scoreGapSelect },\n isAnswered: (response) =>\n Object.values(response?.selections ?? {}).some((choiceId) => choiceId !== ''),\n fieldPolicy: GAP_SELECT_FIELD_POLICY,\n redactedSchema: RedactedGapSelectDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n // `matching`, not `choice` or `fill-in`. The learner pairs a set of sources\n // (the gaps) with a set of targets (the choices), which is exactly what the\n // xAPI matching interaction describes, and it is the only built-in type\n // whose pattern can name WHICH gap took which answer. `fill-in` — what the\n // Fill-in-the-Blanks descriptor uses — would flatten the gaps into an\n // ordered list of strings and lose that.\n xapiInteractionType: 'matching',\n correctResponsesPattern: (data) => [\n data.gaps.map((gap) => `${gap.id}[.]${gap.correctChoiceId}`).join('[,]'),\n ],\n },\n interactions: ['gap-selected', 'submitted'],\n authoring: gapSelectAuthoring,\n});\n\n/**\n * Built-in Dictation descriptor. Graded synchronously: the score is the\n * character-level similarity of the typed text to the transcript, a pure\n * computation identical in a browser and on a server.\n */\nexport const dictationType = defineActivityType<DictationData, DictationLearnerResponse>({\n type: 'dictation',\n schema: DictationDataSchema as unknown as z.ZodType<DictationData>,\n scoring: { kind: 'sync', score: scoreDictation },\n // Whitespace-only or punctuation-only text is no answer. Without the\n // tolerance, which this signature cannot see: the one gap is an attempt made\n // ONLY of a symbol an equivalence names, which reads as unanswered here and\n // is scored as the rewritten word — documented in the authoring guide. The\n // reverse cannot happen: a rule inserts words, never nothing, and the text is\n // cut where the scorer cuts, so text past the cap never counts as an answer.\n isAnswered: (response) =>\n normalizeDictationText(\n truncateCodePoints(\n typeof response?.text === 'string' ? response.text : '',\n DICTATION_MAX_TEXT_LENGTH,\n ),\n ) !== '',\n fieldPolicy: DICTATION_FIELD_POLICY,\n redactedSchema: RedactedDictationDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n // `fill-in`, as for fill-in-the-blanks: a typed answer with a key pattern.\n // `long-fill-in` is what written-response uses for free text nothing scores.\n xapiInteractionType: 'fill-in',\n correctResponsesPattern: (data) => [data.transcript, ...(data.acceptedTranscripts ?? [])],\n },\n interactions: ['text-changed', 'hint-requested', 'submitted'],\n authoring: dictationAuthoring,\n});\n\n/**\n * Read Aloud, the type with no answer key at all.\n *\n * Everything an author writes is shown to the learner — the text to read most of\n * all — so every field is `public`, and redaction removes only the authored\n * pass/fail feedback, which is written about a grade that does not exist yet.\n * The bounds, the slow recording and the dimension weights are classified key by\n * key rather than as one leaf each, for the reason `media` was: a scalar\n * classification assigns the author's object by reference without recursing, so\n * an unclassified key parked inside it survives `redact()` AND\n * `assertRedacted()`.\n *\n * The weights are public deliberately. They tell the learner what the reading is\n * judged on, and they are no key: knowing that fluency counts twice cannot tell\n * anyone how to pronounce a word. They are not tightenable per call either, the\n * way a rubric is: `RedactedReadAloudDataSchema` REQUIRES `scoring` — as it\n * requires `recording`, `referenceText` and `locale` — so a policy override that\n * classified any of them `author-only` would produce a projection the schema\n * refuses, and `redact()` would throw rather than hide the field.\n */\nconst READ_ALOUD_FIELD_POLICY: FieldPolicy = {\n ...SHARED_PUBLIC_FIELDS,\n instructions: 'public',\n referenceText: 'public',\n recording: { maxSeconds: 'public', minSeconds: 'public', maxTakes: 'public' },\n slowMedia: { type: 'public', url: 'public', alt: 'public' },\n scoring: { dimensions: { name: 'public', weight: 'public' } },\n feedback: FEEDBACK_FIELD_POLICY,\n};\n\n/**\n * Whether a response carries a stored recording: an object whose `key` is a\n * non-empty string. Read defensively, because both callers are handed whatever\n * an application stored — `evaluate()` passes `partial` a response that may be\n * absent (a slot nobody answered), and a take that failed to upload is never a\n * `null` recording but may be anything at all in an older row.\n */\nfunction hasRecording(response: ReadAloudLearnerResponse | undefined): boolean {\n const recording = (response as { recording?: unknown } | undefined)?.recording;\n return (\n typeof recording === 'object' &&\n recording !== null &&\n typeof (recording as { key?: unknown }).key === 'string' &&\n (recording as { key: string }).key !== ''\n );\n}\n\n/**\n * Built-in Read Aloud descriptor. Grading is DEFERRED: the SDK calls no\n * assessor, so the synchronous outcome reports only whether a recording was\n * stored, and `gradeReadAloud` produces the grade once the application holds a\n * speech assessment.\n */\nexport const readAloudType = defineActivityType<ReadAloudData, ReadAloudLearnerResponse>({\n type: 'read-aloud',\n schema: ReadAloudDataSchema as unknown as z.ZodType<ReadAloudData>,\n scoring: {\n kind: 'deferred',\n reason: 'requires_async_grading',\n partial: (_data, response) => ({ hasRecording: hasRecording(response) }),\n },\n isAnswered: (response) => hasRecording(response),\n fieldPolicy: READ_ALOUD_FIELD_POLICY,\n redactedSchema: RedactedReadAloudDataSchema,\n interop: {\n xapiActivityTypeIri: 'http://adlnet.gov/expapi/activities/cmi.interaction',\n // `other`, not `performance`: xAPI's performance interaction describes a set\n // of steps with their own responses, and a reading has one. There is no\n // pattern to publish either — the \"correct response\" is a pronunciation, not\n // a string a statement can carry.\n xapiInteractionType: 'other',\n correctResponsesPattern: () => [],\n },\n interactions: ['recording-started', 'recording-stopped', 'recording-uploaded', 'submitted'],\n authoring: readAloudAuthoring,\n});\n\nregisterActivityType(multipleChoiceType);\nregisterActivityType(fillInTheBlanksType);\nregisterActivityType(writtenResponseType);\nregisterActivityType(gapSelectType);\nregisterActivityType(dictationType);\nregisterActivityType(readAloudType);\n"],"mappings":";AAGO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YACkB,cACA,QAChB;AACA,UAAM,mCAAmC,YAAY,GAAG;AAHxC;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EALkB;AAAA,EACA;AAKpB;AAGO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,YAA4B,cAAsB;AAChD,UAAM,kBAAkB,YAAY,qBAAqB;AAD/B;AAE1B,SAAK,OAAO;AAAA,EACd;AAAA,EAH4B;AAI9B;AAYO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAA4B,cAAsB;AAChD;AAAA,MACE,sBAAsB,YAAY;AAAA,IAEpC;AAJ0B;AAK1B,SAAK,OAAO;AAAA,EACd;AAAA,EAN4B;AAO9B;AASO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAA4B,cAAsB;AAChD;AAAA,MACE,kBAAkB,YAAY;AAAA,IAEhC;AAJ0B;AAK1B,SAAK,OAAO;AAAA,EACd;AAAA,EAN4B;AAO9B;;;AC1CA,IAAI,SAAsC;AAGnC,SAAS,oBAAuB,KAAiB;AACtD,MAAI,WAAW,MAAM;AACnB,WAAO,IAAI;AAAA,EACb;AACA,WAAS,oBAAI,IAAI;AACjB,MAAI;AACF,WAAO,IAAI;AAAA,EACb,UAAE;AACA,aAAS;AAAA,EACX;AACF;AAMO,SAAS,WAAc,KAAa,QAAgC;AACzE,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,OAAO,IAAI,GAAG;AAC1B,MAAI,UAAU,QAAW;AACvB,YAAQ,OAAO;AACf,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AACA,SAAO;AACT;;;ACvBO,IAAM,kCAAkC;AAMxC,IAAM,4BAA4B;AAElC,IAAM,mCAAmC;AAKzC,IAAM,6BAA6B;AAKnC,IAAM,qCAAqC;AAc3C,IAAM,iBAAiB,IAAI;AASlC,IAAM,iCAAiC;AAOvC,IAAM,KAAK,IAAI,WAA6B,OAAO,cAAc,GAAG,MAAM;AAE1E,IAAM,YAAY,GAAG,GAAI;AACzB,IAAM,aAAa,GAAG,GAAI;AAC1B,IAAM,gBAAgB,GAAG,IAAM;AAC/B,IAAM,mBAAmB,GAAG,IAAM;AAClC,IAAM,oBAAoB;AAC1B,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,4BAA4B;AAElC,IAAM,UAAU,GAAG,MAAM,KAAK,yBAAyB;AAEvD,IAAM,sBAAsB,GAAG,MAAQ,MAAQ,MAAQ,IAAM;AAE7D,IAAM,OAAO,GAAG,GAAG,MAAO,CAAC,IAAI,GAAG,MAAO,CAAC;AAE1C,IAAM,aAAa,GAAG,MAAQ,IAAM;AAgBpC,IAAM,mBAAmB,IAAI;AAAA,EAC3B,IAAI;AAAA,IACF,GAAG,GAAI;AAAA,IACP,GAAG,KAAM,KAAM,KAAM,KAAM,GAAI;AAAA,IAC/B,GAAG,GAAG,GAAI,CAAC,IAAI,GAAG,GAAI,CAAC;AAAA,IACvB,GAAG,GAAG,GAAK,CAAC,IAAI,GAAG,GAAK,CAAC;AAAA,IACzB,GAAG,GAAG,GAAK,CAAC,IAAI,GAAG,GAAK,CAAC;AAAA,IACzB,GAAG,GAAK;AAAA,IACR,GAAG,GAAG,GAAK,CAAC,IAAI,GAAG,GAAK,CAAC;AAAA,IACzB,GAAG,GAAG,GAAK,CAAC,IAAI,GAAG,GAAK,CAAC;AAAA,IACzB,GAAG,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,IAC3B,GAAG,GAAG,KAAM,CAAC,IAAI,GAAG,KAAM,CAAC;AAAA,EAC7B,EAAE,KAAK,EAAE,CAAC;AAAA,EACV;AACF;AAQA,IAAM,qBAAqB,IAAI;AAAA,EAC7B,MAAM,KAAK,EAAE,QAAQ,QAAS,QAAS,EAAE,GAAG,CAAC,GAAG,UAAU;AACxD,UAAM,OAAO,GAAG,QAAS,KAAK;AAC9B,WAAO,CAAC,KAAK,UAAU,MAAM,GAAG,IAAI;AAAA,EACtC,CAAC;AACH;AASA,IAAM,iBAAiB,oBAAI,IAAoB;AAAA,EAC7C,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,IAAM,CAAC;AAAA,EACpC,CAAC,GAAG,MAAQ,MAAQ,IAAM,GAAG,GAAG,MAAQ,MAAQ,IAAM,CAAC;AAAA,EACvD,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,MAAQ,IAAM,CAAC;AAAA,EAC5C,CAAC,GAAG,MAAQ,MAAQ,GAAG,GAAG,GAAG,MAAQ,IAAM,CAAC;AAC9C,CAAC;AACD,IAAM,oBAAoB,IAAI,OAAO,CAAC,GAAG,eAAe,KAAK,CAAC,EAAE,KAAK,GAAG,GAAG,IAAI;AAM/E,IAAM,wBAAwB,IAAI;AAAA,EAChC,GAAG,GAAG,IAAM,CAAC,KAAK,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC,MAAM,GAAG,IAAM,CAAC;AAAA,EAC1D;AACF;AACA,IAAM,uBAAuB,IAAI;AAAA,EAC/B,GAAG,GAAG,IAAM,CAAC,KAAK,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC,MAAM,GAAG,IAAM,CAAC;AAAA,EAC1D;AACF;AAEA,SAAS,cAAc,MAAsB;AAC3C,SAAO,KACJ,QAAQ,mBAAmB,CAAC,aAAa,eAAe,IAAI,QAAQ,CAAW,EAC/E,QAAQ,uBAAuB,KAAK,GAAG,IAAM,CAAC,EAAE,EAChD,QAAQ,sBAAsB,KAAK,GAAG,IAAM,CAAC,EAAE;AACpD;AAYA,IAAM,SAAS,IAAI;AAAA,EAEf;AAAA,IACE,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,KAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,KAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,IAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,GAAG;AAAA;AAAA,IACZ,CAAC,MAAQ,aAAa;AAAA;AAAA,IACtB,CAAC,OAAQ,GAAG,KAAM,CAAC;AAAA;AAAA,EACrB,EACA,IAAI,CAAC,CAAC,OAAO,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC;AAChD;AACA,IAAM,YAAY,IAAI,OAAO,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI;AAMrE,IAAM,0BAA0B,GAAG,GAAG,IAAM,CAAC,IAAI,GAAG,IAAM,CAAC,GAAG,GAAG,MAAQ,MAAQ,MAAQ,MAAQ,MAAQ,OAAS,KAAO,CAAC;AAa1H,IAAM,eAAe,IAAI;AAAA,EACvB,sBAAsB,SAAS,GAAG,OAAO,GAAG,mBAAmB,GAAG,IAAI,GAAG,uBAAuB,qDAAqD,UAAU;AAAA,EAC/J;AACF;AAEA,IAAM,qBAAqB,IAAI,OAAO,KAAK,OAAO,UAAU,IAAI;AAMhE,IAAM,oBAAoB;AAC1B,IAAM,wBAAwB,GAAG,KAAM;AACvC,IAAM,gBAAgB,IAAI,OAAO,IAAI,OAAO,GAAG,mBAAmB,GAAG,IAAI,KAAK,GAAG;AACjF,IAAM,gBAAgB,IAAI,OAAO,OAAO,SAAS,MAAM,IAAI;AAM3D,IAAM,iBACJ;AAMF,IAAM,qBAAqB,eAAe,cAAc;AAMxD,IAAM,sBAAsB,OAAO,UAAU,GAAG,aAAa;AAW7D,IAAM,iBAAiB,IAAI;AAAA,EACzB;AAAA,IACE,IAAI,mBAAmB,6BAA6B,mBAAmB;AAAA,IACvE,IAAI,mBAAmB;AAAA,IACvB,GAAG,UAAU,OAAO,kBAAkB,GAAG,UAAU;AAAA,IACnD,GAAG,UAAU,MAAM,kBAAkB;AAAA,IACrC,UAAU,gBAAgB,YAAY,mBAAmB;AAAA,EAC3D,EAAE,KAAK,GAAG;AAAA,EACV;AACF;AAEA,IAAM,qBAAqB,IAAI,OAAO,UAAU,gBAAgB,WAAW,IAAI;AAE/E,IAAM,qBAAqB,IAAI,OAAO,kBAAkB,GAAG,MAAQ,IAAM,CAAC,eAAe,IAAI;AAG7F,IAAM,sBAAsB,IAAI,OAAO,uBAAuB,cAAc,KAAK,IAAI;AAKrF,IAAM,2BAA2B,IAAI;AAAA,EACnC,OAAO,kBAAkB,2DAA2D,kBAAkB;AAAA,EACtG;AACF;AAOA,IAAM,UAAU,IAAI,OAAO,uBAAuB,OAAO,MAAM,GAAG;AAClE,IAAM,wBAAwB,IAAI,OAAO,IAAI,kBAAkB,KAAK,GAAG;AAOvE,IAAM,qBAAqB,IAAI,OAAO,aAAa,kBAAkB,IAAI,GAAG;AASrE,SAAS,uBAAuB,MAAuB;AAC5D,SAAO,mBAAmB,KAAK,IAAI;AACrC;AAEA,IAAM,eAAe,IAAI,OAAO,WAAW,OAAO,GAAG,IAAI,MAAM,GAAG;AAElE,IAAM,OAAO;AACb,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,QAAQ;AASd,IAAM,QAAoC,CAAC;AAE3C,SAAS,iBAAiB,WAA2B;AACnD,SACE,SACC,QAAQ,KAAK,SAAS,IAAI,OAAO,MACjC,sBAAsB,KAAK,SAAS,IAAI,WAAW,MACnD,aAAa,KAAK,SAAS,IAAI,YAAY;AAEhD;AAGA,SAAS,QAAQ,MAAc,OAAuB;AACpD,QAAM,QAAQ,KAAK,YAAY,KAAK;AACpC,MAAI,QAAQ,MAAM,SAAS,EAAE;AAC7B,MAAI,UAAU,QAAW;AACvB,YAAQ,IAAI,WAAW,KAAO;AAC9B,UAAM,SAAS,EAAE,IAAI;AAAA,EACvB;AACA,QAAM,SAAS,QAAQ;AACvB,MAAI,QAAQ,MAAM,MAAM;AACxB,MAAI,UAAU,GAAG;AACf,YAAQ,iBAAiB,OAAO,cAAc,KAAK,CAAC;AACpD,UAAM,MAAM,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAOA,IAAM,UAAU,oBAAI,IAAI;AAAA,EACtB;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAC3F;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACvF;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAC1F;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAC1F;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAC1F;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAC1E,CAAC;AAOD,IAAM,gBAAwD;AAAA,EAC5D,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,QAAS,MAAO;AACnB;AAEA,IAAM,iBAAyD;AAAA,EAC7D,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAO,IAAK;AAAA,EACb,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,OAAS,KAAO;AAAA,EACjB,CAAC,QAAS,MAAO;AACnB;AAMA,IAAM,iBAAiB,IAAI,OAAO,mBAAmB,GAAG,MAAO,CAAC,GAAG,IAAI,MAAM,GAAG;AAEhF,SAAS,mBAAmB,MAAsB;AAChD,SAAO,KAAK,QAAQ,eAAe,GAAG,EAAE,KAAK;AAC/C;AAGO,SAAS,gBAAgB,MAAsB;AACpD,MAAI,SAAS;AACb,aAAW,KAAK,MAAM;AACpB,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAOO,SAAS,mBAAmB,MAAc,KAAqB;AACpE,MAAI,KAAK,UAAU,KAAK;AACtB,WAAO;AAAA,EACT;AACA,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,aAAW,aAAa,MAAM;AAC5B,QAAI,WAAW,KAAK;AAClB,aAAO,KAAK,MAAM,GAAG,KAAK;AAAA,IAC5B;AACA,cAAU;AACV,aAAS,UAAU;AAAA,EACrB;AACA,SAAO;AACT;AAGO,SAAS,aAAa,MAAc,KAAsB;AAC/D,MAAI,KAAK,UAAU,KAAK;AACtB,WAAO;AAAA,EACT;AACA,SAAO,KAAK,SAAS,IAAI,OAAO,mBAAmB,MAAM,GAAG,EAAE,SAAS,KAAK;AAC9E;AAEA,SAAS,SAAS,OAAe,QAAyD;AACxF,SAAO,OAAO,KAAK,CAAC,CAAC,OAAO,IAAI,MAAM,SAAS,SAAS,SAAS,IAAI;AACvE;AAGA,SAAS,wBAAwB,OAAoC;AACnE,SAAO,UAAU,UAAa,SAAS,QAAU,SAAS;AAC5D;AAGA,SAAS,oBAAoB,OAAoC;AAC/D,SAAO,UAAU,WAAe,SAAS,QAAU,SAAS,QAAW,UAAU;AACnF;AAGA,SAAS,aAAa,QAA2B,OAAmC;AAClF,WAAS,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG;AACzC,UAAM,QAAQ,OAAO,EAAE;AACvB,QAAI,CAAC,eAAe,KAAK,KAAK,GAAG;AAC/B,aAAO,MAAM,YAAY,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAA2B,OAAmC;AACjF,WAAS,KAAK,QAAQ,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,UAAM,QAAQ,OAAO,EAAE;AACvB,QAAI,CAAC,eAAe,KAAK,KAAK,GAAG;AAC/B,aAAO,MAAM,YAAY,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,oBAAoB;AAwB1B,SAAS,eAAe,QAA2B,OAAwB;AACzE,QAAM,QAAS,OAAO,KAAK,EAAa,YAAY,CAAC;AACrD,QAAM,WAAW,OAAO,QAAQ,CAAC;AACjC,QAAM,cAAc,aAAa,UAAa,QAAQ,IAAI,SAAS,YAAY,CAAC,CAAW;AAC3F,MAAI,UAAU,KAAK;AACjB,UAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,WACE,eACC,aAAa,UACZ,kBAAkB,KAAK,QAAQ,KAC/B,SAAS,UACT,QAAQ,IAAI,KAAK,YAAY,CAAC,CAAW;AAAA,EAE/C;AACA,MAAI,UAAU,MAAM;AAClB,QAAI,aAAa;AACf,aAAO;AAAA,IACT;AACA,UAAM,SAAS,aAAa,QAAQ,KAAK;AACzC,UAAM,QAAQ,YAAY,QAAQ,KAAK;AACvC,WACE,WAAW,UACX,UAAU,UACV,SAAS,QAAQ,aAAa,KAC9B,SAAS,OAAO,cAAc;AAAA,EAElC;AACA,QAAM,gBAAgB,UAAU,YAAY,CAAC;AAC7C,MAAI,UAAU,2BAA2B;AACvC,YACG,wBAAwB,aAAa,KAAK,oBAAoB,aAAa,MAC5E,wBAAwB,OAAO,QAAQ,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,EAE7D;AACA,SAAO,wBAAwB,aAAa;AAC9C;AAUA,SAAS,OAAO,MAAsB;AACpC,MAAI,UAAU;AACd,aAAS;AACP,QAAI,OAAO,cAAc,OAAO;AAChC,QAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,YAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,oBAAoB,IAAI,CAAC;AAEhE,UAAI,SAAS;AACb,aAAO,OACJ,OAAO,CAAC,WAAW,UAAU;AAC5B,cAAM,QAAQ,UAAU,YAAY,CAAC;AACrC,YAAI,SAAS,UAAW,SAAS,QAAS;AACxC,iBAAO;AAAA,QACT;AACA,iBAAS,UAAU;AACnB,eAAO,CAAC,cAAc,KAAK,SAAS,KAAK,eAAe,QAAQ,KAAK;AAAA,MACvE,CAAC,EACA,KAAK,EAAE;AAAA,IACZ;AACA,WAAO,mBAAmB,IAAI,EAAE,UAAU,KAAK;AAC/C,QAAI,SAAS,SAAS;AACpB,aAAO;AAAA,IACT;AACA,cAAU;AAAA,EACZ;AACF;AAGA,IAAM,mBAAmB,IAAI,OAAO,MAAM,GAAG,IAAM,CAAC,gBAAgB,GAAG;AACvE,IAAM,uBAAuB,IAAI,OAAO,OAAO,SAAS,KAAK,GAAG;AAChE,IAAM,2BAA2B;AAejC,SAAS,kBAAkB,WAAmB,QAAgB,MAAsB;AAClF,QAAM,SAAS,UAAU,UAAU,MAAM;AACzC,QAAM,UAAU,iBAAiB,KAAK,MAAM;AAC5C,MAAI,YAAY,MAAM;AACpB,WAAO,mBAAmB,IAAI,MAAM,KAAK;AAAA,EAC3C;AACA,SAAO,yBAAyB,KAAK,SAAS,KAC5C,WAAW,KACX,qBAAqB,KAAK,KAAK,OAAO,SAAS,CAAC,CAAC,IAC/C,YACC,QAAQ,CAAC;AAChB;AAUO,SAAS,kBAAkB,MAAsB;AACtD,SAAO;AAAA,IACL;AAAA,MACE,KACG,QAAQ,mBAAmB,qBAAqB,EAChD,UAAU,KAAK,EACf,QAAQ,kBAAkB,iBAAiB,EAC3C,QAAQ,cAAc,EAAE,EACxB,QAAQ,WAAW,CAAC,cAAc,OAAO,IAAI,SAAS,CAAW,EACjE,YAAY;AAAA,IACjB,EAAE,UAAU,KAAK;AAAA,EACnB;AACF;AAGA,SAAS,cAAc,MAAc,SAAiB,aAA6B;AACjF,MAAI,UAAU;AACd,aAAS;AACP,UAAM,OAAO,OAAO,QAAQ,QAAQ,SAAS,WAAW,CAAC;AACzD,QAAI,SAAS,SAAS;AACpB,aAAO;AAAA,IACT;AACA,cAAU;AAAA,EACZ;AACF;AAGA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO,cAAc,MAAM,gBAAgB,EAAE;AAC/C;AAQA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO,cAAc,KAAK,QAAQ,oBAAoB,EAAE,GAAG,oBAAoB,GAAG;AACpF;AAEA,SAAS,gBAAgB,MAAuB;AAC9C,SAAO,QAAQ,SAAU,QAAQ;AACnC;AAEA,SAAS,eAAe,MAAuB;AAC7C,SAAO,QAAQ,SAAU,QAAQ;AACnC;AAGA,SAAS,uBAAuB,MAAc,OAAuB;AACnE,SAAO,SAAS,KACd,eAAe,KAAK,WAAW,QAAQ,CAAC,CAAC,KACzC,gBAAgB,KAAK,WAAW,QAAQ,CAAC,CAAC,IACxC,QAAQ,IACR,QAAQ;AACd;AAOA,SAAS,mBAAmB,UAAkB,UAA0B;AACtE,SAAO,aAAa,KAChB,WACA,SAAS,MAAM,uBAAuB,UAAU,SAAS,MAAM,CAAC;AACtE;AAOA,SAAS,gBAAgB,MAAuB;AAC9C,SAAQ,QAAQ,MAAQ,QAAQ,MAAU,QAAQ,MAAQ,QAAQ;AACpE;AAEA,SAAS,SAAS,MAAc,OAAwB;AACtD,QAAM,OAAO,KAAK,WAAW,KAAK;AAClC,SAAO,OAAO,MAAO,gBAAgB,IAAI,KAAK,QAAQ,MAAM,KAAK,IAAI,UAAU;AACjF;AAGA,SAAS,eAAe,MAAc,OAAwB;AAC5D,QAAM,OAAO,KAAK,WAAW,KAAK;AAClC,SAAO,OAAO,MAAO,gBAAgB,IAAI,KAAK,QAAQ,MAAM,KAAK,KAAK,OAAO,eAAe;AAC9F;AAEA,SAAS,cAAc,MAAc,OAAwB;AAC3D,SAAO,KAAK,WAAW,KAAK,KAAK,QAAS,QAAQ,MAAM,KAAK,IAAI,eAAe;AAClF;AAyBA,SAAS,OAAO,MAAc,OAAe,UAAoC;AAC/E,MAAI,CAAC,SAAS,MAAM,KAAK,GAAG;AAC1B,WAAO;AAAA,EACT;AACA,SAAO,eAAe,MAAM,KAAK,IAAI,WAAW;AAClD;AAEA,SAAS,SAAS,MAAc,UAAsC;AACpE,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,OAAO,MAAM,GAAG,QAAQ;AAAA,IAChC,OAAO,OAAO,MAAM,uBAAuB,MAAM,KAAK,MAAM,GAAG,QAAQ;AAAA,EACzE;AACF;AAEA,SAAS,OAAO,MAAY,MAAc,WAA4B;AACpE,MAAI,SAAS,QAAQ;AACnB,WAAO;AAAA,EACT;AACA,SAAO,SAAS,WAAW,eAAe,MAAM,SAAS,IAAI,SAAS,MAAM,SAAS;AACvF;AASA,SAAS,UAAU,MAAc,QAAgB,IAAqB;AACpE,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,MAAI,MAAM,KAAK,WAAW,cAAc,MAAM,GAAG,KAAK,OAAO,OAAO,OAAO,MAAM,GAAG,IAAI;AACtF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,KAAK,KAAK,OAAO,OAAO,QAAQ,MAAM,uBAAuB,MAAM,EAAE,CAAC;AACjF;AASA,SAAS,cAAc,MAAc,QAAgB,OAAuB;AAC1E,QAAM,SAAS,OAAO;AACtB,MAAI,KAAK,KAAK,QAAQ,QAAQ,KAAK;AACnC,SAAO,OAAO,IAAI;AAChB,QACE,OAAO,WAAW,YAClB,KAAK,KACL,eAAe,MAAM,uBAAuB,MAAM,EAAE,CAAC,GACrD;AACA,UAAI,OAAO;AACX,aAAO,OAAO,KAAK,UAAU,eAAe,MAAM,IAAI,GAAG;AACvD,gBACE,gBAAgB,KAAK,WAAW,IAAI,CAAC,KAAK,eAAe,KAAK,WAAW,OAAO,CAAC,CAAC,IAC9E,IACA;AAAA,MACR;AACA,WAAK,KAAK,QAAQ,QAAQ,IAAI;AAC9B;AAAA,IACF;AACA,QAAI,UAAU,MAAM,QAAQ,EAAE,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,SAAK,KAAK,QAAQ,QAAQ,KAAK,CAAC;AAAA,EAClC;AACA,SAAO;AACT;AAQA,SAAS,eAAe,MAAc,QAAyB;AAC7D,QAAM,SAAS,OAAO;AACtB,QAAM,SAAS,OAAO;AACtB,MAAI,WAAW,KAAK,SAAS,KAAK,QAAQ;AACxC,WAAO;AAAA,EACT;AACA,QAAM,WAAW,IAAI,WAAW,MAAM;AACtC,WAAS,QAAQ,GAAG,UAAU,GAAG,QAAQ,QAAQ,SAAS,GAAG;AAC3D,WAAO,UAAU,KAAK,OAAO,WAAW,KAAK,MAAM,OAAO,WAAW,OAAO,GAAG;AAC7E,gBAAU,SAAS,UAAU,CAAC;AAAA,IAChC;AACA,QAAI,OAAO,WAAW,KAAK,MAAM,OAAO,WAAW,OAAO,GAAG;AAC3D,iBAAW;AAAA,IACb;AACA,aAAS,KAAK,IAAI;AAAA,EACpB;AACA,WAAS,QAAQ,GAAG,UAAU,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;AAChE,WAAO,UAAU,KAAK,KAAK,WAAW,KAAK,MAAM,OAAO,WAAW,OAAO,GAAG;AAC3E,gBAAU,SAAS,UAAU,CAAC;AAAA,IAChC;AACA,QAAI,KAAK,WAAW,KAAK,MAAM,OAAO,WAAW,OAAO,GAAG;AACzD,iBAAW;AAAA,IACb;AACA,QAAI,YAAY,QAAQ;AACtB,UAAI,UAAU,MAAM,QAAQ,QAAQ,SAAS,CAAC,GAAG;AAC/C,eAAO;AAAA,MACT;AACA,gBAAU,SAAS,UAAU,CAAC;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AA+BA,SAAS,aAAa,WAAyC;AAC7D,QAAM,eAAe,WAAW;AAChC,MAAI,CAAC,MAAM,QAAQ,YAAY,GAAG;AAChC,WAAO,EAAE,OAAO,CAAC,GAAG,WAAW,KAAK;AAAA,EACtC;AACA,QAAM,QAAgB,CAAC;AACvB,aAAW,QAAQ,aAAa,MAAM,GAAG,0BAA0B,GAAG;AACpE,UAAM,UAAmB,MAAM;AAC/B,UAAM,QAAiB,MAAM;AAC7B,QACE,OAAO,YAAY,YACnB,OAAO,UAAU,YACjB,aAAa,SAAS,gCAAgC,KACtD,aAAa,OAAO,gCAAgC,GACpD;AACA;AAAA,IACF;AACA,UAAM,OAAO,kBAAkB,OAAO;AACtC,UAAM,KAAK,iBAAiB,kBAAkB,KAAK,CAAC;AACpD,QAAI,SAAS,MAAM,OAAO,IAAI;AAC5B;AAAA,IACF;AACA,UAAM,KAAK;AAAA,MACT,MAAM,SAAS,MAAM,MAAM;AAAA,MAC3B;AAAA,MACA,aAAa,eAAe,IAAI,CAAC;AAAA,MACjC,YAAY,eAAe,IAAI,uBAAuB,IAAI,GAAG,MAAM,CAAC;AAAA,IACtE,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,KAAK,UAAU,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC;AAAA,EAC1E;AACF;AASA,SAAS,QAAQ,MAA8B;AAC7C,SAAO,aAAa,MAAM,cAAc,IACpC,EAAE,MAAM,mBAAmB,MAAM,cAAc,GAAG,KAAK,KAAK,IAC5D,EAAE,MAAM,KAAK,MAAM;AACzB;AAcA,SAAS,QAAQ,MAAc,MAA4B;AACzD,MAAI,SAAS;AACb,MAAI,WAAW;AAIf,MAAI,OAAO;AACX,WACM,KAAK,cAAc,MAAM,KAAK,MAAM,CAAC,GACzC,OAAO,IACP,KAAK,cAAc,MAAM,KAAK,MAAM,QAAQ,GAC5C;AACA,UAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AAGvC,QAAI,KAAK,cAAc,WAAW,KAAK,YAAY,MAAM,eAAe,SAAS,CAAC,GAAG;AACnF,gBAAU;AAAA,IACZ;AACA,cAAU;AACV,WAAO,mBAAmB,MAAM,OAAO;AACvC,QAAI,KAAK,eAAe,SAAS,MAAM,eAAe,MAAM,CAAC,GAAG;AAC9D,gBAAU;AACV,aAAO;AAAA,IACT;AACA,cAAU,KAAK;AACf,WAAO,mBAAmB,MAAM,KAAK,EAAE;AACvC,eAAW,KAAK,KAAK,KAAK,KAAK;AAE/B,QAAI,OAAO,SAAS,IAAI,gBAAgB;AACtC,aAAO,EAAE,MAAM,mBAAmB,QAAQ,cAAc,GAAG,KAAK,KAAK;AAAA,IACvE;AAAA,EACF;AACA,QAAM,OAAO,KAAK,MAAM,QAAQ;AAChC,QAAM,QAAQ,KAAK,cAAc,WAAW,KAAK,SAAS,MAAM,eAAe,MAAM,CAAC;AACtF,SAAO,QAAQ,QAAQ,GAAG,MAAM,IAAI,IAAI,KAAK,SAAS,IAAI;AAC5D;AAEA,IAAM,gBAAgB,uBAAO,yBAAyB;AACtD,IAAM,uBAAuB,uBAAO,gCAAgC;AACpE,IAAM,cAAc,uBAAO,uBAAuB;AAClD,IAAM,UAAU,uBAAO,0BAA0B;AAGjD,SAAS,UAAa,KAAa,WAAmC;AACpE,QAAM,cAAc,WAAW,KAAK,MAAM,oBAAI,IAA4B,CAAC;AAC3E,MAAI,gBAAgB,QAAW;AAC7B,WAAO,oBAAI,IAAI;AAAA,EACjB;AACA,MAAI,MAAM,YAAY,IAAI,SAAS;AACnC,MAAI,QAAQ,QAAW;AACrB,UAAM,oBAAI,IAAI;AACd,gBAAY,IAAI,WAAW,GAAG;AAAA,EAChC;AACA,SAAO;AACT;AACA,IAAM,kBAAkB,uBAAO,2BAA2B;AAG1D,SAAS,gBAAgB,MAA8B;AACrD,QAAM,OAAO,WAAW,iBAAiB,MAAM,oBAAI,IAA4B,CAAC;AAChF,MAAI,SAAS,MAAM,IAAI,IAAI;AAC3B,MAAI,WAAW,QAAW;AACxB,aAAS,QAAQ,kBAAkB,IAAI,CAAC;AACxC,UAAM,IAAI,MAAM,MAAM;AAAA,EACxB;AACA,SAAO;AACT;AAQO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,gBAAgB,IAAI,EAAE,SAAS;AACxC;AAOA,SAAS,YACP,MACA,UACA,cAA8B,gBAAgB,IAAI,GAClC;AAChB,MAAI,SAAS,MAAM,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,OAAO,WAAW,eAAe,MAAM,oBAAI,IAAyC,CAAC;AAC3F,MAAI,QAAQ,MAAM,IAAI,SAAS,SAAS;AACxC,QAAM,aAAa,OAAO,IAAI,IAAI;AAClC,MAAI,eAAe,QAAW;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,UAAU;AACd,MAAI,MAAM,QAAQ;AAClB,aAAW,QAAQ,SAAS,OAAO;AACjC,cAAU,QAAQ,QAAQ,MAAM,IAAI;AACpC,YAAQ,QAAQ;AAAA,EAClB;AACA,QAAM,SAAS,EAAE,MAAM,QAAQ,MAAM,IAAI;AACzC,MAAI,SAAS,QAAW;AACtB,QAAI,UAAU,QAAW;AACvB,cAAQ,oBAAI,IAAI;AAChB,WAAK,IAAI,SAAS,WAAW,KAAK;AAAA,IACpC;AACA,UAAM,IAAI,MAAM,MAAM;AAAA,EACxB;AACA,SAAO;AACT;AAMO,SAAS,8BACd,MACA,WACgB;AAChB,QAAM,UAAU,YAAY,MAAM,aAAa,SAAS,CAAC;AACzD,SAAO,EAAE,MAAM,iBAAiB,QAAQ,IAAI,GAAG,KAAK,QAAQ,IAAI;AAClE;AA2CO,SAAS,uBAAuB,MAAc,WAAmC;AACtF,SAAO,8BAA8B,MAAM,SAAS,EAAE;AACxD;AAuDA,IAAM,oBAAmC,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,QAAQ,QAAQ,OAAO,OAAO;AAGjG,SAAS,eAAe,MAAsB;AAC5C,SAAO,iBAAiB,KAAK,QAAQ,oBAAoB,EAAE,CAAC;AAC9D;AAQA,SAAS,UAAU,SAAiB,SAAgC;AAClE,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,kBAAkB,eAAe,OAAO;AAC9C,QAAM,SAAS,oBAAoB,KAAK,kBAAkB;AAC1D,MAAI,WAAW,IAAI;AACjB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,MAAM,mBAAmB,GAAG,UAAU,MAAM;AACjE,QAAM,EAAE,QAAQ,MAAM,IAAI,SAAS,QAAQ,OAAO,SAAS,SAAS;AACpE,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,iBAAiB,OAAO;AAAA,IACxB;AAAA,IACA,iBAAiB,OAAO;AAAA,EAC1B,EAAE,IAAI,CAAC,SAAU,OAAO,KAAK,QAAQ,0BAA0B,EAAE,IAAI,IAAK;AAC1E,SAAO,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,EAAE,OAAO,CAAC,SAAS,SAAS,EAAE,GAAG,MAAM,QAAQ,MAAM;AACzF;AAeO,SAAS,oBAAoB,WAA+C;AACjF,QAAM,WAAW,aAAa,SAAS;AAIvC,QAAM,WAAW,UAA8B,sBAAsB,SAAS,SAAS;AACvF,QAAM,aAAa,UAAqB,aAAa,SAAS,SAAS;AACvE,SAAO;AAAA,IACL,QAAQ,MAAM;AACZ,UAAI,SAAS,SAAS,IAAI,IAAI;AAC9B,UAAI,WAAW,QAAW;AACxB,YAAI,aAAa,MAAM,+BAA+B,GAAG;AACvD,mBAAS,EAAE,SAAS,MAAM,YAAY,MAAM,QAAQ,kBAAkB;AAAA,QACxE,OAAO;AACL,gBAAM,UAAU,gBAAgB,IAAI;AACpC,gBAAM,UAAU,YAAY,MAAM,UAAU,OAAO;AACnD,gBAAM,aAAa,iBAAiB,QAAQ,IAAI;AAChD,mBAAS;AAAA,YACP,SAAS,QAAQ,OAAO,aAAa,YAAY,+BAA+B;AAAA,YAChF;AAAA,YACA,QAAQ,UAAU,QAAQ,MAAM,QAAQ,IAAI;AAAA,UAC9C;AAAA,QACF;AACA,iBAAS,IAAI,MAAM,MAAM;AAAA,MAC3B;AACA,aAAO;AAAA,IACT;AAAA,IACA,MAAM,MAAM;AACV,UAAI,SAAS,WAAW,IAAI,IAAI;AAChC,UAAI,WAAW,QAAW;AAIxB,cAAM,YAAY,aAAa,MAAM,cAAc,IAAI,OAAO,gBAAgB,IAAI;AAClF,cAAM,UAAU,cAAc,OAAO,OAAO,YAAY,MAAM,UAAU,SAAS;AACjF,YAAI,cAAc,QAAQ,YAAY,QAAQ,QAAQ,KAAK;AACzD,mBAAS,EAAE,SAAS,MAAM,OAAO,CAAC,EAAE;AAAA,QACtC,OAAO;AACL,gBAAM,QAAQ,UAAU;AACxB,mBAAS;AAAA,YACP,SAAS;AAAA,YACT,OAAO;AAAA,cACL,GAAG,oBAAI,IAAI;AAAA,gBACT,eAAe,QAAQ,IAAI;AAAA,gBAC3B,iBAAiB,QAAQ,IAAI;AAAA,gBAC7B,eAAe,KAAK;AAAA,gBACpB,iBAAiB,KAAK;AAAA,cACxB,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AACA,mBAAW,IAAI,MAAM,MAAM;AAAA,MAC7B;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAeO,SAAS,iBAAiB,OAA0B,WAAwC;AACjG,QAAM,OAAO,WAAW,SAAS,MAAM,oBAAI,QAA0C,CAAC;AACtF,QAAM,QAAQ,MAAM,IAAI,KAAK,GAAG,IAAI,SAAS;AAC7C,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,QAAM,SAAS,aAAa,OAAO,SAAS;AAC5C,MAAI,SAAS,QAAW;AACtB,UAAM,cAAc,KAAK,IAAI,KAAK,KAAK,oBAAI,QAAyB;AACpE,gBAAY,IAAI,WAAW,MAAM;AACjC,SAAK,IAAI,OAAO,WAAW;AAAA,EAC7B;AACA,SAAO;AACT;AAGA,IAAM,kBAAkB,oBAAI,QAA8C;AAE1E,SAAS,aAAa,OAA0B,WAAwC;AACtF,QAAM,EAAE,OAAO,IAAI;AACnB,MAAI,UAAU,SAAS;AACrB,WAAO;AAAA,EACT;AACA,MAAI,SAAS;AACb,MAAI,OAAO,MAAM;AACf,aACE,gBAAgB,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,QAAQ,0BAA0B,EAAE,CAAC;AAC9F,oBAAgB,IAAI,OAAO,MAAM;AAAA,EACnC;AACA,SAAO,OAAO,MAAM,KAAK,CAAC,SAAS;AACjC,UAAM,SAAiB,EAAE,MAAM,QAAQ,OAAO,QAAQ,OAAO,OAAO,MAAM;AAC1E,WAAO,OAAO,KAAK,CAAC,UAAU,eAAe,OAAO,MAAM,CAAC;AAAA,EAC7D,CAAC;AACH;;;ACx5CA,SAAS,SAAS;AAOX,IAAM,iBAAiB,EAAE,YAAY;AAAA,EAC1C,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACxC,CAAC;;;ACVD,SAAS,KAAAA,UAAS;AA0BX,IAAM,iBAAiBA,GAAE,MAAM;AAAA,EACpCA,GAAE,IAAI,EAAE,OAAO,CAAC,UAAU,wBAAwB,KAAK,KAAK,GAAG;AAAA,IAC7D,OAAO;AAAA,EACT,CAAC;AAAA,EACDA,GAAE,OAAO,EAAE,MAAM,iBAAiB;AAAA,IAChC,OAAO;AAAA,EACT,CAAC;AACH,CAAC;AAcM,IAAM,0BAA0BA,GAAE,KAAK,CAAC,iBAAiB,WAAW,CAAC;AAerE,IAAM,sBAAsBA,GAAE,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhD,UAAUA,GAAE,KAAK,CAAC,UAAU,SAAS,CAAC,EAAE,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjD,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA;AAAA,EAEnD,MAAMA,GAAE,KAAK,CAAC,SAAS,MAAM,CAAC,EAAE,SAAS;AAAA;AAAA,EAEzC,MAAMA,GAAE,KAAK,CAAC,SAAS,OAAO,CAAC,EAAE,SAAS;AAAA;AAAA,EAE1C,oBAAoBA,GAAE,MAAM,uBAAuB,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAC9E,CAAC;AAEM,IAAM,cAAcA,GACxB,YAAY;AAAA,EACX,MAAMA,GAAE,KAAK,CAAC,SAAS,SAAS,SAAS,OAAO,CAAC;AAAA,EACjD,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChC,aAAa,eAAe,SAAS;AAAA,EACrC,UAAU,oBAAoB,SAAS;AACzC,CAAC,EACA;AAAA,EACC,CAAC,MACE,EAAE,SAAS,WAAW,EAAE,SAAS,WAAa,OAAO,EAAE,QAAQ,YAAY,EAAE,IAAI,SAAS;AAAA,EAC7F;AAAA,IACE,OAAO;AAAA,IACP,MAAM,CAAC,KAAK;AAAA,EACd;AACF,EACC,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,gBAAgB,KAAK,EAAE,GAAG,GAAG;AAAA,EAChE,OACE;AAAA,EACF,MAAM,CAAC,KAAK;AACd,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,aAAa,UAAa,EAAE,SAAS,SAAS;AAAA,EAC7D,OACE;AAAA,EACF,MAAM,CAAC,UAAU;AACnB,CAAC,EACA;AAAA,EACC,CAAC,MACC,EAAE,UAAU,aAAa,YACxB,EAAE,SAAS,aAAa,UACvB,EAAE,SAAS,SAAS,UACpB,EAAE,SAAS,SAAS;AAAA,EACxB;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,YAAY,UAAU;AAAA,EAC/B;AACF,EACC,OAAO,CAAC,MAAM,EAAE,UAAU,aAAa,UAAa,EAAE,SAAS,SAAS,SAAS;AAAA,EAChF,OACE;AAAA,EACF,MAAM,CAAC,YAAY,MAAM;AAC3B,CAAC,EACA;AAAA,EACC,CAAC,MACC,EAAE,UAAU,aAAa,aACzB,EAAE,SAAS,aAAa,UACxB,EAAE,SAAS,SAAS,UACpB,EAAE,SAAS,SAAS;AAAA,EACtB;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,YAAY,UAAU;AAAA,EAC/B;AACF,EACC;AAAA,EACC,CAAC,MACC,EAAE,UAAU,uBAAuB,UAClC,EAAE,SAAS,aAAa,aACvB,EAAE,SAAS,aAAa,UACxB,EAAE,SAAS,SAAS,UACpB,EAAE,SAAS,SAAS;AAAA,EACxB;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,YAAY,oBAAoB;AAAA,EACzC;AACF;AAWK,IAAM,sBAAsBA,GAChC,aAAa;AAAA,EACZ,MAAMA,GAAE,KAAK,CAAC,SAAS,SAAS,SAAS,OAAO,CAAC;AAAA,EACjD,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChC,aAAa,eAAe,SAAS;AAAA,EACrC,UAAU,oBAAoB,SAAS;AACzC,CAAC,EACA;AAAA,EACC,CAAC,MACE,EAAE,SAAS,WAAW,EAAE,SAAS,WAAa,OAAO,EAAE,QAAQ,YAAY,EAAE,IAAI,SAAS;AAAA,EAC7F;AAAA,IACE,OAAO;AAAA,IACP,MAAM,CAAC,KAAK;AAAA,EACd;AACF,EACC,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,gBAAgB,KAAK,EAAE,GAAG,GAAG;AAAA,EAChE,OACE;AAAA,EACF,MAAM,CAAC,KAAK;AACd,CAAC;;;ACxLH,SAAS,KAAAC,UAAS;AAwBX,IAAM,2BAA2BC,GAAE,YAAY;AAAA,EACpD,MAAMA,GAAE,QAAQ,OAAO;AAAA,EACvB,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAClC,CAAC;AAOD,IAAM,iBAAiBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,iCAAiC,CAAC;AAG7F,IAAM,uBAAuBA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,gCAAgC,CAAC;AAO3F,IAAM,6BAA6BA,GACvC,YAAY;AAAA,EACX,MAAM;AAAA,EACN,IAAI;AACN,CAAC,EACA,OAAO,CAAC,SAAS,gBAAgB,KAAK,IAAI,KAAK,kCAAkC;AAAA,EAChF,OAAO,2BAA2B,gCAAgC;AAAA,EAClE,MAAM,CAAC,MAAM;AACf,CAAC,EACA,OAAO,CAAC,SAAS,gBAAgB,KAAK,EAAE,KAAK,kCAAkC;AAAA,EAC9E,OAAO,wBAAwB,gCAAgC;AAAA,EAC/D,MAAM,CAAC,IAAI;AACb,CAAC;AAGI,IAAM,2BAA2BA,GAAE,YAAY;AAAA,EACpD,cAAcA,GACX,MAAM,0BAA0B,EAChC,IAAI,4BAA4B;AAAA,IAC/B,OAAO,+BAA+B,0BAA0B;AAAA,EAClE,CAAC,EACA,SAAS;AACd,CAAC;AAcD,IAAM,cAAc,oBAAI,QAAqC;AAE7D,SAAS,cAAc,MAA6C;AAClE,MAAI,aAAa,YAAY,IAAI,IAAI;AACrC,MAAI,eAAe,QAAW;AAC5B,iBAAa,oBAAoB,KAAK,SAAS;AAC/C,gBAAY,IAAI,MAAM,UAAU;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,MAAwB,MAAkC;AACzE,SAAO,cAAc,IAAI,EAAE,QAAQ,IAAI;AACzC;AAGA,SAAS,aAAa,MAAkC;AACtD,SAAO,CAAC,KAAK,YAAY,GAAI,KAAK,uBAAuB,CAAC,CAAE;AAC9D;AAGA,SAAS,QAAQ,UAAuC;AACtD,SAAO,SAAS,eAAe;AACjC;AASA,SAAS,qBACP,OACqB;AACrB,QAAM,QAA6B,IAAIA,GAAE,KAAK,UAAU,EAAE,OAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AAC7F,QAAM,KAAK,QAAQ;AACnB,SAAO;AACT;AA0CA,IAAM,qBAAqBA,GAAE,YAAY;AAAA,EACvC,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,WAAW;AAAA,EAC3B,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,YAAY;AAAA,EACZ,qBAAqBA,GAClB,MAAM,oBAAoB,EAC1B,IAAI,kCAAkC,EACtC,SAAS;AAAA,EACZ,OAAO,YAAY,SAAS;AAAA,EAC5B,WAAW,yBAAyB,SAAS;AAAA,EAC7C,OAAOA,GAAE,YAAY,EAAE,MAAMA,GAAE,QAAQ,mBAAmB,EAAE,CAAC,EAAE,SAAS;AAAA,EACxE,WAAW,yBAAyB,SAAS;AAAA,EAC7C,UAAU,eAAe,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC;AAEM,IAAM,sBAAsB,mBAAmB;AAAA,EACpD,qBAA0D,CAAC,QAAQ;AAEjE,QAAI,IAAI,OAAO,KAAK,CAACC,YAAWA,OAAM,MAAM,UAAU,OAAO,CAAC,GAAG;AAC/D;AAAA,IACF;AACA,UAAM,OAAO,IAAI;AAMjB,UAAM,UAAU,IAAI,IAAI,IAAI,OAAO,IAAI,CAACA,WAAU,OAAOA,OAAM,OAAO,CAAC,CAAC,CAAC,CAAC;AAC1E,UAAM,SAAS,CAAC,MAAoC,OAAgB,YAAoB;AACtF,UAAI,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,SAAS,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;AAAA,IACrE;AACA,UAAM,aAAa,CAAC,QAAQ,IAAI,WAAW;AAC3C,UAAM,mBAAmB,CAAC,QAAQ,IAAI,YAAY;AAElD,QAAI,cAAc,kBAAkB;AAClC,YAAM,aAAa,QAAQ,MAAM,KAAK,UAAU;AAChD,UAAI,QAAQ,UAAU,GAAG;AACvB;AAAA,UACE,CAAC,YAAY;AAAA,UACb,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AACA,UAAI,WAAW,SAAS;AACtB;AAAA,UACE,CAAC,YAAY;AAAA,UACb,KAAK;AAAA,UACL,2BAA2B,+BAA+B,yGAAyG,cAAc;AAAA,QACnL;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,IAAI,qBAAqB,GAAG;AACvC,cAAM,WAAW,KAAK,uBAAuB,CAAC;AAC9C,YACE,SAAS,KAAK,CAAC,UAAU;AACvB,gBAAM,WAAW,QAAQ,MAAM,KAAK;AACpC,iBAAO,SAAS,WAAW,QAAQ,QAAQ;AAAA,QAC7C,CAAC,GACD;AACA;AAAA,YACE,CAAC,qBAAqB;AAAA,YACtB,KAAK;AAAA,YACL,wEAAwE,+BAA+B,yGAAyG,cAAc;AAAA,UAChO;AAAA,QACF;AAEA,cAAM,aAAa,aAAa,IAAI,EACjC,IAAI,CAAC,UAAU,QAAQ,MAAM,KAAK,EAAE,UAAU,EAC9C,OAAO,CAAC,UAA2B,UAAU,IAAI;AACpD,YAAI,IAAI,IAAI,UAAU,EAAE,SAAS,WAAW,QAAQ;AAClD;AAAA,YACE,CAAC,qBAAqB;AAAA,YACtB,KAAK;AAAA,YACL;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,QAAQ,IAAI,OAAO;AACxC,UAAM,aAAa,CAAC,QAAQ,IAAI,WAAW;AAC3C,QAAI,eAAe,KAAK,UAAU,UAAa,KAAK,MAAM,SAAS,SAAS;AAC1E;AAAA,QACE,CAAC,SAAS,MAAM;AAAA,QAChB,KAAK,MAAM;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,cAAc,KAAK,WAAW,aAAa,QAAW;AACxD;AAAA,QACE,CAAC,aAAa,UAAU;AAAA,QACxB,KAAK,UAAU;AAAA,QACf;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAe,cAAc,KAAK,cAAc,QAAW;AAC7D,UAAI,KAAK,OAAO,UAAU,aAAa,QAAW;AAChD;AAAA,UACE,CAAC,WAAW;AAAA,UACZ,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAGA,UACE,OAAO,KAAK,UAAU,QAAQ,YAC9B,OAAO,KAAK,OAAO,QAAQ,YAC3B,KAAK,UAAU,QAAQ,KAAK,MAAM,KAClC;AACA;AAAA,UACE,CAAC,aAAa,KAAK;AAAA,UACnB,KAAK,UAAU;AAAA,UACf;AAAA,QACF;AAAA,MACF;AACA,UAAI,KAAK,UAAU,QAAW;AAC5B;AAAA,UACE,CAAC,OAAO;AAAA,UACR,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,QACE,cACA,EAAE,KAAK,WAAW,gBAAgB,CAAC,GAAG;AAAA,MACpC,CAAC,SAAS,kBAAkB,KAAK,IAAI,MAAM,MAAM,uBAAuB,KAAK,EAAE,MAAM;AAAA,IACvF,GACA;AACA;AAAA,QACE,CAAC,aAAa,cAAc;AAAA,QAC5B,KAAK,WAAW;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAEA,eAAW,SAAS,CAAC,SAAS,WAAW,GAAY;AACnD,YAAM,YAAY,KAAK,KAAK;AAC5B,UAAI,WAAW,gBAAgB,QAAW;AACxC;AAAA,UACE,CAAC,OAAO,aAAa;AAAA,UACrB,UAAU;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,cAAc,CAAC,kBAAkB;AACpC;AAAA,IACF;AACA,UAAM,aAAa,cAAc,IAAI;AACrC,UAAM,aAAa,aAAa,IAAI,EAAE,IAAI,CAAC,UAAU,WAAW,QAAQ,KAAK,CAAC;AAC9E,UAAM,QAA8D;AAAA,MAClE,CAAC,CAAC,OAAO,GAAG,QAAQ,IAAI,OAAO,IAAI,SAAY,KAAK,KAAK;AAAA,MACzD,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,OAAO,GAAG;AAAA,MAClC,CAAC,CAAC,aAAa,KAAK,GAAG,KAAK,WAAW,GAAG;AAAA,IAC5C;AACA,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO;AACjC,UAAI,UAAU,QAAW;AACvB;AAAA,MACF;AACA,YAAM,EAAE,SAAS,MAAM,IAAI,WAAW,MAAM,KAAK;AACjD,UAAI,SAAS;AACX;AAAA,UACE;AAAA,UACA;AAAA,UACA,mHAAmH,cAAc;AAAA,QACnI;AAAA,MACF,WAAW,WAAW,KAAK,CAAC,cAAc,iBAAiB,OAAO,SAAS,CAAC,GAAG;AAC7E;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAUO,SAAS,6BACd,OACA,cAAiD,CAAC,gBAAgB,gBAAgB,QACzE;AACT,QAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AACrE,MAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,QAAQ,KAAK,CAAC,SAAS,MAAM,SAAS,KAAK,GAAG;AACpF,WAAO;AAAA,EACT;AACA,MAAI,CAAC,YAAY,MAAM,SAAS,MAAM,WAAW,KAAK,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG;AACjF,WAAO;AAAA,EACT;AACA,SAAO,MAAM,MAAM;AAAA,IACjB,CAAC,SAAS,SAAS,IAAI,KAAK,KAAK,SAAS,eAAe,KAAK,UAAU;AAAA,EAC1E;AACF;AAGO,IAAM,kCACX;;;ACrXF,SAAS,KAAAC,UAAS;AASX,IAAM,iBAAiB;AAMvB,IAAM,wBAAwBC,GAAE,YAAY;AAAA,EACjD,eAAeA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACpC,MAAMA,GAAE,QAAQ,EAAE,SAAS;AAAA,EAC3B,WAAWA,GAAE,KAAK,CAAC,QAAQ,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EACpD,gBAAgBA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,yBAAyBA,GAAE,QAAQ,EAAE,SAAS;AAAA,EAC9C,mBAAmBA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACxC,aAAaA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC9C,QAAQA,GAAE,OAAO,EAAE,SAAS;AAC9B,CAAC;AAQM,IAAM,oBAAoBA,GAAE,YAAY;AAAA,EAC7C,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,iBAAiBA,GACd;AAAA,IACCA,GACG,OAAO,EACP,IAAI,CAAC,EACL,OAAO,CAAC,WAAW,OAAO,KAAK,EAAE,SAAS,GAAG;AAAA,MAC5C,OAAO;AAAA,IACT,CAAC;AAAA,EACL,EACC,IAAI,CAAC;AAAA,EACR,eAAeA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACpC,gBAAgBA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,OAAO,sBAAsB,SAAS;AAAA,EACtC,MAAMA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,UAAUA,GAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAYM,IAAM,4BAA4BA,GACtC,YAAY;AAAA,EACX,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,oBAAoB;AAAA,EACpC,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQA,GAAE,MAAM,iBAAiB,EAAE,IAAI,CAAC;AAAA,EACxC,iBAAiBA,GAAE,KAAK,CAAC,kBAAkB,SAAS,CAAC;AAAA,EACrD,OAAO,YAAY,SAAS;AAAA,EAC5B,UAAU,eAAe,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC,EACA;AAAA,EACC,CAAC,SAAS;AACR,UAAMC,qBAAoB,oBAAI,IAAoB;AAClD,eAAW,SAAS,KAAK,QAAQ,SAAS,cAAc,GAAG;AACzD,YAAM,KAAK,MAAM,CAAC;AAClB,MAAAA,mBAAkB,IAAI,KAAKA,mBAAkB,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,IAChE;AACA,UAAM,WAAW,KAAK,OAAO,IAAI,CAAC,UAAU,MAAM,EAAE;AACpD,QAAI,IAAI,IAAI,QAAQ,EAAE,SAAS,SAAS,QAAQ;AAC9C,aAAO;AAAA,IACT;AACA,QAAIA,mBAAkB,SAAS,SAAS,QAAQ;AAC9C,aAAO;AAAA,IACT;AACA,WAAO,SAAS,MAAM,CAAC,OAAOA,mBAAkB,IAAI,EAAE,MAAM,CAAC;AAAA,EAC/D;AAAA,EACA;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,SAAS;AAAA,EAClB;AACF;;;ACnGF,SAAS,KAAAC,UAAS;AA0BX,IAAM,kCAAkCC,GAC5C,YAAY;AAAA;AAAA;AAAA;AAAA,EAIX,MAAMA,GAAE,KAAK,CAAC,SAAS,OAAO,CAAC;AAAA,EAC/B,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChC,aAAa,eAAe,SAAS;AACvC,CAAC,EAGA,OAAO,CAAC,UAAU,MAAM,SAAS,WAAY,MAAM,QAAQ,UAAa,MAAM,QAAQ,IAAK;AAAA,EAC1F,OAAO;AAAA,EACP,MAAM,CAAC,KAAK;AACd,CAAC,EAMA,OAAO,CAAC,UAAU,EAAE,cAAc,QAAQ;AAAA,EACzC,OACE;AAAA,EACF,MAAM,CAAC,UAAU;AACnB,CAAC;AAEI,IAAM,6BAA6BA,GAAE,YAAY;AAAA,EACtD,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,WAAWA,GAAE,QAAQ;AAAA,EACrB,UAAUA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,OAAO,gCAAgC,SAAS;AAClD,CAAC;AAiBM,IAAM,2BAA2BA,GACrC,YAAY;AAAA,EACX,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,iBAAiB;AAAA,EACjC,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,cAAcA,GAAE,OAAO,EAAE,SAAS;AAAA,EAClC,MAAMA,GAAE,KAAK,CAAC,UAAU,OAAO,CAAC;AAAA,EAChC,SAASA,GAAE,MAAM,0BAA0B,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EAC1D,iBAAiBA,GAAE,KAAK,CAAC,kBAAkB,SAAS,CAAC;AAAA,EACrD,OAAO,YAAY,SAAS;AAAA,EAC5B,UAAU,eAAe,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,SAASA,GAAE,QAAQ,EAAE,SAAS;AAAA,EAC9B,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC,EACA,OAAO,CAAC,SAAS,KAAK,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,GAAG;AAAA,EACjE,OAAO;AAAA,EACP,MAAM,CAAC,SAAS;AAClB,CAAC,EACA;AAAA,EACC,CAAC,SACC,KAAK,SAAS,YAAY,KAAK,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,EAAE,WAAW;AAAA,EACzF;AAAA,IACE,OAAO;AAAA,IACP,MAAM,CAAC,SAAS;AAAA,EAClB;AACF,EACC,OAAO,CAAC,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC,EAAE,SAAS,KAAK,QAAQ,QAAQ;AAAA,EAC/F,OAAO;AAAA,EACP,MAAM,CAAC,SAAS;AAClB,CAAC;;;AClGI,IAAM,kCAAkC;AAGxC,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;AAO7B,IAAM,kCAAkC;AAUxC,IAAM,8BAA8B;AAuBpC,IAAM,oCAAoC;;;AC1DjD,SAAS,KAAAC,UAAS;;;ACcX,IAAM,sBAAsB;;;ADD5B,IAAM,wBAAwB,CAAC,YAAY,WAAW,gBAAgB,SAAS;AAGtF,IAAM,4BAA4B,sBAAsB;AAYjD,IAAM,2BAA2BC,GAAE,aAAa;AAAA,EACrD,MAAMA,GAAE,QAAQ,OAAO;AAAA,EACvB,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAClC,CAAC;AAOD,IAAM,sBAAsBA,GACzB,OAAO,EACP,IAAI,CAAC,EACL,OAAO,CAAC,SAAS,KAAK,KAAK,MAAM,IAAI;AAAA,EACpC,OAAO;AACT,CAAC,EACA,KAAK,EAAE,WAAW,gCAAgC,CAAC;AAGtD,IAAM,wBAAwBA,GAAE,YAAY;AAAA,EAC1C,YAAYA,GAAE,OAAO,EAAE,GAAG,CAAC,EAAE,IAAI,sBAAsB;AAAA,EACvD,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACvC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,oBAAoB,EAAE,SAAS;AACvE,CAAC;AAGD,IAAM,iCAAiCA,GAAE,YAAY;AAAA,EACnD,MAAMA,GAAE,KAAK,qBAAqB;AAAA,EAClC,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,+BAA+B;AAC/D,CAAC;AAGD,IAAM,yBAAyBA,GAAE,YAAY;AAAA,EAC3C,YAAYA,GAAE,MAAM,8BAA8B,EAAE,IAAI,CAAC,EAAE,IAAI,yBAAyB;AAC1F,CAAC;AAUD,SAASC,sBACP,OACqB;AACrB,QAAM,QAA6B,IAAID,GAAE,KAAK,UAAU,EAAE,OAAO,UAAU,MAAM,MAAM,KAAK,CAAC;AAC7F,QAAM,KAAK,QAAQ;AACnB,SAAO;AACT;AAEA,IAAM,qBAAqBA,GAAE,YAAY;AAAA,EACvC,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,cAAcA,GAAE,OAAO,EAAE,SAAS;AAAA,EAClC,eAAe;AAAA,EACf,QAAQA,GAAE,OAAO,EAAE,MAAM,qBAAqB;AAAA,IAC5C,OACE;AAAA,EACJ,CAAC;AAAA,EACD,OAAO,YAAY,SAAS;AAAA,EAC5B,WAAW,yBAAyB,SAAS;AAAA,EAC7C,WAAW;AAAA,EACX,SAAS;AAAA,EACT,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,UAAU,eAAe,SAAS;AAAA,EAClC,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC;AA2CM,IAAM,sBAAsB,mBAAmB;AAAA,EACpDC,sBAA0D,CAAC,QAAQ;AAEjE,QAAI,IAAI,OAAO,KAAK,CAACC,YAAWA,OAAM,MAAM,UAAU,OAAO,CAAC,GAAG;AAC/D;AAAA,IACF;AACA,UAAM,OAAO,IAAI;AACjB,UAAM,UAAU,IAAI,IAAI,IAAI,OAAO,IAAI,CAACA,WAAU,OAAOA,OAAM,OAAO,CAAC,CAAC,CAAC,CAAC;AAC1E,UAAM,SAAS,CAAC,MAAoC,OAAgB,YAAoB;AACtF,UAAI,OAAO,KAAK,EAAE,MAAM,UAAU,OAAO,SAAS,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;AAAA,IACrE;AAEA,QAAI,CAAC,QAAQ,IAAI,eAAe,GAAG;AACjC,YAAM,OAAO,KAAK;AAKlB,YAAM,WAAW,oBAAoB,MAAS,EAAE,QAAQ,IAAI;AAC5D,UAAI,SAAS,SAAS;AACpB;AAAA,UACE,CAAC,eAAe;AAAA,UAChB;AAAA,UACA,qCAAqC,+BAA+B;AAAA,QACtE;AAAA,MACF;AACA,UAAI,SAAS,eAAe,IAAI;AAC9B;AAAA,UACE,CAAC,eAAe;AAAA,UAChB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI,uBAAuB,IAAI,GAAG;AAChC;AAAA,UACE,CAAC,eAAe;AAAA,UAChB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,QAAQ,IAAI,OAAO;AACxC,UAAM,aAAa,CAAC,QAAQ,IAAI,WAAW;AAC3C,QAAI,eAAe,KAAK,UAAU,UAAa,KAAK,MAAM,SAAS,SAAS;AAC1E;AAAA,QACE,CAAC,SAAS,MAAM;AAAA,QAChB,KAAK,MAAM;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,eAAe,cAAc,KAAK,cAAc,QAAW;AAC7D,UAAI,KAAK,UAAU,QAAW;AAC5B;AAAA,UACE,CAAC,OAAO;AAAA,UACR,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AACA,UAAI,KAAK,OAAO,UAAU,aAAa,QAAW;AAChD;AAAA,UACE,CAAC,WAAW;AAAA,UACZ,KAAK;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAGA,UACE,OAAO,KAAK,UAAU,QAAQ,YAC9B,OAAO,KAAK,OAAO,QAAQ,YAC3B,KAAK,UAAU,QAAQ,KAAK,MAAM,KAClC;AACA;AAAA,UACE,CAAC,aAAa,KAAK;AAAA,UACnB,KAAK,UAAU;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,IAAI,SAAS,GAAG;AAC3B,YAAM,aAAa,KAAK,QAAQ;AAChC,YAAM,OAAO,oBAAI,IAAY;AAC7B,iBAAW,CAAC,OAAO,SAAS,KAAK,WAAW,QAAQ,GAAG;AACrD,YAAI,KAAK,IAAI,UAAU,IAAI,GAAG;AAC5B;AAAA,YACE,CAAC,WAAW,cAAc,OAAO,MAAM;AAAA,YACvC,UAAU;AAAA,YACV,IAAI,UAAU,IAAI;AAAA,UACpB;AAAA,QACF;AACA,aAAK,IAAI,UAAU,IAAI;AAAA,MACzB;AACA,UAAI,WAAW,MAAM,CAAC,cAAc,UAAU,WAAW,CAAC,GAAG;AAC3D;AAAA,UACE,CAAC,WAAW,YAAY;AAAA,UACxB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,IAAI,WAAW,GAAG;AAC7B,YAAM,EAAE,YAAY,WAAW,IAAI,KAAK;AACxC,UAAI,eAAe,UAAa,cAAc,YAAY;AACxD;AAAA,UACE,CAAC,aAAa,YAAY;AAAA,UAC1B;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AE1PO,SAAS,WAAW,MAAsB;AAG/C,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,YAAY,IAAI;AAClB,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,MAAM,KAAK,EAAE;AAC9B;;;AClBA,SAAS,KAAAC,UAAS;AASX,IAAM,wBAAwBC,GAAE,YAAY;AAAA,EACjD,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAGM,IAAM,sBAAsBA,GAAE,YAAY;AAAA,EAC/C,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,SAASA,GAAE,MAAM,qBAAqB,EAAE,IAAI,CAAC;AAC/C,CAAC;AAGM,IAAM,qBAAqBA,GAAE,YAAY;AAAA,EAC9C,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,SAASA,GAAE,MAAM,qBAAqB,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACxD,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACnC,iBAAiBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACjC,UAAUA,GAAE,OAAO,EAAE,SAAS;AAChC,CAAC;AAGD,SAAS,eACP,KACA,OACqD;AACrD,MAAI,IAAI,YAAY,QAAW;AAC7B,WAAO,IAAI,WAAW,SAAY,IAAI,UAAU;AAAA,EAClD;AACA,MAAI,IAAI,WAAW,QAAW;AAC5B,WAAO;AAAA,EACT;AACA,SAAO,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,IAAI,MAAM,GAAG;AACvD;AAGA,SAAS,kBAAkB,SAAsC;AAC/D,QAAM,SAAS,oBAAI,IAAoB;AACvC,aAAW,SAAS,QAAQ,SAAS,cAAc,GAAG;AACpD,UAAM,KAAK,MAAM,CAAC;AAClB,WAAO,IAAI,KAAK,OAAO,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EAC1C;AACA,SAAO;AACT;AAwBO,IAAM,sBAAsBA,GAChC,YAAY;AAAA,EACX,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,MAAMA,GAAE,MAAM,kBAAkB,EAAE,IAAI,CAAC;AAAA,EACvC,OAAOA,GAAE,MAAM,mBAAmB,EAAE,SAAS;AAAA,EAC7C,iBAAiBA,GAAE,KAAK,CAAC,kBAAkB,SAAS,CAAC;AAAA,EACrD,cAAcA,GAAE,QAAQ,UAAU,EAAE,SAAS;AAAA,EAC7C,gBAAgBA,GAAE,QAAQ,EAAE,SAAS;AAAA,EACrC,OAAO,YAAY,SAAS;AAAA,EAC5B,UAAU,eAAe,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC,EACA;AAAA,EACC,CAAC,SAAS;AACR,UAAM,OAAO,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,EAAE;AACpD,WAAO,IAAI,IAAI,GAAG,EAAE,SAAS,IAAI;AAAA,EACnC;AAAA,EACA,EAAE,OAAO,iCAAiC,MAAM,CAAC,OAAO,EAAE;AAC5D,EACC;AAAA,EACC,CAAC,SAAS;AACR,UAAM,MAAM,KAAK,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACzC,WAAO,IAAI,IAAI,GAAG,EAAE,SAAS,IAAI;AAAA,EACnC;AAAA,EACA,EAAE,OAAO,2BAA2B,MAAM,CAAC,MAAM,EAAE;AACrD,EACC;AAAA,EACC,CAAC,SAAS;AACR,UAAM,SAAS,kBAAkB,KAAK,OAAO;AAC7C,UAAM,MAAM,KAAK,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACzC,WACE,OAAO,SAAS,IAAI,IAAI,GAAG,EAAE,QAC7B,IAAI,MAAM,CAAC,OAAO,OAAO,IAAI,EAAE,MAAM,CAAC,KACtC,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,UAAU,CAAC;AAAA,EAErD;AAAA,EACA;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,SAAS;AAAA,EAClB;AACF,EACC;AAAA,EACC,CAAC,SAAS,KAAK,KAAK,MAAM,CAAC,QAAS,IAAI,YAAY,YAAgB,IAAI,WAAW,OAAU;AAAA,EAC7F;AAAA,IACE,OAAO;AAAA,IACP,MAAM,CAAC,MAAM;AAAA,EACf;AACF,EACC;AAAA,EACC,CAAC,SAAS;AACR,UAAM,UAAU,IAAI,KAAK,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AACjE,WAAO,KAAK,KAAK,MAAM,CAAC,QAAQ,IAAI,WAAW,UAAa,QAAQ,IAAI,IAAI,MAAM,CAAC;AAAA,EACrF;AAAA,EACA,EAAE,OAAO,0DAA0D,MAAM,CAAC,MAAM,EAAE;AACpF,EACC;AAAA,EACC,CAAC,SAAS;AACR,UAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,WAAO,KAAK,KAAK,MAAM,CAAC,QAAQ;AAC9B,YAAM,UAAU,eAAe,KAAK,KAAK;AACzC,UAAI,YAAY,QAAW;AAEzB,eAAO;AAAA,MACT;AACA,YAAM,MAAM,QAAQ,IAAI,CAAC,WAAW,OAAO,EAAE;AAC7C,aAAO,IAAI,IAAI,GAAG,EAAE,SAAS,IAAI,UAAU,IAAI,SAAS,IAAI,eAAe;AAAA,IAC7E,CAAC;AAAA,EACH;AAAA,EACA;AAAA,IACE,OACE;AAAA,IACF,MAAM,CAAC,MAAM;AAAA,EACf;AACF;;;AC7JF,SAAS,KAAAC,UAAS;AAKX,IAAM,uCAAuCC,GAAE,YAAY;AAAA,EAChE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC1B,CAAC;AAGM,IAAM,8BAA8BA,GAAE,YAAY;AAAA,EACvD,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAUA,GAAE,MAAM,oCAAoC,EAAE,IAAI,CAAC;AAC/D,CAAC;AAYM,IAAM,+CAA+CA,GAAE,aAAa;AAAA,EACzE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC1B,CAAC;AAEM,IAAM,sCAAsCA,GAAE,aAAa;AAAA,EAChE,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,UAAUA,GAAE,MAAM,4CAA4C,EAAE,IAAI,CAAC;AACvE,CAAC;AAUM,IAAM,4BAA4BA,GACtC,YAAY;AAAA,EACX,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,MAAMA,GAAE,QAAQ,kBAAkB;AAAA,EAClC,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,QAAQA,GAAE,OAAO;AAAA,EACjB,YAAYA,GAAE,OAAO,EAAE,SAAS;AAAA,EAChC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,EAChC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,EAChC,QAAQ,4BAA4B,SAAS;AAAA,EAC7C,gBAAgBA,GAAE,OAAO,EAAE,SAAS;AAAA,EACpC,OAAO,YAAY,SAAS;AAAA,EAC5B,UAAU,eAAe,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD,CAAC,EACA,OAAO,CAAC,SAAS,KAAK,YAAY,KAAK,UAAU;AAAA,EAChD,OAAO;AAAA,EACP,MAAM,CAAC,UAAU;AACnB,CAAC;;;ACpEH,SAAS,KAAAC,UAAS;AAoBlB,IAAM,eAAe;AAAA;AAAA,EAEnB,UAAUC,GAAE,QAAQ,IAAI;AAAA;AAAA,EAExB,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACpC,eAAeA,GAAE,QAAQ,KAAK;AAAA,EAC9B,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,OAAOA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACvB,OAAO,oBAAoB,SAAS;AAAA,EACpC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,oBAAoBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACjD,iBAAiBA,GAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE,SAAS;AACvD;AAGO,IAAM,0CAA0CA,GAAE,aAAa;AAAA,EACpE,MAAMA,GAAE,KAAK,CAAC,SAAS,OAAO,CAAC;AAAA,EAC/B,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACrB,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChC,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAC1C,CAAC;AAWM,IAAM,qCAAqCA,GAAE,aAAa;AAAA,EAC/D,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,OAAO,wCAAwC,SAAS;AAC1D,CAAC;AASM,IAAM,mCAAmCA,GAAE,aAAa;AAAA,EAC7D,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,iBAAiB;AAAA,EACjC,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,cAAcA,GAAE,OAAO,EAAE,SAAS;AAAA,EAClC,MAAMA,GAAE,KAAK,CAAC,UAAU,OAAO,CAAC;AAAA,EAChC,SAASA,GAAE,MAAM,kCAAkC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;AAAA,EAClE,SAASA,GAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;AAGM,IAAM,4BAA4BA,GAAE,aAAa;AAAA,EACtD,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAMA,GAAE,OAAO,EAAE,SAAS;AAC5B,CAAC;AAGM,IAAM,oCAAoCA,GAAE,aAAa;AAAA,EAC9D,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,oBAAoB;AAAA,EACpC,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQA,GAAE,MAAM,yBAAyB,EAAE,IAAI,CAAC;AAClD,CAAC;AAGM,IAAM,gCAAgCA,GAAE,aAAa;AAAA,EAC1D,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC;AACxB,CAAC;AAGM,IAAM,8BAA8BA,GAAE,aAAa;AAAA,EACxD,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,SAASA,GAAE,MAAM,6BAA6B,EAAE,IAAI,CAAC;AACvD,CAAC;AAGM,IAAM,6BAA6BA,GAAE,aAAa;AAAA,EACvD,IAAIA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACpB,SAASA,GAAE,MAAM,6BAA6B,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAChE,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AACrC,CAAC;AAiBM,IAAM,8BAA8BA,GAAE,aAAa;AAAA,EACxD,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACzB,aAAaA,GAAE,OAAO,EAAE,SAAS;AAAA,EACjC,MAAMA,GAAE,MAAM,0BAA0B,EAAE,IAAI,CAAC;AAAA,EAC/C,OAAOA,GAAE,MAAM,2BAA2B,EAAE,SAAS;AAAA,EACrD,cAAcA,GAAE,QAAQ,UAAU,EAAE,SAAS;AAAA,EAC7C,gBAAgBA,GAAE,QAAQ,EAAE,SAAS;AACvC,CAAC;AAOM,IAAM,oCAAoCA,GAAE,aAAa;AAAA,EAC9D,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,kBAAkB;AAAA,EAClC,QAAQA,GAAE,OAAO;AAAA,EACjB,YAAYA,GAAE,OAAO,EAAE,SAAS;AAAA,EAChC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,EAChC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC;AAAA,EAChC,QAAQ,oCAAoC,SAAS;AAAA,EACrD,gBAAgBA,GAAE,OAAO,EAAE,SAAS;AACtC,CAAC;AAGM,IAAM,mCAAmCA,GAAE,aAAa;AAAA,EAC7D,MAAMA,GAAE,QAAQ,OAAO;AAAA,EACvB,KAAK;AAAA,EACL,KAAKA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAClC,CAAC;AAeM,IAAM,8BAA8BA,GACxC,aAAa;AAAA,EACZ,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,WAAW;AAAA,EAC3B,WAAW,iCAAiC,SAAS;AAAA,EACrD,OAAOA,GAAE,aAAa,EAAE,MAAMA,GAAE,QAAQ,mBAAmB,EAAE,CAAC,EAAE,SAAS;AAC3E,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,QAAM,OAAO,IAAI;AACjB,MAAI,KAAK,OAAO,gBAAgB,QAAW;AACzC,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK,MAAM;AAAA,MAClB,SACE;AAAA,MACF,MAAM,CAAC,SAAS,aAAa;AAAA,IAC/B,CAAC;AAAA,EACH;AACA,MAAI,KAAK,cAAc,UAAa,KAAK,OAAO,UAAU,aAAa,QAAW;AAChF,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SACE;AAAA,MACF,MAAM,CAAC,WAAW;AAAA,IACpB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,cAAc,UAAa,KAAK,UAAU,QAAW;AAC5D,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SAAS;AAAA,MACT,MAAM,CAAC,OAAO;AAAA,IAChB,CAAC;AAAA,EACH;AACF,CAAC;AAiBI,IAAM,8BAA8BA,GACxC,aAAa;AAAA,EACZ,GAAG;AAAA,EACH,MAAMA,GAAE,QAAQ,YAAY;AAAA,EAC5B,QAAQA,GAAE,OAAO,EAAE,MAAM,mBAAmB;AAAA,EAC5C,cAAcA,GAAE,OAAO,EAAE,SAAS;AAAA,EAClC,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,WAAW,yBAAyB,SAAS;AAAA,EAC7C,WAAWA,GAAE,aAAa;AAAA,IACxB,YAAYA,GAAE,OAAO,EAAE,GAAG,CAAC,EAAE,IAAI,sBAAsB;AAAA,IACvD,YAAYA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,IACvC,UAAUA,GAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,oBAAoB,EAAE,SAAS;AAAA,EACvE,CAAC;AAAA,EACD,SAASA,GAAE,aAAa;AAAA,IACtB,YAAYA,GACT;AAAA,MACCA,GAAE,aAAa;AAAA,QACb,MAAMA,GAAE,KAAK,qBAAqB;AAAA,QAClC,QAAQA,GAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,+BAA+B;AAAA,MAC/D,CAAC;AAAA,IACH,EACC,IAAI,CAAC,EACL,IAAI,sBAAsB,MAAM;AAAA,EACrC,CAAC;AACH,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,QAAM,OAAO,IAAI;AACjB,MAAI,KAAK,UAAU,UAAa,KAAK,MAAM,SAAS,SAAS;AAC3D,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK,MAAM;AAAA,MAClB,SAAS;AAAA,MACT,MAAM,CAAC,SAAS,MAAM;AAAA,IACxB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,cAAc,UAAa,KAAK,UAAU,QAAW;AAC5D,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SAAS;AAAA,MACT,MAAM,CAAC,OAAO;AAAA,IAChB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,cAAc,UAAa,KAAK,OAAO,UAAU,aAAa,QAAW;AAChF,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,SACE;AAAA,MACF,MAAM,CAAC,WAAW;AAAA,IACpB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,cAAc,UAAa,KAAK,UAAU,QAAQ,KAAK,OAAO,KAAK;AAC1E,QAAI,OAAO,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK,UAAU;AAAA,MACtB,SAAS;AAAA,MACT,MAAM,CAAC,aAAa,KAAK;AAAA,IAC3B,CAAC;AAAA,EACH;AACF,CAAC;;;ACvRI,SAAS,WAAW,MAAwB;AACjD,SAAO,MAAM,KAAK,MAAM,CAAC,cAAc,UAAU,YAAY,CAAC,CAAW;AAC3E;AAaO,SAAS,aAAa,GAAW,GAAmB;AACzD,MAAI,MAAM,GAAG;AACX,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,CAAC;AACvB,MAAI,QAAQ,WAAW,CAAC;AACxB,MAAI,KAAK,SAAS,MAAM,QAAQ;AAC9B,KAAC,MAAM,KAAK,IAAI,CAAC,OAAO,IAAI;AAAA,EAC9B;AACA,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,KAAK;AAAA,EACd;AAEA,MAAI,WAAW,IAAI,YAAY,MAAM,SAAS,CAAC;AAC/C,MAAI,UAAU,IAAI,YAAY,MAAM,SAAS,CAAC;AAC9C,WAAS,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK,GAAG;AACzC,aAAS,CAAC,IAAI;AAAA,EAChB;AACA,WAAS,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK,GAAG;AACxC,YAAQ,CAAC,IAAI;AACb,UAAM,YAAY,KAAK,IAAI,CAAC;AAC5B,aAAS,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK,GAAG;AACzC,YAAM,WAAY,SAAS,CAAC,IAAe;AAC3C,YAAM,YAAa,QAAQ,IAAI,CAAC,IAAe;AAC/C,YAAM,eAAgB,SAAS,IAAI,CAAC,KAAgB,cAAc,MAAM,IAAI,CAAC,IAAI,IAAI;AACrF,cAAQ,CAAC,IAAI,KAAK,IAAI,UAAU,WAAW,YAAY;AAAA,IACzD;AACA,KAAC,UAAU,OAAO,IAAI,CAAC,SAAS,QAAQ;AAAA,EAC1C;AACA,SAAO,SAAS,MAAM,MAAM;AAC9B;AASO,SAAS,WAAW,GAAW,GAAmB;AACvD,QAAM,MAAM,KAAK,IAAI,WAAW,CAAC,EAAE,QAAQ,WAAW,CAAC,EAAE,MAAM;AAC/D,SAAO,QAAQ,IAAI,KAAK,MAAM,aAAa,GAAG,CAAC,KAAK;AACtD;;;ACSO,SAAS,wBACd,MAC0D;AAC1D,SAAO,cAAc,IAAI,EAAE;AAAA,IAAI,CAAC,cAC9B,SAAS,mBAAmB,WAAW,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,WAAW;AAAA,MAClE,QAAQ,IAAI,QAAQ,CAAC;AAAA,MACrB;AAAA,IACF,EAAE;AAAA,EACJ;AACF;AAoBO,SAAS,eAAe,MAA0B,MAAkC;AACzF,QAAM,MAAM,OAAO,SAAS,WAAW,OAAO;AAC9C,QAAM,SAAS,mBAAmB,KAAK,yBAAyB;AAChE,QAAM,EAAE,MAAM,YAAY,IAAI,IAAI,8BAA8B,QAAQ,KAAK,SAAS;AACtF,QAAM,UAAU,MAAM,YAAY,yBAAyB;AAC3D,QAAM,YAAY,OAAO,OAAO,SAAS,IAAI,UAAU,QAAQ,SAAS,WAAW;AAEnF,QAAM,aAAgD,CAAC;AACvD,gBAAc,IAAI,EAAE,QAAQ,CAAC,WAAW,UAAU;AAChD,UAAM,gBAAgB,mBAAmB,WAAW,IAAI;AACxD,QAAI,kBAAkB,IAAI;AACxB,iBAAW,KAAK,EAAE,OAAO,MAAM,cAAc,CAAC;AAAA,IAChD;AAAA,EACF,CAAC;AACD,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,EAAE,gBAAgB,IAAI,WAAW,IAAI,SAAS,WAAW,YAAY,GAAG,OAAO,CAAC,EAAE;AAAA,EAC3F;AAEA,MAAI,SAAS,WAAW,CAAC;AACzB,MAAI,OAAO,WAAW,OAAO,MAAM,OAAO;AAC1C,aAAW,aAAa,WAAW,MAAM,CAAC,GAAG;AAC3C,UAAM,sBAAsB,WAAW,UAAU,MAAM,OAAO;AAC9D,QAAI,sBAAsB,MAAM;AAC9B,aAAO;AACP,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL,gBAAgB,OAAO;AAAA,IACvB,WAAW,OAAO;AAAA,IAClB;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,OAAO,WAAW,SAAS,OAAO,IAAI,GAAG,SAAS,OAAO,CAAC;AAAA,EAC5D;AACF;AAOA,SAAS,cAAc,MAAqC;AAC1D,SAAO;AAAA,IACL,KAAK;AAAA,IACL,GAAI,MAAM,QAAQ,KAAK,mBAAmB,IACtC,KAAK,oBAAoB,MAAM,GAAG,kCAAkC,IACpE,CAAC;AAAA,EACP;AACF;AAOA,SAAS,mBAAmB,WAAoB,MAAkC;AAChF,SAAO,OAAO,cAAc,WACxB;AAAA,IACE;AAAA,MACE,mBAAmB,WAAW,+BAA+B;AAAA,MAC7D,KAAK;AAAA,IACP;AAAA,IACA;AAAA,EACF,IACA;AACN;AAOA,SAAS,MAAM,YAAoB,KAAqB;AACtD,QAAM,MAAM,mBAAmB,YAAY,GAAG;AAC9C,SAAO,IAAI,SAAS,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAChD;AAGA,SAAS,SAAS,YAA8B;AAC9C,SAAO,eAAe,KAAK,CAAC,IAAI,WAAW,MAAM,GAAG;AACtD;AAGO,IAAM,OAAO;AAAA;AAAA,EAElB,MAAM;AAAA;AAAA,EAEN,SAAS;AAAA;AAAA,EAET,OAAO;AACT;AAYO,SAAS,eACd,WACA,SACgE;AAChE,QAAM,OAAO,UAAU;AACvB,QAAM,UAAU,QAAQ;AACxB,QAAM,QAAQ,UAAU;AACxB,QAAM,QAAQ,IAAI,YAAY,OAAO,KAAK,KAAK;AAC/C,MAAI,WAAW,IAAI,YAAY,KAAK;AACpC,MAAI,UAAU,IAAI,YAAY,KAAK;AACnC,WAASC,KAAI,GAAGA,MAAK,SAASA,MAAK,GAAG;AACpC,aAASA,EAAC,IAAIA;AACd,UAAMA,EAAC,IAAI,KAAK;AAAA,EAClB;AACA,WAASC,KAAI,GAAGA,MAAK,MAAMA,MAAK,GAAG;AACjC,YAAQ,CAAC,IAAIA;AACb,UAAMA,KAAI,KAAK,IAAI,KAAK;AACxB,UAAM,gBAAgB,UAAUA,KAAI,CAAC;AACrC,aAASD,KAAI,GAAGA,MAAK,SAASA,MAAK,GAAG;AACpC,YAAM,SAAU,SAASA,KAAI,CAAC,KAAgB,kBAAkB,QAAQA,KAAI,CAAC,IAAI,IAAI;AACrF,YAAM,UAAW,SAASA,EAAC,IAAe;AAC1C,YAAM,QAAS,QAAQA,KAAI,CAAC,IAAe;AAC3C,YAAM,OAAO,KAAK,IAAI,QAAQ,SAAS,KAAK;AAC5C,cAAQA,EAAC,IAAI;AACb,YAAMC,KAAI,QAAQD,EAAC,IACjB,WAAW,OAAO,KAAK,OAAO,YAAY,OAAO,KAAK,UAAU,KAAK;AAAA,IACzE;AACA,KAAC,UAAU,OAAO,IAAI,CAAC,SAAS,QAAQ;AAAA,EAC1C;AAEA,QAAM,OAAuE,CAAC;AAC9E,MAAI,IAAI;AACR,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,IAAI,GAAG;AACrB,UAAM,OAAO,MAAM,IAAI,QAAQ,CAAC;AAChC,QAAI,SAAS,KAAK,MAAM;AACtB,WAAK;AACL,WAAK;AAAA,IACP,WAAW,SAAS,KAAK,SAAS;AAChC,WAAK;AAAA,IACP,OAAO;AACL,WAAK;AAAA,IACP;AACA,SAAK,KAAK,EAAE,MAAM,gBAAgB,GAAG,cAAc,EAAE,CAAC;AAAA,EACxD;AACA,SAAO,KAAK,QAAQ;AACtB;AAEA,SAAS,WACP,WACA,SAC0B;AAC1B,QAAM,QAAkC,CAAC;AACzC,MAAI,UAAU;AACd,aAAW,EAAE,MAAM,gBAAgB,aAAa,KAAK,eAAe,WAAW,OAAO,GAAG;AACvF,QAAI,SAAS,KAAK,OAAO;AACvB,YAAM,KAAK;AAAA,QACT,WAAW;AAAA,QACX,SAAS,QAAQ,YAAY;AAAA,QAC7B,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,eAAW;AACX,UAAM,gBAAgB,UAAU,cAAc;AAC9C,QAAI,SAAS,KAAK,SAAS;AACzB,YAAM,KAAK;AAAA,QACT,QAAQ,IAAI,OAAO;AAAA,QACnB,WAAW;AAAA,QACX,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,UAAM,cAAc,QAAQ,YAAY;AACxC,UAAM,KAAK;AAAA,MACT,QAAQ,IAAI,OAAO;AAAA,MACnB,WAAW;AAAA,MACX,SAAS;AAAA,MACT,YAAY,WAAW,eAAe,WAAW;AAAA,MACjD,QAAQ,kBAAkB,cAAc,YAAY;AAAA,IACtD,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;AC9OA,IAAM,qBAAqB;AAM3B,IAAME,kBAAiB;AAEvB,SAAS,cAAc,OAAe,QAAiC;AACrE,MAAI,SAAS;AACb,MAAI,OAAO,SAAS,OAAO;AACzB,aAAS,OAAO,KAAK;AAAA,EACvB;AACA,MAAI,OAAO,kBAAkB,MAAM;AACjC,aAAS,OAAO,SAAS,OAAO,kBAAkB,OAAO,MAAM,IAAI,OAAO,YAAY;AAAA,EACxF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAe,QAAiC;AAC1E,MAAI,SAAS;AACb,MAAI,OAAO,cAAc,SAAS,OAAO,cAAc,QAAQ;AAC7D,aAAS,OAAO,UAAU,OAAO,SAAS;AAAA,EAC5C;AACA,MAAI,OAAO,sBAAsB,MAAM;AACrC,aAAS,OAAO,QAAQA,iBAAgB,EAAE;AAAA,EAC5C;AACA,MAAI,OAAO,4BAA4B,MAAM;AAI3C,aAAS,OAAO,QAAQ,qBAAqB,GAAG;AAChD,QAAI,OAAO,SAAS,OAAO;AACzB,eAAS,OAAO,KAAK;AAAA,IACvB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MAAM,UAAU,KAAK,EAAE,QAAQ,oBAAoB,EAAE,EAAE,UAAU,KAAK;AAC/E;AAMO,SAAS,oBAAoB,GAAW,GAAW,KAAqB;AAC7E,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,KAAK,IAAI,EAAE,SAAS,EAAE,MAAM,IAAI,IAAK,QAAO,MAAM;AACtD,MAAI,EAAE,WAAW,EAAG,QAAO,EAAE;AAC7B,MAAI,EAAE,WAAW,EAAG,QAAO,EAAE;AAE7B,MAAI,WAAW,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAC/D,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK,GAAG;AACrC,UAAM,UAAU,CAAC,CAAC;AAClB,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK,GAAG;AACrC,YAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI;AACzC,YAAM,QAAQ,KAAK;AAAA,QAChB,SAAS,CAAC,IAAe;AAAA,QACzB,QAAQ,IAAI,CAAC,IAAe;AAAA,QAC5B,SAAS,IAAI,CAAC,IAAe;AAAA,MAChC;AACA,cAAQ,KAAK,KAAK;AAClB,UAAI,QAAQ,OAAQ,UAAS;AAAA,IAC/B;AACA,QAAI,SAAS,IAAK,QAAO,MAAM;AAC/B,eAAW;AAAA,EACb;AACA,SAAO,SAAS,EAAE,MAAM;AAC1B;AAYO,SAAS,UACd,OACA,UACA,SAA0B,CAAC,GACV;AACjB,QAAM,eAAe,OAAO,aAAa,WAAW,CAAC,QAAQ,IAAI;AACjE,QAAM,gBAAgB,cAAc,OAAO,MAAM;AACjD,QAAM,mBAAmB,aAAa,IAAI,CAAC,WAAW,cAAc,QAAQ,MAAM,CAAC;AAEnF,MAAI,iBAAiB,KAAK,CAAC,WAAW,WAAW,aAAa,GAAG;AAC/D,WAAO,EAAE,SAAS,MAAM,KAAK,QAAQ;AAAA,EACvC;AAEA,QAAM,oBACJ,OAAO,cAAc,SACrB,OAAO,cAAc,UACrB,OAAO,sBAAsB,QAC7B,OAAO,4BAA4B;AACrC,QAAM,kBAAkB,oBACpB,mBAAmB,eAAe,MAAM,IACxC;AACJ,QAAM,qBAAqB,oBACvB,iBAAiB,IAAI,CAAC,WAAW,mBAAmB,QAAQ,MAAM,CAAC,IACnE;AAEJ,MAAI,qBAAqB,mBAAmB,KAAK,CAAC,WAAW,WAAW,eAAe,GAAG;AACxF,WAAO,EAAE,SAAS,MAAM,KAAK,aAAa;AAAA,EAC5C;AAEA,QAAM,cACJ,OAAO,mBAAmB,OAAO,mBAAmB,eAAe,IAAI;AACzE,QAAM,iBACJ,OAAO,mBAAmB,OACtB,mBAAmB,IAAI,CAAC,WAAW,mBAAmB,MAAM,CAAC,IAC7D;AAEN,MAAI,OAAO,mBAAmB,QAAQ,eAAe,KAAK,CAAC,WAAW,WAAW,WAAW,GAAG;AAC7F,WAAO,EAAE,SAAS,MAAM,KAAK,SAAS;AAAA,EACxC;AASA,QAAM,cAAc,OAAO,eAAe;AAC1C,MACE,cAAc,KACd,YAAY,KAAK,EAAE,SAAS,KAC5B,eAAe;AAAA,IACb,CAAC,WAAW,oBAAoB,aAAa,QAAQ,WAAW,KAAK;AAAA,EACvE,GACA;AACA,WAAO,EAAE,SAAS,MAAM,KAAK,QAAQ;AAAA,EACvC;AAEA,SAAO,EAAE,SAAS,OAAO,KAAK,OAAO;AACvC;;;ACzBA,IAAM,WAAW,oBAAI,IAA8C;AAU5D,SAAS,mBACd,YAC0C;AAC1C,SAAO;AACT;AAUO,SAAS,qBACd,YACM;AACN,MAAI,WAAW,SAAS,cAAc;AAKpC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,SAAS,IAAI,WAAW,IAAI;AAC7C,MAAI,aAAa,QAAW;AAC1B,QAAK,aAA0B,YAAwB;AACrD;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR,kBAAkB,WAAW,IAAI;AAAA,IAEnC;AAAA,EACF;AACA,WAAS,IAAI,WAAW,MAAM,UAAyD;AACzF;AAGO,SAAS,0BACd,MAC8C;AAC9C,SAAO,SAAS,IAAI,IAAI;AAC1B;AAGO,SAAS,0BAAoC;AAClD,SAAO,CAAC,GAAG,SAAS,KAAK,CAAC;AAC5B;;;ACxNO,IAAM,uBAAuB;AAAA;AAAA,EAElC,kBAAkB;AAAA;AAAA,EAElB,wBAAwB;AAAA,EACxB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,eAAe;AAAA;AAAA,EAEf,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,gCAAgC;AAAA,EAChC,sBAAsB;AAAA,EACtB,4BAA4B;AAAA,EAC5B,4BAA4B;AAAA;AAAA,EAE5B,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EAErB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA,EAC3B,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,4BAA4B;AAAA,EAC5B,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA;AAAA,EAEpB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,kBAAkB;AAAA,EAClB,yBAAyB;AAAA,EACzB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA;AAAA,EAE3B,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,6BAA6B;AAAA,EAC7B,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,6BAA6B;AAAA,EAC7B,wBAAwB;AAAA,EACxB,6BAA6B;AAAA;AAAA,EAE7B,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,wBAAwB;AAAA,EACxB,8BAA8B;AAAA,EAC9B,iCAAiC;AAAA,EACjC,kCAAkC;AAAA,EAClC,kCAAkC;AAAA,EAClC,eAAe;AAAA,EACf,yBAAyB;AAAA,EACzB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,wBAAwB;AAAA,EACxB,6BAA6B;AAAA,EAC7B,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,8BAA8B;AAAA,EAC9B,4BAA4B;AAAA,EAC5B,sBAAsB;AAAA;AAAA,EAEtB,4BAA4B;AAAA,EAC5B,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,mCAAmC;AAAA,EACnC,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,6BAA6B;AAAA,EAC7B,6BAA6B;AAAA,EAC7B,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,2BAA2B;AAAA,EAC3B,wBAAwB;AAAA,EACxB,6BAA6B;AAAA,EAC7B,2BAA2B;AAAA,EAC3B,eAAe;AAAA,EACf,6BAA6B;AAAA,EAC7B,8BAA8B;AAAA,EAC9B,iCAAiC;AACnC;AAcO,SAAS,MACd,MACA,MACA,SACY;AACZ,SAAO,EAAE,MAAM,KAAK,IAAI,MAAM,GAAG,SAAS,MAAM,UAAU,qBAAqB,IAAI,EAAE;AACvF;AAEO,SAAS,SAAS,OAAsC;AAC7D,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAMO,SAAS,QAAQ,MAAe,MAAuC;AAC5E,MAAI,OAAgB;AACpB,aAAW,WAAW,MAAM;AAC1B,QAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,aAAO;AAAA,IACT;AACA,WAAQ,KAAsC,OAAO;AAAA,EACvD;AACA,SAAO;AACT;AAcO,SAAS,aAAa,MAAe,aAAmC;AAC7E,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,GAAG,EAAE;AACxB,QAAM,OAAO,QAAQ,MAAM,KAAK,MAAM,GAAG,EAAE,CAAC;AAC5C,QAAM,QACJ,UAAU,QACT,UAAU,UACT,OAAO,UAAU,YACjB,MAAM,QAAQ,IAAI,KAClB,QAAQ,KAAK;AACjB,SAAO,UAAU,YAAY,UAAU,SAAS,YAAY,SAAS;AACvE;AAGO,SAAS,QAAQ,OAAyB;AAC/C,SAAO,UAAU,UAAa,UAAU;AAC1C;AAUO,SAAS,YAAY,OAAyB;AACnD,SAAO,QAAQ,KAAK,KAAM,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM;AAC1E;AAGO,SAAS,YAAY,OAAyB;AACnD,SAAO,QAAQ,KAAK,KAAK,UAAU;AACrC;AAMO,SAAS,cAAc,OAAiC;AAC7D,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK;AAChE;AAGO,SAAS,WAAW,OAAyB;AAClD,SAAO,OAAO,UAAU,YAAY,QAAQ,OAAO,oBAAoB,OAAO,UAAU,KAAK;AAC/F;AAOO,SAAS,cAAc,OAAoB,MAA4B;AAC5E,QAAM,SAAuB,CAAC;AAC9B,MAAI,MAAM,kBAAkB,OAAO;AACjC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,eAAe;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,SAAS,MAAM;AACvB,WAAO,KAAK,MAAM,iBAAiB,CAAC,MAAM,GAAG,6BAA6B,IAAI,IAAI,CAAC;AAAA,EACrF;AACA,MAAI,YAAY,MAAM,EAAE,GAAG;AACzB,WAAO,KAAK,MAAM,eAAe,CAAC,IAAI,GAAG,yBAAyB,CAAC;AAAA,EACrE;AACA,MAAI,YAAY,MAAM,KAAK,GAAG;AAC5B,WAAO,KAAK,MAAM,kBAAkB,CAAC,OAAO,GAAG,cAAc,CAAC;AAAA,EAChE;AACA,SAAO;AACT;AAGO,SAAS,qBAAqB,OAAkC;AACrE,SAAO,YAAY,MAAM,eAAe,IACpC;AAAA,IACE;AAAA,MACE;AAAA,MACA,CAAC,iBAAiB;AAAA,MAClB;AAAA,IACF;AAAA,EACF,IACA,CAAC;AACP;AAEA,IAAM,oBAAwC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;AAOrD,SAAS,oBAAoB,OAAkC;AACpE,QAAM,SAAuB,CAAC;AAC9B,QAAM,YAAY,MAAM;AACxB,MAAI,CAAC,QAAQ,SAAS,KAAK,EAAE,OAAO,cAAc,YAAY,aAAa,KAAK,aAAa,IAAI;AAC/F,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,eAAe;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,MAAM,eAAe,KAAK,CAAC,kBAAkB,SAAS,MAAM,eAAe,GAAG;AACzF,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,iBAAiB;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,MAAM;AACvB,MAAI,SAAS,QAAQ,GAAG;AACtB,eAAW,OAAO,CAAC,WAAW,WAAW,GAAY;AACnD,YAAM,UAAU,SAAS,GAAG;AAC5B,UAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM,IAAI;AACxD,eAAO;AAAA,UACL,MAAM,kBAAkB,CAAC,YAAY,GAAG,GAAG,4CAA4C;AAAA,QACzF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,MAAM,aAAa,MAAM;AAC3B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,MAAM,KAAK,GAAG;AACzB,WAAO,KAAK,GAAG,WAAW,MAAM,KAAK,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAEA,IAAM,aACJ;AACF,IAAM,YACJ;AAQK,SAAS,WACd,OACA,SAAuC,CAAC,OAAO,GAC/C,SAAsD,aACxC;AACd,QAAM,SAAuB,CAAC;AAC9B,QAAM,KAAK,IAAI,SAA8B,CAAC,GAAG,QAAQ,GAAG,IAAI;AAChE,QAAM,SAAS,OAAO,UAAU,OAAO,EAAE,aAAa,KAAK,CAAC;AAG5D,QAAM,eAAe,OAAO,UACxB,CAAC,IACD,OAAO,MAAM,OAAO,OAAO,CAAC,UAAU,CAAC,aAAa,OAAO,KAAK,CAAC;AACrE,QAAM,UAAU,CAAC,UAAkB,aAAa,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,MAAM,KAAK;AAEvF,QAAM,YAAY,YAAY,MAAM,IAAI;AACxC,MAAI,WAAW;AACb,WAAO,KAAK,MAAM,uBAAuB,GAAG,MAAM,GAAG,oCAAoC,CAAC;AAAA,EAC5F;AAEA,MAAI,YAAY,MAAM,GAAG,GAAG;AAC1B,WAAO,KAAK,MAAM,sBAAsB,GAAG,KAAK,GAAG,oCAAoC,CAAC;AAAA,EAC1F,OAAO;AACL,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,YAAY,QAAW;AAGzB,aAAO;AAAA,QACL,MAAM,qBAAqB,GAAG,KAAK,GAAG,QAAQ,SAAS,WAAW,YAAY,UAAU;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,SAAS,WAAW,MAAM,SAAS;AAC1D,QAAM,MAAM,MAAM;AAClB,MAAK,YAAY,YAAY,GAAG,KAAO,OAAO,QAAQ,YAAY,IAAI,KAAK,MAAM,IAAK;AACpF,WAAO,KAAK,MAAM,sBAAsB,GAAG,KAAK,GAAG,sCAAsC,CAAC;AAAA,EAC5F;AAEA,QAAM,WAAW,MAAM;AACvB,MAAI,OAAO,aAAa,YAAY,SAAS,KAAK,MAAM,IAAI;AAE1D,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,GAAG,aAAa;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,QAAQ,aAAa,MAAM,QAAW;AAC/C,WAAO,KAAK,MAAM,qBAAqB,GAAG,aAAa,GAAG,UAAU,CAAC;AAAA,EACvE;AAEA,aAAW,eAAe,cAAc;AACtC,UAAM,QAAQ,YAAY,KAAK,CAAC;AAChC,QACE,UAAU,SACV,UAAU,SACV,UAAU,iBACT,UAAU,UAAU,WACrB;AACA;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,QACE,UAAU,aAAa,2BAA2B;AAAA,QAClD,GAAG,GAAG,YAAY,KAAK,IAAI,MAAM,CAAC;AAAA,QAClC,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,OAAO,UAA6B,QAAoC;AACtF,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,SAAS,WAAW;AAAA,EAC7B;AAIA,SAAO,OAAO,MAAM,CAAC,SAAS,UAAU,SAAS,KAAK,MAAM,OAAO;AACrE;AAGO,SAAS,QAAQ,MAAkC;AACxD,SAAO,KAAK,WAAW,IAAI,KAAK,KAAK,UAAU,KAAK,IAAI,MAAM,CAAC;AACjE;AAUO,SAAS,eAAe,UAAgE;AAC7F,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,SAAS,UAAU;AAC5B,QAAI,MAAM,KAAK,WAAW,GAAG;AAC3B,cAAQ,IAAI,EAAE;AAAA,IAChB;AACA,aAAS,SAAS,GAAG,UAAU,MAAM,KAAK,QAAQ,UAAU,GAAG;AAC7D,cAAQ,IAAI,QAAQ,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC,CAAC;AAAA,IAClD;AAAA,EACF;AACA,SAAO;AACT;;;AC1aO,IAAM,qBAA2D;AAAA,EACtE,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA,YAAY;AACd;AAEA,SAAS,oBAAoB,OAAkC;AAC7D,QAAM,SAAS,cAAc,OAAO,WAAW;AAC/C,QAAM,YAAY,SAAS,MAAM,SAAS,IACrC,MAAM,YACP;AAEJ,QAAM,aAAa,oBAAoB,SAAS;AAEhD,SAAO,KAAK,GAAG,gBAAgB,MAAM,YAAY,UAAU,CAAC;AAC5D,SAAO,KAAK,GAAG,yBAAyB,OAAO,UAAU,CAAC;AAC1D,SAAO,KAAK,GAAG,eAAe,MAAM,KAAK,CAAC;AAC1C,SAAO,KAAK,GAAG,mBAAmB,KAAK,CAAC;AACxC,SAAO,KAAK,GAAG,cAAc,OAAO,UAAU,CAAC;AAC/C,SAAO,KAAK,GAAG,WAAW,MAAM,KAAK,CAAC;AACtC,SAAO,KAAK,GAAG,eAAe,MAAM,SAAS,CAAC;AAC9C,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAQA,SAAS,sBAAsB,OAAyB;AACtD,MAAI,OAAO,UAAU,YAAY,aAAa,OAAO,+BAA+B,GAAG;AACrF,WAAO;AAAA,EACT;AACA,SAAO,YAAY,KAAK,KAAM,OAAO,UAAU,YAAY,mBAAmB,KAAK;AACrF;AAQA,SAAS,wBAAwB,UAAkD;AACjF,SAAO,SAAS,MAAM,GAAG,kCAAkC;AAC7D;AAEA,SAAS,gBAAgB,YAAqB,YAA+C;AAC3F,MAAI,sBAAsB,UAAU,GAAG;AACrC,WAAO,CAAC,MAAM,0BAA0B,CAAC,YAAY,GAAG,uBAAuB,CAAC;AAAA,EAClF;AACA,MAAI,OAAO,eAAe,UAAU;AAElC,WAAO,CAAC;AAAA,EACV;AACA,QAAM,WAAW,WAAW,QAAQ,UAAU;AAC9C,MAAI,SAAS,SAAS;AACpB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,YAAY;AAAA,QACb,iCAAiC,+BAA+B,kFAAkF,cAAc;AAAA,MAClK;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,eAAe,IAAI;AAC9B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,YAAY;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC;AACV;AAEA,SAAS,yBACP,OACA,YACc;AACd,QAAM,WAAW,MAAM;AACvB,MAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;AAC5B,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAuB,CAAC;AAC9B,MAAI,SAAS,SAAS,oCAAoC;AACxD,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,qBAAqB;AAAA,QACtB,WAAW,kCAAkC;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACA,aAAW,CAAC,OAAO,IAAI,KAAK,SAAS,QAAQ,GAAG;AAE9C,QAAI,OAAO,SAAS,UAAU;AAC5B;AAAA,IACF;AACA,QAAI,SAAS,oCAAoC;AAE/C,UAAI,SAAS,IAAI;AACf,eAAO,KAAK,wBAAwB,KAAK,CAAC;AAAA,MAC5C;AACA;AAAA,IACF;AACA,UAAM,WAAW,WAAW,QAAQ,IAAI;AACxC,QAAI,SAAS,SAAS;AACpB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,uBAAuB,KAAK;AAAA,UAC7B,uBAAuB,QAAQ,CAAC,mBAAmB,+BAA+B,kFAAkF,cAAc;AAAA,QACpL;AAAA,MACF;AAAA,IACF,WAAW,SAAS,eAAe,IAAI;AACrC,aAAO,KAAK,wBAAwB,KAAK,CAAC;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,MAAM,YAAY,GAAG,wBAAwB,QAAQ,CAAC,EACvE,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,IAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,EAAE,UAAU,EACjD,OAAO,CAAC,SAAyB,SAAS,QAAQ,SAAS,EAAE;AAChE,MAAI,IAAI,IAAI,UAAU,EAAE,SAAS,WAAW,QAAQ;AAClD,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,qBAAqB;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,wBAAwB,OAA2B;AAC1D,SAAO;AAAA,IACL;AAAA,IACA,CAAC,uBAAuB,KAAK;AAAA,IAC7B,uBAAuB,QAAQ,CAAC;AAAA,EAClC;AACF;AAGA,SAAS,eAAe,OAA8B;AACpD,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAuB,CAAC;AAC9B,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,YAAY,IAAI,KAAK,SAAS,SAAS;AAC1C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,SAAS,MAAM;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,MAAM,WAAW,GAAG;AAC/B,WAAO;AAAA,MACL,MAAM,2BAA2B,CAAC,SAAS,aAAa,GAAG,uBAAuB;AAAA,IACpF;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,0BACJ;AAWF,SAAS,mBAAmB,OAAkC;AAC5D,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,SAAS,IAAI,GAAG;AACnB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAuB,CAAC;AAC9B,QAAM,OAAO,KAAK;AAClB,QAAM,YAAY,CAAC,YAAY,IAAI,KAAK,SAAS;AACjD,MAAI,WAAW;AACb,WAAO;AAAA,MACL,MAAM,sBAAsB,CAAC,aAAa,MAAM,GAAG,mCAAmC;AAAA,IACxF;AAAA,EACF;AACA,SAAO;AAAA,IACL,GAAG;AAAA,MACD,YAAY,EAAE,GAAG,MAAM,MAAM,QAAQ,IAAI;AAAA,MACzC,CAAC,WAAW;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,WAAO;AAAA,MACL,MAAM,2BAA2B,CAAC,aAAa,aAAa,GAAG,uBAAuB;AAAA,IACxF;AAAA,EACF;AACA,MAAI,KAAK,aAAa,QAAW;AAC/B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,aAAa,UAAU;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AACpB,MAAI,QAAQ,KAAK,GAAG;AAClB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,OAAO;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,MAAM,QAAQ,KAAK,MAAM,SAAS,aAAa,QAAW;AACrE,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,WAAW;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,QAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAC7E,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,aAAa,KAAK;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,cAAc,OAAoB,YAA+C;AACxF,QAAM,aAAa;AAAA,IACjB,MAAM;AAAA,IACN,GAAI,MAAM,QAAQ,MAAM,mBAAmB,IACvC,wBAAwB,MAAM,mBAAmB,IACjD,CAAC;AAAA,EACP,EACG,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,IAAI,CAAC,SAAS,WAAW,QAAQ,IAAI,CAAC;AACzC,QAAM,QAAmD;AAAA,IACvD,CAAC,CAAC,OAAO,GAAG,MAAM,KAAK;AAAA,IACvB,CAAC,CAAC,SAAS,KAAK,GAAG,SAAS,MAAM,KAAK,IAAI,MAAM,MAAM,MAAM,MAAS;AAAA,IACtE,CAAC,CAAC,aAAa,KAAK,GAAG,SAAS,MAAM,SAAS,IAAI,MAAM,UAAU,MAAM,MAAS;AAAA,EACpF;AACA,QAAM,SAAuB,CAAC;AAC9B,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO;AAChC,QAAI,OAAO,SAAS,UAAU;AAC5B;AAAA,IACF;AACA,UAAM,EAAE,SAAS,MAAM,IAAI,WAAW,MAAM,IAAI;AAChD,QAAI,SAAS;AACX,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA;AAAA,UACA,GAAG,KAAK,CAAC,MAAM,UAAU,cAAc,iBAAiB,0DAA0D,cAAc;AAAA,QAClI;AAAA,MACF;AAAA,IACF,WAAW,WAAW,KAAK,CAAC,cAAc,iBAAiB,OAAO,SAAS,CAAC,GAAG;AAC7E,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA;AAAA,UACA,KAAK,CAAC,MAAM,UACR,6HACA;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAA8B;AAChD,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,OAAO,MAAM;AACnB,MAAI,YAAY,IAAI,GAAG;AACrB,WAAO,CAAC,MAAM,0BAA0B,CAAC,SAAS,MAAM,GAAG,6BAA6B,CAAC;AAAA,EAC3F;AACA,MAAI,SAAS,qBAAqB;AAChC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,SAAS,MAAM;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC;AACV;AAOA,SAAS,eAAe,WAAkC;AACxD,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,CAAC;AAAA,EACV;AACA,MAAI,CAAC,SAAS,SAAS,GAAG;AACxB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,WAAW;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAuB,CAAC;AAC9B,QAAM,QAAQ,UAAU;AACxB,MAAI,MAAM,QAAQ,KAAK,GAAG;AAIxB,QAAI,MAAM,SAAS,4BAA4B;AAC7C,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,aAAa,cAAc;AAAA,UAC5B,+BAA+B,0BAA0B;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AACA,eAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC3C,UAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,MACF;AACA,YAAM,OAAO,CAAC,aAAa,gBAAgB,KAAK;AAChD,YAAM,OAAO,KAAK;AAClB,UAAI,YAAY,IAAI,KAAM,OAAO,SAAS,YAAY,kBAAkB,IAAI,MAAM,IAAK;AACrF,eAAO;AAAA,UACL;AAAA,YACE;AAAA,YACA,CAAC,GAAG,MAAM,MAAM;AAAA,YAChB,iBAAiB,QAAQ,CAAC;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AACA,YAAM,KAAK,KAAK;AAChB,UAAI,YAAY,EAAE,KAAM,OAAO,OAAO,YAAY,uBAAuB,EAAE,MAAM,IAAK;AACpF,eAAO;AAAA,UACL;AAAA,YACE;AAAA,YACA,CAAC,GAAG,MAAM,IAAI;AAAA,YACd,iBAAiB,QAAQ,CAAC;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,yBAAyB,UAAU,WAAW,EAAE,aAAa,KAAK,CAAC;AAClF,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,UAAU,eAAe,MAAM;AACrC,eAAW,eAAe,OAAO,MAAM,QAAQ;AAC7C,YAAM,OAAO,CAAC,aAAa,GAAG,YAAY,KAAK,IAAI,MAAM,CAAC;AAG1D,UAAI,aAAa,WAAW,WAAW,KAAK,QAAQ,IAAI,QAAQ,IAAI,CAAC,GAAG;AACtE;AAAA,MACF;AACA,aAAO,KAAK,MAAM,wBAAwB,MAAM,YAAY,OAAO,CAAC;AACpE,iBAAW,OAAO,eAAe,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG;AAC5C,gBAAQ,IAAI,GAAG;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC3ZO,IAAM,2BAAuE;AAAA,EAClF,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,QAAQ,CAAC;AAAA,IACT,iBAAiB;AAAA,EACnB;AAAA,EACA,YAAY;AACd;AAEA,SAAS,0BAA0B,OAAkC;AACnE,QAAM,SAAS,cAAc,OAAO,oBAAoB;AACxD,QAAM,UAAU,MAAM;AACtB,MAAI,YAAY,OAAO,GAAG;AACxB,WAAO,KAAK,MAAM,wBAAwB,CAAC,SAAS,GAAG,oBAAoB,CAAC;AAAA,EAC9E;AACA,SAAO,KAAK,GAAG,qBAAqB,KAAK,CAAC;AAE1C,QAAM,SAAS,QAAQ,MAAM,MAAM,IAAI,CAAC,IAAI,MAAM;AAClD,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,QAAQ;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,UAAM,MAAgB,CAAC;AACvB,eAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,GAAG;AAC7C,UAAI,CAAC,SAAS,KAAK,GAAG;AACpB;AAAA,MACF;AACA,YAAM,OAAO,OAAO,MAAM,OAAO,YAAY,MAAM,OAAO,KAAK,IAAI,MAAM,EAAE,MAAM,QAAQ;AACzF,UAAI,YAAY,MAAM,EAAE,GAAG;AACzB,eAAO;AAAA,UACL,MAAM,yBAAyB,CAAC,UAAU,OAAO,IAAI,GAAG,SAAS,QAAQ,CAAC,aAAa;AAAA,QACzF;AAAA,MACF,WAAW,OAAO,MAAM,OAAO,UAAU;AACvC,YAAI,KAAK,MAAM,EAAE;AAAA,MACnB;AAEA,YAAM,UAAU,QAAQ,MAAM,eAAe,IAAI,CAAC,IAAI,MAAM;AAC5D,UAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,YAAI,QAAQ,WAAW,GAAG;AACxB,iBAAO;AAAA,YACL;AAAA,cACE;AAAA,cACA,CAAC,UAAU,OAAO,iBAAiB;AAAA,cACnC,oCAAoC,IAAI;AAAA,YAC1C;AAAA,UACF;AAAA,QACF;AACA,mBAAW,CAAC,UAAU,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAElD,cAAI,OAAO,WAAW,YAAY,OAAO,KAAK,MAAM,IAAI;AACtD,mBAAO;AAAA,cACL;AAAA,gBACE;AAAA,gBACA,CAAC,UAAU,OAAO,mBAAmB,QAAQ;AAAA,gBAC7C,gCAAgC,IAAI;AAAA,cACtC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,MAAM,KAAK,GAAG;AACzB,eAAO,KAAK,GAAG,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF;AAGA,QAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM,IAAI;AACxD,aAAO,KAAK,GAAG,aAAa,SAAS,QAAQ,GAAG,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAUA,SAAS,WAAW,OAA0C,OAA6B;AACzF,QAAM,SAAuB,CAAC;AAC9B,QAAM,KAAK,IAAI,SAA8B,CAAC,UAAU,OAAO,SAAS,GAAG,IAAI;AAE/E,QAAM,YAAY,MAAM;AACxB,MAAI,CAAC,QAAQ,SAAS,KAAK,EAAE,cAAc,SAAS,KAAK,aAAa,IAAI;AACxE,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,GAAG,aAAa;AAAA,QAChB,WAAW,SAAS,IAChB,iCACA;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACrB,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM,CAAC,cAAc,MAAM,GAAG;AACzE,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,GAAG,QAAQ;AAAA,QACX,IAAI,MAAM;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,sBAAsB,UAAU,OAAO,EAAE,aAAa,KAAK,CAAC;AAC3E,MAAI,CAAC,OAAO,SAAS;AACnB,eAAW,eAAe,OAAO,MAAM,QAAQ;AAG7C,UAAI,YAAY,KAAK,CAAC,MAAM,iBAAiB,aAAa,OAAO,WAAW,GAAG;AAC7E;AAAA,MACF;AACA,aAAO;AAAA,QACL,MAAM,qBAAqB,GAAG,GAAG,YAAY,KAAK,IAAI,MAAM,CAAC,GAAG,YAAY,OAAO;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,cAAc,KAAsB;AAC3C,MAAI;AACF,SAAK,oBAAoB,GAAG;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,SAAiB,QAA4B,KAAwB;AACzF,QAAM,SAAuB,CAAC;AAC9B,QAAM,eAAe,oBAAI,IAAoB;AAC7C,aAAW,SAAS,QAAQ,SAAS,cAAc,GAAG;AACpD,UAAM,KAAK,MAAM,CAAC;AAClB,iBAAa,IAAI,KAAK,aAAa,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EACtD;AACA,QAAM,WAAW,oBAAI,IAAoB;AACzC,aAAW,MAAM,KAAK;AACpB,aAAS,IAAI,KAAK,SAAS,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EAC9C;AAEA,aAAW,CAAC,IAAI,KAAK,KAAK,UAAU;AAClC,QAAI,QAAQ,GAAG;AACb,aAAO;AAAA,QACL,MAAM,0BAA0B,CAAC,SAAS,GAAG,mCAAmC,EAAE,IAAI;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACA,aAAW,CAAC,IAAI,KAAK,KAAK,cAAc;AACtC,QAAI,QAAQ,GAAG;AACb,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,KAAK,EAAE;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,SAAS,IAAI,EAAE,GAAG;AACrB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,qBAAqB,EAAE;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,MAAM,SAAS,KAAK,GAAG;AAChC,QAAI,CAAC,aAAa,IAAI,EAAE,GAAG;AACzB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,UAAU,EAAE,kCAAkC,EAAE;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAMA,MAAI,OAAO,WAAW,KAAK,CAAC,cAAc,cAAc,MAAM,GAAG;AAC/D,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,SAAS;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,cAAc,cAA2C,QAA4B;AAC5F,QAAM,WAAW,OAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,MAAM,EAAE;AAChE,MAAI,IAAI,IAAI,QAAQ,EAAE,SAAS,SAAS,QAAQ;AAC9C,WAAO;AAAA,EACT;AACA,MAAI,aAAa,SAAS,SAAS,QAAQ;AACzC,WAAO;AAAA,EACT;AACA,SAAO,SAAS,MAAM,CAAC,OAAO,OAAO,OAAO,YAAY,aAAa,IAAI,EAAE,MAAM,CAAC;AACpF;;;ACrPA,IAAM,cAAc;AAiBb,IAAM,qBAA2D;AAAA,EACtE,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM,CAAC;AAAA,IACP,iBAAiB;AAAA,EACnB;AAAA,EACA,YAAY;AACd;AAEA,SAAS,oBAAoB,OAAkC;AAC7D,QAAM,SAAS,cAAc,OAAO,YAAY;AAChD,QAAM,UAAU,MAAM;AACtB,MAAI,YAAY,OAAO,GAAG;AACxB,WAAO,KAAK,MAAM,uBAAuB,CAAC,SAAS,GAAG,oBAAoB,CAAC;AAAA,EAC7E;AACA,SAAO,KAAK,GAAG,qBAAqB,KAAK,CAAC;AAE1C,MAAI,CAAC,QAAQ,MAAM,YAAY,KAAK,MAAM,iBAAiB,YAAY;AACrE,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,cAAc;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI,CAAC,IAAI,MAAM;AAChD,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,KAAK,GAAG,WAAW,OAAO,OAAO,CAAC;AAAA,EAC3C;AAEA,QAAM,OAAO,QAAQ,MAAM,IAAI,IAAI,CAAC,IAAI,MAAM;AAC9C,QAAM,SAAmB,CAAC;AAC1B,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,MAAM;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK,GAAG,UAAU,MAAM,OAAO,SAAS,MAAM,CAAC;AAGtD,QAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM,IAAI;AACxD,aAAO,KAAK,GAAGC,cAAa,SAAS,MAAM,MAAM,CAAC;AAAA,IACpD;AAAA,EACF;AAEA,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAGA,SAAS,WAAW,OAA2B,SAAoC;AACjF,QAAM,SAAuB,CAAC;AAC9B,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,CAAC,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC3C,QAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,IACF;AACA,QAAI,YAAY,KAAK,EAAE,GAAG;AACxB,aAAO;AAAA,QACL,MAAM,uBAAuB,CAAC,SAAS,OAAO,IAAI,GAAG,aAAa,QAAQ,CAAC,aAAa;AAAA,MAC1F;AAAA,IACF,WAAW,OAAO,KAAK,OAAO,UAAU;AACtC,UAAI,QAAQ,IAAI,KAAK,EAAE,GAAG;AACxB,iBAAS,IAAI,KAAK,EAAE;AAAA,MACtB;AACA,cAAQ,IAAI,KAAK,EAAE;AAAA,IACrB;AACA,UAAM,OAAO,OAAO,KAAK,OAAO,YAAY,KAAK,OAAO,KAAK,IAAI,KAAK,EAAE,MAAM,QAAQ;AACtF,WAAO,KAAK,GAAG,gBAAgB,KAAK,SAAS,CAAC,SAAS,OAAO,SAAS,GAAG,aAAa,IAAI,EAAE,CAAC;AAAA,EAChG;AACA,aAAW,MAAM,UAAU;AACzB,WAAO;AAAA,MACL,MAAM,wBAAwB,CAAC,OAAO,GAAG,uCAAuC,EAAE,IAAI;AAAA,IACxF;AAAA,EACF;AACA,SAAO;AACT;AASA,SAAS,UACP,MACA,OACA,SACA,QACc;AACd,QAAM,SAAuB,CAAC;AAC9B,QAAM,SAAuB,CAAC;AAC9B,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,OAAO,oBAAI,IAAY;AAE7B,aAAW,CAAC,OAAO,GAAG,KAAK,KAAK,QAAQ,GAAG;AACzC,QAAI,CAAC,SAAS,GAAG,GAAG;AAClB;AAAA,IACF;AACA,UAAM,UAAU,QAAQ;AACxB,UAAM,OAAO,OAAO,IAAI,OAAO,YAAY,IAAI,OAAO,KAAK,IAAI,IAAI,EAAE,MAAM;AAE3E,QAAI,YAAY,IAAI,EAAE,GAAG;AACvB,aAAO,KAAK,MAAM,sBAAsB,CAAC,QAAQ,OAAO,IAAI,GAAG,OAAO,OAAO,aAAa,CAAC;AAAA,IAC7F,WAAW,OAAO,IAAI,OAAO,UAAU;AACrC,UAAI,KAAK,IAAI,IAAI,EAAE,GAAG;AACpB,iBAAS,IAAI,IAAI,EAAE;AAAA,MACrB;AACA,WAAK,IAAI,IAAI,EAAE;AACf,aAAO,KAAK,IAAI,EAAE;AAAA,IACpB;AAEA,UAAM,SAAS,CAAC,QAAQ,IAAI,OAAO;AACnC,UAAM,UAAU,CAAC,YAAY,IAAI,MAAM;AAKvC,UAAM,YAAY,OAAO,IAAI,WAAW,YAAY,IAAI,OAAO,KAAK,MAAM;AAC1E,QAAI,WAAW;AACb,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,QAAQ,OAAO,QAAQ;AAAA,UACxB,8BAA8B,IAAI;AAAA,QACpC;AAAA,MACF;AAAA,IACF,WAAW,UAAU,SAAS;AAC5B,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,MAAM;AAAA,UACP,OAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF,WAAW,CAAC,UAAU,CAAC,SAAS;AAC9B,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,MAAM;AAAA,UACP,YAAY,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,WAAW,OAAO,IAAI,WAAW,YAAY,CAAC,QAAQ,IAAI,IAAI,MAAM,GAAG;AACzE,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,MAAM;AAAA,UACP,OAAO,IAAI,wBAAwB,IAAI,MAAM;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAMA,QAAI,QAAQ;AACV,aAAO,KAAK,GAAG,gBAAgB,IAAI,SAAS,CAAC,QAAQ,OAAO,SAAS,GAAG,OAAO,IAAI,EAAE,CAAC;AAAA,IACxF;AAEA,WAAO,KAAK,GAAG,eAAe,KAAK,OAAO,MAAM,OAAO,OAAO,CAAC;AAAA,EACjE;AAEA,aAAW,MAAM,UAAU;AACzB,WAAO,KAAK,MAAM,uBAAuB,CAAC,MAAM,GAAG,iCAAiC,EAAE,IAAI,CAAC;AAAA,EAC7F;AACA,SAAO,CAAC,GAAG,QAAQ,GAAG,MAAM;AAC9B;AAGA,SAAS,eACP,KACA,OACA,MACA,OACA,SACc;AACd,QAAM,MAAM,IAAI;AAChB,MAAI,YAAY,GAAG,GAAG;AAMpB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,QAAQ,OAAO,iBAAiB;AAAA,QACjC,mCAAmC,IAAI;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,UAAU;AAE3B,WAAO,CAAC;AAAA,EACV;AACA,QAAM,UAAUC,gBAAe,KAAK,OAAO,OAAO;AAClD,MAAI,YAAY,QAAW;AAGzB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,UAAU,QAAQ,KAAK,CAAC,WAAW,SAAS,MAAM,KAAK,OAAO,OAAO,GAAG;AAC9E,SAAO,UACH,CAAC,IACD;AAAA,IACE;AAAA,MACE;AAAA,MACA,CAAC,MAAM;AAAA,MACP,OAAO,IAAI,0BAA0B,GAAG;AAAA,IAC1C;AAAA,EACF;AACN;AAGA,SAASA,gBACP,KACA,OACA,SACgC;AAChC,QAAM,SAAS,CAAC,QAAQ,IAAI,OAAO;AACnC,QAAM,UAAU,CAAC,YAAY,IAAI,MAAM;AACvC,MAAI,WAAW,SAAS;AACtB,WAAO;AAAA,EACT;AACA,MAAI,QAAQ;AACV,WAAO,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,UAAU;AAAA,EACpD;AACA,MAAI,OAAO,IAAI,WAAW,YAAY,CAAC,QAAQ,IAAI,IAAI,MAAM,KAAK,CAAC,MAAM,QAAQ,KAAK,GAAG;AACvF,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,KAAK,CAAC,UAAU,SAAS,KAAK,KAAK,MAAM,OAAO,IAAI,MAAM;AAC7E,SAAO,SAAS,IAAI,KAAK,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU;AACxE;AAGA,SAAS,gBACP,SACA,MACA,OACc;AACd,QAAM,SAAuB,CAAC;AAC9B,QAAM,OAAO,QAAQ,OAAO,IAAI,CAAC,IAAI;AACrC,MAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,SAAS,aAAa;AAC7B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA;AAAA,QACA,QAAQ,KAAK,aAAa,WAAW;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAAW,oBAAI,IAAY;AACjC,aAAW,CAAC,OAAO,MAAM,KAAK,KAAK,QAAQ,GAAG;AAC5C,QAAI,CAAC,SAAS,MAAM,GAAG;AACrB;AAAA,IACF;AACA,QAAI,YAAY,OAAO,EAAE,GAAG;AAC1B,aAAO;AAAA,QACL,MAAM,yBAAyB,CAAC,GAAG,MAAM,OAAO,IAAI,GAAG,UAAU,QAAQ,CAAC,aAAa;AAAA,MACzF;AAAA,IACF,WAAW,OAAO,OAAO,OAAO,UAAU;AACxC,UAAI,KAAK,IAAI,OAAO,EAAE,GAAG;AACvB,iBAAS,IAAI,OAAO,EAAE;AAAA,MACxB;AACA,WAAK,IAAI,OAAO,EAAE;AAAA,IACpB;AACA,QAAI,YAAY,OAAO,IAAI,GAAG;AAC5B,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,OAAO,MAAM;AAAA,UACvB,4BAA4B,QAAQ,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,MAAM,UAAU;AACzB,WAAO,KAAK,MAAM,0BAA0B,MAAM,oCAAoC,EAAE,IAAI,CAAC;AAAA,EAC/F;AACA,SAAO;AACT;AAQA,SAASD,cACP,SACA,MACA,QACc;AACd,QAAM,SAAuB,CAAC;AAC9B,QAAM,eAAe,oBAAI,IAAoB;AAC7C,aAAW,SAAS,QAAQ,SAAS,cAAc,GAAG;AACpD,UAAM,KAAK,MAAM,CAAC;AAClB,iBAAa,IAAI,KAAK,aAAa,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EACtD;AACA,QAAM,MAAM,IAAI,IAAI,MAAM;AAE1B,aAAW,CAAC,IAAI,KAAK,KAAK,cAAc;AACtC,QAAI,QAAQ,GAAG;AACb,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,KAAK,EAAE;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,IAAI,IAAI,EAAE,GAAG;AAChB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,qBAAqB,EAAE;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,MAAM,KAAK;AACpB,QAAI,CAAC,aAAa,IAAI,EAAE,GAAG;AACzB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,QAAQ,EAAE,kCAAkC,EAAE;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIA,MAAI,OAAO,WAAW,KAAK,CAACE,eAAc,cAAc,IAAI,GAAG;AAC7D,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,SAAS;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAASA,eAAc,cAA2C,MAA0B;AAC1F,QAAM,MAAM,KAAK,OAAO,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;AACrD,MAAI,IAAI,IAAI,GAAG,EAAE,SAAS,IAAI,UAAU,aAAa,SAAS,IAAI,QAAQ;AACxE,WAAO;AAAA,EACT;AACA,SAAO,IAAI,MAAM,CAAC,OAAO,OAAO,OAAO,YAAY,aAAa,IAAI,EAAE,MAAM,CAAC;AAC/E;;;ACtYA,IAAM,cAAc;AACpB,IAAM,cAAc;AAeb,IAAM,0BAAqE;AAAA,EAChF,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP,EAAE,IAAI,MAAM,GAAG,MAAM,IAAI,WAAW,MAAM;AAAA,MAC1C,EAAE,IAAI,MAAM,GAAG,MAAM,IAAI,WAAW,MAAM;AAAA,IAC5C;AAAA,EACF;AAAA,EACA,YAAY;AACd;AAEA,SAAS,yBAAyB,OAAkC;AAClE,QAAM,SAAS,cAAc,OAAO,iBAAiB;AACrD,MAAI,YAAY,MAAM,QAAQ,GAAG;AAC/B,WAAO,KAAK,MAAM,wBAAwB,CAAC,UAAU,GAAG,qBAAqB,CAAC;AAAA,EAChF;AACA,MAAI,YAAY,MAAM,IAAI,GAAG;AAC3B,WAAO;AAAA,MACL,MAAM,oBAAoB,CAAC,MAAM,GAAG,uDAAuD;AAAA,IAC7F;AAAA,EACF;AACA,SAAO,KAAK,GAAG,qBAAqB,KAAK,CAAC;AAE1C,QAAM,UAAU,QAAQ,MAAM,OAAO,IAAI,CAAC,IAAI,MAAM;AACpD,MAAI,MAAM,QAAQ,OAAO,GAAG;AAG1B,QAAI,QAAQ,SAAS,aAAa;AAChC,aAAO,KAAK,MAAM,sBAAsB,CAAC,SAAS,GAAG,2BAA2B,CAAC;AAAA,IACnF;AACA,QAAI,QAAQ,SAAS,aAAa;AAChC,aAAO;AAAA,QACL,MAAM,uBAAuB,CAAC,SAAS,GAAG,oBAAoB,WAAW,WAAW;AAAA,MACtF;AAAA,IACF;AACA,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,WAAW,oBAAI,IAAY;AACjC,QAAI,UAAU;AACd,eAAW,CAAC,OAAO,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC/C,UAAI,CAAC,SAAS,MAAM,GAAG;AACrB;AAAA,MACF;AACA,YAAM,UAAU,QAAQ;AACxB,UAAI,YAAY,OAAO,EAAE,GAAG;AAC1B,eAAO;AAAA,UACL,MAAM,yBAAyB,CAAC,WAAW,OAAO,IAAI,GAAG,UAAU,OAAO,aAAa;AAAA,QACzF;AAAA,MACF,WAAW,OAAO,OAAO,OAAO,UAAU;AACxC,YAAI,KAAK,IAAI,OAAO,EAAE,GAAG;AACvB,mBAAS,IAAI,OAAO,EAAE;AAAA,QACxB;AACA,aAAK,IAAI,OAAO,EAAE;AAAA,MACpB;AACA,UAAI,YAAY,OAAO,IAAI,GAAG;AAC5B,eAAO;AAAA,UACL;AAAA,YACE;AAAA,YACA,CAAC,WAAW,OAAO,MAAM;AAAA,YACzB,4BAA4B,OAAO;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAIA,UAAI,QAAQ,OAAO,SAAS,GAAG;AAC7B,eAAO;AAAA,UACL;AAAA,YACE;AAAA,YACA,CAAC,WAAW,OAAO,WAAW;AAAA,YAC9B,sBAAsB,OAAO;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AACA,UAAI,OAAO,cAAc,MAAM;AAC7B,mBAAW;AAAA,MACb;AACA,UAAI,SAAS,OAAO,KAAK,GAAG;AAC1B,eAAO,KAAK,GAAG,iBAAiB,OAAO,OAAO,OAAO,OAAO,CAAC;AAAA,MAC/D;AAAA,IACF;AACA,eAAW,MAAM,UAAU;AACzB,aAAO;AAAA,QACL,MAAM,0BAA0B,CAAC,SAAS,GAAG,oCAAoC,EAAE,IAAI;AAAA,MACzF;AAAA,IACF;AACA,QAAI,YAAY,GAAG;AACjB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV,MAAM,SAAS,UACX,yCACA;AAAA,QACN;AAAA,MACF;AAAA,IACF,WAAW,MAAM,SAAS,YAAY,UAAU,GAAG;AACjD,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,SAAS;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAYA,SAAS,iBACP,OACA,OACA,SACc;AACd,QAAM,OAAO,CAAC,WAAW,OAAO,OAAO;AACvC,QAAM,OAAO,MAAM;AACnB,MAAI,SAAS,WAAW,SAAS,SAAS;AACxC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,GAAG,MAAM,MAAM;AAAA,QAChB,UAAU,OAAO,YAAY,SAAS,UAAU,uBAAuB,SAAS;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACA,SAAO,WAAW,OAAO,MAAM,+BAA+B;AAChE;;;ACzIO,IAAM,qBAA2D;AAAA,EACtE,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,WAAW,EAAE,YAAY,EAAE;AAAA,IAC3B,SAAS,EAAE,YAAY,CAAC,EAAE;AAAA,EAC5B;AAAA,EACA,YAAY;AACd;AAEA,SAAS,oBAAoB,OAAkC;AAC7D,QAAM,SAAS,cAAc,OAAO,YAAY;AAChD,SAAO,KAAK,GAAG,mBAAmB,MAAM,aAAa,CAAC;AACtD,SAAO,KAAK,GAAG,YAAY,MAAM,MAAM,CAAC;AACxC,SAAO,KAAK,GAAGC,gBAAe,MAAM,SAAS,CAAC;AAC9C,SAAO,KAAK,GAAG,gBAAgB,MAAM,OAAO,CAAC;AAC7C,SAAO,KAAK,GAAG,oBAAoB,MAAM,KAAK,CAAC;AAC/C,SAAO,KAAK,GAAGC,oBAAmB,KAAK,CAAC;AACxC,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAEA,SAAS,mBAAmB,eAAsC;AAChE,MAAI,YAAY,aAAa,GAAG;AAC9B,WAAO;AAAA,MACL,MAAM,8BAA8B,CAAC,eAAe,GAAG,kCAAkC;AAAA,IAC3F;AAAA,EACF;AACA,MAAI,OAAO,kBAAkB,UAAU;AAErC,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAuB,CAAC;AAG9B,QAAM,WAAW,oBAAoB,MAAS,EAAE,QAAQ,aAAa;AACrE,MAAI,SAAS,SAAS;AACpB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,eAAe;AAAA,QAChB,2BAA2B,+BAA+B;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,eAAe,IAAI;AAC9B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,eAAe;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,uBAAuB,aAAa,GAAG;AACzC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,eAAe;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAA+B;AAClD,MAAI,YAAY,MAAM,GAAG;AACvB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,QAAQ;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,WAAW,YAAY,oBAAoB,KAAK,MAAM,GAAG;AAClE,WAAO,CAAC;AAAA,EACV;AACA,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAASD,gBAAe,WAAkC;AACxD,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,CAAC,mBAAmB,CAAC;AAAA,EAC9B;AACA,MAAI,CAAC,SAAS,SAAS,GAAG;AACxB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAuB,CAAC;AAC9B,QAAM,MAAM,UAAU;AACtB,MAAI,QAAQ,GAAG,KAAK,QAAQ,GAAG;AAC7B,WAAO,KAAK,mBAAmB,CAAC;AAAA,EAClC,WACE,OAAO,QAAQ,YACf,EAAE,OAAO,SAAS,GAAG,KAAK,MAAM,KAAK,OAAO,yBAC5C;AACA,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,aAAa,YAAY;AAAA,QAC1B,qBAAqB,sBAAsB;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,UAAU;AACtB,MAAI,OAAO,QAAQ,YAAY,EAAE,OAAO,SAAS,GAAG,KAAK,OAAO,IAAI;AAClE,WAAO,KAAK,qBAAqB,sDAAsD,CAAC;AAAA,EAC1F,WAAW,OAAO,QAAQ,YAAY,OAAO,QAAQ,YAAY,MAAM,KAAK,OAAO,KAAK;AACtF,WAAO,KAAK,qBAAqB,qDAAqD,CAAC;AAAA,EACzF;AACA,QAAM,QAAQ,UAAU;AACxB,MACE,OAAO,UAAU,YACjB,EAAE,cAAc,KAAK,KAAK,SAAS,KAAK,SAAS,uBACjD;AACA,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,aAAa,UAAU;AAAA,QACxB,mEAAmE,oBAAoB;AAAA,MACzF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,qBAAiC;AACxC,SAAO;AAAA,IACL;AAAA,IACA,CAAC,aAAa,YAAY;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,SAA6B;AACzD,SAAO,MAAM,+BAA+B,CAAC,aAAa,YAAY,GAAG,OAAO;AAClF;AAEA,IAAM,kBAAsC;AAG5C,SAAS,gBAAgB,SAAgC;AACvD,MAAI,QAAQ,OAAO,GAAG;AACpB,WAAO,CAAC,mBAAmB,CAAC;AAAA,EAC9B;AACA,MAAI,CAAC,SAAS,OAAO,GAAG;AACtB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,aAAa,QAAQ;AAC3B,MAAI,QAAQ,UAAU,GAAG;AACvB,WAAO,CAAC,mBAAmB,CAAC;AAAA,EAC9B;AACA,MAAI,CAAC,MAAM,QAAQ,UAAU,GAAG;AAC9B,WAAO,CAAC;AAAA,EACV;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,CAAC,mBAAmB,CAAC;AAAA,EAC9B;AAEA,QAAM,SAAuB,CAAC;AAC9B,QAAM,OAAO,oBAAI,IAAa;AAI9B,MAAI,oBAAoB;AACxB,MAAI,cAAc;AAClB,aAAW,CAAC,OAAO,SAAS,KAAK,WAAW,QAAQ,GAAG;AACrD,QAAI,CAAC,SAAS,SAAS,GAAG;AACxB,0BAAoB;AACpB;AAAA,IACF;AACA,UAAM,OAAO,CAAC,WAAW,cAAc,KAAK;AAC5C,UAAM,OAAO,UAAU;AACvB,QAAI,CAAC,gBAAgB,SAAS,IAAI,GAAG;AACnC,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,MAAM;AAAA,UAChB,yBAAyB,QAAQ,CAAC,cAAc,sBAAsB,KAAK,IAAI,CAAC;AAAA,QAClF;AAAA,MACF;AAAA,IACF,WAAW,KAAK,IAAI,IAAI,GAAG;AACzB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,MAAM;AAAA,UAChB,IAAI,OAAO,IAAI,CAAC;AAAA,QAClB;AAAA,MACF;AAAA,IACF,OAAO;AACL,WAAK,IAAI,IAAI;AAAA,IACf;AACA,UAAM,SAAS,UAAU;AACzB,QACE,OAAO,WAAW,YAClB,OAAO,SAAS,MAAM,KACtB,UAAU,KACV,UAAU,iCACV;AACA,qBAAe;AAAA,IACjB,OAAO;AACL,0BAAoB;AACpB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,QAAQ;AAAA,UAClB,kBAAkB,QAAQ,CAAC,iCAAiC,+BAA+B;AAAA,QAC7F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,qBAAqB,gBAAgB,GAAG;AAC1C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,WAAW,YAAY;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,qBAAiC;AACxC,SAAO;AAAA,IACL;AAAA,IACA,CAAC,WAAW,YAAY;AAAA,IACxB;AAAA,EACF;AACF;AAGA,SAAS,oBAAoB,OAA8B;AACzD,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,OAAO,MAAM;AACnB,SAAO,YAAY,IAAI,KAAK,SAAS,UACjC,CAAC,IACD;AAAA,IACE;AAAA,MACE;AAAA,MACA,CAAC,SAAS,MAAM;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACN;AAgBA,SAASC,oBAAmB,OAAkC;AAC5D,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,SAAS,IAAI,GAAG;AACnB,WAAO,CAAC;AAAA,EACV;AACA,QAAM,OAAO,KAAK;AAClB,QAAM,YAAY,CAAC,YAAY,IAAI,KAAK,SAAS;AACjD,QAAM,SAAuB,YACzB,CAAC,MAAM,iBAAiB,CAAC,aAAa,MAAM,GAAG,yCAAyC,CAAC,IACzF,CAAC;AACL,SAAO;AAAA,IACL,GAAG;AAAA,MACD,YAAY,EAAE,GAAG,MAAM,MAAM,QAAQ,IAAI;AAAA,MACzC,CAAC,WAAW;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AACpB,MAAI,QAAQ,KAAK,GAAG;AAClB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,OAAO;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,MAAM,QAAQ,KAAK,MAAM,SAAS,aAAa,QAAW;AACrE,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,WAAW;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,QAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAC7E,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,aAAa,KAAK;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC5UO,IAAM,2BAAuE;AAAA,EAClF,aAAa,CAAC,EAAE,MAAM,OAAO;AAAA,IAC3B,eAAe;AAAA,IACf,MAAM;AAAA,IACN,IAAI,MAAM;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,UAAU;AAAA,EACZ;AAAA,EACA,YAAY;AACd;AAEA,SAAS,0BAA0B,OAAkC;AACnE,QAAM,SAAS,cAAc,OAAO,kBAAkB;AACtD,MAAI,YAAY,MAAM,MAAM,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,QAAQ;AAAA,QACT,YAAY,MAAM,UAAU,IACxB,sBACA;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,MAAM;AAClB,QAAM,MAAM,MAAM;AAClB,MAAI,QAAQ,GAAG,GAAG;AAChB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,EAAE,cAAc,GAAG,KAAK,OAAO,IAAI;AAC5C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU;AAAA,QACX,WAAW,GAAG,IACV,yCACA;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,MAAI,QAAQ,GAAG,KAAK,QAAQ,GAAG;AAC7B,WAAO,KAAK,MAAM,yBAAyB,CAAC,UAAU,GAAG,kCAAkC,CAAC;AAAA,EAC9F,WAAW,EAAE,cAAc,GAAG,KAAK,OAAO,IAAI;AAC5C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU;AAAA,QACX,WAAW,GAAG,IACV,yCACA;AAAA,MACN;AAAA,IACF;AAAA,EACF,WAAW,OAAO,QAAQ,YAAY,MAAM,KAAK;AAE/C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACrB,MAAI,SAAS,MAAM,GAAG;AACpB,UAAM,WAAW,QAAQ,OAAO,QAAQ,IAAI,CAAC,IAAI,OAAO;AACxD,QAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,aAAO,KAAK,GAAG,cAAc,QAAQ,CAAC;AAAA,IACxC;AAAA,EACF;AAEA,SAAO,KAAK,GAAG,oBAAoB,KAAK,CAAC;AACzC,SAAO;AACT;AAEA,SAAS,cAAc,UAA4C;AACjE,QAAM,SAAuB,CAAC;AAC9B,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU,UAAU;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,MAAI,cAAc;AAClB,MAAI,oBAAoB;AACxB,aAAW,CAAC,OAAO,SAAS,KAAK,SAAS,QAAQ,GAAG;AACnD,QAAI,CAAC,SAAS,SAAS,GAAG;AACxB,0BAAoB;AACpB;AAAA,IACF;AACA,UAAM,UAAU,QAAQ;AACxB,UAAM,OAAO,CAAC,UAAU,YAAY,KAAK;AACzC,QAAI,YAAY,UAAU,IAAI,GAAG;AAC/B,aAAO;AAAA,QACL,MAAM,8BAA8B,CAAC,GAAG,MAAM,MAAM,GAAG,yBAAyB,OAAO,GAAG;AAAA,MAC5F;AAAA,IACF;AACA,UAAM,SAAS,UAAU;AACzB,QAAI,QAAQ,MAAM,GAAG;AACnB,0BAAoB;AACpB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,QAAQ;AAAA,UAClB,yBAAyB,OAAO;AAAA,QAClC;AAAA,MACF;AAAA,IACF,WAAW,EAAE,OAAO,WAAW,YAAY,OAAO,SAAS,MAAM,KAAK,UAAU,IAAI;AAClF,0BAAoB;AACpB,aAAO;AAAA,QACL;AAAA,UACE;AAAA,UACA,CAAC,GAAG,MAAM,QAAQ;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,qBAAe;AAAA,IACjB;AAAA,EACF;AACA,MAAI,qBAAqB,gBAAgB,GAAG;AAC1C,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU,UAAU;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,gBAAgB,OAAO,mBAAmB;AAInD,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,CAAC,UAAU,UAAU;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACtKO,SAAS,eACd,MACA,UACsB;AACtB,QAAM,YAAY,eAAe,MAAM,OAAO,UAAU,SAAS,WAAW,SAAS,OAAO,EAAE;AAC9F,QAAM,UAA2B,CAAC;AAClC,aAAW,QAAQ,UAAU,OAAO;AAClC,QAAI,KAAK,WAAW,SAAS;AAC3B;AAAA,IACF;AACA,YAAQ,KAAK;AAAA,MACX,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK,WAAW;AAAA,MACzB,SACE,KAAK,WAAW,YACZ,YACA,KAAK,WAAW,cACd,cACA;AAAA,MACR,iBAAiB,KAAK;AAAA,MACtB,iBAAiB,KAAK;AAAA,MACtB,OAAO,KAAK;AAAA,MACZ,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,SAAO,EAAE,OAAO,UAAU,YAAY,UAAU,GAAG,UAAU,MAAM,QAAQ;AAC7E;;;AC7CO,SAAS,qBAAqB,cAAiC;AACpE,SAAO,aAAa,MAAM,CAAC,cAAc,SAAS,IAAI,IAAI;AAC5D;;;ACaO,SAAS,gBACd,iBACA,mBACA,cACA,gBACQ;AACR,QAAM,SAAS,kBAAkB;AACjC,QAAM,UAAU,mBAAmB,IAAI,IAAI,oBAAoB;AAC/D,SAAO,KAAK,IAAI,GAAG,SAAS,OAAO;AACrC;AAYO,SAAS,qBAAqB,eAAuB,aAA6B;AACvF,SAAO,gBAAgB;AACzB;;;AC3BA,SAAS,UAAU,OAAqC;AACtD,SAAO;AAAA,IACL,GAAI,MAAM,kBAAkB,SAAY,EAAE,eAAe,MAAM,cAAc,IAAI,CAAC;AAAA,IAClF,GAAI,MAAM,mBAAmB,SAAY,EAAE,MAAM,MAAM,eAAe,IAAI,CAAC;AAAA,IAC3E,GAAG,MAAM;AAAA,EACX;AACF;AAOO,SAAS,qBACd,MACA,UACsB;AACtB,QAAM,UAA2B,CAAC;AAClC,QAAM,kBAA6B,CAAC;AAEpC,aAAW,SAAS,KAAK,QAAQ;AAC/B,UAAM,WAAW,SAAS,QAAQ,MAAM,EAAE;AAC1C,UAAM,QAAQ,OAAO,aAAa,WAAW,WAAW;AACxD,UAAM,UAAU,UAAU,OAAO,MAAM,iBAAiB,UAAU,KAAK,CAAC,EAAE;AAE1E,oBAAgB,KAAK,OAAO;AAC5B,YAAQ,KAAK;AAAA,MACX,QAAQ,MAAM;AAAA,MACd,SAAS;AAAA,MACT,SAAS,UAAU,YAAY;AAAA,MAC/B,iBAAiB,CAAC,KAAK;AAAA,MACvB,iBAAiB,CAAC,GAAG,MAAM,eAAe;AAAA,MAC1C,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,gBAAgB,OAAO,OAAO,EAAE;AACtD,QAAM,aACJ,KAAK,oBAAoB,mBACrB,qBAAqB,eAAe,IACpC,qBAAqB,eAAe,KAAK,OAAO,MAAM;AAE5D,SAAO,EAAE,OAAO,YAAY,UAAU,GAAG,UAAU,MAAM,QAAQ;AACnE;;;ACzCA,SAAS,WAAW,MAAqB,KAA+C;AACtF,MAAI,IAAI,YAAY,QAAW;AAC7B,WAAO,IAAI;AAAA,EACb;AACA,SAAO,KAAK,OAAO,KAAK,CAAC,SAAS,KAAK,OAAO,IAAI,MAAM,GAAG,WAAW,CAAC;AACzE;AAiBO,SAAS,eACd,MACA,UACsB;AACtB,QAAM,UAA2B,CAAC;AAClC,QAAM,gBAA2B,CAAC;AAElC,aAAW,OAAO,KAAK,MAAM;AAC3B,UAAM,MAAM,SAAS,WAAW,IAAI,EAAE;AACtC,UAAM,WAAW,OAAO,QAAQ,WAAW,MAAM;AACjD,UAAM,UAAU,WAAW,MAAM,GAAG;AACpC,UAAM,UAAU,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ;AAC/D,UAAM,WAAW,aAAa,MAAM;AACpC,UAAM,UAAU,YAAY,aAAa,IAAI;AAE7C,kBAAc,KAAK,OAAO;AAC1B,YAAQ,KAAK;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ;AAAA,MACA,SAAS,UAAU,YAAY,WAAW,cAAc;AAAA,MACxD,iBAAiB,CAAC,QAAQ;AAAA,MAC1B,iBAAiB,CAAC,IAAI,eAAe;AAAA,MACrC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,cAAc,OAAO,OAAO,EAAE;AAClD,QAAM,aACJ,KAAK,oBAAoB,mBACrB,qBAAqB,aAAa,IAClC,qBAAqB,aAAa,KAAK,KAAK,MAAM;AAExD,SAAO,EAAE,OAAO,YAAY,UAAU,GAAG,UAAU,MAAM,QAAQ;AACnE;;;AC3DO,SAAS,oBACd,MACA,UACsB;AACtB,QAAM,aAAa,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;AAC5E,QAAM,WAAW,IAAI,IAAI,SAAS,iBAAiB;AAEnD,QAAM,eAAe,KAAK,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,EAAE;AACvE,QAAM,iBAAiB,KAAK,QAAQ,SAAS;AAE7C,MAAI;AAEJ,MAAI,KAAK,oBAAoB,kBAAkB;AAC7C,QAAI,KAAK,SAAS,UAAU;AAC1B,mBACE,SAAS,kBAAkB,WAAW,KACtC,WAAW,IAAI,SAAS,kBAAkB,CAAC,CAAC,GAAG,cAAc,OACzD,IACA;AAAA,IACR,OAAO;AACL,YAAM,aAAa,KAAK,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE;AAC1E,YAAM,qBAAqB,WAAW,MAAM,CAAC,OAAO,SAAS,IAAI,EAAE,CAAC;AACpE,mBAAa,SAAS,SAAS,WAAW,UAAU,qBAAqB,IAAI;AAAA,IAC/E;AAAA,EACF,OAAO;AACL,QAAI,kBAAkB;AACtB,QAAI,oBAAoB;AACxB,eAAW,MAAM,UAAU;AACzB,YAAM,SAAS,WAAW,IAAI,EAAE;AAChC,UAAI,QAAQ,WAAW;AACrB,2BAAmB;AAAA,MACrB,OAAO;AACL,6BAAqB;AAAA,MACvB;AAAA,IACF;AACA,iBAAa,gBAAgB,iBAAiB,mBAAmB,cAAc,cAAc;AAAA,EAC/F;AAEA,QAAM,UAA2B,KAAK,QAAQ,IAAI,CAAC,WAAW;AAC5D,UAAM,cAAc,SAAS,IAAI,OAAO,EAAE;AAC1C,UAAM,UAAU,cACZ,OAAO,YACL,YACA,cACF,OAAO,YACL,uBACA;AACN,WAAO;AAAA,MACL,QAAQ,OAAO;AAAA,MACf,SAAS,gBAAgB,OAAO;AAAA,MAChC;AAAA,MACA,iBAAiB,CAAC,cAAc,aAAa,cAAc;AAAA,MAC3D,iBAAiB,CAAC,OAAO,YAAY,aAAa,cAAc;AAAA,MAChE,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,EAAE,OAAO,YAAY,UAAU,GAAG,UAAU,MAAM,QAAQ;AACnE;;;ACDO,IAAM,qBAAkC;AAAA,EAC7C,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,aAAa;AAAA,EACb,UAAU;AAAA,IACR,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,oBAAoB;AAAA,EACtB;AACF;AAEA,IAAM,uBAAoC;AAAA,EACxC,eAAe;AAAA,EACf,MAAM;AAAA,EACN,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAKJ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,eAAe;AAAA,EACf,QAAQ;AAAA,EACR,oBAAoB;AAAA,EACpB,iBAAiB;AACnB;AAgBO,IAAM,wBAAqC;AAAA,EAChD,SAAS;AAAA,EACT,WAAW;AACb;AASO,IAAM,0BAAuC;AAAA,EAClD,eAAe;AAAA,EACf,MAAM;AAAA,EACN,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,yBAAyB;AAAA,EACzB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EACb,QAAQ;AACV;AAEA,IAAM,+BAA4C;AAAA,EAChD,GAAG;AAAA,EACH,UAAU;AAAA,EACV,cAAc;AAAA,EACd,MAAM;AAAA,EACN,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,WAAW;AAAA,IACX,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOV,OAAO;AAAA,MACL,MAAM;AAAA,MACN,KAAK;AAAA,MACL,KAAK;AAAA,MACL,aAAa;AAAA,IACf;AAAA,EACF;AACF;AAaA,IAAM,0BAAuC;AAAA,EAC3C,GAAG;AAAA,EACH,SAAS;AAAA,EACT,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,SAAS,EAAE,IAAI,UAAU,MAAM,SAAS;AAAA,EAC1C;AAAA,EACA,MAAM;AAAA,IACJ,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS,EAAE,IAAI,UAAU,MAAM,SAAS;AAAA,IACxC,iBAAiB;AAAA,IACjB,UAAU;AAAA,EACZ;AACF;AAEA,IAAM,kCAA+C;AAAA,EACnD,GAAG;AAAA,EACH,SAAS;AAAA,EACT,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,QAAQ;AAAA,IACN,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AACF;AAkBA,IAAM,yBAAsC;AAAA,EAC1C,GAAG;AAAA,EACH,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,WAAW,EAAE,MAAM,UAAU,KAAK,UAAU,KAAK,SAAS;AAAA,EAC1D,OAAO,EAAE,MAAM,SAAS;AAAA,EACxB,WAAW;AAAA,EACX,UAAU;AACZ;AAaO,IAAM,sBAAmC;AAAA,EAC9C,OAAO;AAAA;AAAA,EAEP,UAAU;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,QAAQ;AAAA,EACV;AACF;AAEA,IAAM,gCAA6C;AAAA,EACjD,GAAG;AAAA,EACH,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQV,QAAQ;AACV;AAGO,IAAM,qBAAqB,mBAGhC;AAAA,EACA,MAAM;AAAA;AAAA;AAAA,EAGN,QAAQ;AAAA,EACR,SAAS,EAAE,MAAM,QAAQ,OAAO,oBAAoB;AAAA,EACpD,YAAY,CAAC,cAAc,UAAU,kBAAkB,UAAU,KAAK;AAAA,EACtE,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,yBAAyB,CAAC,SAAS;AAAA,MACjC,KAAK,QACF,OAAO,CAAC,WAAW,OAAO,SAAS,EACnC,IAAI,CAAC,WAAW,OAAO,EAAE,EACzB,KAAK,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EACA,cAAc,CAAC,mBAAmB,qBAAqB,WAAW;AAAA,EAClE,WAAW;AACb,CAAC;AAGM,IAAM,sBAAsB,mBAGjC;AAAA,EACA,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS,EAAE,MAAM,QAAQ,OAAO,qBAAqB;AAAA,EACrD,YAAY,CAAC,aACX,OAAO,OAAO,UAAU,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,OAAO,KAAK,EAAE,SAAS,CAAC;AAAA,EAClF,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA,IACrB,qBAAqB;AAAA;AAAA;AAAA;AAAA,IAIrB,yBAAyB,CAAC,SAAS;AAAA,MACjC,KAAK,OAAO,IAAI,CAAC,UAAU,MAAM,gBAAgB,CAAC,KAAK,EAAE,EAAE,KAAK,KAAK;AAAA,IACvE;AAAA,EACF;AAAA,EACA,cAAc,CAAC,gBAAgB,kBAAkB,WAAW;AAAA,EAC5D,WAAW;AACb,CAAC;AASM,IAAM,sBAAsB,mBAGjC;AAAA,EACA,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS,CAAC,MAAM,aAAa;AAC3B,YAAM,YAAY,WAAW,UAAU,QAAQ,EAAE;AACjD,aAAO;AAAA,QACL,kBACE,aAAa,UAAa,aAAa,KAAK,YAAY,aAAa,KAAK;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,YAAY,CAAC,cAAc,UAAU,KAAK,KAAK,EAAE,UAAU,KAAK;AAAA,EAChE,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,yBAAyB,MAAM,CAAC;AAAA,EAClC;AAAA,EACA,cAAc,CAAC,gBAAgB,WAAW;AAAA,EAC1C,WAAW;AACb,CAAC;AAGM,IAAM,gBAAgB,mBAA4D;AAAA,EACvF,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS,EAAE,MAAM,QAAQ,OAAO,eAAe;AAAA,EAC/C,YAAY,CAAC,aACX,OAAO,OAAO,UAAU,cAAc,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,aAAa,EAAE;AAAA,EAC9E,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOrB,qBAAqB;AAAA,IACrB,yBAAyB,CAAC,SAAS;AAAA,MACjC,KAAK,KAAK,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,MAAM,IAAI,eAAe,EAAE,EAAE,KAAK,KAAK;AAAA,IACzE;AAAA,EACF;AAAA,EACA,cAAc,CAAC,gBAAgB,WAAW;AAAA,EAC1C,WAAW;AACb,CAAC;AAOM,IAAM,gBAAgB,mBAA4D;AAAA,EACvF,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS,EAAE,MAAM,QAAQ,OAAO,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/C,YAAY,CAAC,aACX;AAAA,IACE;AAAA,MACE,OAAO,UAAU,SAAS,WAAW,SAAS,OAAO;AAAA,MACrD;AAAA,IACF;AAAA,EACF,MAAM;AAAA,EACR,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA;AAAA;AAAA,IAGrB,qBAAqB;AAAA,IACrB,yBAAyB,CAAC,SAAS,CAAC,KAAK,YAAY,GAAI,KAAK,uBAAuB,CAAC,CAAE;AAAA,EAC1F;AAAA,EACA,cAAc,CAAC,gBAAgB,kBAAkB,WAAW;AAAA,EAC5D,WAAW;AACb,CAAC;AAsBD,IAAM,0BAAuC;AAAA,EAC3C,GAAG;AAAA,EACH,cAAc;AAAA,EACd,eAAe;AAAA,EACf,WAAW,EAAE,YAAY,UAAU,YAAY,UAAU,UAAU,SAAS;AAAA,EAC5E,WAAW,EAAE,MAAM,UAAU,KAAK,UAAU,KAAK,SAAS;AAAA,EAC1D,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,QAAQ,SAAS,EAAE;AAAA,EAC5D,UAAU;AACZ;AASA,SAAS,aAAa,UAAyD;AAC7E,QAAM,YAAa,UAAkD;AACrE,SACE,OAAO,cAAc,YACrB,cAAc,QACd,OAAQ,UAAgC,QAAQ,YAC/C,UAA8B,QAAQ;AAE3C;AAQO,IAAM,gBAAgB,mBAA4D;AAAA,EACvF,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS,CAAC,OAAO,cAAc,EAAE,cAAc,aAAa,QAAQ,EAAE;AAAA,EACxE;AAAA,EACA,YAAY,CAAC,aAAa,aAAa,QAAQ;AAAA,EAC/C,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKrB,qBAAqB;AAAA,IACrB,yBAAyB,MAAM,CAAC;AAAA,EAClC;AAAA,EACA,cAAc,CAAC,qBAAqB,qBAAqB,sBAAsB,WAAW;AAAA,EAC1F,WAAW;AACb,CAAC;AAED,qBAAqB,kBAAkB;AACvC,qBAAqB,mBAAmB;AACxC,qBAAqB,mBAAmB;AACxC,qBAAqB,aAAa;AAClC,qBAAqB,aAAa;AAClC,qBAAqB,aAAa;","names":["z","z","z","issue","z","z","placeholderCounts","z","z","z","z","checkEvenAfterIssues","issue","z","z","z","z","z","z","j","i","PUNCTUATION_RE","checkPairing","resolveChoices","pairsOneToOne","checkRecording","checkSlowRecording"]}