totally-tubular 0.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ # top-most EditorConfig file
2
+ root = true
3
+
4
+ # Unix-style newlines with a newline ending every file
5
+ [*]
6
+ indent_style = space
7
+ indent_size = 2
8
+ end_of_line = lf
9
+ charset = utf-8
10
+ insert_final_newline = true
11
+ max_line_length = 100
12
+ trim_trailing_whitespace = true
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ben Duran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # totally-tubular
2
+
3
+ A completely framework-agnostic, JavaScript / TypeScript state management and update broadcasting library wit zero dependencies, solid performance and a micro file size.
4
+
5
+ **Woahhhh**
6
+
7
+ ![Keanu in Point Break, bruh](./totally-tubular.jpg)
8
+
9
+ ## But why is there _another_ state management library? I already use XYZ library.
10
+
11
+ Great! I'm glad you found something that works for you!
12
+ This library (`totally-tubular`) was an experiment that manifested out of feeling
13
+ like state management is overcomplicated these days and often riddled with "gotchas"
14
+ and easy-to-oopsie performance faux pas.
15
+
16
+ Additionally, our shipped JavaScript bundles should be getting **smaller**, not **bigger**,
17
+ but it's all-too-easy, especially when crafting enterprise software, to keep shoving more libraries
18
+ into your bundle with questionable benefits, when there often exists a much simpler, closer-to-vanilla
19
+ way to do what you need to do.
20
+
21
+ As a final point, your state management system should be fully-portable, so if you swap out
22
+ your rendering layer, be it React, Svelte, Angular, or something else, the meat-and-potatoes of
23
+ your state management shouldn't need to be fully rewritten.
24
+
25
+ ## Install
26
+
27
+ ```
28
+ npm i totally-tubular --save
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ```typescript
34
+ import { Tubular } from "./src/totally-tubular.js";
35
+
36
+ interface MyStateShape {
37
+ isActive: boolean;
38
+ count: number;
39
+ meta: {
40
+ title: string;
41
+ };
42
+ name: string;
43
+ }
44
+
45
+ const initialState: MyStateShape = {
46
+ isActive: false,
47
+ count: 123,
48
+ meta: {
49
+ title: "Director",
50
+ },
51
+ name: "Test User",
52
+ };
53
+
54
+ const t = new Tubular(initialState);
55
+
56
+ // listen to any of the keys / values in your state object for changes
57
+ // and do whatever you need with the values
58
+ t.observe("meta.title", (newVal, oldVal, propPath) =>
59
+ console.info(propPath, "was updated from", oldVal, "to", newVal),
60
+ );
61
+
62
+ console.info(t.read("meta.title")); // will print 'Director'
63
+
64
+ // update any of the keys / values in your state object.
65
+ // all observers will be notified after the value has been set
66
+ t.update("meta.title", (prev) => `Supreme ${prev}`);
67
+ console.info(t.read("meta.title")); // will print the updated 'Supreme Director' value
68
+ ```
69
+
70
+ ### React example
71
+
72
+ Since React is one of the most popular UI libraries, here is how you might mimick the vanilla example, above, but in a React application.
73
+
74
+ **TODO**
75
+
76
+ ## Things you should know
77
+
78
+ - This library _does not_ support observing arbitrary indices in an array, or objects inside of an array.
79
+ This is intentional.
80
+ If you want to be notified of changes to an array or you want to update some item in an array, you should use the path to the array in your state object, then perform reads or transformations on that.
81
+ - This library **mutates your originally-provided `initialState`**.
82
+ This is for performance reasons, as mutating objects doesn't require cloning and iterating on all object properties, saving on computation time.
83
+ If you want to ensure immutability, you should make your calls to `.update()` return new objects, array, etc.
84
+
85
+ ## (Extra) Synthetic Performance Benchmarks
86
+
87
+ ### Machine specs
88
+
89
+ #### Apple Machine
90
+
91
+ CPU: M3 Max
92
+ RAM: 36GB
93
+ OS: MacOS Sequoia 15.5
94
+
95
+ ```
96
+ -----shallow state object-----
97
+
98
+ 1,000 updates to "name" string val with 10 observers: 0.0036749580000000038s
99
+ 1,000 updates to "name" string val with 100 observers: 0.0004760830000000027s
100
+ 1,000 updates to "name" string val with 1000 observers: 0.002774291000000005s
101
+ 10,000 updates to "name" string val with 10 observers: 0.0014766249999999986s
102
+ 10,000 updates to "name" string val with 100 observers: 0.0023700000000000045s
103
+ 10,000 updates to "name" string val with 1000 observers: 0.010886792s
104
+ 100,000 updates to "name" string val with 10 observers: 0.012480834000000001s
105
+ 100,000 updates to "name" string val with 100 observers: 0.022641292000000007s
106
+ 100,000 updates to "name" string val with 1000 observers: 0.102325125s
107
+ 1,000,000 updates to "name" string val with 10 observers: 0.16086666700000002s
108
+ 1,000,000 updates to "name" string val with 100 observers: 0.26716437499999995s
109
+ 1,000,000 updates to "name" string val with 1000 observers: 1.0726739170000001s
110
+
111
+ -----medium state object-----
112
+
113
+ 1,000 updates to "user.settings.theme" string val with 10 observers: 0.000344333000000006s
114
+ 1,000 updates to "user.settings.theme" string val with 100 observers: 0.00034458300000005695s
115
+ 1,000 updates to "user.settings.theme" string val with 1000 observers: 0.0011382089999999608s
116
+ 10,000 updates to "user.settings.theme" string val with 10 observers: 0.0012657919999999195s
117
+ 10,000 updates to "user.settings.theme" string val with 100 observers: 0.002091625000000022s
118
+ 10,000 updates to "user.settings.theme" string val with 1000 observers: 0.010777082999999948s
119
+ 100,000 updates to "user.settings.theme" string val with 10 observers: 0.009487583000000086s
120
+ 100,000 updates to "user.settings.theme" string val with 100 observers: 0.01902945799999998s
121
+ 100,000 updates to "user.settings.theme" string val with 1000 observers: 0.11656079100000011s
122
+ 1,000,000 updates to "user.settings.theme" string val with 10 observers: 0.14811841699999992s
123
+ 1,000,000 updates to "user.settings.theme" string val with 100 observers: 0.27773633299999984s
124
+ 1,000,000 updates to "user.settings.theme" string val with 1000 observers: 1.0240415829999998s
125
+
126
+ -----deep state object-----
127
+
128
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.0012417919999998049s
129
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.0003201250000001892s
130
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.0011683750000001964s
131
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.0014113330000000133s
132
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.002289249999999811s
133
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.01099350000000004s
134
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.014709209000000101s
135
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.024220374999999877s
136
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.10777845800000023s
137
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.22398191699999961s
138
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.2829787500000002s
139
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 1.1015419579999999s
140
+ ```
141
+
142
+ #### Windows Machine
143
+
144
+ CPU: Intel i9-13900HX
145
+ RAM: 32GB
146
+ OS: Windows 11
147
+
148
+ ```
149
+ -----shallow state object-----
150
+
151
+ 1,000 updates to "name" string val with 10 observers: 0.0046989000000000015s
152
+ 1,000 updates to "name" string val with 100 observers: 0.0009328000000000003s
153
+ 1,000 updates to "name" string val with 1000 observers: 0.0012964000000000055s
154
+ 10,000 updates to "name" string val with 10 observers: 0.002852000000000004s
155
+ 10,000 updates to "name" string val with 100 observers: 0.003517799999999994s
156
+ 10,000 updates to "name" string val with 1000 observers: 0.012490899999999996s
157
+ 100,000 updates to "name" string val with 10 observers: 0.0199987s
158
+ 100,000 updates to "name" string val with 100 observers: 0.03123379999999999s
159
+ 100,000 updates to "name" string val with 1000 observers: 0.1196829s
160
+ 1,000,000 updates to "name" string val with 10 observers: 0.2171915s
161
+ 1,000,000 updates to "name" string val with 100 observers: 0.24709929999999997s
162
+ 1,000,000 updates to "name" string val with 1000 observers: 1.1660891999999998s
163
+
164
+ -----medium state object-----
165
+
166
+ 1,000 updates to "user.settings.theme" string val with 10 observers: 0.0007828999999999269s
167
+ 1,000 updates to "user.settings.theme" string val with 100 observers: 0.0004992999999999483s
168
+ 1,000 updates to "user.settings.theme" string val with 1000 observers: 0.0013275000000001s
169
+ 10,000 updates to "user.settings.theme" string val with 10 observers: 0.0032917999999999667s
170
+ 10,000 updates to "user.settings.theme" string val with 100 observers: 0.004183099999999967s
171
+ 10,000 updates to "user.settings.theme" string val with 1000 observers: 0.012704099999999927s
172
+ 100,000 updates to "user.settings.theme" string val with 10 observers: 0.023384700000000067s
173
+ 100,000 updates to "user.settings.theme" string val with 100 observers: 0.03393959999999993s
174
+ 100,000 updates to "user.settings.theme" string val with 1000 observers: 0.1242770999999998s
175
+ 1,000,000 updates to "user.settings.theme" string val with 10 observers: 0.23742110000000002s
176
+ 1,000,000 updates to "user.settings.theme" string val with 100 observers: 0.26894860000000015s
177
+ 1,000,000 updates to "user.settings.theme" string val with 1000 observers: 1.2531396s
178
+
179
+ -----deep state object-----
180
+
181
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.0003414999999999964s
182
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.00033490000000028884s
183
+ 1,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.0016161000000001877s
184
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.0022989000000002307s
185
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.0037910000000001675s
186
+ 10,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.014288300000000163s
187
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.025205199999999876s
188
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.03601400000000012s
189
+ 100,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 0.13781609999999955s
190
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 10 observers: 0.27891269999999985s
191
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 100 observers: 0.36865729999999985s
192
+ 1,000,000 updates to "zdeepSettings.level1.level2.level3.level4.level5.level6.currentValue" string val with 1000 observers: 1.3965289000000003s
193
+ ```