jssm 5.151.2 → 5.152.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
@@ -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.151.2 at 6/28/2026, 3:30:17 PM
21
+ * Generated for version 5.152.0 at 6/28/2026, 3:43:56 PM
22
22
 
23
23
  -->
24
- # jssm 5.151.2
24
+ # jssm 5.152.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,739 tests at 100.0% line coverage
315
+ library.** 7,760 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,739 tests***, run 82,385 times.
448
+ ***7,760 tests***, run 82,406 times.
449
449
 
450
- - 6,985 specs with 100.0% coverage
451
- - 754 fuzz tests with 56.0% coverage
452
- - 9,230 TypeScript lines - 0.8 tests per line, 8.9 generated tests per line
450
+ - 7,006 specs with 100.0% coverage
451
+ - 754 fuzz tests with 55.4% coverage
452
+ - 9,346 TypeScript lines - 0.8 tests per line, 8.8 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)
@@ -444,7 +444,7 @@
444
444
  "kind": "variable",
445
445
  "name": "FSL_COLOR_KEYS",
446
446
  "default": "new Set([ 'color', 'text-color', 'background-color', 'border-color', 'edge-color', ])",
447
- "description": "Attribute keys whose value is a color, mirroring the editor's `COLOR_KEYS`\n(language_service). The value token following one of these (plus its `:`) is\ntagged `color` and rendered with a swatch, matching the editor overlay."
447
+ "description": "Attribute keys whose value is a color, mirroring the editor's `COLOR_KEYS`\r\n(language_service). The value token following one of these (plus its `:`) is\r\ntagged `color` and rendered with a swatch, matching the editor overlay."
448
448
  },
449
449
  {
450
450
  "kind": "function",
@@ -462,7 +462,7 @@
462
462
  }
463
463
  }
464
464
  ],
465
- "description": "Tokenize FSL source into `{cls, text}` runs for syntax highlighting. A pure,\nregex-driven scanner — never parses, so it cannot throw on malformed input.\n`cls` is null for uncategorized text (punctuation, identifiers, whitespace).\n\nBeyond the lexical classes it tracks one bit of structural context: an\nidentifier immediately before a `:` is retro-tagged `key` (an attribute key,\nunless it is already a `keyword`), and the value token after a color key's\ncolon — or any hex literal — is tagged `color`. The context never spans a\n`;`, so a value can't leak past its statement."
465
+ "description": "Tokenize FSL source into `{cls, text}` runs for syntax highlighting. A pure,\r\nregex-driven scanner — never parses, so it cannot throw on malformed input.\r\n`cls` is null for uncategorized text (punctuation, identifiers, whitespace).\r\n\r\nBeyond the lexical classes it tracks one bit of structural context: an\r\nidentifier immediately before a `:` is retro-tagged `key` (an attribute key,\r\nunless it is already a `keyword`), and the value token after a color key's\r\ncolon — or any hex literal — is tagged `color`. The context never spans a\r\n`;`, so a value can't leak past its statement."
466
466
  },
467
467
  {
468
468
  "kind": "function",
@@ -480,7 +480,7 @@
480
480
  }
481
481
  }
482
482
  ],
483
- "description": "Highlight FSL source to an HTML string of `<span class=\"fsl-tok-…\">` runs.\nA `color` token is preceded by an inline `<span class=\"fsl-swatch\">` whose\nbackground is the literal color text (a CSS-valid named color or hex), giving\nthe docs the same swatch the editor overlay shows. Color text is a hex or\nidentifier run, so it is a safe `background:` value."
483
+ "description": "Highlight FSL source to an HTML string of `<span class=\"fsl-tok-…\">` runs.\r\nA `color` token is preceded by an inline `<span class=\"fsl-swatch\">` whose\r\nbackground is the literal color text (a CSS-valid named color or hex), giving\r\nthe docs the same swatch the editor overlay shows. Color text is a hex or\r\nidentifier run, so it is a safe `background:` value."
484
484
  },
485
485
  {
486
486
  "kind": "function",
@@ -558,7 +558,7 @@
558
558
  "declarations": [
559
559
  {
560
560
  "kind": "class",
561
- "description": "`<fsl-docs>` — the language-docs content engine: drill-in nav over the bundled\ncurriculum (Getting Started / About State Machines / Tutorials / Example\nMachines / Index / Search), a markdown page renderer, and \"load into editor\"\nfor tagged FSL examples. Content-only; slot it into `<fsl-help>`.",
561
+ "description": "`<fsl-docs>` — the language-docs content engine: drill-in nav over the bundled\r\ncurriculum (Getting Started / About State Machines / Tutorials / Example\r\nMachines / Index / Search), a markdown page renderer, and \"load into editor\"\r\nfor tagged FSL examples. Content-only; slot it into `<fsl-help>`.",
562
562
  "name": "FslDocs",
563
563
  "members": [
564
564
  {
@@ -2695,6 +2695,17 @@
2695
2695
  "privacy": "private",
2696
2696
  "description": "Gutter double-click: reset the split to 50/50."
2697
2697
  },
2698
+ {
2699
+ "kind": "method",
2700
+ "name": "_permalinkSegmentPresent",
2701
+ "privacy": "private",
2702
+ "return": {
2703
+ "type": {
2704
+ "text": "boolean"
2705
+ }
2706
+ },
2707
+ "description": "True when this instance owns a URL permalink segment (keyed by its\r\n`uhash`/`id`) that a pending FslPermalinkSync restore will turn into\r\nits FSL source — so `connectedCallback` can defer the machine build to that\r\nrestore instead of throwing on an otherwise-absent source."
2708
+ },
2698
2709
  {
2699
2710
  "kind": "method",
2700
2711
  "name": "_install_jssm_on_children",
@@ -3025,6 +3036,501 @@
3025
3036
  }
3026
3037
  ]
3027
3038
  },
3039
+ {
3040
+ "kind": "javascript-module",
3041
+ "path": "src/ts/wc/fsl_permalink.ts",
3042
+ "declarations": [
3043
+ {
3044
+ "kind": "variable",
3045
+ "name": "DEFAULT_PERMALINK_KEY",
3046
+ "type": {
3047
+ "text": "string"
3048
+ },
3049
+ "default": "'m'",
3050
+ "description": "Default fragment key for the single-machine case (back-compat with 5.150)."
3051
+ },
3052
+ {
3053
+ "kind": "function",
3054
+ "name": "bytes_to_base64url",
3055
+ "return": {
3056
+ "type": {
3057
+ "text": "string"
3058
+ }
3059
+ },
3060
+ "parameters": [
3061
+ {
3062
+ "name": "bytes",
3063
+ "type": {
3064
+ "text": "Uint8Array"
3065
+ }
3066
+ }
3067
+ ],
3068
+ "description": "URL-safe base64 (RFC 4648 §5) of raw bytes: standard base64 with `+`→`-`,\n`/`→`_`, and trailing `=` padding stripped."
3069
+ },
3070
+ {
3071
+ "kind": "function",
3072
+ "name": "base64url_to_bytes",
3073
+ "return": {
3074
+ "type": {
3075
+ "text": "Uint8Array"
3076
+ }
3077
+ },
3078
+ "parameters": [
3079
+ {
3080
+ "name": "text",
3081
+ "type": {
3082
+ "text": "string"
3083
+ }
3084
+ }
3085
+ ],
3086
+ "description": "Inverse of bytes_to_base64url."
3087
+ },
3088
+ {
3089
+ "kind": "function",
3090
+ "name": "deflate_raw",
3091
+ "return": {
3092
+ "type": {
3093
+ "text": "Promise<Uint8Array>"
3094
+ }
3095
+ },
3096
+ "parameters": [
3097
+ {
3098
+ "name": "bytes",
3099
+ "type": {
3100
+ "text": "Uint8Array"
3101
+ }
3102
+ }
3103
+ ],
3104
+ "description": "DEFLATE `bytes` (raw, headerless) via the platform `CompressionStream`."
3105
+ },
3106
+ {
3107
+ "kind": "function",
3108
+ "name": "inflate_raw",
3109
+ "return": {
3110
+ "type": {
3111
+ "text": "Promise<Uint8Array>"
3112
+ }
3113
+ },
3114
+ "parameters": [
3115
+ {
3116
+ "name": "bytes",
3117
+ "type": {
3118
+ "text": "Uint8Array"
3119
+ }
3120
+ }
3121
+ ],
3122
+ "description": "Inverse of deflate_raw."
3123
+ },
3124
+ {
3125
+ "kind": "function",
3126
+ "name": "encode_machine",
3127
+ "return": {
3128
+ "type": {
3129
+ "text": "Promise<string>"
3130
+ }
3131
+ },
3132
+ "parameters": [
3133
+ {
3134
+ "name": "fsl",
3135
+ "type": {
3136
+ "text": "string"
3137
+ }
3138
+ }
3139
+ ],
3140
+ "description": "Encode FSL to a `<scheme><payload>` segment value (the part after `key=`).\nDEFLATE is used (scheme `1`) only when it is strictly shorter than the raw\nbytes (scheme `0`)."
3141
+ },
3142
+ {
3143
+ "kind": "function",
3144
+ "name": "decode_machine",
3145
+ "return": {
3146
+ "type": {
3147
+ "text": "Promise<string>"
3148
+ }
3149
+ },
3150
+ "parameters": [
3151
+ {
3152
+ "name": "segment",
3153
+ "type": {
3154
+ "text": "string"
3155
+ }
3156
+ }
3157
+ ],
3158
+ "description": "Inverse of encode_machine: decode a `<scheme><payload>` segment back\nto FSL. Async because inflate is async."
3159
+ },
3160
+ {
3161
+ "kind": "function",
3162
+ "name": "read_fragment_param",
3163
+ "return": {
3164
+ "type": {
3165
+ "text": ""
3166
+ }
3167
+ },
3168
+ "parameters": [
3169
+ {
3170
+ "name": "hash",
3171
+ "type": {
3172
+ "text": "string"
3173
+ }
3174
+ },
3175
+ {
3176
+ "name": "key",
3177
+ "type": {
3178
+ "text": "string"
3179
+ }
3180
+ }
3181
+ ],
3182
+ "description": "Read one segment's value out of a `#a=…&b=…` fragment."
3183
+ },
3184
+ {
3185
+ "kind": "function",
3186
+ "name": "set_fragment_param",
3187
+ "return": {
3188
+ "type": {
3189
+ "text": "string"
3190
+ }
3191
+ },
3192
+ "parameters": [
3193
+ {
3194
+ "name": "hash",
3195
+ "type": {
3196
+ "text": "string"
3197
+ }
3198
+ },
3199
+ {
3200
+ "name": "key",
3201
+ "type": {
3202
+ "text": "string"
3203
+ }
3204
+ },
3205
+ {
3206
+ "name": "value",
3207
+ "type": {
3208
+ "text": "string"
3209
+ }
3210
+ }
3211
+ ],
3212
+ "description": "Return a new fragment body (no leading `#`) with `key`'s segment set to\n`value`, preserving every other segment and its order; appends if absent."
3213
+ },
3214
+ {
3215
+ "kind": "function",
3216
+ "name": "permalink_key_for",
3217
+ "return": {
3218
+ "type": {
3219
+ "text": "string | null"
3220
+ }
3221
+ },
3222
+ "parameters": [
3223
+ {
3224
+ "name": "host",
3225
+ "type": {
3226
+ "text": "HTMLElement"
3227
+ }
3228
+ }
3229
+ ],
3230
+ "description": "The fragment key an element owns: its `uhash` attribute if set, else its\n`id`, else `null` (does not participate in URL sync). The single source of\nthis rule, shared by the toolbar export and the sync controller."
3231
+ },
3232
+ {
3233
+ "kind": "function",
3234
+ "name": "permalink_for",
3235
+ "return": {
3236
+ "type": {
3237
+ "text": ""
3238
+ }
3239
+ },
3240
+ "parameters": [
3241
+ {
3242
+ "name": "fsl",
3243
+ "type": {
3244
+ "text": "string"
3245
+ }
3246
+ },
3247
+ {
3248
+ "name": "key",
3249
+ "default": "DEFAULT_PERMALINK_KEY",
3250
+ "type": {
3251
+ "text": "string"
3252
+ }
3253
+ },
3254
+ {
3255
+ "name": "href",
3256
+ "default": "location.href",
3257
+ "type": {
3258
+ "text": "string"
3259
+ }
3260
+ },
3261
+ {
3262
+ "name": "currentHash",
3263
+ "default": "location.hash",
3264
+ "type": {
3265
+ "text": "string"
3266
+ }
3267
+ }
3268
+ ],
3269
+ "description": "A shareable URL for `fsl` under `key`, merging into `currentHash` so sibling\nmachines' segments survive. Browser-defaulted (`location`) but injectable for\ntests."
3270
+ },
3271
+ {
3272
+ "kind": "function",
3273
+ "name": "fsl_from_permalink",
3274
+ "return": {
3275
+ "type": {
3276
+ "text": ""
3277
+ }
3278
+ },
3279
+ "parameters": [
3280
+ {
3281
+ "name": "url",
3282
+ "type": {
3283
+ "text": "string"
3284
+ }
3285
+ },
3286
+ {
3287
+ "name": "key",
3288
+ "default": "DEFAULT_PERMALINK_KEY",
3289
+ "type": {
3290
+ "text": "string"
3291
+ }
3292
+ }
3293
+ ],
3294
+ "description": "Recover the FSL for `key` from a permalink URL (or bare fragment)."
3295
+ }
3296
+ ],
3297
+ "exports": [
3298
+ {
3299
+ "kind": "js",
3300
+ "name": "DEFAULT_PERMALINK_KEY",
3301
+ "declaration": {
3302
+ "name": "DEFAULT_PERMALINK_KEY",
3303
+ "module": "src/ts/wc/fsl_permalink.ts"
3304
+ }
3305
+ },
3306
+ {
3307
+ "kind": "js",
3308
+ "name": "bytes_to_base64url",
3309
+ "declaration": {
3310
+ "name": "bytes_to_base64url",
3311
+ "module": "src/ts/wc/fsl_permalink.ts"
3312
+ }
3313
+ },
3314
+ {
3315
+ "kind": "js",
3316
+ "name": "base64url_to_bytes",
3317
+ "declaration": {
3318
+ "name": "base64url_to_bytes",
3319
+ "module": "src/ts/wc/fsl_permalink.ts"
3320
+ }
3321
+ },
3322
+ {
3323
+ "kind": "js",
3324
+ "name": "deflate_raw",
3325
+ "declaration": {
3326
+ "name": "deflate_raw",
3327
+ "module": "src/ts/wc/fsl_permalink.ts"
3328
+ }
3329
+ },
3330
+ {
3331
+ "kind": "js",
3332
+ "name": "inflate_raw",
3333
+ "declaration": {
3334
+ "name": "inflate_raw",
3335
+ "module": "src/ts/wc/fsl_permalink.ts"
3336
+ }
3337
+ },
3338
+ {
3339
+ "kind": "js",
3340
+ "name": "encode_machine",
3341
+ "declaration": {
3342
+ "name": "encode_machine",
3343
+ "module": "src/ts/wc/fsl_permalink.ts"
3344
+ }
3345
+ },
3346
+ {
3347
+ "kind": "js",
3348
+ "name": "decode_machine",
3349
+ "declaration": {
3350
+ "name": "decode_machine",
3351
+ "module": "src/ts/wc/fsl_permalink.ts"
3352
+ }
3353
+ },
3354
+ {
3355
+ "kind": "js",
3356
+ "name": "read_fragment_param",
3357
+ "declaration": {
3358
+ "name": "read_fragment_param",
3359
+ "module": "src/ts/wc/fsl_permalink.ts"
3360
+ }
3361
+ },
3362
+ {
3363
+ "kind": "js",
3364
+ "name": "set_fragment_param",
3365
+ "declaration": {
3366
+ "name": "set_fragment_param",
3367
+ "module": "src/ts/wc/fsl_permalink.ts"
3368
+ }
3369
+ },
3370
+ {
3371
+ "kind": "js",
3372
+ "name": "permalink_key_for",
3373
+ "declaration": {
3374
+ "name": "permalink_key_for",
3375
+ "module": "src/ts/wc/fsl_permalink.ts"
3376
+ }
3377
+ },
3378
+ {
3379
+ "kind": "js",
3380
+ "name": "permalink_for",
3381
+ "declaration": {
3382
+ "name": "permalink_for",
3383
+ "module": "src/ts/wc/fsl_permalink.ts"
3384
+ }
3385
+ },
3386
+ {
3387
+ "kind": "js",
3388
+ "name": "fsl_from_permalink",
3389
+ "declaration": {
3390
+ "name": "fsl_from_permalink",
3391
+ "module": "src/ts/wc/fsl_permalink.ts"
3392
+ }
3393
+ }
3394
+ ]
3395
+ },
3396
+ {
3397
+ "kind": "javascript-module",
3398
+ "path": "src/ts/wc/fsl_permalink_sync.ts",
3399
+ "declarations": [
3400
+ {
3401
+ "kind": "variable",
3402
+ "name": "PERMALINK_WRITE_DEBOUNCE_MS",
3403
+ "type": {
3404
+ "text": "number"
3405
+ },
3406
+ "default": "300",
3407
+ "description": "Debounce before a live edit is written to the URL fragment."
3408
+ },
3409
+ {
3410
+ "kind": "class",
3411
+ "description": "Binds an `<fsl-instance>` to a segment of the URL fragment: restores from it\non connect and writes back (debounced, via `history.replaceState`) whenever\nthe machine is rebuilt. Inert when the host has no key (permalink_key_for\nreturns `null`), so an `fsl-instance` without an `id`/`uhash` never touches\n`location`.\n\nEcho guard: `_last` holds the segment most recently read or written, so a\nrestore→rebuild→write cycle and a self-induced `hashchange` are both no-ops.",
3412
+ "name": "FslPermalinkSync",
3413
+ "members": [
3414
+ {
3415
+ "kind": "field",
3416
+ "name": "host",
3417
+ "type": {
3418
+ "text": "SyncHost"
3419
+ },
3420
+ "privacy": "private",
3421
+ "readonly": true,
3422
+ "default": "host"
3423
+ },
3424
+ {
3425
+ "kind": "field",
3426
+ "name": "key",
3427
+ "type": {
3428
+ "text": "string | null"
3429
+ },
3430
+ "privacy": "private",
3431
+ "default": "null"
3432
+ },
3433
+ {
3434
+ "kind": "field",
3435
+ "name": "_last",
3436
+ "type": {
3437
+ "text": "string | null"
3438
+ },
3439
+ "privacy": "private",
3440
+ "default": "null"
3441
+ },
3442
+ {
3443
+ "kind": "field",
3444
+ "name": "_timer",
3445
+ "type": {
3446
+ "text": "ReturnType<typeof setTimeout> | undefined"
3447
+ },
3448
+ "privacy": "private"
3449
+ },
3450
+ {
3451
+ "kind": "field",
3452
+ "name": "_onRebuilt",
3453
+ "privacy": "private",
3454
+ "readonly": true
3455
+ },
3456
+ {
3457
+ "kind": "field",
3458
+ "name": "_onHashChange",
3459
+ "privacy": "private",
3460
+ "readonly": true
3461
+ },
3462
+ {
3463
+ "kind": "method",
3464
+ "name": "hostConnected",
3465
+ "return": {
3466
+ "type": {
3467
+ "text": "void"
3468
+ }
3469
+ }
3470
+ },
3471
+ {
3472
+ "kind": "method",
3473
+ "name": "hostDisconnected",
3474
+ "return": {
3475
+ "type": {
3476
+ "text": "void"
3477
+ }
3478
+ }
3479
+ },
3480
+ {
3481
+ "kind": "method",
3482
+ "name": "_restore",
3483
+ "privacy": "private",
3484
+ "return": {
3485
+ "type": {
3486
+ "text": "Promise<void>"
3487
+ }
3488
+ },
3489
+ "description": "Read our segment and, if new, push it into the host (overriding declared source)."
3490
+ },
3491
+ {
3492
+ "kind": "method",
3493
+ "name": "_scheduleWrite",
3494
+ "privacy": "private",
3495
+ "return": {
3496
+ "type": {
3497
+ "text": "void"
3498
+ }
3499
+ }
3500
+ },
3501
+ {
3502
+ "kind": "method",
3503
+ "name": "_write",
3504
+ "privacy": "private",
3505
+ "return": {
3506
+ "type": {
3507
+ "text": "Promise<void>"
3508
+ }
3509
+ },
3510
+ "description": "Encode the current source and merge it into the fragment, history-silently."
3511
+ }
3512
+ ]
3513
+ }
3514
+ ],
3515
+ "exports": [
3516
+ {
3517
+ "kind": "js",
3518
+ "name": "PERMALINK_WRITE_DEBOUNCE_MS",
3519
+ "declaration": {
3520
+ "name": "PERMALINK_WRITE_DEBOUNCE_MS",
3521
+ "module": "src/ts/wc/fsl_permalink_sync.ts"
3522
+ }
3523
+ },
3524
+ {
3525
+ "kind": "js",
3526
+ "name": "FslPermalinkSync",
3527
+ "declaration": {
3528
+ "name": "FslPermalinkSync",
3529
+ "module": "src/ts/wc/fsl_permalink_sync.ts"
3530
+ }
3531
+ }
3532
+ ]
3533
+ },
3028
3534
  {
3029
3535
  "kind": "javascript-module",
3030
3536
  "path": "src/ts/wc/fsl_simulation_wc.ts",
@@ -3320,42 +3826,6 @@
3320
3826
  "kind": "javascript-module",
3321
3827
  "path": "src/ts/wc/fsl_toolbar_wc.ts",
3322
3828
  "declarations": [
3323
- {
3324
- "kind": "function",
3325
- "name": "permalink_for",
3326
- "return": {
3327
- "type": {
3328
- "text": ""
3329
- }
3330
- },
3331
- "parameters": [
3332
- {
3333
- "name": "fsl",
3334
- "type": {
3335
- "text": "string"
3336
- }
3337
- }
3338
- ],
3339
- "description": "A shareable URL for the given FSL: the current page URL with the source\r\ncompressed into the hash as `#m=<scheme><payload>`. The payload is URL-safe\r\nbase64, so no characters need percent-escaping; `<scheme>` is a single digit\r\n— `1` when DEFLATE made the source smaller, `0` for the raw bytes when it did\r\nnot — so a short machine's link is never longer than its uncompressed form.\r\nDecode with fsl_from_permalink. Browser-only (uses `location` and the\r\nplatform compression streams), like the rest of the toolbar."
3340
- },
3341
- {
3342
- "kind": "function",
3343
- "name": "fsl_from_permalink",
3344
- "return": {
3345
- "type": {
3346
- "text": ""
3347
- }
3348
- },
3349
- "parameters": [
3350
- {
3351
- "name": "url",
3352
- "type": {
3353
- "text": "string"
3354
- }
3355
- }
3356
- ],
3357
- "description": "Recover the FSL source from a permalink produced by permalink_for:\r\nread the `#m=<scheme><payload>` fragment, decode the URL-safe base64, and\r\ninflate when `<scheme>` is `1`. Accepts a full URL or a bare fragment, with\r\nor without the leading `#`."
3358
- },
3359
3829
  {
3360
3830
  "kind": "function",
3361
3831
  "name": "embed_snippet_for",