@kernhq/module-quire 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/contract/models.d.ts +394 -0
  2. package/dist/contract/models.d.ts.map +1 -1
  3. package/dist/contract/models.js +284 -0
  4. package/dist/contract/models.js.map +1 -1
  5. package/dist/contract/permissions.d.ts +14 -0
  6. package/dist/contract/permissions.d.ts.map +1 -1
  7. package/dist/contract/permissions.js +114 -0
  8. package/dist/contract/permissions.js.map +1 -1
  9. package/dist/contract/properties.d.ts +7 -7
  10. package/dist/contract/router.d.ts +1124 -96
  11. package/dist/contract/router.d.ts.map +1 -1
  12. package/dist/contract/router.js +343 -1
  13. package/dist/contract/router.js.map +1 -1
  14. package/dist/server/_impl.d.ts +983 -1168
  15. package/dist/server/_impl.d.ts.map +1 -1
  16. package/dist/server/_impl.js +308 -0
  17. package/dist/server/_impl.js.map +1 -1
  18. package/dist/server/export/html.d.ts +67 -0
  19. package/dist/server/export/html.d.ts.map +1 -0
  20. package/dist/server/export/html.js +206 -0
  21. package/dist/server/export/html.js.map +1 -0
  22. package/dist/server/export/markdown.d.ts +51 -0
  23. package/dist/server/export/markdown.d.ts.map +1 -0
  24. package/dist/server/export/markdown.js +351 -0
  25. package/dist/server/export/markdown.js.map +1 -0
  26. package/dist/server/export/pdf.d.ts +20 -0
  27. package/dist/server/export/pdf.d.ts.map +1 -0
  28. package/dist/server/export/pdf.js +91 -0
  29. package/dist/server/export/pdf.js.map +1 -0
  30. package/dist/server/export/zip.d.ts +31 -0
  31. package/dist/server/export/zip.d.ts.map +1 -0
  32. package/dist/server/export/zip.js +158 -0
  33. package/dist/server/export/zip.js.map +1 -0
  34. package/dist/server/import/csv.d.ts +77 -0
  35. package/dist/server/import/csv.d.ts.map +1 -0
  36. package/dist/server/import/csv.js +263 -0
  37. package/dist/server/import/csv.js.map +1 -0
  38. package/dist/server/import/html.d.ts +52 -0
  39. package/dist/server/import/html.d.ts.map +1 -0
  40. package/dist/server/import/html.js +472 -0
  41. package/dist/server/import/html.js.map +1 -0
  42. package/dist/server/import/markdown.d.ts +63 -0
  43. package/dist/server/import/markdown.d.ts.map +1 -0
  44. package/dist/server/import/markdown.js +692 -0
  45. package/dist/server/import/markdown.js.map +1 -0
  46. package/dist/server/import/plan.d.ts +70 -0
  47. package/dist/server/import/plan.d.ts.map +1 -0
  48. package/dist/server/import/plan.js +761 -0
  49. package/dist/server/import/plan.js.map +1 -0
  50. package/dist/server/import/ydoc.d.ts +35 -0
  51. package/dist/server/import/ydoc.d.ts.map +1 -0
  52. package/dist/server/import/ydoc.js +91 -0
  53. package/dist/server/import/ydoc.js.map +1 -0
  54. package/dist/server/import/zip.d.ts +63 -0
  55. package/dist/server/import/zip.d.ts.map +1 -0
  56. package/dist/server/import/zip.js +308 -0
  57. package/dist/server/import/zip.js.map +1 -0
  58. package/dist/server/index.d.ts.map +1 -1
  59. package/dist/server/index.js +3 -1
  60. package/dist/server/index.js.map +1 -1
  61. package/dist/server/render.d.ts +158 -0
  62. package/dist/server/render.d.ts.map +1 -1
  63. package/dist/server/render.js +240 -0
  64. package/dist/server/render.js.map +1 -1
  65. package/dist/server/schema.d.ts +704 -1
  66. package/dist/server/schema.d.ts.map +1 -1
  67. package/dist/server/schema.js +254 -1
  68. package/dist/server/schema.js.map +1 -1
  69. package/dist/server/services/databases.d.ts +10 -10
  70. package/dist/server/services/export.d.ts +176 -0
  71. package/dist/server/services/export.d.ts.map +1 -0
  72. package/dist/server/services/export.js +822 -0
  73. package/dist/server/services/export.js.map +1 -0
  74. package/dist/server/services/import.d.ts +109 -0
  75. package/dist/server/services/import.d.ts.map +1 -0
  76. package/dist/server/services/import.js +570 -0
  77. package/dist/server/services/import.js.map +1 -0
  78. package/dist/server/services/index.d.ts +32 -1
  79. package/dist/server/services/index.d.ts.map +1 -1
  80. package/dist/server/services/index.js +87 -4
  81. package/dist/server/services/index.js.map +1 -1
  82. package/dist/server/services/macros.d.ts +83 -0
  83. package/dist/server/services/macros.d.ts.map +1 -0
  84. package/dist/server/services/macros.js +488 -0
  85. package/dist/server/services/macros.js.map +1 -0
  86. package/dist/server/services/publications.d.ts +2 -1
  87. package/dist/server/services/publications.d.ts.map +1 -1
  88. package/dist/server/services/publications.js +43 -4
  89. package/dist/server/services/publications.js.map +1 -1
  90. package/dist/server/services/templates.d.ts +135 -0
  91. package/dist/server/services/templates.d.ts.map +1 -0
  92. package/dist/server/services/templates.js +897 -0
  93. package/dist/server/services/templates.js.map +1 -0
  94. package/dist/server/services/versions.d.ts +13 -1
  95. package/dist/server/services/versions.d.ts.map +1 -1
  96. package/dist/server/services/versions.js +3 -1
  97. package/dist/server/services/versions.js.map +1 -1
  98. package/migrations/0010_transfers.sql +154 -0
  99. package/migrations/0011_templates.sql +157 -0
  100. package/migrations/meta/_journal.json +14 -0
  101. package/package.json +5 -5
  102. package/src/client/components/ExportDialog.svelte +685 -0
  103. package/src/client/components/ImportDialog.svelte +702 -0
  104. package/src/client/components/ImportReport.svelte +310 -0
  105. package/src/client/components/NewSpaceDialog.svelte +77 -8
  106. package/src/client/components/PageEditor.svelte +80 -0
  107. package/src/client/components/PagePicker.svelte +264 -0
  108. package/src/client/components/SaveAsTemplateDialog.svelte +502 -0
  109. package/src/client/components/SidebarSpaces.svelte +112 -1
  110. package/src/client/components/TemplatePicker.svelte +437 -0
  111. package/src/client/i18n.ts +941 -0
  112. package/src/client/index.ts +51 -0
  113. package/src/client/mock.ts +592 -0
  114. package/src/client/module.ts +36 -0
  115. package/src/client/pages/PageView.svelte +86 -0
  116. package/src/client/pages/SpacePage.svelte +20 -4
  117. package/src/client/pages/TransfersPage.svelte +570 -0
  118. package/src/client/permissions.ts +11 -0
  119. package/src/client/query.ts +40 -0
  120. package/src/client/transfers.ts +142 -0
  121. package/src/contract/models.ts +343 -0
  122. package/src/contract/permissions.ts +117 -0
  123. package/src/contract/router.ts +375 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * A zip container, written by hand.
3
+ *
4
+ * An export of a subtree or a space is a folder per page with its pictures beside it, which is a zip
5
+ * and cannot reasonably be anything else. Writing the container here rather than taking a dependency
6
+ * is a deliberate trade and worth stating: the format needed is the 1989 one — deflate or store, no
7
+ * encryption, no ZIP64, sizes known before the entry is written — and that is about a hundred lines
8
+ * of well-specified header. `archiver` and `jszip` are streaming libraries whose job is everything
9
+ * this does not need, and every module repository commits its own lockfile, so a dependency here is
10
+ * a lockfile refresh outside the workspace in six places before it is a line of code.
11
+ *
12
+ * The limits are the honest part of that trade, and they are checked rather than assumed. No ZIP64
13
+ * means an archive is refused above 4 GiB, an entry above 4 GiB, or 65,535 entries — refused with a
14
+ * message, never truncated into a file that unzips to something incomplete. An export that would hit
15
+ * one of those is a job that fails and says so, which is the rule the whole slice is built on.
16
+ *
17
+ * Everything is deflated except what deflate makes bigger, which is most already-compressed pictures.
18
+ * The comparison is done rather than guessed from the extension: a PNG of flat colour still shrinks.
19
+ */
20
+ import { deflateRawSync } from 'node:zlib';
21
+ /** 0xFFFFFFFF. Above this an offset or a size needs ZIP64, which this writer does not emit. */
22
+ const MAX_32 = 0xffff_ffff;
23
+ /** The count field in the end-of-central-directory record is 16 bits. */
24
+ const MAX_ENTRIES = 0xffff;
25
+ /** `crc32` over the whole entry, which the reader checks — a wrong one is a "corrupt archive". */
26
+ const CRC_TABLE = (() => {
27
+ const table = new Int32Array(256);
28
+ for (let n = 0; n < 256; n++) {
29
+ let c = n;
30
+ for (let k = 0; k < 8; k++)
31
+ c = c & 1 ? 0xedb8_8320 ^ (c >>> 1) : c >>> 1;
32
+ table[n] = c;
33
+ }
34
+ return table;
35
+ })();
36
+ export function crc32(data) {
37
+ let c = 0xffff_ffff;
38
+ for (let i = 0; i < data.length; i++)
39
+ c = CRC_TABLE[(c ^ data[i]) & 0xff] ^ (c >>> 8);
40
+ return (c ^ 0xffff_ffff) >>> 0;
41
+ }
42
+ /**
43
+ * The MS-DOS date and time a zip entry carries, from a real clock.
44
+ *
45
+ * Not a constant: an archive whose every file is stamped 1980 looks broken in a file manager, and
46
+ * some restore tools treat it as such. The resolution is two seconds, which is the format's, and the
47
+ * clock is UTC because the format has no zone and a local one would make the same export produce
48
+ * different bytes on two machines.
49
+ */
50
+ function dosStamp(at) {
51
+ const year = Math.max(1980, at.getUTCFullYear());
52
+ return {
53
+ time: (at.getUTCHours() << 11) | (at.getUTCMinutes() << 5) | (at.getUTCSeconds() >> 1),
54
+ date: ((year - 1980) << 9) | ((at.getUTCMonth() + 1) << 5) | at.getUTCDate(),
55
+ };
56
+ }
57
+ /** What a caller may not exceed, so a refusal can name the number it broke. */
58
+ export const ZIP_LIMITS = { maxTotalBytes: MAX_32, maxEntryBytes: MAX_32, maxEntries: MAX_ENTRIES };
59
+ /**
60
+ * A path that cannot escape the folder somebody unzips into.
61
+ *
62
+ * A page title becomes a folder name, and a title is user input: `../../etc/whatever` is a real
63
+ * archive somebody can be handed, and a tool that follows it writes outside the extraction
64
+ * directory. Every segment is cleaned rather than rejected, because refusing an export because
65
+ * somebody named a page `C:\` would be absurd — but the cleaning is total, so nothing survives that
66
+ * a reader could resolve upwards.
67
+ */
68
+ export function safeZipPath(path) {
69
+ const segments = path
70
+ .split('/')
71
+ .map((segment) => segment
72
+ .normalize('NFC')
73
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: control characters are exactly what a filename must not carry
74
+ .replace(/[\u0000-\u001f\u007f\\:*?"<>|]+/g, '-')
75
+ .replace(/^\.+$/, '-')
76
+ .replace(/^\s+|[\s.]+$/g, '')
77
+ .slice(0, 100))
78
+ .filter((segment) => segment.length > 0);
79
+ return segments.join('/');
80
+ }
81
+ /**
82
+ * Every entry, in one buffer.
83
+ *
84
+ * Sizes are known before anything is written, so no entry carries a data descriptor and no reader
85
+ * has to seek backwards — which is what makes the output openable by the strictest tools as well as
86
+ * by the forgiving ones.
87
+ */
88
+ export function writeZip(entries, now = new Date()) {
89
+ if (entries.length > MAX_ENTRIES)
90
+ throw new Error(`This export has ${entries.length} files, and a zip may hold ${MAX_ENTRIES}`);
91
+ const { time, date } = dosStamp(now);
92
+ const locals = [];
93
+ const centrals = [];
94
+ let offset = 0;
95
+ for (const entry of entries) {
96
+ const name = Buffer.from(entry.path, 'utf8');
97
+ const raw = Buffer.from(entry.data.buffer, entry.data.byteOffset, entry.data.byteLength);
98
+ if (raw.length > MAX_32)
99
+ throw new Error(`${entry.path} is larger than 4 GB, which a zip cannot hold`);
100
+ const deflated = raw.length > 0 ? deflateRawSync(raw, { level: 6 }) : Buffer.alloc(0);
101
+ // Stored rather than deflated when deflate did not help — which is the normal case for a JPEG.
102
+ const compress = deflated.length < raw.length;
103
+ const body = compress ? deflated : raw;
104
+ const crc = crc32(raw);
105
+ if (offset > MAX_32 - body.length - name.length - 30)
106
+ throw new Error('This export is larger than 4 GB, which is more than a zip can address');
107
+ const local = Buffer.alloc(30);
108
+ local.writeUInt32LE(0x0403_4b50, 0);
109
+ local.writeUInt16LE(20, 4);
110
+ // 0x0800: the name below is UTF-8. Without it a Persian page title is mojibake on extraction.
111
+ local.writeUInt16LE(0x0800, 6);
112
+ local.writeUInt16LE(compress ? 8 : 0, 8);
113
+ local.writeUInt16LE(time, 10);
114
+ local.writeUInt16LE(date, 12);
115
+ local.writeUInt32LE(crc, 14);
116
+ local.writeUInt32LE(body.length, 18);
117
+ local.writeUInt32LE(raw.length, 22);
118
+ local.writeUInt16LE(name.length, 26);
119
+ local.writeUInt16LE(0, 28);
120
+ locals.push(local, name, body);
121
+ const central = Buffer.alloc(46);
122
+ central.writeUInt32LE(0x0201_4b50, 0);
123
+ // Made by a Unix zip (3), spec 2.0 — so the permission bits below are read as Unix ones.
124
+ central.writeUInt16LE(0x0314, 4);
125
+ central.writeUInt16LE(20, 6);
126
+ central.writeUInt16LE(0x0800, 8);
127
+ central.writeUInt16LE(compress ? 8 : 0, 10);
128
+ central.writeUInt16LE(time, 12);
129
+ central.writeUInt16LE(date, 14);
130
+ central.writeUInt32LE(crc, 16);
131
+ central.writeUInt32LE(body.length, 20);
132
+ central.writeUInt32LE(raw.length, 24);
133
+ central.writeUInt16LE(name.length, 28);
134
+ central.writeUInt16LE(0, 30);
135
+ central.writeUInt16LE(0, 32);
136
+ central.writeUInt16LE(0, 34);
137
+ central.writeUInt16LE(0, 36);
138
+ // 0o100644 in the high half: a regular file somebody owns and everyone may read.
139
+ // Multiplied rather than shifted — `<<` is a 32-bit *signed* operator, so `0o100644 << 16` is
140
+ // negative and `writeUInt32LE` throws on it. The whole archive fails to build, at the last entry.
141
+ central.writeUInt32LE(0o100644 * 0x1_0000, 38);
142
+ central.writeUInt32LE(offset, 42);
143
+ centrals.push(central, name);
144
+ offset += local.length + name.length + body.length;
145
+ }
146
+ const directory = Buffer.concat(centrals);
147
+ const end = Buffer.alloc(22);
148
+ end.writeUInt32LE(0x0605_4b50, 0);
149
+ end.writeUInt16LE(0, 4);
150
+ end.writeUInt16LE(0, 6);
151
+ end.writeUInt16LE(entries.length, 8);
152
+ end.writeUInt16LE(entries.length, 10);
153
+ end.writeUInt32LE(directory.length, 12);
154
+ end.writeUInt32LE(offset, 16);
155
+ end.writeUInt16LE(0, 20);
156
+ return Buffer.concat([...locals, directory, end]);
157
+ }
158
+ //# sourceMappingURL=zip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip.js","sourceRoot":"","sources":["../../../src/server/export/zip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * A CSV as a Quire database: columns with guessed types, and rows carrying values of those types.
3
+ *
4
+ * A Notion export writes one `.csv` per database — the same file you would get from a spreadsheet —
5
+ * and a CSV has no types at all. Importing every column as text is the safe answer and the useless
6
+ * one: a date column you cannot sort by date and a number column that sorts 10 before 9 are the two
7
+ * complaints a database import gets, and both are the same complaint.
8
+ *
9
+ * **So the type is guessed, and the guess is reported.** Every column contributes a sentence to the
10
+ * import report saying what it was read as and on what evidence — "Due: read as a date, from 12 of 12
11
+ * values" — because a guess nobody is told about is indistinguishable from a mistake. The rules are
12
+ * ordered from most specific to least and every one of them demands *every* non-empty value in the
13
+ * column agree; one stray `n/a` in a date column makes it text, which is the right way round. A
14
+ * column that is text is not a failure and does not say "failed": it is a column whose values did not
15
+ * all look like anything narrower.
16
+ *
17
+ * What this file does **not** do is guess a relation, a rollup, a formula or a person. Each of those
18
+ * names something outside the file — another database, another column, a member of this workspace —
19
+ * and inventing one from a string of text is how an import produces a database that looks right and
20
+ * computes nothing. They stay text, and the report says so.
21
+ */
22
+ import type { PropertyConfig, PropertyType } from '../../contract/index.js';
23
+ /**
24
+ * RFC 4180, with the two departures every real file needs.
25
+ *
26
+ * A quoted field may hold newlines and `""` for a literal quote — that part is the specification. The
27
+ * departures are that a lone `\r` or a `\r\n` both end a record (Excel writes one, Notion the other)
28
+ * and that a stray quote inside an unquoted field is a character rather than an error, because the
29
+ * alternative is refusing a file every other reader opens.
30
+ */
31
+ export declare function parseCsv(source: string): string[][];
32
+ /** `2026-01-05`, `January 5, 2026`, `05/01/2026` — as the ISO string a date cell stores. */
33
+ export declare function parseDateValue(raw: string): string | null;
34
+ /** `1,234.50`, `$99`, `12%` — as the number a numeric sort and a rollup can use. */
35
+ export declare function parseNumberValue(raw: string): {
36
+ value: number;
37
+ percent: boolean;
38
+ currency: string | null;
39
+ } | null;
40
+ export interface GuessedColumn {
41
+ name: string;
42
+ type: PropertyType;
43
+ config: PropertyConfig;
44
+ /**
45
+ * What the guess was and what it was made from, in one sentence for the report.
46
+ *
47
+ * Written here rather than at the call site because only this function knows the evidence — how
48
+ * many values agreed, how many distinct choices there were — and a reason assembled later would
49
+ * either repeat the rules or say less than it could.
50
+ */
51
+ note: string;
52
+ }
53
+ /**
54
+ * One column's type, from its values.
55
+ *
56
+ * The order is the whole algorithm: checkbox before number (`1` and `0` are both), number before
57
+ * date (a bare year is a number), url and email before select (a column of six addresses is not six
58
+ * choices), multi-select before select (a comma is the only thing that separates them), and select
59
+ * before text (the fallback that is always true).
60
+ */
61
+ export declare function guessColumn(name: string, rawValues: string[]): GuessedColumn;
62
+ /** `Design, Urgent` → `['Design', 'Urgent']`, which is how every exporter writes a multi-select. */
63
+ export declare const splitChoices: (value: string) => string[];
64
+ /**
65
+ * One cell, as the value the column's type stores.
66
+ *
67
+ * Null means "leave the key out of `props`" rather than "write null": an absent key is what every
68
+ * other writer in this module produces for an empty cell, and a null would make a `not_contains`
69
+ * filter behave differently for an imported row than for one somebody typed.
70
+ *
71
+ * `select` stores a **scalar** option id and `multi_select` an **array** of them, and the difference
72
+ * is load-bearing rather than cosmetic: `query.ts` compares a select as text through `props->>'key'`,
73
+ * so an array there stringifies to `["done"]` and matches no filter anybody can write. The cell
74
+ * component reads either shape, which is exactly why this is easy to get wrong and invisible on screen.
75
+ */
76
+ export declare function coerceValue(column: GuessedColumn, raw: string): unknown;
77
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../../src/server/import/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAgB,MAAM,yBAAyB,CAAA;AAWzF;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA4DnD;AAeD,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAazD;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,GACV;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CASrE;AAyBD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,YAAY,CAAA;IAClB,MAAM,EAAE,cAAc,CAAA;IACtB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,aAAa,CAmF5E;AAED,oGAAoG;AACpG,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAM,EAIb,CAAA;AAEtC;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAwBvE"}
@@ -0,0 +1,263 @@
1
+ /** Enough rows for the guess to mean something without reading a whole spreadsheet twice. */
2
+ const SAMPLE = 500;
3
+ /** Above this a column of distinct strings is prose, not a set of choices. */
4
+ const MAX_OPTIONS = 40;
5
+ /** A choice longer than this is a sentence somebody typed, not a label. */
6
+ const MAX_OPTION_LENGTH = 60;
7
+ /**
8
+ * RFC 4180, with the two departures every real file needs.
9
+ *
10
+ * A quoted field may hold newlines and `""` for a literal quote — that part is the specification. The
11
+ * departures are that a lone `\r` or a `\r\n` both end a record (Excel writes one, Notion the other)
12
+ * and that a stray quote inside an unquoted field is a character rather than an error, because the
13
+ * alternative is refusing a file every other reader opens.
14
+ */
15
+ export function parseCsv(source) {
16
+ const rows = [];
17
+ let row = [];
18
+ let field = '';
19
+ let quoted = false;
20
+ let sawField = false;
21
+ const endField = () => {
22
+ row.push(field);
23
+ field = '';
24
+ sawField = false;
25
+ };
26
+ const endRow = () => {
27
+ endField();
28
+ rows.push(row);
29
+ row = [];
30
+ };
31
+ for (let i = 0; i < source.length; i++) {
32
+ const ch = source[i];
33
+ if (quoted) {
34
+ if (ch !== '"') {
35
+ field += ch;
36
+ continue;
37
+ }
38
+ if (source[i + 1] === '"') {
39
+ field += '"';
40
+ i++;
41
+ continue;
42
+ }
43
+ quoted = false;
44
+ continue;
45
+ }
46
+ if (ch === '"' && !sawField) {
47
+ quoted = true;
48
+ sawField = true;
49
+ continue;
50
+ }
51
+ if (ch === ',') {
52
+ endField();
53
+ continue;
54
+ }
55
+ if (ch === '\r') {
56
+ if (source[i + 1] === '\n')
57
+ i++;
58
+ endRow();
59
+ continue;
60
+ }
61
+ if (ch === '\n') {
62
+ endRow();
63
+ continue;
64
+ }
65
+ field += ch;
66
+ sawField = true;
67
+ }
68
+ // A file ending in a newline has already closed its last row; anything else is still open.
69
+ if (field.length > 0 || row.length > 0)
70
+ endRow();
71
+ // A trailing empty record is what a file ending in a newline leaves behind, and it is not a row.
72
+ while (rows.length > 0 && rows.at(-1).every((value) => value.trim() === ''))
73
+ rows.pop();
74
+ return rows;
75
+ }
76
+ const TRUE_WORDS = new Set(['yes', 'true', 'checked', 'done', 'y', '✓', '✔']);
77
+ const FALSE_WORDS = new Set(['no', 'false', 'unchecked', 'n', '✗', '✘']);
78
+ const RE_NUMBER = /^-?\d{1,3}(?:,\d{3})*(?:\.\d+)?$|^-?\d*\.?\d+$/;
79
+ const RE_CURRENCY = /^\s*([$£€¥₹﷼])\s*/;
80
+ const RE_EMAIL = /^[^\s@]+@[^\s@.]+\.[^\s@]+$/;
81
+ const RE_ISO_DATE = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
82
+ const RE_LONG_DATE = /^[A-Za-z]{3,9}\s+\d{1,2},?\s+\d{4}(?:\s+\d{1,2}:\d{2}\s*(?:[AaPp][Mm])?)?$/;
83
+ const RE_SLASH_DATE = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
84
+ /** A value nobody filled in. Notion writes an empty cell; a spreadsheet sometimes writes a dash. */
85
+ const isEmpty = (value) => value.trim() === '' || value.trim() === '-';
86
+ /** `2026-01-05`, `January 5, 2026`, `05/01/2026` — as the ISO string a date cell stores. */
87
+ export function parseDateValue(raw) {
88
+ const value = raw.trim();
89
+ if (RE_ISO_DATE.test(value)) {
90
+ const at = new Date(value.length === 10 ? `${value}T00:00:00Z` : value.replace(' ', 'T'));
91
+ return Number.isNaN(at.getTime()) ? null : value.length === 10 ? value : at.toISOString();
92
+ }
93
+ if (RE_LONG_DATE.test(value) || RE_SLASH_DATE.test(value)) {
94
+ const at = new Date(value);
95
+ if (Number.isNaN(at.getTime()))
96
+ return null;
97
+ // Date-only input has no time to keep, and a `date` cell draws the first ten characters.
98
+ return /\d{1,2}:\d{2}/.test(value) ? at.toISOString() : at.toISOString().slice(0, 10);
99
+ }
100
+ return null;
101
+ }
102
+ /** `1,234.50`, `$99`, `12%` — as the number a numeric sort and a rollup can use. */
103
+ export function parseNumberValue(raw) {
104
+ let value = raw.trim();
105
+ const currency = RE_CURRENCY.exec(value)?.[1] ?? null;
106
+ if (currency)
107
+ value = value.replace(RE_CURRENCY, '');
108
+ const percent = value.endsWith('%');
109
+ if (percent)
110
+ value = value.slice(0, -1).trim();
111
+ if (!RE_NUMBER.test(value))
112
+ return null;
113
+ const parsed = Number(value.replaceAll(',', ''));
114
+ return Number.isFinite(parsed) ? { value: parsed, percent, currency } : null;
115
+ }
116
+ /** An option id derived from its label, so the same choice keeps the same id across two imports. */
117
+ function optionId(label, taken) {
118
+ const base = label
119
+ .toLowerCase()
120
+ .normalize('NFKD')
121
+ .replace(/[^\p{Letter}\p{Number}]+/gu, '-')
122
+ .replace(/^-+|-+$/g, '')
123
+ .slice(0, 48) || 'option';
124
+ let id = base;
125
+ for (let n = 2; taken.has(id); n++)
126
+ id = `${base}-${n}`;
127
+ taken.add(id);
128
+ return id;
129
+ }
130
+ /**
131
+ * The palette a select column's choices are coloured from.
132
+ *
133
+ * Cycled rather than random: two imports of the same file produce the same colours, and a person
134
+ * re-running an import after fixing one row does not find their board repainted.
135
+ */
136
+ const OPTION_COLOURS = ['slate', 'accent', 'success', 'warning', 'danger', 'info'];
137
+ /**
138
+ * One column's type, from its values.
139
+ *
140
+ * The order is the whole algorithm: checkbox before number (`1` and `0` are both), number before
141
+ * date (a bare year is a number), url and email before select (a column of six addresses is not six
142
+ * choices), multi-select before select (a comma is the only thing that separates them), and select
143
+ * before text (the fallback that is always true).
144
+ */
145
+ export function guessColumn(name, rawValues) {
146
+ const values = rawValues.slice(0, SAMPLE).filter((value) => !isEmpty(value));
147
+ const total = values.length;
148
+ const from = `from ${total} of ${rawValues.length} value${rawValues.length === 1 ? '' : 's'}`;
149
+ const plain = (type, what, config = {}) => ({
150
+ name,
151
+ type,
152
+ config,
153
+ note: `${name}: read as ${what}, ${from}`,
154
+ });
155
+ if (total === 0)
156
+ return {
157
+ name,
158
+ type: 'text',
159
+ config: {},
160
+ note: `${name}: read as text — the column has no values to judge`,
161
+ };
162
+ const lower = values.map((value) => value.trim().toLowerCase());
163
+ if (lower.every((value) => TRUE_WORDS.has(value) || FALSE_WORDS.has(value)))
164
+ return plain('checkbox', 'a checkbox');
165
+ const numbers = values.map(parseNumberValue);
166
+ if (numbers.every((n) => n !== null)) {
167
+ const percent = numbers.every((n) => n.percent);
168
+ const currency = numbers.find((n) => n.currency)?.currency ?? null;
169
+ const decimals = values.map((value) => (value.split('.')[1] ?? '').replace(/[^\d]/g, '').length);
170
+ const config = {
171
+ format: percent ? 'percent' : currency ? 'currency' : 'plain',
172
+ precision: Math.min(8, Math.max(...decimals, 0)),
173
+ };
174
+ return plain('number', percent ? 'a percentage' : currency ? 'an amount of money' : 'a number', config);
175
+ }
176
+ if (values.every((value) => parseDateValue(value) !== null)) {
177
+ const withTime = values.some((value) => /\d{1,2}:\d{2}/.test(value));
178
+ return plain('date', withTime ? 'a date and time' : 'a date', { includeTime: withTime });
179
+ }
180
+ if (values.every((value) => /^https?:\/\/\S+$/i.test(value.trim())))
181
+ return plain('url', 'a link');
182
+ if (values.every((value) => RE_EMAIL.test(value.trim())))
183
+ return plain('email', 'an email address');
184
+ const options = (labels) => {
185
+ const taken = new Set();
186
+ const ids = new Map();
187
+ const out = [];
188
+ labels.forEach((label, index) => {
189
+ const id = optionId(label, taken);
190
+ ids.set(label, id);
191
+ out.push({ id, label, colour: OPTION_COLOURS[index % OPTION_COLOURS.length] });
192
+ });
193
+ return { options: out, ids };
194
+ };
195
+ // A multi-select is a select whose cells hold more than one choice, so it is only worth guessing
196
+ // when at least one cell actually does — otherwise every select with a comma in one label becomes
197
+ // one, and the column silently stops matching an `equals` filter.
198
+ if (values.some((value) => value.includes(','))) {
199
+ const parts = [...new Set(values.flatMap((value) => splitChoices(value)))];
200
+ if (parts.length > 0 && parts.length <= MAX_OPTIONS && parts.every((p) => p.length <= MAX_OPTION_LENGTH))
201
+ return {
202
+ name,
203
+ type: 'multi_select',
204
+ config: { options: options(parts).options },
205
+ note: `${name}: read as a multi-select with ${parts.length} choices, ${from}`,
206
+ };
207
+ }
208
+ const distinct = [...new Set(values.map((value) => value.trim()))];
209
+ if (distinct.length <= MAX_OPTIONS &&
210
+ distinct.length < total &&
211
+ distinct.every((value) => value.length <= MAX_OPTION_LENGTH))
212
+ return {
213
+ name,
214
+ type: 'select',
215
+ config: { options: options(distinct).options },
216
+ note: `${name}: read as a select with ${distinct.length} choices, ${from}`,
217
+ };
218
+ return plain('text', 'text');
219
+ }
220
+ /** `Design, Urgent` → `['Design', 'Urgent']`, which is how every exporter writes a multi-select. */
221
+ export const splitChoices = (value) => value
222
+ .split(',')
223
+ .map((part) => part.trim())
224
+ .filter((part) => part.length > 0);
225
+ /**
226
+ * One cell, as the value the column's type stores.
227
+ *
228
+ * Null means "leave the key out of `props`" rather than "write null": an absent key is what every
229
+ * other writer in this module produces for an empty cell, and a null would make a `not_contains`
230
+ * filter behave differently for an imported row than for one somebody typed.
231
+ *
232
+ * `select` stores a **scalar** option id and `multi_select` an **array** of them, and the difference
233
+ * is load-bearing rather than cosmetic: `query.ts` compares a select as text through `props->>'key'`,
234
+ * so an array there stringifies to `["done"]` and matches no filter anybody can write. The cell
235
+ * component reads either shape, which is exactly why this is easy to get wrong and invisible on screen.
236
+ */
237
+ export function coerceValue(column, raw) {
238
+ if (isEmpty(raw))
239
+ return null;
240
+ const value = raw.trim();
241
+ switch (column.type) {
242
+ case 'checkbox':
243
+ return TRUE_WORDS.has(value.toLowerCase());
244
+ case 'number':
245
+ return parseNumberValue(value)?.value ?? null;
246
+ case 'date':
247
+ return parseDateValue(value);
248
+ case 'select': {
249
+ const option = (column.config.options ?? []).find((o) => o.label === value);
250
+ return option ? option.id : null;
251
+ }
252
+ case 'multi_select': {
253
+ const wanted = splitChoices(value);
254
+ const ids = wanted
255
+ .map((label) => (column.config.options ?? []).find((o) => o.label === label)?.id)
256
+ .filter((id) => id !== undefined);
257
+ return ids.length > 0 ? ids : null;
258
+ }
259
+ default:
260
+ return value;
261
+ }
262
+ }
263
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.js","sourceRoot":"","sources":["../../../src/server/import/csv.ts"],"names":[],"mappings":"AAuBA,6FAA6F;AAC7F,MAAM,MAAM,GAAG,GAAG,CAAA;AAElB,8EAA8E;AAC9E,MAAM,WAAW,GAAG,EAAE,CAAA;AAEtB,2EAA2E;AAC3E,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAE5B;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,MAAM,IAAI,GAAe,EAAE,CAAA;IAC3B,IAAI,GAAG,GAAa,EAAE,CAAA;IACtB,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,IAAI,QAAQ,GAAG,KAAK,CAAA;IAEpB,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACf,KAAK,GAAG,EAAE,CAAA;QACV,QAAQ,GAAG,KAAK,CAAA;IAClB,CAAC,CAAA;IACD,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,QAAQ,EAAE,CAAA;QACV,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACd,GAAG,GAAG,EAAE,CAAA;IACV,CAAC,CAAA;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;QACrB,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACf,KAAK,IAAI,EAAE,CAAA;gBACX,SAAQ;YACV,CAAC;YACD,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC1B,KAAK,IAAI,GAAG,CAAA;gBACZ,CAAC,EAAE,CAAA;gBACH,SAAQ;YACV,CAAC;YACD,MAAM,GAAG,KAAK,CAAA;YACd,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,MAAM,GAAG,IAAI,CAAA;YACb,QAAQ,GAAG,IAAI,CAAA;YACf,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,QAAQ,EAAE,CAAA;YACV,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAA;YAC/B,MAAM,EAAE,CAAA;YACR,SAAQ;QACV,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,EAAE,CAAA;YACR,SAAQ;QACV,CAAC;QACD,KAAK,IAAI,EAAE,CAAA;QACX,QAAQ,GAAG,IAAI,CAAA;IACjB,CAAC;IACD,2FAA2F;IAC3F,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,EAAE,CAAA;IAEhD,iGAAiG;IACjG,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAAE,IAAI,CAAC,GAAG,EAAE,CAAA;IACxF,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAC7E,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;AAExE,MAAM,SAAS,GAAG,gDAAgD,CAAA;AAClE,MAAM,WAAW,GAAG,mBAAmB,CAAA;AACvC,MAAM,QAAQ,GAAG,6BAA6B,CAAA;AAC9C,MAAM,WAAW,GAAG,qFAAqF,CAAA;AACzG,MAAM,YAAY,GAAG,4EAA4E,CAAA;AACjG,MAAM,aAAa,GAAG,2BAA2B,CAAA;AAEjD,oGAAoG;AACpG,MAAM,OAAO,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,CAAA;AAEvF,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACxB,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACzF,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAA;IAC3F,CAAC;IACD,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAA;QAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,IAAI,CAAA;QAC3C,yFAAyF;QACzF,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACvF,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAC9B,GAAW;IAEX,IAAI,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACtB,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;IACrD,IAAI,QAAQ;QAAE,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;IACnC,IAAI,OAAO;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9E,CAAC;AAED,oGAAoG;AACpG,SAAS,QAAQ,CAAC,KAAa,EAAE,KAAkB;IACjD,MAAM,IAAI,GACR,KAAK;SACF,WAAW,EAAE;SACb,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,4BAA4B,EAAE,GAAG,CAAC;SAC1C,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAA;IAC7B,IAAI,EAAE,GAAG,IAAI,CAAA;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;QAAE,EAAE,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAA;IACvD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACb,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;;GAKG;AACH,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;AAgBlF;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,SAAmB;IAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAA;IAC3B,MAAM,IAAI,GAAG,QAAQ,KAAK,OAAO,SAAS,CAAC,MAAM,SAAS,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IAC7F,MAAM,KAAK,GAAG,CAAC,IAAkB,EAAE,IAAY,EAAE,SAAyB,EAAE,EAAiB,EAAE,CAAC,CAAC;QAC/F,IAAI;QACJ,IAAI;QACJ,MAAM;QACN,IAAI,EAAE,GAAG,IAAI,aAAa,IAAI,KAAK,IAAI,EAAE;KAC1C,CAAC,CAAA;IAEF,IAAI,KAAK,KAAK,CAAC;QACb,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,GAAG,IAAI,oDAAoD;SAClE,CAAA;IAEH,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;IAC/D,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;IAExC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,CAAC,OAAO,CAAC,CAAA;QAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,CAAC,QAAQ,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAA;QACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;QAChG,MAAM,MAAM,GAAmB;YAC7B,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO;YAC7D,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC;SACjD,CAAA;QACD,OAAO,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IACzG,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QAC5D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACpE,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC1F,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAClG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAA;IAEnG,MAAM,OAAO,GAAG,CAAC,MAAgB,EAAyD,EAAE;QAC1F,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAA;QACrC,MAAM,GAAG,GAAmB,EAAE,CAAA;QAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YACjC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;YAClB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,MAAM,CAAE,EAAE,CAAC,CAAA;QACjF,CAAC,CAAC,CAAA;QACF,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;IAC9B,CAAC,CAAA;IAED,iGAAiG;IACjG,kGAAkG;IAClG,kEAAkE;IAClE,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,iBAAiB,CAAC;YACtG,OAAO;gBACL,IAAI;gBACJ,IAAI,EAAE,cAAc;gBACpB,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE;gBAC3C,IAAI,EAAE,GAAG,IAAI,iCAAiC,KAAK,CAAC,MAAM,aAAa,IAAI,EAAE;aAC9E,CAAA;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAClE,IACE,QAAQ,CAAC,MAAM,IAAI,WAAW;QAC9B,QAAQ,CAAC,MAAM,GAAG,KAAK;QACvB,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,iBAAiB,CAAC;QAE5D,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;YAC9C,IAAI,EAAE,GAAG,IAAI,2BAA2B,QAAQ,CAAC,MAAM,aAAa,IAAI,EAAE;SAC3E,CAAA;IAEH,OAAO,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAC9B,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAY,EAAE,CACtD,KAAK;KACF,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;KAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;AAEtC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,GAAW;IAC5D,IAAI,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACxB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU;YACb,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;QAC5C,KAAK,QAAQ;YACX,OAAO,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,IAAI,IAAI,CAAA;QAC/C,KAAK,MAAM;YACT,OAAO,cAAc,CAAC,KAAK,CAAC,CAAA;QAC9B,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;YAC3E,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAClC,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;YAClC,MAAM,GAAG,GAAG,MAAM;iBACf,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;iBAChF,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAA;YACjD,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QACpC,CAAC;QACD;YACE,OAAO,KAAK,CAAA;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * HTML to a page document, for the one source that has no Markdown: a Confluence space export.
3
+ *
4
+ * Confluence's "Export space → HTML" writes one already-rendered `.html` per page, an `index.html`
5
+ * listing them, and an `attachments/` tree. There is no storage format to read — the wiki markup and
6
+ * the `<ac:…>` macros are gone by the time the export is written — so the rendered HTML *is* the
7
+ * document, and reading it is the only route in that does not ask a customer to install a plugin.
8
+ *
9
+ * **A tag soup parser, deliberately, and not a DOM.** No `jsdom`, no `parse5`: this reads one
10
+ * bounded, machine-written dialect, and the whole job is a tokeniser plus a stack that closes
11
+ * implied tags. Nothing here is executed, nothing is fetched, and no attribute reaches a page
12
+ * unfiltered — a `src` and an `href` come out as raw strings for `plan.ts` to resolve and everything
13
+ * else is dropped, so a `<script>`, an `onclick` or a `javascript:` href has nowhere to land. That is
14
+ * the property that matters, because the input is a file somebody uploaded.
15
+ *
16
+ * Confluence's own furniture is recognised where it maps onto something Quire has — an information
17
+ * macro is a callout, a `syntaxhighlighter` block is a code block, an inline task list is a task list
18
+ * — and ignored where it does not. What it cannot map, it keeps as prose rather than dropping, which
19
+ * is the same rule the Markdown reader follows and for the same reason.
20
+ */
21
+ import type { PageDoc } from '@kernhq/ui/editor/page-doc';
22
+ export interface HtmlElement {
23
+ tag: string;
24
+ attrs: Record<string, string>;
25
+ children: HtmlNode[];
26
+ }
27
+ export type HtmlNode = HtmlElement | {
28
+ text: string;
29
+ };
30
+ /** `&amp;`, `&#8212;`, `&#x2014;` — everything else is left exactly as it was written. */
31
+ export declare function decodeEntities(value: string): string;
32
+ /**
33
+ * A document as a tree.
34
+ *
35
+ * Comments, doctypes and processing instructions are skipped rather than kept: none of them can
36
+ * become a node, and a comment holding `<div>` would otherwise open one.
37
+ */
38
+ export declare function parseHtml(source: string): HtmlElement;
39
+ /** The first element matching a predicate, depth first. */
40
+ export declare function findElement(node: HtmlNode, match: (el: HtmlElement) => boolean): HtmlElement | null;
41
+ /** Every class on an element, lower-cased — Confluence's markup is identified entirely by class. */
42
+ export declare const classesOf: (el: HtmlElement) => string[];
43
+ /** All the text under a node, with runs of whitespace collapsed. What a title needs and no more. */
44
+ export declare function textContent(node: HtmlNode): string;
45
+ /**
46
+ * An HTML fragment as a page document.
47
+ *
48
+ * Always a `doc`: an element holding nothing is an empty page, which is a correct reading of an empty
49
+ * page rather than a failure.
50
+ */
51
+ export declare function htmlToPageDoc(node: HtmlNode): PageDoc;
52
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/server/import/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,OAAO,EAA4B,MAAM,4BAA4B,CAAA;AAMnF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,QAAQ,EAAE,CAAA;CACrB;AACD,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AA6ErD,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAapD;AAaD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAyErD;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,GAAG,WAAW,GAAG,IAAI,CAQnG;AAED,oGAAoG;AACpG,eAAO,MAAM,SAAS,GAAI,IAAI,WAAW,KAAG,MAAM,EAAuD,CAAA;AAEzG,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAGlD;AAkSD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAErD"}