{"version":3,"sources":["../src/index.js","../src/date.js","../src/numbers.js","../src/table.js"],"sourcesContent":["export { formatThaiDate } from './date.js';\nexport { formatNumber, validateNumber } from './numbers.js';\nexport { createAccessibleTable, normalizeRows } from './table.js';\n","const ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:T(?:\\d{2}):(?:\\d{2})(?::(?:\\d{2})(?:\\.\\d{1,3})?)?(?:Z|[+-]\\d{2}:\\d{2})?)?$/;\n\nfunction isPlainDateOptions(value) {\n  if (value === null || Object.prototype.toString.call(value) !== '[object Object]') return false;\n  const prototype = Object.getPrototypeOf(value);\n  return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\n\nfunction toDate(value) {\n  if (!(value instanceof Date) && typeof value !== 'number' && typeof value !== 'string') {\n    throw new TypeError('date must be a valid Date, timestamp, or ISO date string');\n  }\n  if (typeof value === 'string' && !ISO_DATE.test(value)) {\n    throw new TypeError('date must be a valid Date, timestamp, or ISO date string');\n  }\n  if (typeof value === 'number' && !Number.isFinite(value)) {\n    throw new TypeError('date must be a valid Date, timestamp, or ISO date string');\n  }\n\n  const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);\n  if (Number.isNaN(date.getTime())) {\n    throw new TypeError('date must be a valid Date, timestamp, or ISO date string');\n  }\n  if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n    const [year, month, day] = value.split('-').map(Number);\n    if (date.getUTCFullYear() !== year || date.getUTCMonth() + 1 !== month || date.getUTCDate() !== day) {\n      throw new TypeError('date must be a valid Date, timestamp, or ISO date string');\n    }\n  }\n  return date;\n}\n\nexport function formatThaiDate(value, options = {}) {\n  if (!isPlainDateOptions(options)) throw new TypeError('options must be a plain object');\n  const date = toDate(value);\n  let formatter;\n  try {\n    formatter = new Intl.DateTimeFormat('th-TH', { ...options });\n  } catch {\n    throw new RangeError('options contain unsupported Intl.DateTimeFormat values');\n  }\n  return formatter.format(date);\n}\n","function invalid(error) {\n  return { valid: false, normalized: null, error };\n}\n\nexport function validateNumber(value, digits) {\n  if (!Number.isSafeInteger(digits) || digits <= 0) {\n    return invalid('digits must be a positive safe integer');\n  }\n\n  let text;\n  if (typeof value === 'number') {\n    if (value < 0) return invalid('value must not be negative');\n    if (!Number.isFinite(value) || !Number.isInteger(value)) {\n      return invalid('value must be an integer without decimals');\n    }\n    if (!Number.isSafeInteger(value)) return invalid('value must be a safe integer');\n    text = String(value);\n  } else if (typeof value === 'string') {\n    if (value.startsWith('-')) return invalid('value must not be negative');\n    if (value.includes('.')) return invalid('value must be an integer without decimals');\n    if (!/^\\d+$/.test(value)) return invalid('value must contain digits only');\n    text = value;\n  } else {\n    return invalid('value must be a string or number containing digits only');\n  }\n\n  if (text.length > digits) return invalid(`value must not exceed ${digits} digits`);\n  return { valid: true, normalized: text.padStart(digits, '0'), error: null };\n}\n\nexport function formatNumber(value, digits) {\n  const result = validateNumber(value, digits);\n  if (!result.valid) {\n    const ErrorType = result.error.startsWith('digits') || /negative|exceed/.test(result.error)\n      ? RangeError\n      : TypeError;\n    throw new ErrorType(result.error);\n  }\n  return result.normalized;\n}\n","import { formatThaiDate } from './date.js';\nimport { formatNumber } from './numbers.js';\n\nconst DEFAULT_DATE_OPTIONS = Object.freeze({\n  timeZone: 'Asia/Bangkok',\n  year: 'numeric',\n  month: '2-digit',\n  day: '2-digit',\n});\n\nfunction isPlainObject(value) {\n  if (value === null || Object.prototype.toString.call(value) !== '[object Object]') return false;\n  const prototype = Object.getPrototypeOf(value);\n  return prototype === null || Object.getPrototypeOf(prototype) === null;\n}\n\nfunction validateOptions(options) {\n  if (!isPlainObject(options)) throw new TypeError('options must be a plain object');\n  const digits = Object.hasOwn(options, 'digits') ? options.digits : 4;\n  if (!Number.isSafeInteger(digits) || digits <= 0) {\n    throw new RangeError('digits must be a positive safe integer');\n  }\n  const dateOptions = Object.hasOwn(options, 'dateOptions') ? options.dateOptions : DEFAULT_DATE_OPTIONS;\n  if (!isPlainObject(dateOptions)) throw new TypeError('options.dateOptions must be a plain object');\n  return { digits, dateOptions: { ...dateOptions } };\n}\n\nexport function normalizeRows(rows, options = {}) {\n  if (!Array.isArray(rows)) throw new TypeError('rows must be an array');\n  const { digits, dateOptions } = validateOptions(options);\n\n  return rows.map((row, index) => {\n    if (!isPlainObject(row)) throw new TypeError(`rows[${index}] must be a plain object`);\n    if (typeof row.label !== 'string' || row.label.trim() === '') {\n      throw new TypeError(`rows[${index}].label must be a non-empty string`);\n    }\n    if (!Object.hasOwn(row, 'number')) throw new TypeError(`rows[${index}].number is required`);\n    if (!Object.hasOwn(row, 'date')) throw new TypeError(`rows[${index}].date is required`);\n    if (Object.hasOwn(row, 'metadata') && !isPlainObject(row.metadata)) {\n      throw new TypeError(`rows[${index}].metadata must be a plain object`);\n    }\n\n    let number;\n    let date;\n    try {\n      number = formatNumber(row.number, digits);\n    } catch (error) {\n      throw new TypeError(`rows[${index}].number: ${error.message}`);\n    }\n    try {\n      date = formatThaiDate(row.date, dateOptions);\n    } catch (error) {\n      throw new TypeError(`rows[${index}].date: ${error.message}`);\n    }\n\n    const normalized = { label: row.label, number, date };\n    if (Object.hasOwn(row, 'metadata')) normalized.metadata = { ...row.metadata };\n    return normalized;\n  });\n}\n\nfunction escapeHtml(value) {\n  return String(value)\n    .replaceAll('&', '&amp;')\n    .replaceAll('<', '&lt;')\n    .replaceAll('>', '&gt;')\n    .replaceAll('\"', '&quot;')\n    .replaceAll(\"'\", '&#39;');\n}\n\nexport function createAccessibleTable(rows, options = {}) {\n  if (!isPlainObject(options)) throw new TypeError('options must be a plain object');\n  const caption = options.caption ?? 'Formatted data';\n  const classPrefix = options.classPrefix ?? 'amb1234';\n  if (typeof caption !== 'string' || caption.trim() === '') {\n    throw new TypeError('caption must be a non-empty string');\n  }\n  if (typeof classPrefix !== 'string' || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(classPrefix)) {\n    throw new TypeError('classPrefix must start with a letter and contain only letters, numbers, underscores, or hyphens');\n  }\n\n  const normalized = normalizeRows(rows, options);\n  const body = normalized.map((row) => (\n    `<tr><th scope=\"row\">${escapeHtml(row.label)}</th><td>${escapeHtml(row.number)}</td><td>${escapeHtml(row.date)}</td></tr>`\n  )).join('');\n\n  return `<table class=\"${escapeHtml(classPrefix)}-table\"><caption>${escapeHtml(caption)}</caption><thead><tr><th scope=\"col\">Label</th><th scope=\"col\">Number</th><th scope=\"col\">Date</th></tr></thead><tbody>${body}</tbody></table>`;\n}\n"],"names":[],"mappings":"","file":"index.js"}
