@jsenv/navi 0.10.1 → 0.11.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/dist/jsenv_navi.js +13858 -23295
- package/dist/jsenv_navi.js.map +1281 -0
- package/package.json +5 -7
- package/index.js +0 -122
- package/src/action_private_properties.js +0 -11
- package/src/action_proxy_test.html +0 -353
- package/src/action_run_states.js +0 -5
- package/src/actions.js +0 -1401
- package/src/browser_integration/browser_integration.js +0 -216
- package/src/browser_integration/document_back_and_forward.js +0 -17
- package/src/browser_integration/document_loading_signal.js +0 -100
- package/src/browser_integration/document_state_signal.js +0 -9
- package/src/browser_integration/document_url_signal.js +0 -9
- package/src/browser_integration/use_is_visited.js +0 -19
- package/src/browser_integration/via_history.js +0 -232
- package/src/browser_integration/via_navigation.js +0 -168
- package/src/components/action_execution/form_context.js +0 -5
- package/src/components/action_execution/render_actionable_component.jsx +0 -29
- package/src/components/action_execution/use_action.js +0 -99
- package/src/components/action_execution/use_execute_action.js +0 -177
- package/src/components/action_execution/use_run_on_mount.js +0 -9
- package/src/components/action_renderer.jsx +0 -125
- package/src/components/callout/callout.js +0 -990
- package/src/components/callout/callout_demo.html +0 -201
- package/src/components/callout/test_dynamic_positioning.html +0 -161
- package/src/components/callout/test_html_document_iframe.html +0 -182
- package/src/components/demos/0_button_demo.html +0 -707
- package/src/components/demos/10_column_reordering_debug.html +0 -277
- package/src/components/demos/11_table_selection_debug.html +0 -432
- package/src/components/demos/1_checkbox_demo.html +0 -754
- package/src/components/demos/2_input_textual_demo.html +0 -286
- package/src/components/demos/3_radio_demo.html +0 -874
- package/src/components/demos/4_select_demo.html +0 -100
- package/src/components/demos/5_list_scrollable_demo.html +0 -153
- package/src/components/demos/6_tablist_demo.html +0 -77
- package/src/components/demos/7_table_selection_demo.html +0 -176
- package/src/components/demos/8_table_fixed_headers_demo.html +0 -584
- package/src/components/demos/9_table_column_drag_demo.html +0 -325
- package/src/components/demos/action/0_button_demo.html +0 -204
- package/src/components/demos/action/10_shortcuts_demo.html +0 -189
- package/src/components/demos/action/11_nested_shortcuts_demo.xhtml +0 -401
- package/src/components/demos/action/1_input_text_demo.html +0 -876
- package/src/components/demos/action/2_form_multiple.html +0 -303
- package/src/components/demos/action/3_details_demo.html +0 -203
- package/src/components/demos/action/4_input_checkbox_demo.html +0 -731
- package/src/components/demos/action/5_input_checkbox_state_demo.html +0 -270
- package/src/components/demos/action/6_checkbox_list_demo.html +0 -341
- package/src/components/demos/action/7_radio_list_demo.html +0 -357
- package/src/components/demos/action/8_editable_demo.html +0 -431
- package/src/components/demos/action/9_link_demo.html +0 -194
- package/src/components/demos/demo.md +0 -0
- package/src/components/demos/route/basic/basic.html +0 -14
- package/src/components/demos/route/basic/basic_route_demo.jsx +0 -224
- package/src/components/demos/route/multi/multi.html +0 -14
- package/src/components/demos/route/multi/multi_route_demo.jsx +0 -277
- package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +0 -695
- package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +0 -429
- package/src/components/demos/ui_transition/2_height_transition_test.html +0 -295
- package/src/components/details/details.jsx +0 -245
- package/src/components/details/summary_marker.jsx +0 -141
- package/src/components/edition/editable.jsx +0 -186
- package/src/components/error_boundary_context.js +0 -9
- package/src/components/field/README.md +0 -247
- package/src/components/field/button.jsx +0 -429
- package/src/components/field/checkbox_list.jsx +0 -185
- package/src/components/field/collect_form_element_values.js +0 -82
- package/src/components/field/custom_field.js +0 -106
- package/src/components/field/form.jsx +0 -209
- package/src/components/field/input.jsx +0 -16
- package/src/components/field/input_checkbox.jsx +0 -434
- package/src/components/field/input_radio.jsx +0 -432
- package/src/components/field/input_textual.jsx +0 -389
- package/src/components/field/label.jsx +0 -46
- package/src/components/field/radio_list.jsx +0 -183
- package/src/components/field/select.jsx +0 -256
- package/src/components/field/use_action_events.js +0 -132
- package/src/components/field/use_form_events.js +0 -59
- package/src/components/field/use_ui_state_controller.js +0 -506
- package/src/components/item_tracker/README.md +0 -461
- package/src/components/item_tracker/use_isolated_item_tracker.jsx +0 -209
- package/src/components/item_tracker/use_isolated_item_tracker_demo.html +0 -148
- package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +0 -460
- package/src/components/item_tracker/use_item_tracker.jsx +0 -143
- package/src/components/item_tracker/use_item_tracker_demo.html +0 -207
- package/src/components/item_tracker/use_item_tracker_demo.jsx +0 -216
- package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +0 -87
- package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +0 -61
- package/src/components/keyboard_shortcuts/keyboard_key_meta.js +0 -17
- package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +0 -371
- package/src/components/keyboard_shortcuts/os.js +0 -9
- package/src/components/layout/demos/demo_flex.html +0 -638
- package/src/components/layout/demos/demo_layout_style_buttons.html +0 -351
- package/src/components/layout/demos/demo_layout_style_input.html +0 -226
- package/src/components/layout/demos/demo_layout_style_text.html +0 -514
- package/src/components/layout/flex.jsx +0 -109
- package/src/components/layout/layout_context.jsx +0 -3
- package/src/components/layout/spacing.jsx +0 -20
- package/src/components/layout/use_layout_style.js +0 -249
- package/src/components/link/link.jsx +0 -267
- package/src/components/link/link_with_icon.jsx +0 -52
- package/src/components/loader/loader_background.jsx +0 -372
- package/src/components/loader/loading_spinner.jsx +0 -68
- package/src/components/loader/network_speed.js +0 -83
- package/src/components/loader/rectangle_loading.jsx +0 -244
- package/src/components/props_composition/demos/demo_with_props_style.html +0 -81
- package/src/components/props_composition/with_props_class_name.js +0 -37
- package/src/components/props_composition/with_props_style.js +0 -26
- package/src/components/route.jsx +0 -19
- package/src/components/selection/selection.jsx +0 -1583
- package/src/components/svg/font_sized_svg.jsx +0 -59
- package/src/components/svg/icon_and_text.jsx +0 -21
- package/src/components/svg/svg_mask_overlay.jsx +0 -105
- package/src/components/table/drag/table_drag.jsx +0 -506
- package/src/components/table/resize/table_resize.jsx +0 -650
- package/src/components/table/resize/table_size.js +0 -43
- package/src/components/table/selection/table_selection.js +0 -106
- package/src/components/table/selection/table_selection.jsx +0 -203
- package/src/components/table/sticky/sticky_group.js +0 -354
- package/src/components/table/sticky/table_sticky.js +0 -25
- package/src/components/table/sticky/table_sticky.jsx +0 -501
- package/src/components/table/table.jsx +0 -721
- package/src/components/table/table_css.js +0 -211
- package/src/components/table/table_ui.jsx +0 -49
- package/src/components/table/use_cells_and_columns.js +0 -90
- package/src/components/table/use_object_array_to_cells.js +0 -46
- package/src/components/table/z_indexes.js +0 -23
- package/src/components/tablist/tablist.jsx +0 -99
- package/src/components/text/demos/demo_text_and_icon.html +0 -421
- package/src/components/text/overflow.jsx +0 -15
- package/src/components/text/text.jsx +0 -83
- package/src/components/text/text_and_count.jsx +0 -28
- package/src/components/ui_transition.jsx +0 -128
- package/src/components/use_auto_focus.js +0 -94
- package/src/components/use_batch_during_render.js +0 -33
- package/src/components/use_debounce_true.js +0 -31
- package/src/components/use_dependencies_diff.js +0 -35
- package/src/components/use_focus_group.js +0 -20
- package/src/components/use_initial_value.js +0 -78
- package/src/components/use_is_visited.js +0 -19
- package/src/components/use_ref_array.js +0 -38
- package/src/components/use_signal_sync.js +0 -50
- package/src/components/use_stable_callback.js +0 -68
- package/src/components/use_state_array.js +0 -47
- package/src/docs/actions.md +0 -250
- package/src/docs/demos/resource/action_status.jsx +0 -42
- package/src/docs/demos/resource/demo.md +0 -1
- package/src/docs/demos/resource/resource_demo_0.html +0 -84
- package/src/docs/demos/resource/resource_demo_10_post_gc.html +0 -364
- package/src/docs/demos/resource/resource_demo_11_describe_many.html +0 -362
- package/src/docs/demos/resource/resource_demo_2.html +0 -173
- package/src/docs/demos/resource/resource_demo_3_filtered_users.html +0 -415
- package/src/docs/demos/resource/resource_demo_4_details.html +0 -284
- package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +0 -115
- package/src/docs/demos/resource/resource_demo_6_gc.html +0 -217
- package/src/docs/demos/resource/resource_demo_7_child_gc.html +0 -240
- package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +0 -319
- package/src/docs/demos/resource/resource_demo_9_describe_one.html +0 -472
- package/src/docs/demos/resource/tata.jsx +0 -3
- package/src/docs/demos/resource/toto.jsx +0 -3
- package/src/docs/demos/user_nav/user_nav.html +0 -12
- package/src/docs/demos/user_nav/user_nav.jsx +0 -330
- package/src/docs/resource_dependencies.md +0 -103
- package/src/docs/resource_with_params.md +0 -80
- package/src/navi_css_vars.js +0 -14
- package/src/notes.md +0 -34
- package/src/route/route.js +0 -596
- package/src/route/route.xtest.html +0 -228
- package/src/store/array_signal_store.js +0 -537
- package/src/store/local_storage_signal.js +0 -17
- package/src/store/resource_graph.js +0 -1304
- package/src/store/tests/resource_graph_autoreload_demo.html +0 -12
- package/src/store/tests/resource_graph_autoreload_demo.jsx +0 -964
- package/src/store/tests/resource_graph_dependencies.test_manual.js +0 -95
- package/src/store/value_in_local_storage.js +0 -187
- package/src/symbol_object_signal.js +0 -1
- package/src/use_action_data.js +0 -10
- package/src/use_action_status.js +0 -47
- package/src/utils/add_many_event_listeners.js +0 -15
- package/src/utils/array_add_remove.js +0 -61
- package/src/utils/array_signal.js +0 -15
- package/src/utils/compare_two_js_values.js +0 -172
- package/src/utils/execute_with_cleanup.js +0 -21
- package/src/utils/get_caller_info.js +0 -85
- package/src/utils/is_signal.js +0 -20
- package/src/utils/js_value_weak_map.js +0 -162
- package/src/utils/js_value_weak_map_demo.html +0 -690
- package/src/utils/merge_two_js_values.js +0 -53
- package/src/utils/stringify_for_display.js +0 -131
- package/src/utils/weak_effect.js +0 -48
- package/src/validation/constraints/confirm_constraint.js +0 -14
- package/src/validation/constraints/create_unique_value_constraint.js +0 -27
- package/src/validation/constraints/native_constraints.js +0 -338
- package/src/validation/constraints/readonly_constraint.js +0 -41
- package/src/validation/constraints/same_as_constraint.js +0 -42
- package/src/validation/constraints/single_space_constraint.js +0 -13
- package/src/validation/custom_constraint_validation.js +0 -793
- package/src/validation/custom_message.js +0 -18
- package/src/validation/demos/browser_style.png +0 -0
- package/src/validation/demos/demo_same_as_constraint.html +0 -259
- package/src/validation/demos/form_validation_demo.html +0 -142
- package/src/validation/demos/form_validation_demo_preact.html +0 -87
- package/src/validation/demos/form_validation_native_popover_demo.html +0 -168
- package/src/validation/demos/form_validation_vs_native_demo.html +0 -172
- package/src/validation/hooks/use_constraints.js +0 -23
- package/src/validation/hooks/use_custom_validation_ref.js +0 -73
- package/src/validation/hooks/use_validation_message.js +0 -19
- package/src/validation/input_change_effect.js +0 -106
package/src/actions.js
DELETED
|
@@ -1,1401 +0,0 @@
|
|
|
1
|
-
import { createIterableWeakSet } from "@jsenv/dom";
|
|
2
|
-
import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
|
|
3
|
-
import { batch, computed, effect, signal } from "@preact/signals";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
getActionPrivateProperties,
|
|
7
|
-
setActionPrivateProperties,
|
|
8
|
-
} from "./action_private_properties.js";
|
|
9
|
-
import {
|
|
10
|
-
ABORTED,
|
|
11
|
-
COMPLETED,
|
|
12
|
-
FAILED,
|
|
13
|
-
IDLE,
|
|
14
|
-
RUNNING,
|
|
15
|
-
} from "./action_run_states.js";
|
|
16
|
-
import { SYMBOL_OBJECT_SIGNAL } from "./symbol_object_signal.js";
|
|
17
|
-
import { isSignal } from "./utils/is_signal.js";
|
|
18
|
-
import { createJsValueWeakMap } from "./utils/js_value_weak_map.js";
|
|
19
|
-
import { mergeTwoJsValues } from "./utils/merge_two_js_values.js";
|
|
20
|
-
import { stringifyForDisplay } from "./utils/stringify_for_display.js";
|
|
21
|
-
import { weakEffect } from "./utils/weak_effect.js";
|
|
22
|
-
|
|
23
|
-
let DEBUG = false;
|
|
24
|
-
export const enableDebugActions = () => {
|
|
25
|
-
DEBUG = true;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const ACTION_AS_FUNCTION = true;
|
|
29
|
-
|
|
30
|
-
let dispatchActions = (params) => {
|
|
31
|
-
const { requestedResult } = updateActions({
|
|
32
|
-
globalAbortSignal: new AbortController().signal,
|
|
33
|
-
abortSignal: new AbortController().signal,
|
|
34
|
-
...params,
|
|
35
|
-
});
|
|
36
|
-
return requestedResult;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const dispatchSingleAction = (action, method, options) => {
|
|
40
|
-
const requestedResult = dispatchActions({
|
|
41
|
-
prerunSet: method === "prerun" ? new Set([action]) : undefined,
|
|
42
|
-
runSet: method === "run" ? new Set([action]) : undefined,
|
|
43
|
-
rerunSet: method === "rerun" ? new Set([action]) : undefined,
|
|
44
|
-
resetSet: method === "reset" ? new Set([action]) : undefined,
|
|
45
|
-
...options,
|
|
46
|
-
});
|
|
47
|
-
if (requestedResult && typeof requestedResult.then === "function") {
|
|
48
|
-
return requestedResult.then((resolvedResult) =>
|
|
49
|
-
resolvedResult ? resolvedResult[0] : undefined,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
return requestedResult ? requestedResult[0] : undefined;
|
|
53
|
-
};
|
|
54
|
-
export const setActionDispatcher = (value) => {
|
|
55
|
-
dispatchActions = value;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const getActionDispatcher = () => dispatchActions;
|
|
59
|
-
|
|
60
|
-
export const rerunActions = async (
|
|
61
|
-
actionSet,
|
|
62
|
-
{ reason = "rerunActions was called" } = {},
|
|
63
|
-
) => {
|
|
64
|
-
return dispatchActions({
|
|
65
|
-
rerunSet: actionSet,
|
|
66
|
-
reason,
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const resetActions = async (
|
|
71
|
-
actionSet,
|
|
72
|
-
{ reason = "resetActions was called" } = {},
|
|
73
|
-
) => {
|
|
74
|
-
return dispatchActions({
|
|
75
|
-
resetSet: actionSet,
|
|
76
|
-
reason,
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
export const abortRunningActions = (
|
|
80
|
-
reason = "abortRunningActions was called",
|
|
81
|
-
) => {
|
|
82
|
-
const { runningSet } = getActivationInfo();
|
|
83
|
-
for (const runningAction of runningSet) {
|
|
84
|
-
runningAction.abort(reason);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Registry that prevents prerun actions from being garbage collected.
|
|
90
|
-
*
|
|
91
|
-
* When an action is prerun, it might not have any active references yet
|
|
92
|
-
* (e.g., the component that will use it hasn't loaded yet due to dynamic imports).
|
|
93
|
-
* This registry keeps a reference to prerun actions for a configurable duration
|
|
94
|
-
* to ensure they remain available when needed.
|
|
95
|
-
*
|
|
96
|
-
* Actions are automatically unprotected when:
|
|
97
|
-
* - The protection duration expires (default: 5 minutes)
|
|
98
|
-
* - The action is explicitly stopped via .stop()
|
|
99
|
-
*/
|
|
100
|
-
const prerunProtectionRegistry = (() => {
|
|
101
|
-
const protectedActionMap = new Map(); // action -> { timeoutId, timestamp }
|
|
102
|
-
const PROTECTION_DURATION = 5 * 60 * 1000; // 5 minutes en millisecondes
|
|
103
|
-
|
|
104
|
-
const unprotect = (action) => {
|
|
105
|
-
const protection = protectedActionMap.get(action);
|
|
106
|
-
if (protection) {
|
|
107
|
-
clearTimeout(protection.timeoutId);
|
|
108
|
-
protectedActionMap.delete(action);
|
|
109
|
-
if (DEBUG) {
|
|
110
|
-
const elapsed = Date.now() - protection.timestamp;
|
|
111
|
-
console.debug(`"${action}": GC protection removed after ${elapsed}ms`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
protect(action) {
|
|
118
|
-
// Si déjà protégée, étendre la protection
|
|
119
|
-
if (protectedActionMap.has(action)) {
|
|
120
|
-
const existing = protectedActionMap.get(action);
|
|
121
|
-
clearTimeout(existing.timeoutId);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const timestamp = Date.now();
|
|
125
|
-
const timeoutId = setTimeout(() => {
|
|
126
|
-
unprotect(action);
|
|
127
|
-
if (DEBUG) {
|
|
128
|
-
console.debug(
|
|
129
|
-
`"${action}": prerun protection expired after ${PROTECTION_DURATION}ms`,
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}, PROTECTION_DURATION);
|
|
133
|
-
|
|
134
|
-
protectedActionMap.set(action, { timeoutId, timestamp });
|
|
135
|
-
|
|
136
|
-
if (DEBUG) {
|
|
137
|
-
console.debug(
|
|
138
|
-
`"${action}": protected from GC for ${PROTECTION_DURATION}ms`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
unprotect,
|
|
144
|
-
|
|
145
|
-
isProtected(action) {
|
|
146
|
-
return protectedActionMap.has(action);
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
// Pour debugging
|
|
150
|
-
getProtectedActions() {
|
|
151
|
-
return Array.from(protectedActionMap.keys());
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
// Nettoyage manuel si nécessaire
|
|
155
|
-
clear() {
|
|
156
|
-
for (const [, protection] of protectedActionMap) {
|
|
157
|
-
clearTimeout(protection.timeoutId);
|
|
158
|
-
}
|
|
159
|
-
protectedActionMap.clear();
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
})();
|
|
163
|
-
|
|
164
|
-
export const formatActionSet = (actionSet, prefix = "") => {
|
|
165
|
-
let message = "";
|
|
166
|
-
message += `${prefix}`;
|
|
167
|
-
for (const action of actionSet) {
|
|
168
|
-
message += "\n";
|
|
169
|
-
message += prefixFirstAndIndentRemainingLines(String(action), {
|
|
170
|
-
prefix: " -",
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
return message;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const actionAbortMap = new Map();
|
|
177
|
-
const actionPromiseMap = new Map();
|
|
178
|
-
const activationWeakSet = createIterableWeakSet("activation");
|
|
179
|
-
|
|
180
|
-
const getActivationInfo = () => {
|
|
181
|
-
const runningSet = new Set();
|
|
182
|
-
const settledSet = new Set();
|
|
183
|
-
|
|
184
|
-
for (const action of activationWeakSet) {
|
|
185
|
-
const privateProps = getActionPrivateProperties(action);
|
|
186
|
-
const runningState = privateProps.runningStateSignal.peek();
|
|
187
|
-
|
|
188
|
-
if (runningState === RUNNING) {
|
|
189
|
-
runningSet.add(action);
|
|
190
|
-
} else if (
|
|
191
|
-
runningState === COMPLETED ||
|
|
192
|
-
runningState === FAILED ||
|
|
193
|
-
runningState === ABORTED
|
|
194
|
-
) {
|
|
195
|
-
settledSet.add(action);
|
|
196
|
-
} else {
|
|
197
|
-
throw new Error(
|
|
198
|
-
`An action in the activation weak set must be RUNNING, ABORTED, FAILED or COMPLETED, found "${runningState.id}" for action "${action}"`,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
runningSet,
|
|
205
|
-
settledSet,
|
|
206
|
-
};
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
if (import.meta.dev) {
|
|
210
|
-
window.__actions__ = {
|
|
211
|
-
activationWeakSet,
|
|
212
|
-
getActivationInfo,
|
|
213
|
-
inspectActivations: () => {
|
|
214
|
-
const activations = [];
|
|
215
|
-
for (const action of activationWeakSet) {
|
|
216
|
-
activations.push({
|
|
217
|
-
name: action.name,
|
|
218
|
-
runningState: action.runningState.id,
|
|
219
|
-
error: action.error,
|
|
220
|
-
params: action.params,
|
|
221
|
-
isProxy: action.isProxy || false,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
console.table(activations);
|
|
225
|
-
return activations;
|
|
226
|
-
},
|
|
227
|
-
cleanup: {
|
|
228
|
-
activation: {
|
|
229
|
-
forceCleanup: () => activationWeakSet.forceCleanup(),
|
|
230
|
-
schedule: () => activationWeakSet.schedule(),
|
|
231
|
-
getStats: () => activationWeakSet.getStats(),
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export const updateActions = ({
|
|
238
|
-
globalAbortSignal,
|
|
239
|
-
abortSignal,
|
|
240
|
-
isReplace = false,
|
|
241
|
-
reason,
|
|
242
|
-
prerunSet = new Set(),
|
|
243
|
-
runSet = new Set(),
|
|
244
|
-
rerunSet = new Set(),
|
|
245
|
-
resetSet = new Set(),
|
|
246
|
-
abortSignalMap = new Map(),
|
|
247
|
-
onComplete,
|
|
248
|
-
onAbort,
|
|
249
|
-
onError,
|
|
250
|
-
} = {}) => {
|
|
251
|
-
/*
|
|
252
|
-
* Action update flow:
|
|
253
|
-
*
|
|
254
|
-
* Input: 4 sets of requested operations
|
|
255
|
-
* - prerunSet: actions to prerun (background, low priority)
|
|
256
|
-
* - runSet: actions to run (user-visible, medium priority)
|
|
257
|
-
* - rerunSet: actions to force rerun (highest priority)
|
|
258
|
-
* - resetSet: actions to reset/clear
|
|
259
|
-
*
|
|
260
|
-
* Priority resolution:
|
|
261
|
-
* - reset always wins (explicit cleanup)
|
|
262
|
-
* - rerun > run > prerun (rerun forces refresh even if already running)
|
|
263
|
-
* - An action in multiple sets triggers warnings in dev mode
|
|
264
|
-
*
|
|
265
|
-
* Output: Internal operation sets that track what will actually happen
|
|
266
|
-
* - willResetSet: actions that will be reset/cleared
|
|
267
|
-
* - willPrerunSet: actions that will be prerun
|
|
268
|
-
* - willRunSet: actions that will be run
|
|
269
|
-
* - willPromoteSet: prerun actions that become run-requested
|
|
270
|
-
* - stays*Set: actions that remain in their current state
|
|
271
|
-
*/
|
|
272
|
-
|
|
273
|
-
const { runningSet, settledSet } = getActivationInfo();
|
|
274
|
-
|
|
275
|
-
// Warn about overlapping sets in development
|
|
276
|
-
if (import.meta.dev) {
|
|
277
|
-
const allSets = [
|
|
278
|
-
{ name: "prerun", set: prerunSet },
|
|
279
|
-
{ name: "run", set: runSet },
|
|
280
|
-
{ name: "rerun", set: rerunSet },
|
|
281
|
-
{ name: "reset", set: resetSet },
|
|
282
|
-
];
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < allSets.length; i++) {
|
|
285
|
-
for (let j = i + 1; j < allSets.length; j++) {
|
|
286
|
-
const setA = allSets[i];
|
|
287
|
-
const setB = allSets[j];
|
|
288
|
-
for (const action of setA.set) {
|
|
289
|
-
if (setB.set.has(action)) {
|
|
290
|
-
console.warn(
|
|
291
|
-
`Action "${action}" is found in both ${setA.name}Set and ${setB.name}Set. This may lead to unexpected behavior.`,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (DEBUG) {
|
|
300
|
-
let argSource = `reason: \`${reason}\``;
|
|
301
|
-
if (isReplace) {
|
|
302
|
-
argSource += `, isReplace: true`;
|
|
303
|
-
}
|
|
304
|
-
console.group(`updateActions({ ${argSource} })`);
|
|
305
|
-
const lines = [
|
|
306
|
-
...(prerunSet.size ? [formatActionSet(prerunSet, "- prerun:")] : []),
|
|
307
|
-
...(runSet.size ? [formatActionSet(runSet, "- run:")] : []),
|
|
308
|
-
...(rerunSet.size ? [formatActionSet(rerunSet, "- rerun:")] : []),
|
|
309
|
-
...(resetSet.size ? [formatActionSet(resetSet, "- reset:")] : []),
|
|
310
|
-
];
|
|
311
|
-
console.debug(
|
|
312
|
-
`requested operations:
|
|
313
|
-
${lines.join("\n")}`,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Internal sets that track what operations will actually be performed
|
|
318
|
-
const willResetSet = new Set();
|
|
319
|
-
const willPrerunSet = new Set();
|
|
320
|
-
const willRunSet = new Set();
|
|
321
|
-
const willPromoteSet = new Set(); // prerun -> run requested
|
|
322
|
-
const staysRunningSet = new Set();
|
|
323
|
-
const staysAbortedSet = new Set();
|
|
324
|
-
const staysFailedSet = new Set();
|
|
325
|
-
const staysCompletedSet = new Set();
|
|
326
|
-
|
|
327
|
-
// Step 1: Determine which actions will be reset
|
|
328
|
-
collect_actions_to_reset: {
|
|
329
|
-
for (const actionToReset of resetSet) {
|
|
330
|
-
if (actionToReset.runningState !== IDLE) {
|
|
331
|
-
willResetSet.add(actionToReset);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Step 2: Process prerun, run, and rerun sets
|
|
337
|
-
collect_actions_to_prerun_and_run: {
|
|
338
|
-
const handleActionRequest = (
|
|
339
|
-
action,
|
|
340
|
-
requestType, // "prerun", "run", or "rerun"
|
|
341
|
-
) => {
|
|
342
|
-
const isPrerun = requestType === "prerun";
|
|
343
|
-
const isRerun = requestType === "rerun";
|
|
344
|
-
|
|
345
|
-
if (
|
|
346
|
-
action.runningState === RUNNING ||
|
|
347
|
-
action.runningState === COMPLETED
|
|
348
|
-
) {
|
|
349
|
-
// Action is already running/completed
|
|
350
|
-
// By default, we don't interfere with already active actions
|
|
351
|
-
// Unless it's a rerun or the action is also being reset
|
|
352
|
-
if (isRerun || willResetSet.has(action)) {
|
|
353
|
-
// Force reset first, then rerun/run
|
|
354
|
-
willResetSet.add(action);
|
|
355
|
-
if (isPrerun) {
|
|
356
|
-
willPrerunSet.add(action);
|
|
357
|
-
} else {
|
|
358
|
-
willRunSet.add(action);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
// Otherwise, ignore the request (action stays as-is)
|
|
362
|
-
} else if (isPrerun) {
|
|
363
|
-
willPrerunSet.add(action);
|
|
364
|
-
} else {
|
|
365
|
-
willRunSet.add(action);
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
// Process prerunSet (lowest priority)
|
|
370
|
-
for (const actionToPrerun of prerunSet) {
|
|
371
|
-
if (runSet.has(actionToPrerun) || rerunSet.has(actionToPrerun)) {
|
|
372
|
-
// run/rerun wins over prerun - skip prerun
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
handleActionRequest(actionToPrerun, "prerun");
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Process runSet (medium priority)
|
|
379
|
-
for (const actionToRun of runSet) {
|
|
380
|
-
if (rerunSet.has(actionToRun)) {
|
|
381
|
-
// rerun wins over run - skip run
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
if (actionToRun.isPrerun && actionToRun.runningState !== IDLE) {
|
|
385
|
-
// Special case: action was prerun but not yet requested to run
|
|
386
|
-
// Just promote it to "run requested" without rerunning
|
|
387
|
-
willPromoteSet.add(actionToRun);
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
handleActionRequest(actionToRun, "run");
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Process rerunSet (highest priority)
|
|
394
|
-
for (const actionToRerun of rerunSet) {
|
|
395
|
-
handleActionRequest(actionToRerun, "rerun");
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
const allThenableArray = [];
|
|
399
|
-
|
|
400
|
-
// Step 3: Determine which actions will stay in their current state
|
|
401
|
-
collect_actions_that_stay: {
|
|
402
|
-
for (const actionRunning of runningSet) {
|
|
403
|
-
if (willResetSet.has(actionRunning)) {
|
|
404
|
-
// will be reset (aborted), we don't want to wait
|
|
405
|
-
} else if (
|
|
406
|
-
willRunSet.has(actionRunning) ||
|
|
407
|
-
willPrerunSet.has(actionRunning)
|
|
408
|
-
) {
|
|
409
|
-
// will be run, we'll wait for the new run promise
|
|
410
|
-
} else {
|
|
411
|
-
// an action that was running and not affected by this update
|
|
412
|
-
const actionPromise = actionPromiseMap.get(actionRunning);
|
|
413
|
-
allThenableArray.push(actionPromise);
|
|
414
|
-
staysRunningSet.add(actionRunning);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
for (const actionSettled of settledSet) {
|
|
418
|
-
if (willResetSet.has(actionSettled)) {
|
|
419
|
-
// will be reset
|
|
420
|
-
} else if (actionSettled.runningState === ABORTED) {
|
|
421
|
-
staysAbortedSet.add(actionSettled);
|
|
422
|
-
} else if (actionSettled.runningState === FAILED) {
|
|
423
|
-
staysFailedSet.add(actionSettled);
|
|
424
|
-
} else {
|
|
425
|
-
staysCompletedSet.add(actionSettled);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
if (DEBUG) {
|
|
430
|
-
const lines = [
|
|
431
|
-
...(willResetSet.size
|
|
432
|
-
? [formatActionSet(willResetSet, "- will reset:")]
|
|
433
|
-
: []),
|
|
434
|
-
...(willPrerunSet.size
|
|
435
|
-
? [formatActionSet(willPrerunSet, "- will prerun:")]
|
|
436
|
-
: []),
|
|
437
|
-
...(willPromoteSet.size
|
|
438
|
-
? [formatActionSet(willPromoteSet, "- will promote:")]
|
|
439
|
-
: []),
|
|
440
|
-
...(willRunSet.size ? [formatActionSet(willRunSet, "- will run:")] : []),
|
|
441
|
-
...(staysRunningSet.size
|
|
442
|
-
? [formatActionSet(staysRunningSet, "- stays running:")]
|
|
443
|
-
: []),
|
|
444
|
-
...(staysAbortedSet.size
|
|
445
|
-
? [formatActionSet(staysAbortedSet, "- stays aborted:")]
|
|
446
|
-
: []),
|
|
447
|
-
...(staysFailedSet.size
|
|
448
|
-
? [formatActionSet(staysFailedSet, "- stays failed:")]
|
|
449
|
-
: []),
|
|
450
|
-
...(staysCompletedSet.size
|
|
451
|
-
? [formatActionSet(staysCompletedSet, "- stays completed:")]
|
|
452
|
-
: []),
|
|
453
|
-
];
|
|
454
|
-
console.debug(`operations that will be performed:
|
|
455
|
-
${lines.join("\n")}`);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Step 4: Execute resets
|
|
459
|
-
execute_resets: {
|
|
460
|
-
for (const actionToReset of willResetSet) {
|
|
461
|
-
const actionToResetPrivateProperties =
|
|
462
|
-
getActionPrivateProperties(actionToReset);
|
|
463
|
-
actionToResetPrivateProperties.performStop({ reason });
|
|
464
|
-
activationWeakSet.delete(actionToReset);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const resultArray = []; // Store results with their execution order
|
|
469
|
-
let hasAsync = false;
|
|
470
|
-
|
|
471
|
-
// Step 5: Execute preruns and runs
|
|
472
|
-
execute_preruns_and_runs: {
|
|
473
|
-
const onActionToRunOrPrerun = (actionToPrerunOrRun, isPrerun) => {
|
|
474
|
-
if (import.meta.dev && actionToPrerunOrRun.isProxy) {
|
|
475
|
-
// maybe remove this check one the API is stable because
|
|
476
|
-
// nothing in the API should allow this to happen
|
|
477
|
-
throw new Error(
|
|
478
|
-
`Proxy should not be reach this point, use the underlying action instead`,
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
const actionSpecificSignal = abortSignalMap.get(actionToPrerunOrRun);
|
|
482
|
-
const effectiveSignal = actionSpecificSignal || abortSignal;
|
|
483
|
-
|
|
484
|
-
const actionToRunPrivateProperties =
|
|
485
|
-
getActionPrivateProperties(actionToPrerunOrRun);
|
|
486
|
-
const performRunResult = actionToRunPrivateProperties.performRun({
|
|
487
|
-
globalAbortSignal,
|
|
488
|
-
abortSignal: effectiveSignal,
|
|
489
|
-
reason,
|
|
490
|
-
isPrerun,
|
|
491
|
-
onComplete,
|
|
492
|
-
onAbort,
|
|
493
|
-
onError,
|
|
494
|
-
});
|
|
495
|
-
activationWeakSet.add(actionToPrerunOrRun);
|
|
496
|
-
|
|
497
|
-
if (performRunResult && typeof performRunResult.then === "function") {
|
|
498
|
-
actionPromiseMap.set(actionToPrerunOrRun, performRunResult);
|
|
499
|
-
allThenableArray.push(performRunResult);
|
|
500
|
-
hasAsync = true;
|
|
501
|
-
// Store async result with order info
|
|
502
|
-
resultArray.push({
|
|
503
|
-
type: "async",
|
|
504
|
-
promise: performRunResult,
|
|
505
|
-
});
|
|
506
|
-
} else {
|
|
507
|
-
// Store sync result with order info
|
|
508
|
-
resultArray.push({
|
|
509
|
-
type: "sync",
|
|
510
|
-
result: performRunResult,
|
|
511
|
-
});
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
// Execute preruns
|
|
516
|
-
for (const actionToPrerun of willPrerunSet) {
|
|
517
|
-
onActionToRunOrPrerun(actionToPrerun, true);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// Execute runs
|
|
521
|
-
for (const actionToRun of willRunSet) {
|
|
522
|
-
onActionToRunOrPrerun(actionToRun, false);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Execute promotions (prerun -> run requested)
|
|
526
|
-
for (const actionToPromote of willPromoteSet) {
|
|
527
|
-
const actionToPromotePrivateProperties =
|
|
528
|
-
getActionPrivateProperties(actionToPromote);
|
|
529
|
-
actionToPromotePrivateProperties.isPrerunSignal.value = false;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
if (DEBUG) {
|
|
533
|
-
console.groupEnd();
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Calculate requestedResult based on the execution results
|
|
537
|
-
let requestedResult;
|
|
538
|
-
if (resultArray.length === 0) {
|
|
539
|
-
requestedResult = null;
|
|
540
|
-
} else if (hasAsync) {
|
|
541
|
-
requestedResult = Promise.all(
|
|
542
|
-
resultArray.map((item) =>
|
|
543
|
-
item.type === "sync" ? item.result : item.promise,
|
|
544
|
-
),
|
|
545
|
-
);
|
|
546
|
-
} else {
|
|
547
|
-
requestedResult = resultArray.map((item) => item.result);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const allResult = allThenableArray.length
|
|
551
|
-
? Promise.allSettled(allThenableArray)
|
|
552
|
-
: null;
|
|
553
|
-
const runningActionSet = new Set([...willPrerunSet, ...willRunSet]);
|
|
554
|
-
return {
|
|
555
|
-
requestedResult,
|
|
556
|
-
allResult,
|
|
557
|
-
runningActionSet,
|
|
558
|
-
};
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
const NO_PARAMS = {};
|
|
562
|
-
const initialParamsDefault = NO_PARAMS;
|
|
563
|
-
|
|
564
|
-
const actionWeakMap = new WeakMap();
|
|
565
|
-
export const createAction = (callback, rootOptions = {}) => {
|
|
566
|
-
const existing = actionWeakMap.get(callback);
|
|
567
|
-
if (existing) {
|
|
568
|
-
return existing;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
let rootAction;
|
|
572
|
-
|
|
573
|
-
const createActionCore = (options, { parentAction } = {}) => {
|
|
574
|
-
let {
|
|
575
|
-
name = callback.name || "anonymous",
|
|
576
|
-
params,
|
|
577
|
-
isPrerun = true,
|
|
578
|
-
runningState = IDLE,
|
|
579
|
-
aborted = false,
|
|
580
|
-
error = null,
|
|
581
|
-
data,
|
|
582
|
-
computedData,
|
|
583
|
-
compute,
|
|
584
|
-
completed = false,
|
|
585
|
-
renderLoadedAsync,
|
|
586
|
-
sideEffect = () => {},
|
|
587
|
-
keepOldData = false,
|
|
588
|
-
meta = {},
|
|
589
|
-
dataEffect,
|
|
590
|
-
completeSideEffect,
|
|
591
|
-
} = options;
|
|
592
|
-
if (!Object.hasOwn(options, "params")) {
|
|
593
|
-
// even undefined should be respect it's only when not provided at all we use default
|
|
594
|
-
params = initialParamsDefault;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const initialData = data;
|
|
598
|
-
const paramsSignal = signal(params);
|
|
599
|
-
const isPrerunSignal = signal(isPrerun);
|
|
600
|
-
const runningStateSignal = signal(runningState);
|
|
601
|
-
const errorSignal = signal(error);
|
|
602
|
-
const dataSignal = signal(initialData);
|
|
603
|
-
const computedDataSignal = compute
|
|
604
|
-
? computed(() => {
|
|
605
|
-
const data = dataSignal.value;
|
|
606
|
-
return compute(data);
|
|
607
|
-
})
|
|
608
|
-
: dataSignal;
|
|
609
|
-
computedData =
|
|
610
|
-
computedData === undefined
|
|
611
|
-
? compute
|
|
612
|
-
? compute(data)
|
|
613
|
-
: data
|
|
614
|
-
: computedData;
|
|
615
|
-
|
|
616
|
-
const prerun = (options) => {
|
|
617
|
-
return dispatchSingleAction(action, "prerun", options);
|
|
618
|
-
};
|
|
619
|
-
const run = (options) => {
|
|
620
|
-
return dispatchSingleAction(action, "run", options);
|
|
621
|
-
};
|
|
622
|
-
const rerun = (options) => {
|
|
623
|
-
return dispatchSingleAction(action, "rerun", options);
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* Stop the action completely - this will:
|
|
627
|
-
* 1. Abort the action if it's currently running
|
|
628
|
-
* 2. Reset the action to IDLE state
|
|
629
|
-
* 3. Clean up any resources and side effects
|
|
630
|
-
* 4. Reset data to initial value (unless keepOldData is true)
|
|
631
|
-
*/
|
|
632
|
-
const stop = (options) => {
|
|
633
|
-
return dispatchSingleAction(action, "stop", options);
|
|
634
|
-
};
|
|
635
|
-
const abort = (reason) => {
|
|
636
|
-
if (runningState !== RUNNING) {
|
|
637
|
-
return false;
|
|
638
|
-
}
|
|
639
|
-
const actionAbort = actionAbortMap.get(action);
|
|
640
|
-
if (!actionAbort) {
|
|
641
|
-
return false;
|
|
642
|
-
}
|
|
643
|
-
if (DEBUG) {
|
|
644
|
-
console.log(`"${action}": aborting (reason: ${reason})`);
|
|
645
|
-
}
|
|
646
|
-
actionAbort(reason);
|
|
647
|
-
return true;
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
let action;
|
|
651
|
-
|
|
652
|
-
const childActionWeakSet = createIterableWeakSet("child_action");
|
|
653
|
-
/*
|
|
654
|
-
* Ephemeron behavior is critical here: actions must keep params alive.
|
|
655
|
-
* Without this, bindParams(params) could create a new action while code
|
|
656
|
-
* still references the old action with GC'd params. This would cause:
|
|
657
|
-
* - Duplicate actions in activationWeakSet (old + new)
|
|
658
|
-
* - Cache misses when looking up existing actions
|
|
659
|
-
* - Subtle bugs where different parts of code use different action instances
|
|
660
|
-
* The ephemeron pattern ensures params and actions have synchronized lifetimes.
|
|
661
|
-
*/
|
|
662
|
-
const childActionWeakMap = createJsValueWeakMap();
|
|
663
|
-
const _bindParams = (newParamsOrSignal, options = {}) => {
|
|
664
|
-
// ✅ CAS 1: Signal direct -> proxy
|
|
665
|
-
if (isSignal(newParamsOrSignal)) {
|
|
666
|
-
const combinedParamsSignal = computed(() => {
|
|
667
|
-
const newParams = newParamsOrSignal.value;
|
|
668
|
-
const result = mergeTwoJsValues(params, newParams);
|
|
669
|
-
return result;
|
|
670
|
-
});
|
|
671
|
-
return createActionProxyFromSignal(
|
|
672
|
-
action,
|
|
673
|
-
combinedParamsSignal,
|
|
674
|
-
options,
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// ✅ CAS 2: Objet -> vérifier s'il contient des signals
|
|
679
|
-
if (newParamsOrSignal && typeof newParamsOrSignal === "object") {
|
|
680
|
-
const staticParams = {};
|
|
681
|
-
const signalMap = new Map();
|
|
682
|
-
|
|
683
|
-
const keyArray = Object.keys(newParamsOrSignal);
|
|
684
|
-
for (const key of keyArray) {
|
|
685
|
-
const value = newParamsOrSignal[key];
|
|
686
|
-
if (isSignal(value)) {
|
|
687
|
-
signalMap.set(key, value);
|
|
688
|
-
} else {
|
|
689
|
-
const objectSignal = value ? value[SYMBOL_OBJECT_SIGNAL] : null;
|
|
690
|
-
if (objectSignal) {
|
|
691
|
-
signalMap.set(key, objectSignal);
|
|
692
|
-
} else {
|
|
693
|
-
staticParams[key] = value;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
if (signalMap.size === 0) {
|
|
699
|
-
// Pas de signals, merge statique normal
|
|
700
|
-
if (params === null || typeof params !== "object") {
|
|
701
|
-
return createChildAction({
|
|
702
|
-
...options,
|
|
703
|
-
params: newParamsOrSignal,
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
const combinedParams = mergeTwoJsValues(params, newParamsOrSignal);
|
|
707
|
-
return createChildAction({
|
|
708
|
-
...options,
|
|
709
|
-
params: combinedParams,
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Combiner avec les params existants pour les valeurs statiques
|
|
714
|
-
const paramsSignal = computed(() => {
|
|
715
|
-
const params = {};
|
|
716
|
-
for (const key of keyArray) {
|
|
717
|
-
const signalForThisKey = signalMap.get(key);
|
|
718
|
-
if (signalForThisKey) {
|
|
719
|
-
params[key] = signalForThisKey.value;
|
|
720
|
-
} else {
|
|
721
|
-
params[key] = staticParams[key];
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return params;
|
|
725
|
-
});
|
|
726
|
-
return createActionProxyFromSignal(action, paramsSignal, options);
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// ✅ CAS 3: Primitive -> action enfant
|
|
730
|
-
return createChildAction({
|
|
731
|
-
params: newParamsOrSignal,
|
|
732
|
-
...options,
|
|
733
|
-
});
|
|
734
|
-
};
|
|
735
|
-
const bindParams = (newParamsOrSignal, options = {}) => {
|
|
736
|
-
const existingChildAction = childActionWeakMap.get(newParamsOrSignal);
|
|
737
|
-
if (existingChildAction) {
|
|
738
|
-
return existingChildAction;
|
|
739
|
-
}
|
|
740
|
-
const childAction = _bindParams(newParamsOrSignal, options);
|
|
741
|
-
childActionWeakMap.set(newParamsOrSignal, childAction);
|
|
742
|
-
childActionWeakSet.add(childAction);
|
|
743
|
-
|
|
744
|
-
return childAction;
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
const createChildAction = (childOptions) => {
|
|
748
|
-
const childActionOptions = {
|
|
749
|
-
...rootOptions,
|
|
750
|
-
...childOptions,
|
|
751
|
-
meta: {
|
|
752
|
-
...rootOptions.meta,
|
|
753
|
-
...childOptions.meta,
|
|
754
|
-
},
|
|
755
|
-
};
|
|
756
|
-
const childAction = createActionCore(childActionOptions, {
|
|
757
|
-
parentAction: action,
|
|
758
|
-
});
|
|
759
|
-
return childAction;
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
// ✅ Implement matchAllSelfOrDescendant
|
|
763
|
-
const matchAllSelfOrDescendant = (predicate, { includeProxies } = {}) => {
|
|
764
|
-
const matches = [];
|
|
765
|
-
|
|
766
|
-
const traverse = (currentAction) => {
|
|
767
|
-
if (currentAction.isProxy && !includeProxies) {
|
|
768
|
-
// proxy action should be ignored because the underlying action will be found anyway
|
|
769
|
-
// and if we check the proxy action we'll end up with duplicates
|
|
770
|
-
// (loading the proxy would load the action it proxies)
|
|
771
|
-
// and as they are 2 different objects they would be added to the set
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
if (predicate(currentAction)) {
|
|
776
|
-
matches.push(currentAction);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// Get child actions from the current action
|
|
780
|
-
const currentActionPrivateProps =
|
|
781
|
-
getActionPrivateProperties(currentAction);
|
|
782
|
-
const childActionWeakSet = currentActionPrivateProps.childActionWeakSet;
|
|
783
|
-
for (const childAction of childActionWeakSet) {
|
|
784
|
-
traverse(childAction);
|
|
785
|
-
}
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
traverse(action);
|
|
789
|
-
return matches;
|
|
790
|
-
};
|
|
791
|
-
|
|
792
|
-
name = generateActionName(name, params);
|
|
793
|
-
if (ACTION_AS_FUNCTION) {
|
|
794
|
-
// Create the action as a function that can be called directly
|
|
795
|
-
action = function actionFunction(params) {
|
|
796
|
-
const boundAction = bindParams(params);
|
|
797
|
-
return boundAction.rerun();
|
|
798
|
-
};
|
|
799
|
-
Object.defineProperty(action, "name", {
|
|
800
|
-
configurable: true,
|
|
801
|
-
writable: true,
|
|
802
|
-
value: name,
|
|
803
|
-
});
|
|
804
|
-
} else {
|
|
805
|
-
action = { name };
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// Assign all the action properties and methods to the function
|
|
809
|
-
Object.assign(action, {
|
|
810
|
-
isAction: true,
|
|
811
|
-
callback,
|
|
812
|
-
rootAction,
|
|
813
|
-
parentAction,
|
|
814
|
-
params,
|
|
815
|
-
isPrerun,
|
|
816
|
-
runningState,
|
|
817
|
-
aborted,
|
|
818
|
-
error,
|
|
819
|
-
data,
|
|
820
|
-
computedData,
|
|
821
|
-
completed,
|
|
822
|
-
prerun,
|
|
823
|
-
run,
|
|
824
|
-
rerun,
|
|
825
|
-
stop,
|
|
826
|
-
abort,
|
|
827
|
-
bindParams,
|
|
828
|
-
matchAllSelfOrDescendant, // ✅ Add the new method
|
|
829
|
-
replaceParams: (newParams) => {
|
|
830
|
-
const currentParams = paramsSignal.value;
|
|
831
|
-
const nextParams = mergeTwoJsValues(currentParams, newParams);
|
|
832
|
-
if (nextParams === currentParams) {
|
|
833
|
-
return false;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// Update the weak map BEFORE updating the signal
|
|
837
|
-
// so that any code triggered by the signal update finds this action
|
|
838
|
-
if (parentAction) {
|
|
839
|
-
const parentActionPrivateProps =
|
|
840
|
-
getActionPrivateProperties(parentAction);
|
|
841
|
-
const parentChildActionWeakMap =
|
|
842
|
-
parentActionPrivateProps.childActionWeakMap;
|
|
843
|
-
parentChildActionWeakMap.delete(currentParams);
|
|
844
|
-
parentChildActionWeakMap.set(nextParams, action);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
params = nextParams;
|
|
848
|
-
action.params = nextParams;
|
|
849
|
-
action.name = generateActionName(name, nextParams);
|
|
850
|
-
paramsSignal.value = nextParams;
|
|
851
|
-
return true;
|
|
852
|
-
},
|
|
853
|
-
toString: () => action.name,
|
|
854
|
-
meta,
|
|
855
|
-
});
|
|
856
|
-
Object.preventExtensions(action);
|
|
857
|
-
|
|
858
|
-
// Effects pour synchroniser les propriétés
|
|
859
|
-
effects: {
|
|
860
|
-
weakEffect([action], (actionRef) => {
|
|
861
|
-
isPrerun = isPrerunSignal.value;
|
|
862
|
-
actionRef.isPrerun = isPrerun;
|
|
863
|
-
});
|
|
864
|
-
weakEffect([action], (actionRef) => {
|
|
865
|
-
runningState = runningStateSignal.value;
|
|
866
|
-
actionRef.runningState = runningState;
|
|
867
|
-
aborted = runningState === ABORTED;
|
|
868
|
-
actionRef.aborted = aborted;
|
|
869
|
-
completed = runningState === COMPLETED;
|
|
870
|
-
actionRef.completed = completed;
|
|
871
|
-
});
|
|
872
|
-
weakEffect([action], (actionRef) => {
|
|
873
|
-
error = errorSignal.value;
|
|
874
|
-
actionRef.error = error;
|
|
875
|
-
});
|
|
876
|
-
weakEffect([action], (actionRef) => {
|
|
877
|
-
data = dataSignal.value;
|
|
878
|
-
computedData = computedDataSignal.value;
|
|
879
|
-
actionRef.data = data;
|
|
880
|
-
actionRef.computedData = computedData;
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// Propriétés privées
|
|
885
|
-
private_properties: {
|
|
886
|
-
const ui = {
|
|
887
|
-
renderLoaded: null,
|
|
888
|
-
renderLoadedAsync,
|
|
889
|
-
hasRenderers: false, // Flag to track if action is bound to UI components
|
|
890
|
-
};
|
|
891
|
-
let sideEffectCleanup;
|
|
892
|
-
|
|
893
|
-
const performRun = (runParams) => {
|
|
894
|
-
const {
|
|
895
|
-
globalAbortSignal,
|
|
896
|
-
abortSignal,
|
|
897
|
-
reason,
|
|
898
|
-
isPrerun,
|
|
899
|
-
onComplete,
|
|
900
|
-
onAbort,
|
|
901
|
-
onError,
|
|
902
|
-
} = runParams;
|
|
903
|
-
|
|
904
|
-
if (isPrerun) {
|
|
905
|
-
prerunProtectionRegistry.protect(action);
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
const internalAbortController = new AbortController();
|
|
909
|
-
const internalAbortSignal = internalAbortController.signal;
|
|
910
|
-
const abort = (abortReason) => {
|
|
911
|
-
runningStateSignal.value = ABORTED;
|
|
912
|
-
internalAbortController.abort(abortReason);
|
|
913
|
-
actionAbortMap.delete(action);
|
|
914
|
-
if (isPrerun && (globalAbortSignal.aborted || abortSignal.aborted)) {
|
|
915
|
-
prerunProtectionRegistry.unprotect(action);
|
|
916
|
-
}
|
|
917
|
-
if (DEBUG) {
|
|
918
|
-
console.log(`"${action}": aborted (reason: ${abortReason})`);
|
|
919
|
-
}
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
const onAbortFromSpecific = () => {
|
|
923
|
-
abort(abortSignal.reason);
|
|
924
|
-
};
|
|
925
|
-
const onAbortFromGlobal = () => {
|
|
926
|
-
abort(globalAbortSignal.reason);
|
|
927
|
-
};
|
|
928
|
-
|
|
929
|
-
if (abortSignal) {
|
|
930
|
-
abortSignal.addEventListener("abort", onAbortFromSpecific);
|
|
931
|
-
}
|
|
932
|
-
if (globalAbortSignal) {
|
|
933
|
-
globalAbortSignal.addEventListener("abort", onAbortFromGlobal);
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
actionAbortMap.set(action, abort);
|
|
937
|
-
|
|
938
|
-
batch(() => {
|
|
939
|
-
errorSignal.value = null;
|
|
940
|
-
runningStateSignal.value = RUNNING;
|
|
941
|
-
if (!isPrerun) {
|
|
942
|
-
isPrerunSignal.value = false;
|
|
943
|
-
}
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
const args = [];
|
|
947
|
-
args.push(params);
|
|
948
|
-
args.push({ signal: internalAbortSignal, reason, isPrerun });
|
|
949
|
-
const returnValue = sideEffect(...args);
|
|
950
|
-
if (typeof returnValue === "function") {
|
|
951
|
-
sideEffectCleanup = returnValue;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
let runResult;
|
|
955
|
-
let rejected = false;
|
|
956
|
-
let rejectedValue;
|
|
957
|
-
const onRunEnd = () => {
|
|
958
|
-
if (abortSignal) {
|
|
959
|
-
abortSignal.removeEventListener("abort", onAbortFromSpecific);
|
|
960
|
-
}
|
|
961
|
-
if (globalAbortSignal) {
|
|
962
|
-
globalAbortSignal.removeEventListener("abort", onAbortFromGlobal);
|
|
963
|
-
}
|
|
964
|
-
prerunProtectionRegistry.unprotect(action);
|
|
965
|
-
actionAbortMap.delete(action);
|
|
966
|
-
actionPromiseMap.delete(action);
|
|
967
|
-
/*
|
|
968
|
-
* Critical: dataEffect, onComplete and completeSideEffect must be batched together to prevent
|
|
969
|
-
* UI inconsistencies. The dataEffect might modify shared state (e.g.,
|
|
970
|
-
* deleting items from a store), and onLoad callbacks might trigger
|
|
971
|
-
* dependent action state changes.
|
|
972
|
-
*
|
|
973
|
-
* Without batching, the UI could render with partially updated state:
|
|
974
|
-
* - dataEffect deletes a resource from the store
|
|
975
|
-
* - UI renders immediately and tries to display the deleted resource
|
|
976
|
-
* - onLoad hasn't yet updated dependent actions to loading state
|
|
977
|
-
*
|
|
978
|
-
* Example: When deleting a resource, we need to both update the store
|
|
979
|
-
* AND put the action that loaded that resource back into loading state
|
|
980
|
-
* before the UI attempts to render the now-missing resource.
|
|
981
|
-
*/
|
|
982
|
-
batch(() => {
|
|
983
|
-
dataSignal.value = dataEffect
|
|
984
|
-
? dataEffect(runResult, action)
|
|
985
|
-
: runResult;
|
|
986
|
-
runningStateSignal.value = COMPLETED;
|
|
987
|
-
onComplete?.(computedDataSignal.peek(), action);
|
|
988
|
-
completeSideEffect?.(action);
|
|
989
|
-
});
|
|
990
|
-
if (DEBUG) {
|
|
991
|
-
console.log(`"${action}": completed`);
|
|
992
|
-
}
|
|
993
|
-
return computedDataSignal.peek();
|
|
994
|
-
};
|
|
995
|
-
const onRunError = (e) => {
|
|
996
|
-
if (abortSignal) {
|
|
997
|
-
abortSignal.removeEventListener("abort", onAbortFromSpecific);
|
|
998
|
-
}
|
|
999
|
-
if (globalAbortSignal) {
|
|
1000
|
-
globalAbortSignal.removeEventListener("abort", onAbortFromGlobal);
|
|
1001
|
-
}
|
|
1002
|
-
actionAbortMap.delete(action);
|
|
1003
|
-
actionPromiseMap.delete(action);
|
|
1004
|
-
if (internalAbortSignal.aborted && e === internalAbortSignal.reason) {
|
|
1005
|
-
runningStateSignal.value = ABORTED;
|
|
1006
|
-
if (isPrerun && abortSignal.aborted) {
|
|
1007
|
-
prerunProtectionRegistry.unprotect(action);
|
|
1008
|
-
}
|
|
1009
|
-
onAbort(e, action);
|
|
1010
|
-
return e;
|
|
1011
|
-
}
|
|
1012
|
-
if (e.name === "AbortError") {
|
|
1013
|
-
throw new Error(
|
|
1014
|
-
"never supposed to happen, abort error should be handled by the abort signal",
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
if (DEBUG) {
|
|
1018
|
-
console.log(
|
|
1019
|
-
`"${action}": failed (error: ${e}, handled by ui: ${ui.hasRenderers})`,
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
batch(() => {
|
|
1023
|
-
errorSignal.value = e;
|
|
1024
|
-
runningStateSignal.value = FAILED;
|
|
1025
|
-
onError?.(e, action);
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
if (ui.hasRenderers || onError) {
|
|
1029
|
-
console.error(e);
|
|
1030
|
-
// For UI-bound actions: error is properly handled by logging + UI display
|
|
1031
|
-
// Return error instead of throwing to signal it's handled and prevent:
|
|
1032
|
-
// - jsenv error overlay from appearing
|
|
1033
|
-
// - error being treated as unhandled by runtime
|
|
1034
|
-
return e;
|
|
1035
|
-
}
|
|
1036
|
-
throw e;
|
|
1037
|
-
};
|
|
1038
|
-
|
|
1039
|
-
try {
|
|
1040
|
-
const thenableArray = [];
|
|
1041
|
-
const callbackResult = callback(...args);
|
|
1042
|
-
if (callbackResult && typeof callbackResult.then === "function") {
|
|
1043
|
-
thenableArray.push(
|
|
1044
|
-
callbackResult.then(
|
|
1045
|
-
(value) => {
|
|
1046
|
-
runResult = value;
|
|
1047
|
-
},
|
|
1048
|
-
(e) => {
|
|
1049
|
-
rejected = true;
|
|
1050
|
-
rejectedValue = e;
|
|
1051
|
-
},
|
|
1052
|
-
),
|
|
1053
|
-
);
|
|
1054
|
-
} else {
|
|
1055
|
-
runResult = callbackResult;
|
|
1056
|
-
}
|
|
1057
|
-
if (ui.renderLoadedAsync && !ui.renderLoaded) {
|
|
1058
|
-
const renderLoadedPromise = ui.renderLoadedAsync(...args).then(
|
|
1059
|
-
(renderLoaded) => {
|
|
1060
|
-
ui.renderLoaded = renderLoaded;
|
|
1061
|
-
},
|
|
1062
|
-
(e) => {
|
|
1063
|
-
if (!rejected) {
|
|
1064
|
-
rejected = true;
|
|
1065
|
-
rejectedValue = e;
|
|
1066
|
-
}
|
|
1067
|
-
},
|
|
1068
|
-
);
|
|
1069
|
-
thenableArray.push(renderLoadedPromise);
|
|
1070
|
-
}
|
|
1071
|
-
if (thenableArray.length === 0) {
|
|
1072
|
-
return onRunEnd();
|
|
1073
|
-
}
|
|
1074
|
-
return Promise.all(thenableArray).then(() => {
|
|
1075
|
-
if (rejected) {
|
|
1076
|
-
return onRunError(rejectedValue);
|
|
1077
|
-
}
|
|
1078
|
-
return onRunEnd();
|
|
1079
|
-
});
|
|
1080
|
-
} catch (e) {
|
|
1081
|
-
return onRunError(e);
|
|
1082
|
-
}
|
|
1083
|
-
};
|
|
1084
|
-
|
|
1085
|
-
const performStop = ({ reason }) => {
|
|
1086
|
-
abort(reason);
|
|
1087
|
-
if (DEBUG) {
|
|
1088
|
-
console.log(`"${action}": stopping (reason: ${reason})`);
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
prerunProtectionRegistry.unprotect(action);
|
|
1092
|
-
|
|
1093
|
-
if (sideEffectCleanup) {
|
|
1094
|
-
sideEffectCleanup(reason);
|
|
1095
|
-
sideEffectCleanup = undefined;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
actionPromiseMap.delete(action);
|
|
1099
|
-
batch(() => {
|
|
1100
|
-
errorSignal.value = null;
|
|
1101
|
-
if (!keepOldData) {
|
|
1102
|
-
dataSignal.value = initialData;
|
|
1103
|
-
}
|
|
1104
|
-
isPrerunSignal.value = true;
|
|
1105
|
-
runningStateSignal.value = IDLE;
|
|
1106
|
-
});
|
|
1107
|
-
};
|
|
1108
|
-
|
|
1109
|
-
const privateProperties = {
|
|
1110
|
-
initialData,
|
|
1111
|
-
|
|
1112
|
-
paramsSignal,
|
|
1113
|
-
runningStateSignal,
|
|
1114
|
-
isPrerunSignal,
|
|
1115
|
-
dataSignal,
|
|
1116
|
-
computedDataSignal,
|
|
1117
|
-
errorSignal,
|
|
1118
|
-
|
|
1119
|
-
performRun,
|
|
1120
|
-
performStop,
|
|
1121
|
-
ui,
|
|
1122
|
-
|
|
1123
|
-
childActionWeakSet,
|
|
1124
|
-
childActionWeakMap,
|
|
1125
|
-
};
|
|
1126
|
-
setActionPrivateProperties(action, privateProperties);
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
return action;
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
rootAction = createActionCore(rootOptions);
|
|
1133
|
-
actionWeakMap.set(callback, rootAction);
|
|
1134
|
-
return rootAction;
|
|
1135
|
-
};
|
|
1136
|
-
|
|
1137
|
-
const createActionProxyFromSignal = (
|
|
1138
|
-
action,
|
|
1139
|
-
paramsSignal,
|
|
1140
|
-
{ rerunOnChange = false, onChange } = {},
|
|
1141
|
-
) => {
|
|
1142
|
-
const actionTargetChangeCallbackSet = new Set();
|
|
1143
|
-
const onActionTargetChange = (callback) => {
|
|
1144
|
-
actionTargetChangeCallbackSet.add(callback);
|
|
1145
|
-
return () => {
|
|
1146
|
-
actionTargetChangeCallbackSet.delete(callback);
|
|
1147
|
-
};
|
|
1148
|
-
};
|
|
1149
|
-
const changeCleanupCallbackSet = new Set();
|
|
1150
|
-
const triggerTargetChange = (actionTarget, previousTarget) => {
|
|
1151
|
-
for (const changeCleanupCallback of changeCleanupCallbackSet) {
|
|
1152
|
-
changeCleanupCallback();
|
|
1153
|
-
}
|
|
1154
|
-
changeCleanupCallbackSet.clear();
|
|
1155
|
-
for (const callback of actionTargetChangeCallbackSet) {
|
|
1156
|
-
const returnValue = callback(actionTarget, previousTarget);
|
|
1157
|
-
if (typeof returnValue === "function") {
|
|
1158
|
-
changeCleanupCallbackSet.add(returnValue);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
};
|
|
1162
|
-
|
|
1163
|
-
let actionTarget = null;
|
|
1164
|
-
let currentAction = action;
|
|
1165
|
-
let currentActionPrivateProperties = getActionPrivateProperties(action);
|
|
1166
|
-
let actionTargetPreviousWeakRef = null;
|
|
1167
|
-
let isFirstEffect = true;
|
|
1168
|
-
|
|
1169
|
-
const _updateTarget = (params) => {
|
|
1170
|
-
const previousActionTarget = actionTargetPreviousWeakRef?.deref();
|
|
1171
|
-
|
|
1172
|
-
if (params === NO_PARAMS) {
|
|
1173
|
-
actionTarget = null;
|
|
1174
|
-
currentAction = action;
|
|
1175
|
-
currentActionPrivateProperties = getActionPrivateProperties(action);
|
|
1176
|
-
} else {
|
|
1177
|
-
actionTarget = action.bindParams(params);
|
|
1178
|
-
if (previousActionTarget === actionTarget) {
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
currentAction = actionTarget;
|
|
1182
|
-
currentActionPrivateProperties = getActionPrivateProperties(actionTarget);
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (isFirstEffect) {
|
|
1186
|
-
isFirstEffect = false;
|
|
1187
|
-
}
|
|
1188
|
-
actionTargetPreviousWeakRef = actionTarget
|
|
1189
|
-
? new WeakRef(actionTarget)
|
|
1190
|
-
: null;
|
|
1191
|
-
triggerTargetChange(actionTarget, previousActionTarget);
|
|
1192
|
-
};
|
|
1193
|
-
|
|
1194
|
-
const proxyMethod = (method) => {
|
|
1195
|
-
return (...args) => {
|
|
1196
|
-
/*
|
|
1197
|
-
* Ensure the proxy targets the correct action before method execution.
|
|
1198
|
-
* This prevents race conditions where external effects run before our
|
|
1199
|
-
* internal parameter synchronization effect. Using peek() avoids creating
|
|
1200
|
-
* reactive dependencies within this pass-through method.
|
|
1201
|
-
*/
|
|
1202
|
-
_updateTarget(proxyParamsSignal.peek());
|
|
1203
|
-
return currentAction[method](...args);
|
|
1204
|
-
};
|
|
1205
|
-
};
|
|
1206
|
-
|
|
1207
|
-
const nameSignal = signal();
|
|
1208
|
-
let actionProxy;
|
|
1209
|
-
if (ACTION_AS_FUNCTION) {
|
|
1210
|
-
actionProxy = function actionProxyFunction() {
|
|
1211
|
-
return actionProxy.rerun();
|
|
1212
|
-
};
|
|
1213
|
-
Object.defineProperty(actionProxy, "name", {
|
|
1214
|
-
configurable: true,
|
|
1215
|
-
get() {
|
|
1216
|
-
return nameSignal.value;
|
|
1217
|
-
},
|
|
1218
|
-
});
|
|
1219
|
-
} else {
|
|
1220
|
-
actionProxy = {
|
|
1221
|
-
get name() {
|
|
1222
|
-
return nameSignal.value;
|
|
1223
|
-
},
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
Object.assign(actionProxy, {
|
|
1227
|
-
isProxy: true,
|
|
1228
|
-
callback: undefined,
|
|
1229
|
-
params: undefined,
|
|
1230
|
-
isPrerun: undefined,
|
|
1231
|
-
runningState: undefined,
|
|
1232
|
-
aborted: undefined,
|
|
1233
|
-
error: undefined,
|
|
1234
|
-
data: undefined,
|
|
1235
|
-
computedData: undefined,
|
|
1236
|
-
completed: undefined,
|
|
1237
|
-
prerun: proxyMethod("prerun"),
|
|
1238
|
-
run: proxyMethod("run"),
|
|
1239
|
-
rerun: proxyMethod("rerun"),
|
|
1240
|
-
stop: proxyMethod("stop"),
|
|
1241
|
-
abort: proxyMethod("abort"),
|
|
1242
|
-
matchAllSelfOrDescendant: proxyMethod("matchAllSelfOrDescendant"),
|
|
1243
|
-
getCurrentAction: () => {
|
|
1244
|
-
_updateTarget(proxyParamsSignal.peek());
|
|
1245
|
-
return currentAction;
|
|
1246
|
-
},
|
|
1247
|
-
bindParams: () => {
|
|
1248
|
-
throw new Error(
|
|
1249
|
-
`bindParams() is not supported on action proxies, use the underlying action instead`,
|
|
1250
|
-
);
|
|
1251
|
-
},
|
|
1252
|
-
replaceParams: null, // Will be set below
|
|
1253
|
-
toString: () => actionProxy.name,
|
|
1254
|
-
meta: {},
|
|
1255
|
-
});
|
|
1256
|
-
Object.preventExtensions(actionProxy);
|
|
1257
|
-
|
|
1258
|
-
onActionTargetChange((actionTarget) => {
|
|
1259
|
-
const currentAction = actionTarget || action;
|
|
1260
|
-
nameSignal.value = `[Proxy] ${currentAction.name}`;
|
|
1261
|
-
actionProxy.callback = currentAction.callback;
|
|
1262
|
-
actionProxy.params = currentAction.params;
|
|
1263
|
-
actionProxy.isPrerun = currentAction.isPrerun;
|
|
1264
|
-
actionProxy.runningState = currentAction.runningState;
|
|
1265
|
-
actionProxy.aborted = currentAction.aborted;
|
|
1266
|
-
actionProxy.error = currentAction.error;
|
|
1267
|
-
actionProxy.data = currentAction.data;
|
|
1268
|
-
actionProxy.computedData = currentAction.computedData;
|
|
1269
|
-
actionProxy.completed = currentAction.completed;
|
|
1270
|
-
});
|
|
1271
|
-
|
|
1272
|
-
const proxyPrivateSignal = (signalPropertyName, propertyName) => {
|
|
1273
|
-
const signalProxy = signal();
|
|
1274
|
-
let dispose;
|
|
1275
|
-
onActionTargetChange(() => {
|
|
1276
|
-
if (dispose) {
|
|
1277
|
-
dispose();
|
|
1278
|
-
dispose = undefined;
|
|
1279
|
-
}
|
|
1280
|
-
dispose = effect(() => {
|
|
1281
|
-
const currentActionSignal =
|
|
1282
|
-
currentActionPrivateProperties[signalPropertyName];
|
|
1283
|
-
const currentActionSignalValue = currentActionSignal.value;
|
|
1284
|
-
signalProxy.value = currentActionSignalValue;
|
|
1285
|
-
if (propertyName) {
|
|
1286
|
-
actionProxy[propertyName] = currentActionSignalValue;
|
|
1287
|
-
}
|
|
1288
|
-
});
|
|
1289
|
-
return dispose;
|
|
1290
|
-
});
|
|
1291
|
-
return signalProxy;
|
|
1292
|
-
};
|
|
1293
|
-
const proxyPrivateMethod = (method) => {
|
|
1294
|
-
return (...args) => currentActionPrivateProperties[method](...args);
|
|
1295
|
-
};
|
|
1296
|
-
|
|
1297
|
-
// Create our own signal for params that we control completely
|
|
1298
|
-
const proxyParamsSignal = signal(paramsSignal.value);
|
|
1299
|
-
|
|
1300
|
-
// Watch for changes in the original paramsSignal and update ours
|
|
1301
|
-
// (original signal wins over any replaceParams calls)
|
|
1302
|
-
weakEffect(
|
|
1303
|
-
[paramsSignal, proxyParamsSignal],
|
|
1304
|
-
(paramsSignalRef, proxyParamsSignalRef) => {
|
|
1305
|
-
proxyParamsSignalRef.value = paramsSignalRef.value;
|
|
1306
|
-
},
|
|
1307
|
-
);
|
|
1308
|
-
|
|
1309
|
-
const proxyPrivateProperties = {
|
|
1310
|
-
get currentAction() {
|
|
1311
|
-
return currentAction;
|
|
1312
|
-
},
|
|
1313
|
-
paramsSignal: proxyParamsSignal,
|
|
1314
|
-
isPrerunSignal: proxyPrivateSignal("isPrerunSignal", "isPrerun"),
|
|
1315
|
-
runningStateSignal: proxyPrivateSignal(
|
|
1316
|
-
"runningStateSignal",
|
|
1317
|
-
"runningState",
|
|
1318
|
-
),
|
|
1319
|
-
errorSignal: proxyPrivateSignal("errorSignal", "error"),
|
|
1320
|
-
dataSignal: proxyPrivateSignal("dataSignal", "data"),
|
|
1321
|
-
computedDataSignal: proxyPrivateSignal("computedDataSignal"),
|
|
1322
|
-
performRun: proxyPrivateMethod("performRun"),
|
|
1323
|
-
performStop: proxyPrivateMethod("performStop"),
|
|
1324
|
-
ui: currentActionPrivateProperties.ui,
|
|
1325
|
-
};
|
|
1326
|
-
|
|
1327
|
-
onActionTargetChange((actionTarget, previousTarget) => {
|
|
1328
|
-
proxyPrivateProperties.ui = currentActionPrivateProperties.ui;
|
|
1329
|
-
if (previousTarget && actionTarget) {
|
|
1330
|
-
const previousPrivateProps = getActionPrivateProperties(previousTarget);
|
|
1331
|
-
if (previousPrivateProps.ui.hasRenderers) {
|
|
1332
|
-
const newPrivateProps = getActionPrivateProperties(actionTarget);
|
|
1333
|
-
newPrivateProps.ui.hasRenderers = true;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
proxyPrivateProperties.childActionWeakSet =
|
|
1337
|
-
currentActionPrivateProperties.childActionWeakSet;
|
|
1338
|
-
});
|
|
1339
|
-
setActionPrivateProperties(actionProxy, proxyPrivateProperties);
|
|
1340
|
-
|
|
1341
|
-
{
|
|
1342
|
-
weakEffect([action], () => {
|
|
1343
|
-
const params = proxyParamsSignal.value;
|
|
1344
|
-
_updateTarget(params);
|
|
1345
|
-
});
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
actionProxy.replaceParams = (newParams) => {
|
|
1349
|
-
if (currentAction === action) {
|
|
1350
|
-
const currentParams = proxyParamsSignal.value;
|
|
1351
|
-
const nextParams = mergeTwoJsValues(currentParams, newParams);
|
|
1352
|
-
if (nextParams === currentParams) {
|
|
1353
|
-
return false;
|
|
1354
|
-
}
|
|
1355
|
-
proxyParamsSignal.value = nextParams;
|
|
1356
|
-
return true;
|
|
1357
|
-
}
|
|
1358
|
-
if (!currentAction.replaceParams(newParams)) {
|
|
1359
|
-
return false;
|
|
1360
|
-
}
|
|
1361
|
-
proxyParamsSignal.value =
|
|
1362
|
-
currentActionPrivateProperties.paramsSignal.peek();
|
|
1363
|
-
return true;
|
|
1364
|
-
};
|
|
1365
|
-
|
|
1366
|
-
if (rerunOnChange) {
|
|
1367
|
-
onActionTargetChange((actionTarget, actionTargetPrevious) => {
|
|
1368
|
-
if (
|
|
1369
|
-
actionTarget &&
|
|
1370
|
-
actionTargetPrevious &&
|
|
1371
|
-
!actionTargetPrevious.isPrerun
|
|
1372
|
-
) {
|
|
1373
|
-
actionTarget.rerun();
|
|
1374
|
-
}
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
|
-
if (onChange) {
|
|
1378
|
-
onActionTargetChange((actionTarget, actionTargetPrevious) => {
|
|
1379
|
-
onChange(actionTarget, actionTargetPrevious);
|
|
1380
|
-
});
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
return actionProxy;
|
|
1384
|
-
};
|
|
1385
|
-
|
|
1386
|
-
const generateActionName = (name, params) => {
|
|
1387
|
-
if (params === NO_PARAMS) {
|
|
1388
|
-
return `${name}({})`;
|
|
1389
|
-
}
|
|
1390
|
-
// Use stringifyForDisplay with asFunctionArgs option for the entire args array
|
|
1391
|
-
const argsString = stringifyForDisplay([params], 3, 0, {
|
|
1392
|
-
asFunctionArgs: true,
|
|
1393
|
-
});
|
|
1394
|
-
return `${name}${argsString}`;
|
|
1395
|
-
};
|
|
1396
|
-
|
|
1397
|
-
if (import.meta.hot) {
|
|
1398
|
-
import.meta.hot.dispose(() => {
|
|
1399
|
-
abortRunningActions();
|
|
1400
|
-
});
|
|
1401
|
-
}
|