mdast-control 0.1.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 (45) hide show
  1. package/README.md +359 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +534 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.js +5 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/lsp/requests.d.ts +215 -0
  9. package/dist/lsp/requests.js +613 -0
  10. package/dist/lsp/requests.js.map +1 -0
  11. package/dist/lsp/server.d.ts +1 -0
  12. package/dist/lsp/server.js +110 -0
  13. package/dist/lsp/server.js.map +1 -0
  14. package/dist/lsp-methods.d.ts +25 -0
  15. package/dist/lsp-methods.js +26 -0
  16. package/dist/lsp-methods.js.map +1 -0
  17. package/dist/markdown.d.ts +17 -0
  18. package/dist/markdown.js +140 -0
  19. package/dist/markdown.js.map +1 -0
  20. package/dist/plugins.d.ts +144 -0
  21. package/dist/plugins.js +833 -0
  22. package/dist/plugins.js.map +1 -0
  23. package/dist/query.d.ts +8 -0
  24. package/dist/query.js +518 -0
  25. package/dist/query.js.map +1 -0
  26. package/dist/table-interchange.d.ts +22 -0
  27. package/dist/table-interchange.js +191 -0
  28. package/dist/table-interchange.js.map +1 -0
  29. package/dist/table-model.d.ts +15 -0
  30. package/dist/table-model.js +105 -0
  31. package/dist/table-model.js.map +1 -0
  32. package/dist/table-structural.d.ts +9 -0
  33. package/dist/table-structural.js +190 -0
  34. package/dist/table-structural.js.map +1 -0
  35. package/dist/table.d.ts +49 -0
  36. package/dist/table.js +131 -0
  37. package/dist/table.js.map +1 -0
  38. package/dist/transport.d.ts +6 -0
  39. package/dist/transport.js +7 -0
  40. package/dist/transport.js.map +1 -0
  41. package/dist/types.d.ts +57 -0
  42. package/dist/types.js +2 -0
  43. package/dist/types.js.map +1 -0
  44. package/package.json +60 -0
  45. package/scripts/verify-package.mjs +252 -0
package/README.md ADDED
@@ -0,0 +1,359 @@
1
+ # mdast-control
2
+
3
+ Markdown の AST 変換、Markdown 復元、独自 Query 言語による抽出と挿入、CLI と LSP をまとめた TypeScript 実装です。
4
+
5
+ 詳細仕様は [docs/specification.md](docs/specification.md) を参照してください。
6
+ 表操作と CSV/TSV 相互変換の設計は [docs/table-design.md](docs/table-design.md) を参照してください。
7
+ AST 要素プラグインと introspection の設計は [docs/plugin-architecture.md](docs/plugin-architecture.md) を参照してください。
8
+ MVP 受入境界は [docs/acceptance/mvp-baseline.md](docs/acceptance/mvp-baseline.md)、現在のローカル候補保証は [docs/acceptance/local-release-candidate.md](docs/acceptance/local-release-candidate.md)、確定した設計判断は [docs/adr/README.md](docs/adr/README.md) を参照してください。
9
+
10
+ ## インストール
11
+
12
+ ```bash
13
+ npm install mdast-control
14
+ ```
15
+
16
+ TypeScript / JavaScript API に加えて、`mdastctl` CLI と `mdast-lsp` language server が含まれます。
17
+
18
+ ## 機能
19
+
20
+ - Markdown から mdast 互換 AST への変換
21
+ - GFM table の parse / stringify
22
+ - GFM table の正規化モデル抽出と座標解決
23
+ - CSV/TSV の robust な export/import と table AST 生成
24
+ - table cell/row/column の構造編集と安定ソート
25
+ - AST JSON から Markdown への復元
26
+ - Query 言語によるノード抽出
27
+ - anchored glob と数値比較を含む Query predicate
28
+ - Query 結果に対する before/after/prepend/append 挿入
29
+ - Query 結果に対する置換と削除
30
+ - Query 結果に対する wrap、unwrap、move
31
+ - 明示的な wrapper placeholder、move target policy、非破壊 copy
32
+ - plugin registry と capability introspection
33
+ - table plugin の `updateCell` と `exportDelimited`
34
+ - codeBlock plugin の literal edit/extract と JSON validation/formatting
35
+ - table query/export/import/edit の CLI フロント
36
+ - LSP による Markdown 構造シンボル、Query 診断、table query/export/import/edit
37
+
38
+ ## Query 言語
39
+
40
+ 単純なセレクタ列を採用します。
41
+
42
+ - `heading`
43
+ - `root`
44
+ - `heading[depth=2]`
45
+ - `heading[depth=1,text*=Release]`
46
+ - `heading[text*=Release]`
47
+ - `heading[text^=Release]`
48
+ - `heading[text$=Notes]`
49
+ - `heading[text!=Changelog]`
50
+ - `heading[text~=Release*]`
51
+ - `heading[depth>=2]`
52
+ - `tableRow[rowIndex=0]`
53
+ - `tableCell[columnIndex=1,cellText=Open]`
54
+ - `tableCell[rowIndex=0,headerText=Status,cellText=Open]`
55
+ - `table[rowCount=2,columnCount=3]`
56
+ - `paragraph > text`
57
+ - `listItem text[value*=TODO]`
58
+
59
+ 対応演算子:
60
+
61
+ - `=`: 完全一致
62
+ - `*=`: 部分一致
63
+ - `^=`: 前方一致
64
+ - `$=`: 後方一致
65
+ - `!=`: 不一致
66
+ - `~=`: 文字列全体に対する glob 一致(`*` は 0 文字以上、`?` は 1 文字、`\` は escape)
67
+ - `>` / `>=` / `<` / `<=`: 有限数値フィールドと 10 進 operand の数値比較
68
+
69
+ 特別なフィールド:
70
+
71
+ - `text`: ノード配下の可視テキストを連結して比較
72
+ - `rowIndex`: `tableRow` と `tableCell` に対する body row の 0-based index
73
+ - `columnIndex`: `tableCell` に対する 0-based column index
74
+ - `headerText`: `tableCell` が属する列の header cell text
75
+ - `cellText`: `tableCell` の可視テキスト
76
+ - `rowCount`: `table` の header を除く body row 数
77
+ - `columnCount`: `table` の正規化後の列数
78
+
79
+ 子孫は空白、直下の子は `>` で指定します。
80
+
81
+ ## CLI
82
+
83
+ ```bash
84
+ npm install
85
+ npm run build
86
+
87
+ echo '# Title' | node dist/cli.js parse
88
+ echo '{"type":"root","children":[]}' | node dist/cli.js print
89
+ echo '# Title' | node dist/cli.js query --select 'heading[depth=1]'
90
+ echo '# Title' | node dist/cli.js insert --select 'heading[depth=1]' --position after --markdown 'Inserted text'
91
+ echo '# Title' | node dist/cli.js replace --select 'heading[depth=1]' --markdown 'New Title'
92
+ echo '# Title\n\nBody' | node dist/cli.js delete --select 'paragraph[text=Body]'
93
+ echo '# Title' | node dist/cli.js wrap --select 'heading' --markdown '> Quote'
94
+ echo '> Quote' | node dist/cli.js unwrap --select 'blockquote'
95
+ echo '# One\n\n# Two' | node dist/cli.js move --select 'heading[text=One]' --to 'heading[text=Two]' --position after
96
+ echo '# One\n\n# Target\n\n# Target' | node dist/cli.js move --select 'heading[text=One]' --to 'heading[text=Target]' --position after --target-policy last
97
+ echo '# One\n\n# Target\n\n# Target' | node dist/cli.js copy --select 'heading[text=One]' --to 'heading[text=Target]' --position after --target-policy first
98
+ node dist/cli.js capabilities --format json
99
+ node dist/cli.js plugin list
100
+ node dist/cli.js plugin show --plugin table --format json
101
+ node dist/cli.js plugin operation --plugin table --name updateCell --format json
102
+ printf '| Name | Value |\n| - | - |\n| A | 1 |\n' | node dist/cli.js plugin run --plugin table --name updateCell --select 'tableCell' --args '{"markdown":"Updated"}'
103
+ printf '| Name | Value |\n| - | - |\n| A | 1 |\n' | node dist/cli.js plugin run --plugin table --name exportDelimited --select 'table' --args '{"format":"csv"}'
104
+ printf '```json\n{"ok":true}\n```\n' | node dist/cli.js plugin run --plugin codeBlock --name validateContent --select 'code' --args '{"format":"json"}'
105
+ printf '```json\n{"ok":true}\n```\n' | node dist/cli.js plugin run --plugin codeBlock --name formatContent --select 'code' --args '{"format":"json","indent":2}'
106
+ printf '| Name | Value |\n| - | - |\n| A | 1 |\n' | node dist/cli.js table query --select table
107
+ printf '| Name | Note |\n| - | - |\n| A | x, y |\n' | node dist/cli.js table export --select table --format csv
108
+ printf 'Name,Value\nA,1\n' | node dist/cli.js table import --format csv
109
+ printf '| Old | Value |\n| - | - |\n| A | 1 |\n' | node dist/cli.js table import --format csv --replace table --data $'New,Value\nB,2'
110
+ printf '| Name | Status |\n| - | - |\n| A | Open |\n' | node dist/cli.js table cell set --select table --row 0 --column Status --value Closed
111
+ printf '| Name | Status |\n| - | - |\n| A | Open |\n' | node dist/cli.js table row insert --select table --index 1 --values '["B"]'
112
+ printf '| Name | Status |\n| - | - |\n| A | Open |\n' | node dist/cli.js table column add --select table --index 1 --header Owner --values '["Kai"]'
113
+ printf '| Name | Status |\n| - | - |\n| B | Open |\n| A | Closed |\n' | node dist/cli.js table sort --select table --column Name --direction asc
114
+ ```
115
+
116
+ `query` の出力は transport-safe な `{ path, node }` 配列です。
117
+
118
+ `print` と API の `jsonToAst` は、JSON の各 node に空でない文字列 `type` があり、存在する `children` が同じ node 形状の配列であることを検証します。その他の mdast 拡張プロパティは保持します。
119
+
120
+ `capabilities` は core の query operator / generic operation / custom LSP request と plugin 群の snapshot を返します。`plugin list`、`plugin show`、`plugin operation` で element plugin の metadata を introspection できます。
121
+ `plugin run` は stdin の Markdown を対象に plugin operation を実行します。built-in の table 操作は `updateCell` と `exportDelimited`、codeBlock 操作は `setLanguage`、`replaceContent`、`appendContent`、`extractContent`、`validateContent`、`formatContent` です。codeBlock の validation/formatting は明示的な `json` format だけを扱い、多言語 formatter を暗黙に選びません。
122
+
123
+ operation descriptor は `targetCardinality` を持ち、registry が引数、対象数、対象 node kind を handler の前で検証します。未知の引数は拒否されます。API の operation failure は `PluginOperationError` として `{ code, plugin, operation, message, details? }` を保持し、LSP は同じ shape を JSON-RPC error data に載せます。
124
+
125
+ `table query` と `table export` は exactly one の table を要求します。`table import` は単体変換では stdin を CSV/TSV として読みます。`--replace` または `--insert` を使う場合は stdin を Markdown とし、CSV/TSV は `--data` で明示します。import は plain-text cell を生成し、inline Markdown の書式は保持しません。
126
+
127
+ table 構造編集も exactly one の table を要求します。body row と column は 0-based、header 名は完全一致で重複時に失敗します。短い row/column 値は空 cell で補完し、暗黙の列追加になる過剰値は拒否します。cell set は plain text を書き込み、sort は既存の inline AST を保ったまま body row だけを安定ソートします。
128
+
129
+ `root` は Query 対象にできます。編集では `prepend` と `append` は有効ですが、`before`、`after`、`delete`、`replace` の対象にはできません。
130
+
131
+ `wrap` は従来どおり 1 個の top-level container node の末尾へ追加できます。wrapper Markdown に standalone block marker `<!-- mdast:content -->` をちょうど 1 個置くと、複数 top-level node または blockquote/list item 内のその位置へ matched node を配置できます。
132
+
133
+ `move` と `copy` は source query の入れ子一致をそのまま複製せず、外側の一致だけへ正規化します。target policy は後方互換の `exactly-one`(既定)、document order の `first`、`last` です。`move` は原本を削除し、`copy` は clone を挿入して原本を保ちます。
134
+
135
+ ## LSP
136
+
137
+ ```bash
138
+ node dist/lsp/server.js --stdio
139
+ ```
140
+
141
+ LSP は次を提供します。
142
+
143
+ - Markdown 文書の AST ベース document symbols
144
+ - ```mdast-query``` フェンス内 Query の構文診断
145
+ - カスタム request `mdast/query` による抽出
146
+ - カスタム request `mdast/capabilities` による capability snapshot の取得
147
+ - カスタム request `mdast/pluginList` による plugin 一覧の取得
148
+ - カスタム request `mdast/pluginShow` による plugin descriptor の取得
149
+ - カスタム request `mdast/elementCapabilities` による element kind 別 capability の取得
150
+ - カスタム request `mdast/pluginOperation` による operation descriptor の取得
151
+ - カスタム request `mdast/pluginRun` による plugin operation 実行
152
+ - カスタム request `mdast/insert` による Markdown 更新案の返却
153
+ - カスタム request `mdast/delete` による削除後 Markdown の返却
154
+ - カスタム request `mdast/replace` による置換後 Markdown の返却
155
+ - カスタム request `mdast/wrap` による wrap 後 Markdown の返却
156
+ - カスタム request `mdast/unwrap` による unwrap 後 Markdown の返却
157
+ - カスタム request `mdast/move` による move 後 Markdown の返却
158
+ - カスタム request `mdast/copy` による非破壊 copy 後 Markdown の返却
159
+ - カスタム request `mdast/tableQuery` による正規化 table model の取得
160
+ - カスタム request `mdast/tableExport` による CSV/TSV 取得
161
+ - カスタム request `mdast/tableImport` による table の置換または挿入案の返却
162
+ - カスタム request `mdast/tableUpdateCell`、`mdast/tableInsertRow`、`mdast/tableDeleteRow` による cell/row 編集案の返却
163
+ - カスタム request `mdast/tableAddColumn`、`mdast/tableDeleteColumn`、`mdast/tableSort` による column/順序編集案の返却
164
+
165
+ `mdast/query` request 例:
166
+
167
+ ```json
168
+ {
169
+ "uri": "file:///tmp/sample.md",
170
+ "query": "heading[depth=2]"
171
+ }
172
+ ```
173
+
174
+ `mdast/query` の応答も transport-safe な `{ path, node }` 配列です。
175
+
176
+ `mdast/capabilities` は core と plugin の capability snapshot を返します。`mdast/pluginShow` と `mdast/pluginOperation` は未解決の対象に対して `null` を返します。
177
+ `mdast/pluginRun` は plugin operation の結果を返します。mutation 系は `{ kind: "mutation", changed, markdown }`、text conversion は `{ kind: "text", text }`、structured analysis は `{ kind: "json", value }` を返します。
178
+
179
+ `mdast/insert` は `{ inserted, markdown }` を返します。`mdast/delete` と `mdast/replace` は `{ changed, markdown }` を返します。
180
+ `mdast/wrap`、`mdast/unwrap`、`mdast/move` も `{ changed, markdown }` を返します。
181
+ `mdast/move` と `mdast/copy` は optional `targetPolicy` として `exactly-one`、`first`、`last` を受け取ります。
182
+
183
+ `mdast/tableQuery` は正規化 model、`mdast/tableExport` は CSV/TSV text、table import/edit request は `{ changed, markdown }` を返します。table query/export/replacement/structural edit は exactly one の table、interchange insertion は exactly one の target を要求します。
184
+
185
+ 対象文書が LSP に未オープンの場合、`mdast/query` は空配列、table query/export は `null`、編集系 request は件数 0 と空 Markdown を返します。
186
+
187
+ payload が不正な場合、LSP custom request は invalid params エラーで失敗します。
188
+
189
+ `mdast/insert` request 例:
190
+
191
+ ```json
192
+ {
193
+ "uri": "file:///tmp/sample.md",
194
+ "query": "heading[depth=1]",
195
+ "position": "after",
196
+ "markdown": "Inserted paragraph"
197
+ }
198
+ ```
199
+
200
+ `mdast/delete` request 例:
201
+
202
+ ```json
203
+ {
204
+ "uri": "file:///tmp/sample.md",
205
+ "query": "paragraph[text=Body]"
206
+ }
207
+ ```
208
+
209
+ `mdast/wrap` request 例:
210
+
211
+ ```json
212
+ {
213
+ "uri": "file:///tmp/sample.md",
214
+ "query": "heading",
215
+ "markdown": "> Quote"
216
+ }
217
+ ```
218
+
219
+ `mdast/unwrap` request 例:
220
+
221
+ ```json
222
+ {
223
+ "uri": "file:///tmp/sample.md",
224
+ "query": "blockquote"
225
+ }
226
+ ```
227
+
228
+ `mdast/move` request 例:
229
+
230
+ ```json
231
+ {
232
+ "uri": "file:///tmp/sample.md",
233
+ "query": "heading[text=One]",
234
+ "target": "heading[text=Two]",
235
+ "position": "after"
236
+ }
237
+ ```
238
+
239
+ `mdast/replace` request 例:
240
+
241
+ ```json
242
+ {
243
+ "uri": "file:///tmp/sample.md",
244
+ "query": "heading[depth=1]",
245
+ "markdown": "## Replaced Title"
246
+ }
247
+ ```
248
+
249
+ `mdast/capabilities` response 例:
250
+
251
+ ```json
252
+ {
253
+ "core": {
254
+ "queryOperators": ["=", "*=", "^=", "$=", "!=", "~=", ">", ">=", "<", "<="],
255
+ "genericOperations": ["query", "insert", "delete", "replace", "wrap", "unwrap", "move", "copy"],
256
+ "customRequests": ["mdast/capabilities", "mdast/query", "mdast/copy", "mdast/tableQuery", "mdast/tableExport", "mdast/tableImport", "mdast/tableUpdateCell", "mdast/tableInsertRow", "mdast/tableDeleteRow", "mdast/tableAddColumn", "mdast/tableDeleteColumn", "mdast/tableSort"]
257
+ },
258
+ "plugins": [
259
+ {
260
+ "id": "table",
261
+ "availability": "available"
262
+ },
263
+ {
264
+ "id": "codeBlock",
265
+ "availability": "available"
266
+ }
267
+ ]
268
+ }
269
+ ```
270
+
271
+ `mdast/pluginRun` request 例:
272
+
273
+ ```json
274
+ {
275
+ "uri": "file:///tmp/sample.md",
276
+ "plugin": "table",
277
+ "operation": "exportDelimited",
278
+ "query": "table",
279
+ "args": {
280
+ "format": "csv"
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## 検証
286
+
287
+ ```bash
288
+ npm run check
289
+ npm run build
290
+ npm run test
291
+ npm run verify:package
292
+ ```
293
+
294
+ `verify:package` は一時領域へ tarball を作り、runtime entrypoint と compiled test の除外を確認した後、別の一時プロジェクトへ clean install して public API、CLI、LSP stdio を検証します。tarball や install tree はリポジトリ内に残しません。
295
+
296
+ ## API
297
+
298
+ ```ts
299
+ import {
300
+ copyMarkdown,
301
+ deleteMarkdown,
302
+ addTableColumn,
303
+ deleteTableColumn,
304
+ deleteTableRow,
305
+ extractTableModel,
306
+ csvToMarkdownTable,
307
+ getCapabilities,
308
+ getTableCell,
309
+ insertMarkdown,
310
+ insertTableRow,
311
+ listPlugins,
312
+ moveMarkdown,
313
+ parseMarkdown,
314
+ queryAst,
315
+ replaceMarkdown,
316
+ resolveTableColumnIndexes,
317
+ tableToCsv,
318
+ sortTable,
319
+ updateTableCell,
320
+ runPluginOperation,
321
+ stringifyAst,
322
+ unwrapMarkdown,
323
+ wrapMarkdown,
324
+ } from 'mdast-control';
325
+
326
+ const tree = parseMarkdown('# Title');
327
+ const matches = queryAst(tree, 'heading[depth=1]');
328
+ const updated = insertMarkdown('# Title', 'heading[depth=1]', 'after', 'Body');
329
+ const replaced = replaceMarkdown('# Title', 'heading[text=Title]', '## New Title');
330
+ const removed = deleteMarkdown('# Title\n\nBody', 'paragraph[text=Body]');
331
+ const wrapped = wrapMarkdown('# Title', 'heading', '> Quote');
332
+ const unwrapped = unwrapMarkdown('> Quote', 'blockquote');
333
+ const moved = moveMarkdown('# One\n\n# Two', 'heading[text=One]', 'heading[text=Two]', 'after');
334
+ const copied = copyMarkdown('# One\n\n# Target', 'heading[text=One]', 'heading[text=Target]', 'after');
335
+ const table = extractTableModel('| Name | Status |\n| - | - |\n| A | Open |', 'table');
336
+ const statusColumns = resolveTableColumnIndexes(table, 'Status');
337
+ const firstStatus = getTableCell(table, 0, statusColumns[0]);
338
+ const csv = tableToCsv('| Name | Status |\n| - | - |\n| A | Open |', 'table');
339
+ const importedTable = csvToMarkdownTable('Name,Status\nA,Open');
340
+ const editedCell = updateTableCell('| Name | Status |\n| - | - |\n| A | Open |', 'table', 0, 'Status', 'Closed');
341
+ const sortedTable = sortTable('| Name |\n| - |\n| B |\n| A |', 'table', 'Name', 'asc');
342
+ const capabilities = getCapabilities();
343
+ const plugins = listPlugins();
344
+ const exported = runPluginOperation({
345
+ plugin: 'table',
346
+ operation: 'exportDelimited',
347
+ markdown: '| Name | Value |\n| - | - |\n| A | 1 |\n',
348
+ query: 'table',
349
+ args: { format: 'csv' },
350
+ });
351
+ const formattedJson = runPluginOperation({
352
+ plugin: 'codeBlock',
353
+ operation: 'formatContent',
354
+ markdown: '```json\n{"ok":true}\n```\n',
355
+ query: 'code',
356
+ args: { format: 'json', indent: 2 },
357
+ });
358
+ console.log(stringifyAst(updated.tree));
359
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};