smartrte-core 0.2.0 → 1.0.0-beta.1

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 (329) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +23 -0
  3. package/dist/command.d.ts +8 -8
  4. package/dist/editor.d.ts +32 -0
  5. package/dist/editor.js +121 -0
  6. package/dist/foundation/annotations/index.d.ts +2 -0
  7. package/dist/foundation/annotations/index.js +2 -0
  8. package/dist/foundation/annotations/range.d.ts +54 -0
  9. package/dist/foundation/annotations/range.js +158 -0
  10. package/dist/foundation/annotations/types.d.ts +33 -0
  11. package/dist/foundation/annotations/types.js +1 -0
  12. package/dist/foundation/atom/commands.d.ts +11 -0
  13. package/dist/foundation/atom/commands.js +118 -0
  14. package/dist/foundation/atom/composition.d.ts +8 -0
  15. package/dist/foundation/atom/composition.js +22 -0
  16. package/dist/foundation/atom/declarations.d.ts +2 -0
  17. package/dist/foundation/atom/declarations.js +17 -0
  18. package/dist/foundation/atom/formats.d.ts +21 -0
  19. package/dist/foundation/atom/formats.js +77 -0
  20. package/dist/foundation/atom/index.d.ts +10 -0
  21. package/dist/foundation/atom/index.js +10 -0
  22. package/dist/foundation/atom/lifecycle.d.ts +12 -0
  23. package/dist/foundation/atom/lifecycle.js +41 -0
  24. package/dist/foundation/atom/persistence.d.ts +3 -0
  25. package/dist/foundation/atom/persistence.js +20 -0
  26. package/dist/foundation/atom/plugin.d.ts +20 -0
  27. package/dist/foundation/atom/plugin.js +55 -0
  28. package/dist/foundation/atom/schema.d.ts +3 -0
  29. package/dist/foundation/atom/schema.js +29 -0
  30. package/dist/foundation/atom/security.d.ts +10 -0
  31. package/dist/foundation/atom/security.js +20 -0
  32. package/dist/foundation/atom/selection.d.ts +3 -0
  33. package/dist/foundation/atom/selection.js +4 -0
  34. package/dist/foundation/atom/types.d.ts +47 -0
  35. package/dist/foundation/atom/types.js +1 -0
  36. package/dist/foundation/block/commands.d.ts +17 -0
  37. package/dist/foundation/block/commands.js +197 -0
  38. package/dist/foundation/block/formats.d.ts +33 -0
  39. package/dist/foundation/block/formats.js +57 -0
  40. package/dist/foundation/block/index.d.ts +5 -0
  41. package/dist/foundation/block/index.js +5 -0
  42. package/dist/foundation/block/input.d.ts +19 -0
  43. package/dist/foundation/block/input.js +67 -0
  44. package/dist/foundation/block/plugin.d.ts +26 -0
  45. package/dist/foundation/block/plugin.js +79 -0
  46. package/dist/foundation/block/schema.d.ts +49 -0
  47. package/dist/foundation/block/schema.js +27 -0
  48. package/dist/foundation/block/types.d.ts +40 -0
  49. package/dist/foundation/block/types.js +1 -0
  50. package/dist/foundation/boundaries.d.ts +11 -0
  51. package/dist/foundation/boundaries.js +117 -0
  52. package/dist/foundation/clipboard/detection.d.ts +3 -0
  53. package/dist/foundation/clipboard/detection.js +29 -0
  54. package/dist/foundation/clipboard/diagnostics.d.ts +24 -0
  55. package/dist/foundation/clipboard/diagnostics.js +59 -0
  56. package/dist/foundation/clipboard/index.d.ts +8 -0
  57. package/dist/foundation/clipboard/index.js +8 -0
  58. package/dist/foundation/clipboard/insertion.d.ts +22 -0
  59. package/dist/foundation/clipboard/insertion.js +210 -0
  60. package/dist/foundation/clipboard/normalizers.d.ts +8 -0
  61. package/dist/foundation/clipboard/normalizers.js +229 -0
  62. package/dist/foundation/clipboard/pipeline.d.ts +13 -0
  63. package/dist/foundation/clipboard/pipeline.js +58 -0
  64. package/dist/foundation/clipboard/sanitizer.d.ts +3 -0
  65. package/dist/foundation/clipboard/sanitizer.js +58 -0
  66. package/dist/foundation/clipboard/serialization.d.ts +5 -0
  67. package/dist/foundation/clipboard/serialization.js +33 -0
  68. package/dist/foundation/clipboard/transfer.d.ts +11 -0
  69. package/dist/foundation/clipboard/transfer.js +108 -0
  70. package/dist/foundation/clipboard/types.d.ts +65 -0
  71. package/dist/foundation/clipboard/types.js +1 -0
  72. package/dist/foundation/collab/index.d.ts +3 -0
  73. package/dist/foundation/collab/index.js +3 -0
  74. package/dist/foundation/collab/rebase.d.ts +28 -0
  75. package/dist/foundation/collab/rebase.js +82 -0
  76. package/dist/foundation/collab/transform.d.ts +73 -0
  77. package/dist/foundation/collab/transform.js +251 -0
  78. package/dist/foundation/collab/types.d.ts +52 -0
  79. package/dist/foundation/collab/types.js +1 -0
  80. package/dist/foundation/comments/fromSelection.d.ts +16 -0
  81. package/dist/foundation/comments/fromSelection.js +24 -0
  82. package/dist/foundation/comments/index.d.ts +3 -0
  83. package/dist/foundation/comments/index.js +3 -0
  84. package/dist/foundation/comments/rebase.d.ts +31 -0
  85. package/dist/foundation/comments/rebase.js +43 -0
  86. package/dist/foundation/comments/types.d.ts +37 -0
  87. package/dist/foundation/comments/types.js +1 -0
  88. package/dist/foundation/diff/documentDiff.d.ts +12 -0
  89. package/dist/foundation/diff/documentDiff.js +148 -0
  90. package/dist/foundation/diff/index.d.ts +3 -0
  91. package/dist/foundation/diff/index.js +3 -0
  92. package/dist/foundation/diff/textDiff.d.ts +12 -0
  93. package/dist/foundation/diff/textDiff.js +114 -0
  94. package/dist/foundation/diff/types.d.ts +74 -0
  95. package/dist/foundation/diff/types.js +1 -0
  96. package/dist/foundation/editor.d.ts +138 -0
  97. package/dist/foundation/editor.js +398 -0
  98. package/dist/foundation/formats/codec.d.ts +45 -0
  99. package/dist/foundation/formats/codec.js +1 -0
  100. package/dist/foundation/formats/docx/export.d.ts +4 -0
  101. package/dist/foundation/formats/docx/export.js +285 -0
  102. package/dist/foundation/formats/docx/import.d.ts +9 -0
  103. package/dist/foundation/formats/docx/import.js +37 -0
  104. package/dist/foundation/formats/docx/nestingGuard.d.ts +40 -0
  105. package/dist/foundation/formats/docx/nestingGuard.js +66 -0
  106. package/dist/foundation/formats/docx/portableAtoms.d.ts +4 -0
  107. package/dist/foundation/formats/docx/portableAtoms.js +66 -0
  108. package/dist/foundation/formats/docx/styledImport.d.ts +10 -0
  109. package/dist/foundation/formats/docx/styledImport.js +205 -0
  110. package/dist/foundation/formats/featureCodecs.d.ts +37 -0
  111. package/dist/foundation/formats/featureCodecs.js +69 -0
  112. package/dist/foundation/formats/fidelity.d.ts +31 -0
  113. package/dist/foundation/formats/fidelity.js +123 -0
  114. package/dist/foundation/formats/index.d.ts +7 -0
  115. package/dist/foundation/formats/index.js +7 -0
  116. package/dist/foundation/formats/pdf/format.d.ts +29 -0
  117. package/dist/foundation/formats/pdf/format.js +190 -0
  118. package/dist/foundation/history.d.ts +20 -0
  119. package/dist/foundation/history.js +121 -0
  120. package/dist/foundation/identity.d.ts +8 -0
  121. package/dist/foundation/identity.js +30 -0
  122. package/dist/foundation/index.d.ts +30 -0
  123. package/dist/foundation/index.js +30 -0
  124. package/dist/foundation/list/commands.d.ts +28 -0
  125. package/dist/foundation/list/commands.js +422 -0
  126. package/dist/foundation/list/formats.d.ts +20 -0
  127. package/dist/foundation/list/formats.js +795 -0
  128. package/dist/foundation/list/index.d.ts +8 -0
  129. package/dist/foundation/list/index.js +8 -0
  130. package/dist/foundation/list/input.d.ts +35 -0
  131. package/dist/foundation/list/input.js +320 -0
  132. package/dist/foundation/list/plugin.d.ts +21 -0
  133. package/dist/foundation/list/plugin.js +71 -0
  134. package/dist/foundation/list/presets.d.ts +16 -0
  135. package/dist/foundation/list/presets.js +21 -0
  136. package/dist/foundation/list/schema.d.ts +35 -0
  137. package/dist/foundation/list/schema.js +29 -0
  138. package/dist/foundation/list/shadow.d.ts +54 -0
  139. package/dist/foundation/list/shadow.js +75 -0
  140. package/dist/foundation/list/shadowCorpus.d.ts +10 -0
  141. package/dist/foundation/list/shadowCorpus.js +41 -0
  142. package/dist/foundation/list/types.d.ts +50 -0
  143. package/dist/foundation/list/types.js +1 -0
  144. package/dist/foundation/mapping.d.ts +11 -0
  145. package/dist/foundation/mapping.js +40 -0
  146. package/dist/foundation/marks/canonical.d.ts +9 -0
  147. package/dist/foundation/marks/canonical.js +72 -0
  148. package/dist/foundation/marks/commands.d.ts +21 -0
  149. package/dist/foundation/marks/commands.js +136 -0
  150. package/dist/foundation/marks/dom.d.ts +2 -0
  151. package/dist/foundation/marks/dom.js +46 -0
  152. package/dist/foundation/marks/editor.d.ts +8 -0
  153. package/dist/foundation/marks/editor.js +50 -0
  154. package/dist/foundation/marks/formats.d.ts +37 -0
  155. package/dist/foundation/marks/formats.js +49 -0
  156. package/dist/foundation/marks/hardBreak.d.ts +8 -0
  157. package/dist/foundation/marks/hardBreak.js +80 -0
  158. package/dist/foundation/marks/index.d.ts +11 -0
  159. package/dist/foundation/marks/index.js +11 -0
  160. package/dist/foundation/marks/normalization.d.ts +2 -0
  161. package/dist/foundation/marks/normalization.js +49 -0
  162. package/dist/foundation/marks/plugin.d.ts +14 -0
  163. package/dist/foundation/marks/plugin.js +63 -0
  164. package/dist/foundation/marks/resolveMarkRun.d.ts +8 -0
  165. package/dist/foundation/marks/resolveMarkRun.js +47 -0
  166. package/dist/foundation/marks/schema.d.ts +167 -0
  167. package/dist/foundation/marks/schema.js +50 -0
  168. package/dist/foundation/marks/stored.d.ts +2 -0
  169. package/dist/foundation/marks/stored.js +31 -0
  170. package/dist/foundation/marks/types.d.ts +40 -0
  171. package/dist/foundation/marks/types.js +1 -0
  172. package/dist/foundation/modelDom.d.ts +38 -0
  173. package/dist/foundation/modelDom.js +327 -0
  174. package/dist/foundation/normalization.d.ts +22 -0
  175. package/dist/foundation/normalization.js +132 -0
  176. package/dist/foundation/operations.d.ts +28 -0
  177. package/dist/foundation/operations.js +568 -0
  178. package/dist/foundation/plugin/builtins.d.ts +10 -0
  179. package/dist/foundation/plugin/builtins.js +27 -0
  180. package/dist/foundation/plugin/dispatch.d.ts +33 -0
  181. package/dist/foundation/plugin/dispatch.js +34 -0
  182. package/dist/foundation/plugin/index.d.ts +4 -0
  183. package/dist/foundation/plugin/index.js +4 -0
  184. package/dist/foundation/plugin/registry.d.ts +36 -0
  185. package/dist/foundation/plugin/registry.js +115 -0
  186. package/dist/foundation/plugin/types.d.ts +141 -0
  187. package/dist/foundation/plugin/types.js +13 -0
  188. package/dist/foundation/positions.d.ts +20 -0
  189. package/dist/foundation/positions.js +121 -0
  190. package/dist/foundation/schema.d.ts +57 -0
  191. package/dist/foundation/schema.js +371 -0
  192. package/dist/foundation/schemaBuilder.d.ts +41 -0
  193. package/dist/foundation/schemaBuilder.js +96 -0
  194. package/dist/foundation/scope/index.d.ts +2 -0
  195. package/dist/foundation/scope/index.js +2 -0
  196. package/dist/foundation/scope/resolveScope.d.ts +13 -0
  197. package/dist/foundation/scope/resolveScope.js +860 -0
  198. package/dist/foundation/scope/types.d.ts +122 -0
  199. package/dist/foundation/scope/types.js +1 -0
  200. package/dist/foundation/security/urlPolicy.d.ts +21 -0
  201. package/dist/foundation/security/urlPolicy.js +75 -0
  202. package/dist/foundation/structural/contentTarget.d.ts +21 -0
  203. package/dist/foundation/structural/contentTarget.js +51 -0
  204. package/dist/foundation/structural/index.d.ts +2 -0
  205. package/dist/foundation/structural/index.js +2 -0
  206. package/dist/foundation/structural/move.d.ts +7 -0
  207. package/dist/foundation/structural/move.js +35 -0
  208. package/dist/foundation/suggestions/index.d.ts +3 -0
  209. package/dist/foundation/suggestions/index.js +3 -0
  210. package/dist/foundation/suggestions/inline.d.ts +64 -0
  211. package/dist/foundation/suggestions/inline.js +116 -0
  212. package/dist/foundation/suggestions/structural.d.ts +40 -0
  213. package/dist/foundation/suggestions/structural.js +47 -0
  214. package/dist/foundation/suggestions/types.d.ts +19 -0
  215. package/dist/foundation/suggestions/types.js +1 -0
  216. package/dist/foundation/surface/index.d.ts +3 -0
  217. package/dist/foundation/surface/index.js +3 -0
  218. package/dist/foundation/surface/input.d.ts +127 -0
  219. package/dist/foundation/surface/input.js +1761 -0
  220. package/dist/foundation/surface/renderer.d.ts +84 -0
  221. package/dist/foundation/surface/renderer.js +974 -0
  222. package/dist/foundation/surface/types.d.ts +48 -0
  223. package/dist/foundation/surface/types.js +1 -0
  224. package/dist/foundation/table/commands.d.ts +31 -0
  225. package/dist/foundation/table/commands.js +470 -0
  226. package/dist/foundation/table/formats.d.ts +26 -0
  227. package/dist/foundation/table/formats.js +40 -0
  228. package/dist/foundation/table/grid.d.ts +8 -0
  229. package/dist/foundation/table/grid.js +178 -0
  230. package/dist/foundation/table/index.d.ts +6 -0
  231. package/dist/foundation/table/index.js +6 -0
  232. package/dist/foundation/table/plugin.d.ts +23 -0
  233. package/dist/foundation/table/plugin.js +119 -0
  234. package/dist/foundation/table/schema.d.ts +2 -0
  235. package/dist/foundation/table/schema.js +38 -0
  236. package/dist/foundation/table/selection.d.ts +7 -0
  237. package/dist/foundation/table/selection.js +35 -0
  238. package/dist/foundation/table/types.d.ts +109 -0
  239. package/dist/foundation/table/types.js +1 -0
  240. package/dist/foundation/transactions.d.ts +41 -0
  241. package/dist/foundation/transactions.js +90 -0
  242. package/dist/foundation/types.d.ts +261 -0
  243. package/dist/foundation/types.js +1 -0
  244. package/dist/foundation/versioning/diffVersions.d.ts +5 -0
  245. package/dist/foundation/versioning/diffVersions.js +3 -0
  246. package/dist/foundation/versioning/index.d.ts +2 -0
  247. package/dist/foundation/versioning/index.js +2 -0
  248. package/dist/foundation/versioning/types.d.ts +17 -0
  249. package/dist/foundation/versioning/types.js +1 -0
  250. package/dist/history.d.ts +20 -0
  251. package/dist/history.js +165 -0
  252. package/dist/html/compatibility.js +7 -1
  253. package/dist/index.d.ts +1 -14
  254. package/dist/index.js +9 -14
  255. package/dist/legacy/index.d.ts +45 -0
  256. package/dist/legacy/index.js +45 -0
  257. package/dist/legacyCommands/alignment.d.ts +20 -0
  258. package/dist/{commands → legacyCommands}/alignment.js +17 -0
  259. package/dist/legacyCommands/blocks.d.ts +14 -0
  260. package/dist/legacyCommands/blocks.js +105 -0
  261. package/dist/legacyCommands/checklist.d.ts +11 -0
  262. package/dist/legacyCommands/checklist.js +45 -0
  263. package/dist/legacyCommands/code.d.ts +15 -0
  264. package/dist/{commands → legacyCommands}/code.js +25 -1
  265. package/dist/legacyCommands/formula.d.ts +7 -0
  266. package/dist/legacyCommands/formula.js +67 -0
  267. package/dist/legacyCommands/inlineAtoms.d.ts +7 -0
  268. package/dist/legacyCommands/inlineAtoms.js +58 -0
  269. package/dist/legacyCommands/list.d.ts +38 -0
  270. package/dist/legacyCommands/list.js +477 -0
  271. package/dist/{commands → legacyCommands}/marks.d.ts +3 -0
  272. package/dist/{commands → legacyCommands}/marks.js +45 -11
  273. package/dist/legacyCommands/media.d.ts +31 -0
  274. package/dist/legacyCommands/media.js +205 -0
  275. package/dist/legacyCommands/move.d.ts +14 -0
  276. package/dist/legacyCommands/move.js +91 -0
  277. package/dist/legacyCommands/table.d.ts +91 -0
  278. package/dist/legacyCommands/table.js +547 -0
  279. package/dist/listPresets.d.ts +12 -0
  280. package/dist/listPresets.js +6 -0
  281. package/dist/listScope.d.ts +27 -0
  282. package/dist/listScope.js +104 -0
  283. package/dist/markdown/compatibility.d.ts +2 -0
  284. package/dist/markdown/compatibility.js +94 -2
  285. package/dist/marks.d.ts +5 -5
  286. package/dist/model.d.ts +65 -9
  287. package/dist/plugin.d.ts +15 -0
  288. package/dist/plugin.js +31 -0
  289. package/dist/plugins/alignment.d.ts +3 -0
  290. package/dist/plugins/alignment.js +6 -0
  291. package/dist/plugins/basicFormatting.d.ts +9 -0
  292. package/dist/plugins/basicFormatting.js +24 -0
  293. package/dist/plugins/blockType.d.ts +3 -0
  294. package/dist/plugins/blockType.js +6 -0
  295. package/dist/plugins/blockquote.d.ts +3 -0
  296. package/dist/plugins/blockquote.js +6 -0
  297. package/dist/plugins/checklist.d.ts +3 -0
  298. package/dist/plugins/checklist.js +10 -0
  299. package/dist/plugins/codeBlock.d.ts +3 -0
  300. package/dist/plugins/codeBlock.js +6 -0
  301. package/dist/plugins/formula.d.ts +3 -0
  302. package/dist/plugins/formula.js +9 -0
  303. package/dist/plugins/list.d.ts +3 -0
  304. package/dist/plugins/list.js +10 -0
  305. package/dist/plugins/media.d.ts +3 -0
  306. package/dist/plugins/media.js +12 -0
  307. package/dist/plugins/move.d.ts +3 -0
  308. package/dist/plugins/move.js +9 -0
  309. package/dist/plugins/table.d.ts +3 -0
  310. package/dist/plugins/table.js +22 -0
  311. package/dist/preset.d.ts +12 -0
  312. package/dist/preset.js +40 -0
  313. package/dist/schema.d.ts +22 -0
  314. package/dist/schema.js +333 -0
  315. package/dist/security/urlPolicy.d.ts +1 -17
  316. package/dist/security/urlPolicy.js +3 -69
  317. package/dist/selection.d.ts +1 -1
  318. package/dist/selectionMapping.d.ts +4 -0
  319. package/dist/selectionMapping.js +222 -0
  320. package/dist/table.d.ts +3 -3
  321. package/dist/transaction.d.ts +35 -12
  322. package/dist/transaction.js +174 -28
  323. package/dist/tree.d.ts +8 -2
  324. package/dist/tree.js +44 -0
  325. package/package.json +27 -8
  326. package/dist/commands/alignment.d.ts +0 -18
  327. package/dist/commands/code.d.ts +0 -13
  328. package/dist/commands/list.d.ts +0 -20
  329. package/dist/commands/list.js +0 -52
@@ -0,0 +1,90 @@
1
+ import { applyOperations, invertOperations } from "./operations.js";
2
+ import { isTextNode } from "./identity.js";
3
+ import { nodeAtPath, resolvePos } from "./positions.js";
4
+ import { validate } from "./schema.js";
5
+ export class StaleTransactionError extends Error {
6
+ constructor(expected, actual) {
7
+ super(`Stale transaction: base revision ${actual}, current revision ${expected}.`);
8
+ this.name = "StaleTransactionError";
9
+ }
10
+ }
11
+ /**
12
+ * A transaction was behind the current revision, but not too far behind to
13
+ * rebase - and at least one of its operations could not be safely
14
+ * transformed against what was committed in the meantime (see
15
+ * collab/transform.ts's doc comment for exactly which shapes this covers:
16
+ * concurrent moves sharing an array, or a concurrent edit to content a
17
+ * merge absorbed). The transaction is rejected outright rather than
18
+ * partially or incorrectly applied - the caller (a real transport
19
+ * implementation, in 12b-server/transport) decides what happens to the
20
+ * user's edit from here (e.g. surface it for manual resolution, retry as a
21
+ * fresh transaction against current state, or convert it to a suggestion).
22
+ */
23
+ export class RebaseConflictError extends Error {
24
+ constructor(reasons) {
25
+ super(`Transaction could not be rebased: ${reasons.map((entry) => entry.reason).join("; ")}`);
26
+ this.reasons = reasons;
27
+ this.name = "RebaseConflictError";
28
+ }
29
+ }
30
+ /**
31
+ * A transaction's baseRevision is older than anything this editor instance
32
+ * has retained (see FoundationEditor's revisionLogLimit) - there is not
33
+ * enough history left to rebase through. The caller must resync (reload
34
+ * the full document at the current revision, e.g. via replaceState) rather
35
+ * than attempt a partial rebase against an incomplete operation log.
36
+ */
37
+ export class ResyncRequiredError extends Error {
38
+ constructor(atRevision) {
39
+ super(`Transaction is too far behind to rebase (current revision ${atRevision}); a full resync is required.`);
40
+ this.atRevision = atRevision;
41
+ this.name = "ResyncRequiredError";
42
+ }
43
+ }
44
+ export const invertTransaction = (transaction) => ({
45
+ id: `inverse:${transaction.id}`,
46
+ baseRevision: transaction.baseRevision + 1,
47
+ operations: invertOperations(transaction.operations),
48
+ selectionBefore: structuredClone(transaction.selectionAfter),
49
+ selectionAfter: structuredClone(transaction.selectionBefore),
50
+ ...(transaction.storedMarksAfter ? { storedMarksBefore: structuredClone(transaction.storedMarksAfter) } : {}),
51
+ ...(transaction.storedMarksBefore ? { storedMarksAfter: structuredClone(transaction.storedMarksBefore) } : {}),
52
+ metadata: {
53
+ ...structuredClone(transaction.metadata),
54
+ source: "api",
55
+ addToHistory: false,
56
+ },
57
+ });
58
+ export const assertTransactionSerializable = (transaction) => {
59
+ const json = JSON.stringify(transaction);
60
+ if (json === undefined)
61
+ throw new Error("Transaction is not JSON-serializable.");
62
+ const parsed = JSON.parse(json);
63
+ if (JSON.stringify(parsed) !== json)
64
+ throw new Error("Transaction is not stably JSON-serializable.");
65
+ };
66
+ export const applyTransactionAtomic = (state, transaction, schema) => {
67
+ if (transaction.baseRevision !== state.revision)
68
+ throw new StaleTransactionError(state.revision, transaction.baseRevision);
69
+ assertTransactionSerializable(transaction);
70
+ const document = applyOperations(state.document, transaction.operations);
71
+ const textOnly = transaction.operations.every((operation) => operation.type === "insertText" || operation.type === "deleteText" || operation.type === "addMark" || operation.type === "removeMark");
72
+ const affectedPaths = [...new Set(transaction.operations.flatMap((operation) => operation.type === "addMark" || operation.type === "removeMark"
73
+ ? [JSON.stringify(operation.range.from.path), JSON.stringify(operation.range.to.path)]
74
+ : "pos" in operation ? [JSON.stringify(operation.pos.path)] : []))];
75
+ const errors = textOnly
76
+ ? affectedPaths.flatMap((pathJson) => {
77
+ const node = nodeAtPath(document, JSON.parse(pathJson));
78
+ if (!node || isTextNode(node))
79
+ return [{ path: JSON.parse(pathJson), code: "invalid-text-owner", message: "Text operation owner no longer exists." }];
80
+ return validate({ type: "doc", id: "local-validation-root", children: [node] }, schema)
81
+ .filter((error) => error.path.length > 0);
82
+ })
83
+ : validate(document, schema);
84
+ if (errors.length)
85
+ throw new Error(`Transaction produced an invalid document: ${errors[0].message}`);
86
+ resolvePos(document, transaction.selectionAfter.anchor);
87
+ resolvePos(document, transaction.selectionAfter.head);
88
+ return { schemaVersion: schema.version, revision: state.revision + 1, document };
89
+ };
90
+ export const documentsEqual = (left, right) => JSON.stringify(left) === JSON.stringify(right);
@@ -0,0 +1,261 @@
1
+ export type NodeGroup = "document" | "block" | "inline";
2
+ export type Attrs = Readonly<Record<string, unknown>>;
3
+ export interface AttributeSpec {
4
+ default?: unknown;
5
+ required?: boolean;
6
+ validate?: (value: unknown) => boolean;
7
+ }
8
+ export interface NodeSpec {
9
+ type: string;
10
+ group: NodeGroup;
11
+ content?: string;
12
+ marks?: string[] | "_all" | "";
13
+ attributes?: Record<string, AttributeSpec>;
14
+ atomic?: boolean;
15
+ isolating?: boolean;
16
+ selectable?: boolean;
17
+ defining?: boolean;
18
+ /** Structural role for scope resolution. Falls back to `type` when absent. */
19
+ semanticRole?: "list" | "list-item" | "table" | "table-row" | "table-cell";
20
+ }
21
+ export interface MarkSpec {
22
+ type: string;
23
+ attributes?: Record<string, AttributeSpec>;
24
+ inclusive?: boolean;
25
+ excludes?: string[];
26
+ spanning?: boolean;
27
+ }
28
+ export interface SmartSchema {
29
+ readonly nodes: Readonly<Record<string, Readonly<NodeSpec>>>;
30
+ readonly marks: Readonly<Record<string, Readonly<MarkSpec>>>;
31
+ readonly topNode: string;
32
+ readonly version: number;
33
+ }
34
+ export interface SmartMark {
35
+ readonly type: string;
36
+ readonly attrs?: Attrs;
37
+ }
38
+ export interface SmartTextNode {
39
+ readonly type: "text";
40
+ readonly text: string;
41
+ readonly marks?: readonly SmartMark[];
42
+ }
43
+ export interface SmartElementNode {
44
+ readonly type: string;
45
+ readonly id: string;
46
+ readonly attrs?: Attrs;
47
+ readonly children?: readonly SmartNode[];
48
+ }
49
+ export type SmartNode = SmartTextNode | SmartElementNode;
50
+ export interface SmartDocument extends SmartElementNode {
51
+ readonly type: "doc";
52
+ readonly children: readonly SmartNode[];
53
+ }
54
+ export interface PersistedEditorDocument {
55
+ schemaVersion: number;
56
+ revision: number;
57
+ document: SmartDocument;
58
+ }
59
+ export interface SmartPos {
60
+ /**
61
+ * Path of the owning non-text node. Offset meaning is resolved from that
62
+ * node's schema role: UTF-16 units for an inline-content owner, child
63
+ * boundaries for a structural owner. Inline atomic children occupy exactly
64
+ * one UTF-16-offset unit and have no internal cursor positions.
65
+ *
66
+ * This is intentionally not a stored discriminated union: a stored kind
67
+ * could disagree with the schema after version skew. `ResolvedPos.kind` is
68
+ * the authoritative, schema-derived discriminant consumed by commands.
69
+ */
70
+ path: number[];
71
+ offset: number;
72
+ }
73
+ export interface ResolvedPos {
74
+ pos: SmartPos;
75
+ kind: "inline" | "structural";
76
+ nodeId: string;
77
+ parent: SmartElementNode;
78
+ depth: number;
79
+ affinity: "forward" | "backward";
80
+ ancestors: readonly SmartElementNode[];
81
+ nodeBefore: SmartNode | null;
82
+ nodeAfter: SmartNode | null;
83
+ atStart: boolean;
84
+ atEnd: boolean;
85
+ indexAt(depth: number): number;
86
+ }
87
+ export interface SmartRange {
88
+ from: SmartPos;
89
+ to: SmartPos;
90
+ }
91
+ export interface SmartSelection {
92
+ anchor: SmartPos;
93
+ head: SmartPos;
94
+ type: "text" | "node" | "cell" | "none";
95
+ }
96
+ export type SmartOperation = {
97
+ type: "insertNode";
98
+ pos: SmartPos;
99
+ node: SmartNode;
100
+ } | {
101
+ type: "removeNode";
102
+ pos: SmartPos;
103
+ node: SmartNode;
104
+ /**
105
+ * Set when this removal is semantically a merge - the node's content
106
+ * was absorbed into the node at this id, not simply deleted. Purely
107
+ * informational: every existing consumer of `removeNode` (apply,
108
+ * invert, map, normalization, history rebasing) ignores it, since
109
+ * removal itself is unaffected either way. Populated by
110
+ * `mergeTableCellsCommand` for an absorbed cell; consumed by
111
+ * `rebaseAnnotationRange` to decide whether an annotation anchored to
112
+ * this id should snap to the surviving node instead of orphaning.
113
+ */
114
+ mergedInto?: string;
115
+ } | {
116
+ type: "replaceNode";
117
+ pos: SmartPos;
118
+ before: SmartNode;
119
+ after: SmartNode;
120
+ /**
121
+ * Ids present in `before`'s subtree that have no counterpart in
122
+ * `after` because their content was folded into a node that does
123
+ * survive, paired with which surviving id each one merged into (a
124
+ * single `replaceNode` can retire more than one id into more than
125
+ * one survivor - e.g. list item merge retires both the outer
126
+ * `list_item` wrapper and its inner paragraph, into the target
127
+ * item's wrapper and paragraph respectively, not one shared
128
+ * survivor). Every other id difference between before/after is an
129
+ * ordinary content change, not a merge. Same "purely informational,
130
+ * ignored by every existing consumer" contract as
131
+ * `removeNode.mergedInto` above. Populated by list item merge
132
+ * (`mergeItems`); consumed by `rebaseAnnotationRange`.
133
+ */
134
+ retiredInto?: readonly {
135
+ readonly retiredId: string;
136
+ readonly survivorId: string;
137
+ }[];
138
+ } | {
139
+ type: "moveNode";
140
+ from: SmartPos;
141
+ to: SmartPos;
142
+ nodeId: string;
143
+ } | {
144
+ type: "splitNode";
145
+ pos: SmartPos;
146
+ depth: number;
147
+ newId: string;
148
+ } | {
149
+ type: "mergeNode";
150
+ pos: SmartPos;
151
+ depth: number;
152
+ retiredId: string;
153
+ splitOffset: number;
154
+ } | {
155
+ type: "setNodeAttributes";
156
+ pos: SmartPos;
157
+ before: Attrs;
158
+ after: Attrs;
159
+ } | {
160
+ type: "setNodeType";
161
+ pos: SmartPos;
162
+ before: string;
163
+ after: string;
164
+ beforeAttrs: Attrs;
165
+ afterAttrs: Attrs;
166
+ } | {
167
+ type: "insertText";
168
+ pos: SmartPos;
169
+ text: string;
170
+ marks?: SmartMark[];
171
+ } | {
172
+ type: "deleteText";
173
+ pos: SmartPos;
174
+ text: string;
175
+ marks?: SmartMark[];
176
+ } | {
177
+ type: "addMark";
178
+ range: SmartRange;
179
+ mark: SmartMark;
180
+ } | {
181
+ type: "removeMark";
182
+ range: SmartRange;
183
+ mark: SmartMark;
184
+ };
185
+ export interface SmartTransaction {
186
+ id: string;
187
+ baseRevision: number;
188
+ operations: SmartOperation[];
189
+ selectionBefore: SmartSelection;
190
+ selectionAfter: SmartSelection;
191
+ storedMarksBefore?: SmartMark[];
192
+ storedMarksAfter?: SmartMark[];
193
+ metadata: {
194
+ source: "input" | "keyboard" | "toolbar" | "paste" | "cut" | "drop" | "api";
195
+ timestamp: number;
196
+ historyGroup?: string;
197
+ addToHistory: boolean;
198
+ compositionId?: string;
199
+ authorId?: string;
200
+ };
201
+ }
202
+ export interface TransactionMap {
203
+ map(pos: SmartPos, bias?: -1 | 1): SmartPos;
204
+ mapRange(range: SmartRange): SmartRange;
205
+ mapSelection(selection: SmartSelection): SmartSelection;
206
+ deleted(pos: SmartPos): boolean;
207
+ }
208
+ export interface NormalizerContext {
209
+ readonly schema: SmartSchema;
210
+ readonly affectedPath: readonly number[];
211
+ readonly pass: number;
212
+ readonly documentWide: boolean;
213
+ }
214
+ export interface NormalizerResult {
215
+ operations: SmartOperation[];
216
+ }
217
+ export interface NormalizerRegistration {
218
+ id: string;
219
+ priority: number;
220
+ documentWide?: boolean;
221
+ normalize(document: SmartDocument, context: NormalizerContext): NormalizerResult;
222
+ }
223
+ export interface HistoryEntry {
224
+ forward: SmartTransaction;
225
+ inverse: SmartTransaction;
226
+ estimatedBytes: number;
227
+ }
228
+ export interface SmartHistory {
229
+ readonly undo: readonly HistoryEntry[];
230
+ readonly redo: readonly HistoryEntry[];
231
+ readonly limit: number;
232
+ /** Maximum estimated serialized bytes retained in either history stack. */
233
+ readonly byteLimit: number;
234
+ readonly coalescenceWindowMs: number;
235
+ }
236
+ export interface ModelDomMapping {
237
+ nodeToDom(nodeId: string): HTMLElement | null;
238
+ domToNode(el: Node): {
239
+ nodeId: string;
240
+ node: SmartNode;
241
+ } | null;
242
+ posToDom(pos: SmartPos): {
243
+ node: Node;
244
+ offset: number;
245
+ } | null;
246
+ domToPos(node: Node, offset: number): SmartPos | null;
247
+ isEditorUiNode(el: Node): boolean;
248
+ rebuild(root: HTMLElement, document: SmartDocument): void;
249
+ }
250
+ export interface ValidationError {
251
+ path: number[];
252
+ code: string;
253
+ message: string;
254
+ }
255
+ export interface Repair {
256
+ path: number[];
257
+ code: string;
258
+ message: string;
259
+ before?: unknown;
260
+ after?: unknown;
261
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { DocumentDiff } from "../diff/types.js";
2
+ import type { SmartSchema } from "../types.js";
3
+ import type { DocumentVersion } from "./types.js";
4
+ /** Thin convenience wrapper over `diffDocuments` for the common "diff two saved versions" call shape. */
5
+ export declare const diffVersions: (a: DocumentVersion, b: DocumentVersion, schema: SmartSchema) => DocumentDiff;
@@ -0,0 +1,3 @@
1
+ import { diffDocuments } from "../diff/documentDiff.js";
2
+ /** Thin convenience wrapper over `diffDocuments` for the common "diff two saved versions" call shape. */
3
+ export const diffVersions = (a, b, schema) => diffDocuments(a.envelope.document, b.envelope.document, schema);
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./diffVersions.js";
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./diffVersions.js";
@@ -0,0 +1,17 @@
1
+ import type { PersistedEditorDocument } from "../types.js";
2
+ /**
3
+ * A named, saved snapshot of the document a user can return to or compare
4
+ * against - distinct from the undo/redo history stack (`history.ts`), which
5
+ * is moment-to-moment edit state, not something a user browses or restores
6
+ * from days later. Reuses the existing `PersistedEditorDocument` envelope
7
+ * verbatim as its payload rather than a new document shape - a version
8
+ * *is* a full document snapshot with a revision, which is exactly what
9
+ * that envelope already represents.
10
+ */
11
+ export interface DocumentVersion {
12
+ readonly id: string;
13
+ readonly label?: string;
14
+ readonly createdAt: number;
15
+ readonly authorId?: string;
16
+ readonly envelope: PersistedEditorDocument;
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { type SmartEditorState, type LegacySmartTransaction } from "./transaction.js";
2
+ export interface LegacyHistoryEntry {
3
+ forward: LegacySmartTransaction;
4
+ inverse: LegacySmartTransaction;
5
+ }
6
+ export interface SmartHistoryState {
7
+ undo: LegacyHistoryEntry[];
8
+ redo: LegacyHistoryEntry[];
9
+ limit: number;
10
+ }
11
+ export interface HistoryResult {
12
+ state: SmartEditorState;
13
+ history: SmartHistoryState;
14
+ applied: boolean;
15
+ }
16
+ export declare const createHistoryState: (limit?: number) => SmartHistoryState;
17
+ export declare const invertLegacyTransaction: (state: SmartEditorState, transaction: LegacySmartTransaction) => LegacySmartTransaction;
18
+ export declare const applyTransactionWithHistory: (state: SmartEditorState, history: SmartHistoryState, transaction: LegacySmartTransaction) => HistoryResult;
19
+ export declare const undoHistory: (state: SmartEditorState, history: SmartHistoryState) => HistoryResult;
20
+ export declare const redoHistory: (state: SmartEditorState, history: SmartHistoryState) => HistoryResult;
@@ -0,0 +1,165 @@
1
+ import { mapSelectionThroughOperation } from "./selectionMapping.js";
2
+ import { normalizeSmartDocument } from "./schema.js";
3
+ import { applyOperationToState, applyTransaction, getMoveDestinationAfterRemoval, } from "./transaction.js";
4
+ import { getNodeAtTreePath, isSmartContainer } from "./tree.js";
5
+ export const createHistoryState = (limit = 100) => ({
6
+ undo: [],
7
+ redo: [],
8
+ limit: Math.max(1, Math.floor(limit)),
9
+ });
10
+ const parentPath = (path) => path.slice(0, -1);
11
+ const inverseForOperation = (document, selection, operation) => {
12
+ if (operation.type === "insertNode")
13
+ return [{ type: "removeNode", path: operation.path }];
14
+ if (operation.type === "removeNode") {
15
+ return [{ type: "insertNode", path: operation.path, node: getNodeAtTreePath(document, operation.path) }];
16
+ }
17
+ if (operation.type === "replaceNode") {
18
+ return [{ type: "replaceNode", path: operation.path, node: getNodeAtTreePath(document, operation.path) }];
19
+ }
20
+ if (operation.type === "moveNode") {
21
+ const movedNode = getNodeAtTreePath(document, operation.from);
22
+ const destination = getMoveDestinationAfterRemoval(operation.from, operation.to);
23
+ return [
24
+ { type: "removeNode", path: destination },
25
+ { type: "insertNode", path: operation.from, node: movedNode },
26
+ ];
27
+ }
28
+ if (operation.type === "splitNode") {
29
+ const rightPath = [
30
+ ...parentPath(operation.path),
31
+ operation.path[operation.path.length - 1] + 1,
32
+ ];
33
+ return [{ type: "mergeNode", path: rightPath }];
34
+ }
35
+ if (operation.type === "mergeNode") {
36
+ const leftPath = [
37
+ ...parentPath(operation.path),
38
+ operation.path[operation.path.length - 1] - 1,
39
+ ];
40
+ const left = getNodeAtTreePath(document, leftPath);
41
+ const position = left?.type === "text"
42
+ ? left.text.length
43
+ : isSmartContainer(left)
44
+ ? left.children.length
45
+ : 0;
46
+ return [{ type: "splitNode", path: leftPath, position }];
47
+ }
48
+ if (operation.type === "setNodeAttrs") {
49
+ const node = getNodeAtTreePath(document, operation.path);
50
+ const attrs = Object.fromEntries(Object.keys(operation.attrs).map((name) => [name, Object.prototype.hasOwnProperty.call(node, name) ? node[name] : undefined]));
51
+ return [{ type: "setNodeAttrs", path: operation.path, attrs }];
52
+ }
53
+ if (operation.type === "replaceText") {
54
+ const node = getNodeAtTreePath(document, operation.path);
55
+ return [{
56
+ type: "replaceText",
57
+ path: operation.path,
58
+ start: operation.start,
59
+ end: operation.start + operation.text.length,
60
+ text: node.text.slice(operation.start, operation.end),
61
+ }];
62
+ }
63
+ if (operation.type === "addMark" || operation.type === "removeMark") {
64
+ const path = parentPath(operation.path);
65
+ return [{ type: "replaceNode", path, node: getNodeAtTreePath(document, path) }];
66
+ }
67
+ return [{ type: "setSelection", selection }];
68
+ };
69
+ export const invertLegacyTransaction = (state, transaction) => {
70
+ let document = state.document;
71
+ let selection = state.selection;
72
+ const inverseGroups = [];
73
+ transaction.operations.forEach((operation) => {
74
+ inverseGroups.unshift(inverseForOperation(document, selection, operation));
75
+ const before = document;
76
+ const next = applyOperationToState(document, selection, operation);
77
+ document = next.document;
78
+ selection = operation.type === "setSelection"
79
+ ? next.selection
80
+ : mapSelectionThroughOperation(selection, operation, before);
81
+ });
82
+ return {
83
+ id: `undo:${transaction.id}`,
84
+ source: "history",
85
+ operations: inverseGroups.flat(),
86
+ selectionBefore: transaction.selectionAfter ?? selection,
87
+ selectionAfter: state.selection,
88
+ addToHistory: false,
89
+ timestamp: transaction.timestamp,
90
+ };
91
+ };
92
+ export const applyTransactionWithHistory = (state, history, transaction) => {
93
+ let inverse = invertLegacyTransaction(state, transaction);
94
+ const nextState = applyTransaction(state, transaction);
95
+ const restored = applyTransaction(nextState, inverse);
96
+ const expectedDocument = normalizeSmartDocument(state.document);
97
+ if (JSON.stringify(restored.document) !== JSON.stringify(expectedDocument) ||
98
+ JSON.stringify(restored.selection) !== JSON.stringify(state.selection)) {
99
+ inverse = {
100
+ ...inverse,
101
+ operations: [{ type: "replaceNode", path: [], node: expectedDocument }],
102
+ selectionAfter: state.selection,
103
+ };
104
+ }
105
+ if (!transaction.addToHistory) {
106
+ return { state: nextState, history, applied: true };
107
+ }
108
+ const previous = history.undo[history.undo.length - 1];
109
+ const canGroup = Boolean(transaction.historyGroup &&
110
+ previous?.forward.historyGroup === transaction.historyGroup);
111
+ const entry = canGroup
112
+ ? {
113
+ forward: {
114
+ ...previous.forward,
115
+ operations: [...previous.forward.operations, ...transaction.operations],
116
+ selectionAfter: transaction.selectionAfter ?? nextState.selection,
117
+ timestamp: transaction.timestamp,
118
+ },
119
+ inverse: {
120
+ ...inverse,
121
+ operations: [...inverse.operations, ...previous.inverse.operations],
122
+ selectionBefore: transaction.selectionAfter ?? nextState.selection,
123
+ selectionAfter: previous.inverse.selectionAfter,
124
+ },
125
+ }
126
+ : { forward: transaction, inverse };
127
+ const undo = canGroup
128
+ ? [...history.undo.slice(0, -1), entry]
129
+ : [...history.undo, entry];
130
+ if (undo.length > history.limit)
131
+ undo.splice(0, undo.length - history.limit);
132
+ return {
133
+ state: nextState,
134
+ history: { ...history, undo, redo: [] },
135
+ applied: true,
136
+ };
137
+ };
138
+ export const undoHistory = (state, history) => {
139
+ const entry = history.undo[history.undo.length - 1];
140
+ if (!entry)
141
+ return { state, history, applied: false };
142
+ return {
143
+ state: applyTransaction(state, entry.inverse),
144
+ history: {
145
+ ...history,
146
+ undo: history.undo.slice(0, -1),
147
+ redo: [...history.redo, entry],
148
+ },
149
+ applied: true,
150
+ };
151
+ };
152
+ export const redoHistory = (state, history) => {
153
+ const entry = history.redo[history.redo.length - 1];
154
+ if (!entry)
155
+ return { state, history, applied: false };
156
+ return {
157
+ state: applyTransaction(state, entry.forward),
158
+ history: {
159
+ ...history,
160
+ undo: [...history.undo, entry],
161
+ redo: history.redo.slice(0, -1),
162
+ },
163
+ applied: true,
164
+ };
165
+ };
@@ -3,6 +3,12 @@ const BLOCK_TAGS = new Set([
3
3
  "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li",
4
4
  "blockquote", "pre", "table", "div",
5
5
  ]);
6
+ const SEMANTIC_SRTE_ATTRIBUTES = new Set([
7
+ "data-srte-checklist",
8
+ "data-srte-checklist-strike",
9
+ "data-srte-checked",
10
+ "data-srte-list-preset",
11
+ ]);
6
12
  const attrsOf = (node) => node.attrs || [];
7
13
  const hasAttr = (node, name, value) => attrsOf(node).some((attr) => attr.name === name && (value == null || attr.value === value));
8
14
  const isTag = (node, tagName) => node.tagName === tagName;
@@ -38,7 +44,7 @@ const normalizeNode = (node) => {
38
44
  return false;
39
45
  if (attr.name === "data-row-index" || attr.name === "data-col-index")
40
46
  return false;
41
- return !attr.name.startsWith("data-srte-");
47
+ return !attr.name.startsWith("data-srte-") || SEMANTIC_SRTE_ATTRIBUTES.has(attr.name);
42
48
  });
43
49
  if (!node.childNodes)
44
50
  return;
package/dist/index.d.ts CHANGED
@@ -1,14 +1 @@
1
- export * from "./model.js";
2
- export * from "./command.js";
3
- export * from "./marks.js";
4
- export * from "./selection.js";
5
- export * from "./table.js";
6
- export * from "./transaction.js";
7
- export * from "./tree.js";
8
- export * from "./commands/list.js";
9
- export * from "./commands/code.js";
10
- export * from "./commands/marks.js";
11
- export * from "./commands/alignment.js";
12
- export * from "./html/compatibility.js";
13
- export * from "./markdown/compatibility.js";
14
- export * from "./security/urlPolicy.js";
1
+ export * from "./foundation/index.js";
package/dist/index.js CHANGED
@@ -1,14 +1,9 @@
1
- export * from "./model.js";
2
- export * from "./command.js";
3
- export * from "./marks.js";
4
- export * from "./selection.js";
5
- export * from "./table.js";
6
- export * from "./transaction.js";
7
- export * from "./tree.js";
8
- export * from "./commands/list.js";
9
- export * from "./commands/code.js";
10
- export * from "./commands/marks.js";
11
- export * from "./commands/alignment.js";
12
- export * from "./html/compatibility.js";
13
- export * from "./markdown/compatibility.js";
14
- export * from "./security/urlPolicy.js";
1
+ // The package root is the canonical, framework-agnostic public API: the
2
+ // foundation document model and editing engine, and nothing else. Everything
3
+ // built on the pre-canonical model (legacyCommands/*, plugins/*, the
4
+ // html/markdown compatibility layers, and the discriminated-union model
5
+ // types in model.ts et al.) lives exclusively behind "smartrte-core/legacy"
6
+ // - see legacy/index.ts. Do not re-add those exports here; that flat
7
+ // re-export used to defeat the whole point of the /legacy subpath by making
8
+ // the same symbols reachable from the root import too.
9
+ export * from "./foundation/index.js";