@jsenv/navi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/index.js +51 -0
  2. package/package.json +38 -0
  3. package/src/action_private_properties.js +11 -0
  4. package/src/action_proxy_test.html +353 -0
  5. package/src/action_run_states.js +5 -0
  6. package/src/actions.js +1377 -0
  7. package/src/browser_integration/browser_integration.js +191 -0
  8. package/src/browser_integration/document_back_and_forward.js +17 -0
  9. package/src/browser_integration/document_loading_signal.js +100 -0
  10. package/src/browser_integration/document_state_signal.js +9 -0
  11. package/src/browser_integration/document_url_signal.js +9 -0
  12. package/src/browser_integration/use_is_visited.js +19 -0
  13. package/src/browser_integration/via_history.js +199 -0
  14. package/src/browser_integration/via_navigation.js +168 -0
  15. package/src/components/action_execution/form_context.js +8 -0
  16. package/src/components/action_execution/render_actionable_component.jsx +27 -0
  17. package/src/components/action_execution/use_action.js +330 -0
  18. package/src/components/action_execution/use_execute_action.js +161 -0
  19. package/src/components/action_renderer.jsx +136 -0
  20. package/src/components/collect_form_element_values.js +79 -0
  21. package/src/components/demos/0_button_demo.html +155 -0
  22. package/src/components/demos/1_checkbox_demo.html +257 -0
  23. package/src/components/demos/2_input_textual_demo.html +354 -0
  24. package/src/components/demos/3_radio_demo.html +222 -0
  25. package/src/components/demos/4_select_demo.html +104 -0
  26. package/src/components/demos/5_list_scrollable_demo.html +153 -0
  27. package/src/components/demos/action/0_button_demo.html +204 -0
  28. package/src/components/demos/action/10_shortcuts_demo.html +189 -0
  29. package/src/components/demos/action/11_nested_shortcuts_demo.html +401 -0
  30. package/src/components/demos/action/1_input_text_demo.html +461 -0
  31. package/src/components/demos/action/2_form_multiple.html +303 -0
  32. package/src/components/demos/action/3_details_demo.html +172 -0
  33. package/src/components/demos/action/4_input_checkbox_demo.html +611 -0
  34. package/src/components/demos/action/6_checkbox_list_demo.html +109 -0
  35. package/src/components/demos/action/7_radio_list_demo.html +217 -0
  36. package/src/components/demos/action/8_editable_text_demo.html +442 -0
  37. package/src/components/demos/action/9_link_demo.html +172 -0
  38. package/src/components/demos/demo.md +0 -0
  39. package/src/components/demos/route/basic/basic.html +14 -0
  40. package/src/components/demos/route/basic/basic_route_demo.jsx +224 -0
  41. package/src/components/demos/route/multi/multi.html +14 -0
  42. package/src/components/demos/route/multi/multi_route_demo.jsx +277 -0
  43. package/src/components/details/details.jsx +248 -0
  44. package/src/components/details/summary_marker.jsx +141 -0
  45. package/src/components/editable_text/editable_text.jsx +96 -0
  46. package/src/components/error_boundary_context.js +9 -0
  47. package/src/components/form.jsx +144 -0
  48. package/src/components/input/button.jsx +333 -0
  49. package/src/components/input/checkbox_list.jsx +294 -0
  50. package/src/components/input/field.jsx +61 -0
  51. package/src/components/input/field_css.js +118 -0
  52. package/src/components/input/input.jsx +15 -0
  53. package/src/components/input/input_checkbox.jsx +370 -0
  54. package/src/components/input/input_radio.jsx +299 -0
  55. package/src/components/input/input_textual.jsx +338 -0
  56. package/src/components/input/radio_list.jsx +283 -0
  57. package/src/components/input/select.jsx +273 -0
  58. package/src/components/input/use_form_event.js +20 -0
  59. package/src/components/input/use_on_change.js +12 -0
  60. package/src/components/link/link.jsx +291 -0
  61. package/src/components/loader/loader_background.jsx +324 -0
  62. package/src/components/loader/loading_spinner.jsx +68 -0
  63. package/src/components/loader/network_speed.js +83 -0
  64. package/src/components/loader/rectangle_loading.jsx +225 -0
  65. package/src/components/route.jsx +15 -0
  66. package/src/components/selection/selection.js +5 -0
  67. package/src/components/selection/selection_context.jsx +262 -0
  68. package/src/components/shortcut/os.js +9 -0
  69. package/src/components/shortcut/shortcut_context.jsx +390 -0
  70. package/src/components/use_action_events.js +37 -0
  71. package/src/components/use_auto_focus.js +43 -0
  72. package/src/components/use_debounce_true.js +31 -0
  73. package/src/components/use_focus_group.js +19 -0
  74. package/src/components/use_initial_value.js +104 -0
  75. package/src/components/use_is_visited.js +19 -0
  76. package/src/components/use_ref_array.js +38 -0
  77. package/src/components/use_signal_sync.js +50 -0
  78. package/src/components/use_state_array.js +40 -0
  79. package/src/docs/actions.md +228 -0
  80. package/src/docs/demos/resource/action_status.jsx +42 -0
  81. package/src/docs/demos/resource/demo.md +1 -0
  82. package/src/docs/demos/resource/resource_demo_0.html +84 -0
  83. package/src/docs/demos/resource/resource_demo_10_post_gc.html +364 -0
  84. package/src/docs/demos/resource/resource_demo_11_describe_many.html +362 -0
  85. package/src/docs/demos/resource/resource_demo_2.html +173 -0
  86. package/src/docs/demos/resource/resource_demo_3_filtered_users.html +415 -0
  87. package/src/docs/demos/resource/resource_demo_4_details.html +284 -0
  88. package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +115 -0
  89. package/src/docs/demos/resource/resource_demo_6_gc.html +217 -0
  90. package/src/docs/demos/resource/resource_demo_7_child_gc.html +240 -0
  91. package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +319 -0
  92. package/src/docs/demos/resource/resource_demo_9_describe_one.html +472 -0
  93. package/src/docs/demos/resource/tata.jsx +3 -0
  94. package/src/docs/demos/resource/toto.jsx +3 -0
  95. package/src/docs/demos/user_nav/user_nav.html +12 -0
  96. package/src/docs/demos/user_nav/user_nav.jsx +330 -0
  97. package/src/docs/resource_dependencies.md +103 -0
  98. package/src/docs/resource_with_params.md +80 -0
  99. package/src/notes.md +13 -0
  100. package/src/route/route.js +518 -0
  101. package/src/route/route.test.html +228 -0
  102. package/src/store/array_signal_store.js +537 -0
  103. package/src/store/local_storage_signal.js +17 -0
  104. package/src/store/resource_graph.js +1303 -0
  105. package/src/store/tests/resource_graph_autoreload_demo.html +12 -0
  106. package/src/store/tests/resource_graph_autoreload_demo.jsx +964 -0
  107. package/src/store/tests/resource_graph_dependencies.test.js +95 -0
  108. package/src/store/value_in_local_storage.js +187 -0
  109. package/src/symbol_object_signal.js +1 -0
  110. package/src/use_action_data.js +10 -0
  111. package/src/use_action_status.js +47 -0
  112. package/src/utils/add_many_event_listeners.js +15 -0
  113. package/src/utils/array_add_remove.js +61 -0
  114. package/src/utils/array_signal.js +15 -0
  115. package/src/utils/compare_two_js_values.js +172 -0
  116. package/src/utils/execute_with_cleanup.js +21 -0
  117. package/src/utils/get_caller_info.js +85 -0
  118. package/src/utils/iterable_weak_set.js +62 -0
  119. package/src/utils/js_value_weak_map.js +162 -0
  120. package/src/utils/js_value_weak_map_demo.html +690 -0
  121. package/src/utils/merge_two_js_values.js +53 -0
  122. package/src/utils/stringify_for_display.js +150 -0
  123. package/src/utils/weak_effect.js +48 -0
@@ -0,0 +1,85 @@
1
+ export const getCallerInfo = (targetFunction = null, additionalOffset = 0) => {
2
+ const originalPrepareStackTrace = Error.prepareStackTrace;
3
+ try {
4
+ Error.prepareStackTrace = (_, stack) => stack;
5
+
6
+ const error = new Error();
7
+ const stack = error.stack;
8
+
9
+ if (!stack || stack.length === 0 || !Array.isArray(stack)) {
10
+ return { raw: "unknown" };
11
+ }
12
+
13
+ let targetIndex = -1;
14
+
15
+ if (targetFunction) {
16
+ // ✅ Chercher la fonction cible par référence directe
17
+ for (let i = 0; i < stack.length; i++) {
18
+ const frame = stack[i];
19
+ const frameFunction = frame.getFunction();
20
+
21
+ // ✅ Comparaison directe par référence
22
+ if (frameFunction === targetFunction) {
23
+ targetIndex = i;
24
+ break;
25
+ }
26
+ }
27
+
28
+ if (targetIndex === -1) {
29
+ return {
30
+ raw: `target function not found in stack`,
31
+ targetFunction: targetFunction.name,
32
+ };
33
+ }
34
+
35
+ // ✅ Prendre la fonction qui appelle targetFunction + offset
36
+ const callerIndex = targetIndex + 1 + additionalOffset;
37
+
38
+ if (callerIndex >= stack.length) {
39
+ return {
40
+ raw: `caller at offset ${additionalOffset} not found`,
41
+ targetFunction: targetFunction.name,
42
+ requestedIndex: callerIndex,
43
+ stackLength: stack.length,
44
+ };
45
+ }
46
+
47
+ const callerFrame = stack[callerIndex];
48
+ return {
49
+ file: callerFrame.getFileName(),
50
+ line: callerFrame.getLineNumber(),
51
+ column: callerFrame.getColumnNumber(),
52
+ function: callerFrame.getFunctionName() || "<anonymous>",
53
+ raw: callerFrame.toString(),
54
+ targetFunction: targetFunction.name,
55
+ offset: additionalOffset,
56
+ };
57
+ }
58
+
59
+ // ✅ Comportement original si pas de targetFunction
60
+ if (stack.length > 2) {
61
+ const callerFrame = stack[2 + additionalOffset];
62
+
63
+ if (!callerFrame) {
64
+ return {
65
+ raw: `caller at offset ${additionalOffset} not found`,
66
+ requestedIndex: 2 + additionalOffset,
67
+ stackLength: stack.length,
68
+ };
69
+ }
70
+
71
+ return {
72
+ file: callerFrame.getFileName(),
73
+ line: callerFrame.getLineNumber(),
74
+ column: callerFrame.getColumnNumber(),
75
+ function: callerFrame.getFunctionName() || "<anonymous>",
76
+ raw: callerFrame.toString(),
77
+ offset: additionalOffset,
78
+ };
79
+ }
80
+
81
+ return { raw: "unknown" };
82
+ } finally {
83
+ Error.prepareStackTrace = originalPrepareStackTrace;
84
+ }
85
+ };
@@ -0,0 +1,62 @@
1
+ export const createIterableWeakSet = () => {
2
+ const objectWeakRefSet = new Set();
3
+
4
+ return {
5
+ add: (object) => {
6
+ const objectWeakRef = new WeakRef(object);
7
+ objectWeakRefSet.add(objectWeakRef);
8
+ },
9
+
10
+ delete: (object) => {
11
+ for (const weakRef of objectWeakRefSet) {
12
+ if (weakRef.deref() === object) {
13
+ objectWeakRefSet.delete(weakRef);
14
+ return true;
15
+ }
16
+ }
17
+ return false;
18
+ },
19
+
20
+ *[Symbol.iterator]() {
21
+ for (const objectWeakRef of objectWeakRefSet) {
22
+ const object = objectWeakRef.deref();
23
+ if (object === undefined) {
24
+ objectWeakRefSet.delete(objectWeakRef);
25
+ continue;
26
+ }
27
+ yield object;
28
+ }
29
+ },
30
+
31
+ has: (object) => {
32
+ for (const weakRef of objectWeakRefSet) {
33
+ const objectCandidate = weakRef.deref();
34
+ if (objectCandidate === undefined) {
35
+ objectWeakRefSet.delete(weakRef);
36
+ continue;
37
+ }
38
+ if (objectCandidate === object) {
39
+ return true;
40
+ }
41
+ }
42
+ return false;
43
+ },
44
+
45
+ get size() {
46
+ return objectWeakRefSet.size;
47
+ },
48
+
49
+ getStats: () => {
50
+ let alive = 0;
51
+ let dead = 0;
52
+ for (const weakRef of objectWeakRefSet) {
53
+ if (weakRef.deref() !== undefined) {
54
+ alive++;
55
+ } else {
56
+ dead++;
57
+ }
58
+ }
59
+ return { total: objectWeakRefSet.size, alive, dead };
60
+ },
61
+ };
62
+ };
@@ -0,0 +1,162 @@
1
+ /**
2
+ * jsenv/navi - createJsValueWeakMap
3
+ *
4
+ * Key/value cache with true ephemeron behavior and deep equality support.
5
+ *
6
+ * Features:
7
+ * - Mutual retention: key keeps value alive, value keeps key alive
8
+ * - Deep equality: different objects with same content are treated as identical keys
9
+ * - Automatic GC: entries are eligible for collection when unreferenced
10
+ * - Iteration support: can iterate over live entries for deep equality lookup
11
+ *
12
+ * Implementation:
13
+ * - Dual WeakMap (key->value, value->key) provides ephemeron behavior
14
+ * - WeakRef registry enables iteration without preventing GC
15
+ * - Primitives stored in Map (permanent retention - avoid for keys)
16
+ *
17
+ * Use case: Action caching where params (key) and action (value) should have
18
+ * synchronized lifetimes while allowing natural garbage collection.
19
+ */
20
+
21
+ import { compareTwoJsValues } from "./compare_two_js_values.js";
22
+
23
+ export const createJsValueWeakMap = () => {
24
+ // Core ephemeron maps for mutual retention
25
+ const keyToValue = new WeakMap(); // key -> value
26
+ const valueToKey = new WeakMap(); // value -> key
27
+
28
+ // Registry for iteration/deep equality (holds WeakRefs)
29
+ const keyRegistry = new Set(); // Set of WeakRef(key)
30
+
31
+ // Primitive cache
32
+ const primitiveCache = new Map();
33
+
34
+ function cleanupKeyRegistry() {
35
+ for (const keyRef of keyRegistry) {
36
+ if (keyRef.deref() === undefined) {
37
+ keyRegistry.delete(keyRef);
38
+ }
39
+ }
40
+ }
41
+
42
+ return {
43
+ *[Symbol.iterator]() {
44
+ cleanupKeyRegistry();
45
+ for (const keyRef of keyRegistry) {
46
+ const key = keyRef.deref();
47
+ if (key && keyToValue.has(key)) {
48
+ yield [key, keyToValue.get(key)];
49
+ }
50
+ }
51
+ for (const [k, v] of primitiveCache) {
52
+ yield [k, v];
53
+ }
54
+ },
55
+
56
+ get(key) {
57
+ const isObject =
58
+ key && (typeof key === "object" || typeof key === "function");
59
+ if (isObject) {
60
+ // Fast path: exact key match
61
+ if (keyToValue.has(key)) {
62
+ return keyToValue.get(key);
63
+ }
64
+
65
+ // Slow path: deep equality search
66
+ cleanupKeyRegistry();
67
+ for (const keyRef of keyRegistry) {
68
+ const existingKey = keyRef.deref();
69
+ if (existingKey && compareTwoJsValues(existingKey, key)) {
70
+ return keyToValue.get(existingKey);
71
+ }
72
+ }
73
+ return undefined;
74
+ }
75
+ return primitiveCache.get(key);
76
+ },
77
+
78
+ set(key, value) {
79
+ const isObject =
80
+ key && (typeof key === "object" || typeof key === "function");
81
+ if (isObject) {
82
+ cleanupKeyRegistry();
83
+
84
+ // Remove existing deep-equal key
85
+ for (const keyRef of keyRegistry) {
86
+ const existingKey = keyRef.deref();
87
+ if (existingKey && compareTwoJsValues(existingKey, key)) {
88
+ const existingValue = keyToValue.get(existingKey);
89
+ keyToValue.delete(existingKey);
90
+ valueToKey.delete(existingValue);
91
+ keyRegistry.delete(keyRef);
92
+ break;
93
+ }
94
+ }
95
+
96
+ // Set ephemeron pair
97
+ keyToValue.set(key, value);
98
+ valueToKey.set(value, key);
99
+ keyRegistry.add(new WeakRef(key));
100
+ } else {
101
+ primitiveCache.set(key, value);
102
+ }
103
+ },
104
+
105
+ delete(key) {
106
+ const isObject =
107
+ key && (typeof key === "object" || typeof key === "function");
108
+ if (isObject) {
109
+ cleanupKeyRegistry();
110
+
111
+ // Try exact match first
112
+ if (keyToValue.has(key)) {
113
+ const value = keyToValue.get(key);
114
+ keyToValue.delete(key);
115
+ valueToKey.delete(value);
116
+
117
+ // Remove from registry
118
+ for (const keyRef of keyRegistry) {
119
+ if (keyRef.deref() === key) {
120
+ keyRegistry.delete(keyRef);
121
+ break;
122
+ }
123
+ }
124
+ return true;
125
+ }
126
+
127
+ // Try deep equality
128
+ for (const keyRef of keyRegistry) {
129
+ const existingKey = keyRef.deref();
130
+ if (existingKey && compareTwoJsValues(existingKey, key)) {
131
+ const value = keyToValue.get(existingKey);
132
+ keyToValue.delete(existingKey);
133
+ valueToKey.delete(value);
134
+ keyRegistry.delete(keyRef);
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ }
140
+ return primitiveCache.delete(key);
141
+ },
142
+
143
+ getStats: () => {
144
+ cleanupKeyRegistry();
145
+ const aliveKeys = Array.from(keyRegistry).filter((ref) =>
146
+ ref.deref(),
147
+ ).length;
148
+
149
+ return {
150
+ ephemeronPairs: {
151
+ total: keyRegistry.size,
152
+ alive: aliveKeys,
153
+ note: "True ephemeron: key ↔ value mutual retention via dual WeakMap",
154
+ },
155
+ primitive: {
156
+ total: primitiveCache.size,
157
+ note: "Primitive keys never GC'd",
158
+ },
159
+ };
160
+ },
161
+ };
162
+ };