dsh-ensp 0.7.0 → 0.8.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.
@@ -1,103 +0,0 @@
1
- /**
2
- * Curriculum knowledge attached to scenarios.
3
- *
4
- * The scenario presets say what to build and in what order. They deliberately do
5
- * not carry commands, because the whole point of the tool is to catch a copied
6
- * command that does not fit. What they were missing is the vocabulary the lab is
7
- * teaching: which LSA belongs to which area, which attribute wins a BGP decision,
8
- * which port role only MSTP has. Short, checkable statements about those terms
9
- * are what a learner needs before touching a device.
10
- *
11
- * This module reads a small generated index: one topic, the scenarios that teach
12
- * it, and a list of short question/answer pairs. Shipping only the index keeps the
13
- * package small and keeps third-party source material out of a GPL distribution,
14
- * while `npm run knowledge` still makes every entry reproducible.
15
- *
16
- * @module dsh-ensp/knowledge
17
- */
18
-
19
- import { readFileSync } from 'node:fs'
20
-
21
- let cache = null
22
-
23
- /**
24
- * Load the generated index, once.
25
- *
26
- * Read through `new URL` so the path resolves from this module rather than from
27
- * whatever directory the caller happens to run in.
28
- *
29
- * @returns {object}
30
- */
31
- function catalogue() {
32
- if (cache) return cache
33
- const text = readFileSync(new URL('./knowledge.json', import.meta.url), 'utf8')
34
- cache = JSON.parse(text)
35
- return cache
36
- }
37
-
38
- /** @returns {string[]} the topics the index covers. */
39
- export function knowledgeTopics() {
40
- return Object.keys(catalogue().topics).sort()
41
- }
42
-
43
- /**
44
- * Knowledge points for one scenario.
45
- *
46
- * A topic can feed several scenarios: `ospf` covers six of them, and each gets the
47
- * same points. That repetition is intentional. A learner running the multi-area
48
- * lab should not have to know that the material also sits under another scene.
49
- *
50
- * @param {string} scenarioId
51
- * @returns {Array<{topic: string, n: number, q: string, a: string}>}
52
- */
53
- export function knowledgeFor(scenarioId) {
54
- const out = []
55
- for (const [topic, entry] of Object.entries(catalogue().topics)) {
56
- if (!entry.scenarios?.includes(scenarioId)) continue
57
- for (const point of entry.points || []) out.push({ topic, ...point })
58
- }
59
- return out
60
- }
61
-
62
- /**
63
- * Search the index by question or answer text.
64
- *
65
- * Case-insensitive substring match, same contract as {@link searchScenarios} so the
66
- * two can be chained in a CLI command without surprising the user.
67
- *
68
- * @param {string} query
69
- * @returns {Array<{topic: string, n: number, q: string, a: string}>}
70
- */
71
- export function searchKnowledge(query) {
72
- const needle = String(query ?? '').trim().toLowerCase()
73
- if (!needle) return []
74
- const out = []
75
- for (const [topic, entry] of Object.entries(catalogue().topics)) {
76
- for (const point of entry.points || []) {
77
- if (`${point.q} ${point.a}`.toLowerCase().includes(needle)) out.push({ topic, ...point })
78
- }
79
- }
80
- return out
81
- }
82
-
83
- /**
84
- * Render the knowledge section for a scenario brief.
85
- *
86
- * Returns an empty string when the scenario has none, so callers can append it
87
- * unconditionally.
88
- *
89
- * @param {string} scenarioId
90
- * @param {object} [options]
91
- * @param {number} [options.limit=10]
92
- * @returns {string}
93
- */
94
- export function renderKnowledge(scenarioId, { limit = 10 } = {}) {
95
- const points = knowledgeFor(scenarioId).slice(0, limit)
96
- if (!points.length) return ''
97
- const lines = ['', '## 知识点(按主题归类)']
98
- for (const p of points) {
99
- lines.push(`- ${p.q}`)
100
- lines.push(` 答: ${p.a}`)
101
- }
102
- return lines.join('\n')
103
- }