wendang 0.1.0 → 0.2.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 (98) hide show
  1. package/LICENSE +43 -2
  2. package/NOTICE.md +1 -1
  3. package/README.md +120 -52
  4. package/dist/cjs/document.js +1 -19
  5. package/dist/cjs/index.js +1 -25
  6. package/dist/cjs/mindmap.js +1 -22
  7. package/dist/cjs/schema.js +1 -11
  8. package/dist/cjs/shared/anchor.js +1 -201
  9. package/dist/cjs/shared/callout.js +1 -64
  10. package/dist/cjs/shared/code-block.js +1 -33
  11. package/dist/cjs/shared/code-languages.js +1 -51
  12. package/dist/cjs/shared/doc-compatibility.js +1 -23
  13. package/dist/cjs/shared/doc-text.js +1 -52
  14. package/dist/cjs/shared/document-colors.js +1 -61
  15. package/dist/cjs/shared/headings.js +1 -0
  16. package/dist/cjs/shared/inline-comments.js +1 -179
  17. package/dist/cjs/shared/mind-map-folding.js +1 -14
  18. package/dist/cjs/shared/mind-map-phase2.js +1 -133
  19. package/dist/cjs/shared/mind-map.js +1 -445
  20. package/dist/cjs/shared/mind-topic-text.js +1 -57
  21. package/dist/cjs/shared/node-catalog.js +1 -62
  22. package/dist/cjs/shared/presence.js +1 -141
  23. package/dist/cjs/shared/safe-href.js +1 -36
  24. package/dist/cjs/shared/table-cell-format.js +1 -18
  25. package/dist/cjs/shared/table-layout.js +1 -28
  26. package/dist/cjs/shared/types.js +1 -2
  27. package/dist/cjs/shared/video-embed.js +1 -102
  28. package/dist/cjs/shared/workbook.js +1 -152
  29. package/dist/cjs/spreadsheet.js +1 -17
  30. package/dist/cjs/types.js +1 -17
  31. package/dist/cjs/validation/callout.js +1 -17
  32. package/dist/cjs/validation/doc-node-whitelist.js +1 -71
  33. package/dist/cjs/validation/error.js +1 -17
  34. package/dist/cjs/validation/mind-map.js +1 -129
  35. package/dist/cjs/validation/schema-version.js +1 -5
  36. package/dist/cjs/validation/schema.js +1 -376
  37. package/dist/cjs/validation/video-embed.js +1 -17
  38. package/dist/cjs/validation/workbook.js +1 -17
  39. package/dist/esm/document.js +1 -3
  40. package/dist/esm/index.js +1 -3
  41. package/dist/esm/mindmap.js +1 -4
  42. package/dist/esm/schema.js +1 -3
  43. package/dist/esm/shared/anchor.js +1 -192
  44. package/dist/esm/shared/callout.js +1 -56
  45. package/dist/esm/shared/code-block.js +1 -25
  46. package/dist/esm/shared/code-languages.js +1 -46
  47. package/dist/esm/shared/doc-compatibility.js +1 -20
  48. package/dist/esm/shared/doc-text.js +1 -48
  49. package/dist/esm/shared/document-colors.js +1 -56
  50. package/dist/esm/shared/headings.js +1 -0
  51. package/dist/esm/shared/inline-comments.js +1 -173
  52. package/dist/esm/shared/mind-map-folding.js +1 -10
  53. package/dist/esm/shared/mind-map-phase2.js +1 -121
  54. package/dist/esm/shared/mind-map.js +1 -427
  55. package/dist/esm/shared/mind-topic-text.js +1 -49
  56. package/dist/esm/shared/node-catalog.js +1 -57
  57. package/dist/esm/shared/presence.js +1 -130
  58. package/dist/esm/shared/safe-href.js +1 -32
  59. package/dist/esm/shared/table-cell-format.js +1 -14
  60. package/dist/esm/shared/table-layout.js +1 -23
  61. package/dist/esm/shared/types.js +1 -1
  62. package/dist/esm/shared/video-embed.js +1 -96
  63. package/dist/esm/shared/workbook.js +1 -144
  64. package/dist/esm/spreadsheet.js +1 -1
  65. package/dist/esm/types.js +1 -1
  66. package/dist/esm/validation/callout.js +1 -1
  67. package/dist/esm/validation/doc-node-whitelist.js +1 -68
  68. package/dist/esm/validation/error.js +1 -13
  69. package/dist/esm/validation/mind-map.js +1 -125
  70. package/dist/esm/validation/schema-version.js +1 -2
  71. package/dist/esm/validation/schema.js +1 -372
  72. package/dist/esm/validation/video-embed.js +1 -1
  73. package/dist/esm/validation/workbook.js +1 -1
  74. package/dist/types/document.d.ts +1 -0
  75. package/dist/types/shared/anchor.d.ts +0 -55
  76. package/dist/types/shared/callout.d.ts +39 -2
  77. package/dist/types/shared/code-block.d.ts +0 -1
  78. package/dist/types/shared/code-languages.d.ts +0 -19
  79. package/dist/types/shared/doc-text.d.ts +0 -14
  80. package/dist/types/shared/document-colors.d.ts +0 -4
  81. package/dist/types/shared/headings.d.ts +7 -0
  82. package/dist/types/shared/inline-comments.d.ts +0 -42
  83. package/dist/types/shared/mind-map-folding.d.ts +0 -2
  84. package/dist/types/shared/mind-map.d.ts +0 -4
  85. package/dist/types/shared/mind-topic-text.d.ts +0 -2
  86. package/dist/types/shared/node-catalog.d.ts +0 -17
  87. package/dist/types/shared/presence.d.ts +0 -51
  88. package/dist/types/shared/safe-href.d.ts +0 -1
  89. package/dist/types/shared/table-cell-format.d.ts +0 -1
  90. package/dist/types/shared/table-layout.d.ts +0 -3
  91. package/dist/types/shared/types.d.ts +0 -4
  92. package/dist/types/shared/video-embed.d.ts +9 -2
  93. package/dist/types/shared/workbook.d.ts +0 -2
  94. package/dist/types/validation/doc-node-whitelist.d.ts +0 -19
  95. package/dist/types/validation/mind-map.d.ts +0 -2
  96. package/dist/types/validation/schema-version.d.ts +0 -1
  97. package/dist/types/validation/schema.d.ts +0 -34
  98. package/package.json +9 -9
package/LICENSE CHANGED
@@ -1,3 +1,44 @@
1
- UNLICENSED
1
+ Wendang 商业使用许可 1.0
2
2
 
3
- No license to use, copy, modify or distribute the Wendang source is granted by this file. Contact the maintainer for terms. Third-party components retain their own licenses; see NOTICE.md.
3
+ Copyright (c) 2026 ztes. All rights reserved.
4
+
5
+ 本许可适用于 Wendang 项目及 wendang、wendang-vue 包中由 ztes 拥有版权的代码。
6
+ 使用、复制或分发本软件即表示接受以下条款。
7
+
8
+ 1. 允许的使用
9
+ 在遵守本许可的前提下,授予您非独占、全球、免许可费的权利:
10
+ a. 将本软件用于个人、内部或商业用途,包括收费产品、客户项目及 SaaS 服务;
11
+ b. 为开发、集成、测试、维护和运行您的应用而复制本软件,并修改源码;
12
+ c. 将原版或修改后的编译代码作为您的应用组成部分交付给客户或最终用户,
13
+ 包括网站、浏览器应用、桌面应用、移动应用及服务端部署;
14
+ d. 向受保密义务约束的雇员、承包商或客户提供维护上述应用所需的源码,
15
+ 前提是这些接收者只能为该应用使用源码并遵守本许可。
16
+ 您无需公开应用源码,也无需向 ztes 支付商业使用许可费。
17
+
18
+ 2. 限制及保留的权利
19
+ 本许可未授予您将本软件或其修改版作为独立库、SDK、编辑器组件包或源码
20
+ 产品进行公开再发布、再许可、改名发布、出售或出租的权利。
21
+ 上述限制不妨碍您销售或运营包含本软件的应用,也不妨碍按第 1 条交付应用。
22
+ 超出本许可范围的独立分发或源码再许可,须另获 ztes 书面授权。
23
+ 不得移除本软件中的版权、许可及第三方权利声明。本许可不授予商标权;
24
+ 其他未明确授予的权利由各权利人保留。
25
+
26
+ 3. 声明的保留
27
+ 分发包含本软件的应用时,应在随附许可文件、第三方声明或应用关于页面中
28
+ 保留本许可及版权声明。无需在应用主界面展示品牌或链接。
29
+
30
+ 4. 第三方组件
31
+ 第三方组件及其版权声明继续适用各自许可证,见 NOTICE.md 和
32
+ third-party-notices/。本许可不限制第三方许可证已经授予的权利。
33
+
34
+ 5. 终止
35
+ 如违反本许可且未在收到书面通知后 30 日内纠正,本许可授予的权利终止。
36
+ 终止不影响终止前已产生的义务及权利人的救济权。
37
+
38
+ 6. 免责声明与责任限制
39
+ 在适用法律允许的最大范围内,本软件按“现状”提供,不附带任何明示或
40
+ 默示担保,包括适销性、特定用途适用性及不侵权担保。对于使用或无法使用
41
+ 本软件产生的损害、数据丢失、利润损失或其他责任,作者及权利人不承担责任。
42
+
43
+ 这是保留源码权利的商业使用许可,不是开源许可证。
44
+ 授权联系:https://doc.yjmf.com
package/NOTICE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Third-party notices
2
2
 
3
3
  Generated from the installed runtime dependency graph; package licenses remain unchanged.
4
- Wendang itself remains UNLICENSED. This inventory is not a grant of rights or a statement that optional Pro services are included.
4
+ Wendang is licensed under the Wendang Commercial Use License; see LICENSE. Third-party components retain their own licenses.
5
5
 
6
6
  | Package | Version | Declared license | Included upstream notice |
7
7
  | --- | --- | --- | --- |
package/README.md CHANGED
@@ -1,104 +1,172 @@
1
1
  # wendang
2
2
 
3
- 文档、电子表格、思维导图的内容协议与校验核心。适用于前端数据层和 Node 服务端,不包含可视编辑器。当前源码 0.1.0 为开发版,npm 0.0.1 仅为占位。
3
+ 文档、电子表格和思维导图的内容协议与校验核心,适用于浏览器数据层和 Node.js 服务端。
4
4
 
5
- 源码位于 [ztes/wendang 的 packages/core](https://gitee.com/ztes/wendang/tree/main/packages/core)。本项目单仓双包维护,开发、问题和版本规范见 [仓库 README](https://gitee.com/ztes/wendang/blob/main/README.md)。
5
+ [主页](https://doc.yjmf.com) · [Vue 编辑组件](https://www.npmjs.com/package/wendang-vue)
6
6
 
7
- ## 安装与使用
8
-
9
- 对应版本正式发布后:
7
+ ## 安装
10
8
 
11
9
  ```sh
12
10
  npm install wendang
13
11
  ```
14
12
 
13
+ 支持 Node.js >=20.19、ESM、CommonJS 和 TypeScript。无运行时依赖,不初始化 DOM,也不发起网络请求。
14
+
15
+ ## 文档校验
16
+
15
17
  ```ts
16
18
  import { ContentSchemaService, SCHEMA_VERSION } from 'wendang/schema'
17
- import { emptyWorkbook, validateWorkbook } from 'wendang/spreadsheet'
18
- import { createMindMap, readMindMap, mindMapToJson } from 'wendang/mindmap'
19
- import type { ProseMirrorDoc, WorkbookDocument } from 'wendang/types'
19
+ import type { ProseMirrorDoc } from 'wendang/types'
20
20
 
21
- const result = new ContentSchemaService().validate({
21
+ const doc: ProseMirrorDoc = {
22
22
  type: 'doc',
23
- content: [{ type: 'paragraph', content: [{ type: 'text', text: '你好' }] }],
24
- }, SCHEMA_VERSION)
23
+ content: [{
24
+ type: 'paragraph',
25
+ content: [{ type: 'text', text: '你好,Wendang' }],
26
+ }],
27
+ }
25
28
 
26
- const book = validateWorkbook(emptyWorkbook(crypto.randomUUID()))
27
- const map = readMindMap(createMindMap('both'))
29
+ const result = new ContentSchemaService().validate(doc, SCHEMA_VERSION)
30
+ console.log(result.doc)
31
+ console.log(result.attachmentIds)
32
+ console.log(result.linkTargetIds)
33
+ console.log(result.mentionUserIds)
28
34
  ```
29
35
 
30
- 支持 Node >=20.19、ESM 和 CommonJS:
36
+ CommonJS:
31
37
 
32
38
  ```js
33
39
  const { ContentSchemaService, SCHEMA_VERSION } = require('wendang/schema')
40
+
34
41
  const result = new ContentSchemaService().validate(
35
42
  { type: 'doc', content: [{ type: 'paragraph' }] },
36
43
  SCHEMA_VERSION,
37
44
  )
38
45
  ```
39
46
 
40
- 同步校验不发网络请求,不初始化框架、登录、数据库或 DOM。布局默认使用确定的保守度量,Vue 层按需注入浏览器字体度量;核心不执行 Excel 公式。
41
-
42
- ## API 入口
47
+ `validate(raw, schemaVersion)` 返回以下字段:
43
48
 
44
- | 入口 | 内容 |
49
+ | 字段 | 含义 |
45
50
  | --- | --- |
46
- | `wendang` | schema、版本、主要类型、创建工作簿 / 导图 |
47
- | `wendang/schema` | `ContentSchemaService`、`ContentValidationError`、`SCHEMA_VERSION`、节点 / mark 白名单 |
48
- | `wendang/document` | 文档类型、节点目录、文本工具和 schema |
49
- | `wendang/spreadsheet` | 工作簿类型、`emptyWorkbook`、`validateWorkbook`、`workbookText` |
50
- | `wendang/mindmap` | 创建 / 解析 / 序列化、布局、分支、搜索及校验 |
51
- | `wendang/types` | 内容、工作簿、导图和讨论类型 |
51
+ | `doc` | 校验并按白名单处理后的内容 |
52
+ | `schemaVersion` | 内容 schema 版本 |
53
+ | `attachmentIds` | 附件 ID 列表 |
54
+ | `linkTargetIds` | 被引用页面的 ID 列表 |
55
+ | `mentionUserIds` | 被提及用户的 ID 列表 |
52
56
 
53
- `internal/*` / `validation/*` 仅为现有应用迁移兼容入口,三方新增代码优先使用上表入口,不依赖 dist 深层文件。
57
+ 未知节点、非法值和不支持的更高版本会被拒绝。`schemaVersion` 必须为正整数;普通文档当前版本为 `10`。
54
58
 
55
- ## 文档校验与服务端规范
59
+ ### 处理校验错误
56
60
 
57
- `new ContentSchemaService().validate(raw, schemaVersion)` 返回:
58
-
59
- - `doc`、`schemaVersion`:按支持规则校验 / 处理后的内容;
60
- - `attachmentIds`、`linkTargetIds`、`mentionUserIds`:供业务授权、引用、通知使用。
61
-
62
- 失败抛 `ContentValidationError`,包含 `message`、`code`(`validation.failed`)、`status`(422)、可选 `details`;宿主自行转为框架异常。schemaVersion 必须为正整数。未知节点拒绝,普通脏属性按白名单处理,未知更高版本拒绝写入。
61
+ ```ts
62
+ import {
63
+ ContentSchemaService,
64
+ ContentValidationError,
65
+ SCHEMA_VERSION,
66
+ } from 'wendang/schema'
67
+
68
+ try {
69
+ new ContentSchemaService().validate({ type: 'unknown' }, SCHEMA_VERSION)
70
+ } catch (error) {
71
+ if (error instanceof ContentValidationError) {
72
+ console.error(error.code, error.message, error.details)
73
+ } else {
74
+ throw error
75
+ }
76
+ }
77
+ ```
63
78
 
64
- **校验不等于授权。** 后端仍需检查写入权限、附件归属、引用可见性和审批发布;前端检查不替代后端。当前普通文档 schemaVersion 为 **10**,不是 npm 包版本。
79
+ `ContentValidationError` 包含 `code`、`message`、`status` 和可选 `details`。普通校验错误的 `code` 为 `validation.failed`,`status` 为 `422`;可由应用映射成 HTTP 响应。
65
80
 
66
- 当前协议的页面、用户、附件引用使用 UUID。宿主若使用数字或其他业务主键,应建立稳定映射;不要把存储 URL 或临时 token 当作 ID。
81
+ 内容校验不执行授权。服务端仍需检查写入权限、附件归属和引用资源可见性。页面、用户、附件引用使用 UUID;其他业务主键需建立稳定映射。
67
82
 
68
- ## 工作簿规范
83
+ ## 电子表格
69
84
 
70
85
  ```ts
86
+ import {
87
+ emptyWorkbook,
88
+ validateWorkbook,
89
+ workbookText,
90
+ } from 'wendang/spreadsheet'
91
+
71
92
  const doc = emptyWorkbook(crypto.randomUUID())
93
+ doc.workbook.name = '销售统计'
72
94
  doc.workbook.sheets.sheet1.cellData = {
73
95
  0: {
74
- 0: { v: 12, t: 2 },
75
- 1: { f: '=A1*2', v: 24, t: 2 },
96
+ 0: { v: '金额', t: 1 },
97
+ 1: { v: 12, t: 2 },
98
+ 2: { f: '=B1*2', v: 24, t: 2 },
76
99
  },
77
100
  }
101
+
78
102
  const snapshot = validateWorkbook(doc)
103
+ const searchableText = workbookText(snapshot)
79
104
  ```
80
105
 
81
- 根为 `{ type: 'workbook', formatVersion: 1, workbook: ... }`。`f` 为表达式,`v` 为数值或保存的公式缓存。最多 20 Sheet、每表 10000 行 / 200 列、实际单元格总数 50000、JSON 4MiB。验证拒绝危险属性、非法值、越界、重叠合并,并返回独立快照。
106
+ 工作簿结构为 `{ type: 'workbook', formatVersion: 1, workbook: ... }`:
82
107
 
83
- workbook 含 Univer 1.1.0 兼容数据与资源,是已版本化的引擎适配格式;不承诺任意引擎大版本无损兼容。XLSX 转换在 `wendang-vue/spreadsheet/xlsx`,本包不引入 ExcelJS。
108
+ - `f` 保存公式表达式,`v` 保存值或公式计算缓存。
109
+ - `validateWorkbook` 返回独立快照,检查越界、危险属性、非法值和重叠合并。
110
+ - 核心包保存和校验公式,不执行公式计算。可视编辑和 XLSX 转换使用 `wendang-vue`。
111
+ - 工作簿采用与 Univer 1.1.0 兼容的数据结构;升级引擎大版本时需检查格式兼容。
84
112
 
85
- ## 思维导图规范
113
+ | 限制 | 上限 |
114
+ | --- | --- |
115
+ | Sheet 数 | 20 |
116
+ | 每表行数 / 列数 | 10000 / 200 |
117
+ | 实际单元格总数 | 50000 |
118
+ | JSON 大小 | 4 MiB |
86
119
 
87
- `createMindMap(layout)` 创建持久化 `MindMapJson`,`readMindMap(json)` 返回 `MindMapModel | null`,`mindMapToJson(model)` 序列化。当前 formatVersion 为 **3**,支持读取已有受支持旧版。
120
+ ## 思维导图
88
121
 
89
- 主题和关系线使用稳定 ID;主题包含 parentId、order、文字及可选富文本、描述、备注、附件与样式。单图最多 500 主题,标题 2000 字符。视口、选区、折叠属于视图状态。复制分支重建身份,不能直接沿用旧讨论锚点。
122
+ ```ts
123
+ import {
124
+ createMindMap,
125
+ readMindMap,
126
+ mindMapToJson,
127
+ } from 'wendang/mindmap'
128
+
129
+ const json = createMindMap('both')
130
+ const model = readMindMap(json)
131
+
132
+ if (model) {
133
+ model.topics[0].text = '项目计划'
134
+ const snapshot = mindMapToJson(model)
135
+ console.log(snapshot)
136
+ }
137
+ ```
138
+
139
+ | API | 用途 |
140
+ | --- | --- |
141
+ | `createMindMap(layout?)` | 创建可持久化的 `MindMapJson` |
142
+ | `readMindMap(json)` | 解析为 `MindMapModel`,无效内容返回 `null` |
143
+ | `mindMapToJson(model)` | 将模型序列化为 JSON |
144
+
145
+ 导图当前 `formatVersion` 为 `3`,支持受支持旧版的读取。主题和关系线拥有稳定 ID,支持主题文字、富文本、描述、备注、附件及样式;单图最多 500 个主题,单主题标题最多 2000 字符。
146
+
147
+ 持久化 JSON 数据;视口和选区属于展示状态。复制分支应重新生成身份,避免讨论锚点指向旧主题。
148
+
149
+ ## 导入入口
150
+
151
+ | 入口 | 内容 |
152
+ | --- | --- |
153
+ | `wendang` | 校验、版本、主要类型、工作簿和导图创建函数 |
154
+ | `wendang/schema` | 校验服务、异常类型、内容版本和节点白名单 |
155
+ | `wendang/document` | 文档类型、节点目录、文本工具和校验 |
156
+ | `wendang/spreadsheet` | 工作簿类型、创建、校验和文本提取 |
157
+ | `wendang/mindmap` | 导图模型、解析、序列化、布局、分支和搜索 |
158
+ | `wendang/types` | 文档、工作簿、导图及讨论类型 |
90
159
 
91
- ## 升级与扩展
160
+ 应用应通过以上入口导入,不引用 `dist` 内部文件。
92
161
 
93
- 1. SDK semver、文档 schemaVersion、工作簿 / 导图 formatVersion 分开管理。
94
- 2. 关闭新增按钮不能剥离已有内容;不支持的更高版本提示升级或只读保留。
95
- 3. 不把 HTML、截图或绘图坐标当作唯一源数据。
96
- 4. 核心不自动迁移宿主数据库;迁移必须有明确操作、样本和回退,失败保留原文。
97
- 5. 自定义节点必须同步编辑、阅读、校验和导出,不仅放宽前端 JSON。
98
- 6. ESM / CommonJS 通过实际 tarball 消费验证,禁止靠源码路径掩盖缺失导出。
162
+ ## 数据与升级规范
99
163
 
100
- ## 开发与发布
164
+ - npm 包版本、文档 `schemaVersion` 与工作簿 / 导图 `formatVersion` 分别管理。
165
+ - 保存时同时保存内容和对应格式版本;加载更高版本失败时保留原始数据。
166
+ - HTML、截图及渲染坐标不能替代 JSON 源数据。
167
+ - 自定义节点需同时支持编辑、阅读、校验和导出;只增加前端节点不会自动放宽后端校验。
168
+ - 数据库升级和历史数据迁移由应用控制。
101
169
 
102
- 在仓库根运行 `pnpm build`、`pnpm test`、`pnpm typecheck`、`pnpm pack:all`、`pnpm check:package`。只发布 dist、文档及许可文件。完整本地 link 和发布流程见仓库 README。
170
+ ## License
103
171
 
104
- 当前许可 `UNLICENSED`;维护者另行决定自身授权,不改变第三方依赖授权边界。
172
+ 采用随包附带的 [Wendang 商业使用许可](LICENSE):允许免费商业使用、应用集成修改和随应用分发运行代码,无需公开应用源码。独立 SDK 或源码产品的再发布、再许可及出售需书面授权。
@@ -1,19 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./schema.js"), exports);
18
- __exportStar(require("./shared/node-catalog.js"), exports);
19
- __exportStar(require("./shared/doc-text.js"), exports);
1
+ "use strict";var e=this&&this.__createBinding||(Object.create?function(e,r,t,o){void 0===o&&(o=t);var i=Object.getOwnPropertyDescriptor(r,t);i&&!("get"in i?!r.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return r[t]}}),Object.defineProperty(e,o,i)}:function(e,r,t,o){void 0===o&&(o=t),e[o]=r[t]}),r=this&&this.__exportStar||function(r,t){for(var o in r)"default"===o||Object.prototype.hasOwnProperty.call(t,o)||e(t,r,o)};Object.defineProperty(exports,"__esModule",{value:!0}),r(require("./schema.js"),exports),r(require("./shared/node-catalog.js"),exports),r(require("./shared/doc-text.js"),exports),r(require("./shared/headings.js"),exports);
package/dist/cjs/index.js CHANGED
@@ -1,25 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.mindMapToJson = exports.readMindMap = exports.createMindMap = exports.validateWorkbook = exports.emptyWorkbook = void 0;
18
- __exportStar(require("./schema.js"), exports);
19
- var spreadsheet_js_1 = require("./spreadsheet.js");
20
- Object.defineProperty(exports, "emptyWorkbook", { enumerable: true, get: function () { return spreadsheet_js_1.emptyWorkbook; } });
21
- Object.defineProperty(exports, "validateWorkbook", { enumerable: true, get: function () { return spreadsheet_js_1.validateWorkbook; } });
22
- var mindmap_js_1 = require("./mindmap.js");
23
- Object.defineProperty(exports, "createMindMap", { enumerable: true, get: function () { return mindmap_js_1.createMindMap; } });
24
- Object.defineProperty(exports, "readMindMap", { enumerable: true, get: function () { return mindmap_js_1.readMindMap; } });
25
- Object.defineProperty(exports, "mindMapToJson", { enumerable: true, get: function () { return mindmap_js_1.mindMapToJson; } });
1
+ "use strict";var e=this&&this.__createBinding||(Object.create?function(e,r,t,o){void 0===o&&(o=t);var n=Object.getOwnPropertyDescriptor(r,t);n&&!("get"in n?!r.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return r[t]}}),Object.defineProperty(e,o,n)}:function(e,r,t,o){void 0===o&&(o=t),e[o]=r[t]}),r=this&&this.__exportStar||function(r,t){for(var o in r)"default"===o||Object.prototype.hasOwnProperty.call(t,o)||e(t,r,o)};Object.defineProperty(exports,"__esModule",{value:!0}),exports.mindMapToJson=exports.readMindMap=exports.createMindMap=exports.validateWorkbook=exports.emptyWorkbook=void 0,r(require("./schema.js"),exports);var t=require("./spreadsheet.js");Object.defineProperty(exports,"emptyWorkbook",{enumerable:!0,get:function(){return t.emptyWorkbook}}),Object.defineProperty(exports,"validateWorkbook",{enumerable:!0,get:function(){return t.validateWorkbook}});var o=require("./mindmap.js");Object.defineProperty(exports,"createMindMap",{enumerable:!0,get:function(){return o.createMindMap}}),Object.defineProperty(exports,"readMindMap",{enumerable:!0,get:function(){return o.readMindMap}}),Object.defineProperty(exports,"mindMapToJson",{enumerable:!0,get:function(){return o.mindMapToJson}});
@@ -1,22 +1 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.validateMindMap = void 0;
18
- __exportStar(require("./shared/mind-map.js"), exports);
19
- __exportStar(require("./shared/mind-map-phase2.js"), exports);
20
- __exportStar(require("./shared/mind-map-folding.js"), exports);
21
- var mind_map_js_1 = require("./validation/mind-map.js");
22
- Object.defineProperty(exports, "validateMindMap", { enumerable: true, get: function () { return mind_map_js_1.validateMindMap; } });
1
+ "use strict";var e=this&&this.__createBinding||(Object.create?function(e,r,t,i){void 0===i&&(i=t);var a=Object.getOwnPropertyDescriptor(r,t);a&&!("get"in a?!r.__esModule:a.writable||a.configurable)||(a={enumerable:!0,get:function(){return r[t]}}),Object.defineProperty(e,i,a)}:function(e,r,t,i){void 0===i&&(i=t),e[i]=r[t]}),r=this&&this.__exportStar||function(r,t){for(var i in r)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||e(t,r,i)};Object.defineProperty(exports,"__esModule",{value:!0}),exports.validateMindMap=void 0,r(require("./shared/mind-map.js"),exports),r(require("./shared/mind-map-phase2.js"),exports),r(require("./shared/mind-map-folding.js"),exports);var t=require("./validation/mind-map.js");Object.defineProperty(exports,"validateMindMap",{enumerable:!0,get:function(){return t.validateMindMap}});
@@ -1,11 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ALLOWED_MARKS = exports.ALLOWED_NODES = exports.ContentValidationError = exports.SCHEMA_VERSION = exports.ContentSchemaService = void 0;
4
- var schema_js_1 = require("./validation/schema.js");
5
- Object.defineProperty(exports, "ContentSchemaService", { enumerable: true, get: function () { return schema_js_1.ContentSchemaService; } });
6
- Object.defineProperty(exports, "SCHEMA_VERSION", { enumerable: true, get: function () { return schema_js_1.SCHEMA_VERSION; } });
7
- var error_js_1 = require("./validation/error.js");
8
- Object.defineProperty(exports, "ContentValidationError", { enumerable: true, get: function () { return error_js_1.ContentValidationError; } });
9
- var doc_node_whitelist_js_1 = require("./validation/doc-node-whitelist.js");
10
- Object.defineProperty(exports, "ALLOWED_NODES", { enumerable: true, get: function () { return doc_node_whitelist_js_1.ALLOWED_NODES; } });
11
- Object.defineProperty(exports, "ALLOWED_MARKS", { enumerable: true, get: function () { return doc_node_whitelist_js_1.ALLOWED_MARKS; } });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ALLOWED_MARKS=exports.ALLOWED_NODES=exports.ContentValidationError=exports.SCHEMA_VERSION=exports.ContentSchemaService=void 0;var e=require("./validation/schema.js");Object.defineProperty(exports,"ContentSchemaService",{enumerable:!0,get:function(){return e.ContentSchemaService}}),Object.defineProperty(exports,"SCHEMA_VERSION",{enumerable:!0,get:function(){return e.SCHEMA_VERSION}});var r=require("./validation/error.js");Object.defineProperty(exports,"ContentValidationError",{enumerable:!0,get:function(){return r.ContentValidationError}});var t=require("./validation/doc-node-whitelist.js");Object.defineProperty(exports,"ALLOWED_NODES",{enumerable:!0,get:function(){return t.ALLOWED_NODES}}),Object.defineProperty(exports,"ALLOWED_MARKS",{enumerable:!0,get:function(){return t.ALLOWED_MARKS}});
@@ -1,201 +1 @@
1
- "use strict";
2
- /**
3
- * 行内评论的锚点定位(纯函数,可单测)。
4
- *
5
- * ## 为什么不能存位置
6
- *
7
- * 直觉做法是「记录选中文字的 start/end 偏移」。但文档会被编辑:
8
- * 在选区前面插入一个字,所有后续位置全部偏移——锚点立刻指向错误文字,
9
- * 而且**错得很安静**(评论跑到别的地方,没人会立刻发现)。
10
- *
11
- * ## 采用方案:文本引用(TextQuoteSelector 思路,W3C Web Annotation)
12
- *
13
- * 存三样东西:
14
- * - `quote`:选中的原文
15
- * - `prefix` / `suffix`:前后各一小段上下文(用于区分重复文本)
16
- *
17
- * 定位时在文档纯文本里搜索 quote;有多处命中时,用 prefix/suffix 的匹配长度打分,
18
- * 取最匹配的一处。这样:
19
- * - 在文档**别处**编辑(不碰该段文字)→ 锚点依然准确定位;
20
- * - 该段文字本身被改动 → 匹配不到,此时用 prefix+suffix 的接缝尽力定位,
21
- * 返回一个「降级」结果(exact=false),前端据此提示「原文已改动」;
22
- * - 完全找不到(整段被删)→ 返回 null,前端把评论归入「已失联」,
23
- * **而不是指向一处无关文字**。
24
- *
25
- * 宁可显示「已失联」,也不要指向错误位置——后者会让用户读到与上下文无关的评论,
26
- * 比看得见的缺失更有害。
27
- */
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.ANCHOR_QUOTE_MAX = exports.ANCHOR_CONTEXT_LENGTH = void 0;
30
- exports.flattenDocText = flattenDocText;
31
- exports.locateBlock = locateBlock;
32
- exports.resolveAnchor = resolveAnchor;
33
- exports.commonSuffixLength = commonSuffixLength;
34
- exports.commonPrefixLength = commonPrefixLength;
35
- exports.clipContext = clipContext;
36
- /** 上下文最大长度(与提交时截断长度一致)。 */
37
- exports.ANCHOR_CONTEXT_LENGTH = 64;
38
- exports.ANCHOR_QUOTE_MAX = 500;
39
- /**
40
- * 把若干块文本拼成扁平文本,记录片段边界。
41
- * 块之间插入换行(可用块间匹配不应跨越块边界,换行同时避免把块首块尾误连成词)。
42
- */
43
- function flattenDocText(blocks) {
44
- const segments = [];
45
- let text = '';
46
- blocks.forEach((blockText, blockIndex) => {
47
- if (blockIndex > 0)
48
- text += '\n';
49
- segments.push({ start: text.length, blockIndex, length: blockText.length });
50
- text += blockText;
51
- });
52
- return { text, segments };
53
- }
54
- /** 由扁平字符偏移定位到 (块索引, 块内偏移)。 */
55
- function locateBlock(flat, charOffset) {
56
- for (const seg of flat.segments) {
57
- if (charOffset >= seg.start && charOffset <= seg.start + seg.length) {
58
- return { blockIndex: seg.blockIndex, offsetInBlock: charOffset - seg.start };
59
- }
60
- }
61
- // 落在块间换行或末尾时,归到最后一个片段
62
- const last = flat.segments[flat.segments.length - 1];
63
- if (!last)
64
- return null;
65
- if (charOffset > last.start + last.length) {
66
- return { blockIndex: last.blockIndex, offsetInBlock: last.length };
67
- }
68
- return null;
69
- }
70
- /**
71
- * 定位锚点。返回 null 表示无法可信定位(整段被删),调用方应标记为「已失联」。
72
- */
73
- function resolveAnchor(text, anchor) {
74
- const quote = (anchor.quote ?? '').trim();
75
- if (quote.length === 0)
76
- return null;
77
- const prefix = anchor.prefix ?? '';
78
- const suffix = anchor.suffix ?? '';
79
- const expectedStart = anchor.start ?? -1;
80
- // 1) 收集 quote 的全部出现位置
81
- const candidates = [];
82
- let idx = text.indexOf(quote);
83
- while (idx !== -1) {
84
- candidates.push(idx);
85
- // 从下一个字符继续,避免重叠匹配导致死循环
86
- idx = text.indexOf(quote, idx + 1);
87
- }
88
- if (candidates.length > 0) {
89
- let best = null;
90
- for (const start of candidates) {
91
- const score = scoreCandidate(text, start, quote.length, prefix, suffix, expectedStart);
92
- if (!best || score > best.score)
93
- best = { start, score };
94
- }
95
- if (best) {
96
- return {
97
- from: best.start,
98
- to: best.start + quote.length,
99
- score: best.score,
100
- exact: true,
101
- };
102
- }
103
- }
104
- // 2) quote 已不在文档中:用上下文降级定位,返回零长度锚点。
105
- // 两种情况:
106
- // a) quote 整段被删除 → prefix 与 suffix 在文档中相邻,用接缝精确落点;
107
- // b) quote 被改写但上下文还在 → 用较长的一侧贴住,落在其边缘。
108
- // 两者都标记 exact=false,前端据此提示「原文已改动」。
109
- // 2a) prefix 与 suffix 相邻(说明中间的原引用被删掉了)
110
- if (prefix.length > 0 && suffix.length > 0) {
111
- const needle = prefix + suffix;
112
- const at = text.indexOf(needle);
113
- if (at !== -1) {
114
- const pos = at + prefix.length;
115
- return { from: pos, to: pos, score: 50, exact: false };
116
- }
117
- }
118
- // 2b) 用较长的一侧贴住边缘。
119
- // 注意两点(都是实测教训):
120
- // - 阈值取 4 而非 8:中文一个词常只有 2–4 字,阈值过高会让中文文档
121
- // 几乎无法降级定位;
122
- // - 必须遍历**全部**出现位置并取最接近原偏移的那个:
123
- // 短上下文在文档中可能重复,取首个出现会落到错误位置。
124
- const side = prefix.length >= suffix.length ? prefix : suffix;
125
- if (side.length >= 4) {
126
- const at = nearestOccurrence(text, side, expectedStart);
127
- if (at !== -1) {
128
- const pos = prefix.length >= suffix.length ? at + side.length : at;
129
- return { from: pos, to: pos, score: 10, exact: false };
130
- }
131
- }
132
- return null;
133
- }
134
- /**
135
- * 在文本中找出 needle 的全部出现位置,返回最接近 expected 的那个。
136
- * expected < 0(无提示)时退化为返回第一个出现位置。
137
- * 这样短上下文重复时不会盲目落到首次出现处。
138
- */
139
- function nearestOccurrence(text, needle, expected) {
140
- if (needle.length === 0)
141
- return -1;
142
- const positions = [];
143
- let idx = text.indexOf(needle);
144
- while (idx !== -1) {
145
- positions.push(idx);
146
- if (positions.length > 500)
147
- break; // 防御:极端重复文本
148
- idx = text.indexOf(needle, idx + 1);
149
- }
150
- if (positions.length === 0)
151
- return -1;
152
- if (expected < 0)
153
- return positions[0];
154
- let best = positions[0];
155
- for (const p of positions) {
156
- if (Math.abs(p - expected) < Math.abs(best - expected))
157
- best = p;
158
- }
159
- return best;
160
- }
161
- /**
162
- * 给候选位置打分:
163
- * - 引用本身匹配是基础分;
164
- * - 前后文匹配越长越可信(能区分「同一句话在文档里出现多次」);
165
- * - 与提交时偏移越近,略微加分(仅在文本重复且上下文缺失时起作用)。
166
- */
167
- function scoreCandidate(text, start, quoteLength, prefix, suffix, expectedStart) {
168
- let score = 1000;
169
- if (prefix.length > 0) {
170
- const actualPrefix = text.slice(Math.max(0, start - prefix.length), start);
171
- score += commonSuffixLength(actualPrefix, prefix) * 4;
172
- }
173
- if (suffix.length > 0) {
174
- const actualSuffix = text.slice(start + quoteLength, start + quoteLength + suffix.length);
175
- score += commonPrefixLength(actualSuffix, suffix) * 4;
176
- }
177
- if (expectedStart >= 0) {
178
- score -= Math.min(Math.abs(start - expectedStart), 200);
179
- }
180
- return score;
181
- }
182
- /** 两个字符串从末尾开始相同的字符数。 */
183
- function commonSuffixLength(a, b) {
184
- let n = 0;
185
- while (n < a.length && n < b.length && a[a.length - 1 - n] === b[b.length - 1 - n]) {
186
- n += 1;
187
- }
188
- return n;
189
- }
190
- /** 两个字符串从头开始相同的字符数。 */
191
- function commonPrefixLength(a, b) {
192
- let n = 0;
193
- while (n < a.length && n < b.length && a[n] === b[n]) {
194
- n += 1;
195
- }
196
- return n;
197
- }
198
- /** 截断上下文到限定长度(提交时使用)。 */
199
- function clipContext(text) {
200
- return text.slice(0, exports.ANCHOR_CONTEXT_LENGTH);
201
- }
1
+ "use strict";function scoreCandidate(t,e,n,o,r,l){let s=1e3;return o.length>0&&(s+=4*commonSuffixLength(t.slice(Math.max(0,e-o.length),e),o)),r.length>0&&(s+=4*commonPrefixLength(t.slice(e+n,e+n+r.length),r)),l>=0&&(s-=Math.min(Math.abs(e-l),200)),s}function commonSuffixLength(t,e){let n=0;for(;n<t.length&&n<e.length&&t[t.length-1-n]===e[e.length-1-n];)n+=1;return n}function commonPrefixLength(t,e){let n=0;for(;n<t.length&&n<e.length&&t[n]===e[n];)n+=1;return n}Object.defineProperty(exports,"__esModule",{value:!0}),exports.ANCHOR_QUOTE_MAX=exports.ANCHOR_CONTEXT_LENGTH=void 0,exports.flattenDocText=function flattenDocText(t){const e=[];let n="";return t.forEach((t,o)=>{o>0&&(n+="\n"),e.push({start:n.length,blockIndex:o,length:t.length}),n+=t}),{text:n,segments:e}},exports.locateBlock=function locateBlock(t,e){for(const n of t.segments)if(e>=n.start&&e<=n.start+n.length)return{blockIndex:n.blockIndex,offsetInBlock:e-n.start};const n=t.segments[t.segments.length-1];return n&&e>n.start+n.length?{blockIndex:n.blockIndex,offsetInBlock:n.length}:null},exports.resolveAnchor=function resolveAnchor(t,e){const n=(e.quote??"").trim();if(0===n.length)return null;const o=e.prefix??"",r=e.suffix??"",l=e.start??-1,s=[];let c=t.indexOf(n);for(;-1!==c;)s.push(c),c=t.indexOf(n,c+1);if(s.length>0){let e=null;for(const c of s){const s=scoreCandidate(t,c,n.length,o,r,l);(!e||s>e.score)&&(e={start:c,score:s})}if(e)return{from:e.start,to:e.start+n.length,score:e.score,exact:!0}}if(o.length>0&&r.length>0){const e=o+r,n=t.indexOf(e);if(-1!==n){const t=n+o.length;return{from:t,to:t,score:50,exact:!1}}}const f=o.length>=r.length?o:r;if(f.length>=4){const e=function nearestOccurrence(t,e,n){if(0===e.length)return-1;const o=[];let r=t.indexOf(e);for(;-1!==r&&(o.push(r),!(o.length>500));)r=t.indexOf(e,r+1);if(0===o.length)return-1;if(n<0)return o[0];let l=o[0];for(const t of o)Math.abs(t-n)<Math.abs(l-n)&&(l=t);return l}(t,f,l);if(-1!==e){const t=o.length>=r.length?e+f.length:e;return{from:t,to:t,score:10,exact:!1}}}return null},exports.commonSuffixLength=commonSuffixLength,exports.commonPrefixLength=commonPrefixLength,exports.clipContext=function clipContext(t){return t.slice(0,exports.ANCHOR_CONTEXT_LENGTH)},exports.ANCHOR_CONTEXT_LENGTH=64,exports.ANCHOR_QUOTE_MAX=500;