totally-tubular 0.3.0 → 0.3.2

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.
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "Tubular", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return _totallytubular.Tubular;
9
+ }
10
+ });
11
+ const _totallytubular = require("./totally-tubular.cjs");
@@ -0,0 +1 @@
1
+ export { Tubular } from './totally-tubular.js';
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "useTubular", {
8
8
  return useTubular;
9
9
  }
10
10
  });
11
- const _react = require("react");
11
+ const _react = require("./react.cjs");
12
12
  function useTubular(tubular, key) {
13
13
  /** state */ const [val, setVal] = (0, _react.useState)(()=>tubular.read(key));
14
14
  /** callbacks */ const handleUpdateVal = (0, _react.useCallback)((newValOrCallback)=>{
@@ -0,0 +1 @@
1
+ export { Tubular } from './totally-tubular.js';
@@ -0,0 +1 @@
1
+ export { Tubular } from './totally-tubular.mjs';
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useState } from 'react';
1
+ import { useCallback, useEffect, useState } from './react.mjs';
2
2
  /**
3
3
  * A React hook that binds to a {@link Tubular} instance and a specific key path
4
4
  * within its state.
package/package.json CHANGED
@@ -3,10 +3,10 @@
3
3
  "description": "A completely framework-agnostic, JavaScript / TypeScript state management and update broadcasting library wit zero dependencies, solid performance and a micro file size.",
4
4
  "name": "totally-tubular",
5
5
  "type": "module",
6
- "main": "./dist/totally-tubular.js",
7
- "types": "./dist/totally-tubular.d.ts",
6
+ "main": "./dist/cjs/index.cjs",
7
+ "types": "./dist/cjs/index.d.ts",
8
8
  "typings": "./dist/totally-tubular.d.ts",
9
- "version": "0.3.0",
9
+ "version": "0.3.2",
10
10
  "keywords": [
11
11
  "state",
12
12
  "management",
@@ -54,24 +54,24 @@
54
54
  "react": ">=18"
55
55
  },
56
56
  "exports": {
57
- "./totally-tubular-react": {
57
+ ".": {
58
58
  "require": {
59
- "types": "./dist/cjs/totally-tubular-react.d.ts",
60
- "default": "./dist/cjs/totally-tubular-react.cjs"
59
+ "types": "./dist/cjs/index.d.ts",
60
+ "default": "./dist/cjs/index.cjs"
61
61
  },
62
62
  "import": {
63
- "types": "./dist/esm/totally-tubular-react.d.ts",
64
- "default": "./dist/esm/totally-tubular-react.mjs"
63
+ "types": "./dist/esm/index.d.ts",
64
+ "default": "./dist/esm/index.mjs"
65
65
  }
66
66
  },
67
- "./totally-tubular-react.test": {
67
+ "./react": {
68
68
  "require": {
69
- "types": "./dist/cjs/totally-tubular-react.test.d.ts",
70
- "default": "./dist/cjs/totally-tubular-react.test.cjs"
69
+ "types": "./dist/cjs/react.d.ts",
70
+ "default": "./dist/cjs/react.cjs"
71
71
  },
72
72
  "import": {
73
- "types": "./dist/esm/totally-tubular-react.test.d.ts",
74
- "default": "./dist/esm/totally-tubular-react.test.mjs"
73
+ "types": "./dist/esm/react.d.ts",
74
+ "default": "./dist/esm/react.mjs"
75
75
  }
76
76
  },
77
77
  "./totally-tubular": {
@@ -84,16 +84,6 @@
84
84
  "default": "./dist/esm/totally-tubular.mjs"
85
85
  }
86
86
  },
87
- "./totally-tubular.test": {
88
- "require": {
89
- "types": "./dist/cjs/totally-tubular.test.d.ts",
90
- "default": "./dist/cjs/totally-tubular.test.cjs"
91
- },
92
- "import": {
93
- "types": "./dist/esm/totally-tubular.test.d.ts",
94
- "default": "./dist/esm/totally-tubular.test.mjs"
95
- }
96
- },
97
87
  "./types": {
98
88
  "require": {
99
89
  "types": "./dist/cjs/types.d.ts",
@@ -105,5 +95,6 @@
105
95
  }
106
96
  },
107
97
  "./package.json": "./package.json"
108
- }
98
+ },
99
+ "module": "./dist/esm/index.mjs"
109
100
  }
@@ -1,80 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- const _buntest = require("bun:test");
6
- const _react = require("@testing-library/react");
7
- const _totallytubular = require("./totally-tubular.cjs");
8
- const _totallytubularreact = require("./totally-tubular-react.cjs");
9
- const makeInitialState = ()=>({
10
- animals: [],
11
- drink: {
12
- beer: true,
13
- kind: 'guiness'
14
- },
15
- food: {
16
- pasta: false,
17
- pizza: true
18
- }
19
- });
20
- (0, _buntest.describe)('useTubular', ()=>{
21
- (0, _buntest.it)('should read an initial value from a Tubular instance', ()=>{
22
- const initial = makeInitialState();
23
- initial.drink.kind = 'Guiness 0.0%';
24
- const t = new _totallytubular.Tubular(initial);
25
- const { result } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'drink.kind'));
26
- (0, _buntest.expect)(result.current[0]).toBe('Guiness 0.0%');
27
- });
28
- (0, _buntest.it)('should update a top-level value and reflect the change', ()=>{
29
- const t = new _totallytubular.Tubular(makeInitialState());
30
- const animals = [
31
- 'cats',
32
- 'dogs'
33
- ];
34
- const { result, rerender } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'animals'));
35
- (0, _buntest.expect)(result.current[0]).toEqual([]);
36
- (0, _react.act)(()=>{
37
- result.current[1](animals);
38
- });
39
- rerender();
40
- (0, _buntest.expect)(result.current[0]).toEqual(animals);
41
- });
42
- (0, _buntest.it)('should update a nested value using a callback updater', ()=>{
43
- const t = new _totallytubular.Tubular(makeInitialState());
44
- const { result, rerender } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'food.pasta'));
45
- (0, _buntest.expect)(result.current[0]).toBeFalse();
46
- (0, _react.act)(()=>{
47
- result.current[1]((prev)=>!prev);
48
- });
49
- rerender();
50
- (0, _buntest.expect)(result.current[0]).toBeTrue();
51
- });
52
- (0, _buntest.it)('should notify all hooks observing the same key when one updates', ()=>{
53
- const t = new _totallytubular.Tubular(makeInitialState());
54
- const { result: resultA, rerender: rerenderA } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'drink.beer'));
55
- const { result: resultB, rerender: rerenderB } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'drink.beer'));
56
- (0, _buntest.expect)(resultA.current[0]).toBeTrue();
57
- (0, _buntest.expect)(resultB.current[0]).toBeTrue();
58
- (0, _react.act)(()=>{
59
- resultA.current[1](false);
60
- });
61
- rerenderA();
62
- rerenderB();
63
- (0, _buntest.expect)(resultA.current[0]).toBeFalse();
64
- (0, _buntest.expect)(resultB.current[0]).toBeFalse();
65
- });
66
- (0, _buntest.it)('should unobserve when the hook unmounts', ()=>{
67
- const t = new _totallytubular.Tubular(makeInitialState());
68
- const { result, unmount } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'drink.kind'));
69
- (0, _buntest.expect)(result.current[0]).toBe('guiness');
70
- unmount();
71
- // Update after unmount — the store still works, but the unmounted hook
72
- // should no longer be observing so no "update on unmounted component" error occurs.
73
- (0, _react.act)(()=>{
74
- t.update('drink.kind', ()=>'stella');
75
- });
76
- // Verify the store update took effect via a fresh hook instance.
77
- const { result: freshResult } = (0, _react.renderHook)(()=>(0, _totallytubularreact.useTubular)(t, 'drink.kind'));
78
- (0, _buntest.expect)(freshResult.current[0]).toBe('stella');
79
- });
80
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,140 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- const _buntest = require("bun:test");
6
- const _totallytubular = require("./totally-tubular.cjs");
7
- const makeInitialState = ()=>({
8
- animals: [],
9
- drink: {
10
- beer: true,
11
- kind: 'guiness'
12
- },
13
- food: {
14
- pasta: false,
15
- pizza: true
16
- }
17
- });
18
- (0, _buntest.describe)('totally-tubular', ()=>{
19
- (0, _buntest.test)('should update one single property', ()=>{
20
- const t = new _totallytubular.Tubular(makeInitialState());
21
- const newVal = 'old speckled hen';
22
- t.update('drink.kind', ()=>newVal);
23
- (0, _buntest.expect)(t.read('drink.kind')).toBe(newVal);
24
- });
25
- (0, _buntest.test)('should ensure update is called multiple times', ()=>{
26
- const t = new _totallytubular.Tubular(makeInitialState());
27
- const initialBeer = t.read('drink.kind');
28
- const v1 = 'ginger beer';
29
- const v2 = 'corona';
30
- const v3 = "London's Pride";
31
- const ob = _buntest.vi.fn();
32
- t.observe('drink.kind', ob);
33
- t.update('drink.kind', ()=>v1);
34
- t.update('drink.kind', ()=>v2);
35
- t.update('drink.kind', ()=>v3);
36
- (0, _buntest.expect)(ob).toHaveBeenNthCalledWith(1, v1, initialBeer, 'drink.kind');
37
- (0, _buntest.expect)(ob).toHaveBeenNthCalledWith(2, v2, v1, 'drink.kind');
38
- (0, _buntest.expect)(ob).toHaveBeenNthCalledWith(3, v3, v2, 'drink.kind');
39
- });
40
- (0, _buntest.test)('should update an array', ()=>{
41
- const t = new _totallytubular.Tubular(makeInitialState());
42
- t.update('animals', (prev)=>[
43
- ...prev,
44
- 'birds'
45
- ]);
46
- t.update('animals', (prev)=>[
47
- ...prev,
48
- 'cats'
49
- ]);
50
- t.update('animals', (prev)=>[
51
- ...prev,
52
- 'dogs'
53
- ]);
54
- (0, _buntest.expect)(t.read('animals')).toStrictEqual([
55
- 'birds',
56
- 'cats',
57
- 'dogs'
58
- ]);
59
- });
60
- (0, _buntest.test)('should update one item in an array', ()=>{
61
- const t = new _totallytubular.Tubular({
62
- ...makeInitialState(),
63
- animals: [
64
- 'beagle',
65
- 'corgie',
66
- 'dachshund'
67
- ]
68
- });
69
- t.update('animals', (prev)=>prev.toSpliced(1, 1, 'yorkie'));
70
- (0, _buntest.expect)(t.read('animals')).toStrictEqual([
71
- 'beagle',
72
- 'yorkie',
73
- 'dachshund'
74
- ]);
75
- });
76
- (0, _buntest.test)('should observe and then disconnect', ()=>{
77
- const t = new _totallytubular.Tubular(makeInitialState());
78
- const foodOb = _buntest.vi.fn();
79
- const pizzaOb = _buntest.vi.fn();
80
- t.observe('food.pasta', foodOb);
81
- t.observe('food.pizza', pizzaOb);
82
- t.update('food.pasta', (prev)=>!prev);
83
- t.update('food.pizza', (prev)=>!prev);
84
- t.unobserve('food.pasta', foodOb);
85
- t.update('food.pasta', (prev)=>!prev);
86
- t.update('food.pizza', (prev)=>!prev);
87
- (0, _buntest.expect)(t.read('food.pasta')).toBeFalsy();
88
- (0, _buntest.expect)(t.read('food.pizza')).toBeTruthy();
89
- (0, _buntest.expect)(foodOb).toHaveBeenCalledTimes(1);
90
- (0, _buntest.expect)(pizzaOb).toHaveBeenCalledTimes(2);
91
- });
92
- (0, _buntest.test)('should observe an array and object', ()=>{
93
- const t = new _totallytubular.Tubular(makeInitialState());
94
- const animalsOb = _buntest.vi.fn();
95
- const drinkOb = _buntest.vi.fn();
96
- t.observe('animals', animalsOb);
97
- t.observe('drink', drinkOb);
98
- t.update('animals', (prev)=>[
99
- ...prev,
100
- 'dogs'
101
- ]);
102
- t.update('animals', (prev)=>[
103
- ...prev,
104
- 'more dogs'
105
- ]);
106
- t.update('drink', (prev)=>({
107
- ...prev,
108
- beer: false,
109
- kind: 'water'
110
- }));
111
- t.update('drink', (prev)=>({
112
- ...prev,
113
- beer: true,
114
- kind: 'is not beer'
115
- }));
116
- (0, _buntest.expect)(animalsOb).toHaveBeenNthCalledWith(1, [
117
- 'dogs'
118
- ], [], 'animals');
119
- (0, _buntest.expect)(animalsOb).toHaveBeenNthCalledWith(2, [
120
- 'dogs',
121
- 'more dogs'
122
- ], [
123
- 'dogs'
124
- ], 'animals');
125
- (0, _buntest.expect)(drinkOb).toHaveBeenNthCalledWith(1, {
126
- beer: false,
127
- kind: 'water'
128
- }, {
129
- beer: true,
130
- kind: 'guiness'
131
- }, 'drink');
132
- (0, _buntest.expect)(drinkOb).toHaveBeenNthCalledWith(2, {
133
- beer: true,
134
- kind: 'is not beer'
135
- }, {
136
- beer: false,
137
- kind: 'water'
138
- }, 'drink');
139
- });
140
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,76 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import { act, renderHook } from '@testing-library/react';
3
- import { Tubular } from './totally-tubular.mjs';
4
- import { useTubular } from './totally-tubular-react.mjs';
5
- const makeInitialState = ()=>({
6
- animals: [],
7
- drink: {
8
- beer: true,
9
- kind: 'guiness'
10
- },
11
- food: {
12
- pasta: false,
13
- pizza: true
14
- }
15
- });
16
- describe('useTubular', ()=>{
17
- it('should read an initial value from a Tubular instance', ()=>{
18
- const initial = makeInitialState();
19
- initial.drink.kind = 'Guiness 0.0%';
20
- const t = new Tubular(initial);
21
- const { result } = renderHook(()=>useTubular(t, 'drink.kind'));
22
- expect(result.current[0]).toBe('Guiness 0.0%');
23
- });
24
- it('should update a top-level value and reflect the change', ()=>{
25
- const t = new Tubular(makeInitialState());
26
- const animals = [
27
- 'cats',
28
- 'dogs'
29
- ];
30
- const { result, rerender } = renderHook(()=>useTubular(t, 'animals'));
31
- expect(result.current[0]).toEqual([]);
32
- act(()=>{
33
- result.current[1](animals);
34
- });
35
- rerender();
36
- expect(result.current[0]).toEqual(animals);
37
- });
38
- it('should update a nested value using a callback updater', ()=>{
39
- const t = new Tubular(makeInitialState());
40
- const { result, rerender } = renderHook(()=>useTubular(t, 'food.pasta'));
41
- expect(result.current[0]).toBeFalse();
42
- act(()=>{
43
- result.current[1]((prev)=>!prev);
44
- });
45
- rerender();
46
- expect(result.current[0]).toBeTrue();
47
- });
48
- it('should notify all hooks observing the same key when one updates', ()=>{
49
- const t = new Tubular(makeInitialState());
50
- const { result: resultA, rerender: rerenderA } = renderHook(()=>useTubular(t, 'drink.beer'));
51
- const { result: resultB, rerender: rerenderB } = renderHook(()=>useTubular(t, 'drink.beer'));
52
- expect(resultA.current[0]).toBeTrue();
53
- expect(resultB.current[0]).toBeTrue();
54
- act(()=>{
55
- resultA.current[1](false);
56
- });
57
- rerenderA();
58
- rerenderB();
59
- expect(resultA.current[0]).toBeFalse();
60
- expect(resultB.current[0]).toBeFalse();
61
- });
62
- it('should unobserve when the hook unmounts', ()=>{
63
- const t = new Tubular(makeInitialState());
64
- const { result, unmount } = renderHook(()=>useTubular(t, 'drink.kind'));
65
- expect(result.current[0]).toBe('guiness');
66
- unmount();
67
- // Update after unmount — the store still works, but the unmounted hook
68
- // should no longer be observing so no "update on unmounted component" error occurs.
69
- act(()=>{
70
- t.update('drink.kind', ()=>'stella');
71
- });
72
- // Verify the store update took effect via a fresh hook instance.
73
- const { result: freshResult } = renderHook(()=>useTubular(t, 'drink.kind'));
74
- expect(freshResult.current[0]).toBe('stella');
75
- });
76
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,136 +0,0 @@
1
- import { describe, expect, test, vi } from 'bun:test';
2
- import { Tubular } from './totally-tubular.mjs';
3
- const makeInitialState = ()=>({
4
- animals: [],
5
- drink: {
6
- beer: true,
7
- kind: 'guiness'
8
- },
9
- food: {
10
- pasta: false,
11
- pizza: true
12
- }
13
- });
14
- describe('totally-tubular', ()=>{
15
- test('should update one single property', ()=>{
16
- const t = new Tubular(makeInitialState());
17
- const newVal = 'old speckled hen';
18
- t.update('drink.kind', ()=>newVal);
19
- expect(t.read('drink.kind')).toBe(newVal);
20
- });
21
- test('should ensure update is called multiple times', ()=>{
22
- const t = new Tubular(makeInitialState());
23
- const initialBeer = t.read('drink.kind');
24
- const v1 = 'ginger beer';
25
- const v2 = 'corona';
26
- const v3 = "London's Pride";
27
- const ob = vi.fn();
28
- t.observe('drink.kind', ob);
29
- t.update('drink.kind', ()=>v1);
30
- t.update('drink.kind', ()=>v2);
31
- t.update('drink.kind', ()=>v3);
32
- expect(ob).toHaveBeenNthCalledWith(1, v1, initialBeer, 'drink.kind');
33
- expect(ob).toHaveBeenNthCalledWith(2, v2, v1, 'drink.kind');
34
- expect(ob).toHaveBeenNthCalledWith(3, v3, v2, 'drink.kind');
35
- });
36
- test('should update an array', ()=>{
37
- const t = new Tubular(makeInitialState());
38
- t.update('animals', (prev)=>[
39
- ...prev,
40
- 'birds'
41
- ]);
42
- t.update('animals', (prev)=>[
43
- ...prev,
44
- 'cats'
45
- ]);
46
- t.update('animals', (prev)=>[
47
- ...prev,
48
- 'dogs'
49
- ]);
50
- expect(t.read('animals')).toStrictEqual([
51
- 'birds',
52
- 'cats',
53
- 'dogs'
54
- ]);
55
- });
56
- test('should update one item in an array', ()=>{
57
- const t = new Tubular({
58
- ...makeInitialState(),
59
- animals: [
60
- 'beagle',
61
- 'corgie',
62
- 'dachshund'
63
- ]
64
- });
65
- t.update('animals', (prev)=>prev.toSpliced(1, 1, 'yorkie'));
66
- expect(t.read('animals')).toStrictEqual([
67
- 'beagle',
68
- 'yorkie',
69
- 'dachshund'
70
- ]);
71
- });
72
- test('should observe and then disconnect', ()=>{
73
- const t = new Tubular(makeInitialState());
74
- const foodOb = vi.fn();
75
- const pizzaOb = vi.fn();
76
- t.observe('food.pasta', foodOb);
77
- t.observe('food.pizza', pizzaOb);
78
- t.update('food.pasta', (prev)=>!prev);
79
- t.update('food.pizza', (prev)=>!prev);
80
- t.unobserve('food.pasta', foodOb);
81
- t.update('food.pasta', (prev)=>!prev);
82
- t.update('food.pizza', (prev)=>!prev);
83
- expect(t.read('food.pasta')).toBeFalsy();
84
- expect(t.read('food.pizza')).toBeTruthy();
85
- expect(foodOb).toHaveBeenCalledTimes(1);
86
- expect(pizzaOb).toHaveBeenCalledTimes(2);
87
- });
88
- test('should observe an array and object', ()=>{
89
- const t = new Tubular(makeInitialState());
90
- const animalsOb = vi.fn();
91
- const drinkOb = vi.fn();
92
- t.observe('animals', animalsOb);
93
- t.observe('drink', drinkOb);
94
- t.update('animals', (prev)=>[
95
- ...prev,
96
- 'dogs'
97
- ]);
98
- t.update('animals', (prev)=>[
99
- ...prev,
100
- 'more dogs'
101
- ]);
102
- t.update('drink', (prev)=>({
103
- ...prev,
104
- beer: false,
105
- kind: 'water'
106
- }));
107
- t.update('drink', (prev)=>({
108
- ...prev,
109
- beer: true,
110
- kind: 'is not beer'
111
- }));
112
- expect(animalsOb).toHaveBeenNthCalledWith(1, [
113
- 'dogs'
114
- ], [], 'animals');
115
- expect(animalsOb).toHaveBeenNthCalledWith(2, [
116
- 'dogs',
117
- 'more dogs'
118
- ], [
119
- 'dogs'
120
- ], 'animals');
121
- expect(drinkOb).toHaveBeenNthCalledWith(1, {
122
- beer: false,
123
- kind: 'water'
124
- }, {
125
- beer: true,
126
- kind: 'guiness'
127
- }, 'drink');
128
- expect(drinkOb).toHaveBeenNthCalledWith(2, {
129
- beer: true,
130
- kind: 'is not beer'
131
- }, {
132
- beer: false,
133
- kind: 'water'
134
- }, 'drink');
135
- });
136
- });