dexin-content 0.2.1 → 0.3.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.
package/README.md CHANGED
@@ -1,112 +1,120 @@
1
- # dexin-content
2
-
3
- 得心实验室的**结构化内容编译与运行时 package**。
4
-
5
- 负责与具体业务领域无关的通用内容管线:
6
-
7
- - **Compile**:将带 frontmatter 的 Markdown 编译成中性 Content AST,再经宿主注入的 `DomainParser` 产出稳定的 PositiveArtifact
8
- - **Store**:Artifact 与索引(ContentIndex)的 FS/Memory 抽象
9
- - **Query**:按 id / URL path / collection / 条件过滤对 Artifact 做只读查询
10
- - **Collection**:集合声明、发现、批量编译与增量变化检测
11
- - **Diff**:Canonical JSON 序列化与对象级差异原语(Golden/Regress 管线使用)
12
-
13
- ## 目录范围
14
-
15
- dexin-content 是**通用层**:
16
- - `core/` 编译核心(types / compiler / markdown / frontmatter / discovery)
17
- - 根层 `collection.ts`、`store.ts`、`query.ts`、`diff.ts` 是稳定能力
18
- - `scripts/` 仓内脚本(不在发布包 files 内,仅供本地开发/测试;也不作为公开 API)
19
-
20
- 宿主应用负责域扩展(DomainParser)、编排脚本与内容文件所有权,通过 `exports` 里的子路径消费本 package。
21
-
22
- ## 安装
23
-
24
- > 需要 Node.js ≥ 20。Package 以 TypeScript 源码形式分发(`*.ts`),宿主应通过 `tsx`、Nuxt/Vite 等支持 TS 解析的工具链使用。
25
-
26
- ```bash
27
- # Gitee 源(当前发布前)
28
- npm install git+https://gitee.com/cuizhn/dexin-content.git
29
-
30
- # 发布到 npm 后
31
- npm install dexin-content
32
- ```
33
-
34
- ## 公开 API 边界
35
-
36
- 唯一公共 API 边界是 `package.json` 中 `exports` 字段。**只使用下列路径**:
37
-
38
- | 子路径 | 暴露内容 |
39
- |---|---|
40
- | `.` | 门面:核心类型 + `DomainParserRegistry` / `compile` + `ArtifactStore` / `ContentQuery` + `defineCollection / resolveCollections / discover / compileCollections` |
41
- | `./core/types` | 类型:`Inline` / `DocumentBlock` / `DocumentContent` / `DocumentIdentity` / `PositiveArtifact` / `Artifact` / `Meta` / `ParseError` / `DomainParser` / `Schema` / `ParseContext` |
42
- | `./core/compiler` | `compile`、`CompileInput`、`CompileResult`、`DomainParserRegistry` |
43
- | `./core/discovery` | `buildDocumentIdentity` / `normaliseRel` / `readSourceFile` / `sanitizeSourceText` / `mkLineEndingError` |
44
- | `./core/frontmatter` | `splitFrontmatter` / `parseFrontmatter` / `validateSchema` / `projectMeta` |
45
- | `./store` | `ArtifactStore` 接口 + `createFsArtifactStore` / `createMemoryArtifactStore` + `ContentIndex` / `IndexEntry` |
46
- | `./collection` | `defineCollection` / `resolveCollections` / `compileCollections` / `recompileChanged` / `createLocalSource` / `createMemorySource` / `SourceAdapter` / `ResolvedCollection` / `CollectionDefinition` |
47
- | `./query` | `ContentQuery` (byId / byPath / list / collection) + `QueryOptions` |
48
- | `./diff` | `toCanonicalJSON` / `sortKeysDeep` / `stripUnderscoreKeysGolden` / `underscorePrefixedPaths` / `firstDiff` / `shortStr` |
49
-
50
- 任何未列入上表的内部路径均不保证稳定。
51
-
52
- ## 快速示例
53
-
54
- ```ts
55
- import {
56
- defineCollection,
57
- resolveCollections,
58
- createLocalSource,
59
- compileCollections,
60
- } from 'dexin-content/collection'
61
- import { createFsArtifactStore } from 'dexin-content/store'
62
- import { ContentQuery } from 'dexin-content/query'
63
- import { DomainParserRegistry } from 'dexin-content/core/compiler'
64
- import type {
65
- DomainParser,
66
- DocumentContent,
67
- ParseContext,
68
- } from 'dexin-content/core/types'
69
-
70
- // 1) 注入你的域 parser;parser 决定中性 AST 如何映射到目标 Artifact 内容结构
71
- const documentParser: DomainParser = {
72
- domain: 'document',
73
- parse(content: DocumentContent, _ctx: ParseContext) {
74
- return { version: 1, blocks: content.blocks as unknown[] }
75
- },
76
- }
77
- const registry = new DomainParserRegistry()
78
- registry.register(documentParser)
79
-
80
- // 2) 声明集合 + 编译
81
- const collections = resolveCollections(
82
- [defineCollection({ name: 'legal', source: 'legal', domain: 'document' })],
83
- '/path/to/content/root',
84
- )
85
- const source = createLocalSource('/path/to/content/root')
86
- const store = createFsArtifactStore('/path/to/output/dexin-content')
87
- await compileCollections(source, collections, registry, store)
88
-
89
- // 3) 运行时只读查询(不解析任何 Markdown)
90
- const query = new ContentQuery(store)
91
- const entry = await query.byPath('/legal/privacy')
92
- ```
93
-
94
- ## 开发
95
-
96
- ```bash
97
- # 依赖
98
- npm install
99
-
100
- # 类型检查(唯一静态门禁)
101
- npm run typecheck
102
-
103
- # 仓内自检测(需先 build 输出 output/dexin-content/)
104
- npm run runtime:check
105
-
106
- # 预发布 tarball 内容检查
107
- npm run pack
108
- ```
109
-
110
- ## License
111
-
112
- MIT © 得心实验室。详见 [LICENSE](./LICENSE)。
1
+ # dexin-content
2
+
3
+ 得心实验室的**结构化内容编译与运行时 package**。
4
+
5
+ 负责与具体业务领域无关的通用内容管线:
6
+
7
+ - **Compile**:将带 frontmatter 的 Markdown 编译成中性 Content AST,再经宿主注入的 `DomainParser` 产出稳定的 PositiveArtifact
8
+ - **Store**:Artifact 与索引(ContentIndex)的 FS/Memory 抽象
9
+ - **Query**:按 id / URL path / collection / 条件过滤对 Artifact 做只读查询
10
+ - **Collection**:集合声明、发现、批量编译与增量变化检测
11
+ - **Diff**:Canonical JSON 序列化与对象级差异原语(Golden/Regress 管线使用)
12
+
13
+ ## 为内容作者
14
+
15
+ - **格式规范**:[docs/content-format.md](./docs/content-format.md) —— 目录结构、front-matter、块/容器/数学语法、错误码、自检命令、投稿 SOP(§14)
16
+ - **示例课**:[example/](./example/README.md) —— 可直接编译通过的最小内容仓样例
17
+ - **社区投稿**:[proposals/](./proposals/README.md) —— 课程稿件投放区,PR + 门禁校验(`npm run validate:proposals`)+ 人审后迁入私有成品仓
18
+
19
+ ## 目录范围
20
+
21
+ dexin-content 是**通用层**:
22
+ - `core/` 编译核心(types / compiler / parser / artifact)
23
+ - `domains/lesson/` 内置 lesson 域 parser(其余域由宿主自行注入)
24
+ - `cli/` flow 编排(`flowBuild` / `flowPackage` / `flowValidate` / `flowCheck` / `flowServe` 库 API;**CLI 子命令仅有 `build` 与 `help`**,其余 flow 经子路径作库消费)
25
+ - `scripts/` 仓内脚本(不在发布包 files 内,仅供本地开发/测试;也不作为公开 API)
26
+
27
+ 宿主应用负责域扩展(DomainParser)、编排脚本与内容文件所有权,通过 `exports` 里的子路径消费本 package。
28
+
29
+ ## 安装
30
+
31
+ > 需要 Node.js ≥ 20。Package 以 TypeScript 源码形式分发(`*.ts`),宿主应通过 `tsx`、Nuxt/Vite 等支持 TS 解析的工具链使用。
32
+
33
+ ```bash
34
+ npm install dexin-content
35
+ ```
36
+
37
+ ## 公开 API 边界
38
+
39
+ 唯一公共 API 边界是 `package.json` 中 `exports` 字段。**只使用下列路径**:
40
+
41
+ | 子路径 | 暴露内容 |
42
+ |---|---|
43
+ | `.` | 门面:核心类型 + `DomainParserRegistry` / `compile` + `ArtifactStore` / `ContentQuery` + `defineCollection / resolveCollections / discover / compileCollections` |
44
+ | `./core/types` | 类型:`Inline` 家族(`TextInline` / `BoldInline` / `ItalicInline` / `CodeInline` / `LinkInline` / `FormulaInline`) / `Identity` / `DocumentIdentity` / `StructuredIdentity` / `Meta` / `PositiveArtifact` / `Artifact` / `ParseError` / `ParseContext` / `Schema` / `DomainParser` / `DomainName`(LessonAST 块类型在 `./core/types/lessonAST`) |
45
+ | `./core/compiler` | `compile`、`CompileInput`、`CompileResult`、`DomainParserRegistry` |
46
+ | `./core/discovery` | `buildDocumentIdentity` / `buildStructuredIdentity` / `normaliseRel` / `readSourceFile` / `IdentityKind` / `CollectionConfig` / `SourceFile` |
47
+ | `./core/frontmatter` | `splitFrontmatter` / `parseFrontmatter` / `validateSchema` / `projectMeta` |
48
+ | `./store` | `ArtifactStore` 接口 + `createFsArtifactStore` / `createMemoryArtifactStore` + `ContentIndex` / `IndexEntry` |
49
+ | `./collection` | `defineCollection` / `resolveCollections` / `compileCollections` / `recompileChanged` / `createLocalSource` / `createMemorySource` / `SourceAdapter` / `ResolvedCollection` / `CollectionDefinition` |
50
+ | `./query` | `ContentQuery` (byId / byPath / list / collection) + `QueryOptions` |
51
+ | `./diff` | `toCanonicalJSON` / `sortKeysDeep` / `stripUnderscoreKeysGolden` / `underscorePrefixedPaths` / `firstDiff` / `shortStr` |
52
+ | `./core/markdown` | Markdown → 中性 AST 解析(`parseDocument` 等) |
53
+ | `./core/types/lessonAST` | LessonAST 类型与常量 |
54
+ | `./domains/lesson` | 内置 lesson 域:`lessonParser` / `buildLessonIdentity` / `mapBlocks` |
55
+ | `./cli/build` | `flowBuild`(目录发现 → compile → Artifact Store + index.json) |
56
+ | `./cli/package` | `flowPackage`(按 manifest 打包 ContentPackage) |
57
+ | `./cli/validate` | `flowValidate` |
58
+ | `./cli/check` | `flowCheck` |
59
+ | `./cli/serve` | `flowServe`(dev preview HTTP 服务) |
60
+
61
+ 任何未列入上表的内部路径均不保证稳定。
62
+
63
+ ## 快速示例
64
+
65
+ 最简用法是走 CLI(仅编译内置 lesson 域):
66
+
67
+ ```bash
68
+ # 单文件:AST 打印到 stdout,失败非零退出
69
+ npx dexin-content build example/lessons/getting-started/hello-lesson/minimal.md --domain lesson
70
+
71
+ # 整树:编译 + 按 manifest 打包 ContentPackage
72
+ npx dexin-content build example --manifest example/content-manifest.json --out /tmp/dexin-out
73
+ ```
74
+
75
+ 库 API(宿主自行编排时)用内置 `lessonParser` 经 `DomainParserRegistry` 注入:
76
+
77
+ ```ts
78
+ import { DomainParserRegistry, compile } from 'dexin-content/core/compiler'
79
+ import { lessonParser } from 'dexin-content/domains/lesson'
80
+ import { buildLessonIdentity } from 'dexin-content/domains/lesson'
81
+
82
+ const registry = new DomainParserRegistry()
83
+ registry.register(lessonParser)
84
+
85
+ const result = compile(
86
+ {
87
+ fixture: 'getting-started/hello-lesson/minimal',
88
+ domain: 'lesson',
89
+ identity: buildLessonIdentity('minimal', 'getting-started', 'hello-lesson', 'lessons/getting-started/hello-lesson/minimal.md'),
90
+ source: '---\ntitle: 最小的一节课\norder: 1\n---\n\n## 这就是全部了\n',
91
+ file: 'lessons/getting-started/hello-lesson/minimal.md',
92
+ },
93
+ registry,
94
+ )
95
+
96
+ if (result.kind === 'error') throw result.error
97
+ else console.log(result.artifact.content.blocks)
98
+ ```
99
+
100
+ `compile()` 永不抛异常,失败以 `{ kind: 'error', error }` 返回。域扩展由宿主注入其它 `DomainParser`。
101
+
102
+ ## 开发
103
+
104
+ ```bash
105
+ # 依赖
106
+ npm install
107
+
108
+ # 类型检查(唯一静态门禁)
109
+ npm run typecheck
110
+
111
+ # 仓内自检测(脚本自建临时 fixture,无需任何本地产物)
112
+ npm run runtime:check
113
+
114
+ # 预发布 tarball 内容检查
115
+ npm run pack
116
+ ```
117
+
118
+ ## License
119
+
120
+ Apache-2.0 © 得心实验室。详见 [LICENSE](./LICENSE)。
package/cli/build.ts CHANGED
@@ -157,7 +157,7 @@ async function discoverAndCompileDocuments(
157
157
  }
158
158
 
159
159
  async function writeArtifact(outDir: string, artifact: PositiveArtifact): Promise<void> {
160
- const outFile = path.join(outDir, 'lessons', artifact.fixture + '.json')
160
+ const outFile = path.join(outDir, 'content', artifact.fixture + '.json')
161
161
  await mkdir(path.dirname(outFile), { recursive: true })
162
162
  await writeFile(outFile, JSON.stringify(artifact, null, 2) + '\n', 'utf-8')
163
163
  }
@@ -183,6 +183,6 @@ async function walkMdFiles(dir: string): Promise<string[]> {
183
183
  }
184
184
  return out.sort()
185
185
  }
186
-
187
-
188
-
186
+
187
+
188
+