{"_id":"@delta-and-beta/electron-datatable","_rev":"2-a88f73391ebee94dae094e8cb52ae5c6","name":"@delta-and-beta/electron-datatable","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@delta-and-beta/electron-datatable","version":"0.1.0","keywords":["react","table","electron-datatable","data-table","group-by","grouping","tailwind","compound-component"],"license":"MIT","_id":"@delta-and-beta/electron-datatable@0.1.0","maintainers":[{"name":"marchi.lau","email":"marchi.lau@delta-and-beta.com"}],"homepage":"https://github.com/delta-and-beta/electron-datatable#readme","bugs":{"url":"https://github.com/delta-and-beta/electron-datatable/issues"},"dist":{"shasum":"1aafdac65ba675e7cbcdb51022d479b9edd3c29b","tarball":"https://registry.npmjs.org/@delta-and-beta/electron-datatable/-/electron-datatable-0.1.0.tgz","fileCount":15,"integrity":"sha512-XZxhDFkLOX11BaIQT/M1FFyFf4g//4RMvD7JEzbto8Y8TPI96bTblcflUV8INQgb/e46CB++Hkjx07SXI1jtOA==","signatures":[{"sig":"MEYCIQCNDDdhHMTfuWOBM8Zr91EhTk0nn2fZbYPbdbeS60vA3QIhAOogBy8HX4pZMS5XxCB6sVTCfWCT/X+FV+bQ1hJs+vgI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":612217},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tailwind":{"types":"./dist/tailwind.d.ts","import":"./dist/tailwind.js","require":"./dist/tailwind.cjs"},"./styles.css":"./dist/styles.css","./themes/dark.css":"./dist/themes/dark.css","./themes/light.css":"./dist/themes/light.css"},"gitHead":"ff09a92a8f7db56bc25a755ad7344dce93cf2288","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup && tailwindcss -i src/styles-input.css -o dist/styles.css -c tailwind.build.config.ts --minify && mkdir -p dist/themes && cp src/themes/*.css dist/themes/","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"marchi.lau","email":"marchi.lau@delta-and-beta.com"},"repository":{"url":"git+https://github.com/delta-and-beta/electron-datatable.git","type":"git"},"_npmVersion":"11.6.2","description":"A feature-rich, composable React data table with multi-level grouping, attachments, search, and column management","directories":{},"_nodeVersion":"24.13.0","dependencies":{"clsx":"^2.1.0","lucide-react":"^0.468.0","tailwind-merge":"^2.6.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","jsdom":"^25.0.0","react":"^18.3.0","eslint":"^9.39.4","vitest":"^2.1.0","react-dom":"^18.3.0","@eslint/js":"^9.39.4","typescript":"^5.7.0","tailwindcss":"^3.4.0","@types/react":"^18.3.0","@types/react-dom":"^18.3.0","typescript-eslint":"^8.58.0","@testing-library/react":"^16.1.0","@testing-library/jest-dom":"^6.6.3","eslint-plugin-react-hooks":"^5.2.0"},"peerDependencies":{"react":">=18","react-dom":">=18","tailwindcss":">=3"},"_npmOperationalInternal":{"tmp":"tmp/electron-datatable_0.1.0_1775299035519_0.05770999971240487","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@delta-and-beta/electron-datatable","version":"0.2.0","description":"A feature-rich, composable React data table with multi-level grouping, attachments, search, and column management","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./tailwind":{"types":"./dist/tailwind.d.ts","import":"./dist/tailwind.js","require":"./dist/tailwind.cjs"},"./matching":{"types":"./dist/matching.d.ts","import":"./dist/matching.js","require":"./dist/matching.cjs"},"./styles.css":"./dist/styles.css","./themes/dark.css":"./dist/themes/dark.css","./themes/light.css":"./dist/themes/light.css"},"scripts":{"dev":"tsup --watch","build":"tsup && tailwindcss -i src/styles-input.css -o dist/styles.css -c tailwind.build.config.ts --minify && mkdir -p dist/themes && cp src/themes/*.css dist/themes/","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","lint":"eslint src/","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18","react-dom":">=18","tailwindcss":">=3"},"devDependencies":{"@eslint/js":"^9.39.4","@testing-library/jest-dom":"^6.6.3","@testing-library/react":"^16.1.0","@types/react":"^18.3.0","@types/react-dom":"^18.3.0","eslint":"^9.39.4","eslint-plugin-react-hooks":"^5.2.0","jsdom":"^25.0.0","react":"^18.3.0","react-dom":"^18.3.0","tailwindcss":"^3.4.0","tsup":"^8.3.0","typescript":"^5.7.0","typescript-eslint":"^8.58.0","vitest":"^2.1.0"},"dependencies":{"clsx":"^2.1.0","lucide-react":"^0.468.0","tailwind-merge":"^2.6.0"},"keywords":["react","table","electron-datatable","data-table","group-by","grouping","tailwind","compound-component"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/delta-and-beta/electron-datatable.git"},"gitHead":"c443b9610865566304e41fb2c01e1eb5feb48c6b","_id":"@delta-and-beta/electron-datatable@0.2.0","bugs":{"url":"https://github.com/delta-and-beta/electron-datatable/issues"},"homepage":"https://github.com/delta-and-beta/electron-datatable#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-UMr/4vFEv85y9L1Q0qWzwu/4Uf9/fWwk7xba5aBryB7SbAOA7KdQvbb3oGMUcobg72WW56DVhNzHMOf13WkurA==","shasum":"9c5508fbccf6bfb0e74f6f03ea4dd7110e0f7597","tarball":"https://registry.npmjs.org/@delta-and-beta/electron-datatable/-/electron-datatable-0.2.0.tgz","fileCount":21,"unpackedSize":1680870,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFPL/vAiZACEfsbWQIKZ+ENor45WhC9vyDMXGLAGLgBJAiEAlgea9W9BrFsNs46+NH5yjsIxIpYTcmywH/6aQctt508="}]},"_npmUser":{"name":"marchi.lau","email":"marchi.lau@delta-and-beta.com"},"directories":{},"maintainers":[{"name":"marchi.lau","email":"marchi.lau@delta-and-beta.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/electron-datatable_0.2.0_1783729612427_0.8118167021416252"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-04T10:37:15.405Z","modified":"2026-07-11T00:26:52.809Z","0.1.0":"2026-04-04T10:37:15.660Z","0.2.0":"2026-07-11T00:26:52.677Z"},"bugs":{"url":"https://github.com/delta-and-beta/electron-datatable/issues"},"license":"MIT","homepage":"https://github.com/delta-and-beta/electron-datatable#readme","keywords":["react","table","electron-datatable","data-table","group-by","grouping","tailwind","compound-component"],"repository":{"type":"git","url":"git+https://github.com/delta-and-beta/electron-datatable.git"},"description":"A feature-rich, composable React data table with multi-level grouping, attachments, search, and column management","maintainers":[{"name":"marchi.lau","email":"marchi.lau@delta-and-beta.com"}],"readme":"<div align=\"center\">\n  <br />\n  <h1>electron-datatable</h1>\n  <p><strong>A composable React data table with multi-level grouping, filtering, search, sorting, and column management.</strong></p>\n  <p>Styled with Tailwind CSS. Themed via CSS custom properties. Fully type-safe.</p>\n\n  <br />\n\n  [![npm](https://img.shields.io/npm/v/@delta-and-beta/electron-datatable?color=3b82f6&label=npm&style=flat-square)](https://www.npmjs.com/package/@delta-and-beta/electron-datatable)\n  [![license](https://img.shields.io/github/license/delta-and-beta/electron-datatable?color=22c55e&style=flat-square)](./LICENSE)\n  [![TypeScript](https://img.shields.io/badge/TypeScript-5.7+-3178c6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)\n  [![React](https://img.shields.io/badge/React-18+-61dafb?style=flat-square&logo=react&logoColor=white)](https://react.dev/)\n  [![Tailwind](https://img.shields.io/badge/Tailwind_CSS-3+-06b6d4?style=flat-square&logo=tailwindcss&logoColor=white)](https://tailwindcss.com/)\n\n  <br />\n</div>\n\n---\n\n## Highlights\n\n| | |\n|---|---|\n| **Multi-level grouping** | Group by any column, nest groups, bucket dates by month / quarter / year |\n| **Smart filtering** | Nested AND/OR condition groups with type-aware operators |\n| **Full-text search** | Debounced, case-insensitive search across all text columns |\n| **Sorting** | Locale-aware, null-last semantics, click-to-toggle |\n| **Column management** | Toggle visibility, reorder columns at runtime |\n| **Auto-aggregation** | Number & currency columns summed in group headers automatically |\n| **State persistence** | Sort, filter, grouping, and column state saved to `localStorage` |\n| **Compound components** | Use presets for zero-config, or compose your own layout |\n| **Theming** | `dt-*` design tokens via CSS custom properties, dark & light included |\n| **Bulk file matching** | Drop files → OCR → match → confirm → attach, with adapter pattern |\n| **Type-safe** | Generic `defineTable<T>()` with full IntelliSense on column IDs |\n| **Tree-shakeable** | Every hook and pure function importable independently |\n\n---\n\n## Quick Start\n\n### 1. Install\n\n```bash\nnpm install @delta-and-beta/electron-datatable\n```\n\n> Peer dependencies: `react >= 18`, `react-dom >= 18`, `tailwindcss >= 3`\n\n### 2. Configure Tailwind\n\n```ts\n// tailwind.config.ts\nimport { dataTablePreset } from '@delta-and-beta/electron-datatable/tailwind'\n\nexport default {\n  presets: [dataTablePreset],\n  content: [\n    './src/**/*.{ts,tsx}',\n    './node_modules/@delta-and-beta/electron-datatable/dist/**/*.js',\n  ],\n}\n```\n\n### 3. Import styles and a theme\n\n```tsx\nimport '@delta-and-beta/electron-datatable/styles.css'\nimport '@delta-and-beta/electron-datatable/themes/dark.css'   // or themes/light.css\n```\n\n### 4. Define and render your table\n\n```tsx\nimport { DataTable, defineTable } from '@delta-and-beta/electron-datatable'\n\ninterface Transaction {\n  id: string\n  date: string\n  merchant: string\n  amount: number\n  category: string\n  status: 'settled' | 'pending' | 'declined'\n}\n\nconst table = defineTable<Transaction>({\n  rowKey: 'id',\n  storageKey: 'transactions',\n  columns: {\n    date:     { label: 'Date',     type: 'date', datePeriods: ['month', 'quarter'] },\n    merchant: { label: 'Merchant', type: 'text' },\n    amount:   { label: 'Amount',   type: 'currency', currency: 'USD' },\n    category: { label: 'Category', type: 'text', groupable: true },\n    status:   { label: 'Status',   type: 'text', groupable: true },\n  },\n  defaults: {\n    sort: { field: 'date', direction: 'desc' },\n  },\n})\n\nfunction App() {\n  return <DataTable {...table} data={transactions} preset=\"full\" />\n}\n```\n\nThat's it. You get search, filtering, grouping, column toggle, and sorting out of the box.\n\n---\n\n## Presets\n\n| Preset | What renders | Use case |\n|--------|-------------|----------|\n| `\"full\"` | Toolbar (search + filter + group-by + column toggle) + table + footer | Drop in and go |\n| `\"minimal\"` | Table + footer | When you provide your own toolbar |\n| `\"none\"` | Nothing &mdash; you compose with `children` | Full layout control |\n\n```tsx\n// Full — zero config\n<DataTable {...table} data={data} preset=\"full\" />\n\n// Minimal — content only\n<DataTable {...table} data={data} preset=\"minimal\" />\n\n// Custom — compose your own layout\n<DataTable {...table} data={data}>\n  <DataTable.Toolbar>\n    <DataTable.Search className=\"w-80\" />\n    <DataTable.ColumnToggle />\n  </DataTable.Toolbar>\n  <DataTable.Content stickyHeader onRowClick={(row) => console.log(row)} />\n  <DataTable.Footer />\n</DataTable>\n```\n\n---\n\n## Theming\n\nAll colors flow through CSS custom properties. Override them globally or scope them per table.\n\n### Tokens\n\n| Token | Dark | Light | Purpose |\n|-------|------|-------|---------|\n| `--dt-primary` | `#3b82f6` | `#3b82f6` | Buttons, active states |\n| `--dt-primary-hover` | `#2563eb` | `#2563eb` | Hover states |\n| `--dt-bg` | `#1a1a2e` | `#ffffff` | Table background |\n| `--dt-bg-secondary` | `#16213e` | `#f9fafb` | Alternating rows, panels |\n| `--dt-border` | `#374151` | `#e5e7eb` | Borders, dividers |\n| `--dt-text` | `#f3f4f6` | `#111827` | Primary text |\n| `--dt-muted` | `#9ca3af` | `#6b7280` | Secondary text, placeholders |\n| `--dt-positive` | `#22c55e` | `#16a34a` | Positive values |\n| `--dt-negative` | `#ef4444` | `#dc2626` | Negative values |\n\n### Using your own design system\n\nSkip the theme files and map your existing tokens:\n\n```css\n:root {\n  --dt-primary: var(--color-primary);\n  --dt-bg: var(--color-bg);\n  --dt-text: var(--color-text);\n  /* ... */\n}\n```\n\n### Scoped themes\n\nApply different themes to different tables:\n\n```css\n.invoices-table { --dt-primary: #8b5cf6; --dt-bg: #0f172a; }\n.orders-table   { --dt-primary: #06b6d4; --dt-bg: #1e1b4b; }\n```\n\n```tsx\n<DataTable {...invoiceTable} data={invoices} preset=\"full\" className=\"invoices-table\" />\n<DataTable {...orderTable}   data={orders}   preset=\"full\" className=\"orders-table\" />\n```\n\n---\n\n## Column Types & Operators\n\n| Type | Searchable | Auto-sum | Filter operators |\n|------|:----------:|:--------:|------------------|\n| `text` | yes | &mdash; | is, is not, contains, does not contain, is empty, is not empty |\n| `number` | &mdash; | yes | =, !=, >, <, >=, <=, is empty, is not empty |\n| `currency` | &mdash; | yes | Same as number |\n| `date` | &mdash; | &mdash; | is, is before, is after, is on or before, is on or after, is empty, is not empty |\n| `custom` | &mdash; | &mdash; | Use `render` and `format` for display |\n\nAll behavior flags (`sortable`, `searchable`, `groupable`, `visible`, `filterable`) default to `true` and can be overridden per column.\n\n---\n\n## Sub-Components\n\nWhen using `preset=\"none\"`, compose your layout with:\n\n| Component | Description |\n|-----------|-------------|\n| `DataTable.Toolbar` | Flex container for toolbar items |\n| `DataTable.Search` | Search input with debounce |\n| `DataTable.Filter` | Filter toolbar button (badge shows active count) |\n| `DataTable.FilterPanel` | Filter condition builder panel |\n| `DataTable.GroupBy` | Group-by toolbar button |\n| `DataTable.GroupByPanel` | Group-by configuration panel |\n| `DataTable.ColumnToggle` | Column visibility dropdown |\n| `DataTable.DateFilter` | Date range filter |\n| `DataTable.Content` | Table body with rows, groups, and headers |\n| `DataTable.Footer` | Row count footer |\n| `DataTable.GroupHeader` | Custom group header renderer |\n\n---\n\n## Standalone Hooks\n\nEach data pipeline hook is independently importable for building custom table UIs:\n\n```tsx\nimport { useFilter, useSearch, useSort, useGroupBy, useColumns } from '@delta-and-beta/electron-datatable'\n\nfunction CustomTable({ data, columns }) {\n  const filter   = useFilter({ data, columns, storageKey: 'my-table' })\n  const search   = useSearch({ data: filter.filteredData, columns })\n  const sort     = useSort({ data: search.filteredData, storageKey: 'my-table' })\n  const columns_ = useColumns({ columns, storageKey: 'my-table' })\n  const groupBy  = useGroupBy({\n    data: sort.sortedData,\n    columns,\n    sumFields: ['amount'],\n    storageKey: 'my-table',\n  })\n\n  // Build your own UI from groupBy.groupedData, columns_.visibleColumns, etc.\n}\n```\n\nThe hooks must be called in pipeline order: **filter -> search -> sort -> groupBy**. Each hook's output feeds into the next.\n\nAccess full table state from any child component via context:\n\n```tsx\nimport { useDataTable } from '@delta-and-beta/electron-datatable'\n\nfunction StatusBar() {\n  const { filteredData, groupBy } = useDataTable()\n  return <div>{filteredData.length} records, {groupBy.levels.length} groups</div>\n}\n```\n\n---\n\n## API Reference\n\n### `defineTable<T>(config)`\n\n```ts\ndefineTable<Transaction>({\n  rowKey: 'id',                    // keyof T & string — unique row identifier\n  storageKey: 'transactions',      // localStorage key prefix\n  columns: {                       // object-keyed, IDs inferred from keys\n    date: { label: 'Date', type: 'date' },\n    amount: { label: 'Amount', type: 'currency', currency: 'USD' },\n  },\n  defaults: {\n    sort: { field: 'date', direction: 'desc' },\n    groupBy: [{ field: 'category', sort: 'asc' }],\n  },\n})\n```\n\nReturns `{ columns, rowKey, storageKey, defaultSort, defaultGroupBy }` — spread directly onto `<DataTable>`.\n\n### `ColumnDef<T>`\n\n```ts\n{\n  id: string                          // Column identifier (matches data field)\n  label: string                       // Header text\n  type: 'text' | 'number' | 'date' | 'currency' | 'custom'\n\n  // Display\n  render?: (value: unknown, row: T) => ReactNode\n  headerRender?: () => ReactNode\n  width?: string                      // e.g. '200px', '20%'\n  align?: 'left' | 'center' | 'right'\n\n  // Behavior (all default to true)\n  sortable?: boolean\n  groupable?: boolean\n  searchable?: boolean\n  visible?: boolean\n  filterable?: boolean\n\n  // Grouping\n  datePeriods?: ('day' | 'week' | 'month' | 'quarter' | 'year')[]\n  sumInGroup?: boolean                // true by default for number/currency\n\n  // Formatting\n  format?: (value: unknown) => string\n  currency?: string                   // ISO 4217 (e.g. 'USD', 'EUR')\n}\n```\n\n### `DataTableProps<T>`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `data` | `T[]` | required | Row data |\n| `columns` | `ColumnDef<T>[]` | required | Column definitions |\n| `rowKey` | `keyof T & string` | required | Unique identifier field |\n| `storageKey` | `string` | `'dt'` | localStorage key prefix |\n| `preset` | `'full' \\| 'minimal' \\| 'none'` | `'none'` | Render mode |\n| `defaultSort` | `{ field, direction }` | &mdash; | Initial sort (overridden by saved state) |\n| `defaultGroupBy` | `GroupLevel[]` | &mdash; | Initial grouping (overridden by saved state) |\n| `onRowClick` | `(row: T) => void` | &mdash; | Row click handler |\n| `onRowContextMenu` | `(row: T, event) => void` | &mdash; | Row right-click handler |\n| `attachmentAdapter` | `AttachmentAdapter` | &mdash; | File attachment backend |\n| `className` | `string` | &mdash; | Root element class |\n\n### `AttachmentAdapter`\n\n```ts\ninterface AttachmentAdapter {\n  add(rowId: string, filename: string, mimeType: string, dataBase64: string): Promise<Attachment>\n  list(rowId: string): Promise<Attachment[]>\n  delete(attachmentId: string): Promise<void>\n  getCounts(rowIds: string[]): Promise<Record<string, number>>\n}\n```\n\n---\n\n## Bulk File Matching\n\nThe `/matching` entry point adds OCR-based file matching as a composable layer on top of DataTable. Consumers provide a `MatchingAdapter` and the package handles the entire flow.\n\n### Install\n\nThe matching module ships in the same package — just import from the `/matching` path:\n\n```tsx\nimport { MatchingDataTable } from '@delta-and-beta/electron-datatable/matching'\n```\n\n### Zero-config usage\n\n```tsx\nimport { defineTable } from '@delta-and-beta/electron-datatable'\nimport { MatchingDataTable } from '@delta-and-beta/electron-datatable/matching'\n\nconst matchingAdapter = {\n  ocr: (files) => myOcrService.process(files),\n  match: (ocrResults, transactions, onProgress) => myMatcher.match(ocrResults, transactions, onProgress),\n  summarize: (row) => ({\n    id: row.id,\n    date: row.date,\n    amount: row.amount,\n    currency: 'USD',\n    description: row.merchant,\n  }),\n}\n\n<MatchingDataTable\n  {...table}\n  data={data}\n  preset=\"full\"\n  attachmentAdapter={attachmentAdapter}\n  matchingAdapter={matchingAdapter}\n/>\n```\n\nWhen users drop 2+ files onto the table, the matching flow runs automatically:\n1. Files are read and filtered by MIME type (PDF, PNG, JPEG, GIF by default)\n2. OCR is called via `matchingAdapter.ocr()`\n3. Results are matched against transactions via `matchingAdapter.match()`\n4. Duplicates are detected against existing attachments\n5. A report dialog shows matches (with confidence badges) and unmatched files\n6. User confirms, and files are attached via `attachmentAdapter.add()`\n\nSingle-file drops go directly to `attachmentAdapter.add()` (no OCR/matching).\n\n### With shadcn Dialog\n\n```tsx\nimport { Dialog, DialogContent } from '@/components/ui/dialog'\n\n<MatchingDataTable\n  {...table}\n  data={data}\n  preset=\"full\"\n  attachmentAdapter={attachmentAdapter}\n  matchingAdapter={matchingAdapter}\n  matchingDialogWrapper={({ open, onClose, children }) => (\n    <Dialog open={open} onOpenChange={(v) => !v && onClose()}>\n      <DialogContent className=\"max-w-3xl max-h-[80vh] overflow-hidden\">\n        {children}\n      </DialogContent>\n    </Dialog>\n  )}\n/>\n```\n\n### Manual composition\n\n```tsx\nimport { DataTable } from '@delta-and-beta/electron-datatable'\nimport { useMatching, MatchingProvider, BulkDropZone, MatchingReportDialog } from '@delta-and-beta/electron-datatable/matching'\n\nfunction MyTable({ data }) {\n  const matching = useMatching({ matchingAdapter, attachmentAdapter, data })\n\n  return (\n    <MatchingProvider value={matching}>\n      <div className=\"relative\" {...matching.dropHandlers}>\n        <DataTable {...table} data={data} preset=\"full\" />\n        <BulkDropZone />\n        <MatchingReportDialog />\n      </div>\n    </MatchingProvider>\n  )\n}\n```\n\n### `MatchingAdapter<T>`\n\n```ts\ninterface MatchingAdapter<T> {\n  ocr(files: Array<{ filename: string; mimeType: string; dataBase64: string }>): Promise<OcrResult[]>\n  match(\n    ocrResults: OcrResult[],\n    transactions: TransactionSummary[],\n    onProgress: (progress: MatchingProgress) => void,\n  ): Promise<MatchResult>\n  summarize(row: T): TransactionSummary\n}\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}