@rse/specbook 0.9.8 → 1.0.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.
package/README.md CHANGED
@@ -31,7 +31,7 @@ CLI with commands `specbook <xxx>`, and an MCP service with tools
31
31
  - **Versatile Object to Markdown Mapping**:
32
32
  Objects are authored as plain Markdown headings, property lists,
33
33
  and description prose. The objects can be mapped to nested sections
34
- ("complex" format) or a compact bullet point lists ("concise" and
34
+ ("complex" format) or compact bullet point lists ("concise" and
35
35
  "group" format).
36
36
 
37
37
  - **Wiki-Style Object Linking**:
@@ -44,14 +44,15 @@ CLI with commands `specbook <xxx>`, and an MCP service with tools
44
44
  The YAML schema configuration strictly defines the allowed object
45
45
  kinds, hierarchies, and properties, whose values are constrained by an
46
46
  expression language (regex, enum, tags, list, and reference). Violations
47
- are reported as file- and line-precise diagnostics.
47
+ are reported as file- and line-precise diagnostics. A bundled standard
48
+ schema configuration applies if no particular one is given.
48
49
 
49
50
  - **Object Model Diagram Visualisation**:
50
51
  Object kinds can declare "graph", "hub", or "grid" diagrams in the
51
52
  schema, whose nodes and edges are automatically derived from the
52
53
  object model and its references. The diagrams are rendered with the
53
54
  sibling project [Gradia](https://github.com/rse/gradia), which is
54
- specialized on rendering object models.
55
+ specialized in rendering object models.
55
56
 
56
57
  - **CLI, MCP, and API Interface**:
57
58
  All commands are implemented once in the API class `SpecBook`, on
@@ -79,43 +80,38 @@ CLI with commands `specbook <xxx>`, and an MCP service with tools
79
80
  It is intended as a polished, paginated offline document for handing
80
81
  over to customers.
81
82
 
82
- Overview Poster
83
- ---------------
83
+ Overview
84
+ --------
84
85
 
85
- [![poster](etc/poster.png)](etc/poster.pdf)
86
+ [![poster-1](etc/poster-1.png)](etc/poster-1.pdf)
86
87
 
87
- Rendering Example
88
- -----------------
88
+ [![poster-2](etc/poster-2.png)](etc/poster-2.pdf)
89
89
 
90
- ### HTML Rendering (Light Theme)
91
-
92
- ![screenshot-light](etc/screenshot-light.png)
93
-
94
- ### HTML Rendering (Dark Theme)
95
-
96
- ![screenshot-dark](etc/screenshot-dark.png)
90
+ Installation
91
+ ------------
97
92
 
98
- ### PDF Rendering
99
-
100
- ![screenshot-print](etc/screenshot-print.png)
93
+ ```bash
94
+ $ npm install -g @rse/specbook
95
+ ```
101
96
 
102
97
  Usage
103
98
  -----
104
99
 
105
100
  ### CLI
106
101
 
107
- ```
108
- $ specbook init [-v] -c <yaml-file> [-b <basedir>]
109
- $ specbook lint [-v] -c <yaml-file> [-b <basedir>]
110
- $ specbook export [-v] -c <yaml-file> [-b <basedir>] [-o [<format>:]<output-file>] [...]
102
+ ```bash
103
+ $ specbook init [-v] [-c <yaml-file>] [-b <basedir>]
104
+ $ specbook lint [-v] [-c <yaml-file>] [-b <basedir>]
105
+ $ specbook export [-v] [-c <yaml-file>] [-b <basedir>] [-o [<format>:]<output-file>] [...]
111
106
  $ specbook describe [-v] [-c <yaml-file>] [-b <basedir>] [-e] [-o <markdown-file>]
112
107
  $ specbook mcp [-v]
113
108
  ```
114
109
 
115
- The YAML schema configuration `-c`/`--config` is mandatory for `init`,
116
- `lint`, and `export`, as it determines the specification: exactly the
117
- artifact files its `file` fields reference are loaded and parsed, all
118
- other Markdown files below the base directory are ignored. A referenced
110
+ The YAML schema configuration `-c`/`--config` (default: the bundled
111
+ standard schema configuration `specbook-format.yaml`) determines the
112
+ specification: exactly the artifact files its `file` fields reference
113
+ are loaded and parsed; all other Markdown files below the base
114
+ directory are ignored. A referenced
119
115
  file which is absent is reported, unless all of its artifacts are
120
116
  `optional`, and so is an artifact absent from its present file, unless
121
117
  it is `optional` itself. Both `lint` and `export` report all
@@ -149,9 +145,7 @@ its own.
149
145
  The default value of every CLI option `--xxx` can be overridden by a
150
146
  corresponding `SPECBOOK_XXX` environment variable (e.g.
151
147
  `SPECBOOK_BASEDIR`, `SPECBOOK_CONFIG`, `SPECBOOK_OUTPUT`,
152
- `SPECBOOK_VERBOSE`), while an explicitly supplied option always wins. A
153
- `SPECBOOK_CONFIG` value also satisfies the otherwise mandatory
154
- `-c`/`--config` option.
148
+ `SPECBOOK_VERBOSE`), while an explicitly supplied option always wins.
155
149
 
156
150
  ### API
157
151
 
@@ -162,7 +156,6 @@ const specbook = new SpecBook({
162
156
  verbose: (cmd, msg) => console.error(`specbook: ${cmd}: ${renderVerbose(msg)}`)
163
157
  })
164
158
  const result = await specbook.lint({
165
- config: "smp/ase.specbook.yaml",
166
159
  basedir: "smp/broadcast"
167
160
  })
168
161
  ```
@@ -172,6 +165,21 @@ const result = await specbook.lint({
172
165
  Run `specbook mcp` to serve the tools `specbook_init`, `specbook_lint`,
173
166
  `specbook_export`, and `specbook_describe` over stdio.
174
167
 
168
+ Rendering Examples
169
+ ------------------
170
+
171
+ ### HTML Rendering (Light Theme)
172
+
173
+ ![screenshot-light](etc/screenshot-light.png)
174
+
175
+ ### HTML Rendering (Dark Theme)
176
+
177
+ ![screenshot-dark](etc/screenshot-dark.png)
178
+
179
+ ### PDF Rendering
180
+
181
+ ![screenshot-print](etc/screenshot-print.png)
182
+
175
183
  License
176
184
  -------
177
185
 
@@ -9,6 +9,7 @@ export { type LintResult };
9
9
  export type { Spec, SpecArtifact, SpecObject, SpecDescription, SpecProperty } from "./specbook-format-spec.js";
10
10
  export type { Schema, SchemaObject, SchemaProperty } from "./specbook-format-schema.js";
11
11
  export declare const version: string;
12
+ export declare const standardConfig: string;
12
13
  export type VerboseSink = (cmd: string, msg: string) => void;
13
14
  export interface SpecBookOptions {
14
15
  verbose?: VerboseSink;
@@ -17,18 +18,18 @@ export declare class SpecBook {
17
18
  private verbose;
18
19
  constructor(options?: SpecBookOptions);
19
20
  private verboseOf;
20
- private requireConfigFile;
21
+ private configFile;
21
22
  private requireConfig;
22
23
  init(options: {
23
- config: string;
24
+ config?: string;
24
25
  basedir?: string;
25
26
  }): Promise<string[]>;
26
27
  lint(options: {
27
- config: string;
28
+ config?: string;
28
29
  basedir?: string;
29
30
  }): Promise<LintResult>;
30
31
  export(options: {
31
- config: string;
32
+ config?: string;
32
33
  basedir?: string;
33
34
  formats?: ExportFormat[];
34
35
  }): Promise<Buffer[]>;
@@ -4,6 +4,7 @@
4
4
  ** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
5
5
  */
6
6
  import * as fs from "node:fs";
7
+ import { fileURLToPath } from "node:url";
7
8
  import { loadConfig } from "./specbook-config.js";
8
9
  import { renderDiagnostic, renderDiagnosticVerbose } from "./specbook-diagnostic.js";
9
10
  import { initSpecification } from "./specbook-cmd-init.js";
@@ -18,6 +19,10 @@ export { renderDiagnostic, renderDiagnosticVerbose };
18
19
  /* our own version, taken from the package manifest, which resides one
19
20
  level above both the source and the compiled module directory */
20
21
  export const version = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
22
+ /* the bundled standard YAML schema configuration, which resides
23
+ alongside the compiled module and is used whenever no particular
24
+ schema configuration is given */
25
+ export const standardConfig = fileURLToPath(new URL("specbook-format.yaml", import.meta.url));
21
26
  /* the SpecBook API */
22
27
  export class SpecBook {
23
28
  verbose;
@@ -28,16 +33,15 @@ export class SpecBook {
28
33
  verboseOf(cmd) {
29
34
  return (msg) => this.verbose(cmd, msg);
30
35
  }
31
- /* require the file of the mandatory YAML schema configuration */
32
- requireConfigFile(file) {
33
- if (file === undefined)
34
- throw new Error("YAML schema configuration required");
35
- return file;
36
+ /* determine the file of the YAML schema configuration, falling
37
+ back onto the bundled standard schema configuration */
38
+ configFile(file) {
39
+ return file ?? standardConfig;
36
40
  }
37
- /* load a mandatory YAML schema configuration, failing on any problem */
41
+ /* load a YAML schema configuration, failing on any problem */
38
42
  requireConfig(file, verbose) {
39
- file = this.requireConfigFile(file);
40
- verbose(`loading configuration "${literal(file)}"`);
43
+ file = this.configFile(file);
44
+ verbose(`loading schema configuration "${literal(file)}"`);
41
45
  const { config, diagnostics } = loadConfig(file);
42
46
  if (config === undefined)
43
47
  throw new Error("invalid configuration:\n" +
@@ -53,7 +57,7 @@ export class SpecBook {
53
57
  }
54
58
  /* lint the specification Markdown files below the base directory */
55
59
  async lint(options) {
56
- return lint({ config: this.requireConfigFile(options.config),
60
+ return lint({ config: this.configFile(options.config),
57
61
  basedir: options.basedir ?? ".", verbose: this.verboseOf("lint") });
58
62
  }
59
63
  /* export the specification Markdown files below the base directory
@@ -63,7 +67,7 @@ export class SpecBook {
63
67
  as a partial or invalid specification must never be emitted) */
64
68
  async export(options) {
65
69
  const verbose = this.verboseOf("export");
66
- const result = lint({ config: this.requireConfigFile(options.config),
70
+ const result = lint({ config: this.configFile(options.config),
67
71
  basedir: options.basedir ?? ".", verbose });
68
72
  if (result.diagnostics.length > 0)
69
73
  throw new Error("invalid specification:\n" +
@@ -54,9 +54,10 @@ const envDefaultFlag = (name, fallback) => {
54
54
  const withVerboseOption = (command) => command
55
55
  .option("-v, --verbose", "print verbose processing information to stderr", envDefaultFlag("verbose", false));
56
56
  /* provide the common options of the specification processing sub-commands,
57
- for which the YAML schema configuration is mandatory */
57
+ for which the YAML schema configuration falls back onto the standard one */
58
58
  const withCommonOptions = (command) => withVerboseOption(command)
59
- .requiredOption("-c, --config <yaml-file>", "YAML schema configuration file", envDefault("config"))
59
+ .option("-c, --config <yaml-file>", "YAML schema configuration file " +
60
+ "(default: the bundled standard schema configuration)", envDefault("config"))
60
61
  .option("-b, --basedir <directory>", "base directory of the specification Markdown files", envDefault("basedir", "."));
61
62
  /* parse the command line */
62
63
  const program = new Command();
@@ -18,20 +18,17 @@ export const describeFormat = (options) => {
18
18
  "==============================\n");
19
19
  if (options.config !== undefined && options.embed === true)
20
20
  sections.push(textframe(`
21
- The **SpecBook SCHEMA Model** of this particular project is the following
22
- YAML schema configuration of the file \`${options.config}\`:
21
+ The **SpecBook SCHEMA Model** is the following YAML schema configuration:
23
22
  `) + "\n```yaml\n" +
24
23
  fs.readFileSync(options.config, "utf8").replace(/\n*$/, "\n") +
25
24
  "```\n");
26
25
  else if (options.config !== undefined)
27
26
  sections.push(textframe(`
28
- The **SpecBook SCHEMA Model** of this particular project is the YAML
29
- schema configuration in the file \`${options.config}\`.
27
+ The **SpecBook SCHEMA Model** is the YAML schema configuration in file \`${options.config}\`.
30
28
  `));
31
29
  if (options.basedir !== undefined)
32
30
  sections.push(textframe(`
33
- The **SpecBook SPEC Model** of this particular project is the set of
34
- specification Markdown files in the directory \`${options.basedir}\`.
31
+ The **SpecBook SPEC Model** is the set of Markdown specification files in directory \`${options.basedir}\`.
35
32
  `));
36
33
  }
37
34
  return sections.join("\n");
@@ -25,7 +25,7 @@ const schemaFiles = (config) => {
25
25
  export const lint = (options) => {
26
26
  const diagnostics = new Array();
27
27
  /* load the mandatory YAML schema configuration */
28
- options.verbose(`loading configuration "${literal(options.config)}"`);
28
+ options.verbose(`loading schema configuration "${literal(options.config)}"`);
29
29
  const loaded = loadConfig(options.config);
30
30
  diagnostics.push(...loaded.diagnostics);
31
31
  const config = loaded.config;
@@ -37,9 +37,9 @@ task:
37
37
  - **SpecBook SCHEMA Model**:
38
38
  This is an object model, described in *YAML*, outside of
39
39
  **SpecBook**, which defines a particular **SpecBook** *Schema*.
40
- It is user-supplied and non-generic but reusable and applied onto
41
- the **SpecBook SPEC Model** to validate that it is domain-wise
42
- well-structured.
40
+ It is user-supplied (or the bundled standard one) and non-generic
41
+ but reusable and applied onto the **SpecBook SPEC Model** to
42
+ validate that it is domain-wise well-structured.
43
43
 
44
44
  - **SpecBook SPEC Model**:
45
45
  This is an object model, described in *Markdown*, outside
@@ -531,7 +531,9 @@ SpecBook SCHEMA Model
531
531
  ### Artifacts
532
532
 
533
533
  A schema consists of a YAML file, which is exactly a serialization of
534
- the object model defined by the **SpecBook SCHEMA Meta Model**.
534
+ the object model defined by the **SpecBook SCHEMA Meta Model**. If no
535
+ particular schema is given, the standard schema configuration bundled
536
+ with **SpecBook** applies.
535
537
 
536
538
  SpecBook SPEC Model
537
539
  -------------------