dexin-content 0.1.2 → 0.2.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/package.json CHANGED
@@ -1,69 +1,82 @@
1
- {
2
- "name": "dexin-content",
3
- "version": "0.1.2",
4
- "description": "Structured content compilation & runtime layer — neutral Markdown → Content AST → PositiveArtifact + store/query/collection pipeline.",
5
- "keywords": [
6
- "markdown",
7
- "content",
8
- "compiler",
9
- "ast",
10
- "cms",
11
- "remark",
12
- "dexinlabs"
13
- ],
14
- "type": "module",
15
- "license": "MIT",
16
- "author": "得心实验室 <https://gitee.com/cuizhn>",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://gitee.com/cuizhn/dexin-content.git"
20
- },
21
- "homepage": "https://gitee.com/cuizhn/dexin-content#readme",
22
- "bugs": {
23
- "url": "https://gitee.com/cuizhn/dexin-content/issues"
24
- },
25
- "engines": {
26
- "node": ">=20.0.0"
27
- },
28
- "files": [
29
- "index.ts",
30
- "collection.ts",
31
- "store.ts",
32
- "query.ts",
33
- "diff.ts",
34
- "core/**/*.ts",
35
- "README.md",
36
- "LICENSE"
37
- ],
38
- "exports": {
39
- ".": "./index.ts",
40
- "./core/types": "./core/types.ts",
41
- "./core/compiler": "./core/compiler.ts",
42
- "./core/frontmatter": "./core/frontmatter.ts",
43
- "./core/discovery": "./core/discovery.ts",
44
- "./core/markdown": "./core/markdown.ts",
45
- "./store": "./store.ts",
46
- "./collection": "./collection.ts",
47
- "./query": "./query.ts",
48
- "./diff": "./diff.ts"
49
- },
50
- "scripts": {
51
- "typecheck": "tsc --noEmit",
52
- "runtime:check": "tsx scripts/runtime-check.ts",
53
- "pack": "npm pack --dry-run"
54
- },
55
- "dependencies": {
56
- "yaml": "^2.5.1",
57
- "remark-parse": "^11.0.0",
58
- "remark-gfm": "^4.0.0",
59
- "remark-math": "^6.0.0",
60
- "remark-directive": "^3.0.0",
61
- "unified": "^11.0.0"
62
- },
63
- "devDependencies": {
64
- "@types/node": "^22.0.0",
65
- "@types/mdast": "^4.0.4",
66
- "typescript": "^5.5.0",
67
- "tsx": "^4.7.0"
68
- }
69
- }
1
+ {
2
+ "name": "dexin-content",
3
+ "version": "0.2.1",
4
+ "description": "Open content toolchain — Markdown → LessonAST/ContentPackage compiler, CLI and validator (dexinlabs ecosystem).",
5
+ "keywords": [
6
+ "markdown",
7
+ "content",
8
+ "compiler",
9
+ "ast",
10
+ "remark",
11
+ "dexinlabs"
12
+ ],
13
+ "type": "module",
14
+ "license": "Apache-2.0",
15
+ "author": "得心实验室 <https://gitee.com/cuizhn>",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://gitee.com/cuizhn/dexin-content.git"
19
+ },
20
+ "homepage": "https://gitee.com/cuizhn/dexin-content#readme",
21
+ "bugs": {
22
+ "url": "https://gitee.com/cuizhn/dexin-content/issues"
23
+ },
24
+ "engines": {
25
+ "node": ">=20.0.0"
26
+ },
27
+ "bin": {
28
+ "dexin-content": "./bin/dexin-content.mjs"
29
+ },
30
+ "files": [
31
+ "index.ts",
32
+ "core/**/*.ts",
33
+ "cli/**/*.ts",
34
+ "domains/**/*.ts",
35
+ "bin/**",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "exports": {
40
+ "./core/markdown": "./core/parser/markdown.ts",
41
+ ".": "./index.ts",
42
+ "./cli/validate": "./cli/validate.ts",
43
+ "./core/frontmatter": "./core/parser/frontmatter.ts",
44
+ "./diff": "./core/artifact/diff.ts",
45
+ "./cli/package": "./cli/package.ts",
46
+ "./collection": "./core/artifact/collection.ts",
47
+ "./cli/build": "./cli/build.ts",
48
+ "./query": "./core/artifact/query.ts",
49
+ "./core/compiler": "./core/compiler/compiler.ts",
50
+ "./core/types": "./core/types/index.ts",
51
+ "./store": "./core/artifact/store.ts",
52
+ "./cli/serve": "./cli/serve.ts",
53
+ "./cli/check": "./cli/check.ts",
54
+ "./core/discovery": "./core/compiler/discovery.ts",
55
+ "./core/types/lessonAST": "./core/types/lessonAST.ts",
56
+ "./domains/lesson": "./domains/lesson/index.ts",
57
+ "./domains/document": "./domains/document/index.ts"
58
+ },
59
+ "scripts": {
60
+ "typecheck": "tsc --noEmit",
61
+ "runtime:check": "tsx scripts/runtime-check.ts",
62
+ "pack": "npm pack --dry-run",
63
+ "build": "tsx cli/build.ts",
64
+ "package": "tsx cli/package.ts"
65
+ },
66
+ "dependencies": {
67
+ "tsx": "^4.23.0",
68
+ "yaml": "^2.5.1",
69
+ "remark-parse": "^11.0.0",
70
+ "remark-gfm": "^4.0.0",
71
+ "remark-math": "^6.0.0",
72
+ "remark-directive": "^3.0.0",
73
+ "unified": "^11.0.0",
74
+ "remark": "^15.0.0",
75
+ "micromark-extension-directive": "^3.0.0"
76
+ },
77
+ "devDependencies": {
78
+ "@types/node": "^22.0.0",
79
+ "@types/mdast": "^4.0.4",
80
+ "typescript": "^5.5.0"
81
+ }
82
+ }
package/core/discovery.ts DELETED
@@ -1,110 +0,0 @@
1
- // ─────────────────────────────────────────────────────────────
2
- // dexin-content/core/discovery.ts
3
- // Source adapter + collection routing + identity derivation helpers.
4
- //
5
- // Document path convention → DocumentIdentity:
6
- // id = relPath minus .md, minus trailing `/index`
7
- // path = '/' + id
8
- // file = relPath WITH .md extension
9
- // + auxiliary fields: collection, (optionally) index_file:true
10
- //
11
- // Hosts needing domain-specific identity shapes define their own types
12
- // and construct CompileInput identities themselves; core only knows the
13
- // document convention.
14
- // ─────────────────────────────────────────────────────────────
15
-
16
- import path from 'node:path'
17
- import { readFileSync, existsSync, statSync } from 'node:fs'
18
- import type { DocumentIdentity, ParseError } from './types'
19
-
20
- // ── Shared BOM / CRLF sanitisation primitives ─────────────
21
- // Used by both sync (readSourceFile) and async (createLocalSource.read)
22
- // IO entry points to guarantee byte-identical failure behaviour.
23
-
24
- /**
25
- * Build a LINE_ENDING_CONTAMINATION ParseError for a given relative path.
26
- * Caller supplies a short remediation hint matching its context.
27
- */
28
- export function mkLineEndingError (
29
- relPath: string,
30
- hint: string = 'Normalize to LF before processing.'
31
- ): ParseError & Error {
32
- const err = new Error(
33
- `[LINE_ENDING_CONTAMINATION] Source file '${relPath}' contains CRLF line endings. ` +
34
- hint
35
- ) as ParseError & Error
36
- err.code = 'LINE_ENDING_CONTAMINATION'
37
- err.file = relPath
38
- return err
39
- }
40
-
41
- /**
42
- * Strip a leading UTF-8 BOM (U+FEFF) if present and guard against CRLF
43
- * contamination. Returns the cleaned source string; throws with
44
- * LINE_ENDING_CONTAMINATION code on CR presence.
45
- *
46
- * This is intentionally a pure string-in / string-out (or throw) helper so
47
- * both sync and async IO paths share one canonical sanitisation.
48
- */
49
- export function sanitizeSourceText (raw: string, relPath: string): string {
50
- const source = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw
51
- if (source.includes('\r')) throw mkLineEndingError(relPath)
52
- return source
53
- }
54
-
55
- /**
56
- * Normalise a relative path to forward slashes and strip any leading `./`.
57
- */
58
- export function normaliseRel (p: string): string {
59
- let out = p.split(path.sep).join('/')
60
- while (out.startsWith('./')) out = out.slice(2)
61
- return out
62
- }
63
-
64
- /**
65
- * Build DocumentIdentity from a `.md` file path relative
66
- * to the collection source root.
67
- */
68
- export function buildDocumentIdentity (
69
- relPath: string,
70
- collection: string
71
- ): DocumentIdentity {
72
- const norm = normaliseRel(relPath)
73
- const file = norm
74
-
75
- let id = file.endsWith('.md') ? file.slice(0, -'.md'.length) : file
76
- if (id.endsWith('.markdown')) id = id.slice(0, -'.markdown'.length)
77
-
78
- const isIndex = id.endsWith('/index') || id === 'index'
79
- if (isIndex) {
80
- if (id === 'index') id = ''
81
- else id = id.slice(0, -'/index'.length)
82
- }
83
-
84
- const pathUrl = '/' + id
85
- const identity: DocumentIdentity = {
86
- id,
87
- path: pathUrl,
88
- file,
89
- collection
90
- }
91
- if (isIndex) identity.index_file = true
92
- return identity
93
- }
94
-
95
- /** Read a single UTF-8 file and pair it with its relative path. */
96
- export interface SourceFile {
97
- absPath: string
98
- relPath: string
99
- source: string
100
- }
101
-
102
- export function readSourceFile (absPath: string, sourceRoot: string): SourceFile {
103
- if (!existsSync(absPath) || !statSync(absPath).isFile()) {
104
- throw new Error(`[discovery] Not a file or missing: ${absPath}`)
105
- }
106
- const raw = readFileSync(absPath, 'utf8')
107
- const rel = normaliseRel(path.relative(sourceRoot, absPath))
108
- const source = sanitizeSourceText(raw, rel)
109
- return { absPath, relPath: rel, source }
110
- }
package/core/types.ts DELETED
@@ -1,162 +0,0 @@
1
- // ─────────────────────────────────────────────────────────────
2
- // dexin-content/core/types.ts — Generic shared types.
3
- // ─────────────────────────────────────────────────────────────
4
- // This file defines the shared type system used across domains.
5
- // It MUST remain business-agnostic. All names, literals, and
6
- // comment text are written in generic, domain-neutral vocabulary.
7
-
8
- // ── Inline content (shared across the neutral AST and all domains) ──
9
-
10
- export type Inline =
11
- | TextInline
12
- | BoldInline
13
- | ItalicInline
14
- | CodeInline
15
- | LinkInline
16
- | FormulaInline
17
-
18
- export interface TextInline { type: 'text'; value: string }
19
- export interface BoldInline { type: 'bold'; children: Inline[] }
20
- export interface ItalicInline { type: 'italic'; children: Inline[] }
21
- export interface CodeInline { type: 'code'; value: string }
22
- export interface LinkInline { type: 'link'; url: string; children: Inline[] }
23
- /** Inline LaTeX-format formula span (single-paragraph). */
24
- export interface FormulaInline { type: 'math'; latex: string }
25
-
26
- // ── Document-level blocks (neutral AST — no domain mapping) ──
27
-
28
- /**
29
- * HeadingBlock in the neutral AST: level is clamped to 1–4
30
- * (h1→1, h2→2, h3→3, h4→4). `mdDepth` is the ORIGINAL markdown heading
31
- * depth (1..6 inclusive) captured at parse time. It is OPTIONAL and exists
32
- * ONLY so active domain parsers can run their own fail-fast checks for
33
- * out-of-range depths. `mdDepth` MUST NOT leak into the serialised
34
- * canonical Artifact (the `content` output): domain parsers' clone/render
35
- * paths must drop it.
36
- */
37
- export interface HeadingBlock {
38
- type: 'heading'
39
- level: 1 | 2 | 3 | 4
40
- children: Inline[]
41
- /** Original markdown heading depth (1..6). Transient, non-canonical. */
42
- mdDepth?: number
43
- }
44
- export interface ParagraphBlock { type: 'paragraph'; children: Inline[] }
45
- export interface QuoteBlock { type: 'quote'; children: DocumentBlock[] }
46
- export interface DividerBlock { type: 'divider' }
47
- export interface ListBlock { type: 'list'; ordered: boolean; items: Inline[][] }
48
- export type TableCell = Inline[]
49
- export interface TableBlock { type: 'table'; headers: TableCell[]; rows: TableCell[][] }
50
- export interface ImageBlock { type: 'image'; src: string; alt: string; caption?: string }
51
- export interface CodeBlock { type: 'code'; language: string; code: string }
52
- /** Block-level LaTeX-format formula node; display=true for centered fences. */
53
- export interface FormulaBlock { type: 'formula'; latex: string; display: boolean }
54
-
55
- /**
56
- * Neutral ContainerBlock:
57
- * Produced for every directive container encountered in the markdown body.
58
- * The active domain decides whether an unknown name is acceptable;
59
- * the generic layer always preserves the structure as-is.
60
- */
61
- export interface ContainerBlock {
62
- type: 'container'
63
- name: string
64
- attrs: Record<string, string>
65
- children: DocumentBlock[]
66
- }
67
-
68
- export type DocumentBlock =
69
- | ParagraphBlock
70
- | HeadingBlock
71
- | QuoteBlock
72
- | DividerBlock
73
- | ListBlock
74
- | TableBlock
75
- | ImageBlock
76
- | CodeBlock
77
- | FormulaBlock
78
- | ContainerBlock
79
-
80
- export interface DocumentContent {
81
- version: 1
82
- blocks: DocumentBlock[]
83
- }
84
-
85
- // ── Artifact shapes (shared five-field header) ──
86
-
87
- /**
88
- * Generic domain tag. Literal string values are chosen by the host runner
89
- * and flow through to the Artifact output untouched. Core itself does not
90
- * interpret them beyond registry lookup keys.
91
- */
92
- export type DomainName = string & { readonly __brand?: unique symbol }
93
-
94
- /**
95
- * Plain-document identity:
96
- * id — relative to content root; strip `.md` and trailing `/index`
97
- * path — URL semantics: `'/' + id`
98
- * file — physical path relative to content root, INCLUDING `.md` extension
99
- *
100
- * Domain-specific routing fields may be attached by hosts via the index
101
- * signature; core never interprets them.
102
- */
103
- export interface DocumentIdentity {
104
- id: string
105
- path: string // URL semantic: '/' + id
106
- file: string // physical path relative to content root WITH ext
107
- collection: string
108
- [k: string]: unknown // auxiliary fields (e.g. index_file:true)
109
- }
110
-
111
- /** Frontmatter scalar-only projection (SCHEMA-FREE RULE) */
112
- export type Meta = Record<string, string | number | boolean>
113
-
114
- export interface ArtifactHeader {
115
- id: string // Stable document id (unique within a store)
116
- domain: DomainName | string
117
- identity: DocumentIdentity
118
- meta: Meta
119
- }
120
-
121
- export interface PositiveArtifact extends ArtifactHeader {
122
- content: { version: 1; blocks: unknown[] }
123
- }
124
-
125
- /** Compile-failure output — captures first fail-fast error. */
126
- export interface ErrorArtifact extends ArtifactHeader {
127
- kind: 'COMPILE_ERROR'
128
- error: { code: string; message: string }
129
- }
130
-
131
- export type Artifact = PositiveArtifact | ErrorArtifact
132
-
133
- // ── Unified error object ──
134
-
135
- export interface ParseError extends Error {
136
- code: string
137
- message: string
138
- file: string
139
- loc?: { container?: string; headingDepth?: number; invalidEnumValue?: string; allowed?: string }
140
- }
141
-
142
- // ── Parser contract — domains implement; core dispatches ──
143
-
144
- export interface ParseContext {
145
- /** Relative path to the source file, used in error messages. */
146
- file: string
147
- /** Collection-level schema (required fields + scalar type checks). */
148
- schema?: Schema
149
- /** Frontmatter already projected as scalar-only meta. */
150
- meta: Meta
151
- }
152
-
153
- export interface Schema {
154
- required?: string[]
155
- types?: Record<string, 'string' | 'number' | 'boolean'>
156
- }
157
-
158
- export interface DomainParser {
159
- domain: DomainName | string
160
- /** Convert neutral AST → domain-specific Artifact content.blocks shape. May throw ParseError. */
161
- parse(content: DocumentContent, ctx: ParseContext): { version: 1; blocks: unknown[] }
162
- }