jssm 5.151.2 → 5.153.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 +7 -7
- package/custom-elements.json +506 -36
- package/dist/cdn/instance.js +496 -34
- package/dist/cdn/viz.js +229 -16
- package/dist/cli/fsl-export-system-prompt.cjs +1 -1
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/cli/lib.cjs +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/deno/README.md +7 -7
- package/dist/deno/jssm.d.ts +6 -4
- package/dist/deno/jssm.js +1 -1
- package/dist/deno/jssm_types.d.ts +2 -0
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -1
- package/dist/jssm_viz.iife.cjs +1 -1
- package/dist/jssm_viz.mjs +1 -1
- package/dist/wc/instance.js +291 -23
- package/dist/wc/widgets.js +111 -43
- package/jssm.es5.d.cts +8 -4
- package/jssm.es6.d.ts +8 -4
- package/jssm_viz.es5.d.cts +8 -4
- package/jssm_viz.es6.d.ts +8 -4
- package/package.json +1 -1
package/dist/cdn/instance.js
CHANGED
|
@@ -888,7 +888,19 @@ function peg$parse(input, options) {
|
|
|
888
888
|
node.loc = location();
|
|
889
889
|
}
|
|
890
890
|
return node;
|
|
891
|
-
}, peg$c1573 = peg$otherExpectation("
|
|
891
|
+
}, peg$c1573 = peg$otherExpectation("oarrange declaration"), peg$c1574 = "oarrange", peg$c1575 = peg$literalExpectation("oarrange", false), peg$c1576 = function (value) {
|
|
892
|
+
const node = { key: 'oarrange_declaration', value };
|
|
893
|
+
if (options.locations) {
|
|
894
|
+
node.loc = location();
|
|
895
|
+
}
|
|
896
|
+
return node;
|
|
897
|
+
}, peg$c1577 = peg$otherExpectation("farrange declaration"), peg$c1578 = "farrange", peg$c1579 = peg$literalExpectation("farrange", false), peg$c1580 = function (value) {
|
|
898
|
+
const node = { key: 'farrange_declaration', value };
|
|
899
|
+
if (options.locations) {
|
|
900
|
+
node.loc = location();
|
|
901
|
+
}
|
|
902
|
+
return node;
|
|
903
|
+
}, peg$c1581 = peg$otherExpectation("arrange declaration"), peg$currPos = 0, peg$savedPos = 0, peg$posDetailsCache = [{ line: 1, column: 1 }], peg$maxFailPos = 0, peg$maxFailExpected = [], peg$silentFails = 0, peg$result;
|
|
892
904
|
if ("startRule" in options) {
|
|
893
905
|
if (!(options.startRule in peg$startRuleFunctions)) {
|
|
894
906
|
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
|
|
@@ -20456,6 +20468,176 @@ function peg$parse(input, options) {
|
|
|
20456
20468
|
}
|
|
20457
20469
|
return s0;
|
|
20458
20470
|
}
|
|
20471
|
+
function peg$parseOArrangeDeclaration() {
|
|
20472
|
+
var s0, s1, s2, s3, s4, s5, s6;
|
|
20473
|
+
peg$silentFails++;
|
|
20474
|
+
s0 = peg$currPos;
|
|
20475
|
+
if (input.substr(peg$currPos, 8) === peg$c1574) {
|
|
20476
|
+
s1 = peg$c1574;
|
|
20477
|
+
peg$currPos += 8;
|
|
20478
|
+
}
|
|
20479
|
+
else {
|
|
20480
|
+
s1 = peg$FAILED;
|
|
20481
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20482
|
+
peg$fail(peg$c1575);
|
|
20483
|
+
}
|
|
20484
|
+
}
|
|
20485
|
+
if (s1 !== peg$FAILED) {
|
|
20486
|
+
s2 = peg$parseWS();
|
|
20487
|
+
if (s2 === peg$FAILED) {
|
|
20488
|
+
s2 = null;
|
|
20489
|
+
}
|
|
20490
|
+
if (s2 !== peg$FAILED) {
|
|
20491
|
+
s3 = peg$parseLabelOrLabelList();
|
|
20492
|
+
if (s3 !== peg$FAILED) {
|
|
20493
|
+
s4 = peg$parseWS();
|
|
20494
|
+
if (s4 === peg$FAILED) {
|
|
20495
|
+
s4 = null;
|
|
20496
|
+
}
|
|
20497
|
+
if (s4 !== peg$FAILED) {
|
|
20498
|
+
if (input.charCodeAt(peg$currPos) === 59) {
|
|
20499
|
+
s5 = peg$c1213;
|
|
20500
|
+
peg$currPos++;
|
|
20501
|
+
}
|
|
20502
|
+
else {
|
|
20503
|
+
s5 = peg$FAILED;
|
|
20504
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20505
|
+
peg$fail(peg$c1214);
|
|
20506
|
+
}
|
|
20507
|
+
}
|
|
20508
|
+
if (s5 !== peg$FAILED) {
|
|
20509
|
+
s6 = peg$parseWS();
|
|
20510
|
+
if (s6 === peg$FAILED) {
|
|
20511
|
+
s6 = null;
|
|
20512
|
+
}
|
|
20513
|
+
if (s6 !== peg$FAILED) {
|
|
20514
|
+
peg$savedPos = s0;
|
|
20515
|
+
s1 = peg$c1576(s3);
|
|
20516
|
+
s0 = s1;
|
|
20517
|
+
}
|
|
20518
|
+
else {
|
|
20519
|
+
peg$currPos = s0;
|
|
20520
|
+
s0 = peg$FAILED;
|
|
20521
|
+
}
|
|
20522
|
+
}
|
|
20523
|
+
else {
|
|
20524
|
+
peg$currPos = s0;
|
|
20525
|
+
s0 = peg$FAILED;
|
|
20526
|
+
}
|
|
20527
|
+
}
|
|
20528
|
+
else {
|
|
20529
|
+
peg$currPos = s0;
|
|
20530
|
+
s0 = peg$FAILED;
|
|
20531
|
+
}
|
|
20532
|
+
}
|
|
20533
|
+
else {
|
|
20534
|
+
peg$currPos = s0;
|
|
20535
|
+
s0 = peg$FAILED;
|
|
20536
|
+
}
|
|
20537
|
+
}
|
|
20538
|
+
else {
|
|
20539
|
+
peg$currPos = s0;
|
|
20540
|
+
s0 = peg$FAILED;
|
|
20541
|
+
}
|
|
20542
|
+
}
|
|
20543
|
+
else {
|
|
20544
|
+
peg$currPos = s0;
|
|
20545
|
+
s0 = peg$FAILED;
|
|
20546
|
+
}
|
|
20547
|
+
peg$silentFails--;
|
|
20548
|
+
if (s0 === peg$FAILED) {
|
|
20549
|
+
s1 = peg$FAILED;
|
|
20550
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20551
|
+
peg$fail(peg$c1573);
|
|
20552
|
+
}
|
|
20553
|
+
}
|
|
20554
|
+
return s0;
|
|
20555
|
+
}
|
|
20556
|
+
function peg$parseFArrangeDeclaration() {
|
|
20557
|
+
var s0, s1, s2, s3, s4, s5, s6;
|
|
20558
|
+
peg$silentFails++;
|
|
20559
|
+
s0 = peg$currPos;
|
|
20560
|
+
if (input.substr(peg$currPos, 8) === peg$c1578) {
|
|
20561
|
+
s1 = peg$c1578;
|
|
20562
|
+
peg$currPos += 8;
|
|
20563
|
+
}
|
|
20564
|
+
else {
|
|
20565
|
+
s1 = peg$FAILED;
|
|
20566
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20567
|
+
peg$fail(peg$c1579);
|
|
20568
|
+
}
|
|
20569
|
+
}
|
|
20570
|
+
if (s1 !== peg$FAILED) {
|
|
20571
|
+
s2 = peg$parseWS();
|
|
20572
|
+
if (s2 === peg$FAILED) {
|
|
20573
|
+
s2 = null;
|
|
20574
|
+
}
|
|
20575
|
+
if (s2 !== peg$FAILED) {
|
|
20576
|
+
s3 = peg$parseLabelOrLabelList();
|
|
20577
|
+
if (s3 !== peg$FAILED) {
|
|
20578
|
+
s4 = peg$parseWS();
|
|
20579
|
+
if (s4 === peg$FAILED) {
|
|
20580
|
+
s4 = null;
|
|
20581
|
+
}
|
|
20582
|
+
if (s4 !== peg$FAILED) {
|
|
20583
|
+
if (input.charCodeAt(peg$currPos) === 59) {
|
|
20584
|
+
s5 = peg$c1213;
|
|
20585
|
+
peg$currPos++;
|
|
20586
|
+
}
|
|
20587
|
+
else {
|
|
20588
|
+
s5 = peg$FAILED;
|
|
20589
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20590
|
+
peg$fail(peg$c1214);
|
|
20591
|
+
}
|
|
20592
|
+
}
|
|
20593
|
+
if (s5 !== peg$FAILED) {
|
|
20594
|
+
s6 = peg$parseWS();
|
|
20595
|
+
if (s6 === peg$FAILED) {
|
|
20596
|
+
s6 = null;
|
|
20597
|
+
}
|
|
20598
|
+
if (s6 !== peg$FAILED) {
|
|
20599
|
+
peg$savedPos = s0;
|
|
20600
|
+
s1 = peg$c1580(s3);
|
|
20601
|
+
s0 = s1;
|
|
20602
|
+
}
|
|
20603
|
+
else {
|
|
20604
|
+
peg$currPos = s0;
|
|
20605
|
+
s0 = peg$FAILED;
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
else {
|
|
20609
|
+
peg$currPos = s0;
|
|
20610
|
+
s0 = peg$FAILED;
|
|
20611
|
+
}
|
|
20612
|
+
}
|
|
20613
|
+
else {
|
|
20614
|
+
peg$currPos = s0;
|
|
20615
|
+
s0 = peg$FAILED;
|
|
20616
|
+
}
|
|
20617
|
+
}
|
|
20618
|
+
else {
|
|
20619
|
+
peg$currPos = s0;
|
|
20620
|
+
s0 = peg$FAILED;
|
|
20621
|
+
}
|
|
20622
|
+
}
|
|
20623
|
+
else {
|
|
20624
|
+
peg$currPos = s0;
|
|
20625
|
+
s0 = peg$FAILED;
|
|
20626
|
+
}
|
|
20627
|
+
}
|
|
20628
|
+
else {
|
|
20629
|
+
peg$currPos = s0;
|
|
20630
|
+
s0 = peg$FAILED;
|
|
20631
|
+
}
|
|
20632
|
+
peg$silentFails--;
|
|
20633
|
+
if (s0 === peg$FAILED) {
|
|
20634
|
+
s1 = peg$FAILED;
|
|
20635
|
+
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20636
|
+
peg$fail(peg$c1577);
|
|
20637
|
+
}
|
|
20638
|
+
}
|
|
20639
|
+
return s0;
|
|
20640
|
+
}
|
|
20459
20641
|
function peg$parseArrangeDeclaration() {
|
|
20460
20642
|
var s0;
|
|
20461
20643
|
peg$silentFails++;
|
|
@@ -20463,13 +20645,19 @@ function peg$parse(input, options) {
|
|
|
20463
20645
|
if (s0 === peg$FAILED) {
|
|
20464
20646
|
s0 = peg$parseArrangeEndDeclaration();
|
|
20465
20647
|
if (s0 === peg$FAILED) {
|
|
20466
|
-
s0 = peg$
|
|
20648
|
+
s0 = peg$parseOArrangeDeclaration();
|
|
20649
|
+
if (s0 === peg$FAILED) {
|
|
20650
|
+
s0 = peg$parseFArrangeDeclaration();
|
|
20651
|
+
if (s0 === peg$FAILED) {
|
|
20652
|
+
s0 = peg$parseRegularArrangeDeclaration();
|
|
20653
|
+
}
|
|
20654
|
+
}
|
|
20467
20655
|
}
|
|
20468
20656
|
}
|
|
20469
20657
|
peg$silentFails--;
|
|
20470
20658
|
if (s0 === peg$FAILED) {
|
|
20471
20659
|
if (peg$silentFails === 0 && peg$currPos >= peg$maxFailPos) {
|
|
20472
|
-
peg$fail(peg$
|
|
20660
|
+
peg$fail(peg$c1581);
|
|
20473
20661
|
}
|
|
20474
20662
|
}
|
|
20475
20663
|
return s0;
|
|
@@ -22370,7 +22558,8 @@ function compile_rule_handler(rule) {
|
|
|
22370
22558
|
return { agg_as: 'state_declaration', val: { state: rule.name, declarations: rule.value } };
|
|
22371
22559
|
}
|
|
22372
22560
|
if (['arrange_declaration', 'arrange_start_declaration',
|
|
22373
|
-
'arrange_end_declaration'
|
|
22561
|
+
'arrange_end_declaration', 'oarrange_declaration',
|
|
22562
|
+
'farrange_declaration'].includes(rule.key)) {
|
|
22374
22563
|
return { agg_as: rule.key, val: [rule.value] };
|
|
22375
22564
|
}
|
|
22376
22565
|
// things that can only exist once and are just a value under their own name
|
|
@@ -22576,6 +22765,8 @@ function compile(tree) {
|
|
|
22576
22765
|
arrange_declaration: [],
|
|
22577
22766
|
arrange_start_declaration: [],
|
|
22578
22767
|
arrange_end_declaration: [],
|
|
22768
|
+
oarrange_declaration: [],
|
|
22769
|
+
farrange_declaration: [],
|
|
22579
22770
|
machine_version: [],
|
|
22580
22771
|
default_state_config: [],
|
|
22581
22772
|
default_active_state_config: [],
|
|
@@ -22701,6 +22892,7 @@ function compile(tree) {
|
|
|
22701
22892
|
}
|
|
22702
22893
|
});
|
|
22703
22894
|
['arrange_declaration', 'arrange_start_declaration', 'arrange_end_declaration',
|
|
22895
|
+
'oarrange_declaration', 'farrange_declaration',
|
|
22704
22896
|
'machine_author', 'machine_contributor', 'machine_reference', 'theme',
|
|
22705
22897
|
'state_declaration', 'property_definition', 'default_state_config',
|
|
22706
22898
|
'default_start_state_config', 'default_end_state_config',
|
|
@@ -23371,7 +23563,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
23371
23563
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
23372
23564
|
* snapshots so that deserializers can detect version-skew.
|
|
23373
23565
|
*/
|
|
23374
|
-
const version = "5.
|
|
23566
|
+
const version = "5.153.0";
|
|
23375
23567
|
|
|
23376
23568
|
// whargarbl lots of these return arrays could/should be sets
|
|
23377
23569
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -23688,7 +23880,7 @@ function find_connected_components(states, edges) {
|
|
|
23688
23880
|
}
|
|
23689
23881
|
class Machine {
|
|
23690
23882
|
// whargarbl this badly needs to be broken up, monolith master
|
|
23691
|
-
constructor({ start_states, end_states = [], failed_outputs = [], initial_state, start_states_no_enforce, complete = [], transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, npm_name, default_size, state_declaration, property_definition, state_property, fsl_version, dot_preamble = undefined, arrange_declaration = [], arrange_start_declaration = [], arrange_end_declaration = [], theme = ['default'], flow = 'down', graph_layout = 'dot', instance_name, history, boundary_depth_limit, data, default_state_config, default_active_state_config, default_hooked_state_config, default_terminal_state_config, default_start_state_config, default_end_state_config, default_transition_config, default_graph_config, group_registry, group_metadata, group_hooks, state_hooks, allows_override, config_allows_override, allow_islands, editor_config, rng_seed, time_source, timeout_source, clear_timeout_source }) {
|
|
23883
|
+
constructor({ start_states, end_states = [], failed_outputs = [], initial_state, start_states_no_enforce, complete = [], transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, npm_name, default_size, state_declaration, property_definition, state_property, fsl_version, dot_preamble = undefined, arrange_declaration = [], arrange_start_declaration = [], arrange_end_declaration = [], oarrange_declaration = [], farrange_declaration = [], theme = ['default'], flow = 'down', graph_layout = 'dot', instance_name, history, boundary_depth_limit, data, default_state_config, default_active_state_config, default_hooked_state_config, default_terminal_state_config, default_start_state_config, default_end_state_config, default_transition_config, default_graph_config, group_registry, group_metadata, group_hooks, state_hooks, allows_override, config_allows_override, allow_islands, editor_config, rng_seed, time_source, timeout_source, clear_timeout_source }) {
|
|
23692
23884
|
this._time_source = time_source !== null && time_source !== void 0 ? time_source : (() => new Date().getTime());
|
|
23693
23885
|
this._create_started = this._time_source();
|
|
23694
23886
|
this._instance_name = instance_name;
|
|
@@ -23725,6 +23917,8 @@ class Machine {
|
|
|
23725
23917
|
this._arrange_declaration = arrange_declaration;
|
|
23726
23918
|
this._arrange_start_declaration = arrange_start_declaration;
|
|
23727
23919
|
this._arrange_end_declaration = arrange_end_declaration;
|
|
23920
|
+
this._oarrange_declaration = oarrange_declaration;
|
|
23921
|
+
this._farrange_declaration = farrange_declaration;
|
|
23728
23922
|
this._dot_preamble = dot_preamble;
|
|
23729
23923
|
this._themes = theme;
|
|
23730
23924
|
this._flow = flow;
|
|
@@ -24137,11 +24331,11 @@ class Machine {
|
|
|
24137
24331
|
}
|
|
24138
24332
|
this._created = this._time_source();
|
|
24139
24333
|
this.auto_set_state_timeout();
|
|
24140
|
-
this._arrange_declaration.forEach((arrange_pair) => arrange_pair.forEach((possibleState) => {
|
|
24334
|
+
[this._arrange_declaration, this._oarrange_declaration, this._farrange_declaration].forEach((declaration) => declaration.forEach((arrange_pair) => arrange_pair.forEach((possibleState) => {
|
|
24141
24335
|
if (!(this._states.has(possibleState))) {
|
|
24142
24336
|
throw new JssmError(this, `Cannot arrange state that does not exist "${possibleState}"`);
|
|
24143
24337
|
}
|
|
24144
|
-
}));
|
|
24338
|
+
})));
|
|
24145
24339
|
}
|
|
24146
24340
|
/********
|
|
24147
24341
|
*
|
|
@@ -24346,13 +24540,13 @@ class Machine {
|
|
|
24346
24540
|
* `;
|
|
24347
24541
|
*
|
|
24348
24542
|
* traffic_light.state(); // Off
|
|
24349
|
-
* traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true
|
|
24543
|
+
* traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true }
|
|
24350
24544
|
*
|
|
24351
24545
|
* traffic_light.go('Red');
|
|
24352
|
-
* traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true
|
|
24546
|
+
* traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true }
|
|
24353
24547
|
*
|
|
24354
24548
|
* traffic_light.go('Green');
|
|
24355
|
-
* traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false
|
|
24549
|
+
* traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false }
|
|
24356
24550
|
* ```
|
|
24357
24551
|
*
|
|
24358
24552
|
* @returns An object mapping every known property name to its current value
|
|
@@ -29037,6 +29231,242 @@ function build_hook_descriptor(spec, wrapped) {
|
|
|
29037
29231
|
return base;
|
|
29038
29232
|
}
|
|
29039
29233
|
|
|
29234
|
+
/**
|
|
29235
|
+
* Compressed, URL-safe permalink wire format for FSL machines, shared by the
|
|
29236
|
+
* toolbar's Export→Permalink and the per-instance URL sync controller.
|
|
29237
|
+
*
|
|
29238
|
+
* A machine is encoded to a `<scheme><payload>` segment: `<payload>` is URL-safe
|
|
29239
|
+
* base64, `<scheme>` is `1` when DEFLATE shrank the source and `0` for the raw
|
|
29240
|
+
* bytes when it did not (so a short machine's link never grows). Segments live in
|
|
29241
|
+
* a URL fragment as `#<key>=<segment>` joined by `&`, so several machines can
|
|
29242
|
+
* share one URL.
|
|
29243
|
+
*/
|
|
29244
|
+
/**
|
|
29245
|
+
* Default fragment key for the single-machine case (back-compat with 5.150).
|
|
29246
|
+
*
|
|
29247
|
+
* @example
|
|
29248
|
+
* DEFAULT_PERMALINK_KEY; // 'm'
|
|
29249
|
+
*/
|
|
29250
|
+
/**
|
|
29251
|
+
* URL-safe base64 (RFC 4648 §5) of raw bytes: standard base64 with `+`→`-`,
|
|
29252
|
+
* `/`→`_`, and trailing `=` padding stripped.
|
|
29253
|
+
*
|
|
29254
|
+
* @example
|
|
29255
|
+
* bytes_to_base64url(new TextEncoder().encode("a")); // "YQ"
|
|
29256
|
+
*/
|
|
29257
|
+
function bytes_to_base64url(bytes) {
|
|
29258
|
+
let binary = '';
|
|
29259
|
+
for (const byte of bytes) {
|
|
29260
|
+
binary += String.fromCharCode(byte);
|
|
29261
|
+
}
|
|
29262
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
29263
|
+
}
|
|
29264
|
+
/**
|
|
29265
|
+
* Inverse of {@link bytes_to_base64url}.
|
|
29266
|
+
*
|
|
29267
|
+
* @example
|
|
29268
|
+
* new TextDecoder().decode(base64url_to_bytes("YQ")); // "a"
|
|
29269
|
+
*/
|
|
29270
|
+
function base64url_to_bytes(text) {
|
|
29271
|
+
const standard = text.replace(/-/g, '+').replace(/_/g, '/');
|
|
29272
|
+
const padded = standard + '='.repeat((4 - (standard.length % 4)) % 4); // atob wants 4-aligned input
|
|
29273
|
+
const binary = atob(padded);
|
|
29274
|
+
const bytes = new Uint8Array(binary.length);
|
|
29275
|
+
for (let i = 0; i < binary.length; i++) {
|
|
29276
|
+
bytes[i] = binary.charCodeAt(i);
|
|
29277
|
+
}
|
|
29278
|
+
return bytes;
|
|
29279
|
+
}
|
|
29280
|
+
/**
|
|
29281
|
+
* DEFLATE `bytes` (raw, headerless) via the platform `CompressionStream`.
|
|
29282
|
+
*
|
|
29283
|
+
* @example
|
|
29284
|
+
* await deflate_raw(new TextEncoder().encode("aaaaaaaa")); // shorter Uint8Array of raw DEFLATE bytes
|
|
29285
|
+
*/
|
|
29286
|
+
async function deflate_raw(bytes) {
|
|
29287
|
+
const stream = new CompressionStream('deflate-raw');
|
|
29288
|
+
const writer = stream.writable.getWriter();
|
|
29289
|
+
void writer.write(bytes);
|
|
29290
|
+
void writer.close();
|
|
29291
|
+
return new Uint8Array(await new Response(stream.readable).arrayBuffer());
|
|
29292
|
+
}
|
|
29293
|
+
/**
|
|
29294
|
+
* Inverse of {@link deflate_raw}.
|
|
29295
|
+
*
|
|
29296
|
+
* @example
|
|
29297
|
+
* new TextDecoder().decode(await inflate_raw(await deflate_raw(new TextEncoder().encode("hi")))); // "hi"
|
|
29298
|
+
*/
|
|
29299
|
+
async function inflate_raw(bytes) {
|
|
29300
|
+
const stream = new DecompressionStream('deflate-raw');
|
|
29301
|
+
const writer = stream.writable.getWriter();
|
|
29302
|
+
void writer.write(bytes);
|
|
29303
|
+
void writer.close();
|
|
29304
|
+
return new Uint8Array(await new Response(stream.readable).arrayBuffer());
|
|
29305
|
+
}
|
|
29306
|
+
/**
|
|
29307
|
+
* Encode FSL to a `<scheme><payload>` segment value (the part after `key=`).
|
|
29308
|
+
* DEFLATE is used (scheme `1`) only when it is strictly shorter than the raw
|
|
29309
|
+
* bytes (scheme `0`).
|
|
29310
|
+
*
|
|
29311
|
+
* @example
|
|
29312
|
+
* await encode_machine("a -> b;"); // "0YSAtPiBiOw"
|
|
29313
|
+
*/
|
|
29314
|
+
async function encode_machine(fsl) {
|
|
29315
|
+
const utf8 = new TextEncoder().encode(fsl);
|
|
29316
|
+
const raw = bytes_to_base64url(utf8);
|
|
29317
|
+
const deflated = bytes_to_base64url(await deflate_raw(utf8));
|
|
29318
|
+
return deflated.length < raw.length ? `1${deflated}` : `0${raw}`;
|
|
29319
|
+
}
|
|
29320
|
+
/**
|
|
29321
|
+
* Inverse of {@link encode_machine}: decode a `<scheme><payload>` segment back
|
|
29322
|
+
* to FSL. Async because inflate is async.
|
|
29323
|
+
*
|
|
29324
|
+
* @example
|
|
29325
|
+
* await decode_machine("0YSAtPiBiOw"); // "a -> b;"
|
|
29326
|
+
*/
|
|
29327
|
+
async function decode_machine(segment) {
|
|
29328
|
+
const scheme = segment[0];
|
|
29329
|
+
const bytes = base64url_to_bytes(segment.slice(1));
|
|
29330
|
+
const plain = scheme === '1' ? await inflate_raw(bytes) : bytes;
|
|
29331
|
+
return new TextDecoder().decode(plain);
|
|
29332
|
+
}
|
|
29333
|
+
/** Split a fragment (leading `#` optional) into `[key, value]` pairs, dropping empties. */
|
|
29334
|
+
function fragment_pairs(hash) {
|
|
29335
|
+
const body = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
29336
|
+
return body.split('&').filter(Boolean).map(seg => {
|
|
29337
|
+
const eq = seg.indexOf('=');
|
|
29338
|
+
return eq === -1
|
|
29339
|
+
? [seg, '']
|
|
29340
|
+
: [seg.slice(0, eq), seg.slice(eq + 1)];
|
|
29341
|
+
});
|
|
29342
|
+
}
|
|
29343
|
+
/**
|
|
29344
|
+
* Read one segment's value out of a `#a=…&b=…` fragment.
|
|
29345
|
+
*
|
|
29346
|
+
* @returns The value, or `null` if `key` is absent.
|
|
29347
|
+
*
|
|
29348
|
+
* @example
|
|
29349
|
+
* read_fragment_param('#a=0AAA&b=1BBB', 'b'); // "1BBB"
|
|
29350
|
+
*/
|
|
29351
|
+
function read_fragment_param(hash, key) {
|
|
29352
|
+
const found = fragment_pairs(hash).find(([k]) => k === key);
|
|
29353
|
+
return found ? found[1] : null;
|
|
29354
|
+
}
|
|
29355
|
+
/**
|
|
29356
|
+
* Return a new fragment body (no leading `#`) with `key`'s segment set to
|
|
29357
|
+
* `value`, preserving every other segment and its order; appends if absent.
|
|
29358
|
+
*
|
|
29359
|
+
* @example
|
|
29360
|
+
* set_fragment_param('#a=0AAA', 'b', '1BBB'); // "a=0AAA&b=1BBB"
|
|
29361
|
+
*/
|
|
29362
|
+
function set_fragment_param(hash, key, value) {
|
|
29363
|
+
const pairs = fragment_pairs(hash);
|
|
29364
|
+
const at = pairs.findIndex(([k]) => k === key);
|
|
29365
|
+
if (at === -1) {
|
|
29366
|
+
pairs.push([key, value]);
|
|
29367
|
+
}
|
|
29368
|
+
else {
|
|
29369
|
+
pairs[at] = [key, value];
|
|
29370
|
+
}
|
|
29371
|
+
return pairs.map(([k, v]) => `${k}=${v}`).join('&');
|
|
29372
|
+
}
|
|
29373
|
+
/**
|
|
29374
|
+
* The fragment key an element owns: its `uhash` attribute if set, else its
|
|
29375
|
+
* `id`, else `null` (does not participate in URL sync). The single source of
|
|
29376
|
+
* this rule, shared by the toolbar export and the sync controller.
|
|
29377
|
+
*
|
|
29378
|
+
* @example
|
|
29379
|
+
* permalink_key_for(el); // "myId" (when <el id="myId">, no uhash)
|
|
29380
|
+
*/
|
|
29381
|
+
function permalink_key_for(host) {
|
|
29382
|
+
var _a, _b;
|
|
29383
|
+
return (_b = (_a = host.getAttribute('uhash')) !== null && _a !== void 0 ? _a : host.getAttribute('id')) !== null && _b !== void 0 ? _b : null;
|
|
29384
|
+
}
|
|
29385
|
+
|
|
29386
|
+
/** Debounce before a live edit is written to the URL fragment. */
|
|
29387
|
+
const PERMALINK_WRITE_DEBOUNCE_MS = 300;
|
|
29388
|
+
/**
|
|
29389
|
+
* Binds an `<fsl-instance>` to a segment of the URL fragment: restores from it
|
|
29390
|
+
* on connect and writes back (debounced, via `history.replaceState`) whenever
|
|
29391
|
+
* the machine is rebuilt. Inert when the host has no key ({@link permalink_key_for}
|
|
29392
|
+
* returns `null`), so an `fsl-instance` without an `id`/`uhash` never touches
|
|
29393
|
+
* `location`.
|
|
29394
|
+
*
|
|
29395
|
+
* Echo guard: `_last` holds the segment most recently read or written, so a
|
|
29396
|
+
* restore→rebuild→write cycle and a self-induced `hashchange` are both no-ops.
|
|
29397
|
+
*
|
|
29398
|
+
* @example
|
|
29399
|
+
* // In an element's constructor:
|
|
29400
|
+
* new FslPermalinkSync(this); // reads <el id="k">'s #k=… on connect, writes it on edit
|
|
29401
|
+
*/
|
|
29402
|
+
class FslPermalinkSync {
|
|
29403
|
+
constructor(host) {
|
|
29404
|
+
this.key = null;
|
|
29405
|
+
this._last = null;
|
|
29406
|
+
this._onRebuilt = () => { this._scheduleWrite(); };
|
|
29407
|
+
this._onHashChange = () => { void this._restore(); };
|
|
29408
|
+
this.host = host;
|
|
29409
|
+
host.addController(this);
|
|
29410
|
+
}
|
|
29411
|
+
hostConnected() {
|
|
29412
|
+
this.key = permalink_key_for(this.host);
|
|
29413
|
+
if (this.key === null) {
|
|
29414
|
+
return;
|
|
29415
|
+
}
|
|
29416
|
+
void this._restore();
|
|
29417
|
+
this.host.addEventListener('fsl-machine-rebuilt', this._onRebuilt);
|
|
29418
|
+
window.addEventListener('hashchange', this._onHashChange);
|
|
29419
|
+
}
|
|
29420
|
+
hostDisconnected() {
|
|
29421
|
+
if (this.key === null) {
|
|
29422
|
+
return;
|
|
29423
|
+
}
|
|
29424
|
+
this.host.removeEventListener('fsl-machine-rebuilt', this._onRebuilt);
|
|
29425
|
+
window.removeEventListener('hashchange', this._onHashChange);
|
|
29426
|
+
if (this._timer !== undefined) {
|
|
29427
|
+
clearTimeout(this._timer);
|
|
29428
|
+
this._timer = undefined;
|
|
29429
|
+
}
|
|
29430
|
+
}
|
|
29431
|
+
/** Read our segment and, if new, push it into the host (overriding declared source). */
|
|
29432
|
+
async _restore() {
|
|
29433
|
+
const segment = read_fragment_param(location.hash, this.key);
|
|
29434
|
+
if (segment === null || segment === this._last) {
|
|
29435
|
+
return;
|
|
29436
|
+
}
|
|
29437
|
+
try {
|
|
29438
|
+
const fsl = await decode_machine(segment);
|
|
29439
|
+
this._last = segment;
|
|
29440
|
+
this.host.fsl = fsl;
|
|
29441
|
+
}
|
|
29442
|
+
catch (_a) {
|
|
29443
|
+
// Malformed/truncated segment, or no compression support: leave the
|
|
29444
|
+
// declared source intact. A bad URL never bricks the page.
|
|
29445
|
+
}
|
|
29446
|
+
}
|
|
29447
|
+
_scheduleWrite() {
|
|
29448
|
+
if (this._timer !== undefined) {
|
|
29449
|
+
clearTimeout(this._timer);
|
|
29450
|
+
}
|
|
29451
|
+
this._timer = setTimeout(() => { void this._write(); }, PERMALINK_WRITE_DEBOUNCE_MS);
|
|
29452
|
+
}
|
|
29453
|
+
/** Encode the current source and merge it into the fragment, history-silently. */
|
|
29454
|
+
async _write() {
|
|
29455
|
+
try {
|
|
29456
|
+
const segment = await encode_machine(this.host.fsl);
|
|
29457
|
+
if (segment === this._last) {
|
|
29458
|
+
return;
|
|
29459
|
+
}
|
|
29460
|
+
this._last = segment;
|
|
29461
|
+
const fragment = set_fragment_param(location.hash, this.key, segment);
|
|
29462
|
+
history.replaceState(history.state, '', `#${fragment}`);
|
|
29463
|
+
}
|
|
29464
|
+
catch (_a) {
|
|
29465
|
+
// No compression support: skip the write rather than throw.
|
|
29466
|
+
}
|
|
29467
|
+
}
|
|
29468
|
+
}
|
|
29469
|
+
|
|
29040
29470
|
/**
|
|
29041
29471
|
* Theme registry for the fsl-* suite. A **theme** is a named pair of light/dark
|
|
29042
29472
|
* palettes; a **mode** (`system` | `light` | `dark`) picks which variant applies,
|
|
@@ -29434,8 +29864,10 @@ function split_ratio(coord, start, size) {
|
|
|
29434
29864
|
* @slot footer - Footer slot.
|
|
29435
29865
|
*/
|
|
29436
29866
|
class FslInstance extends i {
|
|
29867
|
+
/** Bind this instance to a URL-fragment segment keyed by its `uhash`/`id`
|
|
29868
|
+
* (inert if it has neither): restore on connect, write debounced on edit. */
|
|
29437
29869
|
constructor() {
|
|
29438
|
-
super(
|
|
29870
|
+
super();
|
|
29439
29871
|
/**
|
|
29440
29872
|
* FSL source attribute. When non-empty, this is the sole channel
|
|
29441
29873
|
* supplying the machine's source. Setting both this and a child
|
|
@@ -29587,6 +30019,7 @@ class FslInstance extends i {
|
|
|
29587
30019
|
this._split = 50;
|
|
29588
30020
|
this.requestUpdate();
|
|
29589
30021
|
};
|
|
30022
|
+
new FslPermalinkSync(this);
|
|
29590
30023
|
}
|
|
29591
30024
|
/**
|
|
29592
30025
|
* Raw machine accessor. Returns the owned {@link Machine} instance.
|
|
@@ -29790,30 +30223,43 @@ class FslInstance extends i {
|
|
|
29790
30223
|
super.connectedCallback();
|
|
29791
30224
|
// Step 1: resolve FSL source.
|
|
29792
30225
|
const resolved = resolve_fsl_source(this, this.fsl);
|
|
29793
|
-
|
|
30226
|
+
// A permalink-only instance has no declared source, but its own URL segment
|
|
30227
|
+
// will supply one asynchronously: FslPermalinkSync (attached in the
|
|
30228
|
+
// constructor) restores `fsl` just after connect, which rebuilds the machine
|
|
30229
|
+
// via willUpdate -> _rebuild_machine. Defer to that instead of throwing;
|
|
30230
|
+
// render() shows the placeholder until the restore lands.
|
|
30231
|
+
const deferToPermalink = resolved.provided_count === 0 && this._permalinkSegmentPresent();
|
|
30232
|
+
if (resolved.error !== undefined && !deferToPermalink) {
|
|
29794
30233
|
throw new Error(`fsl-instance: ${resolved.error}`);
|
|
29795
30234
|
}
|
|
29796
|
-
//
|
|
29797
|
-
//
|
|
29798
|
-
|
|
29799
|
-
|
|
29800
|
-
|
|
29801
|
-
|
|
29802
|
-
|
|
29803
|
-
|
|
29804
|
-
|
|
29805
|
-
|
|
29806
|
-
|
|
29807
|
-
|
|
29808
|
-
|
|
29809
|
-
|
|
29810
|
-
|
|
29811
|
-
|
|
29812
|
-
|
|
29813
|
-
|
|
29814
|
-
|
|
29815
|
-
|
|
29816
|
-
|
|
30235
|
+
// Steps 2-4 + machine-scoped wiring: only when a source is available now. In
|
|
30236
|
+
// the deferred case these run later, from _rebuild_machine, once the restore
|
|
30237
|
+
// sets `fsl`.
|
|
30238
|
+
if (!deferToPermalink) {
|
|
30239
|
+
// Step 2: construct the machine.
|
|
30240
|
+
// (The resolver guarantees `fsl` is a non-empty string when error is undefined.)
|
|
30241
|
+
const fsl_source = resolved.fsl;
|
|
30242
|
+
this._machine = this._build_machine(fsl_source);
|
|
30243
|
+
this._applyEditorConfig();
|
|
30244
|
+
// Step 3: paint initial host attributes + CSS custom properties.
|
|
30245
|
+
this._paint_state_reflection();
|
|
30246
|
+
// Step 4: shadow DOM render is automatic via Lit; requesting an update
|
|
30247
|
+
// here ensures the first paint sees the freshly painted attributes.
|
|
30248
|
+
this.requestUpdate();
|
|
30249
|
+
// #639 mechanism 4: subscribe to library events and re-emit them as
|
|
30250
|
+
// DOM CustomEvents from this host (#638 supplies the event API).
|
|
30251
|
+
this._install_event_reemission();
|
|
30252
|
+
// #641: <jssm-hook> declarative discovery.
|
|
30253
|
+
this._install_declarative_hooks();
|
|
30254
|
+
// #643: <jssm-on> declarative event observation.
|
|
30255
|
+
this._install_jssm_on_children();
|
|
30256
|
+
// #645: discover <jssm-bind> tags and `data-jssm-bind` descendants,
|
|
30257
|
+
// install live machine-to-DOM projections.
|
|
30258
|
+
this._unsubs.push(...install_bindings(this, this._machine));
|
|
30259
|
+
}
|
|
30260
|
+
// #640: <jssm-action> DOM event -> machine action wiring. The listeners read
|
|
30261
|
+
// `this.machine` live on event, so discovery is correct even before a
|
|
30262
|
+
// deferred build completes.
|
|
29817
30263
|
this._discover_jssm_actions();
|
|
29818
30264
|
// Theme: register the palette tokens as animatable colors (once, globally, so
|
|
29819
30265
|
// switches can ease), follow the OS while in `system` mode, then apply the
|
|
@@ -29825,6 +30271,16 @@ class FslInstance extends i {
|
|
|
29825
30271
|
}
|
|
29826
30272
|
this._applyTheme();
|
|
29827
30273
|
}
|
|
30274
|
+
/**
|
|
30275
|
+
* True when this instance owns a URL permalink segment (keyed by its
|
|
30276
|
+
* `uhash`/`id`) that a pending {@link FslPermalinkSync} restore will turn into
|
|
30277
|
+
* its FSL source — so `connectedCallback` can defer the machine build to that
|
|
30278
|
+
* restore instead of throwing on an otherwise-absent source.
|
|
30279
|
+
*/
|
|
30280
|
+
_permalinkSegmentPresent() {
|
|
30281
|
+
const key = permalink_key_for(this);
|
|
30282
|
+
return key !== null && read_fragment_param(location.hash, key) !== null;
|
|
30283
|
+
}
|
|
29828
30284
|
/**
|
|
29829
30285
|
* Discover direct-child `<jssm-on>` elements and install their
|
|
29830
30286
|
* subscriptions on the owned machine. Per #643:
|
|
@@ -30151,6 +30607,12 @@ class FslInstance extends i {
|
|
|
30151
30607
|
*/
|
|
30152
30608
|
_install_action_listener(config) {
|
|
30153
30609
|
const handler = (e) => {
|
|
30610
|
+
// A permalink-only instance wires its actions at connect but builds its
|
|
30611
|
+
// machine asynchronously (deferred restore). An event in that window is a
|
|
30612
|
+
// no-op rather than a throw via the `machine` getter.
|
|
30613
|
+
if (this._machine === undefined) {
|
|
30614
|
+
return;
|
|
30615
|
+
}
|
|
30154
30616
|
if (config.prevent_default) {
|
|
30155
30617
|
e.preventDefault();
|
|
30156
30618
|
}
|