marc-ts 0.1.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.
- package/LICENSE +9 -0
- package/README.md +642 -0
- package/dist/clone.d.ts +53 -0
- package/dist/convenience.d.ts +150 -0
- package/dist/field-ops.d.ts +166 -0
- package/dist/field-utils.d.ts +88 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +936 -0
- package/dist/index.js.map +1 -0
- package/dist/marc8.d.ts +39 -0
- package/dist/marcjson.cjs +3 -0
- package/dist/marcjson.cjs.map +1 -0
- package/dist/marcjson.d.ts +34 -0
- package/dist/marcjson.js +75 -0
- package/dist/marcjson.js.map +1 -0
- package/dist/marctxt.cjs +8 -0
- package/dist/marctxt.cjs.map +1 -0
- package/dist/marctxt.d.ts +22 -0
- package/dist/marctxt.js +88 -0
- package/dist/marctxt.js.map +1 -0
- package/dist/marcxml.cjs +8 -0
- package/dist/marcxml.cjs.map +1 -0
- package/dist/marcxml.d.ts +20 -0
- package/dist/marcxml.js +220 -0
- package/dist/marcxml.js.map +1 -0
- package/dist/parser.d.ts +43 -0
- package/dist/query.d.ts +38 -0
- package/dist/serializer.d.ts +57 -0
- package/dist/types-CJcxHJff.cjs +3 -0
- package/dist/types-CJcxHJff.cjs.map +1 -0
- package/dist/types-c4Mo9m9u.js +12 -0
- package/dist/types-c4Mo9m9u.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/warnings.d.ts +23 -0
- package/package.json +91 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/marc8.ts","../src/parser.ts","../src/warnings.ts","../src/serializer.ts","../src/field-utils.ts","../src/convenience.ts","../src/query.ts","../src/field-ops.ts","../src/clone.ts"],"sourcesContent":["/**\n * MARC-8 character encoding codec.\n *\n * MARC-8 is signaled by leader byte 9 == ' ' (space); UTF-8 uses 'a'.\n * Decoding tracks escape-designated G0/G1 sets and converts known MARC-8\n * character sets to Unicode. Encoding remains conservative: ASCII plus ANSEL\n * Latin/combining characters are supported, and unsupported characters become '?'.\n */\n\nconst ESCAPE = 0x1b;\nconst REPLACEMENT = '\\uFFFD';\n\ntype SingleByteSet =\n | 'ascii'\n | 'ansel'\n | 'greek'\n | 'hebrew'\n | 'cyrillic'\n | 'arabic'\n | 'subscript'\n | 'superscript';\ntype CharSet = SingleByteSet | 'eacc';\n\ninterface DecodeState {\n g0: CharSet;\n g1: CharSet;\n}\n\n// MARC-8 ANSEL Extended Latin non-combining chars, indexed as G0/G1 positions.\nconst ANSEL_NON_COMBINING: ReadonlyMap<number, string> = new Map([\n [0x21, 'Ł'],\n [0x22, 'Ø'],\n [0x23, 'Đ'],\n [0x24, 'Þ'],\n [0x25, 'Æ'],\n [0x26, 'Œ'],\n [0x27, 'ʹ'],\n [0x28, '·'],\n [0x29, '♭'],\n [0x2a, '®'],\n [0x2b, '±'],\n [0x2c, 'Ơ'],\n [0x2d, 'Ư'],\n [0x2e, 'ʼ'],\n [0x30, 'ʻ'],\n [0x31, 'ł'],\n [0x32, 'ø'],\n [0x33, 'đ'],\n [0x34, 'þ'],\n [0x35, 'æ'],\n [0x36, 'œ'],\n [0x37, 'ʺ'],\n [0x38, 'ı'],\n [0x39, '£'],\n [0x3a, 'ð'],\n [0x3b, 'ơ'],\n [0x3c, 'ư'],\n [0x3f, '°'],\n]);\n\n// ANSEL combining diacritics. MARC-8 places these before the base character.\nconst ANSEL_COMBINING: ReadonlyMap<number, string> = new Map([\n [0x60, '\\u0309'],\n [0x61, '\\u0300'],\n [0x62, '\\u0301'],\n [0x63, '\\u0302'],\n [0x64, '\\u0303'],\n [0x65, '\\u0304'],\n [0x66, '\\u0306'],\n [0x67, '\\u0307'],\n [0x68, '\\u0308'],\n [0x69, '\\u030c'],\n [0x6a, '\\u030a'],\n [0x6b, '\\ufe20'],\n [0x6c, '\\ufe21'],\n [0x6d, '\\u0315'],\n [0x6e, '\\u030b'],\n [0x6f, '\\u0310'],\n [0x70, '\\u0327'],\n [0x71, '\\u0328'],\n [0x72, '\\u0323'],\n [0x73, '\\u0324'],\n [0x74, '\\u0325'],\n [0x75, '\\u0333'],\n [0x76, '\\u0332'],\n [0x77, '\\u0326'],\n [0x78, '\\u031c'],\n [0x79, '\\u032e'],\n [0x7a, '\\ufe22'],\n [0x7b, '\\ufe23'],\n [0x7e, '\\u0313'],\n]);\n\nconst COMBINING_REVERSE: ReadonlyMap<string, number> = new Map(\n Array.from(ANSEL_COMBINING.entries()).map(([k, v]) => [v, k + 0x80])\n);\n\nconst NON_COMBINING_REVERSE: ReadonlyMap<string, number> = new Map(\n Array.from(ANSEL_NON_COMBINING.entries()).map(([k, v]) => [v, k + 0x80])\n);\n\nconst BASIC_GREEK: ReadonlyMap<number, string> = tableFromString(\n 0x41,\n 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ',\n 0x61,\n 'αβγδεζηθικλμνξοπρστυφχψω'\n);\n\nconst BASIC_HEBREW: ReadonlyMap<number, string> = tableFromString(\n 0x60,\n 'אבגדהוזחטיךכלםמןנסעףפץצקרשת'\n);\n\nconst BASIC_CYRILLIC: ReadonlyMap<number, string> = tableFromString(\n 0x41,\n 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ',\n 0x61,\n 'абвгдежзийклмнопрстуфхцчшщъыьэюя'\n);\n\nconst BASIC_ARABIC: ReadonlyMap<number, string> = new Map([\n [0x21, 'ء'],\n [0x22, 'آ'],\n [0x23, 'أ'],\n [0x24, 'ؤ'],\n [0x25, 'إ'],\n [0x26, 'ئ'],\n [0x27, 'ا'],\n [0x28, 'ب'],\n [0x29, 'ة'],\n [0x2a, 'ت'],\n [0x2b, 'ث'],\n [0x2c, 'ج'],\n [0x2d, 'ح'],\n [0x2e, 'خ'],\n [0x2f, 'د'],\n [0x30, 'ذ'],\n [0x31, 'ر'],\n [0x32, 'ز'],\n [0x33, 'س'],\n [0x34, 'ش'],\n [0x35, 'ص'],\n [0x36, 'ض'],\n [0x37, 'ط'],\n [0x38, 'ظ'],\n [0x39, 'ع'],\n [0x3a, 'غ'],\n [0x41, 'ف'],\n [0x42, 'ق'],\n [0x43, 'ك'],\n [0x44, 'ل'],\n [0x45, 'م'],\n [0x46, 'ن'],\n [0x47, 'ه'],\n [0x48, 'و'],\n [0x49, 'ى'],\n [0x4a, 'ي'],\n]);\n\nconst SUBSCRIPT: ReadonlyMap<number, string> = new Map([\n [0x28, '₍'],\n [0x29, '₎'],\n [0x2b, '₊'],\n [0x2d, '₋'],\n [0x30, '₀'],\n [0x31, '₁'],\n [0x32, '₂'],\n [0x33, '₃'],\n [0x34, '₄'],\n [0x35, '₅'],\n [0x36, '₆'],\n [0x37, '₇'],\n [0x38, '₈'],\n [0x39, '₉'],\n]);\n\nconst SUPERSCRIPT: ReadonlyMap<number, string> = new Map([\n [0x28, '⁽'],\n [0x29, '⁾'],\n [0x2b, '⁺'],\n [0x2d, '⁻'],\n [0x30, '⁰'],\n [0x31, '¹'],\n [0x32, '²'],\n [0x33, '³'],\n [0x34, '⁴'],\n [0x35, '⁵'],\n [0x36, '⁶'],\n [0x37, '⁷'],\n [0x38, '⁸'],\n [0x39, '⁹'],\n [0x6e, 'ⁿ'],\n]);\n\n// WARNING: this table maps only 33 of the ~16,000 official EACC triples.\n// Real CJK content in a MARC-8 record will largely decode to U+FFFD. Catalogs\n// with Chinese/Japanese/Korean material should be supplied as UTF-8\n// (`leader[9] === 'a'`) instead. See README.md for guidance.\n//\n// The decoder recognizes EACC as a three-byte set; unmapped triples emit\n// U+FFFD.\nconst EACC: ReadonlyMap<number, string> = new Map([\n [0x212121, '一'],\n [0x212122, '丁'],\n [0x212123, '七'],\n [0x212124, '万'],\n [0x212125, '丈'],\n [0x212126, '三'],\n [0x212127, '上'],\n [0x212128, '下'],\n [0x212129, '不'],\n [0x21212a, '与'],\n [0x21212b, '丐'],\n [0x21212c, '丑'],\n [0x21212d, '且'],\n [0x21212e, '世'],\n [0x21212f, '丘'],\n [0x212130, '丙'],\n [0x212131, '业'],\n [0x212132, '丛'],\n [0x212133, '东'],\n [0x212134, '丝'],\n [0x212135, '丞'],\n [0x212136, '丟'],\n [0x212137, '丠'],\n [0x212138, '両'],\n [0x212139, '丢'],\n [0x21213a, '两'],\n [0x21213b, '严'],\n [0x21213c, '並'],\n [0x21213d, '丧'],\n [0x21213e, '丨'],\n [0x21213f, '个'],\n [0x212140, '丫'],\n [0x212141, '中'],\n [0x212142, '丰'],\n]);\n\nconst SINGLE_BYTE_TABLES: Readonly<Record<SingleByteSet, ReadonlyMap<number, string>>> = {\n ascii: new Map(),\n ansel: mergeMaps(ANSEL_NON_COMBINING, ANSEL_COMBINING),\n greek: BASIC_GREEK,\n hebrew: BASIC_HEBREW,\n cyrillic: BASIC_CYRILLIC,\n arabic: BASIC_ARABIC,\n subscript: SUBSCRIPT,\n superscript: SUPERSCRIPT,\n};\n\nfunction tableFromString(\n ...chunks: [number, string, ...Array<number | string>]\n): ReadonlyMap<number, string> {\n const table = new Map<number, string>();\n for (let i = 0; i < chunks.length; i += 2) {\n const start = chunks[i] as number;\n const chars = chunks[i + 1] as string;\n Array.from(chars).forEach((char, offset) => table.set(start + offset, char));\n }\n return table;\n}\n\nfunction mergeMaps(...maps: ReadonlyMap<number, string>[]): ReadonlyMap<number, string> {\n const merged = new Map<number, string>();\n for (const map of maps) {\n for (const [key, value] of map) merged.set(key, value);\n }\n return merged;\n}\n\nfunction isCombiningMark(char: string): boolean {\n const code = char.codePointAt(0)!;\n return (code >= 0x0300 && code <= 0x036f) || (code >= 0xfe20 && code <= 0xfe2f);\n}\n\nfunction normalizedCode(byte: number, isG1: boolean): number {\n return isG1 ? byte - 0x80 : byte;\n}\n\nfunction decodeSingleByte(byte: number, set: SingleByteSet, isG1: boolean): string {\n const code = normalizedCode(byte, isG1);\n if (set === 'ascii')\n return code >= 0x20 && code <= 0x7e ? String.fromCharCode(code) : REPLACEMENT;\n return SINGLE_BYTE_TABLES[set].get(code) ?? REPLACEMENT;\n}\n\nfunction decodeEacc(bytes: Uint8Array, pos: number, isG1: boolean): { char: string; next: number } {\n if (pos + 2 >= bytes.length) return { char: REPLACEMENT, next: bytes.length };\n const b1 = normalizedCode(bytes[pos]!, isG1);\n const b2 = normalizedCode(bytes[pos + 1]!, isG1);\n const b3 = normalizedCode(bytes[pos + 2]!, isG1);\n const key = (b1 << 16) | (b2 << 8) | b3;\n return { char: EACC.get(key) ?? REPLACEMENT, next: pos + 3 };\n}\n\nfunction designate(\n bytes: Uint8Array,\n pos: number,\n state: DecodeState\n): { char: string; next: number } {\n if (pos + 1 >= bytes.length) return { char: REPLACEMENT, next: bytes.length };\n\n const first = bytes[pos + 1]!;\n\n let target: keyof DecodeState | undefined;\n let finalIndex: number;\n\n if (first === 0x28 || first === 0x2c) {\n target = 'g0';\n finalIndex = pos + 2;\n } else if (first === 0x29 || first === 0x2d) {\n target = 'g1';\n finalIndex = pos + 2;\n } else if (first === 0x24) {\n const second = bytes[pos + 2];\n if (second === undefined) return { char: REPLACEMENT, next: bytes.length };\n if (second === 0x28 || second === 0x2c) {\n target = 'g0';\n finalIndex = pos + 3;\n } else if (second === 0x29 || second === 0x2d) {\n target = 'g1';\n finalIndex = pos + 3;\n } else {\n target = 'g0';\n finalIndex = pos + 2;\n }\n } else {\n return { char: REPLACEMENT, next: pos + 2 };\n }\n\n if (finalIndex >= bytes.length) return { char: REPLACEMENT, next: bytes.length };\n\n let final = bytes[finalIndex]!;\n if (final === 0x21) {\n finalIndex++;\n if (finalIndex >= bytes.length) return { char: REPLACEMENT, next: bytes.length };\n final = bytes[finalIndex]!;\n }\n\n const set = characterSetForFinal(final);\n if (!set) return { char: REPLACEMENT, next: finalIndex + 1 };\n return { char: '', next: updateState(state, target, set, finalIndex + 1) };\n}\n\nfunction updateState(\n state: DecodeState,\n target: keyof DecodeState,\n set: CharSet,\n next: number\n): number {\n state[target] = set;\n return next;\n}\n\nfunction characterSetForFinal(final: number): CharSet | undefined {\n switch (final) {\n case 0x31:\n return 'eacc';\n case 0x32:\n return 'hebrew';\n case 0x33:\n return 'arabic';\n case 0x34:\n return 'cyrillic';\n case 0x42:\n return 'ascii';\n case 0x45:\n return 'ansel';\n case 0x4e:\n return 'cyrillic';\n case 0x51:\n return 'greek';\n case 0x53:\n return 'greek';\n case 0x62:\n return 'subscript';\n case 0x70:\n return 'superscript';\n default:\n return undefined;\n }\n}\n\n/**\n * Convert a MARC-8-encoded byte sequence to a Unicode string.\n *\n * Escape-designated G0/G1 sets are decoded instead of skipped. Unknown bytes,\n * unsupported designators, and unmapped EACC triples produce U+FFFD.\n */\nexport function marc8ToUnicode(bytes: Uint8Array): string {\n const state: DecodeState = { g0: 'ascii', g1: 'ansel' };\n const out: string[] = [];\n let pendingCombining = '';\n let i = 0;\n\n const emit = (char: string): void => {\n if (!char) return;\n if (isCombiningMark(char)) {\n pendingCombining += char;\n return;\n }\n out.push(char + pendingCombining);\n pendingCombining = '';\n };\n\n while (i < bytes.length) {\n const byte = bytes[i]!;\n\n if (byte === ESCAPE) {\n const result = designate(bytes, i, state);\n emit(result.char);\n i = result.next;\n continue;\n }\n\n if (byte < 0x20) {\n // Control bytes are passed through as-is. A control byte arriving while\n // pendingCombining is non-empty will incorrectly attach the accumulated\n // diacritics to the control character rather than the next base — control\n // bytes in the middle of field data are non-conformant and rare in practice.\n emit(String.fromCharCode(byte));\n i++;\n continue;\n }\n\n const isG1 = byte >= 0xa0;\n const set = isG1 ? state.g1 : state.g0;\n if (set === 'eacc') {\n const result = decodeEacc(bytes, i, isG1);\n emit(result.char);\n i = result.next;\n continue;\n }\n\n emit(decodeSingleByte(byte, set, isG1));\n i++;\n }\n\n if (pendingCombining) out.push(pendingCombining);\n return out.join('');\n}\n\n/**\n * Convert a Unicode string to MARC-8-encoded bytes.\n *\n * This is intentionally conservative. ASCII and ANSEL Latin/combining\n * characters are encoded. Characters with no supported MARC-8 equivalent are\n * replaced with '?'. For programmatic visibility into how much data was lost,\n * use {@link unicodeToMarc8WithStats}.\n */\nexport function unicodeToMarc8(text: string): Uint8Array {\n return unicodeToMarc8WithStats(text).bytes;\n}\n\n/**\n * Result of {@link unicodeToMarc8WithStats}: the encoded bytes plus a count of\n * characters that had no MARC-8 equivalent and were substituted with '?'.\n */\nexport interface Marc8EncodeResult {\n readonly bytes: Uint8Array;\n readonly lossyCount: number;\n}\n\n/**\n * Like {@link unicodeToMarc8} but also reports how many characters were\n * substituted because they have no MARC-8 equivalent. A non-zero `lossyCount`\n * means the round-trip was destructive.\n */\nexport function unicodeToMarc8WithStats(text: string): Marc8EncodeResult {\n const decomposed = text.normalize('NFD');\n const bytes: number[] = [];\n let lossyCount = 0;\n let i = 0;\n\n while (i < decomposed.length) {\n const cp = decomposed.codePointAt(i)!;\n const ch = decomposed[i]!;\n\n if (isCombiningMark(ch)) {\n bytes.push(0x3f);\n lossyCount++;\n i++;\n continue;\n }\n\n const charWidth = cp > 0xffff ? 2 : 1;\n let j = i + charWidth;\n const diacritics: number[] = [];\n while (j < decomposed.length && isCombiningMark(decomposed[j]!)) {\n const combCode = COMBINING_REVERSE.get(decomposed[j]!);\n if (combCode !== undefined) {\n diacritics.push(combCode);\n } else {\n // Unknown combining mark: substitute with '?' rather than dropping\n // silently, matching the leading-combining-mark behavior above.\n diacritics.push(0x3f);\n lossyCount++;\n }\n j++;\n }\n\n bytes.push(...diacritics);\n\n if (cp < 0x80) {\n bytes.push(cp);\n } else {\n const baseByte = NON_COMBINING_REVERSE.get(ch);\n if (baseByte !== undefined) {\n bytes.push(baseByte);\n } else {\n bytes.push(0x3f);\n lossyCount++;\n }\n }\n\n i = j;\n }\n\n return { bytes: new Uint8Array(bytes), lossyCount };\n}\n","/**\n * ISO2709 binary format parser for MARC21 records.\n * Supports UTF-8 encoding with heuristic detection and error recovery.\n */\n\nimport type {\n MarcRecord,\n ControlField,\n DataField,\n Subfield,\n ParseOptions,\n ParseResult,\n MarcWarning,\n MarcWarningType,\n} from './types';\nimport { marc8ToUnicode } from './marc8';\n\n// ISO2709 separator characters\nconst SUBFIELD_DELIMITER = 0x1f; // ASCII 31 (IS1 - Unit Separator)\nconst FIELD_TERMINATOR = 0x1e; // ASCII 30 (IS2 - Information Separator)\n\n// Leader constants\nconst LEADER_LENGTH = 24;\nconst DIRECTORY_ENTRY_LENGTH = 12;\nconst TAG_LENGTH = 3;\nconst FIELD_LENGTH_SIZE = 4;\n\n// Shared TextDecoder for performance - safe to reuse as UTF-8 decoding is stateless\nconst UTF8_DECODER = new TextDecoder('utf-8', { fatal: false });\n\n/**\n * Best-effort decoder used when the primary decode raises. Falls back to a\n * non-fatal UTF-8 decode so callers retain the shape of the record (with\n * U+FFFD where bytes were invalid) rather than losing the field entirely.\n */\nfunction bestEffortDecode(bytes: Uint8Array): string {\n return UTF8_DECODER.decode(bytes);\n}\n\n/**\n * Format a byte slice as a short hex preview for warning messages.\n * Truncates long sequences so warnings stay readable.\n */\nfunction bytesPreview(bytes: Uint8Array, max = 16): string {\n const slice = bytes.slice(0, max);\n const hex = Array.from(slice, (b) => b.toString(16).padStart(2, '0')).join(' ');\n return bytes.length > max ? `${hex} … (${bytes.length} bytes)` : hex;\n}\n\n/**\n * Internal representation of a directory entry.\n */\ninterface DirectoryEntry {\n tag: string;\n fieldLength: number;\n startingPosition: number;\n}\n\n/**\n * Parse a MARC21 record from ISO2709 binary format.\n *\n * @param buffer - The binary data to parse (use Uint8Array for browser compatibility)\n * @param options - Parsing options (strict mode, max warnings)\n * @returns Parse result containing the record and any warnings\n *\n * @example\n * ```typescript\n * const buffer = new Uint8Array([...]); // MARC21 binary data\n * const result = parseMarcRecord(buffer);\n *\n * if (result.record) {\n * console.log('Parsed successfully');\n * }\n *\n * if (result.warnings.length > 0) {\n * console.warn('Warnings:', result.warnings);\n * }\n * ```\n */\nexport function parseMarcRecord(buffer: Uint8Array, options: ParseOptions = {}): ParseResult {\n const strict = options.strict ?? false;\n const maxWarnings = options.maxWarnings ?? 100;\n const warnings: MarcWarning[] = [];\n\n // Validate minimum record length\n if (buffer.length < LEADER_LENGTH + 1) {\n // Leader + terminator\n const warning = createWarning('truncated_record', `Record too short: ${buffer.length} bytes`);\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n return { record: null, warnings };\n }\n\n // Parse leader\n const leader = decodeLeader(buffer);\n if (!validateLeader(leader, warnings, strict)) {\n if (strict || warnings.length >= maxWarnings) {\n return { record: null, warnings };\n }\n }\n\n // Extract record length from Leader positions 00-04\n const recordLength = parseInt(leader.substring(0, 5), 10);\n if (isNaN(recordLength) || recordLength > buffer.length) {\n const warning = createWarning(\n 'invalid_leader',\n `Invalid record length in leader: ${leader.substring(0, 5)}`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n // Continue with actual buffer length\n } else if (recordLength < buffer.length) {\n // Buffer contains trailing bytes beyond the record. Typically a sign that\n // the caller passed a concatenated stream — they should split on\n // RECORD_TERMINATOR first. Slice down so subsequent indexing doesn't read\n // into the next record's data.\n const warning = createWarning(\n 'truncated_record',\n `Buffer is longer than the record length declared in the leader: ` +\n `leader says ${recordLength}, buffer is ${buffer.length} bytes. ` +\n `Trailing bytes ignored (likely a concatenated stream — split on 0x1D first).`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n buffer = buffer.slice(0, recordLength);\n }\n\n // Extract base address from Leader positions 12-16\n const baseAddress = parseInt(leader.substring(12, 17), 10);\n if (isNaN(baseAddress)) {\n const warning = createWarning(\n 'invalid_leader',\n `Invalid base address in leader: ${leader.substring(12, 17)}`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n return { record: null, warnings };\n }\n\n // Parse directory (starts at byte 24, ends at first FIELD_TERMINATOR)\n const directoryStart = LEADER_LENGTH;\n const directoryEnd = buffer.indexOf(FIELD_TERMINATOR, directoryStart);\n if (directoryEnd === -1) {\n const warning = createWarning('invalid_directory', 'Directory terminator not found');\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n return { record: null, warnings };\n }\n\n const directoryBytes = buffer.slice(directoryStart, directoryEnd);\n const directoryEntries = parseDirectory(directoryBytes, warnings, strict, maxWarnings);\n\n if (directoryEntries.length === 0) {\n const warning = createWarning('invalid_directory', 'No directory entries found');\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n return { record: null, warnings };\n }\n\n // Select decoder based on leader byte 9: ' ' = MARC8, 'a' = UTF-8\n const isMarc8 = leader[9] === ' ';\n if (leader[9] !== ' ' && leader[9] !== 'a') {\n const warning = createWarning(\n 'invalid_leader',\n `Leader position 9 (encoding flag) is '${leader[9]}'; expected 'a' (UTF-8) or ' ' (MARC-8). Defaulting to UTF-8.`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n }\n const decodeBytes: (b: Uint8Array) => string = isMarc8\n ? marc8ToUnicode\n : (b) => UTF8_DECODER.decode(b);\n\n // Parse fields using directory entries\n const fields = parseFields(buffer, directoryEntries, baseAddress, decodeBytes, warnings, strict, maxWarnings);\n\n return {\n record: { leader, fields },\n warnings,\n };\n}\n\n/**\n * Convenience wrapper for strict parsing.\n * Throws an error if parsing fails.\n *\n * @param buffer - The binary data to parse\n * @returns The parsed MARC record\n * @throws Error if parsing fails\n *\n * @example\n * ```typescript\n * try {\n * const record = parseMarcRecordStrict(buffer);\n * console.log('Title:', title(record));\n * } catch (error) {\n * console.error('Parsing failed:', error);\n * }\n * ```\n */\nexport function parseMarcRecordStrict(buffer: Uint8Array): MarcRecord {\n const result = parseMarcRecord(buffer, { strict: true });\n if (!result.record) {\n throw new Error('Failed to parse MARC record in strict mode');\n }\n return result.record;\n}\n\n/**\n * Decode the leader from the buffer.\n */\nfunction decodeLeader(buffer: Uint8Array): string {\n const leaderBytes = buffer.slice(0, LEADER_LENGTH);\n return UTF8_DECODER.decode(leaderBytes);\n}\n\n/**\n * Validate the leader and add warnings if needed.\n */\nfunction validateLeader(leader: string, warnings: MarcWarning[], strict: boolean): boolean {\n if (leader.length !== LEADER_LENGTH) {\n const warning = createWarning(\n 'invalid_leader',\n `Leader length is ${leader.length}, expected ${LEADER_LENGTH}`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n return false;\n }\n\n // Check indicator count (position 10) should be '2'\n if (leader[10] !== '2') {\n const warning = createWarning(\n 'invalid_leader',\n `Leader position 10 (indicator count) is '${leader[10]}', expected '2'`\n );\n // This is a warning but not fatal, continue parsing\n warnings.push(warning);\n }\n\n // Check subfield code length (position 11) should be '2'\n if (leader[11] !== '2') {\n const warning = createWarning(\n 'invalid_leader',\n `Leader position 11 (subfield code length) is '${leader[11]}', expected '2'`\n );\n // This is a warning but not fatal, continue parsing\n warnings.push(warning);\n }\n\n return true;\n}\n\n/**\n * Parse the directory section into entries.\n */\nfunction parseDirectory(\n directoryBytes: Uint8Array,\n warnings: MarcWarning[],\n strict: boolean,\n maxWarnings: number\n): DirectoryEntry[] {\n const entries: DirectoryEntry[] = [];\n\n for (let i = 0; i < directoryBytes.length; i += DIRECTORY_ENTRY_LENGTH) {\n if (warnings.length >= maxWarnings) {\n warnings.push(\n createWarning(\n 'truncated_record',\n `Directory parsing halted after reaching maxWarnings limit (${maxWarnings}); ` +\n `remaining ${directoryBytes.length - i} bytes of directory not parsed.`\n )\n );\n break;\n }\n\n if (i + DIRECTORY_ENTRY_LENGTH > directoryBytes.length) {\n // Partial entry, skip\n break;\n }\n\n const entryBytes = directoryBytes.slice(i, i + DIRECTORY_ENTRY_LENGTH);\n const entryStr = UTF8_DECODER.decode(entryBytes);\n\n const tag = entryStr.substring(0, TAG_LENGTH);\n const fieldLengthStr = entryStr.substring(TAG_LENGTH, TAG_LENGTH + FIELD_LENGTH_SIZE);\n const startingPositionStr = entryStr.substring(\n TAG_LENGTH + FIELD_LENGTH_SIZE,\n DIRECTORY_ENTRY_LENGTH\n );\n\n const fieldLength = parseInt(fieldLengthStr, 10);\n const startingPosition = parseInt(startingPositionStr, 10);\n\n if (isNaN(fieldLength) || isNaN(startingPosition)) {\n const warning = createWarning(\n 'invalid_directory',\n `Invalid directory entry for tag ${tag}: length=${fieldLengthStr}, position=${startingPositionStr}`\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n continue;\n }\n\n entries.push({ tag, fieldLength, startingPosition });\n }\n\n return entries;\n}\n\n/**\n * Parse all fields using directory entries.\n */\nfunction parseFields(\n buffer: Uint8Array,\n directoryEntries: DirectoryEntry[],\n baseAddress: number,\n decodeBytes: (b: Uint8Array) => string,\n warnings: MarcWarning[],\n strict: boolean,\n maxWarnings: number\n): (ControlField | DataField)[] {\n const fields: (ControlField | DataField)[] = [];\n\n for (const entry of directoryEntries) {\n if (warnings.length >= maxWarnings) {\n warnings.push(\n createWarning(\n 'truncated_record',\n `Field parsing halted after reaching maxWarnings limit (${maxWarnings}); ` +\n `not all directory entries were processed.`,\n undefined,\n entry.tag\n )\n );\n break;\n }\n\n const start = baseAddress + entry.startingPosition;\n const end = start + entry.fieldLength - 1; // -1 for field terminator\n\n // Bounds check\n if (start >= buffer.length || end > buffer.length) {\n const warning = createWarning(\n 'invalid_field',\n `Field ${entry.tag} out of bounds: start=${start}, end=${end}, buffer length=${buffer.length}`,\n start,\n entry.tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n continue;\n }\n\n // Verify the byte at `end` is actually the field terminator. If it isn't,\n // the directory's fieldLength was off or the record is malformed — emit a\n // warning and use the full declared span so we don't silently drop the\n // last real byte of data.\n let fieldBytes: Uint8Array;\n if (buffer[end] !== FIELD_TERMINATOR) {\n const warning = createWarning(\n 'invalid_field',\n `Field ${entry.tag} does not end with a field terminator at byte ${end} ` +\n `(found 0x${(buffer[end] ?? 0).toString(16).padStart(2, '0')}); ` +\n `using the full declared length without stripping a terminator byte.`,\n start,\n entry.tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n fieldBytes = buffer.slice(start, start + entry.fieldLength);\n } else {\n fieldBytes = buffer.slice(start, end);\n }\n\n // Control field (00X): no indicators, just data\n if (entry.tag.startsWith('00')) {\n try {\n const data = decodeBytes(fieldBytes);\n fields.push({ tag: entry.tag, data });\n } catch (error) {\n const warning = createWarning(\n 'encoding_error',\n `Failed to decode control field ${entry.tag}: ` +\n `${error instanceof Error ? error.message : String(error)}. ` +\n `Raw bytes (hex): ${bytesPreview(fieldBytes)}.`,\n start,\n entry.tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n // Preserve the field shape with a best-effort decode so callers retain\n // the record's structure rather than losing the field entirely.\n fields.push({ tag: entry.tag, data: bestEffortDecode(fieldBytes) });\n }\n continue;\n }\n\n // Data field (01X-9XX): indicators + subfields\n if (fieldBytes.length < 2) {\n const warning = createWarning(\n 'invalid_field',\n `Data field ${entry.tag} too short for indicators: ${fieldBytes.length} bytes`,\n start,\n entry.tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n continue;\n }\n\n try {\n const indicator1 = String.fromCharCode(fieldBytes[0] ?? 0);\n const indicator2 = String.fromCharCode(fieldBytes[1] ?? 0);\n const subfieldBytes = fieldBytes.slice(2); // Skip 2 indicator bytes\n\n const subfields = parseSubfields(\n subfieldBytes,\n decodeBytes,\n entry.tag,\n warnings,\n strict,\n maxWarnings\n );\n\n fields.push({\n tag: entry.tag,\n indicator1,\n indicator2,\n subfields,\n });\n } catch (error) {\n const warning = createWarning(\n 'invalid_field',\n `Failed to parse data field ${entry.tag}: ${error instanceof Error ? error.message : String(error)}`,\n start,\n entry.tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n }\n }\n\n return fields;\n}\n\n/**\n * Parse subfields from a data field.\n */\nfunction parseSubfields(\n subfieldBytes: Uint8Array,\n decodeBytes: (b: Uint8Array) => string,\n tag: string,\n warnings: MarcWarning[],\n strict: boolean,\n maxWarnings: number\n): Subfield[] {\n const subfields: Subfield[] = [];\n let i = 0;\n\n while (i < subfieldBytes.length) {\n if (warnings.length >= maxWarnings) {\n warnings.push(\n createWarning(\n 'truncated_record',\n `Subfield parsing halted after reaching maxWarnings limit (${maxWarnings}) ` +\n `in field ${tag}; not all subfields were processed.`,\n undefined,\n tag\n )\n );\n break;\n }\n\n // Expect SUBFIELD_DELIMITER\n if (subfieldBytes[i] !== SUBFIELD_DELIMITER) {\n const warning = createWarning(\n 'invalid_field',\n `Expected subfield delimiter in field ${tag} at position ${i}`,\n undefined,\n tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n break;\n }\n\n i++; // Skip delimiter\n\n if (i >= subfieldBytes.length) break;\n\n // Next byte is subfield code\n const code = String.fromCharCode(subfieldBytes[i] ?? 0);\n i++;\n\n // Value extends until next delimiter or end\n const valueStart = i;\n while (i < subfieldBytes.length && subfieldBytes[i] !== SUBFIELD_DELIMITER) {\n i++;\n }\n\n const valueBytes = subfieldBytes.slice(valueStart, i);\n try {\n const value = decodeBytes(valueBytes);\n subfields.push({ code, value });\n } catch (error) {\n const warning = createWarning(\n 'encoding_error',\n `Failed to decode subfield ${tag}$${code}: ` +\n `${error instanceof Error ? error.message : String(error)}. ` +\n `Raw bytes (hex): ${bytesPreview(valueBytes)}.`,\n undefined,\n tag\n );\n if (strict) throw new Error(warning.message);\n warnings.push(warning);\n // Preserve the subfield shape so callers don't lose data structure.\n subfields.push({ code, value: bestEffortDecode(valueBytes) });\n }\n }\n\n return subfields;\n}\n\n/**\n * Create a warning object.\n */\nfunction createWarning(\n type: MarcWarningType,\n message: string,\n position?: number,\n tag?: string\n): MarcWarning {\n return { type, message, position, tag };\n}\n","/**\n * Warning system for MARC parsing.\n * Re-exports types and provides utility functions.\n */\n\nimport type { MarcWarning, MarcWarningType, ParseOptions, ParseResult } from './types';\n\n// Re-export types for convenience\nexport type { MarcWarning, MarcWarningType, ParseOptions, ParseResult };\n\n/**\n * Create a warning object.\n *\n * @param type - The warning type\n * @param message - The warning message\n * @param position - Optional byte position in the record\n * @param tag - Optional field tag associated with the warning\n * @returns A MarcWarning object\n *\n * @example\n * ```typescript\n * const warning = createWarning(\n * 'invalid_leader',\n * 'Leader length is invalid',\n * 0,\n * undefined\n * );\n * ```\n */\nexport function createWarning(\n type: MarcWarningType,\n message: string,\n position?: number,\n tag?: string\n): MarcWarning {\n return { type, message, position, tag };\n}\n","/**\n * ISO2709 binary format serializer for MARC21 records.\n * Converts MarcRecord objects back to binary format.\n */\n\nimport type { MarcRecord, ControlField, DataField, MarcWarning } from './types';\nimport { isControlField } from './types';\nimport { unicodeToMarc8WithStats } from './marc8';\nimport { createWarning } from './warnings';\n\n/**\n * Options for serializing MARC records.\n */\nexport interface SerializeOptions {\n /**\n * Character encoding to use for field content.\n * - 'utf8' (default): UTF-8; sets leader byte 9 to 'a'.\n * - 'marc8': conservative MARC-8/ANSEL output; sets leader byte 9 to ' ' (space).\n * Broad MARC-8 script decoding is supported by the parser, but encoding is limited\n * to ASCII plus ANSEL Latin/combining characters.\n */\n readonly encoding?: 'utf8' | 'marc8';\n}\n\n// ISO2709 separator characters\nconst SUBFIELD_DELIMITER = 0x1f; // ASCII 31 (IS1 - Unit Separator)\nconst FIELD_TERMINATOR = 0x1e; // ASCII 30 (IS2 - Information Separator)\nconst RECORD_TERMINATOR = 0x1d; // ASCII 29 (IS3 - Group Separator)\n\n// Leader constants\nconst LEADER_LENGTH = 24;\nconst TAG_LENGTH = 3;\nconst FIELD_LENGTH_SIZE = 4;\nconst STARTING_POSITION_SIZE = 5;\n\n/**\n * Serialize a MARC record to ISO2709 binary format.\n *\n * @param record - The MARC record to serialize\n * @returns Binary representation of the record (Uint8Array for browser compatibility)\n *\n * @example\n * ```typescript\n * const record: MarcRecord = {\n * leader: '00000nam 2200000 4500',\n * fields: [\n * { tag: '001', data: 'ocm12345678' },\n * {\n * tag: '245',\n * indicator1: '1',\n * indicator2: '0',\n * subfields: [{ code: 'a', value: 'Title' }],\n * },\n * ],\n * };\n *\n * const buffer = serializeMarcRecord(record);\n * // Can now be written to file or transmitted\n * ```\n */\nexport function serializeMarcRecord(\n record: MarcRecord,\n options: SerializeOptions = {}\n): Uint8Array {\n return serializeMarcRecordWithWarnings(record, options).bytes;\n}\n\n/**\n * Result of {@link serializeMarcRecordWithWarnings}: the serialized bytes\n * along with any warnings generated by the encoder (e.g. lossy MARC-8\n * substitutions).\n */\nexport interface SerializeResult {\n readonly bytes: Uint8Array;\n readonly warnings: readonly MarcWarning[];\n}\n\n/**\n * Serialize a MARC record and surface any warnings generated by the encoder.\n * Use this when you need programmatic visibility into lossy encodings — for\n * example, MARC-8 output of records containing characters with no MARC-8\n * equivalent.\n */\nexport function serializeMarcRecordWithWarnings(\n record: MarcRecord,\n options: SerializeOptions = {}\n): SerializeResult {\n validateRecord(record);\n const warnings: MarcWarning[] = [];\n\n const useMarc8 = options.encoding === 'marc8';\n const encoder = new TextEncoder();\n const encodeText: (s: string, tag?: string) => Uint8Array = useMarc8\n ? (s, tag) => {\n const result = unicodeToMarc8WithStats(s);\n if (result.lossyCount > 0) {\n warnings.push(\n createWarning(\n 'encoding_error',\n `MARC-8 encoding substituted ${result.lossyCount} character(s) with '?' ` +\n `because they have no MARC-8 equivalent.`,\n undefined,\n tag\n )\n );\n }\n return result.bytes;\n }\n : (s) => encoder.encode(s);\n\n // Build directory and data sections\n const directoryEntries: string[] = [];\n const dataSegments: Uint8Array[] = [];\n let dataPosition = 0;\n\n for (const field of record.fields) {\n const fieldData = serializeField(field, (s) => encodeText(s, field.tag));\n const fieldLength = fieldData.length + 1; // +1 for field terminator\n\n // Directory entry: tag (3) + length (4) + position (5) = 12 bytes\n const entry =\n field.tag.padEnd(TAG_LENGTH, ' ') +\n fieldLength.toString().padStart(FIELD_LENGTH_SIZE, '0') +\n dataPosition.toString().padStart(STARTING_POSITION_SIZE, '0');\n\n directoryEntries.push(entry);\n dataSegments.push(fieldData);\n dataSegments.push(new Uint8Array([FIELD_TERMINATOR]));\n\n dataPosition += fieldLength;\n }\n\n // Build directory\n const directory = encoder.encode(directoryEntries.join(''));\n const directoryWithTerminator = new Uint8Array(directory.length + 1);\n directoryWithTerminator.set(directory);\n directoryWithTerminator[directory.length] = FIELD_TERMINATOR;\n\n // Calculate base address: leader (24) + directory + terminator\n const baseAddress = LEADER_LENGTH + directoryWithTerminator.length;\n\n // Build data section\n const totalDataLength = dataSegments.reduce((sum, seg) => sum + seg.length, 0);\n const data = new Uint8Array(totalDataLength);\n let offset = 0;\n for (const segment of dataSegments) {\n data.set(segment, offset);\n offset += segment.length;\n }\n\n // Build leader with calculated values\n const recordLength = baseAddress + totalDataLength + 1; // +1 for record terminator\n const leader = buildLeader(record.leader, recordLength, baseAddress, useMarc8);\n\n // Assemble final record\n const bytes = new Uint8Array(recordLength);\n bytes.set(encoder.encode(leader), 0);\n bytes.set(directoryWithTerminator, LEADER_LENGTH);\n bytes.set(data, baseAddress);\n bytes[recordLength - 1] = RECORD_TERMINATOR;\n\n return { bytes, warnings };\n}\n\nfunction isAsciiPrintable(s: string): boolean {\n const c = s.charCodeAt(0);\n return c >= 0x20 && c <= 0x7e;\n}\n\n/**\n * Validate a record at the serializer boundary. Throws on any input that would\n * produce silently-corrupted bytes (e.g. an empty subfield code that would\n * serialize to a 0x00 byte and round-trip back as a space, or a non-ASCII\n * character whose code point > 0xFF would be truncated when stored in a Uint8Array).\n */\nfunction validateRecord(record: MarcRecord): void {\n for (const field of record.fields) {\n if (typeof field.tag !== 'string' || field.tag.length !== 3) {\n throw new Error(\n `MARC field tag must be exactly 3 characters; got ${JSON.stringify(field.tag)}`\n );\n }\n\n if (isControlField(field)) {\n continue;\n }\n\n if (field.indicator1.length !== 1) {\n throw new Error(\n `Field ${field.tag} indicator1 must be exactly 1 character; got ${JSON.stringify(field.indicator1)}`\n );\n }\n if (!isAsciiPrintable(field.indicator1)) {\n throw new Error(\n `Field ${field.tag} indicator1 must be an ASCII printable character (U+0020–U+007E); got ${JSON.stringify(field.indicator1)}`\n );\n }\n if (field.indicator2.length !== 1) {\n throw new Error(\n `Field ${field.tag} indicator2 must be exactly 1 character; got ${JSON.stringify(field.indicator2)}`\n );\n }\n if (!isAsciiPrintable(field.indicator2)) {\n throw new Error(\n `Field ${field.tag} indicator2 must be an ASCII printable character (U+0020–U+007E); got ${JSON.stringify(field.indicator2)}`\n );\n }\n for (const sf of field.subfields) {\n if (sf.code.length !== 1) {\n throw new Error(\n `Field ${field.tag} subfield code must be exactly 1 character; got ${JSON.stringify(sf.code)}`\n );\n }\n if (!isAsciiPrintable(sf.code)) {\n throw new Error(\n `Field ${field.tag} subfield code must be an ASCII printable character (U+0020–U+007E); got ${JSON.stringify(sf.code)}`\n );\n }\n }\n }\n}\n\n/**\n * Serialize a single field (control or data) to bytes.\n */\nfunction serializeField(\n field: ControlField | DataField,\n encodeText: (s: string) => Uint8Array\n): Uint8Array {\n if (isControlField(field)) {\n return encodeText(field.data);\n }\n\n // Data field: indicators (always ASCII) + subfields.\n // validateRecord guarantees indicators and subfield codes are exactly 1 char,\n // so no fallback substitution is needed here.\n const indicatorBytes = new Uint8Array([\n field.indicator1.charCodeAt(0),\n field.indicator2.charCodeAt(0),\n ]);\n\n const subfieldParts: Uint8Array[] = [indicatorBytes];\n for (const subfield of field.subfields) {\n const delimAndCode = new Uint8Array([SUBFIELD_DELIMITER, subfield.code.charCodeAt(0)]);\n subfieldParts.push(delimAndCode, encodeText(subfield.value));\n }\n\n const totalLen = subfieldParts.reduce((n, b) => n + b.length, 0);\n const out = new Uint8Array(totalLen);\n let off = 0;\n for (const part of subfieldParts) {\n out.set(part, off);\n off += part.length;\n }\n return out;\n}\n\n/**\n * Build a leader string with updated record length and base address.\n *\n * @param originalLeader - The original leader (24 characters)\n * @param recordLength - The calculated total record length\n * @param baseAddress - The calculated base address of data\n * @returns Updated leader (24 characters)\n */\nfunction buildLeader(\n originalLeader: string,\n recordLength: number,\n baseAddress: number,\n useMarc8: boolean\n): string {\n // Ensure original leader is 24 characters\n let leader = originalLeader.padEnd(LEADER_LENGTH, ' ').substring(0, LEADER_LENGTH);\n\n // Update record length (positions 00-04)\n const recordLengthStr = recordLength.toString().padStart(5, '0');\n if (recordLengthStr.length > 5) {\n throw new Error(`Record length ${recordLength} exceeds maximum (99999)`);\n }\n leader = recordLengthStr + leader.substring(5);\n\n // Update base address (positions 12-16)\n const baseAddressStr = baseAddress.toString().padStart(5, '0');\n if (baseAddressStr.length > 5) {\n throw new Error(`Base address ${baseAddress} exceeds maximum (99999)`);\n }\n leader = leader.substring(0, 12) + baseAddressStr + leader.substring(17);\n\n // Update leader byte 9 (character encoding scheme): ' ' = MARC8, 'a' = UTF-8\n leader = leader.substring(0, 9) + (useMarc8 ? ' ' : 'a') + leader.substring(10);\n\n return leader;\n}\n","/**\n * Basic field and subfield access utilities.\n * Provides simple getter functions for accessing MARC record data.\n */\n\nimport type { MarcRecord, ControlField, DataField } from './types';\n\n/**\n * Get the first field with the specified tag.\n *\n * @param record - The MARC record to search\n * @param tag - The field tag to find (e.g., '245', '100')\n * @returns The first matching field, or undefined if not found\n *\n * @example\n * ```typescript\n * const titleField = getField(record, '245');\n * if (titleField && isDataField(titleField)) {\n * console.log('Title field found');\n * }\n * ```\n */\nexport function getField(record: MarcRecord, tag: string): ControlField | DataField | undefined {\n return record.fields.find((f) => f.tag === tag);\n}\n\n/**\n * Get all fields with the specified tag.\n *\n * @param record - The MARC record to search\n * @param tag - The field tag to find (e.g., '650', '700')\n * @returns Array of matching fields (empty if none found)\n *\n * @example\n * ```typescript\n * const subjectFields = getFields(record, '650');\n * console.log(`Found ${subjectFields.length} subject fields`);\n * ```\n */\nexport function getFields(record: MarcRecord, tag: string): (ControlField | DataField)[] {\n return record.fields.filter((f) => f.tag === tag);\n}\n\n/**\n * Get the first subfield with the specified code from a data field.\n *\n * @param field - The data field to search\n * @param code - The subfield code to find (e.g., 'a', 'b')\n * @returns The subfield value, or undefined if not found\n *\n * @example\n * ```typescript\n * const field = getField(record, '245');\n * if (field && isDataField(field)) {\n * const title = getSubfield(field, 'a');\n * console.log('Title:', title);\n * }\n * ```\n */\nexport function getSubfield(field: DataField, code: string): string | undefined {\n const subfield = field.subfields.find((sf) => sf.code === code);\n return subfield?.value;\n}\n\n/**\n * Get all subfields with the specified code from a data field.\n * Useful for repeatable subfields.\n *\n * @param field - The data field to search\n * @param code - The subfield code to find (e.g., 'a', 'x')\n * @returns Array of subfield values (empty if none found)\n *\n * @example\n * ```typescript\n * const field = getField(record, '650');\n * if (field && isDataField(field)) {\n * const subdivisions = getSubfields(field, 'x');\n * console.log('Subdivisions:', subdivisions);\n * }\n * ```\n */\nexport function getSubfields(field: DataField, code: string): string[] {\n return field.subfields.filter((sf) => sf.code === code).map((sf) => sf.value);\n}\n\n/**\n * Get all subfields from a data field as an array of {code, value} pairs.\n *\n * @param field - The data field\n * @returns Array of subfield objects\n *\n * @example\n * ```typescript\n * const field = getField(record, '245');\n * if (field && isDataField(field)) {\n * const allSubfields = getAllSubfields(field);\n * for (const sf of allSubfields) {\n * console.log(`$${sf.code}: ${sf.value}`);\n * }\n * }\n * ```\n */\nexport function getAllSubfields(field: DataField): Array<{ code: string; value: string }> {\n return field.subfields.map((sf) => ({ code: sf.code, value: sf.value }));\n}\n","/**\n * Convenience accessor functions for common bibliographic data.\n */\n\nimport type { MarcRecord } from './types';\nimport { isDataField } from './types';\nimport { getField, getSubfield } from './field-utils';\n\n/**\n * Extract the title from a MARC record (245 $a$b).\n *\n * @param record - The MARC record\n * @returns The title, or undefined if not found\n *\n * @example\n * ```typescript\n * const titleText = title(record);\n * // \"The Catcher in the Rye\"\n * ```\n */\nexport function title(record: MarcRecord): string | undefined {\n const field = getField(record, '245');\n if (!field || !isDataField(field)) return undefined;\n\n // 245 $a$b (title proper + remainder of title)\n const a = getSubfield(field, 'a') ?? '';\n const b = getSubfield(field, 'b') ?? '';\n\n const combined = (a + ' ' + b).trim();\n return combined || undefined;\n}\n\n/**\n * Extract the title proper from a MARC record (245 $a only).\n * This is just the main title without subtitle.\n *\n * @param record - The MARC record\n * @returns The title proper, or undefined if not found\n *\n * @example\n * ```typescript\n * const mainTitle = titleProper(record);\n * // \"The Catcher in the Rye\"\n * ```\n */\nexport function titleProper(record: MarcRecord): string | undefined {\n const field = getField(record, '245');\n if (!field || !isDataField(field)) return undefined;\n return getSubfield(field, 'a');\n}\n\n/**\n * Extract the author from a MARC record (100 $a or 110 $a).\n *\n * @param record - The MARC record\n * @returns The author name, or undefined if not found\n *\n * @example\n * ```typescript\n * const authorName = author(record);\n * // \"Salinger, J. D.\"\n * ```\n */\nexport function author(record: MarcRecord): string | undefined {\n // Try 100 $a (personal name)\n const field100 = getField(record, '100');\n if (field100 && isDataField(field100)) {\n return getSubfield(field100, 'a');\n }\n\n // Try 110 $a (corporate name)\n const field110 = getField(record, '110');\n if (field110 && isDataField(field110)) {\n return getSubfield(field110, 'a');\n }\n\n return undefined;\n}\n\n/**\n * Extract the edition statement from a MARC record (250 $a).\n *\n * @param record - The MARC record\n * @returns The edition statement, or undefined if not found\n *\n * @example\n * ```typescript\n * const ed = edition(record);\n * // \"2nd ed.\"\n * ```\n */\nexport function edition(record: MarcRecord): string | undefined {\n const field = getField(record, '250');\n if (!field || !isDataField(field)) return undefined;\n return getSubfield(field, 'a');\n}\n\n/**\n * Extract the publisher from a MARC record (264 $b or 260 $b).\n * Tries RDA field (264) first, then falls back to AACR2 field (260).\n *\n * @param record - The MARC record\n * @returns The publisher name, or undefined if not found\n *\n * @example\n * ```typescript\n * const pub = publisher(record);\n * // \"Little, Brown and Company\"\n * ```\n */\nexport function publisher(record: MarcRecord): string | undefined {\n // Try 264 $b (RDA publication statement)\n const field264 = getField(record, '264');\n if (field264 && isDataField(field264)) {\n const pub = getSubfield(field264, 'b');\n if (pub) return pub;\n }\n\n // Try 260 $b (AACR2 publication statement)\n const field260 = getField(record, '260');\n if (field260 && isDataField(field260)) {\n return getSubfield(field260, 'b');\n }\n\n return undefined;\n}\n\n/**\n * Extract the publication date from a MARC record (264 $c or 260 $c).\n * Tries RDA field (264) first, then falls back to AACR2 field (260).\n *\n * @param record - The MARC record\n * @returns The publication date, or undefined if not found\n *\n * @example\n * ```typescript\n * const date = publicationDate(record);\n * // \"1951\"\n * ```\n */\nexport function publicationDate(record: MarcRecord): string | undefined {\n // Try 264 $c (RDA publication statement)\n const field264 = getField(record, '264');\n if (field264 && isDataField(field264)) {\n const date = getSubfield(field264, 'c');\n if (date) return date;\n }\n\n // Try 260 $c (AACR2 publication statement)\n const field260 = getField(record, '260');\n if (field260 && isDataField(field260)) {\n return getSubfield(field260, 'c');\n }\n\n return undefined;\n}\n\n/**\n * Extract all ISBNs from a MARC record (020 $a).\n * Returns an array because ISBN is a repeatable field.\n *\n * @param record - The MARC record\n * @returns Array of ISBNs (empty if none found)\n *\n * @example\n * ```typescript\n * const isbns = isbn(record);\n * // [\"978-0-316-76948-0\", \"0-316-76948-7\"]\n * ```\n */\nexport function isbn(record: MarcRecord): string[] {\n const results: string[] = [];\n\n for (const field of record.fields) {\n if (field.tag === '020' && isDataField(field)) {\n const value = getSubfield(field, 'a');\n if (value) results.push(value);\n }\n }\n\n return results;\n}\n\n/**\n * Extract the ISSN from a MARC record (022 $a).\n *\n * @param record - The MARC record\n * @returns The ISSN, or undefined if not found\n *\n * @example\n * ```typescript\n * const issnValue = issn(record);\n * // \"0028-0836\"\n * ```\n */\nexport function issn(record: MarcRecord): string | undefined {\n const field = getField(record, '022');\n if (!field || !isDataField(field)) return undefined;\n return getSubfield(field, 'a');\n}\n\n/**\n * Extract the LCCN from a MARC record (010 $a).\n *\n * @param record - The MARC record\n * @returns The LCCN, or undefined if not found\n *\n * @example\n * ```typescript\n * const lccnValue = lccn(record);\n * // \" 50011915 \"\n * ```\n */\nexport function lccn(record: MarcRecord): string | undefined {\n const field = getField(record, '010');\n if (!field || !isDataField(field)) return undefined;\n return getSubfield(field, 'a');\n}\n\n/**\n * Extract all subject headings from a MARC record (6XX $a).\n * Includes all 6XX fields (600-699).\n *\n * @param record - The MARC record\n * @returns Array of subject headings (empty if none found)\n *\n * @example\n * ```typescript\n * const subjectList = subjects(record);\n * // [\"History\", \"Biography\", \"Fiction\"]\n * ```\n */\nexport function subjects(record: MarcRecord): string[] {\n const results: string[] = [];\n\n for (const field of record.fields) {\n // Match all 6XX fields (600-699)\n if (field.tag.startsWith('6') && isDataField(field)) {\n const value = getSubfield(field, 'a');\n if (value) results.push(value);\n }\n }\n\n return results;\n}\n\n/**\n * Extract the series statement from a MARC record (490 $a).\n *\n * @param record - The MARC record\n * @returns The series statement, or undefined if not found\n *\n * @example\n * ```typescript\n * const series = seriesStatement(record);\n * // \"Penguin classics\"\n * ```\n */\nexport function seriesStatement(record: MarcRecord): string | undefined {\n const field = getField(record, '490');\n if (!field || !isDataField(field)) return undefined;\n return getSubfield(field, 'a');\n}\n","/**\n * Wildcard field querying for MARC records.\n * Supports wildcard patterns for flexible field matching.\n */\n\nimport type { MarcRecord, ControlField, DataField } from './types';\n\n/**\n * Check if a MARC tag matches a wildcard pattern.\n * Pattern syntax: digits = exact match, '.' or 'X' (case-insensitive) = any digit\n *\n * @param tag - The MARC tag to test (e.g., '650')\n * @param pattern - The pattern to match against (e.g., '6..', '7XX', '24X')\n * @returns True if the tag matches the pattern\n *\n * @example\n * ```typescript\n * matchesPattern('650', '6..') // true\n * matchesPattern('700', '6..') // false\n * matchesPattern('245', '24X') // true\n * matchesPattern('100', 'X00') // true\n * ```\n */\nfunction matchesPattern(tag: string, pattern: string): boolean {\n if (tag.length !== 3 || pattern.length !== 3) return false;\n\n for (let i = 0; i < 3; i++) {\n const patternChar = pattern[i];\n const tagChar = tag[i];\n\n // Wildcard: '.' or 'X' matches any digit\n if (patternChar === '.' || patternChar?.toUpperCase() === 'X') {\n // Verify tag character is a digit\n if (tagChar && !/\\d/.test(tagChar)) return false;\n continue;\n }\n\n // Exact match required\n if (patternChar !== tagChar) return false;\n }\n\n return true;\n}\n\n/**\n * Get all fields matching a wildcard pattern.\n *\n * @param record - The MARC record to search\n * @param pattern - The pattern to match (e.g., '6..', '7XX', '24X', 'X00')\n * @returns Array of matching fields (empty if none found)\n *\n * @example\n * ```typescript\n * // Get all 6XX subject fields\n * const subjectFields = getFieldsByPattern(record, '6..');\n *\n * // Get all 7XX added entry fields\n * const addedEntries = getFieldsByPattern(record, '7XX');\n *\n * // Get all X00 fields (100, 200, 300, etc.)\n * const x00Fields = getFieldsByPattern(record, 'X00');\n * ```\n */\nexport function getFieldsByPattern(\n record: MarcRecord,\n pattern: string\n): (ControlField | DataField)[] {\n return record.fields.filter((field) => matchesPattern(field.tag, pattern));\n}\n\n/**\n * Get the first field matching a wildcard pattern.\n *\n * @param record - The MARC record to search\n * @param pattern - The pattern to match (e.g., '6..', '7XX')\n * @returns The first matching field, or undefined if not found\n *\n * @example\n * ```typescript\n * const firstSubject = getFirstFieldByPattern(record, '6..');\n * if (firstSubject && isDataField(firstSubject)) {\n * console.log('First subject:', getSubfield(firstSubject, 'a'));\n * }\n * ```\n */\nexport function getFirstFieldByPattern(\n record: MarcRecord,\n pattern: string\n): ControlField | DataField | undefined {\n return record.fields.find((field) => matchesPattern(field.tag, pattern));\n}\n","/**\n * Immutable field operation functions.\n * All operations return new records/fields, never mutating the originals.\n */\n\nimport type { MarcRecord, ControlField, DataField } from './types';\n\n/**\n * Append a field to the end of a record.\n * Returns a new record, does not mutate the original.\n *\n * @param record - The MARC record\n * @param field - The field to append\n * @returns A new record with the field appended\n *\n * @example\n * ```typescript\n * const newField: DataField = {\n * tag: '650',\n * indicator1: ' ',\n * indicator2: '0',\n * subfields: [{ code: 'a', value: 'New subject' }],\n * };\n * const updated = appendField(record, newField);\n * // record is unchanged, updated contains the new field\n * ```\n */\nexport function appendField(record: MarcRecord, field: ControlField | DataField): MarcRecord {\n return {\n ...record,\n fields: [...record.fields, field],\n };\n}\n\n/**\n * Insert a field before the first occurrence of a tag.\n * Returns a new record, does not mutate the original.\n *\n * @param record - The MARC record\n * @param tag - The tag to insert before\n * @param field - The field to insert\n * @returns A new record with the field inserted\n *\n * @example\n * ```typescript\n * const newField: DataField = {\n * tag: '650',\n * indicator1: ' ',\n * indicator2: '0',\n * subfields: [{ code: 'a', value: 'New subject' }],\n * };\n * const updated = insertFieldBefore(record, '700', newField);\n * // Inserts the 650 field before the first 700 field\n * ```\n */\nexport function insertFieldBefore(\n record: MarcRecord,\n tag: string,\n field: ControlField | DataField\n): MarcRecord {\n const index = record.fields.findIndex((f) => f.tag === tag);\n\n if (index === -1) {\n // Tag not found, append to end\n return appendField(record, field);\n }\n\n // Optimized: Use Array.from + splice instead of multiple slice operations\n const newFields = Array.from(record.fields);\n newFields.splice(index, 0, field);\n\n return { ...record, fields: newFields };\n}\n\n/**\n * Insert a field after the first occurrence of a tag.\n * Returns a new record, does not mutate the original.\n *\n * @param record - The MARC record\n * @param tag - The tag to insert after\n * @param field - The field to insert\n * @returns A new record with the field inserted\n *\n * @example\n * ```typescript\n * const updated = insertFieldAfter(record, '245', newField);\n * // Inserts the field after the first 245 field\n * ```\n */\nexport function insertFieldAfter(\n record: MarcRecord,\n tag: string,\n field: ControlField | DataField\n): MarcRecord {\n const index = record.fields.findIndex((f) => f.tag === tag);\n\n if (index === -1) {\n // Tag not found, append to end\n return appendField(record, field);\n }\n\n // Optimized: Use Array.from + splice instead of multiple slice operations\n const newFields = Array.from(record.fields);\n newFields.splice(index + 1, 0, field);\n\n return { ...record, fields: newFields };\n}\n\n/**\n * Insert a field in MARC block order.\n * Maintains proper MARC21 field ordering: LDR → 00X → 0XX → 1XX → ... → 9XX\n *\n * @param record - The MARC record\n * @param field - The field to insert\n * @returns A new record with the field inserted in proper order\n *\n * @example\n * ```typescript\n * const field650: DataField = {\n * tag: '650',\n * indicator1: ' ',\n * indicator2: '0',\n * subfields: [{ code: 'a', value: 'Subject' }],\n * };\n * const updated = insertGroupedField(record, field650);\n * // Field is inserted after other 6XX fields but before 7XX fields\n * ```\n */\nexport function insertGroupedField(\n record: MarcRecord,\n field: ControlField | DataField\n): MarcRecord {\n // MARC fields are ordered by numeric tag: LDR → 001 → 010 → 100 → 245 → 999.\n // Compare tags numerically — a coarse first-digit-block grouping conflates\n // 010 with 100 and silently misorders inserted control fields.\n const targetTag = parseInt(field.tag, 10);\n\n // Find insertion point: before the first existing field whose numeric tag\n // is strictly greater than the new field's.\n let insertIndex = record.fields.length;\n for (let i = 0; i < record.fields.length; i++) {\n const currentField = record.fields[i];\n if (!currentField) continue;\n\n const currentTag = parseInt(currentField.tag, 10);\n // NaN compares false to everything, so non-numeric tags sort to the end —\n // matching today's behavior for tags like 'XX1'.\n if (currentTag > targetTag) {\n insertIndex = i;\n break;\n }\n }\n\n const newFields = Array.from(record.fields);\n newFields.splice(insertIndex, 0, field);\n\n return { ...record, fields: newFields };\n}\n\n/**\n * Remove all fields with a specific tag.\n * Returns a new record, does not mutate the original.\n *\n * @param record - The MARC record\n * @param tag - The tag of fields to remove\n * @returns A new record with the fields removed\n *\n * @example\n * ```typescript\n * const updated = removeFields(record, '650');\n * // All 650 fields are removed from the returned record\n * ```\n */\nexport function removeFields(record: MarcRecord, tag: string): MarcRecord {\n return {\n ...record,\n fields: record.fields.filter((f) => f.tag !== tag),\n };\n}\n\n/**\n * Remove a specific field instance from a record.\n * Uses reference equality to identify the field.\n *\n * @param record - The MARC record\n * @param field - The specific field instance to remove\n * @returns A new record with the field removed\n *\n * @example\n * ```typescript\n * const field = getField(record, '650');\n * if (field) {\n * const updated = removeField(record, field);\n * // That specific field is removed\n * }\n * ```\n */\nexport function removeField(record: MarcRecord, field: ControlField | DataField): MarcRecord {\n return {\n ...record,\n fields: record.fields.filter((f) => f !== field),\n };\n}\n\n/**\n * Add a subfield to a data field.\n * Returns a new field, does not mutate the original.\n *\n * @param field - The data field\n * @param code - The subfield code\n * @param value - The subfield value\n * @returns A new field with the subfield added\n *\n * @example\n * ```typescript\n * const field = getField(record, '245');\n * if (field && isDataField(field)) {\n * const updated = addSubfield(field, 'c', 'Author name');\n * // field is unchanged, updated has the new subfield\n * }\n * ```\n */\nexport function addSubfield(field: DataField, code: string, value: string): DataField {\n return {\n ...field,\n subfields: [...field.subfields, { code, value }],\n };\n}\n\n/**\n * Remove all subfields with a specific code from a data field.\n * Returns a new field, does not mutate the original.\n *\n * @param field - The data field\n * @param code - The subfield code to remove\n * @returns A new field with the subfields removed\n *\n * @example\n * ```typescript\n * const updated = removeSubfield(field, 'x');\n * // All $x subfields are removed\n * ```\n */\nexport function removeSubfield(field: DataField, code: string): DataField {\n return {\n ...field,\n subfields: field.subfields.filter((sf) => sf.code !== code),\n };\n}\n\n/**\n * Replace the first subfield with a specific code in a data field.\n * If the subfield doesn't exist, adds it.\n * Returns a new field, does not mutate the original.\n *\n * @param field - The data field\n * @param code - The subfield code to replace\n * @param newValue - The new value for the subfield\n * @returns A new field with the subfield replaced\n *\n * @example\n * ```typescript\n * const updated = replaceSubfield(field, 'a', 'New title');\n * // First $a subfield is replaced with new value\n * ```\n */\nexport function replaceSubfield(field: DataField, code: string, newValue: string): DataField {\n const index = field.subfields.findIndex((sf) => sf.code === code);\n\n if (index === -1) {\n // Not found, add new subfield\n return addSubfield(field, code, newValue);\n }\n\n const newSubfields = [...field.subfields];\n newSubfields[index] = { code, value: newValue };\n\n return { ...field, subfields: newSubfields };\n}\n","/**\n * Deep cloning and equality checking for MARC records.\n */\n\nimport type { MarcRecord, ControlField, DataField } from './types';\nimport { isControlField } from './types';\n\n/**\n * Create a deep clone of a MARC record.\n *\n * @param record - The MARC record to clone\n * @returns A new, independent copy of the record\n *\n * @example\n * ```typescript\n * const copy = cloneRecord(record);\n * // Modifying copy will not affect record\n * ```\n */\nexport function cloneRecord(record: MarcRecord): MarcRecord {\n // Optimized: Pre-allocate arrays for better performance\n const fields = new Array<ControlField | DataField>(record.fields.length);\n\n for (let i = 0; i < record.fields.length; i++) {\n const field = record.fields[i]!;\n\n if (isControlField(field)) {\n fields[i] = { tag: field.tag, data: field.data };\n } else {\n // Pre-allocate subfield array\n const subfields = new Array<{ code: string; value: string }>(field.subfields.length);\n for (let j = 0; j < field.subfields.length; j++) {\n const sf = field.subfields[j]!;\n subfields[j] = { code: sf.code, value: sf.value };\n }\n\n fields[i] = {\n tag: field.tag,\n indicator1: field.indicator1,\n indicator2: field.indicator2,\n subfields: subfields,\n };\n }\n }\n\n return { leader: record.leader, fields };\n}\n\n/**\n * Check if two MARC records are deeply equal.\n *\n * @param a - First record to compare\n * @param b - Second record to compare\n * @param ignoreFieldOrder - If true, records with same fields in different order are considered equal\n * @returns True if records are equal\n *\n * @example\n * ```typescript\n * if (recordsEqual(record1, record2)) {\n * console.log('Records are identical');\n * }\n *\n * // Ignore field order\n * if (recordsEqual(record1, record2, true)) {\n * console.log('Records have same content');\n * }\n * ```\n */\nexport function recordsEqual(a: MarcRecord, b: MarcRecord, ignoreFieldOrder = false): boolean {\n if (a.leader !== b.leader) return false;\n if (a.fields.length !== b.fields.length) return false;\n\n const fieldsA = ignoreFieldOrder ? [...a.fields].sort(compareFields) : a.fields;\n const fieldsB = ignoreFieldOrder ? [...b.fields].sort(compareFields) : b.fields;\n\n for (let i = 0; i < fieldsA.length; i++) {\n const fieldA = fieldsA[i];\n const fieldB = fieldsB[i];\n if (!fieldA || !fieldB) return false;\n if (!fieldsEqual(fieldA, fieldB)) return false;\n }\n\n return true;\n}\n\n/**\n * Check if two fields are equal.\n *\n * @param a - First field to compare\n * @param b - Second field to compare\n * @returns True if fields are equal\n *\n * @example\n * ```typescript\n * const field1 = getField(record1, '245');\n * const field2 = getField(record2, '245');\n * if (field1 && field2 && fieldsEqual(field1, field2)) {\n * console.log('Title fields are identical');\n * }\n * ```\n */\nexport function fieldsEqual(a: ControlField | DataField, b: ControlField | DataField): boolean {\n if (a.tag !== b.tag) return false;\n\n if (isControlField(a) && isControlField(b)) {\n return a.data === b.data;\n }\n\n if (!isControlField(a) && !isControlField(b)) {\n if (a.indicator1 !== b.indicator1 || a.indicator2 !== b.indicator2) return false;\n if (a.subfields.length !== b.subfields.length) return false;\n\n for (let i = 0; i < a.subfields.length; i++) {\n const sfA = a.subfields[i];\n const sfB = b.subfields[i];\n if (!sfA || !sfB) return false;\n if (sfA.code !== sfB.code || sfA.value !== sfB.value) {\n return false;\n }\n }\n\n return true;\n }\n\n return false; // One is control field, other is data field\n}\n\n/**\n * Compare function for sorting fields by tag.\n */\nfunction compareFields(a: ControlField | DataField, b: ControlField | DataField): number {\n return a.tag.localeCompare(b.tag);\n}\n"],"mappings":";AASA,IAAM,IAAS,IACT,IAAc,KAmBd,IAAmD,oBAAI,IAAI;AAAA,EAC/D,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AACZ,CAAC,GAGK,IAA+C,oBAAI,IAAI;AAAA,EAC3D,CAAC,IAAM,GAAQ;AAAA,EACf,CAAC,IAAM,GAAQ;AAAA,EACf,CAAC,IAAM,GAAQ;AAAA,EACf,CAAC,IAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AAAA,EACf,CAAC,KAAM,GAAQ;AACjB,CAAC,GAEK,IAAiD,IAAI,IACzD,MAAM,KAAK,EAAgB,QAAQ,CAAC,EAAE,IAAA,CAAK,CAAC,GAAG,CAAA,MAAO,CAAC,GAAG,IAAI,GAAI,CAAC,CACrE,GAEM,IAAqD,IAAI,IAC7D,MAAM,KAAK,EAAoB,QAAQ,CAAC,EAAE,IAAA,CAAK,CAAC,GAAG,CAAA,MAAO,CAAC,GAAG,IAAI,GAAI,CAAC,CACzE,GAEM,IAA2C,EAC/C,IACA,4BACA,IACA,0BACF,GAEM,KAA4C,EAChD,IACA,6BACF,GAEM,KAA8C,EAClD,IACA,oCACA,IACA,kCACF,GAEM,KAA4C,oBAAI,IAAI;AAAA,EACxD,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AACZ,CAAC,GAEK,KAAyC,oBAAI,IAAI;AAAA,EACrD,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AACZ,CAAC,GAEK,KAA2C,oBAAI,IAAI;AAAA,EACvD,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,IAAM,GAAG;AAAA,EACV,CAAC,KAAM,GAAG;AACZ,CAAC,GASK,KAAoC,oBAAI,IAAI;AAAA,EAChD,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAAA,EACd,CAAC,SAAU,GAAG;AAChB,CAAC,GAEK,KAAmF;AAAA,EACvF,OAAO,oBAAI,IAAI;AAAA,EACf,OAAO,GAAU,GAAqB,CAAe;AAAA,EACrD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,aAAa;AACf;AAEA,SAAS,KACJ,GAC0B;AAC7B,QAAM,IAAQ,oBAAI,IAAoB;AACtC,WAAS,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK,GAAG;AACzC,UAAM,IAAQ,EAAO,CAAA,GACf,IAAQ,EAAO,IAAI,CAAA;AACzB,UAAM,KAAK,CAAK,EAAE,QAAA,CAAS,GAAM,MAAW,EAAM,IAAI,IAAQ,GAAQ,CAAI,CAAC;AAAA,EAC7E;AACA,SAAO;AACT;AAEA,SAAS,MAAa,GAAkE;AACtF,QAAM,IAAS,oBAAI,IAAoB;AACvC,aAAW,KAAO,EAChB,YAAW,CAAC,GAAK,CAAA,KAAU,EAAK,CAAA,EAAO,IAAI,GAAK,CAAK;AAEvD,SAAO;AACT;AAEA,SAAS,EAAgB,GAAuB;AAC9C,QAAM,IAAO,EAAK,YAAY,CAAC;AAC/B,SAAQ,KAAQ,OAAU,KAAQ,OAAY,KAAQ,SAAU,KAAQ;AAC1E;AAEA,SAAS,EAAe,GAAc,GAAuB;AAC3D,SAAO,IAAO,IAAO,MAAO;AAC9B;AAEA,SAAS,GAAiB,GAAc,GAAoB,GAAuB;AACjF,QAAM,IAAO,EAAe,GAAM,CAAI;AACtC,SAAI,MAAQ,UACH,KAAQ,MAAQ,KAAQ,MAAO,OAAO,aAAa,CAAI,IAAI,IAC7D,GAAmB,CAAA,EAAK,IAAI,CAAI,KAAK;AAC9C;AAEA,SAAS,GAAW,GAAmB,GAAa,GAA+C;AACjG,MAAI,IAAM,KAAK,EAAM,OAAQ,QAAO;AAAA,IAAE,MAAM;AAAA,IAAa,MAAM,EAAM;AAAA,EAAO;AAC5E,QAAM,IAAK,EAAe,EAAM,CAAA,GAAO,CAAI,GACrC,IAAK,EAAe,EAAM,IAAM,CAAA,GAAK,CAAI,GACzC,IAAK,EAAe,EAAM,IAAM,CAAA,GAAK,CAAI,GACzC,IAAO,KAAM,KAAO,KAAM,IAAK;AACrC,SAAO;AAAA,IAAE,MAAM,GAAK,IAAI,CAAG,KAAK;AAAA,IAAa,MAAM,IAAM;AAAA,EAAE;AAC7D;AAEA,SAAS,GACP,GACA,GACA,GACgC;AAChC,MAAI,IAAM,KAAK,EAAM,OAAQ,QAAO;AAAA,IAAE,MAAM;AAAA,IAAa,MAAM,EAAM;AAAA,EAAO;AAE5E,QAAM,IAAQ,EAAM,IAAM,CAAA;AAE1B,MAAI,GACA;AAEJ,MAAI,MAAU,MAAQ,MAAU;AAC9B,IAAA,IAAS,MACT,IAAa,IAAM;AAAA,WACV,MAAU,MAAQ,MAAU;AACrC,IAAA,IAAS,MACT,IAAa,IAAM;AAAA,WACV,MAAU,IAAM;AACzB,UAAM,IAAS,EAAM,IAAM,CAAA;AAC3B,QAAI,MAAW,OAAW,QAAO;AAAA,MAAE,MAAM;AAAA,MAAa,MAAM,EAAM;AAAA,IAAO;AACzE,IAAI,MAAW,MAAQ,MAAW,MAChC,IAAS,MACT,IAAa,IAAM,KACV,MAAW,MAAQ,MAAW,MACvC,IAAS,MACT,IAAa,IAAM,MAEnB,IAAS,MACT,IAAa,IAAM;AAAA,EAEvB,MACE,QAAO;AAAA,IAAE,MAAM;AAAA,IAAa,MAAM,IAAM;AAAA,EAAE;AAG5C,MAAI,KAAc,EAAM,OAAQ,QAAO;AAAA,IAAE,MAAM;AAAA,IAAa,MAAM,EAAM;AAAA,EAAO;AAE/E,MAAI,IAAQ,EAAM,CAAA;AAClB,MAAI,MAAU,IAAM;AAElB,QADA,KACI,KAAc,EAAM,OAAQ,QAAO;AAAA,MAAE,MAAM;AAAA,MAAa,MAAM,EAAM;AAAA,IAAO;AAC/E,IAAA,IAAQ,EAAM,CAAA;AAAA,EAChB;AAEA,QAAM,IAAM,GAAqB,CAAK;AACtC,SAAK,IACE;AAAA,IAAE,MAAM;AAAA,IAAI,MAAM,GAAY,GAAO,GAAQ,GAAK,IAAa,CAAC;AAAA,EAAE,IADxD;AAAA,IAAE,MAAM;AAAA,IAAa,MAAM,IAAa;AAAA,EAAE;AAE7D;AAEA,SAAS,GACP,GACA,GACA,GACA,GACQ;AACR,SAAA,EAAM,CAAA,IAAU,GACT;AACT;AAEA,SAAS,GAAqB,GAAoC;AAChE,UAAQ,GAAR;AAAA,IACE,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE;AAAA,EACJ;AACF;AAQA,SAAgB,GAAe,GAA2B;AACxD,QAAM,IAAqB;AAAA,IAAE,IAAI;AAAA,IAAS,IAAI;AAAA,EAAQ,GAChD,IAAgB,CAAC;AACvB,MAAI,IAAmB,IACnB,IAAI;AAER,QAAM,IAAA,CAAQ,MAAuB;AACnC,QAAK,GACL;AAAA,UAAI,EAAgB,CAAI,GAAG;AACzB,QAAA,KAAoB;AACpB;AAAA,MACF;AACA,MAAA,EAAI,KAAK,IAAO,CAAgB,GAChC,IAAmB;AAAA;AAAA,EACrB;AAEA,SAAO,IAAI,EAAM,UAAQ;AACvB,UAAM,IAAO,EAAM,CAAA;AAEnB,QAAI,MAAS,GAAQ;AACnB,YAAM,IAAS,GAAU,GAAO,GAAG,CAAK;AACxC,MAAA,EAAK,EAAO,IAAI,GAChB,IAAI,EAAO;AACX;AAAA,IACF;AAEA,QAAI,IAAO,IAAM;AAKf,MAAA,EAAK,OAAO,aAAa,CAAI,CAAC,GAC9B;AACA;AAAA,IACF;AAEA,UAAM,IAAO,KAAQ,KACf,IAAM,IAAO,EAAM,KAAK,EAAM;AACpC,QAAI,MAAQ,QAAQ;AAClB,YAAM,IAAS,GAAW,GAAO,GAAG,CAAI;AACxC,MAAA,EAAK,EAAO,IAAI,GAChB,IAAI,EAAO;AACX;AAAA,IACF;AAEA,IAAA,EAAK,GAAiB,GAAM,GAAK,CAAI,CAAC,GACtC;AAAA,EACF;AAEA,SAAI,KAAkB,EAAI,KAAK,CAAgB,GACxC,EAAI,KAAK,EAAE;AACpB;AAUA,SAAgB,GAAe,GAA0B;AACvD,SAAO,EAAwB,CAAI,EAAE;AACvC;AAgBA,SAAgB,EAAwB,GAAiC;AACvE,QAAM,IAAa,EAAK,UAAU,KAAK,GACjC,IAAkB,CAAC;AACzB,MAAI,IAAa,GACb,IAAI;AAER,SAAO,IAAI,EAAW,UAAQ;AAC5B,UAAM,IAAK,EAAW,YAAY,CAAC,GAC7B,IAAK,EAAW,CAAA;AAEtB,QAAI,EAAgB,CAAE,GAAG;AACvB,MAAA,EAAM,KAAK,EAAI,GACf,KACA;AACA;AAAA,IACF;AAGA,QAAI,IAAI,KADU,IAAK,QAAS,IAAI;AAEpC,UAAM,IAAuB,CAAC;AAC9B,WAAO,IAAI,EAAW,UAAU,EAAgB,EAAW,CAAA,CAAG,KAAG;AAC/D,YAAM,IAAW,EAAkB,IAAI,EAAW,CAAA,CAAG;AACrD,MAAI,MAAa,SACf,EAAW,KAAK,CAAQ,KAIxB,EAAW,KAAK,EAAI,GACpB,MAEF;AAAA,IACF;AAIA,QAFA,EAAM,KAAK,GAAG,CAAU,GAEpB,IAAK,IACP,CAAA,EAAM,KAAK,CAAE;AAAA,SACR;AACL,YAAM,IAAW,EAAsB,IAAI,CAAE;AAC7C,MAAI,MAAa,SACf,EAAM,KAAK,CAAQ,KAEnB,EAAM,KAAK,EAAI,GACf;AAAA,IAEJ;AAEA,QAAI;AAAA,EACN;AAEA,SAAO;AAAA,IAAE,OAAO,IAAI,WAAW,CAAK;AAAA,IAAG,YAAA;AAAA,EAAW;AACpD;ACpfA,IAAM,IAAqB,IACrB,IAAmB,IAGnB,IAAgB,IAChB,IAAyB,IACzB,IAAa,GACb,IAAoB,GAGpB,IAAe,IAAI,YAAY,SAAS,EAAE,OAAO,GAAM,CAAC;AAO9D,SAAS,EAAiB,GAA2B;AACnD,SAAO,EAAa,OAAO,CAAK;AAClC;AAMA,SAAS,EAAa,GAAmB,IAAM,IAAY;AACzD,QAAM,IAAQ,EAAM,MAAM,GAAG,CAAG,GAC1B,IAAM,MAAM,KAAK,GAAA,CAAQ,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,GAAG;AAC9E,SAAO,EAAM,SAAS,IAAM,GAAG,CAAA,OAAU,EAAM,MAAA,YAAkB;AACnE;AAgCA,SAAgB,GAAgB,GAAoB,IAAwB,CAAC,GAAgB;AAC3F,QAAM,IAAS,EAAQ,UAAU,IAC3B,IAAc,EAAQ,eAAe,KACrC,IAA0B,CAAC;AAGjC,MAAI,EAAO,SAAS,IAAgB,GAAG;AAErC,UAAM,IAAU,EAAc,oBAAoB,qBAAqB,EAAO,MAAA,QAAc;AAC5F,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,WAAA,EAAS,KAAK,CAAO,GACd;AAAA,MAAE,QAAQ;AAAA,MAAM,UAAA;AAAA,IAAS;AAAA,EAClC;AAGA,QAAM,IAAS,GAAa,CAAM;AAClC,MAAI,CAAC,GAAe,GAAQ,GAAU,CAAM,MACtC,KAAU,EAAS,UAAU;AAC/B,WAAO;AAAA,MAAE,QAAQ;AAAA,MAAM,UAAA;AAAA,IAAS;AAKpC,QAAM,IAAe,SAAS,EAAO,UAAU,GAAG,CAAC,GAAG,EAAE;AACxD,MAAI,MAAM,CAAY,KAAK,IAAe,EAAO,QAAQ;AACvD,UAAM,IAAU,EACd,kBACA,oCAAoC,EAAO,UAAU,GAAG,CAAC,CAAA,EAC3D;AACA,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,IAAA,EAAS,KAAK,CAAO;AAAA,EAEvB,WAAW,IAAe,EAAO,QAAQ;AAKvC,UAAM,IAAU,EACd,oBACA,+EACiB,CAAA,eAA2B,EAAO,MAAA,sFAErD;AACA,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,IAAA,EAAS,KAAK,CAAO,GACrB,IAAS,EAAO,MAAM,GAAG,CAAY;AAAA,EACvC;AAGA,QAAM,IAAc,SAAS,EAAO,UAAU,IAAI,EAAE,GAAG,EAAE;AACzD,MAAI,MAAM,CAAW,GAAG;AACtB,UAAM,IAAU,EACd,kBACA,mCAAmC,EAAO,UAAU,IAAI,EAAE,CAAA,EAC5D;AACA,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,WAAA,EAAS,KAAK,CAAO,GACd;AAAA,MAAE,QAAQ;AAAA,MAAM,UAAA;AAAA,IAAS;AAAA,EAClC;AAGA,QAAM,IAAiB,GACjB,IAAe,EAAO,QAAQ,GAAkB,CAAc;AACpE,MAAI,MAAiB,IAAI;AACvB,UAAM,IAAU,EAAc,qBAAqB,gCAAgC;AACnF,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,WAAA,EAAS,KAAK,CAAO,GACd;AAAA,MAAE,QAAQ;AAAA,MAAM,UAAA;AAAA,IAAS;AAAA,EAClC;AAGA,QAAM,IAAmB,GADF,EAAO,MAAM,GAAgB,CACZ,GAAgB,GAAU,GAAQ,CAAW;AAErF,MAAI,EAAiB,WAAW,GAAG;AACjC,UAAM,IAAU,EAAc,qBAAqB,4BAA4B;AAC/E,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,WAAA,EAAS,KAAK,CAAO,GACd;AAAA,MAAE,QAAQ;AAAA,MAAM,UAAA;AAAA,IAAS;AAAA,EAClC;AAGA,QAAM,IAAU,EAAO,CAAA,MAAO;AAC9B,MAAI,EAAO,CAAA,MAAO,OAAO,EAAO,CAAA,MAAO,KAAK;AAC1C,UAAM,IAAU,EACd,kBACA,yCAAyC,EAAO,CAAA,CAAA,+DAClD;AACA,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,IAAA,EAAS,KAAK,CAAO;AAAA,EACvB;AAQA,SAAO;AAAA,IACL,QAAQ;AAAA,MAAE,QAAA;AAAA,MAAQ,QAHL,GAAY,GAAQ,GAAkB,GALN,IAC3C,KAAA,CACC,MAAM,EAAa,OAAO,CAAC,GAG+C,GAAU,GAAQ,CAG7E;AAAA,IAAO;AAAA,IACzB,UAAA;AAAA,EACF;AACF;AAoBA,SAAgB,GAAsB,GAAgC;AACpE,QAAM,IAAS,GAAgB,GAAQ,EAAE,QAAQ,GAAK,CAAC;AACvD,MAAI,CAAC,EAAO,OACV,OAAM,IAAI,MAAM,4CAA4C;AAE9D,SAAO,EAAO;AAChB;AAKA,SAAS,GAAa,GAA4B;AAChD,QAAM,IAAc,EAAO,MAAM,GAAG,CAAa;AACjD,SAAO,EAAa,OAAO,CAAW;AACxC;AAKA,SAAS,GAAe,GAAgB,GAAyB,GAA0B;AACzF,MAAI,EAAO,WAAW,GAAe;AACnC,UAAM,IAAU,EACd,kBACA,oBAAoB,EAAO,MAAA,cAAoB,CAAA,EACjD;AACA,QAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,WAAA,EAAS,KAAK,CAAO,GACd;AAAA,EACT;AAGA,MAAI,EAAO,EAAA,MAAQ,KAAK;AACtB,UAAM,IAAU,EACd,kBACA,4CAA4C,EAAO,EAAA,CAAA,iBACrD;AAEA,IAAA,EAAS,KAAK,CAAO;AAAA,EACvB;AAGA,MAAI,EAAO,EAAA,MAAQ,KAAK;AACtB,UAAM,IAAU,EACd,kBACA,iDAAiD,EAAO,EAAA,CAAA,iBAC1D;AAEA,IAAA,EAAS,KAAK,CAAO;AAAA,EACvB;AAEA,SAAO;AACT;AAKA,SAAS,GACP,GACA,GACA,GACA,GACkB;AAClB,QAAM,IAA4B,CAAC;AAEnC,WAAS,IAAI,GAAG,IAAI,EAAe,QAAQ,KAAK,GAAwB;AACtE,QAAI,EAAS,UAAU,GAAa;AAClC,MAAA,EAAS,KACP,EACE,oBACA,8DAA8D,CAAA,gBAC/C,EAAe,SAAS,CAAA,iCACzC,CACF;AACA;AAAA,IACF;AAEA,QAAI,IAAI,IAAyB,EAAe,OAE9C;AAGF,UAAM,IAAa,EAAe,MAAM,GAAG,IAAI,CAAsB,GAC/D,IAAW,EAAa,OAAO,CAAU,GAEzC,IAAM,EAAS,UAAU,GAAG,CAAU,GACtC,IAAiB,EAAS,UAAU,GAAY,IAAa,CAAiB,GAC9E,IAAsB,EAAS,UACnC,IAAa,GACb,CACF,GAEM,IAAc,SAAS,GAAgB,EAAE,GACzC,IAAmB,SAAS,GAAqB,EAAE;AAEzD,QAAI,MAAM,CAAW,KAAK,MAAM,CAAgB,GAAG;AACjD,YAAM,IAAU,EACd,qBACA,mCAAmC,CAAA,YAAe,CAAA,cAA4B,CAAA,EAChF;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO;AACrB;AAAA,IACF;AAEA,IAAA,EAAQ,KAAK;AAAA,MAAE,KAAA;AAAA,MAAK,aAAA;AAAA,MAAa,kBAAA;AAAA,IAAiB,CAAC;AAAA,EACrD;AAEA,SAAO;AACT;AAKA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACA,GAC8B;AAC9B,QAAM,IAAuC,CAAC;AAE9C,aAAW,KAAS,GAAkB;AACpC,QAAI,EAAS,UAAU,GAAa;AAClC,MAAA,EAAS,KACP,EACE,oBACA,0DAA0D,CAAA,gDAE1D,QACA,EAAM,GACR,CACF;AACA;AAAA,IACF;AAEA,UAAM,IAAQ,IAAc,EAAM,kBAC5B,IAAM,IAAQ,EAAM,cAAc;AAGxC,QAAI,KAAS,EAAO,UAAU,IAAM,EAAO,QAAQ;AACjD,YAAM,IAAU,EACd,iBACA,SAAS,EAAM,GAAA,yBAA4B,CAAA,SAAc,CAAA,mBAAsB,EAAO,MAAA,IACtF,GACA,EAAM,GACR;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO;AACrB;AAAA,IACF;AAMA,QAAI;AACJ,QAAI,EAAO,CAAA,MAAS,GAAkB;AACpC,YAAM,IAAU,EACd,iBACA,SAAS,EAAM,GAAA,iDAAoD,CAAA,cACpD,EAAO,CAAA,KAAQ,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAA,0EAE7D,GACA,EAAM,GACR;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO,GACrB,IAAa,EAAO,MAAM,GAAO,IAAQ,EAAM,WAAW;AAAA,IAC5D,MACE,CAAA,IAAa,EAAO,MAAM,GAAO,CAAG;AAItC,QAAI,EAAM,IAAI,WAAW,IAAI,GAAG;AAC9B,UAAI;AACF,cAAM,IAAO,EAAY,CAAU;AACnC,QAAA,EAAO,KAAK;AAAA,UAAE,KAAK,EAAM;AAAA,UAAK,MAAA;AAAA,QAAK,CAAC;AAAA,MACtC,SAAS,GAAO;AACd,cAAM,IAAU,EACd,kBACA,kCAAkC,EAAM,GAAA,KACnC,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,CAAA,sBACpC,EAAa,CAAU,CAAA,KAC7C,GACA,EAAM,GACR;AACA,YAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,QAAA,EAAS,KAAK,CAAO,GAGrB,EAAO,KAAK;AAAA,UAAE,KAAK,EAAM;AAAA,UAAK,MAAM,EAAiB,CAAU;AAAA,QAAE,CAAC;AAAA,MACpE;AACA;AAAA,IACF;AAGA,QAAI,EAAW,SAAS,GAAG;AACzB,YAAM,IAAU,EACd,iBACA,cAAc,EAAM,GAAA,8BAAiC,EAAW,MAAA,UAChE,GACA,EAAM,GACR;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO;AACrB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,IAAa,OAAO,aAAa,EAAW,CAAA,KAAM,CAAC,GACnD,IAAa,OAAO,aAAa,EAAW,CAAA,KAAM,CAAC,GAGnD,IAAY,GAFI,EAAW,MAAM,CAGrC,GACA,GACA,EAAM,KACN,GACA,GACA,CACF;AAEA,MAAA,EAAO,KAAK;AAAA,QACV,KAAK,EAAM;AAAA,QACX,YAAA;AAAA,QACA,YAAA;AAAA,QACA,WAAA;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAO;AACd,YAAM,IAAU,EACd,iBACA,8BAA8B,EAAM,GAAA,KAAQ,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,CAAA,IACjG,GACA,EAAM,GACR;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACY;AACZ,QAAM,IAAwB,CAAC;AAC/B,MAAI,IAAI;AAER,SAAO,IAAI,EAAc,UAAQ;AAC/B,QAAI,EAAS,UAAU,GAAa;AAClC,MAAA,EAAS,KACP,EACE,oBACA,6DAA6D,CAAA,cAC/C,CAAA,uCACd,QACA,CACF,CACF;AACA;AAAA,IACF;AAGA,QAAI,EAAc,CAAA,MAAO,GAAoB;AAC3C,YAAM,IAAU,EACd,iBACA,wCAAwC,CAAA,gBAAmB,CAAA,IAC3D,QACA,CACF;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO;AACrB;AAAA,IACF;AAIA,QAFA,KAEI,KAAK,EAAc,OAAQ;AAG/B,UAAM,IAAO,OAAO,aAAa,EAAc,CAAA,KAAM,CAAC;AACtD,IAAA;AAGA,UAAM,IAAa;AACnB,WAAO,IAAI,EAAc,UAAU,EAAc,CAAA,MAAO,IACtD,CAAA;AAGF,UAAM,IAAa,EAAc,MAAM,GAAY,CAAC;AACpD,QAAI;AACF,YAAM,IAAQ,EAAY,CAAU;AACpC,MAAA,EAAU,KAAK;AAAA,QAAE,MAAA;AAAA,QAAM,OAAA;AAAA,MAAM,CAAC;AAAA,IAChC,SAAS,GAAO;AACd,YAAM,IAAU,EACd,kBACA,6BAA6B,CAAA,IAAO,CAAA,KAC/B,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK,CAAA,sBACpC,EAAa,CAAU,CAAA,KAC7C,QACA,CACF;AACA,UAAI,EAAQ,OAAM,IAAI,MAAM,EAAQ,OAAO;AAC3C,MAAA,EAAS,KAAK,CAAO,GAErB,EAAU,KAAK;AAAA,QAAE,MAAA;AAAA,QAAM,OAAO,EAAiB,CAAU;AAAA,MAAE,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,EACP,GACA,GACA,GACA,GACa;AACb,SAAO;AAAA,IAAE,MAAA;AAAA,IAAM,SAAA;AAAA,IAAS,UAAA;AAAA,IAAU,KAAA;AAAA,EAAI;AACxC;ACzfA,SAAgB,GACd,GACA,GACA,GACA,GACa;AACb,SAAO;AAAA,IAAE,MAAA;AAAA,IAAM,SAAA;AAAA,IAAS,UAAA;AAAA,IAAU,KAAA;AAAA,EAAI;AACxC;ACXA,IAAM,KAAqB,IACrB,IAAmB,IACnB,KAAoB,IAGpB,IAAgB,IAChB,KAAa,GACb,KAAoB,GACpB,KAAyB;AA2B/B,SAAgB,GACd,GACA,IAA4B,CAAC,GACjB;AACZ,SAAO,GAAgC,GAAQ,CAAO,EAAE;AAC1D;AAkBA,SAAgB,GACd,GACA,IAA4B,CAAC,GACZ;AACjB,EAAA,GAAe,CAAM;AACrB,QAAM,IAA0B,CAAC,GAE3B,IAAW,EAAQ,aAAa,SAChC,IAAU,IAAI,YAAY,GAC1B,IAAsD,IAAA,CACvD,GAAG,MAAQ;AACV,UAAM,IAAS,EAAwB,CAAC;AACxC,WAAI,EAAO,aAAa,KACtB,EAAS,KACP,GACE,kBACA,+BAA+B,EAAO,UAAA,kEAEtC,QACA,CACF,CACF,GAEK,EAAO;AAAA,EAChB,IAAA,CACC,MAAM,EAAQ,OAAO,CAAC,GAGrB,IAA6B,CAAC,GAC9B,IAA6B,CAAC;AACpC,MAAI,IAAe;AAEnB,aAAW,KAAS,EAAO,QAAQ;AACjC,UAAM,IAAY,GAAe,GAAA,CAAQ,MAAM,EAAW,GAAG,EAAM,GAAG,CAAC,GACjE,IAAc,EAAU,SAAS,GAGjC,IACJ,EAAM,IAAI,OAAO,IAAY,GAAG,IAChC,EAAY,SAAS,EAAE,SAAS,IAAmB,GAAG,IACtD,EAAa,SAAS,EAAE,SAAS,IAAwB,GAAG;AAE9D,IAAA,EAAiB,KAAK,CAAK,GAC3B,EAAa,KAAK,CAAS,GAC3B,EAAa,KAAK,IAAI,WAAW,CAAC,CAAgB,CAAC,CAAC,GAEpD,KAAgB;AAAA,EAClB;AAGA,QAAM,IAAY,EAAQ,OAAO,EAAiB,KAAK,EAAE,CAAC,GACpD,IAA0B,IAAI,WAAW,EAAU,SAAS,CAAC;AACnE,EAAA,EAAwB,IAAI,CAAS,GACrC,EAAwB,EAAU,MAAA,IAAU;AAG5C,QAAM,IAAc,IAAgB,EAAwB,QAGtD,IAAkB,EAAa,OAAA,CAAQ,GAAK,MAAQ,IAAM,EAAI,QAAQ,CAAC,GACvE,IAAO,IAAI,WAAW,CAAe;AAC3C,MAAI,IAAS;AACb,aAAW,KAAW;AACpB,IAAA,EAAK,IAAI,GAAS,CAAM,GACxB,KAAU,EAAQ;AAIpB,QAAM,IAAe,IAAc,IAAkB,GAC/C,IAAS,GAAY,EAAO,QAAQ,GAAc,GAAa,CAAQ,GAGvE,IAAQ,IAAI,WAAW,CAAY;AACzC,SAAA,EAAM,IAAI,EAAQ,OAAO,CAAM,GAAG,CAAC,GACnC,EAAM,IAAI,GAAyB,CAAa,GAChD,EAAM,IAAI,GAAM,CAAW,GAC3B,EAAM,IAAe,CAAA,IAAK,IAEnB;AAAA,IAAE,OAAA;AAAA,IAAO,UAAA;AAAA,EAAS;AAC3B;AAEA,SAAS,EAAiB,GAAoB;AAC5C,QAAM,IAAI,EAAE,WAAW,CAAC;AACxB,SAAO,KAAK,MAAQ,KAAK;AAC3B;AAQA,SAAS,GAAe,GAA0B;AAChD,aAAW,KAAS,EAAO,QAAQ;AACjC,QAAI,OAAO,EAAM,OAAQ,YAAY,EAAM,IAAI,WAAW,EACxD,OAAM,IAAI,MACR,oDAAoD,KAAK,UAAU,EAAM,GAAG,CAAA,EAC9E;AAGF,QAAI,CAAA,EAAe,CAAK,GAIxB;AAAA,UAAI,EAAM,WAAW,WAAW,EAC9B,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,gDAAmD,KAAK,UAAU,EAAM,UAAU,CAAA,EACnG;AAEF,UAAI,CAAC,EAAiB,EAAM,UAAU,EACpC,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,yEAA4E,KAAK,UAAU,EAAM,UAAU,CAAA,EAC5H;AAEF,UAAI,EAAM,WAAW,WAAW,EAC9B,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,gDAAmD,KAAK,UAAU,EAAM,UAAU,CAAA,EACnG;AAEF,UAAI,CAAC,EAAiB,EAAM,UAAU,EACpC,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,yEAA4E,KAAK,UAAU,EAAM,UAAU,CAAA,EAC5H;AAEF,iBAAW,KAAM,EAAM,WAAW;AAChC,YAAI,EAAG,KAAK,WAAW,EACrB,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,mDAAsD,KAAK,UAAU,EAAG,IAAI,CAAA,EAC7F;AAEF,YAAI,CAAC,EAAiB,EAAG,IAAI,EAC3B,OAAM,IAAI,MACR,SAAS,EAAM,GAAA,4EAA+E,KAAK,UAAU,EAAG,IAAI,CAAA,EACtH;AAAA,MAEJ;AAAA;AAAA,EACF;AACF;AAKA,SAAS,GACP,GACA,GACY;AACZ,MAAI,EAAe,CAAK,EACtB,QAAO,EAAW,EAAM,IAAI;AAW9B,QAAM,IAA8B,CAAC,IALV,WAAW,CACpC,EAAM,WAAW,WAAW,CAAC,GAC7B,EAAM,WAAW,WAAW,CAAC,CAC/B,CAEqC,CAAc;AACnD,aAAW,KAAY,EAAM,WAAW;AACtC,UAAM,IAAe,IAAI,WAAW,CAAC,IAAoB,EAAS,KAAK,WAAW,CAAC,CAAC,CAAC;AACrF,IAAA,EAAc,KAAK,GAAc,EAAW,EAAS,KAAK,CAAC;AAAA,EAC7D;AAEA,QAAM,IAAW,EAAc,OAAA,CAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,CAAC,GACzD,IAAM,IAAI,WAAW,CAAQ;AACnC,MAAI,IAAM;AACV,aAAW,KAAQ;AACjB,IAAA,EAAI,IAAI,GAAM,CAAG,GACjB,KAAO,EAAK;AAEd,SAAO;AACT;AAUA,SAAS,GACP,GACA,GACA,GACA,GACQ;AAER,MAAI,IAAS,EAAe,OAAO,GAAe,GAAG,EAAE,UAAU,GAAG,CAAa;AAGjF,QAAM,IAAkB,EAAa,SAAS,EAAE,SAAS,GAAG,GAAG;AAC/D,MAAI,EAAgB,SAAS,EAC3B,OAAM,IAAI,MAAM,iBAAiB,CAAA,0BAAsC;AAEzE,EAAA,IAAS,IAAkB,EAAO,UAAU,CAAC;AAG7C,QAAM,IAAiB,EAAY,SAAS,EAAE,SAAS,GAAG,GAAG;AAC7D,MAAI,EAAe,SAAS,EAC1B,OAAM,IAAI,MAAM,gBAAgB,CAAA,0BAAqC;AAEvE,SAAA,IAAS,EAAO,UAAU,GAAG,EAAE,IAAI,IAAiB,EAAO,UAAU,EAAE,GAGvE,IAAS,EAAO,UAAU,GAAG,CAAC,KAAK,IAAW,MAAM,OAAO,EAAO,UAAU,EAAE,GAEvE;AACT;AC9QA,SAAgB,EAAS,GAAoB,GAAmD;AAC9F,SAAO,EAAO,OAAO,KAAA,CAAM,MAAM,EAAE,QAAQ,CAAG;AAChD;AAeA,SAAgB,GAAU,GAAoB,GAA2C;AACvF,SAAO,EAAO,OAAO,OAAA,CAAQ,MAAM,EAAE,QAAQ,CAAG;AAClD;AAkBA,SAAgB,EAAY,GAAkB,GAAkC;AAE9E,SADiB,EAAM,UAAU,KAAA,CAAM,MAAO,EAAG,SAAS,CACnD,GAAU;AACnB;AAmBA,SAAgB,GAAa,GAAkB,GAAwB;AACrE,SAAO,EAAM,UAAU,OAAA,CAAQ,MAAO,EAAG,SAAS,CAAI,EAAE,IAAA,CAAK,MAAO,EAAG,KAAK;AAC9E;AAmBA,SAAgB,GAAgB,GAA0D;AACxF,SAAO,EAAM,UAAU,IAAA,CAAK,OAAQ;AAAA,IAAE,MAAM,EAAG;AAAA,IAAM,OAAO,EAAG;AAAA,EAAM,EAAE;AACzE;ACpFA,SAAgB,GAAM,GAAwC;AAC5D,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,CAAC,KAAS,CAAC,EAAY,CAAK,EAAG;AAGnC,QAAM,IAAI,EAAY,GAAO,GAAG,KAAK,IAC/B,IAAI,EAAY,GAAO,GAAG,KAAK;AAGrC,UADkB,IAAI,MAAM,GAAG,KACxB,KAAY;AACrB;AAeA,SAAgB,GAAY,GAAwC;AAClE,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,GAAC,KAAS,CAAC,EAAY,CAAK;AAChC,WAAO,EAAY,GAAO,GAAG;AAC/B;AAcA,SAAgB,GAAO,GAAwC;AAE7D,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,EAClC,QAAO,EAAY,GAAU,GAAG;AAIlC,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,EAClC,QAAO,EAAY,GAAU,GAAG;AAIpC;AAcA,SAAgB,GAAQ,GAAwC;AAC9D,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,GAAC,KAAS,CAAC,EAAY,CAAK;AAChC,WAAO,EAAY,GAAO,GAAG;AAC/B;AAeA,SAAgB,GAAU,GAAwC;AAEhE,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,GAAG;AACrC,UAAM,IAAM,EAAY,GAAU,GAAG;AACrC,QAAI,EAAK,QAAO;AAAA,EAClB;AAGA,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,EAClC,QAAO,EAAY,GAAU,GAAG;AAIpC;AAeA,SAAgB,GAAgB,GAAwC;AAEtE,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,GAAG;AACrC,UAAM,IAAO,EAAY,GAAU,GAAG;AACtC,QAAI,EAAM,QAAO;AAAA,EACnB;AAGA,QAAM,IAAW,EAAS,GAAQ,KAAK;AACvC,MAAI,KAAY,EAAY,CAAQ,EAClC,QAAO,EAAY,GAAU,GAAG;AAIpC;AAeA,SAAgB,GAAK,GAA8B;AACjD,QAAM,IAAoB,CAAC;AAE3B,aAAW,KAAS,EAAO,OACzB,KAAI,EAAM,QAAQ,SAAS,EAAY,CAAK,GAAG;AAC7C,UAAM,IAAQ,EAAY,GAAO,GAAG;AACpC,IAAI,KAAO,EAAQ,KAAK,CAAK;AAAA,EAC/B;AAGF,SAAO;AACT;AAcA,SAAgB,GAAK,GAAwC;AAC3D,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,GAAC,KAAS,CAAC,EAAY,CAAK;AAChC,WAAO,EAAY,GAAO,GAAG;AAC/B;AAcA,SAAgB,GAAK,GAAwC;AAC3D,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,GAAC,KAAS,CAAC,EAAY,CAAK;AAChC,WAAO,EAAY,GAAO,GAAG;AAC/B;AAeA,SAAgB,GAAS,GAA8B;AACrD,QAAM,IAAoB,CAAC;AAE3B,aAAW,KAAS,EAAO,OAEzB,KAAI,EAAM,IAAI,WAAW,GAAG,KAAK,EAAY,CAAK,GAAG;AACnD,UAAM,IAAQ,EAAY,GAAO,GAAG;AACpC,IAAI,KAAO,EAAQ,KAAK,CAAK;AAAA,EAC/B;AAGF,SAAO;AACT;AAcA,SAAgB,GAAgB,GAAwC;AACtE,QAAM,IAAQ,EAAS,GAAQ,KAAK;AACpC,MAAI,GAAC,KAAS,CAAC,EAAY,CAAK;AAChC,WAAO,EAAY,GAAO,GAAG;AAC/B;AC/OA,SAAS,EAAe,GAAa,GAA0B;AAC7D,MAAI,EAAI,WAAW,KAAK,EAAQ,WAAW,EAAG,QAAO;AAErD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,IAAc,EAAQ,CAAA,GACtB,IAAU,EAAI,CAAA;AAGpB,QAAI,MAAgB,OAAO,GAAa,YAAY,MAAM,KAAK;AAE7D,UAAI,KAAW,CAAC,KAAK,KAAK,CAAO,EAAG,QAAO;AAC3C;AAAA,IACF;AAGA,QAAI,MAAgB,EAAS,QAAO;AAAA,EACtC;AAEA,SAAO;AACT;AAqBA,SAAgB,GACd,GACA,GAC8B;AAC9B,SAAO,EAAO,OAAO,OAAA,CAAQ,MAAU,EAAe,EAAM,KAAK,CAAO,CAAC;AAC3E;AAiBA,SAAgB,GACd,GACA,GACsC;AACtC,SAAO,EAAO,OAAO,KAAA,CAAM,MAAU,EAAe,EAAM,KAAK,CAAO,CAAC;AACzE;AC/DA,SAAgB,EAAY,GAAoB,GAA6C;AAC3F,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,CAAC,GAAG,EAAO,QAAQ,CAAK;AAAA,EAClC;AACF;AAuBA,SAAgB,GACd,GACA,GACA,GACY;AACZ,QAAM,IAAQ,EAAO,OAAO,UAAA,CAAW,MAAM,EAAE,QAAQ,CAAG;AAE1D,MAAI,MAAU,GAEZ,QAAO,EAAY,GAAQ,CAAK;AAIlC,QAAM,IAAY,MAAM,KAAK,EAAO,MAAM;AAC1C,SAAA,EAAU,OAAO,GAAO,GAAG,CAAK,GAEzB;AAAA,IAAE,GAAG;AAAA,IAAQ,QAAQ;AAAA,EAAU;AACxC;AAiBA,SAAgB,GACd,GACA,GACA,GACY;AACZ,QAAM,IAAQ,EAAO,OAAO,UAAA,CAAW,MAAM,EAAE,QAAQ,CAAG;AAE1D,MAAI,MAAU,GAEZ,QAAO,EAAY,GAAQ,CAAK;AAIlC,QAAM,IAAY,MAAM,KAAK,EAAO,MAAM;AAC1C,SAAA,EAAU,OAAO,IAAQ,GAAG,GAAG,CAAK,GAE7B;AAAA,IAAE,GAAG;AAAA,IAAQ,QAAQ;AAAA,EAAU;AACxC;AAsBA,SAAgB,GACd,GACA,GACY;AAIZ,QAAM,IAAY,SAAS,EAAM,KAAK,EAAE;AAIxC,MAAI,IAAc,EAAO,OAAO;AAChC,WAAS,IAAI,GAAG,IAAI,EAAO,OAAO,QAAQ,KAAK;AAC7C,UAAM,IAAe,EAAO,OAAO,CAAA;AACnC,QAAK,KAEc,SAAS,EAAa,KAAK,EAG1C,IAAa,GAAW;AAC1B,MAAA,IAAc;AACd;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAY,MAAM,KAAK,EAAO,MAAM;AAC1C,SAAA,EAAU,OAAO,GAAa,GAAG,CAAK,GAE/B;AAAA,IAAE,GAAG;AAAA,IAAQ,QAAQ;AAAA,EAAU;AACxC;AAgBA,SAAgB,GAAa,GAAoB,GAAyB;AACxE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,EAAO,OAAO,OAAA,CAAQ,MAAM,EAAE,QAAQ,CAAG;AAAA,EACnD;AACF;AAmBA,SAAgB,GAAY,GAAoB,GAA6C;AAC3F,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,EAAO,OAAO,OAAA,CAAQ,MAAM,MAAM,CAAK;AAAA,EACjD;AACF;AAoBA,SAAgB,GAAY,GAAkB,GAAc,GAA0B;AACpF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,CAAC,GAAG,EAAM,WAAW;AAAA,MAAE,MAAA;AAAA,MAAM,OAAA;AAAA,IAAM,CAAC;AAAA,EACjD;AACF;AAgBA,SAAgB,GAAe,GAAkB,GAAyB;AACxE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,EAAM,UAAU,OAAA,CAAQ,MAAO,EAAG,SAAS,CAAI;AAAA,EAC5D;AACF;AAkBA,SAAgB,GAAgB,GAAkB,GAAc,GAA6B;AAC3F,QAAM,IAAQ,EAAM,UAAU,UAAA,CAAW,MAAO,EAAG,SAAS,CAAI;AAEhE,MAAI,MAAU,GAEZ,QAAO,GAAY,GAAO,GAAM,CAAQ;AAG1C,QAAM,IAAe,CAAC,GAAG,EAAM,SAAS;AACxC,SAAA,EAAa,CAAA,IAAS;AAAA,IAAE,MAAA;AAAA,IAAM,OAAO;AAAA,EAAS,GAEvC;AAAA,IAAE,GAAG;AAAA,IAAO,WAAW;AAAA,EAAa;AAC7C;ACnQA,SAAgB,GAAY,GAAgC;AAE1D,QAAM,IAAS,IAAI,MAAgC,EAAO,OAAO,MAAM;AAEvE,WAAS,IAAI,GAAG,IAAI,EAAO,OAAO,QAAQ,KAAK;AAC7C,UAAM,IAAQ,EAAO,OAAO,CAAA;AAE5B,QAAI,EAAe,CAAK,EACtB,CAAA,EAAO,CAAA,IAAK;AAAA,MAAE,KAAK,EAAM;AAAA,MAAK,MAAM,EAAM;AAAA,IAAK;AAAA,SAC1C;AAEL,YAAM,IAAY,IAAI,MAAuC,EAAM,UAAU,MAAM;AACnF,eAAS,IAAI,GAAG,IAAI,EAAM,UAAU,QAAQ,KAAK;AAC/C,cAAM,IAAK,EAAM,UAAU,CAAA;AAC3B,QAAA,EAAU,CAAA,IAAK;AAAA,UAAE,MAAM,EAAG;AAAA,UAAM,OAAO,EAAG;AAAA,QAAM;AAAA,MAClD;AAEA,MAAA,EAAO,CAAA,IAAK;AAAA,QACV,KAAK,EAAM;AAAA,QACX,YAAY,EAAM;AAAA,QAClB,YAAY,EAAM;AAAA,QACP,WAAA;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IAAE,QAAQ,EAAO;AAAA,IAAQ,QAAA;AAAA,EAAO;AACzC;AAsBA,SAAgB,GAAa,GAAe,GAAe,IAAmB,IAAgB;AAE5F,MADI,EAAE,WAAW,EAAE,UACf,EAAE,OAAO,WAAW,EAAE,OAAO,OAAQ,QAAO;AAEhD,QAAM,IAAU,IAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAa,IAAI,EAAE,QACnE,IAAU,IAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAa,IAAI,EAAE;AAEzE,WAAS,IAAI,GAAG,IAAI,EAAQ,QAAQ,KAAK;AACvC,UAAM,IAAS,EAAQ,CAAA,GACjB,IAAS,EAAQ,CAAA;AAEvB,QADI,CAAC,KAAU,CAAC,KACZ,CAAC,GAAY,GAAQ,CAAM,EAAG,QAAO;AAAA,EAC3C;AAEA,SAAO;AACT;AAkBA,SAAgB,GAAY,GAA6B,GAAsC;AAC7F,MAAI,EAAE,QAAQ,EAAE,IAAK,QAAO;AAE5B,MAAI,EAAe,CAAC,KAAK,EAAe,CAAC,EACvC,QAAO,EAAE,SAAS,EAAE;AAGtB,MAAI,CAAC,EAAe,CAAC,KAAK,CAAC,EAAe,CAAC,GAAG;AAE5C,QADI,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,cACpD,EAAE,UAAU,WAAW,EAAE,UAAU,OAAQ,QAAO;AAEtD,aAAS,IAAI,GAAG,IAAI,EAAE,UAAU,QAAQ,KAAK;AAC3C,YAAM,IAAM,EAAE,UAAU,CAAA,GAClB,IAAM,EAAE,UAAU,CAAA;AAExB,UADI,CAAC,KAAO,CAAC,KACT,EAAI,SAAS,EAAI,QAAQ,EAAI,UAAU,EAAI,MAC7C,QAAO;AAAA,IAEX;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,EAAc,GAA6B,GAAqC;AACvF,SAAO,EAAE,IAAI,cAAc,EAAE,GAAG;AAClC"}
|
package/dist/marc8.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MARC-8 character encoding codec.
|
|
3
|
+
*
|
|
4
|
+
* MARC-8 is signaled by leader byte 9 == ' ' (space); UTF-8 uses 'a'.
|
|
5
|
+
* Decoding tracks escape-designated G0/G1 sets and converts known MARC-8
|
|
6
|
+
* character sets to Unicode. Encoding remains conservative: ASCII plus ANSEL
|
|
7
|
+
* Latin/combining characters are supported, and unsupported characters become '?'.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Convert a MARC-8-encoded byte sequence to a Unicode string.
|
|
11
|
+
*
|
|
12
|
+
* Escape-designated G0/G1 sets are decoded instead of skipped. Unknown bytes,
|
|
13
|
+
* unsupported designators, and unmapped EACC triples produce U+FFFD.
|
|
14
|
+
*/
|
|
15
|
+
export declare function marc8ToUnicode(bytes: Uint8Array): string;
|
|
16
|
+
/**
|
|
17
|
+
* Convert a Unicode string to MARC-8-encoded bytes.
|
|
18
|
+
*
|
|
19
|
+
* This is intentionally conservative. ASCII and ANSEL Latin/combining
|
|
20
|
+
* characters are encoded. Characters with no supported MARC-8 equivalent are
|
|
21
|
+
* replaced with '?'. For programmatic visibility into how much data was lost,
|
|
22
|
+
* use {@link unicodeToMarc8WithStats}.
|
|
23
|
+
*/
|
|
24
|
+
export declare function unicodeToMarc8(text: string): Uint8Array;
|
|
25
|
+
/**
|
|
26
|
+
* Result of {@link unicodeToMarc8WithStats}: the encoded bytes plus a count of
|
|
27
|
+
* characters that had no MARC-8 equivalent and were substituted with '?'.
|
|
28
|
+
*/
|
|
29
|
+
export interface Marc8EncodeResult {
|
|
30
|
+
readonly bytes: Uint8Array;
|
|
31
|
+
readonly lossyCount: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Like {@link unicodeToMarc8} but also reports how many characters were
|
|
35
|
+
* substituted because they have no MARC-8 equivalent. A non-zero `lossyCount`
|
|
36
|
+
* means the round-trip was destructive.
|
|
37
|
+
*/
|
|
38
|
+
export declare function unicodeToMarc8WithStats(text: string): Marc8EncodeResult;
|
|
39
|
+
//# sourceMappingURL=marc8.d.ts.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./types-CJcxHJff.cjs");function w(e){const i=typeof e=="string"?JSON.parse(e):e;if(typeof i.leader!="string")throw new Error('MARC-in-JSON: missing or non-string "leader"');if(!Array.isArray(i.fields))throw new Error('MARC-in-JSON: missing or non-array "fields"');const r=[];for(const t of i.fields){if(typeof t!="object"||t===null||Array.isArray(t))throw new Error("MARC-in-JSON: each field entry must be an object");const o=Object.keys(t);if(o.length!==1)throw new Error(`MARC-in-JSON: field entry must have exactly one key, got ${o.join(", ")}`);const n=o[0],s=t[n];if(typeof s=="string"){r.push({tag:n,data:s});continue}if(typeof s=="object"&&s!==null&&!Array.isArray(s)){const a=s;if(!Array.isArray(a.subfields))throw new Error(`MARC-in-JSON: data field "${n}" missing "subfields" array`);const g=a.subfields.map((l,f)=>{if(typeof l!="object"||l===null)throw new Error(`MARC-in-JSON: subfield entry ${f} of "${n}" is not an object`);const u=Object.keys(l);if(u.length!==1)throw new Error(`MARC-in-JSON: subfield entry ${f} of "${n}" must have exactly one key`);const c=u[0],d=l[c];if(typeof d!="string")throw new Error(`MARC-in-JSON: subfield value for "${n}$${c}" must be a string`);return{code:c,value:d}});r.push({tag:n,indicator1:a.ind1??" ",indicator2:a.ind2??" ",subfields:g});continue}throw new Error(`MARC-in-JSON: field "${n}" value must be a string (control) or object (data)`)}return{leader:i.leader,fields:r}}function b(e){const i=e.fields.map(r=>{if(y.isControlField(r))return{[r.tag]:r.data};if(y.isDataField(r)){const t=r.subfields.map(o=>({[o.code]:o.value}));return{[r.tag]:{subfields:t,ind1:r.indicator1,ind2:r.indicator2}}}throw new Error("Unknown field type")});return{leader:e.leader,fields:i}}function p(e){return JSON.stringify(b(e))}exports.parseMarcJson=w;exports.serializeMarcJson=b;exports.serializeMarcJsonString=p;
|
|
2
|
+
|
|
3
|
+
//# sourceMappingURL=marcjson.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marcjson.cjs","names":[],"sources":["../src/marcjson.ts"],"sourcesContent":["/**\n * MARC-in-JSON parser and serializer.\n *\n * Follows the format described at https://wiki.code4lib.org/MARCJSONification\n * and used by Open Library and various REST APIs:\n *\n * {\n * \"leader\": \"01142cam a2200301 a 4500\",\n * \"fields\": [\n * { \"001\": \"5490\" },\n * { \"245\": {\n * \"subfields\": [{\"a\": \"The Hobbit\"}],\n * \"ind1\": \"1\",\n * \"ind2\": \"0\"\n * }}\n * ]\n * }\n */\n\nimport type { MarcRecord, ControlField, DataField, Subfield } from './types';\nimport { isControlField, isDataField } from './types';\n\n// ─── Raw JSON shape types ─────────────────────────────────────────────────────\n\nexport interface MarcJsonSubfieldEntry {\n [code: string]: string;\n}\n\nexport interface MarcJsonDataFieldValue {\n subfields: MarcJsonSubfieldEntry[];\n ind1: string;\n ind2: string;\n}\n\nexport type MarcJsonField =\n | { [tag: string]: string } // control field\n | { [tag: string]: MarcJsonDataFieldValue }; // data field\n\nexport interface MarcJsonObject {\n leader: string;\n fields: MarcJsonField[];\n}\n\n// ─── Parse ────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a MARC-in-JSON object or JSON string into a MarcRecord.\n *\n * Throws on structural errors (missing `leader`, non-array `fields`,\n * unrecognised field shapes).\n */\nexport function parseMarcJson(json: string | MarcJsonObject): MarcRecord {\n const obj: MarcJsonObject = typeof json === 'string' ? JSON.parse(json) : json;\n\n if (typeof obj.leader !== 'string') {\n throw new Error('MARC-in-JSON: missing or non-string \"leader\"');\n }\n if (!Array.isArray(obj.fields)) {\n throw new Error('MARC-in-JSON: missing or non-array \"fields\"');\n }\n\n const fields: (ControlField | DataField)[] = [];\n\n for (const entry of obj.fields) {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new Error('MARC-in-JSON: each field entry must be an object');\n }\n\n const keys = Object.keys(entry);\n if (keys.length !== 1) {\n throw new Error(`MARC-in-JSON: field entry must have exactly one key, got ${keys.join(', ')}`);\n }\n\n const tag = keys[0]!;\n const value = (entry as Record<string, unknown>)[tag];\n\n if (typeof value === 'string') {\n // Control field\n fields.push({ tag, data: value });\n continue;\n }\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const dv = value as MarcJsonDataFieldValue;\n if (!Array.isArray(dv.subfields)) {\n throw new Error(`MARC-in-JSON: data field \"${tag}\" missing \"subfields\" array`);\n }\n const subfields: Subfield[] = dv.subfields.map((sfEntry, idx) => {\n if (typeof sfEntry !== 'object' || sfEntry === null) {\n throw new Error(`MARC-in-JSON: subfield entry ${idx} of \"${tag}\" is not an object`);\n }\n const sfKeys = Object.keys(sfEntry);\n if (sfKeys.length !== 1) {\n throw new Error(\n `MARC-in-JSON: subfield entry ${idx} of \"${tag}\" must have exactly one key`\n );\n }\n const code = sfKeys[0]!;\n const sfValue = sfEntry[code];\n if (typeof sfValue !== 'string') {\n throw new Error(\n `MARC-in-JSON: subfield value for \"${tag}$${code}\" must be a string`\n );\n }\n return { code, value: sfValue };\n });\n\n fields.push({\n tag,\n indicator1: dv.ind1 ?? ' ',\n indicator2: dv.ind2 ?? ' ',\n subfields,\n });\n continue;\n }\n\n throw new Error(\n `MARC-in-JSON: field \"${tag}\" value must be a string (control) or object (data)`\n );\n }\n\n return { leader: obj.leader, fields };\n}\n\n// ─── Serialize ────────────────────────────────────────────────────────────────\n\n/**\n * Serialize a MarcRecord to a MARC-in-JSON plain object.\n */\nexport function serializeMarcJson(record: MarcRecord): MarcJsonObject {\n const fields: MarcJsonField[] = record.fields.map((field) => {\n if (isControlField(field)) {\n return { [field.tag]: field.data };\n }\n if (isDataField(field)) {\n const subfields: MarcJsonSubfieldEntry[] = field.subfields.map((sf) => ({\n [sf.code]: sf.value,\n }));\n return {\n [field.tag]: {\n subfields,\n ind1: field.indicator1,\n ind2: field.indicator2,\n },\n };\n }\n // Unreachable — isControlField/isDataField are exhaustive — but satisfies TS\n throw new Error('Unknown field type');\n });\n\n return { leader: record.leader, fields };\n}\n\n/**\n * Serialize a MarcRecord to a JSON string.\n */\nexport function serializeMarcJsonString(record: MarcRecord): string {\n return JSON.stringify(serializeMarcJson(record));\n}\n"],"mappings":"2GAmDA,SAAgB,EAAc,EAA2C,CACvE,MAAM,EAAsB,OAAO,GAAS,SAAW,KAAK,MAAM,CAAI,EAAI,EAE1E,GAAI,OAAO,EAAI,QAAW,SACxB,MAAM,IAAI,MAAM,8CAA8C,EAEhE,GAAI,CAAC,MAAM,QAAQ,EAAI,MAAM,EAC3B,MAAM,IAAI,MAAM,6CAA6C,EAG/D,MAAM,EAAuC,CAAC,EAE9C,UAAW,KAAS,EAAI,OAAQ,CAC9B,GAAI,OAAO,GAAU,UAAY,IAAU,MAAQ,MAAM,QAAQ,CAAK,EACpE,MAAM,IAAI,MAAM,kDAAkD,EAGpE,MAAM,EAAO,OAAO,KAAK,CAAK,EAC9B,GAAI,EAAK,SAAW,EAClB,MAAM,IAAI,MAAM,4DAA4D,EAAK,KAAK,IAAI,CAAA,EAAG,EAG/F,MAAM,EAAM,EAAK,CAAA,EACX,EAAS,EAAkC,CAAA,EAEjD,GAAI,OAAO,GAAU,SAAU,CAE7B,EAAO,KAAK,CAAE,IAAA,EAAK,KAAM,CAAM,CAAC,EAChC,QACF,CAEA,GAAI,OAAO,GAAU,UAAY,IAAU,MAAQ,CAAC,MAAM,QAAQ,CAAK,EAAG,CACxE,MAAM,EAAK,EACX,GAAI,CAAC,MAAM,QAAQ,EAAG,SAAS,EAC7B,MAAM,IAAI,MAAM,6BAA6B,CAAA,6BAAgC,EAE/E,MAAM,EAAwB,EAAG,UAAU,IAAA,CAAK,EAAS,IAAQ,CAC/D,GAAI,OAAO,GAAY,UAAY,IAAY,KAC7C,MAAM,IAAI,MAAM,gCAAgC,CAAA,QAAW,CAAA,oBAAuB,EAEpF,MAAM,EAAS,OAAO,KAAK,CAAO,EAClC,GAAI,EAAO,SAAW,EACpB,MAAM,IAAI,MACR,gCAAgC,CAAA,QAAW,CAAA,6BAC7C,EAEF,MAAM,EAAO,EAAO,CAAA,EACd,EAAU,EAAQ,CAAA,EACxB,GAAI,OAAO,GAAY,SACrB,MAAM,IAAI,MACR,qCAAqC,CAAA,IAAO,CAAA,oBAC9C,EAEF,MAAO,CAAE,KAAA,EAAM,MAAO,CAAQ,CAChC,CAAC,EAED,EAAO,KAAK,CACV,IAAA,EACA,WAAY,EAAG,MAAQ,IACvB,WAAY,EAAG,MAAQ,IACvB,UAAA,CACF,CAAC,EACD,QACF,CAEA,MAAM,IAAI,MACR,wBAAwB,CAAA,qDAC1B,CACF,CAEA,MAAO,CAAE,OAAQ,EAAI,OAAQ,OAAA,CAAO,CACtC,CAOA,SAAgB,EAAkB,EAAoC,CACpE,MAAM,EAA0B,EAAO,OAAO,IAAK,GAAU,CAC3D,GAAI,EAAA,eAAe,CAAK,EACtB,MAAO,CAAA,CAAG,EAAM,GAAA,EAAM,EAAM,IAAK,EAEnC,GAAI,EAAA,YAAY,CAAK,EAAG,CACtB,MAAM,EAAqC,EAAM,UAAU,IAAK,IAAQ,CAAA,CACrE,EAAG,IAAA,EAAO,EAAG,KAChB,EAAE,EACF,MAAO,CAAA,CACJ,EAAM,GAAA,EAAM,CACX,UAAA,EACA,KAAM,EAAM,WACZ,KAAM,EAAM,UACd,CACF,CACF,CAEA,MAAM,IAAI,MAAM,oBAAoB,CACtC,CAAC,EAED,MAAO,CAAE,OAAQ,EAAO,OAAQ,OAAA,CAAO,CACzC,CAKA,SAAgB,EAAwB,EAA4B,CAClE,OAAO,KAAK,UAAU,EAAkB,CAAM,CAAC,CACjD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MarcRecord } from './types';
|
|
2
|
+
export interface MarcJsonSubfieldEntry {
|
|
3
|
+
[code: string]: string;
|
|
4
|
+
}
|
|
5
|
+
export interface MarcJsonDataFieldValue {
|
|
6
|
+
subfields: MarcJsonSubfieldEntry[];
|
|
7
|
+
ind1: string;
|
|
8
|
+
ind2: string;
|
|
9
|
+
}
|
|
10
|
+
export type MarcJsonField = {
|
|
11
|
+
[tag: string]: string;
|
|
12
|
+
} | {
|
|
13
|
+
[tag: string]: MarcJsonDataFieldValue;
|
|
14
|
+
};
|
|
15
|
+
export interface MarcJsonObject {
|
|
16
|
+
leader: string;
|
|
17
|
+
fields: MarcJsonField[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a MARC-in-JSON object or JSON string into a MarcRecord.
|
|
21
|
+
*
|
|
22
|
+
* Throws on structural errors (missing `leader`, non-array `fields`,
|
|
23
|
+
* unrecognised field shapes).
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseMarcJson(json: string | MarcJsonObject): MarcRecord;
|
|
26
|
+
/**
|
|
27
|
+
* Serialize a MarcRecord to a MARC-in-JSON plain object.
|
|
28
|
+
*/
|
|
29
|
+
export declare function serializeMarcJson(record: MarcRecord): MarcJsonObject;
|
|
30
|
+
/**
|
|
31
|
+
* Serialize a MarcRecord to a JSON string.
|
|
32
|
+
*/
|
|
33
|
+
export declare function serializeMarcJsonString(record: MarcRecord): string;
|
|
34
|
+
//# sourceMappingURL=marcjson.d.ts.map
|
package/dist/marcjson.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { n as b, t as w } from "./types-c4Mo9m9u.js";
|
|
2
|
+
function p(e) {
|
|
3
|
+
const i = typeof e == "string" ? JSON.parse(e) : e;
|
|
4
|
+
if (typeof i.leader != "string") throw new Error('MARC-in-JSON: missing or non-string "leader"');
|
|
5
|
+
if (!Array.isArray(i.fields)) throw new Error('MARC-in-JSON: missing or non-array "fields"');
|
|
6
|
+
const r = [];
|
|
7
|
+
for (const t of i.fields) {
|
|
8
|
+
if (typeof t != "object" || t === null || Array.isArray(t)) throw new Error("MARC-in-JSON: each field entry must be an object");
|
|
9
|
+
const o = Object.keys(t);
|
|
10
|
+
if (o.length !== 1) throw new Error(`MARC-in-JSON: field entry must have exactly one key, got ${o.join(", ")}`);
|
|
11
|
+
const n = o[0], s = t[n];
|
|
12
|
+
if (typeof s == "string") {
|
|
13
|
+
r.push({
|
|
14
|
+
tag: n,
|
|
15
|
+
data: s
|
|
16
|
+
});
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (typeof s == "object" && s !== null && !Array.isArray(s)) {
|
|
20
|
+
const a = s;
|
|
21
|
+
if (!Array.isArray(a.subfields)) throw new Error(`MARC-in-JSON: data field "${n}" missing "subfields" array`);
|
|
22
|
+
const y = a.subfields.map((l, c) => {
|
|
23
|
+
if (typeof l != "object" || l === null) throw new Error(`MARC-in-JSON: subfield entry ${c} of "${n}" is not an object`);
|
|
24
|
+
const u = Object.keys(l);
|
|
25
|
+
if (u.length !== 1) throw new Error(`MARC-in-JSON: subfield entry ${c} of "${n}" must have exactly one key`);
|
|
26
|
+
const f = u[0], d = l[f];
|
|
27
|
+
if (typeof d != "string") throw new Error(`MARC-in-JSON: subfield value for "${n}$${f}" must be a string`);
|
|
28
|
+
return {
|
|
29
|
+
code: f,
|
|
30
|
+
value: d
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
r.push({
|
|
34
|
+
tag: n,
|
|
35
|
+
indicator1: a.ind1 ?? " ",
|
|
36
|
+
indicator2: a.ind2 ?? " ",
|
|
37
|
+
subfields: y
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`MARC-in-JSON: field "${n}" value must be a string (control) or object (data)`);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
leader: i.leader,
|
|
45
|
+
fields: r
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function g(e) {
|
|
49
|
+
const i = e.fields.map((r) => {
|
|
50
|
+
if (w(r)) return { [r.tag]: r.data };
|
|
51
|
+
if (b(r)) {
|
|
52
|
+
const t = r.subfields.map((o) => ({ [o.code]: o.value }));
|
|
53
|
+
return { [r.tag]: {
|
|
54
|
+
subfields: t,
|
|
55
|
+
ind1: r.indicator1,
|
|
56
|
+
ind2: r.indicator2
|
|
57
|
+
} };
|
|
58
|
+
}
|
|
59
|
+
throw new Error("Unknown field type");
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
leader: e.leader,
|
|
63
|
+
fields: i
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function A(e) {
|
|
67
|
+
return JSON.stringify(g(e));
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
p as parseMarcJson,
|
|
71
|
+
g as serializeMarcJson,
|
|
72
|
+
A as serializeMarcJsonString
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=marcjson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marcjson.js","names":[],"sources":["../src/marcjson.ts"],"sourcesContent":["/**\n * MARC-in-JSON parser and serializer.\n *\n * Follows the format described at https://wiki.code4lib.org/MARCJSONification\n * and used by Open Library and various REST APIs:\n *\n * {\n * \"leader\": \"01142cam a2200301 a 4500\",\n * \"fields\": [\n * { \"001\": \"5490\" },\n * { \"245\": {\n * \"subfields\": [{\"a\": \"The Hobbit\"}],\n * \"ind1\": \"1\",\n * \"ind2\": \"0\"\n * }}\n * ]\n * }\n */\n\nimport type { MarcRecord, ControlField, DataField, Subfield } from './types';\nimport { isControlField, isDataField } from './types';\n\n// ─── Raw JSON shape types ─────────────────────────────────────────────────────\n\nexport interface MarcJsonSubfieldEntry {\n [code: string]: string;\n}\n\nexport interface MarcJsonDataFieldValue {\n subfields: MarcJsonSubfieldEntry[];\n ind1: string;\n ind2: string;\n}\n\nexport type MarcJsonField =\n | { [tag: string]: string } // control field\n | { [tag: string]: MarcJsonDataFieldValue }; // data field\n\nexport interface MarcJsonObject {\n leader: string;\n fields: MarcJsonField[];\n}\n\n// ─── Parse ────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a MARC-in-JSON object or JSON string into a MarcRecord.\n *\n * Throws on structural errors (missing `leader`, non-array `fields`,\n * unrecognised field shapes).\n */\nexport function parseMarcJson(json: string | MarcJsonObject): MarcRecord {\n const obj: MarcJsonObject = typeof json === 'string' ? JSON.parse(json) : json;\n\n if (typeof obj.leader !== 'string') {\n throw new Error('MARC-in-JSON: missing or non-string \"leader\"');\n }\n if (!Array.isArray(obj.fields)) {\n throw new Error('MARC-in-JSON: missing or non-array \"fields\"');\n }\n\n const fields: (ControlField | DataField)[] = [];\n\n for (const entry of obj.fields) {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new Error('MARC-in-JSON: each field entry must be an object');\n }\n\n const keys = Object.keys(entry);\n if (keys.length !== 1) {\n throw new Error(`MARC-in-JSON: field entry must have exactly one key, got ${keys.join(', ')}`);\n }\n\n const tag = keys[0]!;\n const value = (entry as Record<string, unknown>)[tag];\n\n if (typeof value === 'string') {\n // Control field\n fields.push({ tag, data: value });\n continue;\n }\n\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const dv = value as MarcJsonDataFieldValue;\n if (!Array.isArray(dv.subfields)) {\n throw new Error(`MARC-in-JSON: data field \"${tag}\" missing \"subfields\" array`);\n }\n const subfields: Subfield[] = dv.subfields.map((sfEntry, idx) => {\n if (typeof sfEntry !== 'object' || sfEntry === null) {\n throw new Error(`MARC-in-JSON: subfield entry ${idx} of \"${tag}\" is not an object`);\n }\n const sfKeys = Object.keys(sfEntry);\n if (sfKeys.length !== 1) {\n throw new Error(\n `MARC-in-JSON: subfield entry ${idx} of \"${tag}\" must have exactly one key`\n );\n }\n const code = sfKeys[0]!;\n const sfValue = sfEntry[code];\n if (typeof sfValue !== 'string') {\n throw new Error(\n `MARC-in-JSON: subfield value for \"${tag}$${code}\" must be a string`\n );\n }\n return { code, value: sfValue };\n });\n\n fields.push({\n tag,\n indicator1: dv.ind1 ?? ' ',\n indicator2: dv.ind2 ?? ' ',\n subfields,\n });\n continue;\n }\n\n throw new Error(\n `MARC-in-JSON: field \"${tag}\" value must be a string (control) or object (data)`\n );\n }\n\n return { leader: obj.leader, fields };\n}\n\n// ─── Serialize ────────────────────────────────────────────────────────────────\n\n/**\n * Serialize a MarcRecord to a MARC-in-JSON plain object.\n */\nexport function serializeMarcJson(record: MarcRecord): MarcJsonObject {\n const fields: MarcJsonField[] = record.fields.map((field) => {\n if (isControlField(field)) {\n return { [field.tag]: field.data };\n }\n if (isDataField(field)) {\n const subfields: MarcJsonSubfieldEntry[] = field.subfields.map((sf) => ({\n [sf.code]: sf.value,\n }));\n return {\n [field.tag]: {\n subfields,\n ind1: field.indicator1,\n ind2: field.indicator2,\n },\n };\n }\n // Unreachable — isControlField/isDataField are exhaustive — but satisfies TS\n throw new Error('Unknown field type');\n });\n\n return { leader: record.leader, fields };\n}\n\n/**\n * Serialize a MarcRecord to a JSON string.\n */\nexport function serializeMarcJsonString(record: MarcRecord): string {\n return JSON.stringify(serializeMarcJson(record));\n}\n"],"mappings":";AAmDA,SAAgB,EAAc,GAA2C;AACvE,QAAM,IAAsB,OAAO,KAAS,WAAW,KAAK,MAAM,CAAI,IAAI;AAE1E,MAAI,OAAO,EAAI,UAAW,SACxB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,MAAI,CAAC,MAAM,QAAQ,EAAI,MAAM,EAC3B,OAAM,IAAI,MAAM,6CAA6C;AAG/D,QAAM,IAAuC,CAAC;AAE9C,aAAW,KAAS,EAAI,QAAQ;AAC9B,QAAI,OAAO,KAAU,YAAY,MAAU,QAAQ,MAAM,QAAQ,CAAK,EACpE,OAAM,IAAI,MAAM,kDAAkD;AAGpE,UAAM,IAAO,OAAO,KAAK,CAAK;AAC9B,QAAI,EAAK,WAAW,EAClB,OAAM,IAAI,MAAM,4DAA4D,EAAK,KAAK,IAAI,CAAA,EAAG;AAG/F,UAAM,IAAM,EAAK,CAAA,GACX,IAAS,EAAkC,CAAA;AAEjD,QAAI,OAAO,KAAU,UAAU;AAE7B,MAAA,EAAO,KAAK;AAAA,QAAE,KAAA;AAAA,QAAK,MAAM;AAAA,MAAM,CAAC;AAChC;AAAA,IACF;AAEA,QAAI,OAAO,KAAU,YAAY,MAAU,QAAQ,CAAC,MAAM,QAAQ,CAAK,GAAG;AACxE,YAAM,IAAK;AACX,UAAI,CAAC,MAAM,QAAQ,EAAG,SAAS,EAC7B,OAAM,IAAI,MAAM,6BAA6B,CAAA,6BAAgC;AAE/E,YAAM,IAAwB,EAAG,UAAU,IAAA,CAAK,GAAS,MAAQ;AAC/D,YAAI,OAAO,KAAY,YAAY,MAAY,KAC7C,OAAM,IAAI,MAAM,gCAAgC,CAAA,QAAW,CAAA,oBAAuB;AAEpF,cAAM,IAAS,OAAO,KAAK,CAAO;AAClC,YAAI,EAAO,WAAW,EACpB,OAAM,IAAI,MACR,gCAAgC,CAAA,QAAW,CAAA,6BAC7C;AAEF,cAAM,IAAO,EAAO,CAAA,GACd,IAAU,EAAQ,CAAA;AACxB,YAAI,OAAO,KAAY,SACrB,OAAM,IAAI,MACR,qCAAqC,CAAA,IAAO,CAAA,oBAC9C;AAEF,eAAO;AAAA,UAAE,MAAA;AAAA,UAAM,OAAO;AAAA,QAAQ;AAAA,MAChC,CAAC;AAED,MAAA,EAAO,KAAK;AAAA,QACV,KAAA;AAAA,QACA,YAAY,EAAG,QAAQ;AAAA,QACvB,YAAY,EAAG,QAAQ;AAAA,QACvB,WAAA;AAAA,MACF,CAAC;AACD;AAAA,IACF;AAEA,UAAM,IAAI,MACR,wBAAwB,CAAA,qDAC1B;AAAA,EACF;AAEA,SAAO;AAAA,IAAE,QAAQ,EAAI;AAAA,IAAQ,QAAA;AAAA,EAAO;AACtC;AAOA,SAAgB,EAAkB,GAAoC;AACpE,QAAM,IAA0B,EAAO,OAAO,IAAA,CAAK,MAAU;AAC3D,QAAI,EAAe,CAAK,EACtB,QAAO,EAAA,CAAG,EAAM,GAAA,GAAM,EAAM,KAAK;AAEnC,QAAI,EAAY,CAAK,GAAG;AACtB,YAAM,IAAqC,EAAM,UAAU,IAAA,CAAK,OAAQ,EAAA,CACrE,EAAG,IAAA,GAAO,EAAG,MAChB,EAAE;AACF,aAAO,EAAA,CACJ,EAAM,GAAA,GAAM;AAAA,QACX,WAAA;AAAA,QACA,MAAM,EAAM;AAAA,QACZ,MAAM,EAAM;AAAA,MACd,EACF;AAAA,IACF;AAEA,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC,CAAC;AAED,SAAO;AAAA,IAAE,QAAQ,EAAO;AAAA,IAAQ,QAAA;AAAA,EAAO;AACzC;AAKA,SAAgB,EAAwB,GAA4B;AAClE,SAAO,KAAK,UAAU,EAAkB,CAAM,CAAC;AACjD"}
|
package/dist/marctxt.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("./types-CJcxHJff.cjs");function s(e){return e===" "?"\\":e}function u(e){return e==="\\"?" ":e}function l(e){return e.replace(/[{}\\\n$]/g,r=>r==="{"?"{lcub}":r==="}"?"{rcub}":r==="$"?"{dollar}":r==="\\"?"{bsol}":" ")}function f(e){return e.replace(/\{(lcub|rcub|dollar|bsol)\}/g,(r,n)=>n==="lcub"?"{":n==="rcub"?"}":n==="dollar"?"$":"\\")}function h(e){const r=e.split(/\$(.)/),n=[];for(let t=1;t<r.length;t+=2)n.push({code:r[t],value:f(r[t+1]??"")});return n}function a(e){let r="";const n=[];for(const t of e){if(!t.startsWith("="))continue;const i=t.slice(1,4),o=t.slice(6);if(i==="LDR"||i==="000"){r=o;continue}if(i<"010"){n.push({tag:i,data:f(o)});continue}const c=u(o[0]??"\\"),b=u(o[1]??"\\"),$=h(o.slice(2));n.push({tag:i,indicator1:c,indicator2:b,subfields:$})}return{leader:r,fields:n}}function d(e){const r=e.replace(/\r\n/g,`
|
|
2
|
+
`).split(`
|
|
3
|
+
`),n=[];let t=[];for(const i of r)i.trim()===""?t.length>0&&(n.push(a(t)),t=[]):t.push(i);return t.length>0&&n.push(a(t)),n}function M(e){const r=d(e);if(r.length===0)throw new Error("No MARC record found in marctxt input");return r[0]}function p(e){const r=[];r.push(`=LDR ${e.leader}`);for(const n of e.fields)if(g.isControlField(n))r.push(`=${n.tag} ${l(n.data)}`);else{const t=s(n.indicator1),i=s(n.indicator2),o=n.subfields.map(c=>`$${c.code}${l(c.value)}`).join("");r.push(`=${n.tag} ${t}${i}${o}`)}return r.join(`
|
|
4
|
+
`)+`
|
|
5
|
+
`}function x(e){return e.map(p).join(`
|
|
6
|
+
`)}exports.parseMarcTxt=d;exports.parseMarcTxtRecord=M;exports.serializeMarcTxt=x;exports.serializeMarcTxtRecord=p;
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=marctxt.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marctxt.cjs","names":[],"sources":["../src/marctxt.ts"],"sourcesContent":["/**\n * MARCBreaker (marctxt) parser and serializer.\n *\n * Also known as MARCMaker format. Each field is one line:\n *\n * =LDR 00706cam a2200217 a 4500\n * =001 5490\n * =245 14$aThe Hobbit /$cJ.R.R. Tolkien.\n * =650 \\1$aHobbits (Fictitious characters)$vFiction.\n *\n * Blank indicators are represented as `\\`. Records are separated by blank lines.\n * Subfield delimiter is `$` followed by a single character code.\n *\n * Curly-brace escape sequences (per LC MARCMaker spec):\n * `{` → `{lcub}` (left curly brace; reserved by the format)\n * `}` → `{rcub}` (right curly brace; reserved by the format)\n * `$` → `{dollar}` (subfield delimiter)\n * `\\` → `{bsol}` (backslash; reserved as blank-indicator stand-in)\n *\n */\n\nimport type { MarcRecord, ControlField, DataField, Subfield } from './types';\nimport { isControlField } from './types';\n\n// ─── Indicator encoding ───────────────────────────────────────────────────────\n\nfunction encodeIndicator(ind: string): string {\n return ind === ' ' ? '\\\\' : ind;\n}\n\nfunction decodeIndicator(ch: string): string {\n return ch === '\\\\' ? ' ' : ch;\n}\n\n// ─── Value escape (see file header) ───────────────────────────────────────────\n\nfunction escapeValue(s: string): string {\n // Single-pass replacement avoids the problem of earlier escapes being\n // re-escaped by later passes (e.g. '{' → '{lcub}' then '}' → '{lcub{rcub}').\n return s.replace(/[{}\\\\\\n$]/g, (ch) => {\n if (ch === '{') return '{lcub}';\n if (ch === '}') return '{rcub}';\n if (ch === '$') return '{dollar}';\n if (ch === '\\\\') return '{bsol}';\n return ' ';\n });\n}\n\nfunction unescapeValue(s: string): string {\n return s.replace(/\\{(lcub|rcub|dollar|bsol)\\}/g, (_, name) => {\n if (name === 'lcub') return '{';\n if (name === 'rcub') return '}';\n if (name === 'dollar') return '$';\n return '\\\\';\n });\n}\n\n// ─── Subfield parsing ─────────────────────────────────────────────────────────\n\n/**\n * Parse a subfield string like \"$aValue$bOther\" into Subfield objects.\n * Uses split with a capturing group: \"$aFoo$bBar\" → [\"\", \"a\", \"Foo\", \"b\", \"Bar\"].\n * Any character following `$` is treated as a subfield code.\n */\nfunction parseSubfields(str: string): Subfield[] {\n const parts = str.split(/\\$(.)/);\n const subfields: Subfield[] = [];\n // parts[0] is content before the first $ — should be empty for well-formed data\n for (let i = 1; i < parts.length; i += 2) {\n subfields.push({ code: parts[i]!, value: unescapeValue(parts[i + 1] ?? '') });\n }\n return subfields;\n}\n\n// ─── Record block parser ──────────────────────────────────────────────────────\n\n/**\n * Parse a block of non-empty marctxt lines into a MarcRecord.\n * Each line has the form `=TAG content`.\n */\nfunction parseRecordLines(lines: string[]): MarcRecord {\n let leader = '';\n const fields: (ControlField | DataField)[] = [];\n\n for (const line of lines) {\n if (!line.startsWith('=')) continue;\n const tag = line.slice(1, 4);\n // positions 4-5 are the two separator spaces; content starts at 6\n const content = line.slice(6);\n\n if (tag === 'LDR' || tag === '000') {\n leader = content;\n continue;\n }\n\n if (tag < '010') {\n // Control field: content is the raw field data\n fields.push({ tag, data: unescapeValue(content) });\n continue;\n }\n\n // Data field: first two chars are indicators, rest are subfields\n const indicator1 = decodeIndicator(content[0] ?? '\\\\');\n const indicator2 = decodeIndicator(content[1] ?? '\\\\');\n const subfields = parseSubfields(content.slice(2));\n fields.push({ tag, indicator1, indicator2, subfields });\n }\n\n return { leader, fields };\n}\n\n// ─── Public parse API ─────────────────────────────────────────────────────────\n\n/**\n * Parse a marctxt string containing one or more records separated by blank lines.\n * Returns all records found.\n */\nexport function parseMarcTxt(text: string): MarcRecord[] {\n const lines = text.replace(/\\r\\n/g, '\\n').split('\\n');\n const records: MarcRecord[] = [];\n let buffer: string[] = [];\n\n for (const line of lines) {\n if (line.trim() === '') {\n if (buffer.length > 0) {\n records.push(parseRecordLines(buffer));\n buffer = [];\n }\n } else {\n buffer.push(line);\n }\n }\n\n if (buffer.length > 0) {\n records.push(parseRecordLines(buffer));\n }\n\n return records;\n}\n\n/**\n * Parse a marctxt string expected to contain exactly one record.\n * Throws if no record is found.\n */\nexport function parseMarcTxtRecord(text: string): MarcRecord {\n const records = parseMarcTxt(text);\n if (records.length === 0) throw new Error('No MARC record found in marctxt input');\n return records[0]!;\n}\n\n// ─── Serializer ───────────────────────────────────────────────────────────────\n\n/**\n * Serialize a single MarcRecord to marctxt format.\n * Returns a string with one field per line and a trailing newline.\n */\nexport function serializeMarcTxtRecord(record: MarcRecord): string {\n const lines: string[] = [];\n\n lines.push(`=LDR ${record.leader}`);\n\n for (const field of record.fields) {\n if (isControlField(field)) {\n lines.push(`=${field.tag} ${escapeValue(field.data)}`);\n } else {\n const ind1 = encodeIndicator(field.indicator1);\n const ind2 = encodeIndicator(field.indicator2);\n const subfields = field.subfields\n .map((sf) => `$${sf.code}${escapeValue(sf.value)}`)\n .join('');\n lines.push(`=${field.tag} ${ind1}${ind2}${subfields}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Serialize one or more MarcRecords into a marctxt string.\n * Records are separated by blank lines.\n */\nexport function serializeMarcTxt(records: MarcRecord[]): string {\n // Each record ends with '\\n'; joining with '\\n' produces blank lines between records.\n return records.map(serializeMarcTxtRecord).join('\\n');\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MarcRecord } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a marctxt string containing one or more records separated by blank lines.
|
|
4
|
+
* Returns all records found.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseMarcTxt(text: string): MarcRecord[];
|
|
7
|
+
/**
|
|
8
|
+
* Parse a marctxt string expected to contain exactly one record.
|
|
9
|
+
* Throws if no record is found.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseMarcTxtRecord(text: string): MarcRecord;
|
|
12
|
+
/**
|
|
13
|
+
* Serialize a single MarcRecord to marctxt format.
|
|
14
|
+
* Returns a string with one field per line and a trailing newline.
|
|
15
|
+
*/
|
|
16
|
+
export declare function serializeMarcTxtRecord(record: MarcRecord): string;
|
|
17
|
+
/**
|
|
18
|
+
* Serialize one or more MarcRecords into a marctxt string.
|
|
19
|
+
* Records are separated by blank lines.
|
|
20
|
+
*/
|
|
21
|
+
export declare function serializeMarcTxt(records: MarcRecord[]): string;
|
|
22
|
+
//# sourceMappingURL=marctxt.d.ts.map
|
package/dist/marctxt.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { t as b } from "./types-c4Mo9m9u.js";
|
|
2
|
+
function c(n) {
|
|
3
|
+
return n === " " ? "\\" : n;
|
|
4
|
+
}
|
|
5
|
+
function u(n) {
|
|
6
|
+
return n === "\\" ? " " : n;
|
|
7
|
+
}
|
|
8
|
+
function l(n) {
|
|
9
|
+
return n.replace(/[{}\\\n$]/g, (r) => r === "{" ? "{lcub}" : r === "}" ? "{rcub}" : r === "$" ? "{dollar}" : r === "\\" ? "{bsol}" : " ");
|
|
10
|
+
}
|
|
11
|
+
function a(n) {
|
|
12
|
+
return n.replace(/\{(lcub|rcub|dollar|bsol)\}/g, (r, e) => e === "lcub" ? "{" : e === "rcub" ? "}" : e === "dollar" ? "$" : "\\");
|
|
13
|
+
}
|
|
14
|
+
function $(n) {
|
|
15
|
+
const r = n.split(/\$(.)/), e = [];
|
|
16
|
+
for (let t = 1; t < r.length; t += 2) e.push({
|
|
17
|
+
code: r[t],
|
|
18
|
+
value: a(r[t + 1] ?? "")
|
|
19
|
+
});
|
|
20
|
+
return e;
|
|
21
|
+
}
|
|
22
|
+
function f(n) {
|
|
23
|
+
let r = "";
|
|
24
|
+
const e = [];
|
|
25
|
+
for (const t of n) {
|
|
26
|
+
if (!t.startsWith("=")) continue;
|
|
27
|
+
const i = t.slice(1, 4), o = t.slice(6);
|
|
28
|
+
if (i === "LDR" || i === "000") {
|
|
29
|
+
r = o;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (i < "010") {
|
|
33
|
+
e.push({
|
|
34
|
+
tag: i,
|
|
35
|
+
data: a(o)
|
|
36
|
+
});
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const s = u(o[0] ?? "\\"), d = u(o[1] ?? "\\"), p = $(o.slice(2));
|
|
40
|
+
e.push({
|
|
41
|
+
tag: i,
|
|
42
|
+
indicator1: s,
|
|
43
|
+
indicator2: d,
|
|
44
|
+
subfields: p
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
leader: r,
|
|
49
|
+
fields: e
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function h(n) {
|
|
53
|
+
const r = n.replace(/\r\n/g, `
|
|
54
|
+
`).split(`
|
|
55
|
+
`), e = [];
|
|
56
|
+
let t = [];
|
|
57
|
+
for (const i of r) i.trim() === "" ? t.length > 0 && (e.push(f(t)), t = []) : t.push(i);
|
|
58
|
+
return t.length > 0 && e.push(f(t)), e;
|
|
59
|
+
}
|
|
60
|
+
function R(n) {
|
|
61
|
+
const r = h(n);
|
|
62
|
+
if (r.length === 0) throw new Error("No MARC record found in marctxt input");
|
|
63
|
+
return r[0];
|
|
64
|
+
}
|
|
65
|
+
function g(n) {
|
|
66
|
+
const r = [];
|
|
67
|
+
r.push(`=LDR ${n.leader}`);
|
|
68
|
+
for (const e of n.fields) if (b(e)) r.push(`=${e.tag} ${l(e.data)}`);
|
|
69
|
+
else {
|
|
70
|
+
const t = c(e.indicator1), i = c(e.indicator2), o = e.subfields.map((s) => `$${s.code}${l(s.value)}`).join("");
|
|
71
|
+
r.push(`=${e.tag} ${t}${i}${o}`);
|
|
72
|
+
}
|
|
73
|
+
return r.join(`
|
|
74
|
+
`) + `
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
function M(n) {
|
|
78
|
+
return n.map(g).join(`
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
h as parseMarcTxt,
|
|
83
|
+
R as parseMarcTxtRecord,
|
|
84
|
+
M as serializeMarcTxt,
|
|
85
|
+
g as serializeMarcTxtRecord
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=marctxt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marctxt.js","names":[],"sources":["../src/marctxt.ts"],"sourcesContent":["/**\n * MARCBreaker (marctxt) parser and serializer.\n *\n * Also known as MARCMaker format. Each field is one line:\n *\n * =LDR 00706cam a2200217 a 4500\n * =001 5490\n * =245 14$aThe Hobbit /$cJ.R.R. Tolkien.\n * =650 \\1$aHobbits (Fictitious characters)$vFiction.\n *\n * Blank indicators are represented as `\\`. Records are separated by blank lines.\n * Subfield delimiter is `$` followed by a single character code.\n *\n * Curly-brace escape sequences (per LC MARCMaker spec):\n * `{` → `{lcub}` (left curly brace; reserved by the format)\n * `}` → `{rcub}` (right curly brace; reserved by the format)\n * `$` → `{dollar}` (subfield delimiter)\n * `\\` → `{bsol}` (backslash; reserved as blank-indicator stand-in)\n *\n */\n\nimport type { MarcRecord, ControlField, DataField, Subfield } from './types';\nimport { isControlField } from './types';\n\n// ─── Indicator encoding ───────────────────────────────────────────────────────\n\nfunction encodeIndicator(ind: string): string {\n return ind === ' ' ? '\\\\' : ind;\n}\n\nfunction decodeIndicator(ch: string): string {\n return ch === '\\\\' ? ' ' : ch;\n}\n\n// ─── Value escape (see file header) ───────────────────────────────────────────\n\nfunction escapeValue(s: string): string {\n // Single-pass replacement avoids the problem of earlier escapes being\n // re-escaped by later passes (e.g. '{' → '{lcub}' then '}' → '{lcub{rcub}').\n return s.replace(/[{}\\\\\\n$]/g, (ch) => {\n if (ch === '{') return '{lcub}';\n if (ch === '}') return '{rcub}';\n if (ch === '$') return '{dollar}';\n if (ch === '\\\\') return '{bsol}';\n return ' ';\n });\n}\n\nfunction unescapeValue(s: string): string {\n return s.replace(/\\{(lcub|rcub|dollar|bsol)\\}/g, (_, name) => {\n if (name === 'lcub') return '{';\n if (name === 'rcub') return '}';\n if (name === 'dollar') return '$';\n return '\\\\';\n });\n}\n\n// ─── Subfield parsing ─────────────────────────────────────────────────────────\n\n/**\n * Parse a subfield string like \"$aValue$bOther\" into Subfield objects.\n * Uses split with a capturing group: \"$aFoo$bBar\" → [\"\", \"a\", \"Foo\", \"b\", \"Bar\"].\n * Any character following `$` is treated as a subfield code.\n */\nfunction parseSubfields(str: string): Subfield[] {\n const parts = str.split(/\\$(.)/);\n const subfields: Subfield[] = [];\n // parts[0] is content before the first $ — should be empty for well-formed data\n for (let i = 1; i < parts.length; i += 2) {\n subfields.push({ code: parts[i]!, value: unescapeValue(parts[i + 1] ?? '') });\n }\n return subfields;\n}\n\n// ─── Record block parser ──────────────────────────────────────────────────────\n\n/**\n * Parse a block of non-empty marctxt lines into a MarcRecord.\n * Each line has the form `=TAG content`.\n */\nfunction parseRecordLines(lines: string[]): MarcRecord {\n let leader = '';\n const fields: (ControlField | DataField)[] = [];\n\n for (const line of lines) {\n if (!line.startsWith('=')) continue;\n const tag = line.slice(1, 4);\n // positions 4-5 are the two separator spaces; content starts at 6\n const content = line.slice(6);\n\n if (tag === 'LDR' || tag === '000') {\n leader = content;\n continue;\n }\n\n if (tag < '010') {\n // Control field: content is the raw field data\n fields.push({ tag, data: unescapeValue(content) });\n continue;\n }\n\n // Data field: first two chars are indicators, rest are subfields\n const indicator1 = decodeIndicator(content[0] ?? '\\\\');\n const indicator2 = decodeIndicator(content[1] ?? '\\\\');\n const subfields = parseSubfields(content.slice(2));\n fields.push({ tag, indicator1, indicator2, subfields });\n }\n\n return { leader, fields };\n}\n\n// ─── Public parse API ─────────────────────────────────────────────────────────\n\n/**\n * Parse a marctxt string containing one or more records separated by blank lines.\n * Returns all records found.\n */\nexport function parseMarcTxt(text: string): MarcRecord[] {\n const lines = text.replace(/\\r\\n/g, '\\n').split('\\n');\n const records: MarcRecord[] = [];\n let buffer: string[] = [];\n\n for (const line of lines) {\n if (line.trim() === '') {\n if (buffer.length > 0) {\n records.push(parseRecordLines(buffer));\n buffer = [];\n }\n } else {\n buffer.push(line);\n }\n }\n\n if (buffer.length > 0) {\n records.push(parseRecordLines(buffer));\n }\n\n return records;\n}\n\n/**\n * Parse a marctxt string expected to contain exactly one record.\n * Throws if no record is found.\n */\nexport function parseMarcTxtRecord(text: string): MarcRecord {\n const records = parseMarcTxt(text);\n if (records.length === 0) throw new Error('No MARC record found in marctxt input');\n return records[0]!;\n}\n\n// ─── Serializer ───────────────────────────────────────────────────────────────\n\n/**\n * Serialize a single MarcRecord to marctxt format.\n * Returns a string with one field per line and a trailing newline.\n */\nexport function serializeMarcTxtRecord(record: MarcRecord): string {\n const lines: string[] = [];\n\n lines.push(`=LDR ${record.leader}`);\n\n for (const field of record.fields) {\n if (isControlField(field)) {\n lines.push(`=${field.tag} ${escapeValue(field.data)}`);\n } else {\n const ind1 = encodeIndicator(field.indicator1);\n const ind2 = encodeIndicator(field.indicator2);\n const subfields = field.subfields\n .map((sf) => `$${sf.code}${escapeValue(sf.value)}`)\n .join('');\n lines.push(`=${field.tag} ${ind1}${ind2}${subfields}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\n/**\n * Serialize one or more MarcRecords into a marctxt string.\n * Records are separated by blank lines.\n */\nexport function serializeMarcTxt(records: MarcRecord[]): string {\n // Each record ends with '\\n'; joining with '\\n' produces blank lines between records.\n return records.map(serializeMarcTxtRecord).join('\\n');\n}\n"],"mappings":"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"}
|
package/dist/marcxml.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const X=require("./types-CJcxHJff.cjs");var b=new Map([["amp","&"],["lt","<"],["gt",">"],["quot",'"'],["apos","'"]]);function g(t){return t.replace(/&(?:#x([0-9a-fA-F]+)|#([0-9]+)|([a-zA-Z]+));/g,(r,i,c,e)=>{if(i!==void 0){const n=parseInt(i,16);return n>=0&&n<=1114111?String.fromCodePoint(n):"�"}if(c!==void 0){const n=parseInt(c,10);return n>=0&&n<=1114111?String.fromCodePoint(n):"�"}return b.get(e)??r})}function u(t){return t.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g,"�").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/\r/g," ")}function f(t){const r=t.indexOf(":");return r===-1?t:t.slice(r+1)}function m(t){const r={},i=/([a-zA-Z_:][^\s=]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;let c;for(;(c=i.exec(t))!==null;){const e=f(c[1]);r[e]=g(c[2]??c[3]??"")}return r}function v(t){const r=[];let i=0;for(;i<t.length;){const c=t.indexOf("<",i);if(c===-1){t.slice(i).trim()&&r.push({type:"text",text:g(t.slice(i))});break}if(c>i){const o=t.slice(i,c);o.trim()&&r.push({type:"text",text:g(o)})}const e=t.indexOf(">",c);if(e===-1)break;const n=t.slice(c+1,e);if(n.startsWith("!")||n.startsWith("?")){i=e+1;continue}if(n.startsWith("/"))r.push({type:"close",name:f(n.slice(1).trim())});else if(n.endsWith("/")){const o=n.slice(0,-1).trim(),s=o.search(/\s/),l=s===-1?o:o.slice(0,s),p=s===-1?"":o.slice(s);r.push({type:"self-close",name:f(l),attrs:m(p)})}else{const o=n.search(/\s/),s=o===-1?n:n.slice(0,o),l=o===-1?"":n.slice(o);r.push({type:"open",name:f(s),attrs:m(l)})}i=e+1}return r}function w(t,r){let i="";const c=[];let e=r;for(;e<t.length;){const n=t[e];if(n.type==="close"&&n.name==="record")return{record:{leader:i,fields:c},end:e+1};if(n.type==="open"&&n.name==="leader"){e++,e<t.length&&t[e].type==="text"&&(i=t[e].text.trim(),e++),e<t.length&&t[e].type==="close"&&e++;continue}if(n.type==="self-close"&&n.name==="controlfield"){c.push({tag:n.attrs?.tag??"",data:""}),e++;continue}if(n.type==="open"&&n.name==="controlfield"){const o=n.attrs?.tag??"";e++;let s="";e<t.length&&t[e].type==="text"&&(s=t[e].text??"",e++),e<t.length&&t[e].type==="close"&&e++,c.push({tag:o,data:s});continue}if(n.type==="self-close"&&n.name==="datafield"){c.push({tag:n.attrs?.tag??"",indicator1:n.attrs?.ind1??" ",indicator2:n.attrs?.ind2??" ",subfields:[]}),e++;continue}if(n.type==="open"&&n.name==="datafield"){const o=n.attrs?.tag??"",s=n.attrs?.ind1??" ",l=n.attrs?.ind2??" ",p=[];for(e++;e<t.length;){const d=t[e];if(d.type==="close"&&d.name==="datafield"){e++;break}if(d.type==="open"&&d.name==="subfield"){const $=d.attrs?.code??"";e++;let h="";e<t.length&&t[e].type==="text"&&(h=t[e].text??"",e++),e<t.length&&t[e].type==="close"&&e++,p.push({code:$,value:h});continue}e++}c.push({tag:o,indicator1:s,indicator2:l,subfields:p});continue}e++}return{record:{leader:i,fields:c},end:e}}function y(t){const r=v(t),i=[];let c=0;for(;c<r.length;){const e=r[c];if(e.type==="open"&&e.name==="record"){const{record:n,end:o}=w(r,c+1);i.push(n),c=o;continue}c++}return i}function A(t){const r=y(t);if(r.length===0)throw new Error("No MARC record found in MARCXML input");return r[0]}var C=`<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
`,x='xmlns="http://www.loc.gov/MARC21/slim"',a=" ";function M(t){const r=[`<record ${x}>`];r.push(`${a}<leader>${u(t.leader)}</leader>`);for(const i of t.fields)if(X.isControlField(i))r.push(`${a}<controlfield tag="${i.tag}">${u(i.data)}</controlfield>`);else{const c=i.indicator1===" "?" ":i.indicator1,e=i.indicator2===" "?" ":i.indicator2;r.push(`${a}<datafield tag="${i.tag}" ind1="${c}" ind2="${e}">`);for(const n of i.subfields)r.push(`${a}${a}<subfield code="${n.code}">${u(n.value)}</subfield>`);r.push(`${a}</datafield>`)}return r.push("</record>"),r.join(`
|
|
3
|
+
`)}function R(t){const r=[C,`<collection ${x}>`];for(const i of t){const c=M(i).split(`
|
|
4
|
+
`).map(e=>a+e).join(`
|
|
5
|
+
`);r.push(c)}return r.push("</collection>"),r.join(`
|
|
6
|
+
`)}exports.parseMarcXml=y;exports.parseMarcXmlRecord=A;exports.serializeMarcXml=R;exports.serializeMarcXmlRecord=M;
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=marcxml.cjs.map
|