foldkit 0.82.3 → 0.82.5
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/dist/devTools/overlay.d.ts.map +1 -1
- package/dist/devTools/overlay.js +23 -12
- package/dist/runtime/browserListeners.d.ts +1 -2
- package/dist/runtime/browserListeners.d.ts.map +1 -1
- package/dist/runtime/browserListeners.js +12 -12
- package/dist/runtime/messagePriority.d.ts +13 -0
- package/dist/runtime/messagePriority.d.ts.map +1 -0
- package/dist/runtime/messagePriority.js +12 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +102 -24
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,EAGN,OAAO,EAGP,MAAM,EAUP,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/devTools/overlay.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,MAAM,EAGN,OAAO,EAGP,MAAM,EAUP,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAA;AAW9C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAQ3E,OAAO,EAAE,KAAK,aAAa,EAA+B,MAAM,YAAY,CAAA;AA4N5E,eAAO,MAAM,MAAM;;EAA0C,CAAA;AAC7D,eAAO,MAAM,YAAY;;;;;;EAGxB,CAAA;AACD,eAAO,MAAM,aAAa;;;;;;EAGzB,CAAA;AACD,eAAO,MAAM,MAAM;;EAA4C,CAAA;AAC/D,eAAO,MAAM,KAAK;;EAA0C,CAAA;AAC5D,eAAO,MAAM,UAAU;;EAA6C,CAAA;AACpE,eAAO,MAAM,YAAY;;EAAiD,CAAA;AAC1E,eAAO,MAAM,WAAW;;EAA+C,CAAA;AA+2CvE,eAAO,MAAM,aAAa,GACxB,OAAO,aAAa,EACpB,UAAU,gBAAgB,EAC1B,MAAM,YAAY,EAClB,aAAa,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,sCAoDhC,CAAA"}
|
package/dist/devTools/overlay.js
CHANGED
|
@@ -2,7 +2,7 @@ import { clsx } from 'clsx';
|
|
|
2
2
|
import { Array as Array_, Effect, Equal, Function, HashSet, Match as M, Number as Number_, Option, Order, Predicate, Queue, Record, Schema as S, Stream, String as String_, SubscriptionRef, pipe, } from 'effect';
|
|
3
3
|
import * as Command from '../command/index.js';
|
|
4
4
|
import { OptionExt } from '../effectExtensions/index.js';
|
|
5
|
-
import { createKeyedLazy, html, } from '../html/index.js';
|
|
5
|
+
import { createKeyedLazy, createLazy, html, } from '../html/index.js';
|
|
6
6
|
import { m } from '../message/index.js';
|
|
7
7
|
import { makeProgram } from '../runtime/runtime.js';
|
|
8
8
|
import { makeSubscriptions } from '../runtime/subscription.js';
|
|
@@ -390,6 +390,7 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
390
390
|
const lazyTreeNode = createKeyedLazy();
|
|
391
391
|
const lazyMessageRow = createKeyedLazy();
|
|
392
392
|
const lazyTabContent = createKeyedLazy();
|
|
393
|
+
const lazyMessageList = createLazy();
|
|
393
394
|
// JSON TREE
|
|
394
395
|
const leafValueView = (value) => M.value(value).pipe(M.when(Predicate.isNull, () => span([Class('json-null italic')], ['null'])), M.when(Predicate.isUndefined, () => span([Class('json-null italic')], ['undefined'])), M.when(Predicate.isString, stringValue => span([Class('json-string')], [`"${stringValue}"`])), M.when(Predicate.isNumber, numberValue => span([Class('json-number')], [String(numberValue)])), M.when(Predicate.isBoolean, booleanValue => span([Class('json-boolean')], [String(booleanValue)])), M.orElse(unknownValue => span([Class('json-null')], [String(unknownValue)])));
|
|
395
396
|
const keyView = (key) => span([Class('json-key')], [`${key}:\u00a0`]);
|
|
@@ -786,27 +787,22 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
786
787
|
Class('text-2xs text-dt-muted font-mono shrink-0 text-right min-w-5'),
|
|
787
788
|
], [formatTimeDelta(timeDelta)]),
|
|
788
789
|
]);
|
|
789
|
-
const
|
|
790
|
-
const baseTimestamp = pipe(
|
|
790
|
+
const messageListBody = (entries, startIndex, selectedIndex, isPaused, pausedAtIndex, maybeFilterTag) => {
|
|
791
|
+
const baseTimestamp = pipe(entries, Array_.head, Option.match({
|
|
791
792
|
onNone: () => 0,
|
|
792
793
|
onSome: ({ timestamp }) => timestamp,
|
|
793
794
|
}));
|
|
794
|
-
const lastIndex = Array_.isReadonlyArrayEmpty(model.entries)
|
|
795
|
-
? INIT_INDEX
|
|
796
|
-
: model.startIndex + model.entries.length - 1;
|
|
797
|
-
const selectedIndex = M.value(mode).pipe(M.when('TimeTravel', () => model.isPaused ? model.pausedAtIndex : lastIndex), M.when('Inspect', () => model.selectedIndex), M.exhaustive);
|
|
798
795
|
const isInitSelected = selectedIndex === INIT_INDEX;
|
|
799
|
-
const { maybeSubmodelFilter: maybeFilterTag } = model;
|
|
800
796
|
const isFiltered = Option.isSome(maybeFilterTag);
|
|
801
|
-
const indexedEntries = pipe(
|
|
797
|
+
const indexedEntries = pipe(entries, Array_.map((entry, arrayIndex) => ({
|
|
802
798
|
entry,
|
|
803
|
-
absoluteIndex:
|
|
799
|
+
absoluteIndex: startIndex + arrayIndex,
|
|
804
800
|
})), isFiltered
|
|
805
801
|
? Array_.filter(({ entry }) => Array_.contains(entry.submodelPath, maybeFilterTag.value))
|
|
806
802
|
: Function.identity);
|
|
807
803
|
const messageRows = pipe(indexedEntries, Array_.map(({ entry, absoluteIndex }) => {
|
|
808
804
|
const isSelected = selectedIndex === absoluteIndex;
|
|
809
|
-
const isPausedHere =
|
|
805
|
+
const isPausedHere = isPaused && pausedAtIndex === absoluteIndex;
|
|
810
806
|
const displayTag = isFiltered
|
|
811
807
|
? pipe(entry.submodelPath, Array_.findFirstIndex(Equal.equals(maybeFilterTag.value)), Option.flatMap(filterIndex => Array_.get(entry.submodelPath, Number_.increment(filterIndex))), Option.orElse(() => entry.maybeLeafTag), Option.getOrElse(() => entry.tag))
|
|
812
808
|
: entry.tag;
|
|
@@ -823,9 +819,24 @@ const makeView = (position, mode, maybeBanner) => {
|
|
|
823
819
|
? messageRows
|
|
824
820
|
: [
|
|
825
821
|
...messageRows,
|
|
826
|
-
initRowView(isInitSelected,
|
|
822
|
+
initRowView(isInitSelected, isPaused && pausedAtIndex === INIT_INDEX),
|
|
827
823
|
]);
|
|
828
824
|
};
|
|
825
|
+
const messageListView = (model) => {
|
|
826
|
+
const lastIndex = Array_.match(model.entries, {
|
|
827
|
+
onEmpty: () => INIT_INDEX,
|
|
828
|
+
onNonEmpty: () => model.startIndex + model.entries.length - 1,
|
|
829
|
+
});
|
|
830
|
+
const selectedIndex = M.value(mode).pipe(M.when('TimeTravel', () => model.isPaused ? model.pausedAtIndex : lastIndex), M.when('Inspect', () => model.selectedIndex), M.exhaustive);
|
|
831
|
+
return lazyMessageList(messageListBody, [
|
|
832
|
+
model.entries,
|
|
833
|
+
model.startIndex,
|
|
834
|
+
selectedIndex,
|
|
835
|
+
model.isPaused,
|
|
836
|
+
model.pausedAtIndex,
|
|
837
|
+
model.maybeSubmodelFilter,
|
|
838
|
+
]);
|
|
839
|
+
};
|
|
829
840
|
// PANEL
|
|
830
841
|
const panelView = (model) => keyed('div')('dt-panel', [
|
|
831
842
|
Class(clsx('fixed dt-panel dt-panel-wide bg-dt-bg border rounded-lg flex flex-col overflow-hidden font-mono text-dt', PANEL_POSITION_CLASS[position])),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Queue } from 'effect';
|
|
2
1
|
import { RoutingConfig } from './runtime.js';
|
|
3
|
-
export declare const addNavigationEventListeners: <Message>(
|
|
2
|
+
export declare const addNavigationEventListeners: <Message>(dispatch: (message: Message) => void, routingConfig: RoutingConfig<Message>) => void;
|
|
4
3
|
export declare const addBfcacheRestoreListener: () => void;
|
|
5
4
|
//# sourceMappingURL=browserListeners.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browserListeners.d.ts","sourceRoot":"","sources":["../../src/runtime/browserListeners.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browserListeners.d.ts","sourceRoot":"","sources":["../../src/runtime/browserListeners.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAG5C,eAAO,MAAM,2BAA2B,GAAI,OAAO,EACjD,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EACpC,eAAe,aAAa,CAAC,OAAO,CAAC,SAKtC,CAAA;AA6ED,eAAO,MAAM,yBAAyB,YASrC,CAAA"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Option,
|
|
1
|
+
import { Option, String } from 'effect';
|
|
2
2
|
import { OptionExt, StringExt } from '../effectExtensions/index.js';
|
|
3
3
|
import { External, Internal } from './urlRequest.js';
|
|
4
|
-
export const addNavigationEventListeners = (
|
|
5
|
-
addPopStateListener(
|
|
6
|
-
addLinkClickListener(
|
|
7
|
-
addProgrammaticNavigationListener(
|
|
4
|
+
export const addNavigationEventListeners = (dispatch, routingConfig) => {
|
|
5
|
+
addPopStateListener(dispatch, routingConfig);
|
|
6
|
+
addLinkClickListener(dispatch, routingConfig);
|
|
7
|
+
addProgrammaticNavigationListener(dispatch, routingConfig);
|
|
8
8
|
};
|
|
9
|
-
const addPopStateListener = (
|
|
9
|
+
const addPopStateListener = (dispatch, routingConfig) => {
|
|
10
10
|
const onPopState = () => {
|
|
11
|
-
|
|
11
|
+
dispatch(routingConfig.onUrlChange(locationToUrl()));
|
|
12
12
|
};
|
|
13
13
|
window.addEventListener('popstate', onPopState);
|
|
14
14
|
};
|
|
15
|
-
const addLinkClickListener = (
|
|
15
|
+
const addLinkClickListener = (dispatch, routingConfig) => {
|
|
16
16
|
const onLinkClick = (event) => {
|
|
17
17
|
const target = event.target;
|
|
18
18
|
if (!(target instanceof Element)) {
|
|
@@ -30,16 +30,16 @@ const addLinkClickListener = (messageQueue, routingConfig) => {
|
|
|
30
30
|
const linkUrl = new URL(href);
|
|
31
31
|
const currentUrl = new URL(window.location.href);
|
|
32
32
|
if (linkUrl.origin !== currentUrl.origin) {
|
|
33
|
-
|
|
33
|
+
dispatch(routingConfig.onUrlRequest(External({ href })));
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
dispatch(routingConfig.onUrlRequest(Internal({ url: urlToFoldkitUrl(linkUrl) })));
|
|
37
37
|
};
|
|
38
38
|
document.addEventListener('click', onLinkClick);
|
|
39
39
|
};
|
|
40
|
-
const addProgrammaticNavigationListener = (
|
|
40
|
+
const addProgrammaticNavigationListener = (dispatch, routingConfig) => {
|
|
41
41
|
const onProgrammaticNavigation = () => {
|
|
42
|
-
|
|
42
|
+
dispatch(routingConfig.onUrlChange(locationToUrl()));
|
|
43
43
|
};
|
|
44
44
|
window.addEventListener('foldkit:urlchange', onProgrammaticNavigation);
|
|
45
45
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Priority = 'High' | 'Normal';
|
|
2
|
+
export type EnvelopedMessage<Message> = Readonly<{
|
|
3
|
+
priority: Priority;
|
|
4
|
+
message: Message;
|
|
5
|
+
}>;
|
|
6
|
+
/** Reorders a batch of EnvelopedMessages so all `High` envelopes appear
|
|
7
|
+
* before any `Normal` envelope, preserving FIFO order within each priority
|
|
8
|
+
* class. The runtime calls this on each `Queue.takeAll` batch so user input
|
|
9
|
+
* (view dispatch, navigation, subscription events, managed-resource events,
|
|
10
|
+
* external dispatchers) lands ahead of chain-derived work (Command results)
|
|
11
|
+
* whenever both share a frame. */
|
|
12
|
+
export declare const orderByPriority: <Message>(batch: ReadonlyArray<EnvelopedMessage<Message>>) => ReadonlyArray<Message>;
|
|
13
|
+
//# sourceMappingURL=messagePriority.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messagePriority.d.ts","sourceRoot":"","sources":["../../src/runtime/messagePriority.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAExC,MAAM,MAAM,gBAAgB,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC/C,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF;;;;;mCAKmC;AACnC,eAAO,MAAM,eAAe,GAAI,OAAO,EACrC,OAAO,aAAa,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAC9C,aAAa,CAAC,OAAO,CAUvB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Array } from 'effect';
|
|
2
|
+
/** Reorders a batch of EnvelopedMessages so all `High` envelopes appear
|
|
3
|
+
* before any `Normal` envelope, preserving FIFO order within each priority
|
|
4
|
+
* class. The runtime calls this on each `Queue.takeAll` batch so user input
|
|
5
|
+
* (view dispatch, navigation, subscription events, managed-resource events,
|
|
6
|
+
* external dispatchers) lands ahead of chain-derived work (Command results)
|
|
7
|
+
* whenever both share a frame. */
|
|
8
|
+
export const orderByPriority = (batch) => {
|
|
9
|
+
const highs = Array.filter(batch, envelope => envelope.priority === 'High');
|
|
10
|
+
const normals = Array.filter(batch, envelope => envelope.priority === 'Normal');
|
|
11
|
+
return Array.map(Array.appendAll(highs, normals), envelope => envelope.message);
|
|
12
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,OAAO,EACP,MAAM,EAGN,KAAK,EAEL,MAAM,EAON,MAAM,EAIP,MAAM,QAAQ,CAAA;AAGf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,GAAG,EAA+B,MAAM,iBAAiB,CAAA;AAalE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,OAAO,EACP,MAAM,EAGN,KAAK,EAEL,MAAM,EAON,MAAM,EAIP,MAAM,QAAQ,CAAA;AAGf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,GAAG,EAA+B,MAAM,iBAAiB,CAAA;AAalE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAO5C,0DAA0D;AAC1D,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,YAAY,GACZ,UAAU,GACV,SAAS,CAAA;AAEb,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,QAAQ,CAAA;AAEjD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,YAAY,CAAA;AAEnD;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,QAAQ,CAAC;IACP,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAA;IAC3B,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;CACnD,CAAC,CAAA;AAMN,sFAAsF;AACtF,MAAM,MAAM,eAAe,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IACrD,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,EAAE,OAAO,IACrC,KAAK,GACL,QAAQ,CAAC;IACP,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CAChE,CAAC,CAAA;;4BA6BsB,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;2BAC1C,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;;AALrD,8EAA8E;AAC9E,qBAAa,QAAS,SAAQ,aAMN;CAAG;AAE3B,YAAY,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,oFAAoF;AACpF,MAAM,MAAM,aAAa,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC5C,YAAY,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAA;IAC9C,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAA;CACnC,CAAC,CAAA;AAEF,0GAA0G;AAC1G,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAClD,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEF,iFAAiF;AACjF,MAAM,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IACjD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,QAAQ,CAAA;IAC1D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACzD,CAAC,CAAA;AAwEF,KAAK,iBAAiB,CACpB,KAAK,EACL,OAAO,EACP,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,QAAQ,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,MAAM,EAAE,CACN,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,KACb,SAAS;QACZ,KAAK;QACL,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAAC;KAC5E,CAAA;IACD,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,QAAQ,CAAA;IAChC,aAAa,CAAC,EAAE,aAAa,CAC3B,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,GAAG,uBAAuB,CACpC,CAAA;IACD,SAAS,EAAE,WAAW,CAAA;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACnC,QAAQ,CAAC,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACzC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAClC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAA;IAC5E,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B,CAAC,CAAA;AAEF,kEAAkE;AAClE,MAAM,MAAM,6BAA6B,CACvC,KAAK,EACL,OAAO,EACP,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,KAAK,EACL,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,iBAAiB,CACnB,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACC,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IAC/B,IAAI,EAAE,CACJ,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,GAAG,KACL,SAAS;QACZ,KAAK;QACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;KACF,CAAA;CACF,CAAC,CAAA;AAEJ,qEAAqE;AACrE,MAAM,MAAM,oBAAoB,CAC9B,KAAK,EACL,OAAO,EACP,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,iBAAiB,CACnB,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACC,QAAQ,CAAC;IACP,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IAC/B,IAAI,EAAE,CACJ,GAAG,EAAE,GAAG,KACL,SAAS;QACZ,KAAK;QACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;KACF,CAAA;CACF,CAAC,CAAA;AAEJ,qEAAqE;AACrE,MAAM,MAAM,sBAAsB,CAChC,KAAK,EACL,OAAO,EACP,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,KAAK,EACL,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,iBAAiB,CACnB,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACC,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,IAAI,EAAE,CACJ,KAAK,EAAE,KAAK,KACT,SAAS;QACZ,KAAK;QACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;KACF,CAAA;CACF,CAAC,CAAA;AAEJ,oEAAoE;AACpE,MAAM,MAAM,aAAa,CACvB,KAAK,EACL,OAAO,EACP,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,iBAAiB,CACnB,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACC,QAAQ,CAAC;IACP,IAAI,EAAE,MAAM,SAAS;QACnB,KAAK;QACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;KACF,CAAA;CACF,CAAC,CAAA;AAEJ,iEAAiE;AACjE,MAAM,MAAM,WAAW,CACrB,KAAK,EACL,OAAO,EACP,KAAK,GAAG,IAAI,EACZ,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,KAAK,SAAS,IAAI,GAClB,MAAM,SAAS;IACb,KAAK;IACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;CACF,GACD,CACE,KAAK,EAAE,KAAK,KACT,SAAS;IACZ,KAAK;IACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;CACF,CAAA;AAEL,2GAA2G;AAC3G,MAAM,MAAM,kBAAkB,CAC5B,KAAK,EACL,OAAO,EACP,KAAK,GAAG,IAAI,EACZ,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,IAC7B,KAAK,SAAS,IAAI,GAClB,CACE,GAAG,EAAE,GAAG,KACL,SAAS;IACZ,KAAK;IACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;CACF,GACD,CACE,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,GAAG,KACL,SAAS;IACZ,KAAK;IACL,aAAa,CACX,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,uBAAuB,CAAC,CAC7D;CACF,CAAA;AAEL,wGAAwG;AACxG,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CACnD,CAAC,CAAA;AAuvBF,2HAA2H;AAC3H,wBAAgB,WAAW,CACzB,KAAK,EACL,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,KAAK,EACL,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,EAE/B,MAAM,EAAE,6BAA6B,CACnC,KAAK,EACL,OAAO,EACP,aAAa,EACb,KAAK,EACL,SAAS,EACT,uBAAuB,CACxB,GACA,iBAAiB,CAAA;AAEpB,wBAAgB,WAAW,CACzB,KAAK,EACL,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,EAE/B,MAAM,EAAE,oBAAoB,CAC1B,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACA,iBAAiB,CAAA;AAEpB,wBAAgB,WAAW,CACzB,KAAK,EACL,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,KAAK,EACL,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,EAE/B,MAAM,EAAE,sBAAsB,CAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,KAAK,EACL,SAAS,EACT,uBAAuB,CACxB,GACA,iBAAiB,CAAA;AAEpB,wBAAgB,WAAW,CACzB,KAAK,EACL,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,aAAa,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzD,SAAS,GAAG,KAAK,EACjB,uBAAuB,GAAG,KAAK,EAE/B,MAAM,EAAE,aAAa,CACnB,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,uBAAuB,CACxB,GACA,iBAAiB,CAAA;AAqNpB,kEAAkE;AAClE,eAAO,MAAM,GAAG,GAAI,SAAS,iBAAiB,KAAG,IA4ChD,CAAA"}
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -10,6 +10,7 @@ import { addBfcacheRestoreListener, addNavigationEventListeners, } from './brows
|
|
|
10
10
|
import { defaultCrashView, noOpDispatch } from './crashUI.js';
|
|
11
11
|
import { deepFreeze } from './deepFreeze.js';
|
|
12
12
|
import { PreserveModelMessage, RequestModelMessage, RestoreModelMessage, } from './hmrProtocol.js';
|
|
13
|
+
import { orderByPriority } from './messagePriority.js';
|
|
13
14
|
const DEFAULT_DEV_TOOLS_SHOW = 'Development';
|
|
14
15
|
const DEFAULT_DEV_TOOLS_POSITION = 'BottomRight';
|
|
15
16
|
const DEFAULT_DEV_TOOLS_MODE = 'TimeTravel';
|
|
@@ -38,6 +39,28 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
38
39
|
const isFreezeModelActive = freezeModel !== false && !!import.meta.hot;
|
|
39
40
|
const maybeFreezeModel = (model) => isFreezeModelActive ? deepFreeze(model) : model;
|
|
40
41
|
const runtimeId = container?.id ?? '';
|
|
42
|
+
// NOTE: When the message queue drains a chain of dispatches (e.g. recursive
|
|
43
|
+
// Commands, websocket bursts), processing all of them inside one macrotask
|
|
44
|
+
// blocks the browser from painting. Yield via MessageChannel once the
|
|
45
|
+
// current burst exceeds FRAME_BUDGET_MS so the browser gets a frame.
|
|
46
|
+
// setTimeout(0) is clamped to 4ms+; MessageChannel delivers in ~0.5ms.
|
|
47
|
+
const FRAME_BUDGET_MS = 5;
|
|
48
|
+
const yieldToBrowser = Effect.callback(resume => {
|
|
49
|
+
const channel = new MessageChannel();
|
|
50
|
+
channel.port2.onmessage = () => resume(Effect.void);
|
|
51
|
+
channel.port1.postMessage(null);
|
|
52
|
+
return Effect.sync(() => channel.port2.close());
|
|
53
|
+
});
|
|
54
|
+
// NOTE: render coalescing relies on this firing once per frame. Multiple
|
|
55
|
+
// Messages dispatched between frames all flag the renderLoop dirty; the
|
|
56
|
+
// next rAF tick reads the latest model and renders once. Without this,
|
|
57
|
+
// every Message would call render() inline, and during high-rate streams
|
|
58
|
+
// (drag pointermove, websocket bursts) the runtime would paint each
|
|
59
|
+
// intermediate frame with the cursor leading the rendered position.
|
|
60
|
+
const awaitNextFrame = Effect.callback(resume => {
|
|
61
|
+
const handle = requestAnimationFrame(() => resume(Effect.void));
|
|
62
|
+
return Effect.sync(() => cancelAnimationFrame(handle));
|
|
63
|
+
});
|
|
41
64
|
const start = (hmrModel) => Effect.scoped(Effect.gen(function* () {
|
|
42
65
|
if (runtimeId === '') {
|
|
43
66
|
return yield* Effect.die(new Error('[foldkit] Runtime container must have an `id` for HMR model preservation. ' +
|
|
@@ -80,8 +103,18 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
80
103
|
Model);
|
|
81
104
|
const decodeHmrModel = Schema.decodeUnknownExit(ModelJsonCodec);
|
|
82
105
|
const encodeHmrModel = Schema.encodeUnknownSync(ModelJsonCodec);
|
|
106
|
+
// NOTE: Each enqueued Message carries a priority. Within a single
|
|
107
|
+
// takeAll batch the drain loop processes all High before any Normal,
|
|
108
|
+
// so user input (view dispatch, navigation, subscription events,
|
|
109
|
+
// managed-resource events, external dispatchers) lands ahead of
|
|
110
|
+
// chain-derived work (Command results) when they share a frame.
|
|
111
|
+
// FIFO order is preserved within a priority class.
|
|
83
112
|
const messageQueue = yield* Queue.unbounded();
|
|
84
|
-
const
|
|
113
|
+
const enqueueHigh = (message) => Queue.offer(messageQueue, { priority: 'High', message });
|
|
114
|
+
const enqueueNormal = (message) => Queue.offer(messageQueue, { priority: 'Normal', message });
|
|
115
|
+
const enqueueHighUnsafe = (message) => {
|
|
116
|
+
Queue.offerUnsafe(messageQueue, { priority: 'High', message });
|
|
117
|
+
};
|
|
85
118
|
const currentUrl = Option.fromNullishOr(routingConfig).pipe(Option.flatMap(() => urlFromString(window.location.href)));
|
|
86
119
|
const [initModelRaw, initCommands] = Predicate.isNotUndefined(hmrModel)
|
|
87
120
|
? Exit.match(decodeHmrModel(hmrModel), {
|
|
@@ -93,9 +126,9 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
93
126
|
const modelPubSub = yield* PubSub.unbounded();
|
|
94
127
|
yield* Effect.forEach(
|
|
95
128
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
96
|
-
initCommands, command => Effect.forkDetach(command.effect.pipe(Effect.withSpan(command.name), provideAllResources, Effect.flatMap(
|
|
129
|
+
initCommands, command => Effect.forkDetach(command.effect.pipe(Effect.withSpan(command.name), provideAllResources, Effect.flatMap(enqueueNormal))));
|
|
97
130
|
if (routingConfig) {
|
|
98
|
-
addNavigationEventListeners(
|
|
131
|
+
addNavigationEventListeners(enqueueHighUnsafe, routingConfig);
|
|
99
132
|
}
|
|
100
133
|
const modelRef = yield* Ref.make(initModel);
|
|
101
134
|
const maybeCurrentVNodeRef = yield* Ref.make(Option.none());
|
|
@@ -103,25 +136,29 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
103
136
|
const maybeDevToolsStoreRef = yield* Ref.make(Option.none());
|
|
104
137
|
const dispatchSync = (message) => {
|
|
105
138
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
106
|
-
|
|
139
|
+
enqueueHighUnsafe(message);
|
|
107
140
|
};
|
|
108
141
|
const dispatchAsync = (message) =>
|
|
109
142
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
110
|
-
|
|
143
|
+
enqueueHigh(message);
|
|
111
144
|
const dispatch = { dispatchAsync, dispatchSync };
|
|
145
|
+
const isRenderPendingRef = yield* Ref.make(false);
|
|
146
|
+
const lastDirtyMessageRef = yield* Ref.make(Option.none());
|
|
147
|
+
const isPausedEffect = Effect.gen(function* () {
|
|
148
|
+
const maybeStore = yield* Ref.get(maybeDevToolsStoreRef);
|
|
149
|
+
return yield* Option.match(maybeStore, {
|
|
150
|
+
onNone: () => Effect.succeed(false),
|
|
151
|
+
onSome: ({ stateRef }) => SubscriptionRef.get(stateRef).pipe(Effect.map(({ isPaused }) => isPaused)),
|
|
152
|
+
});
|
|
153
|
+
});
|
|
112
154
|
const processMessage = (message) => Effect.gen(function* () {
|
|
113
155
|
const currentModel = yield* Ref.get(modelRef);
|
|
114
156
|
const [nextModelRaw, commands] = update(currentModel, message);
|
|
115
157
|
const nextModel = maybeFreezeModel(nextModelRaw);
|
|
116
158
|
if (currentModel !== nextModel) {
|
|
117
159
|
yield* Ref.set(modelRef, nextModel);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
onSome: ({ stateRef }) => SubscriptionRef.get(stateRef).pipe(Effect.map(({ isPaused }) => isPaused)),
|
|
121
|
-
})));
|
|
122
|
-
if (!isPaused) {
|
|
123
|
-
yield* render(nextModel, Option.some(message));
|
|
124
|
-
}
|
|
160
|
+
yield* Ref.set(isRenderPendingRef, true);
|
|
161
|
+
yield* Ref.set(lastDirtyMessageRef, Option.some(message));
|
|
125
162
|
if (!modelEquivalence(currentModel, nextModel)) {
|
|
126
163
|
PubSub.publishUnsafe(modelPubSub, nextModel);
|
|
127
164
|
preserveModel(runtimeId, encodeHmrModel(nextModel));
|
|
@@ -129,7 +166,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
129
166
|
}
|
|
130
167
|
yield* Effect.forEach(
|
|
131
168
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
132
|
-
commands, command => Effect.forkDetach(command.effect.pipe(Effect.withSpan(command.name), provideAllResources, Effect.flatMap(
|
|
169
|
+
commands, command => Effect.forkDetach(command.effect.pipe(Effect.withSpan(command.name), provideAllResources, Effect.flatMap(enqueueNormal))));
|
|
133
170
|
const maybeDevToolsStore = yield* Ref.get(maybeDevToolsStoreRef);
|
|
134
171
|
yield* Option.match(maybeDevToolsStore, {
|
|
135
172
|
onNone: () => Effect.void,
|
|
@@ -191,12 +228,34 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
191
228
|
: Option.none();
|
|
192
229
|
yield* startWebSocketBridge(devToolsStore, import.meta.hot,
|
|
193
230
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
194
|
-
message =>
|
|
231
|
+
message => enqueueHigh(message),
|
|
195
232
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
196
233
|
maybeMessageSchema);
|
|
197
234
|
}
|
|
198
235
|
}
|
|
199
236
|
yield* render(initModel, Option.none());
|
|
237
|
+
// NOTE: lastDirtyMessageRef holds the most recent dirtying Message, so
|
|
238
|
+
// slow-view callbacks during high-rate bursts attribute to the last
|
|
239
|
+
// Message in the frame batch, not the specific one that pushed the
|
|
240
|
+
// view past threshold. Acceptable for a debug callback; full
|
|
241
|
+
// attribution would require correlating each message with its render
|
|
242
|
+
// contribution, which isn't worth the complexity.
|
|
243
|
+
const renderLoop = Effect.forever(Effect.gen(function* () {
|
|
244
|
+
yield* awaitNextFrame;
|
|
245
|
+
const isPending = yield* Ref.get(isRenderPendingRef);
|
|
246
|
+
if (!isPending) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const isPaused = yield* isPausedEffect;
|
|
250
|
+
if (isPaused) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
yield* Ref.set(isRenderPendingRef, false);
|
|
254
|
+
const model = yield* Ref.get(modelRef);
|
|
255
|
+
const maybeMessage = yield* Ref.get(lastDirtyMessageRef);
|
|
256
|
+
yield* render(model, maybeMessage);
|
|
257
|
+
}));
|
|
258
|
+
yield* Effect.forkDetach(renderLoop);
|
|
200
259
|
addBfcacheRestoreListener();
|
|
201
260
|
if (subscriptions) {
|
|
202
261
|
yield* pipe(subscriptions, Record.toEntries, Effect.forEach(([_key, { schema, modelToDependencies, equivalence: customEquivalence, dependenciesToStream, },]) => {
|
|
@@ -213,7 +272,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
213
272
|
return dependencies;
|
|
214
273
|
}), Stream.changesWith(equivalence), Stream.switchMap(dependencies => dependenciesToStream(dependencies, () => latestDependencies)), Stream.runForEach(message =>
|
|
215
274
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
216
|
-
|
|
275
|
+
enqueueHigh(message)), provideAllResources));
|
|
217
276
|
}, {
|
|
218
277
|
concurrency: 'unbounded',
|
|
219
278
|
discard: true,
|
|
@@ -235,28 +294,47 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
235
294
|
const release = (value) => Effect.gen(function* () {
|
|
236
295
|
yield* config.release(value);
|
|
237
296
|
yield* Ref.set(resourceRef, Option.none());
|
|
238
|
-
yield*
|
|
297
|
+
yield* enqueueHigh(config.onReleased());
|
|
239
298
|
}).pipe(Effect.catchCause(() => Effect.void));
|
|
240
299
|
return pipe(Stream.scoped(Stream.fromEffect(Effect.acquireRelease(acquire, release))), Stream.flatMap(value => Stream.concat(Stream.make(config.onAcquired(value)), Stream.never)), Stream.map(Effect.succeed), Stream.catch(error => Stream.make(Effect.succeed(config.onAcquireError(error)))));
|
|
241
300
|
};
|
|
242
301
|
const forkManagedResourceLifecycle = ({ config, ref: resourceRef, }) => Effect.gen(function* () {
|
|
243
302
|
const modelStream = Stream.concat(Stream.make(initModel), Stream.fromPubSub(modelPubSub));
|
|
244
303
|
const equivalence = Schema.toEquivalence(config.schema);
|
|
245
|
-
yield* Effect.forkDetach(modelStream.pipe(Stream.map(config.modelToMaybeRequirements), Stream.changesWith(equivalence), Stream.switchMap(maybeRequirementsToLifecycle(config, resourceRef)), Stream.runForEach(Effect.flatMap(
|
|
304
|
+
yield* Effect.forkDetach(modelStream.pipe(Stream.map(config.modelToMaybeRequirements), Stream.changesWith(equivalence), Stream.switchMap(maybeRequirementsToLifecycle(config, resourceRef)), Stream.runForEach(Effect.flatMap(enqueueHigh))));
|
|
246
305
|
});
|
|
247
306
|
yield* Effect.forEach(managedResourceRefs, forkManagedResourceLifecycle, {
|
|
248
307
|
concurrency: 'unbounded',
|
|
249
308
|
discard: true,
|
|
250
309
|
});
|
|
310
|
+
const burstStartedAtRef = yield* Ref.make(0);
|
|
311
|
+
const processWithBudget = (message) => Effect.gen(function* () {
|
|
312
|
+
yield* Ref.set(currentMessageRef, Option.some(message));
|
|
313
|
+
yield* processMessage(message);
|
|
314
|
+
const burstStartedAt = yield* Ref.get(burstStartedAtRef);
|
|
315
|
+
if (performance.now() - burstStartedAt < FRAME_BUDGET_MS) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
yield* yieldToBrowser;
|
|
319
|
+
yield* Ref.set(burstStartedAtRef, performance.now());
|
|
320
|
+
});
|
|
321
|
+
const processBatch = (batch) => Effect.forEach(orderByPriority(batch), processWithBudget, {
|
|
322
|
+
discard: true,
|
|
323
|
+
});
|
|
324
|
+
const drainQueue = Effect.gen(function* () {
|
|
325
|
+
const batch = yield* Queue.takeAll(messageQueue);
|
|
326
|
+
if (Array.isReadonlyArrayEmpty(batch)) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
yield* processBatch(batch);
|
|
330
|
+
yield* drainQueue;
|
|
331
|
+
});
|
|
251
332
|
yield* pipe(Effect.forever(Effect.gen(function* () {
|
|
252
333
|
const first = yield* Queue.take(messageQueue);
|
|
253
|
-
yield*
|
|
254
|
-
yield*
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
yield* Ref.set(currentMessageRef, Option.some(message));
|
|
258
|
-
yield* processMessage(message);
|
|
259
|
-
}
|
|
334
|
+
const rest = yield* Queue.takeAll(messageQueue);
|
|
335
|
+
yield* Ref.set(burstStartedAtRef, performance.now());
|
|
336
|
+
yield* processBatch(Array.prepend(rest, first));
|
|
337
|
+
yield* drainQueue;
|
|
260
338
|
})), Effect.catchCause(cause => Effect.sync(() => {
|
|
261
339
|
const squashed = Cause.squash(cause);
|
|
262
340
|
const appError = squashed instanceof Error
|