jssm 5.145.6 → 5.146.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.
@@ -0,0 +1,199 @@
1
+ import { StreamLanguage, HighlightStyle, LanguageSupport } from '@codemirror/language';
2
+ import { Tag, tags } from '@lezer/highlight';
3
+
4
+ /**
5
+ * CodeMirror 6 language support for FSL (the source language of jssm
6
+ * finite-state machines), exposed at the `jssm/cm6` subpath.
7
+ *
8
+ * Built on `StreamLanguage` (token-stream based, not a Lezer tree grammar) —
9
+ * a deliberate v1 choice (see `notes/superpowers/specs/2026-05-12-editor-widget-packaging-design.md`):
10
+ * the tokenizer never blocks on the parser, so a failing parse never kills
11
+ * highlighting. The companion linter (driven by `fsl_parser.parse()`) supplies
12
+ * error squiggles separately.
13
+ *
14
+ * The keyword vocabulary is reconciled against the live grammar
15
+ * (`src/ts/fsl_parser.peg`) and guarded by `fsl_language.spec.ts`, which
16
+ * extracts the grammar's config keys and fails if any is not recognized here —
17
+ * so the highlighter cannot silently drift out of date the way the original
18
+ * `sketch/cm6-lang-fsl` did.
19
+ */
20
+ /**
21
+ * Tag modifier marking FSL syntax that still parses but is deprecated (e.g.
22
+ * the flat `graph_bg_color` key, superseded by the `graph: {}` block). Applied
23
+ * to a base tag — `fslDeprecated(tags.propertyName)` — and rendered distinctly
24
+ * by {@link fslHighlightStyle} (struck through) so editors can flag stale
25
+ * syntax. StreamLanguage expresses this through its `tokenTable`; see
26
+ * {@link fslStreamParser}.
27
+ */
28
+ const fslDeprecated = Tag.defineModifier('fslDeprecated');
29
+ /**
30
+ * Structural / declaration keywords — block openers and the `property`
31
+ * declaration keyword. Highlighted as `keyword`.
32
+ */
33
+ const STRUCTURAL_KEYWORDS = new Set([
34
+ 'state', 'start_state', 'end_state', 'active_state', 'terminal_state', 'hooked_state',
35
+ 'action', 'transition', 'validation', 'configuration', 'graph', 'hooks',
36
+ 'property', 'required', 'default',
37
+ ]);
38
+ /**
39
+ * Configuration / property / style keys (the left side of a `key: value;`
40
+ * config line). Highlighted as `propertyName`. Reconciled against the
41
+ * `"<key>" WS? ":"` rules in `fsl_parser.peg` and guarded by the drift test.
42
+ */
43
+ const PROPERTY_KEYWORDS = new Set([
44
+ // machine metadata
45
+ 'machine_name', 'machine_version', 'machine_author', 'machine_license',
46
+ 'machine_comment', 'machine_contributor', 'machine_definition',
47
+ 'machine_language', 'machine_reference', 'npm_name', 'fsl_version',
48
+ // graph / layout
49
+ 'graph_layout', 'start_states', 'end_states', 'failed_outputs',
50
+ 'allows_override', 'allow_islands', 'edge_color', 'theme', 'flow',
51
+ 'dot_preamble', 'default_size',
52
+ // per-element style keys
53
+ 'label', 'color', 'shape', 'corners', 'linestyle', 'image', 'url',
54
+ ]);
55
+ /**
56
+ * Keys that still parse but are deprecated. Highlighted with the
57
+ * {@link fslDeprecated} modifier so editors can visibly flag them.
58
+ */
59
+ const DEPRECATED_KEYWORDS = new Set([
60
+ 'graph_bg_color', // superseded by the `graph: {}` block
61
+ ]);
62
+ /**
63
+ * Enumerated literal values (layout engines, corner/line styles, directions,
64
+ * booleans, theme names). Highlighted as `atom`.
65
+ */
66
+ const ENUM_KEYWORDS = new Set([
67
+ 'true', 'false', 'none', 'default', 'modern', 'ocean', 'bold',
68
+ 'dot', 'circo', 'fdp', 'neato', 'twopi',
69
+ 'up', 'right', 'down', 'left',
70
+ 'solid', 'dotted', 'dashed',
71
+ 'regular', 'rounded', 'lined',
72
+ 'MIT',
73
+ ]);
74
+ /** Custom token-type string emitted for deprecated keywords; mapped in `tokenTable`. */
75
+ const DEPRECATED_TOKEN = 'fslDeprecatedKeyword';
76
+ const ARROWS = /^(?:<-=>|<-~>|<=->|<=~>|<~->|<~=>|<->|<=>|<~>|->|<-|=>|<=|~>|<~|↔|←|→|⇔|⇐|⇒|↮|↚|↛)/;
77
+ const COMPARATORS = /^(?:>=|<=|>|<)/;
78
+ // Mirrors the grammar's AtomFirstLetter / AtomLetter rules (fsl_parser.peg:263-267).
79
+ // The high-unicode tail is the range U+0080..U+FFFF (escaped below). The
80
+ // original sketch anchored the range at the comma, wrongly swallowing { } ; : |.
81
+ const ATOM_START = /[0-9a-zA-Z._!$^*?,\u0080-\uFFFF]/;
82
+ const ATOM_BODY = /[0-9a-zA-Z.+_^()*&$#@!?,\u0080-\uFFFF]/;
83
+ /**
84
+ * The StreamLanguage tokenizer for FSL. Classifies each token into a standard
85
+ * CodeMirror highlight name, or {@link DEPRECATED_TOKEN} (mapped through
86
+ * `tokenTable` to `fslDeprecated(tags.propertyName)`).
87
+ */
88
+ const fslStreamParser = {
89
+ name: 'fsl',
90
+ startState: () => ({ inBlockComment: false }),
91
+ token(stream, state) {
92
+ if (state.inBlockComment) {
93
+ while (!stream.eol()) {
94
+ if (stream.match('*/')) {
95
+ state.inBlockComment = false;
96
+ return 'comment';
97
+ }
98
+ stream.next();
99
+ }
100
+ return 'comment';
101
+ }
102
+ if (stream.eatSpace()) {
103
+ return null;
104
+ }
105
+ if (stream.match('//')) {
106
+ stream.skipToEnd();
107
+ return 'comment';
108
+ }
109
+ if (stream.match('/*')) {
110
+ state.inBlockComment = true;
111
+ return 'comment';
112
+ }
113
+ if (stream.match(/^"(?:\\.|[^"\\])*"?/)) {
114
+ return 'string';
115
+ }
116
+ if (stream.match(/^'(?:\\.|[^'\\])*'?/)) {
117
+ return 'labelName';
118
+ }
119
+ if (stream.match(ARROWS)) {
120
+ return 'operator';
121
+ }
122
+ if (stream.match(COMPARATORS)) {
123
+ return 'operator';
124
+ }
125
+ if (stream.match(/^\d+(?:\.\d+)*/)) {
126
+ return 'number';
127
+ }
128
+ if (stream.match(/^&[A-Za-z_]\w*/)) {
129
+ return 'variableName.special';
130
+ }
131
+ // Invariant: the loop guards `!eol` and `eatSpace()` returned false above,
132
+ // so a non-space character is present and `peek()` is defined here.
133
+ const ch = stream.peek();
134
+ if (ATOM_START.test(ch)) {
135
+ let tok = '';
136
+ let next = stream.peek();
137
+ while (next !== undefined && ATOM_BODY.test(next)) {
138
+ tok += stream.next();
139
+ next = stream.peek();
140
+ }
141
+ if (DEPRECATED_KEYWORDS.has(tok)) {
142
+ return DEPRECATED_TOKEN;
143
+ }
144
+ if (STRUCTURAL_KEYWORDS.has(tok)) {
145
+ return 'keyword';
146
+ }
147
+ if (PROPERTY_KEYWORDS.has(tok)) {
148
+ return 'propertyName';
149
+ }
150
+ if (ENUM_KEYWORDS.has(tok)) {
151
+ return 'atom';
152
+ }
153
+ return 'variableName';
154
+ }
155
+ if (stream.match(/^[\[\]{}()]/)) {
156
+ return 'bracket';
157
+ }
158
+ if (stream.match(/^[;:,|]/)) {
159
+ return 'punctuation';
160
+ }
161
+ stream.next();
162
+ return null;
163
+ },
164
+ tokenTable: {
165
+ [DEPRECATED_TOKEN]: fslDeprecated(tags.propertyName),
166
+ },
167
+ languageData: {
168
+ commentTokens: { line: '//', block: { open: '/*', close: '*/' } },
169
+ },
170
+ };
171
+ /**
172
+ * CodeMirror 6 `Language` for FSL. Most consumers use {@link fsl} instead.
173
+ */
174
+ const fslLanguage = StreamLanguage.define(fslStreamParser);
175
+ /**
176
+ * Recommended highlight style. Beyond the editor theme's own keyword/string/etc.
177
+ * rules, this renders {@link fslDeprecated} tokens struck-through and dimmed, so
178
+ * deprecated FSL syntax is visible at a glance. Include it alongside your theme;
179
+ * a bare theme without it simply won't distinguish deprecated keywords.
180
+ */
181
+ const fslHighlightStyle = HighlightStyle.define([
182
+ { tag: fslDeprecated(tags.propertyName), textDecoration: 'line-through', opacity: '0.6' },
183
+ ]);
184
+ /**
185
+ * CodeMirror 6 `LanguageSupport` for FSL. Drop into an editor's `extensions`.
186
+ *
187
+ * @returns A `LanguageSupport` extension for FSL highlighting.
188
+ *
189
+ * @example
190
+ * import { EditorView, basicSetup } from 'codemirror';
191
+ * import { fsl } from 'jssm/cm6';
192
+ * new EditorView({ doc: "a 'go' -> b;", parent: document.body,
193
+ * extensions: [ basicSetup, fsl() ] });
194
+ */
195
+ function fsl() {
196
+ return new LanguageSupport(fslLanguage);
197
+ }
198
+
199
+ export { DEPRECATED_KEYWORDS, ENUM_KEYWORDS, PROPERTY_KEYWORDS, STRUCTURAL_KEYWORDS, fsl, fslDeprecated, fslHighlightStyle, fslLanguage, fslStreamParser };
@@ -18,10 +18,10 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
18
18
 
19
19
 
20
20
 
21
- * Generated for version 5.145.6 at 6/23/2026, 6:05:53 AM
21
+ * Generated for version 5.146.0 at 6/23/2026, 6:32:51 AM
22
22
 
23
23
  -->
24
- # jssm 5.145.6
24
+ # jssm 5.146.0
25
25
 
26
26
  [**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
27
27
  [Documentation](https://stonecypher.github.io/jssm/docs/) ·
@@ -312,7 +312,7 @@ That decision shows up everywhere downstream:
312
312
  or run `npm run benny` against your own machine.
313
313
 
314
314
  - **More thoroughly tested than any other JavaScript state-machine
315
- library.** 7,395 tests at 100.0% line coverage
315
+ library.** 7,416 tests at 100.0% line coverage
316
316
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
317
317
  fuzz testing via `fast-check`, with parser test data across ten natural
318
318
  languages and Emoji.
@@ -445,11 +445,11 @@ If your contribution is missing here, please open an issue.
445
445
 
446
446
  <br/>
447
447
 
448
- ***7,395 tests***, run 82,437 times.
448
+ ***7,416 tests***, run 82,458 times.
449
449
 
450
- - 6,637 specs with 100.0% coverage
451
- - 758 fuzz tests with 73.6% coverage
452
- - 6,967 TypeScript lines - 1.1 tests per line, 11.8 generated tests per line
450
+ - 6,658 specs with 100.0% coverage
451
+ - 758 fuzz tests with 70.7% coverage
452
+ - 7,051 TypeScript lines - 1.1 tests per line, 11.7 generated tests per line
453
453
 
454
454
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
455
455
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)