jssm 5.104.2 → 5.112.3

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.
Files changed (62) hide show
  1. package/.gitattributes +17 -6
  2. package/.log-progress.json +9 -0
  3. package/CHANGELOG.md +130 -45
  4. package/CLAUDE.md +11 -0
  5. package/MIGRATING-jssm-viz.md +67 -0
  6. package/README.md +179 -882
  7. package/dist/es6/fsl_parser.js +1 -1
  8. package/dist/es6/jssm.d.ts +773 -39
  9. package/dist/es6/jssm.js +921 -89
  10. package/dist/es6/jssm_arrow.js +24 -0
  11. package/dist/es6/jssm_compiler.d.ts +17 -2
  12. package/dist/es6/jssm_compiler.js +17 -3
  13. package/dist/es6/jssm_constants.d.ts +27 -0
  14. package/dist/es6/jssm_constants.js +27 -0
  15. package/dist/es6/jssm_error.d.ts +19 -0
  16. package/dist/es6/jssm_error.js +19 -0
  17. package/dist/es6/jssm_theme.d.ts +11 -0
  18. package/dist/es6/jssm_theme.js +11 -0
  19. package/dist/es6/jssm_types.d.ts +29 -3
  20. package/dist/es6/jssm_util.d.ts +161 -9
  21. package/dist/es6/jssm_util.js +174 -17
  22. package/dist/es6/jssm_viz.d.ts +175 -0
  23. package/dist/es6/jssm_viz.js +560 -0
  24. package/dist/es6/jssm_viz_colors.d.ts +63 -0
  25. package/dist/es6/jssm_viz_colors.js +63 -0
  26. package/dist/es6/version.js +1 -1
  27. package/dist/jssm.es5.cjs +1 -1
  28. package/dist/jssm.es5.iife.js +1 -0
  29. package/dist/jssm.es5.nonmin.cjs +2201 -870
  30. package/dist/jssm.es6.mjs +1 -1
  31. package/dist/jssm.es6.nonmin.cjs +2200 -871
  32. package/dist/jssm_viz.cjs +1 -0
  33. package/dist/{jssm.es5.iife.nonmin.cjs → jssm_viz.es5.iife.nonmin.cjs} +2589 -1090
  34. package/dist/jssm_viz.es5.nonmin.cjs +24674 -0
  35. package/dist/jssm_viz.es6.nonmin.cjs +24661 -0
  36. package/dist/jssm_viz.iife.cjs +1 -0
  37. package/dist/jssm_viz.mjs +1 -0
  38. package/jest-dragon.config.cjs +4 -1
  39. package/jest-spec.config.cjs +9 -6
  40. package/jest-stoch.config.cjs +4 -1
  41. package/jest-unicode.config.cjs +4 -1
  42. package/jssm.es5.d.cts +950 -41
  43. package/jssm.es6.d.ts +950 -41
  44. package/jssm_viz.es5.d.cts +2127 -0
  45. package/jssm_viz.es6.d.ts +2127 -0
  46. package/log-progress.data.json +28 -0
  47. package/package.json +56 -23
  48. package/rollup.config.viz.es5.js +46 -0
  49. package/rollup.config.viz.es6.js +46 -0
  50. package/rollup.config.viz.iife.js +36 -0
  51. package/typedoc-options.cjs +4 -3
  52. package/dist/jssm.es5.iife.cjs +0 -1
  53. package/fsl_parser.d.ts +0 -6
  54. package/jssm.d.ts +0 -1141
  55. package/jssm_arrow.d.ts +0 -53
  56. package/jssm_compiler.d.ts +0 -135
  57. package/jssm_constants.d.ts +0 -5
  58. package/jssm_error.d.ts +0 -8
  59. package/jssm_theme.d.ts +0 -4
  60. package/jssm_types.d.ts +0 -378
  61. package/jssm_util.d.ts +0 -106
  62. package/version.d.ts +0 -2
@@ -1,26 +1,87 @@
1
1
  import { JssmError } from './jssm_error';
2
2
  /*******
3
3
  *
4
- * Predicate for validating an array for uniqueness. Not generally meant for
5
- * external use.
4
+ * Predicate for validating an array for uniqueness. Returns `true` when
5
+ * `el` is the first occurrence in `source`, `false` otherwise. Intended
6
+ * for use as an `Array.filter` callback. Not generally meant for external
7
+ * use.
8
+ *
9
+ * ```typescript
10
+ * [1, 2, 2, 3].filter(arr_uniq_p); // [1, 2, 3]
11
+ * ```
12
+ *
13
+ * @param el - The current element being tested.
14
+ * @param i - The index of the current element.
15
+ * @param source - The full array being filtered.
16
+ *
17
+ * @returns `true` if `el` is the first occurrence in `source`.
6
18
  *
7
19
  */
8
20
  function arr_uniq_p(el, i, source) {
9
21
  return source.indexOf(el) === i;
10
22
  }
23
+ /*******
24
+ *
25
+ * Wraps a string in an array, or passes through if already non-string.
26
+ * Used to normalize arguments that accept either a single state name or
27
+ * an array of state names.
28
+ *
29
+ * ```typescript
30
+ * array_box_if_string('hello'); // ['hello']
31
+ * array_box_if_string(['a','b']); // ['a','b']
32
+ * ```
33
+ *
34
+ * @param n - A string to box, or a value to pass through unchanged.
35
+ *
36
+ * @returns The input wrapped in an array if it was a string, otherwise the
37
+ * input unchanged.
38
+ *
39
+ */
11
40
  const array_box_if_string = n => typeof n === 'string' ? [n] : n;
41
+ /*******
42
+ *
43
+ * Selects a single item from a weighted array of objects using cumulative
44
+ * probability. Each object in the array should have a numeric property
45
+ * indicating its relative weight (defaults to `'probability'`). Objects
46
+ * missing the property are treated as weight 1.
47
+ *
48
+ * ```typescript
49
+ * const opts = [
50
+ * { value: 'common', probability: 0.8 },
51
+ * { value: 'rare', probability: 0.2 }
52
+ * ];
53
+ *
54
+ * weighted_rand_select(opts); // most often { value: 'common', ... }
55
+ * ```
56
+ *
57
+ * @param options - Non-empty array of objects to choose from.
58
+ * @param probability_property - Name of the numeric weight property on each
59
+ * object. Defaults to `'probability'`.
60
+ * @param rng - Optional random number generator `() => number`
61
+ * in `[0, 1)`. Defaults to `Math.random`.
62
+ *
63
+ * @returns One element from `options`, chosen by weighted random selection.
64
+ *
65
+ * @throws {TypeError} If `options` is not a non-empty array of objects.
66
+ *
67
+ */
12
68
  // this is explicitly about other peoples' data, so it has to be weakly typed
13
69
  /* eslint-disable flowtype/no-weak-types */
14
70
  const weighted_rand_select = (options, probability_property = 'probability', rng) => {
15
71
  if (!Array.isArray(options)) {
16
72
  throw new TypeError('options must be a non-empty array of objects');
17
73
  }
74
+ if (options.length === 0) {
75
+ throw new TypeError('options must be a non-empty array of objects');
76
+ }
18
77
  if (!(typeof options[0] === 'object')) {
19
78
  throw new TypeError('options must be a non-empty array of objects');
20
79
  }
21
- const frand = (cap) => Math.random() * cap, or_one = (item) => item === undefined ? 1 : item, prob_sum = options.reduce((acc, val) => acc + or_one(val[probability_property]), 0), rnd = frand(prob_sum);
80
+ const frand = rng
81
+ ? (cap) => rng() * cap
82
+ : (cap) => Math.random() * cap, or_one = (item) => item === undefined ? 1 : item, prob_sum = options.reduce((acc, val) => acc + or_one(val[probability_property]), 0), rnd = frand(prob_sum);
22
83
  let cursor = 0, cursor_sum = 0;
23
- while ((cursor_sum += or_one(options[cursor++][probability_property])) <= rnd) { } // eslint-disable-line no-empty,fp/no-loops
84
+ while (cursor < options.length && (cursor_sum += or_one(options[cursor++][probability_property])) <= rnd) { } // eslint-disable-line no-empty,fp/no-loops
24
85
  return options[cursor - 1];
25
86
  };
26
87
  /* eslint-enable flowtype/no-weak-types */
@@ -60,21 +121,82 @@ function seq(n) {
60
121
  *
61
122
  */
62
123
  const histograph = (ar) => // eslint-disable-line flowtype/no-weak-types
63
- ar.sort()
124
+ [...ar].sort()
64
125
  .reduce((m, v) => // TODO FIXME eslint-disable-line flowtype/no-weak-types,no-sequences
65
126
  (m.set(v, (m.has(v) ? m.get(v) + 1 : 1)), m), new Map());
66
- const weighted_sample_select = (n, options, probability_property) => // TODO FIXME no any // eslint-disable-line flowtype/no-weak-types
127
+ /*******
128
+ *
129
+ * Draws `n` weighted random samples from an array of objects. Each draw is
130
+ * independent (with replacement), delegating to {@link weighted_rand_select}.
131
+ *
132
+ * ```typescript
133
+ * const opts = [
134
+ * { value: 'a', probability: 0.9 },
135
+ * { value: 'b', probability: 0.1 }
136
+ * ];
137
+ *
138
+ * weighted_sample_select(3, opts, 'probability');
139
+ * // e.g. [ { value: 'a', ... }, { value: 'a', ... }, { value: 'b', ... } ]
140
+ * ```
141
+ *
142
+ * @param n - Number of samples to draw.
143
+ * @param options - Non-empty array of weighted objects.
144
+ * @param probability_property - Name of the numeric weight property.
145
+ * @param rng - Optional random number generator.
146
+ *
147
+ * @returns An array of `n` independently selected items.
148
+ *
149
+ */
150
+ const weighted_sample_select = (n, options, probability_property, rng) => // TODO FIXME no any // eslint-disable-line flowtype/no-weak-types
67
151
  seq(n)
68
152
  .map((_i) => // TODO FIXME eslint-disable-line flowtype/no-weak-types
69
- weighted_rand_select(options, probability_property));
70
- const weighted_histo_key = (n, opts, prob_prop, extract) => // TODO FIXME no any // eslint-disable-line flowtype/no-weak-types
71
- histograph(weighted_sample_select(n, opts, prob_prop)
153
+ weighted_rand_select(options, probability_property, rng));
154
+ /*******
155
+ *
156
+ * Draws `n` weighted random samples, extracts a named key from each, and
157
+ * returns a histograph (`Map`) of how often each key value appeared. Useful
158
+ * for validating that a probabilistic transition distribution is roughly
159
+ * correct over many trials.
160
+ *
161
+ * ```typescript
162
+ * const opts = [
163
+ * { to: 'a', probability: 0.7 },
164
+ * { to: 'b', probability: 0.3 }
165
+ * ];
166
+ *
167
+ * weighted_histo_key(1000, opts, 'probability', 'to');
168
+ * // Map { 'a' => ~700, 'b' => ~300 }
169
+ * ```
170
+ *
171
+ * @param n - Number of samples to draw.
172
+ * @param opts - Non-empty array of weighted objects.
173
+ * @param prob_prop - Name of the numeric weight property.
174
+ * @param extract - Name of the property to extract from each sample for
175
+ * histogramming.
176
+ * @param rng - Optional random number generator.
177
+ *
178
+ * @returns A `Map` from extracted key values to their occurrence counts.
179
+ *
180
+ */
181
+ const weighted_histo_key = (n, opts, prob_prop, extract, rng) => // TODO FIXME no any // eslint-disable-line flowtype/no-weak-types
182
+ histograph(weighted_sample_select(n, opts, prob_prop, rng)
72
183
  .map((s) => s[extract] // TODO FIXME eslint-disable-line flowtype/no-weak-types
73
184
  ));
74
185
  /*******
75
186
  *
76
- * Internal method generating names for edges for the hook lookup map. Not
77
- * meant for external use.
187
+ * Internal method generating composite keys for the hook lookup map by
188
+ * JSON-serializing a `[property, state]` pair. Not meant for external use.
189
+ *
190
+ * ```typescript
191
+ * name_bind_prop_and_state('color', 'Red'); // '["color","Red"]'
192
+ * ```
193
+ *
194
+ * @param prop - The property name (e.g. a data key or hook category).
195
+ * @param state - The state name to bind to.
196
+ *
197
+ * @returns A deterministic JSON string key for the `[prop, state]` pair.
198
+ *
199
+ * @throws {JssmError} If either argument is not a string.
78
200
  *
79
201
  */
80
202
  function name_bind_prop_and_state(prop, state) {
@@ -82,21 +204,42 @@ function name_bind_prop_and_state(prop, state) {
82
204
  throw new JssmError(undefined, `Name of property must be a string; got ${prop}`);
83
205
  }
84
206
  if (typeof state !== 'string') {
85
- throw new JssmError(undefined, `Name of state must be a string; got ${prop}`);
207
+ throw new JssmError(undefined, `Name of state must be a string; got ${state}`);
86
208
  }
87
209
  return JSON.stringify([prop, state]);
88
210
  }
89
211
  /*******
90
212
  *
91
- * Internal method generating names for edges for the hook lookup map. Not
92
- * meant for external use.
213
+ * Internal method generating composite keys for transition hooks by
214
+ * JSON-serializing a `[from, to]` state pair. Used to look up hooks
215
+ * registered on a specific edge. Not meant for external use.
216
+ *
217
+ * ```typescript
218
+ * hook_name('Red', 'Green'); // '["Red","Green"]'
219
+ * ```
220
+ *
221
+ * @param from - The source state name.
222
+ * @param to - The target state name.
223
+ *
224
+ * @returns A deterministic JSON string key for the `[from, to]` pair.
93
225
  *
94
226
  */
95
227
  const hook_name = (from, to) => JSON.stringify([from, to]);
96
228
  /*******
97
229
  *
98
- * Internal method generating names for actions for the hook lookup map. Not
99
- * meant for external use.
230
+ * Internal method generating composite keys for named-action hooks by
231
+ * JSON-serializing a `[from, to, action]` triple. Used to look up hooks
232
+ * registered on a specific action-labeled edge. Not meant for external use.
233
+ *
234
+ * ```typescript
235
+ * named_hook_name('Red', 'Green', 'next'); // '["Red","Green","next"]'
236
+ * ```
237
+ *
238
+ * @param from - The source state name.
239
+ * @param to - The target state name.
240
+ * @param action - The action label on the edge.
241
+ *
242
+ * @returns A deterministic JSON string key for the `[from, to, action]` triple.
100
243
  *
101
244
  */
102
245
  const named_hook_name = (from, to, action) => JSON.stringify([from, to, action]);
@@ -116,7 +259,7 @@ function gen_splitmix32(a) {
116
259
  return function () {
117
260
  a |= 0;
118
261
  a = a + 0x9e3779b9 | 0;
119
- var t = a ^ a >>> 16;
262
+ let t = a ^ a >>> 16;
120
263
  t = Math.imul(t, 0x21f0aaad);
121
264
  t = t ^ t >>> 15;
122
265
  t = Math.imul(t, 0x735a2d97);
@@ -174,6 +317,20 @@ function find_repeated(arr) {
174
317
  return [];
175
318
  }
176
319
  }
320
+ /*******
321
+ *
322
+ * Returns a `Promise` that resolves after `ms` milliseconds. Useful for
323
+ * inserting delays in async test flows or demos.
324
+ *
325
+ * ```typescript
326
+ * await sleep(100); // pauses execution for 100ms
327
+ * ```
328
+ *
329
+ * @param ms - Number of milliseconds to wait before resolving.
330
+ *
331
+ * @returns A `Promise<void>` that resolves after the timeout.
332
+ *
333
+ */
177
334
  function sleep(ms) {
178
335
  return new Promise(resolve => setTimeout(resolve, ms));
179
336
  }
@@ -0,0 +1,175 @@
1
+ import * as jssm from './jssm';
2
+ import { version, build_time } from './version';
3
+ /**
4
+ * Inject runtime configuration for jssm/viz. Currently only accepts a
5
+ * custom `DOMParser` constructor for use by `*_svg_element` functions in
6
+ * environments that do not provide one globally (e.g. Node + jsdom).
7
+ *
8
+ * Idempotent — last call wins. No-op if called with no recognized keys.
9
+ *
10
+ * ```typescript
11
+ * // Node, with jsdom:
12
+ * import { JSDOM } from 'jsdom';
13
+ * import { configure, fsl_to_svg_element } from 'jssm/viz';
14
+ *
15
+ * configure({ DOMParser: new JSDOM().window.DOMParser });
16
+ * const el = await fsl_to_svg_element('a -> b;');
17
+ * ```
18
+ *
19
+ * @param opts Configuration overrides.
20
+ * @param opts.DOMParser Constructor compatible with the WHATWG `DOMParser`
21
+ * interface. Used as a fallback when `globalThis.DOMParser` is undefined.
22
+ *
23
+ * @throws {JssmError} if `DOMParser` is provided and is not a constructor.
24
+ */
25
+ declare function configure(opts: {
26
+ DOMParser?: typeof globalThis.DOMParser;
27
+ }): void;
28
+ /**
29
+ * Look up a color from the default viz palette by key, returning empty
30
+ * string if the key is unknown (so it disappears in feature concatenation).
31
+ *
32
+ * @internal
33
+ */
34
+ declare function vc(col: string): string;
35
+ /**
36
+ * Build a graphviz-safe node identifier for a state, by index. Accepts
37
+ * either a `string[]` (used historically; O(n) per call) or a
38
+ * precomputed `Map<state, index>` (used by rendering hot paths; O(1)
39
+ * per call). The map form is used during dot generation; the array
40
+ * form is retained for direct test access via `_test`.
41
+ *
42
+ * @internal
43
+ */
44
+ declare function node_of(state: string, state_index: string[] | Map<string, number>): string;
45
+ /**
46
+ * Convert an 8-channel hex color (`#RRGGBBAA`) to a 6-channel hex color
47
+ * (`#RRGGBB`), discarding the alpha channel. Throws if the input is not
48
+ * a 9-character `#`-prefixed string.
49
+ *
50
+ * Graphviz dot does not support alpha; this is a lossy projection.
51
+ *
52
+ * @internal
53
+ */
54
+ declare function color8to6(color8: string): string;
55
+ /**
56
+ * Variant of {@link color8to6} that passes `undefined` through.
57
+ *
58
+ * @internal
59
+ */
60
+ declare function u_color8to6(color8?: string): string | undefined;
61
+ /**
62
+ * Read the graphviz shape for a state through {@link jssm.Machine.style_for},
63
+ * so theme-supplied shapes are honoured along with per-state declarations.
64
+ * Returns `undefined` if neither a theme nor a state declaration supplies a
65
+ * shape.
66
+ *
67
+ * @internal
68
+ */
69
+ declare function shape_for_state<T>(u_jssm: jssm.Machine<T>, state: string): string | undefined;
70
+ /**
71
+ * Read the image filename for a state through {@link jssm.Machine.style_for},
72
+ * so theme-supplied images are honoured along with per-state declarations.
73
+ * Returns `undefined` if neither a theme nor a state declaration supplies an
74
+ * image.
75
+ *
76
+ * @internal
77
+ */
78
+ declare function image_for_state<T>(u_jssm: jssm.Machine<T>, state: string): string | undefined;
79
+ /**
80
+ * Compose a graphviz `style` string for a state by looking up its merged
81
+ * style via {@link jssm.Machine.style_for}, then delegating to
82
+ * {@link compose_style_string}. Theme-supplied `corners` and `lineStyle`
83
+ * are honoured along with per-state declarations.
84
+ *
85
+ * @internal
86
+ */
87
+ declare function style_for_state<T>(u_jssm: jssm.Machine<T>, state: string): string;
88
+ /**
89
+ * Render a {@link jssm.Machine} as a graphviz dot string.
90
+ *
91
+ * ```typescript
92
+ * import { sm } from 'jssm';
93
+ * import { machine_to_dot } from 'jssm/viz';
94
+ *
95
+ * const dot = machine_to_dot(sm`a -> b;`);
96
+ * // 'digraph G { ... }'
97
+ * ```
98
+ *
99
+ * @param u_jssm The machine to render.
100
+ * @returns A complete graphviz dot source string.
101
+ */
102
+ declare function machine_to_dot<T>(u_jssm: jssm.Machine<T>): string;
103
+ /**
104
+ * Render an FSL string directly to graphviz dot source.
105
+ *
106
+ * ```typescript
107
+ * import { fsl_to_dot } from 'jssm/viz';
108
+ * const dot = fsl_to_dot('a -> b;');
109
+ * ```
110
+ *
111
+ * @param fsl The FSL source.
112
+ * @returns A complete graphviz dot source string.
113
+ */
114
+ declare function fsl_to_dot(fsl: string): string;
115
+ /**
116
+ * Render a graphviz dot source string to SVG using `@viz-js/viz`. The
117
+ * underlying viz instance is lazy-initialized on first call and cached for
118
+ * the lifetime of the module.
119
+ *
120
+ * ```typescript
121
+ * const svg = await dot_to_svg('digraph G { a -> b }');
122
+ * ```
123
+ *
124
+ * @param dot Graphviz dot source.
125
+ * @returns A promise resolving to an SVG XML string.
126
+ */
127
+ declare function dot_to_svg(dot: string): Promise<string>;
128
+ /**
129
+ * Render an FSL string directly to SVG.
130
+ *
131
+ * @param fsl The FSL source.
132
+ * @returns A promise resolving to an SVG XML string.
133
+ */
134
+ declare function fsl_to_svg_string(fsl: string): Promise<string>;
135
+ /**
136
+ * Render a {@link jssm.Machine} to SVG.
137
+ *
138
+ * @param u_jssm The machine to render.
139
+ * @returns A promise resolving to an SVG XML string.
140
+ */
141
+ declare function machine_to_svg_string<T>(u_jssm: jssm.Machine<T>): Promise<string>;
142
+ /**
143
+ * Render an FSL string directly to a parsed `SVGSVGElement`.
144
+ *
145
+ * @param fsl The FSL source.
146
+ * @returns A promise resolving to a parsed `SVGSVGElement`.
147
+ * @throws {JssmError} if no `DOMParser` is available (Node without `configure`).
148
+ */
149
+ declare function fsl_to_svg_element(fsl: string): Promise<SVGSVGElement>;
150
+ /**
151
+ * Render a {@link jssm.Machine} to a parsed `SVGSVGElement`.
152
+ *
153
+ * @param u_jssm The machine to render.
154
+ * @returns A promise resolving to a parsed `SVGSVGElement`.
155
+ * @throws {JssmError} if no `DOMParser` is available (Node without `configure`).
156
+ */
157
+ declare function machine_to_svg_element<T>(u_jssm: jssm.Machine<T>): Promise<SVGSVGElement>;
158
+ /**
159
+ * Compatibility wrapper for {@link machine_to_dot}, retained from
160
+ * jssm-viz. Will be removed in the next major.
161
+ *
162
+ * @deprecated Use {@link machine_to_dot} instead.
163
+ */
164
+ declare function dot<T>(machine: jssm.Machine<T>): string;
165
+ export { configure, dot, dot_to_svg, fsl_to_dot, fsl_to_svg_string, fsl_to_svg_element, machine_to_dot, machine_to_svg_string, machine_to_svg_element, version, build_time };
166
+ /** @internal — test-only access to private helpers. */
167
+ export declare const _test: {
168
+ color8to6: typeof color8to6;
169
+ u_color8to6: typeof u_color8to6;
170
+ vc: typeof vc;
171
+ node_of: typeof node_of;
172
+ shape_for_state: typeof shape_for_state;
173
+ image_for_state: typeof image_for_state;
174
+ style_for_state: typeof style_for_state;
175
+ };