7 |
8 | **Solid Rewind is a time-trave debugger and component-tree visualizer for the reactive framework, SolidJS**
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Features
17 |
18 | * Redux-style time-travel debugging, allowing you to ‘rewind’ to previous versions of your application’s state.
19 | * Dynamic, D3-visualization of your component tree
20 | * Integration with Chrome’s existing Dev Tools menu so you can troubleshoot your Solid App directly from your browser.
21 |
22 |
23 |
24 |
Installation
25 |
26 | :exclamation::exclamation:Before using, you must download our Chrome extension [here](https://chrome.google.com/webstore/detail/solid-rewind/ejdinegdopmimnkbonknknhfmmcgcdoh):exclamation::exclamation:
27 |
28 | **1. install our package with**
29 |
30 | ```javascript
31 | npm i solid-rewind
32 | ```
33 |
34 | **2. Import our Rewind component at the top level of your app.**
35 | **3. Wrap your top-level component in our component.**
36 | ```javascript
37 | import Rewind from 'solid-rewind';
38 |
39 | render( () => {
40 |
41 |
42 |
43 | }, document.getElementById('root'));
44 |
45 | ```
46 | **That's it! Build your project and access our tool in the chrome devtools menu!**
47 |
48 |
57 | Solid Rewind launched on January 15, 2023 and is currently in active beta development through the OSlabs community initiative. The application is licensed under the terms of the MIT license, making it a fully open source product. Developers are welcome to contribute to the codebase and expand on its features.
58 |
--------------------------------------------------------------------------------
/__tests__/test.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/solid-rewind/24821a1d967a97eba8e38884c1bec171eca89a6f/__tests__/test.js
--------------------------------------------------------------------------------
/rewind-chrome-extension/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | **/.DS_Store
4 | dist.zip
--------------------------------------------------------------------------------
/rewind-chrome-extension/Disclaimer.jsx:
--------------------------------------------------------------------------------
1 | function Disclaimer() {
2 |
3 | return (
4 | <>>
5 | );
6 | }
7 |
8 | export default Disclaimer;
9 |
--------------------------------------------------------------------------------
/rewind-chrome-extension/README.md:
--------------------------------------------------------------------------------
1 | # Solid Rewind
2 |
3 | A time-travel debugger for SoldJs.
4 | This works with our chrome extention found here: _link to will be posted soon_
5 |
);
31 | }
32 | if (props.runInProduction) {
33 | console.log(`%cWARNING: Solid Rewind is a debugger tool meant for development mode. We do not recommend enabling it for production.`, `color:orange; font-weight: bold`);
34 | }
35 |
36 | //establish the owner at the top level component, so that we can pass this owner to internal functions and keep it consistent
37 | //if we tried to run these internal functions with their internal owner, we'd see a very different ownership tree
38 | const owner = getOwner();
39 | console.log(owner)
40 |
41 | // save owner, passed to dev-tool functions
42 | saveOwner(owner);
43 |
44 | // send tree to chrome
45 | const sendTreeStructure = async (_owner) => {
46 | //building component trees
47 | let compTree = await buildComponentTree(_owner);
48 | // send to chrome extention + convert to a json-safe format
49 | sendTreeToChrome(compTree)
50 | }
51 |
52 | // respond to initial request for the component tree
53 | const requestTree = () => {
54 | runWithOwner(owner, async () => {
55 | let ownerObj = await getOwner();
56 | sendTreeStructure(ownerObj); // build component tree and send it to chrome extension
57 | })
58 | }
59 | // listen for initial tree requests
60 | listenFor('INITAL TREE REQUEST', requestTree);
61 |
62 | //function allows us to reset state of a signal
63 | addStoreStateToHistory();
64 |
65 | //listener watches for changes in the reactive graph
66 | //when there is a state change in solid, this listener will run
67 | const listen = () => {
68 |
69 | //creating a new set
70 | const GraphUpdateListeners = new Set();
71 |
72 | const setUpRenderChangeEvent = () => {
73 |
74 | //adding a new data only when we are getting a new data set.
75 | GraphUpdateListeners.add( () => {
76 | // dont run this at all if we are reversing or nexting
77 | if (getDontRecordFlag()) {
78 | unflagDontRecordNextChange();
79 | return;
80 | }
81 | //setting it 0 to initiate the invokation of the function.
82 | runListenerOnce--
83 |
84 | if (runListenerOnce === 0) {
85 | runWithOwner(owner, async () => {
86 | //creating a new owner object
87 | let ownerObj = await getOwner();
88 | //creating a new tree
89 | let ownerTree = await new Tree(ownerObj);
90 | //parsing the tree data
91 | let sourcesState = await ownerTree.parseSources();
92 | sendTreeStructure(ownerObj); // build component tree and send it to chrome extension
93 |
94 | // send this sourcesState to stateParser
95 | analyzeStateChange( sourcesState );
96 | })}
97 | })
98 |
99 | //putting runListenerOnce back to 1 so that function works once
100 | GraphUpdateListeners.add(() => {
101 | runListenerOnce++
102 | })
103 |
104 |
105 | GraphUpdateListeners.add(setHistoryAfterUpdate)
106 | const runListeners = () => {
107 | GraphUpdateListeners.forEach(f => f());
108 | runListenerOnce = 1;
109 | }
110 | window._$afterUpdate = runListeners
111 | }
112 |
113 | setUpRenderChangeEvent();
114 |
115 | }
116 |
117 | listen()
118 |
119 | return (
120 |
{props.children}
121 | )
122 | }
123 |
124 | export default Rewind;
--------------------------------------------------------------------------------
/rewind-devtool/src/logger-treeview/compTree.js:
--------------------------------------------------------------------------------
1 | import { setChildMap } from "../solid-rw";
2 |
3 | const debugMode = false;
4 |
5 | export async function buildComponentTree(root) {
6 | //composition of tree
7 | function newParent() {
8 | return {
9 | componentName: "",
10 | children: [],
11 | names: new Set(),
12 | };
13 | }
14 |
15 | function buildTree(owner, parent, first = false) {
16 | // if this is a compnent we care about
17 | // set its name
18 | if (owner?.componentName) {
19 | if (!first) {
20 | const np = newParent("");
21 | parent.children.push(np);
22 | parent = np;
23 | }
24 | parent.componentName = owner.componentName;
25 |
26 | // setup name
27 | parent.names = new Set([owner.name]);
28 |
29 | // add to child map
30 | buildMapOfChildren(parent.name, parent.componentName);
31 |
32 | // go into children
33 | if (owner.owned && owner.owned.length) {
34 | for (const c of owner.owned) {
35 | buildTree(c, parent);
36 | }
37 | }
38 | if (owner.sdtType === "memo" && owner.sdtSubRoots.length) {
39 | for (const c of owner.sdtSubRoots) {
40 | buildTree(c, parent);
41 | }
42 | }
43 | }
44 | // if not a "named" component, handle its children
45 | else {
46 | // gets names of all non-component childen so we can see if any of them are observers of a given symbol
47 | parent.names = new Set([...parent.names, owner.name]);
48 |
49 | // add to child map
50 | buildMapOfChildren(owner.name, parent.componentName);
51 |
52 | // go into children
53 | if (owner.owned && owner.owned.length) {
54 | for (const c of owner.owned) {
55 | buildTree(c, parent);
56 | }
57 | }
58 | if (owner.sdtType === "memo" && owner.sdtSubRoots.length) {
59 | for (const c of owner.sdtSubRoots) {
60 | buildTree(c, parent);
61 | }
62 | }
63 | }
64 |
65 | // base case -- is this nessesary?!?
66 | if (!owner) {
67 | return tree;
68 | }
69 | }
70 |
71 | // child map will be here
72 | const childMap = {};
73 |
74 | // begin building tree
75 | const tree = newParent("");
76 | buildTree(root, tree, true);
77 |
78 | function buildMapOfChildren(cName, parent) {
79 | if (!cName || !parent) return;
80 | if (debugMode)
81 | console.log("adding to child map - CHILD", { cName, parent });
82 | // if no value is found, make a new set
83 | if (childMap[cName] === undefined) {
84 | childMap[cName] = new Set();
85 | }
86 | // add to child map
87 | childMap[cName].add(parent);
88 | // log it
89 | if (debugMode) console.log("child:", cName, childMap[cName]);
90 | }
91 |
92 | // saveChildMap
93 | setChildMap(childMap);
94 |
95 | return tree;
96 | }
97 |
--------------------------------------------------------------------------------
/rewind-devtool/src/logger-treeview/treeView.js:
--------------------------------------------------------------------------------
1 | import { sendData } from "../sender";
2 |
3 | export const sendTreeToChrome = tree => {
4 | // deciruclarize. Not nessesary anymore
5 | // const nonCiruclar = stringifyCircularJSON(tree);
6 |
7 | // convert name sets into an object
8 | convertNameSetToObj(tree);
9 |
10 | // save to stack
11 | addToTreeStack(JSON.stringify(tree))
12 |
13 | // give chrome tool a moment to load before we send the tree.
14 | sendData(tree, 'TREE');
15 | }
16 |
17 | // itterate though tree at each level and convert set to object
18 | const convertNameSetToObj = (tree) => {
19 | if (tree.names) {
20 | const nameSet = tree.names;
21 | tree.names = Object.assign(...Array.from(nameSet, v => ({[v]:''})));
22 | }
23 |
24 | if (!tree.children) return tree;
25 | else {
26 | tree.children.forEach(element => {
27 | convertNameSetToObj(element);
28 | });
29 | }
30 | }
31 |
32 | // uncircularize object json
33 | export const stringifyCircularJSON = obj => {
34 | const seen = new WeakSet();
35 | return JSON.stringify(obj, (k, v) => {
36 | if (v !== null && typeof v === 'object') {
37 | if (seen.has(v)) return;
38 | seen.add(v);
39 | }
40 | return v;
41 | });
42 | };
43 |
44 | // stacks
45 | const treePast= [];
46 | const treeFuture = []
47 |
48 | const addToTreeStack = (compTreeString) => {
49 | treePast.push(compTreeString);
50 | treeFuture.length = 0;
51 | }
52 |
53 | export const goForwardTree = () => {
54 | if (treeFuture.length === 0) return;
55 | // move future to past
56 | treePast.push(treeFuture.pop());
57 | // send top of past to chrome
58 | sendData(treePast[treePast.length-1], 'TREE');
59 | }
60 |
61 | export const goBackTree = () => {
62 | if (treePast.length <= 1) return;
63 | // move future to past
64 | treeFuture.push(treePast.pop());
65 | // send top of past to chrome
66 | sendData(treePast[treePast.length-1], 'TREE');
67 | }
68 |
--------------------------------------------------------------------------------
/rewind-devtool/src/logger.js:
--------------------------------------------------------------------------------
1 | export default function log (messages, prefixOrFile = '', color = 'orange') {
2 | if (prefixOrFile === '') {
3 | if (Array.isArray(messages)) console.log(`%c ${ messages }`, `color:${color}; font-weight: bold`);
4 | else console.log(`%c ${messages}`, `color:${color}; font-weight: bold` );
5 | }
6 | else {
7 | if (Array.isArray(messages)) console.log(`%c ${prefixOrFile}`, `color:${color}; font-weight: bold`, ...messages);
8 | else console.log(`%c ${prefixOrFile}`, `color:${color}; font-weight: bold`, messages);
9 | }
10 | }
--------------------------------------------------------------------------------
/rewind-devtool/src/rewind-init.js:
--------------------------------------------------------------------------------
1 | import { senderInit } from './sender';
2 | import { initSR } from './solid-rw';
3 |
4 |
5 | // failsafe to be sure this can only be initilized once
6 | let initilized = false;
7 |
8 | // call this ince to initilize stuff
9 | export function init() {
10 | if (initilized) return;
11 |
12 | initSR();
13 | senderInit();
14 |
15 | // initialization complete
16 | initilized = true;
17 | }
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/rewind-devtool/src/rewind-store.ts:
--------------------------------------------------------------------------------
1 | import { DEV } from "solid-js/store";
2 | import { batch } from 'solid-js';
3 | import { ChangeObj, flagDontRecordNextChange } from "./stateParser";
4 | import { addToChangeStack } from './solid-rw.js';
5 |
6 | let currentUpdates = {};
7 |
8 | export function changeStoreState(stateToSet, state) {
9 | const nodes = state[DEV.$NODE];
10 |
11 | // flag dont record
12 | flagDontRecordNextChange();
13 |
14 | // execute changes
15 | batch(() => {
16 | if (Array.isArray(stateToSet)) {
17 | const len = state.length;
18 | for (let i = 0; i < stateToSet.length; i++) {
19 | state[i] = stateToSet[i];
20 | nodes[i]?.$(() => stateToSet[i]);
21 | }
22 |
23 | if (len !== stateToSet.length) {
24 | state.length = stateToSet.length;
25 | nodes.length?.$(stateToSet.length);
26 | }
27 | }
28 |
29 | else {
30 | const stateKeys = new Set(Object.keys(state));
31 | for (const [key, value] of Object.entries(stateToSet)) {
32 | state[key] = value;
33 | nodes[key]?.$(() => value);
34 | stateKeys.delete(key);
35 | }
36 | for (const key of stateKeys) {
37 | delete state[key];
38 | nodes[key]?.$(undefined);
39 | }
40 | }
41 | nodes._?.$();
42 | });
43 | };
44 |
45 | const saveChangeToHistory = (storeChange: any) => {
46 | const change: ChangeObj = {
47 | name: '',
48 | prev: '',
49 | next: '',
50 | path: '',
51 | store: storeChange,
52 | observers: []
53 | }
54 | // add change to stack
55 | addToChangeStack(change);
56 | }
57 |
58 |
59 | export const addStoreStateToHistory = () => {
60 | window._$onStoreNodeUpdate = (state, property, value, prev) => {
61 | const oldcopy = Array.isArray(state) ? state.slice() : Object.assign({}, state);
62 | currentUpdates = {state: state, oldState: oldcopy};
63 | };
64 | }
65 |
66 | export const setHistoryAfterUpdate = () => {
67 | if (Object.keys(currentUpdates).length === 0) return;
68 | // add new state to our change
69 | const newCopy = Array.isArray(currentUpdates.state) ? currentUpdates.state.slice() : Object.assign({}, currentUpdates.state);
70 | currentUpdates.newState = newCopy;
71 |
72 | // send change
73 | saveChangeToHistory(currentUpdates);
74 |
75 | // clear current update
76 | currentUpdates = {};
77 | }
--------------------------------------------------------------------------------
/rewind-devtool/src/sender.js:
--------------------------------------------------------------------------------
1 |
2 | // function to send message to devtool
3 | export function sendData( data, type ) {
4 | // stringify data (in case it's an objec or array)
5 | const payload = (typeof data === 'object') ? JSON.stringify(data) : data;
6 | postMessageToDebugger(payload, type)
7 | }
8 |
9 | // format the data into an object with the from, type and payload. Send it.
10 | function postMessageToDebugger (payload, type) {
11 | const msgObj = { from: "FROM_PAGE", type, payload };
12 | // this should be send on something besides the window.
13 | window.postMessage(msgObj, "*");
14 | }
15 |
16 | // Initilize the sender. This should only ever be run once.
17 | export function senderInit () {
18 | // there will only be one listener which recieves messages and delegates out events
19 | window.addEventListener("message", (event) => {
20 | // only pay attetion to messages from the devtool
21 | if (event.data.from === "FROM_DEVTOOL" ) {
22 | // dispatch an event with the 'type' as the message type. Listeners can then grab it
23 | const msgRecievedEvent = new CustomEvent(event.data.type, {
24 | detail: { ...event.data }
25 | });
26 | document.dispatchEvent(msgRecievedEvent);
27 | }
28 | }, false);
29 |
30 | // send message to client to erase any saved state if the page refreshes
31 | window.onbeforeunload = function(event) {
32 | sendData( undefined, 'RESET_STATE' )
33 | };
34 | }
35 |
36 | // function to call to listen for messages from dev tool
37 | export function listenFor ( type, callback ) {
38 | // pass along only the payload to the callback
39 | document.addEventListener( type, (data) => callback(data.detail) );
40 | }
--------------------------------------------------------------------------------
/rewind-devtool/src/solid-rw.js:
--------------------------------------------------------------------------------
1 | import * as sender from "./sender";
2 | import { DEV, runWithOwner } from 'solid-js';
3 | import { flagDontRecordNextChange, reverseSavedStateHistory, getDontRecordFlag, clearSavedStateFuture, forwardInSavedStateHistory } from "./stateParser";
4 | import { changeStoreState } from "./rewind-store";
5 | import log from "./logger";
6 | import { sendData } from './sender';
7 | import { goBackTree, goForwardTree } from "./logger-treeview/treeView";
8 |
9 | const debugMode = false;
10 | const logFullChangeStack = false;
11 |
12 |
13 | //logic for mapping the children for purposes of mapping named components
14 | const childMap = [{}];
15 |
16 | // get comp tree
17 | export const getChildMap = () => childMap[0];
18 | // set comp tree
19 | export const setChildMap = cm => {
20 | Object.assign(childMap[0], cm);
21 | }
22 |
23 |
24 | // call this once to set up listeners
25 | export function initSR() {
26 | setupListeners();
27 | }
28 |
29 | // call this once to save the original owner object //
30 | export function saveOwner( ownerObj ) {
31 | if (owner.length === 0) owner.push( ownerObj );
32 | }
33 |
34 | ////////////// TIME CONTROL LISTENERS FOR EVENTS FORM CHROME DEVTOOL /////////////////////
35 | function setupListeners() {
36 | sender.listenFor('BACK', travelBack);
37 | sender.listenFor('FORWARD', travelForward);
38 | // sender.listenFor('COPY_STATE', copyState);
39 | // sender.listenFor('LOAD_STATE', loadState);
40 | }
41 |
42 | // REF TO ORIGINAL OWNER
43 | const owner = [];
44 |
45 |
46 | // CHANGE STACKS
47 | const changeStack = [];
48 | const changeFutureStack = [];
49 |
50 |
51 | // debug function to log current change stack. attach this to a button or something for debugging
52 | export const logChangeStack = () => {
53 | console.log ('CHANGE STACK:', changeStack);
54 | }
55 |
56 | // pushes change to stack. called from stateParser
57 | export const addToChangeStack = ( change ) => {
58 | if (debugMode) log([change], 'ADDED TO CHANGE STACK', 'BLUE');
59 | changeStack.push(change);
60 | if (logFullChangeStack) logChangeStack();
61 | clearFutureStack();
62 |
63 | // increment staet in chrome tool
64 | sendData([changeStack.length], 'STATE_INCREMENT');
65 | }
66 |
67 | // clear the future stack. used when recording new things while in the past.
68 | const clearFutureStack = () => {
69 | changeFutureStack.length = 0;
70 |
71 | // clears saved state future in stateParser
72 | clearSavedStateFuture();
73 | }
74 |
75 | // GO BACK IN TIME
76 | export const reverse = () => {
77 | // if stack is empty, do nothing
78 | if (changeStack.length === 0) return;
79 | // get the change to reverse
80 | const rev = changeStack.pop();
81 |
82 | // execute change
83 | if (!rev.store) setState(rev.prev, rev.path);
84 | else { // store
85 | changeStoreState(rev.store.oldState, rev.store.state)
86 | }
87 |
88 | // add change to future stack
89 | changeFutureStack.push(rev);
90 |
91 | // revese saved state history
92 | reverseSavedStateHistory();
93 |
94 | // reverse comp tree
95 | goBackTree();
96 | }
97 |
98 |
99 | // GO FORWARD IN TIME
100 | export const next = () => {
101 | // if stack is empty, do nothing
102 | if (changeFutureStack.length === 0) return;
103 | // get the next change
104 | const next = changeFutureStack.pop();
105 |
106 | // excute change
107 | if (!next.store) setState(next.next, next.path);
108 | else { // store
109 | changeStoreState(next.store.newState, next.store.state)
110 | }
111 |
112 | // add change to change stack
113 | changeStack.push(next);
114 |
115 | // log change stack
116 | if (debugMode) log(changeStack, 'change stack');
117 |
118 | // foward in saved state history
119 | forwardInSavedStateHistory();
120 |
121 | // forward comp tree
122 | goForwardTree();
123 | }
124 |
125 |
126 |
127 | // THESE ARE USED TO TRAVEL FORWARD IN BACK MULTIPLE STEPS AT A TIME
128 | function travelBack( data ) {
129 | const steps = data.payload;
130 | for (let i = 0; i < steps; i++) {
131 | reverse();
132 | }
133 | }
134 | function travelForward( data ) {
135 | const steps = data.payload;
136 | for (let i = 0; i < steps; i++) {
137 | next();
138 | }
139 | }
140 |
141 |
142 |
143 |
144 | // COPY AND PASTE STATE //
145 | export function copyState() {
146 | copyTextToClipboard(JSON.stringify(changeStack));
147 | }
148 |
149 | // this method is nessesart to get around focus persmissions for copying data
150 | function copyTextToClipboard(text) {
151 | //Create a textbox field where we can insert text to.
152 | var copyFrom = document.createElement("textarea");
153 |
154 | //Set the text content to be the text you wished to copy.
155 | copyFrom.textContent = text;
156 |
157 | //Append the textbox field into the body as a child.
158 | //"execCommand()" only works when there exists selected text, and the text is inside
159 | //document.body (meaning the text is part of a valid rendered HTML element).
160 | document.body.appendChild(copyFrom);
161 |
162 | //Select all the text!
163 | copyFrom.select();
164 |
165 | //Execute command
166 | document.execCommand('copy');
167 |
168 | //(Optional) De-select the text using blur().
169 | copyFrom.blur();
170 |
171 | //Remove the textbox field from the document.body, so no other JavaScript nor
172 | //other elements can get access to this.
173 | document.body.removeChild(copyFrom);
174 | }
175 |
176 | // load state
177 | export async function loadState (state) {
178 |
179 | // get state from payload
180 | let stateData = state?.payload;
181 |
182 | // return if empty
183 | if (stateData === "") return;
184 |
185 | // reverse saved state
186 | const stateToDo = JSON.parse(stateData).reverse();
187 |
188 | // execute all state
189 | while (stateToDo.length) {
190 | // get element of state to set
191 | const curr = stateToDo.pop();
192 | console.log(curr);
193 |
194 | // push change into past stack
195 | addToChangeStack( curr );
196 |
197 | // set state
198 | setState(curr.next, curr.path);
199 | }
200 |
201 | // clear future stack
202 | clearFutureStack();
203 | }
204 |
205 | // call this to set the state
206 | const setState = ( value, path ) => {
207 | runWithOwner(owner[0], async () => {
208 | const source = getPathEnd(path);
209 |
210 | // flag upcoming change as one not to record
211 | flagDontRecordNextChange();
212 |
213 | DEV.writeSignal(source, value);
214 | });
215 | }
216 |
217 | // traverses the string path to actually find the object who's data needs to be set
218 | const getPathEnd = ( path ) => {
219 | // get path
220 | const splitPath = path.split('.');
221 | // will be the end of teh path
222 | let pathEnd = owner[0];
223 | // traverse the path
224 | for (const p of splitPath) {
225 | if (p === '') continue;
226 | const pathItem = p.split(/[[\]]+/);
227 | pathEnd = pathEnd[pathItem[0]][Number(pathItem[1])];
228 | }
229 | // return the path
230 | return pathEnd;
231 | }
232 |
--------------------------------------------------------------------------------
/rewind-devtool/src/stateParser.ts:
--------------------------------------------------------------------------------
1 | import { addToChangeStack, getChildMap } from './solid-rw';
2 | import { sendData } from './sender';
3 | import log from './logger';
4 |
5 | const debugMode = false;
6 | const debugShowStore = false;
7 | const debugShowPropigation = false;
8 |
9 | type StateObject = {
10 | name: string,
11 | path: string,
12 | store: boolean,
13 | value: any
14 | }
15 |
16 | export type ChangeObj = {
17 | name: string,
18 | prev: any,
19 | next: any,
20 | path: string,
21 | store: boolean,
22 | observers: any
23 | }
24 |
25 | // new and old state
26 | const stateHistory: any = [];
27 | const stateFuture: any = [];
28 |
29 |
30 | const flagDontRecord = [false];
31 |
32 | export const flagDontRecordNextChange = () => {
33 | flagDontRecord[0] = true;
34 | }
35 |
36 | export const unflagDontRecordNextChange = () => {
37 | flagDontRecord[0] = false;
38 | }
39 |
40 | export const getDontRecordFlag = () => {
41 | return flagDontRecord[0];
42 | }
43 |
44 | export const analyzeStateChange = ( sourcesState: any ) => {
45 | // add state to our last / newState
46 | stateHistory.push( sourcesState );
47 |
48 | // if newState exists, compare the two
49 | if (stateHistory.length >= 2) findStateChanges();
50 | }
51 |
52 | // when we go back in time, we need to also reverse the saved state history
53 | export const reverseSavedStateHistory = () => {
54 | stateFuture.push( stateHistory.pop() );
55 | }
56 |
57 | export const forwardInSavedStateHistory = () => {
58 | stateHistory.push( stateFuture.pop() );
59 | }
60 |
61 | export const clearSavedStateFuture = () => {
62 | stateFuture.length = 0;
63 | }
64 |
65 |
66 | // get all observers of this state item
67 | const getObserverNamesFromChange = ( change: any ) : any => {
68 | const observers = []
69 | if (change?.underlyingSource?.observers?.length) {
70 | for (const o of change.underlyingSource.observers) {
71 | observers.push(o.name);
72 | }
73 | }
74 | return observers;
75 | }
76 |
77 |
78 |
79 | // compare states to find changes
80 | const findStateChanges = () => {
81 | const oldState: any = stateHistory[stateHistory.length-2];
82 | const newState: any = stateHistory[stateHistory.length-1];
83 |
84 | // gets old keys to itterate over
85 | const oldKeys = Object.keys(oldState);
86 |
87 | // a copy of the new state. we'll remove items from here as we check them off. left over items will be anything that was added to the state on this render.
88 | const remainingNewKeys = {...newState}
89 |
90 | // log of changes that occured this render
91 | const changes = [];
92 |
93 | // checks for elements that were changed or removed
94 | for (const k of oldKeys) {
95 |
96 | // key removed
97 | if (!newState[k]) { // dont need this
98 | log(['REMOVED STATE', k], 'NOTE!', 'red');
99 | }
100 |
101 | // change occured
102 | else if (oldState[k].value != newState[k].value) {
103 | const change = createChange(oldState[k], newState[k].value);
104 | changes.push(change);
105 | }
106 | // remove key from list
107 | if (newState[k]) delete remainingNewKeys[k]// remove key from both
108 | }
109 |
110 | // long changes and push them to the change stack
111 | if (debugShowStore) console.log('CHANGES:', changes);
112 |
113 | // add changes to change stack
114 | for (const change of changes) {
115 | if (change.path.includes('sourceMap')) change.store = true;
116 |
117 | // add change to stack
118 | addToChangeStack(change);
119 |
120 | // this is not the right place for this... or at least not the only place
121 | logNamedAppThatChangeAffected(change.observers);
122 | }
123 | }
124 |
125 | ///////////// SEND STATE CHANGE ////////////////
126 |
127 |
128 | const createChange = (obj:StateObject, changedTo = '', newItem = false) => {
129 | const change: ChangeObj = {
130 | name: obj.name,
131 | prev: newItem ? '__new__' : obj.value,
132 | next: changedTo,
133 | path: obj.path,
134 | store: obj.store,
135 | observers: getObserverNamesFromChange(obj)
136 | }
137 | return change;
138 | }
139 |
140 |
141 | // WHEN DO I CALL THIS ?????
142 | const logNamedAppThatChangeAffected = ( observers: Array ) => {
143 | if (debugShowPropigation) console.log('changes observers:', observers);
144 |
145 | const childMap = getChildMap();
146 | if (debugShowPropigation) console.log('child map:', childMap);
147 |
148 | if (!childMap) {
149 | if (debugShowPropigation) console.log("ALERT!!! COMP TREE EMPTY!")
150 | return;
151 | }
152 |
153 | for (const o of observers) {
154 | if (debugShowPropigation) console.log("COMPONENT TOUCHED:", childMap[o])
155 | }
156 |
157 | }
158 |
159 |
160 |
--------------------------------------------------------------------------------
/rewind-devtool/src/tree.js:
--------------------------------------------------------------------------------
1 | export default class OwnershipTree {
2 | constructor(owner, path) {
3 | this.name = this.getName(owner);
4 | this.path = path ? path : '';
5 | this.children = this.getChildren(owner);
6 | this.sourceMap = this.getSourceMap(owner);
7 | this.sources = this.getSources(owner);
8 | }
9 |
10 | //this method gets the name for a particular owner.
11 | //it is invoked in the constructor
12 | getName(owner) {
13 | if (owner?.name) return owner.name;
14 | }
15 |
16 | //this method gets the children for a particular owner.
17 | //it is invoked in the constructor
18 | getChildren(owner) {
19 | const childArray = [];
20 | if (owner?.owned) {
21 | for (const key in owner.owned) {
22 | const child = owner.owned[key];
23 | const childPath = this.path + `.owned[${key}]`
24 | if (child) {
25 | childArray.push(new OwnershipTree(child, childPath))
26 | }
27 | }
28 | }
29 | // for momo - this occurs in for loops among other places. In this case, children are in 'sdtSubRoots' rather than 'owned'
30 | if (owner?.sdtType === "memo" && owner?.sdtSubRoots?.length) {
31 | for (const key in owner.sdtSubRoots) {
32 | const child = owner.sdtSubRoots[key];
33 | const childPath = this.path + `.sdtSubRoots[${key}]`
34 | if (child) {
35 | childArray.push(new OwnershipTree(child, childPath))
36 | }
37 | }
38 | }
39 | return childArray;
40 | }
41 |
42 |
43 | //this method gets the sourcemap for a particular owner.
44 | //it is invoked in the constructor
45 | getSourceMap(owner) {
46 | const listOfSignals = [];
47 | if (owner?.sourceMap) {
48 | const srcMap = owner.sourceMap;
49 | for (const key in srcMap) {
50 | //this means it's a regular signal
51 | if (srcMap[key].name) {
52 | let sourcePath = this.path + `.sourceMap.${key}`;
53 | listOfSignals.push({
54 | name: srcMap[key].name,
55 | value: srcMap[key].value,
56 | path: sourcePath,
57 | store: false
58 | })
59 | }
60 | //this means it's a store
61 | else {
62 | let sourcePath = this.path + `.sourceMap.${key}.value`;
63 | listOfSignals.push({
64 | name: srcMap[key].value["Symbol(store-name)"],
65 | value: srcMap[key].value,
66 | path: sourcePath,
67 | store: true
68 | })
69 | }
70 | }
71 | return listOfSignals
72 | }
73 | }
74 |
75 | //this method gets the sources (not the sourceMap) for a particular owner.
76 | //it is invoked in the constructor
77 | getSources(owner) {
78 | if (owner?.sources) return owner.sources;
79 | }
80 |
81 | //this method parses the source key of every owner on our owner tree
82 | //it returns an object of all of the relevant sources
83 | parseSources(stack = {}, sourceMapSources = {}) {
84 |
85 | // uncomment if we want to explore sourceMap features
86 | // if (this.sourceMap?.length > 0) {
87 | // this.sourceMap.forEach(source => {
88 | // sourceMapSources[source.name] = source;
89 | // })
90 | // }
91 |
92 |
93 | if (this.sources?.length > 0) {
94 | for (let i = 0; i < this.sources.length; i++) {
95 | const source = this.sources[i]
96 | //this helps us track down the source
97 | let sourcePath = this.path + `.sources[${i}]`
98 |
99 | //inspect s9 more...seems to relate to rendered components
100 | //but for now we can ignore it
101 |
102 | //''''consider adding this back if s9 proves unhelpful
103 | // if (source.name && source.name === 's9') continue;
104 |
105 | //comparator seems to be a function that allows these signals/components know if they need to re-render
106 | //everything that gets rendered appears to have a comparator key
107 |
108 | //'''''consider adding
109 | // if (source.comparator) {
110 |
111 | /*
112 | the following if block finds pure signals made with create signal. Sometimes signals
113 | can have the same signal name, even if they're in different components (for example, if a single
114 | signal is a passed down to multiple child components). If we find a signal that goes by the
115 | same name as the signal already in our stack, we check if that signal is being observed by the exact same components
116 | If not, we know it's a unique signal and we add it to our stack at the relevant key
117 | */
118 | if (source?.name && source.name[0].toString() == 's') {
119 | //initially, the stack's values will be an array of signals. Each signal in
120 | // the respective array will have the same name, but a unique set of observers
121 | if (!stack[source.name]) stack[source.name] = [];
122 | let observerString = ''
123 | if (source.observers) {
124 | const observers = source.observers;
125 | for (const obs of observers) {
126 | observerString += (obs.name + '|||');
127 | }
128 | }
129 |
130 | if (stack[source.name].every(el => el.observerString !== observerString)) {
131 | stack[source.name].push({
132 | name: source.name,
133 | value: source.value,
134 | path: sourcePath,
135 | type: "signal",
136 | observerString: observerString,
137 | underlyingSource: source
138 | })
139 | }
140 | }
141 | }
142 | }
143 |
144 | //moves on to the next child and recursively runs the search function on every child node in the tree
145 | if (this.children?.length > 0) {
146 | this.children.forEach(child => {
147 | if (child) {
148 | child.parseSources(stack, sourceMapSources)
149 | }
150 | })
151 | }
152 |
153 | const returnObj = {};
154 |
155 | //flattens the existing stack of signals
156 | for (const keys of Object.values(stack)) {
157 | keys.forEach((el, idx) => returnObj[el.name + "%%%" + idx] = el)
158 | }
159 |
160 | return Object.keys(sourceMapSources).length ? {sources: returnObj, sourceMaps: sourceMapSources} : returnObj;
161 |
162 | }
163 |
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/rewind-devtool/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | "declaration": true /* Generates corresponding '.d.ts' file. */,
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./lib" /* Redirect output structure to the directory. */,
18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true /* Enable all strict type-checking options. */,
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 |
43 | /* Module Resolution Options */
44 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
48 | // "typeRoots": [], /* List of folders to include type definitions from. */
49 | // "types": [], /* Type declaration files to be included in compilation. */
50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
51 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
53 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
54 |
55 | /* Source Map Options */
56 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
60 |
61 | /* Experimental Options */
62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
64 |
65 | /* Advanced Options */
66 | "skipLibCheck": true /* Skip type checking of declaration files. */,
67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
68 | },
69 | "include": ["src"],
70 | "exclude": ["node_modules", "**/__tests__/*"]
71 | }
--------------------------------------------------------------------------------
/rewind-devtool/yarn-error.log:
--------------------------------------------------------------------------------
1 | Arguments:
2 | /Users/willem/.nvm/versions/node/v18.12.1/bin/node /Users/willem/.nvm/versions/node/v18.12.1/bin/yarn login
3 |
4 | PATH:
5 | /Users/willem/.nvm/versions/node/v18.12.1/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Mono.framework/Versions/Current/Commands
6 |
7 | Yarn version:
8 | 1.22.19
9 |
10 | Node version:
11 | 18.12.1
12 |
13 | Platform:
14 | darwin x64
15 |
16 | Trace:
17 | Error: canceled
18 | at Interface. (/Users/willem/.nvm/versions/node/v18.12.1/lib/node_modules/yarn/lib/cli.js:137150:13)
19 | at Interface.emit (node:events:513:28)
20 | at [_ttyWrite] [as _ttyWrite] (node:internal/readline/interface:1124:18)
21 | at ReadStream.onkeypress (node:internal/readline/interface:273:20)
22 | at ReadStream.emit (node:events:513:28)
23 | at emitKeys (node:internal/readline/utils:357:14)
24 | at emitKeys.next ()
25 | at ReadStream.onData (node:internal/readline/emitKeypressEvents:64:36)
26 | at ReadStream.emit (node:events:513:28)
27 | at addChunk (node:internal/streams/readable:324:12)
28 |
29 | npm manifest:
30 | {
31 | "name": "solid-rewind",
32 | "version": "0.0.41",
33 | "description": "SolidJS time-travel debugger",
34 | "main": "src/Rewind.jsx",
35 | "scripts": {
36 | "start": "vite",
37 | "dev": "vite",
38 | "build": "vite build",
39 | "serve": "vite preview"
40 | },
41 | "keywords": [],
42 | "author": "Moonwalk Team",
43 | "license": "MIT",
44 | "repository": {
45 | "type": "git",
46 | "url": "https://github.com/oslabs-beta/solid-rewind.git"
47 | },
48 | "bugs": {
49 | "url": "https://github.com/oslabs-beta/solid-rewind.git"
50 | },
51 | "homepage": "https://github.com/oslabs-beta/solid-rewind#readme",
52 | "dependencies": {
53 | "@solid-devtools/logger": "^0.5.3",
54 | "solid-devtools": "^0.24.0",
55 | "solid-js": "^1.6.4",
56 | "typescript": "^4.9.4"
57 | }
58 | }
59 |
60 | yarn manifest:
61 | No manifest
62 |
63 | Lockfile:
64 | No lockfile
65 |
--------------------------------------------------------------------------------
/rewind-sample-app/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/rewind-sample-app/README.md:
--------------------------------------------------------------------------------
1 | ## Usage
2 |
3 | Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
4 |
5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
6 |
7 | ```bash
8 | $ npm install # or pnpm install or yarn install
9 | ```
10 |
11 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
12 |
13 | ## Available Scripts
14 |
15 | In the project directory, you can run:
16 |
17 | ### `npm dev` or `npm start`
18 |
19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
21 |
22 | The page will reload if you make edits.
23 |
24 | ### `npm run build`
25 |
26 | Builds the app for production to the `dist` folder.
27 | It correctly bundles Solid in production mode and optimizes the build for the best performance.
28 |
29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed!
31 |
32 | ## Deployment
33 |
34 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
35 |
--------------------------------------------------------------------------------
/rewind-sample-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Solid App
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/rewind-sample-app/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | "moduleResolution": "node",
7 | "allowSyntheticDefaultImports": true,
8 | "esModuleInterop": true,
9 | "jsx": "preserve",
10 | "jsxImportSource": "solid-js",
11 | "types": ["vite/client"],
12 | "noEmit": true,
13 | "isolatedModules": true
14 | }
15 | }
--------------------------------------------------------------------------------
/rewind-sample-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-template-solid",
3 | "version": "0.0.0",
4 | "description": "",
5 | "scripts": {
6 | "start": "vite",
7 | "dev": "vite",
8 | "build": "vite build",
9 | "serve": "vite preview"
10 | },
11 | "license": "MIT",
12 | "devDependencies": {
13 | "vite": "^3.0.9",
14 | "vite-plugin-solid": "^2.3.0"
15 | },
16 | "dependencies": {
17 | "@solid-devtools/logger": "^0.5.6",
18 | "d3": "^7.8.0",
19 | "solid-devtools": "^0.24.0",
20 | "solid-js": "^1.5.1",
21 | "solid-rewind": "^0.0.502",
22 | "typescript": "^4.9.4"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/rewind-sample-app/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: 5.4
2 |
3 | specifiers:
4 | solid-js: ^1.5.1
5 | vite: ^3.0.9
6 | vite-plugin-solid: ^2.3.0
7 |
8 | dependencies:
9 | solid-js: 1.5.1
10 |
11 | devDependencies:
12 | vite: 3.0.9
13 | vite-plugin-solid: 2.3.0_solid-js@1.5.1+vite@3.0.9
14 |
15 | packages:
16 |
17 | /@ampproject/remapping/2.2.0:
18 | resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
19 | engines: {node: '>=6.0.0'}
20 | dependencies:
21 | '@jridgewell/gen-mapping': 0.1.1
22 | '@jridgewell/trace-mapping': 0.3.14
23 | dev: true
24 |
25 | /@babel/code-frame/7.18.6:
26 | resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
27 | engines: {node: '>=6.9.0'}
28 | dependencies:
29 | '@babel/highlight': 7.18.6
30 | dev: true
31 |
32 | /@babel/compat-data/7.18.8:
33 | resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==}
34 | engines: {node: '>=6.9.0'}
35 | dev: true
36 |
37 | /@babel/core/7.18.6:
38 | resolution: {integrity: sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==}
39 | engines: {node: '>=6.9.0'}
40 | dependencies:
41 | '@ampproject/remapping': 2.2.0
42 | '@babel/code-frame': 7.18.6
43 | '@babel/generator': 7.18.7
44 | '@babel/helper-compilation-targets': 7.18.6_@babel+core@7.18.6
45 | '@babel/helper-module-transforms': 7.18.8
46 | '@babel/helpers': 7.18.6
47 | '@babel/parser': 7.18.8
48 | '@babel/template': 7.18.6
49 | '@babel/traverse': 7.18.8
50 | '@babel/types': 7.18.8
51 | convert-source-map: 1.8.0
52 | debug: 4.3.4
53 | gensync: 1.0.0-beta.2
54 | json5: 2.2.1
55 | semver: 6.3.0
56 | transitivePeerDependencies:
57 | - supports-color
58 | dev: true
59 |
60 | /@babel/generator/7.18.7:
61 | resolution: {integrity: sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A==}
62 | engines: {node: '>=6.9.0'}
63 | dependencies:
64 | '@babel/types': 7.18.8
65 | '@jridgewell/gen-mapping': 0.3.2
66 | jsesc: 2.5.2
67 | dev: true
68 |
69 | /@babel/helper-annotate-as-pure/7.18.6:
70 | resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
71 | engines: {node: '>=6.9.0'}
72 | dependencies:
73 | '@babel/types': 7.18.8
74 | dev: true
75 |
76 | /@babel/helper-compilation-targets/7.18.6_@babel+core@7.18.6:
77 | resolution: {integrity: sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg==}
78 | engines: {node: '>=6.9.0'}
79 | peerDependencies:
80 | '@babel/core': ^7.0.0
81 | dependencies:
82 | '@babel/compat-data': 7.18.8
83 | '@babel/core': 7.18.6
84 | '@babel/helper-validator-option': 7.18.6
85 | browserslist: 4.21.2
86 | semver: 6.3.0
87 | dev: true
88 |
89 | /@babel/helper-create-class-features-plugin/7.18.6_@babel+core@7.18.6:
90 | resolution: {integrity: sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw==}
91 | engines: {node: '>=6.9.0'}
92 | peerDependencies:
93 | '@babel/core': ^7.0.0
94 | dependencies:
95 | '@babel/core': 7.18.6
96 | '@babel/helper-annotate-as-pure': 7.18.6
97 | '@babel/helper-environment-visitor': 7.18.6
98 | '@babel/helper-function-name': 7.18.6
99 | '@babel/helper-member-expression-to-functions': 7.18.6
100 | '@babel/helper-optimise-call-expression': 7.18.6
101 | '@babel/helper-replace-supers': 7.18.6
102 | '@babel/helper-split-export-declaration': 7.18.6
103 | transitivePeerDependencies:
104 | - supports-color
105 | dev: true
106 |
107 | /@babel/helper-environment-visitor/7.18.6:
108 | resolution: {integrity: sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q==}
109 | engines: {node: '>=6.9.0'}
110 | dev: true
111 |
112 | /@babel/helper-function-name/7.18.6:
113 | resolution: {integrity: sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw==}
114 | engines: {node: '>=6.9.0'}
115 | dependencies:
116 | '@babel/template': 7.18.6
117 | '@babel/types': 7.18.8
118 | dev: true
119 |
120 | /@babel/helper-hoist-variables/7.18.6:
121 | resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
122 | engines: {node: '>=6.9.0'}
123 | dependencies:
124 | '@babel/types': 7.18.8
125 | dev: true
126 |
127 | /@babel/helper-member-expression-to-functions/7.18.6:
128 | resolution: {integrity: sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng==}
129 | engines: {node: '>=6.9.0'}
130 | dependencies:
131 | '@babel/types': 7.18.8
132 | dev: true
133 |
134 | /@babel/helper-module-imports/7.16.0:
135 | resolution: {integrity: sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==}
136 | engines: {node: '>=6.9.0'}
137 | dependencies:
138 | '@babel/types': 7.18.8
139 | dev: true
140 |
141 | /@babel/helper-module-imports/7.18.6:
142 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
143 | engines: {node: '>=6.9.0'}
144 | dependencies:
145 | '@babel/types': 7.18.8
146 | dev: true
147 |
148 | /@babel/helper-module-transforms/7.18.8:
149 | resolution: {integrity: sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA==}
150 | engines: {node: '>=6.9.0'}
151 | dependencies:
152 | '@babel/helper-environment-visitor': 7.18.6
153 | '@babel/helper-module-imports': 7.18.6
154 | '@babel/helper-simple-access': 7.18.6
155 | '@babel/helper-split-export-declaration': 7.18.6
156 | '@babel/helper-validator-identifier': 7.18.6
157 | '@babel/template': 7.18.6
158 | '@babel/traverse': 7.18.8
159 | '@babel/types': 7.18.8
160 | transitivePeerDependencies:
161 | - supports-color
162 | dev: true
163 |
164 | /@babel/helper-optimise-call-expression/7.18.6:
165 | resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
166 | engines: {node: '>=6.9.0'}
167 | dependencies:
168 | '@babel/types': 7.18.8
169 | dev: true
170 |
171 | /@babel/helper-plugin-utils/7.18.6:
172 | resolution: {integrity: sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg==}
173 | engines: {node: '>=6.9.0'}
174 | dev: true
175 |
176 | /@babel/helper-replace-supers/7.18.6:
177 | resolution: {integrity: sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g==}
178 | engines: {node: '>=6.9.0'}
179 | dependencies:
180 | '@babel/helper-environment-visitor': 7.18.6
181 | '@babel/helper-member-expression-to-functions': 7.18.6
182 | '@babel/helper-optimise-call-expression': 7.18.6
183 | '@babel/traverse': 7.18.8
184 | '@babel/types': 7.18.8
185 | transitivePeerDependencies:
186 | - supports-color
187 | dev: true
188 |
189 | /@babel/helper-simple-access/7.18.6:
190 | resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==}
191 | engines: {node: '>=6.9.0'}
192 | dependencies:
193 | '@babel/types': 7.18.8
194 | dev: true
195 |
196 | /@babel/helper-split-export-declaration/7.18.6:
197 | resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
198 | engines: {node: '>=6.9.0'}
199 | dependencies:
200 | '@babel/types': 7.18.8
201 | dev: true
202 |
203 | /@babel/helper-validator-identifier/7.18.6:
204 | resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==}
205 | engines: {node: '>=6.9.0'}
206 | dev: true
207 |
208 | /@babel/helper-validator-option/7.18.6:
209 | resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==}
210 | engines: {node: '>=6.9.0'}
211 | dev: true
212 |
213 | /@babel/helpers/7.18.6:
214 | resolution: {integrity: sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ==}
215 | engines: {node: '>=6.9.0'}
216 | dependencies:
217 | '@babel/template': 7.18.6
218 | '@babel/traverse': 7.18.8
219 | '@babel/types': 7.18.8
220 | transitivePeerDependencies:
221 | - supports-color
222 | dev: true
223 |
224 | /@babel/highlight/7.18.6:
225 | resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
226 | engines: {node: '>=6.9.0'}
227 | dependencies:
228 | '@babel/helper-validator-identifier': 7.18.6
229 | chalk: 2.4.2
230 | js-tokens: 4.0.0
231 | dev: true
232 |
233 | /@babel/parser/7.18.8:
234 | resolution: {integrity: sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA==}
235 | engines: {node: '>=6.0.0'}
236 | hasBin: true
237 | dependencies:
238 | '@babel/types': 7.18.8
239 | dev: true
240 |
241 | /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.18.6:
242 | resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==}
243 | engines: {node: '>=6.9.0'}
244 | peerDependencies:
245 | '@babel/core': ^7.0.0-0
246 | dependencies:
247 | '@babel/core': 7.18.6
248 | '@babel/helper-plugin-utils': 7.18.6
249 | dev: true
250 |
251 | /@babel/plugin-syntax-typescript/7.18.6_@babel+core@7.18.6:
252 | resolution: {integrity: sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==}
253 | engines: {node: '>=6.9.0'}
254 | peerDependencies:
255 | '@babel/core': ^7.0.0-0
256 | dependencies:
257 | '@babel/core': 7.18.6
258 | '@babel/helper-plugin-utils': 7.18.6
259 | dev: true
260 |
261 | /@babel/plugin-transform-typescript/7.18.8_@babel+core@7.18.6:
262 | resolution: {integrity: sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==}
263 | engines: {node: '>=6.9.0'}
264 | peerDependencies:
265 | '@babel/core': ^7.0.0-0
266 | dependencies:
267 | '@babel/core': 7.18.6
268 | '@babel/helper-create-class-features-plugin': 7.18.6_@babel+core@7.18.6
269 | '@babel/helper-plugin-utils': 7.18.6
270 | '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.18.6
271 | transitivePeerDependencies:
272 | - supports-color
273 | dev: true
274 |
275 | /@babel/preset-typescript/7.18.6_@babel+core@7.18.6:
276 | resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==}
277 | engines: {node: '>=6.9.0'}
278 | peerDependencies:
279 | '@babel/core': ^7.0.0-0
280 | dependencies:
281 | '@babel/core': 7.18.6
282 | '@babel/helper-plugin-utils': 7.18.6
283 | '@babel/helper-validator-option': 7.18.6
284 | '@babel/plugin-transform-typescript': 7.18.8_@babel+core@7.18.6
285 | transitivePeerDependencies:
286 | - supports-color
287 | dev: true
288 |
289 | /@babel/template/7.18.6:
290 | resolution: {integrity: sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==}
291 | engines: {node: '>=6.9.0'}
292 | dependencies:
293 | '@babel/code-frame': 7.18.6
294 | '@babel/parser': 7.18.8
295 | '@babel/types': 7.18.8
296 | dev: true
297 |
298 | /@babel/traverse/7.18.8:
299 | resolution: {integrity: sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg==}
300 | engines: {node: '>=6.9.0'}
301 | dependencies:
302 | '@babel/code-frame': 7.18.6
303 | '@babel/generator': 7.18.7
304 | '@babel/helper-environment-visitor': 7.18.6
305 | '@babel/helper-function-name': 7.18.6
306 | '@babel/helper-hoist-variables': 7.18.6
307 | '@babel/helper-split-export-declaration': 7.18.6
308 | '@babel/parser': 7.18.8
309 | '@babel/types': 7.18.8
310 | debug: 4.3.4
311 | globals: 11.12.0
312 | transitivePeerDependencies:
313 | - supports-color
314 | dev: true
315 |
316 | /@babel/types/7.18.8:
317 | resolution: {integrity: sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw==}
318 | engines: {node: '>=6.9.0'}
319 | dependencies:
320 | '@babel/helper-validator-identifier': 7.18.6
321 | to-fast-properties: 2.0.0
322 | dev: true
323 |
324 | /@esbuild/linux-loong64/0.14.54:
325 | resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
326 | engines: {node: '>=12'}
327 | cpu: [loong64]
328 | os: [linux]
329 | requiresBuild: true
330 | dev: true
331 | optional: true
332 |
333 | /@jridgewell/gen-mapping/0.1.1:
334 | resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
335 | engines: {node: '>=6.0.0'}
336 | dependencies:
337 | '@jridgewell/set-array': 1.1.2
338 | '@jridgewell/sourcemap-codec': 1.4.14
339 | dev: true
340 |
341 | /@jridgewell/gen-mapping/0.3.2:
342 | resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
343 | engines: {node: '>=6.0.0'}
344 | dependencies:
345 | '@jridgewell/set-array': 1.1.2
346 | '@jridgewell/sourcemap-codec': 1.4.14
347 | '@jridgewell/trace-mapping': 0.3.14
348 | dev: true
349 |
350 | /@jridgewell/resolve-uri/3.1.0:
351 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
352 | engines: {node: '>=6.0.0'}
353 | dev: true
354 |
355 | /@jridgewell/set-array/1.1.2:
356 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
357 | engines: {node: '>=6.0.0'}
358 | dev: true
359 |
360 | /@jridgewell/sourcemap-codec/1.4.14:
361 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
362 | dev: true
363 |
364 | /@jridgewell/trace-mapping/0.3.14:
365 | resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==}
366 | dependencies:
367 | '@jridgewell/resolve-uri': 3.1.0
368 | '@jridgewell/sourcemap-codec': 1.4.14
369 | dev: true
370 |
371 | /ansi-styles/3.2.1:
372 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
373 | engines: {node: '>=4'}
374 | dependencies:
375 | color-convert: 1.9.3
376 | dev: true
377 |
378 | /babel-plugin-jsx-dom-expressions/0.33.12_@babel+core@7.18.6:
379 | resolution: {integrity: sha512-FQeNcBvC+PrPYGpeUztI7AiiAqJL2H8e7mL4L6qHZ7B4wZfbgyREsHZwKmmDqxAehlyAUolTdhDNk9xfyHdIZw==}
380 | peerDependencies:
381 | '@babel/core': ^7.0.0
382 | dependencies:
383 | '@babel/core': 7.18.6
384 | '@babel/helper-module-imports': 7.16.0
385 | '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.18.6
386 | '@babel/types': 7.18.8
387 | html-entities: 2.3.2
388 | dev: true
389 |
390 | /babel-preset-solid/1.4.6_@babel+core@7.18.6:
391 | resolution: {integrity: sha512-5n+nm1zgj7BK9cv0kYu0p+kbsXgGbrxLmA5bv5WT0V5WnqRgshWILInPWLJNZbvP5gBj+huDKwk3J4RhhbFlhA==}
392 | peerDependencies:
393 | '@babel/core': ^7.0.0
394 | dependencies:
395 | '@babel/core': 7.18.6
396 | babel-plugin-jsx-dom-expressions: 0.33.12_@babel+core@7.18.6
397 | dev: true
398 |
399 | /browserslist/4.21.2:
400 | resolution: {integrity: sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==}
401 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
402 | hasBin: true
403 | dependencies:
404 | caniuse-lite: 1.0.30001366
405 | electron-to-chromium: 1.4.189
406 | node-releases: 2.0.6
407 | update-browserslist-db: 1.0.4_browserslist@4.21.2
408 | dev: true
409 |
410 | /caniuse-lite/1.0.30001366:
411 | resolution: {integrity: sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA==}
412 | dev: true
413 |
414 | /chalk/2.4.2:
415 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
416 | engines: {node: '>=4'}
417 | dependencies:
418 | ansi-styles: 3.2.1
419 | escape-string-regexp: 1.0.5
420 | supports-color: 5.5.0
421 | dev: true
422 |
423 | /color-convert/1.9.3:
424 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
425 | dependencies:
426 | color-name: 1.1.3
427 | dev: true
428 |
429 | /color-name/1.1.3:
430 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
431 | dev: true
432 |
433 | /convert-source-map/1.8.0:
434 | resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
435 | dependencies:
436 | safe-buffer: 5.1.2
437 | dev: true
438 |
439 | /csstype/3.1.0:
440 | resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==}
441 |
442 | /debug/4.3.4:
443 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
444 | engines: {node: '>=6.0'}
445 | peerDependencies:
446 | supports-color: '*'
447 | peerDependenciesMeta:
448 | supports-color:
449 | optional: true
450 | dependencies:
451 | ms: 2.1.2
452 | dev: true
453 |
454 | /electron-to-chromium/1.4.189:
455 | resolution: {integrity: sha512-dQ6Zn4ll2NofGtxPXaDfY2laIa6NyCQdqXYHdwH90GJQW0LpJJib0ZU/ERtbb0XkBEmUD2eJtagbOie3pdMiPg==}
456 | dev: true
457 |
458 | /esbuild-android-64/0.14.54:
459 | resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==}
460 | engines: {node: '>=12'}
461 | cpu: [x64]
462 | os: [android]
463 | requiresBuild: true
464 | dev: true
465 | optional: true
466 |
467 | /esbuild-android-arm64/0.14.54:
468 | resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==}
469 | engines: {node: '>=12'}
470 | cpu: [arm64]
471 | os: [android]
472 | requiresBuild: true
473 | dev: true
474 | optional: true
475 |
476 | /esbuild-darwin-64/0.14.54:
477 | resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==}
478 | engines: {node: '>=12'}
479 | cpu: [x64]
480 | os: [darwin]
481 | requiresBuild: true
482 | dev: true
483 | optional: true
484 |
485 | /esbuild-darwin-arm64/0.14.54:
486 | resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==}
487 | engines: {node: '>=12'}
488 | cpu: [arm64]
489 | os: [darwin]
490 | requiresBuild: true
491 | dev: true
492 | optional: true
493 |
494 | /esbuild-freebsd-64/0.14.54:
495 | resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==}
496 | engines: {node: '>=12'}
497 | cpu: [x64]
498 | os: [freebsd]
499 | requiresBuild: true
500 | dev: true
501 | optional: true
502 |
503 | /esbuild-freebsd-arm64/0.14.54:
504 | resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==}
505 | engines: {node: '>=12'}
506 | cpu: [arm64]
507 | os: [freebsd]
508 | requiresBuild: true
509 | dev: true
510 | optional: true
511 |
512 | /esbuild-linux-32/0.14.54:
513 | resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==}
514 | engines: {node: '>=12'}
515 | cpu: [ia32]
516 | os: [linux]
517 | requiresBuild: true
518 | dev: true
519 | optional: true
520 |
521 | /esbuild-linux-64/0.14.54:
522 | resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==}
523 | engines: {node: '>=12'}
524 | cpu: [x64]
525 | os: [linux]
526 | requiresBuild: true
527 | dev: true
528 | optional: true
529 |
530 | /esbuild-linux-arm/0.14.54:
531 | resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==}
532 | engines: {node: '>=12'}
533 | cpu: [arm]
534 | os: [linux]
535 | requiresBuild: true
536 | dev: true
537 | optional: true
538 |
539 | /esbuild-linux-arm64/0.14.54:
540 | resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==}
541 | engines: {node: '>=12'}
542 | cpu: [arm64]
543 | os: [linux]
544 | requiresBuild: true
545 | dev: true
546 | optional: true
547 |
548 | /esbuild-linux-mips64le/0.14.54:
549 | resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==}
550 | engines: {node: '>=12'}
551 | cpu: [mips64el]
552 | os: [linux]
553 | requiresBuild: true
554 | dev: true
555 | optional: true
556 |
557 | /esbuild-linux-ppc64le/0.14.54:
558 | resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==}
559 | engines: {node: '>=12'}
560 | cpu: [ppc64]
561 | os: [linux]
562 | requiresBuild: true
563 | dev: true
564 | optional: true
565 |
566 | /esbuild-linux-riscv64/0.14.54:
567 | resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==}
568 | engines: {node: '>=12'}
569 | cpu: [riscv64]
570 | os: [linux]
571 | requiresBuild: true
572 | dev: true
573 | optional: true
574 |
575 | /esbuild-linux-s390x/0.14.54:
576 | resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==}
577 | engines: {node: '>=12'}
578 | cpu: [s390x]
579 | os: [linux]
580 | requiresBuild: true
581 | dev: true
582 | optional: true
583 |
584 | /esbuild-netbsd-64/0.14.54:
585 | resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==}
586 | engines: {node: '>=12'}
587 | cpu: [x64]
588 | os: [netbsd]
589 | requiresBuild: true
590 | dev: true
591 | optional: true
592 |
593 | /esbuild-openbsd-64/0.14.54:
594 | resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==}
595 | engines: {node: '>=12'}
596 | cpu: [x64]
597 | os: [openbsd]
598 | requiresBuild: true
599 | dev: true
600 | optional: true
601 |
602 | /esbuild-sunos-64/0.14.54:
603 | resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==}
604 | engines: {node: '>=12'}
605 | cpu: [x64]
606 | os: [sunos]
607 | requiresBuild: true
608 | dev: true
609 | optional: true
610 |
611 | /esbuild-windows-32/0.14.54:
612 | resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==}
613 | engines: {node: '>=12'}
614 | cpu: [ia32]
615 | os: [win32]
616 | requiresBuild: true
617 | dev: true
618 | optional: true
619 |
620 | /esbuild-windows-64/0.14.54:
621 | resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==}
622 | engines: {node: '>=12'}
623 | cpu: [x64]
624 | os: [win32]
625 | requiresBuild: true
626 | dev: true
627 | optional: true
628 |
629 | /esbuild-windows-arm64/0.14.54:
630 | resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==}
631 | engines: {node: '>=12'}
632 | cpu: [arm64]
633 | os: [win32]
634 | requiresBuild: true
635 | dev: true
636 | optional: true
637 |
638 | /esbuild/0.14.54:
639 | resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==}
640 | engines: {node: '>=12'}
641 | hasBin: true
642 | requiresBuild: true
643 | optionalDependencies:
644 | '@esbuild/linux-loong64': 0.14.54
645 | esbuild-android-64: 0.14.54
646 | esbuild-android-arm64: 0.14.54
647 | esbuild-darwin-64: 0.14.54
648 | esbuild-darwin-arm64: 0.14.54
649 | esbuild-freebsd-64: 0.14.54
650 | esbuild-freebsd-arm64: 0.14.54
651 | esbuild-linux-32: 0.14.54
652 | esbuild-linux-64: 0.14.54
653 | esbuild-linux-arm: 0.14.54
654 | esbuild-linux-arm64: 0.14.54
655 | esbuild-linux-mips64le: 0.14.54
656 | esbuild-linux-ppc64le: 0.14.54
657 | esbuild-linux-riscv64: 0.14.54
658 | esbuild-linux-s390x: 0.14.54
659 | esbuild-netbsd-64: 0.14.54
660 | esbuild-openbsd-64: 0.14.54
661 | esbuild-sunos-64: 0.14.54
662 | esbuild-windows-32: 0.14.54
663 | esbuild-windows-64: 0.14.54
664 | esbuild-windows-arm64: 0.14.54
665 | dev: true
666 |
667 | /escalade/3.1.1:
668 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
669 | engines: {node: '>=6'}
670 | dev: true
671 |
672 | /escape-string-regexp/1.0.5:
673 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
674 | engines: {node: '>=0.8.0'}
675 | dev: true
676 |
677 | /fsevents/2.3.2:
678 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
679 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
680 | os: [darwin]
681 | requiresBuild: true
682 | dev: true
683 | optional: true
684 |
685 | /function-bind/1.1.1:
686 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
687 | dev: true
688 |
689 | /gensync/1.0.0-beta.2:
690 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
691 | engines: {node: '>=6.9.0'}
692 | dev: true
693 |
694 | /globals/11.12.0:
695 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
696 | engines: {node: '>=4'}
697 | dev: true
698 |
699 | /has-flag/3.0.0:
700 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
701 | engines: {node: '>=4'}
702 | dev: true
703 |
704 | /has/1.0.3:
705 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
706 | engines: {node: '>= 0.4.0'}
707 | dependencies:
708 | function-bind: 1.1.1
709 | dev: true
710 |
711 | /html-entities/2.3.2:
712 | resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==}
713 | dev: true
714 |
715 | /is-core-module/2.10.0:
716 | resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
717 | dependencies:
718 | has: 1.0.3
719 | dev: true
720 |
721 | /is-what/4.1.7:
722 | resolution: {integrity: sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==}
723 | engines: {node: '>=12.13'}
724 | dev: true
725 |
726 | /js-tokens/4.0.0:
727 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
728 | dev: true
729 |
730 | /jsesc/2.5.2:
731 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
732 | engines: {node: '>=4'}
733 | hasBin: true
734 | dev: true
735 |
736 | /json5/2.2.1:
737 | resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
738 | engines: {node: '>=6'}
739 | hasBin: true
740 | dev: true
741 |
742 | /merge-anything/5.0.2:
743 | resolution: {integrity: sha512-POPQBWkBC0vxdgzRJ2Mkj4+2NTKbvkHo93ih+jGDhNMLzIw+rYKjO7949hOQM2X7DxMHH1uoUkwWFLIzImw7gA==}
744 | engines: {node: '>=12.13'}
745 | dependencies:
746 | is-what: 4.1.7
747 | ts-toolbelt: 9.6.0
748 | dev: true
749 |
750 | /ms/2.1.2:
751 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
752 | dev: true
753 |
754 | /nanoid/3.3.4:
755 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
756 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
757 | hasBin: true
758 | dev: true
759 |
760 | /node-releases/2.0.6:
761 | resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
762 | dev: true
763 |
764 | /path-parse/1.0.7:
765 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
766 | dev: true
767 |
768 | /picocolors/1.0.0:
769 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
770 | dev: true
771 |
772 | /postcss/8.4.16:
773 | resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==}
774 | engines: {node: ^10 || ^12 || >=14}
775 | dependencies:
776 | nanoid: 3.3.4
777 | picocolors: 1.0.0
778 | source-map-js: 1.0.2
779 | dev: true
780 |
781 | /resolve/1.22.1:
782 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
783 | hasBin: true
784 | dependencies:
785 | is-core-module: 2.10.0
786 | path-parse: 1.0.7
787 | supports-preserve-symlinks-flag: 1.0.0
788 | dev: true
789 |
790 | /rollup/2.77.3:
791 | resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
792 | engines: {node: '>=10.0.0'}
793 | hasBin: true
794 | optionalDependencies:
795 | fsevents: 2.3.2
796 | dev: true
797 |
798 | /safe-buffer/5.1.2:
799 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
800 | dev: true
801 |
802 | /semver/6.3.0:
803 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
804 | hasBin: true
805 | dev: true
806 |
807 | /solid-js/1.5.1:
808 | resolution: {integrity: sha512-Y6aKystIxnrB0quV5nhqNuJV+l2Fk3/PQy1mMya/bzxlGiMHAym7v1NaqEgqDIvctbkxOi5dBj0ER/ewrH060g==}
809 | dependencies:
810 | csstype: 3.1.0
811 |
812 | /solid-refresh/0.4.1_solid-js@1.5.1:
813 | resolution: {integrity: sha512-v3tD/OXQcUyXLrWjPW1dXZyeWwP7/+GQNs8YTL09GBq+5FguA6IejJWUvJDrLIA4M0ho9/5zK2e9n+uy+4488g==}
814 | peerDependencies:
815 | solid-js: ^1.3
816 | dependencies:
817 | '@babel/generator': 7.18.7
818 | '@babel/helper-module-imports': 7.18.6
819 | '@babel/types': 7.18.8
820 | solid-js: 1.5.1
821 | dev: true
822 |
823 | /source-map-js/1.0.2:
824 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
825 | engines: {node: '>=0.10.0'}
826 | dev: true
827 |
828 | /supports-color/5.5.0:
829 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
830 | engines: {node: '>=4'}
831 | dependencies:
832 | has-flag: 3.0.0
833 | dev: true
834 |
835 | /supports-preserve-symlinks-flag/1.0.0:
836 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
837 | engines: {node: '>= 0.4'}
838 | dev: true
839 |
840 | /to-fast-properties/2.0.0:
841 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
842 | engines: {node: '>=4'}
843 | dev: true
844 |
845 | /ts-toolbelt/9.6.0:
846 | resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==}
847 | dev: true
848 |
849 | /update-browserslist-db/1.0.4_browserslist@4.21.2:
850 | resolution: {integrity: sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==}
851 | hasBin: true
852 | peerDependencies:
853 | browserslist: '>= 4.21.0'
854 | dependencies:
855 | browserslist: 4.21.2
856 | escalade: 3.1.1
857 | picocolors: 1.0.0
858 | dev: true
859 |
860 | /vite-plugin-solid/2.3.0_solid-js@1.5.1+vite@3.0.9:
861 | resolution: {integrity: sha512-N2sa54C3UZC2nN5vpj5o6YP+XdIAZW6n6xv8OasxNAcAJPFeZT7EOVvumL0V4c8hBz1yuYniMWdESY8807fVSg==}
862 | peerDependencies:
863 | solid-js: ^1.3.17
864 | vite: ^3.0.0
865 | dependencies:
866 | '@babel/core': 7.18.6
867 | '@babel/preset-typescript': 7.18.6_@babel+core@7.18.6
868 | babel-preset-solid: 1.4.6_@babel+core@7.18.6
869 | merge-anything: 5.0.2
870 | solid-js: 1.5.1
871 | solid-refresh: 0.4.1_solid-js@1.5.1
872 | vite: 3.0.9
873 | transitivePeerDependencies:
874 | - supports-color
875 | dev: true
876 |
877 | /vite/3.0.9:
878 | resolution: {integrity: sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==}
879 | engines: {node: ^14.18.0 || >=16.0.0}
880 | hasBin: true
881 | peerDependencies:
882 | less: '*'
883 | sass: '*'
884 | stylus: '*'
885 | terser: ^5.4.0
886 | peerDependenciesMeta:
887 | less:
888 | optional: true
889 | sass:
890 | optional: true
891 | stylus:
892 | optional: true
893 | terser:
894 | optional: true
895 | dependencies:
896 | esbuild: 0.14.54
897 | postcss: 8.4.16
898 | resolve: 1.22.1
899 | rollup: 2.77.3
900 | optionalDependencies:
901 | fsevents: 2.3.2
902 | dev: true
903 |
--------------------------------------------------------------------------------
/rewind-sample-app/src/App.jsx:
--------------------------------------------------------------------------------
1 | import logo from './logo.svg';
2 | import styles from './App.module.css';
3 | import { createSignal, getOwner, createMemo} from 'solid-js';
4 | import Hello from './Hello';
5 | import TreeComp from './tree-component';
6 | import 'solid-devtools';
7 |
8 |
9 |
10 |
11 | const [first, setFirst] = createSignal("AboveApp");
12 |
13 | function App() {
14 |
15 |
16 |
17 |
18 | const rewindOwner = getOwner()
19 |
20 | const [count, setCount] = createSignal(1);
21 |
22 | const doubleCount = () => count() * 2;
23 |
24 | function fibonacci(num) {
25 | if (num <= 1) return 1;
26 |
27 | return fibonacci(num - 1) + fibonacci(num - 2);
28 | }
29 |
30 | const fib = createMemo(() => fibonacci(count()));
31 |
32 |
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Edit {first()} src/App.jsx and save to reload.
42 |