@stencil/store 2.1.3 → 2.2.1-dev.1763338163.54383e1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,25 @@
1
1
  'use strict';
2
2
 
3
- var core = require('@stencil/core');
3
+ var StencilCore = require('@stencil/core');
4
+
5
+ function _interopNamespaceDefault(e) {
6
+ var n = Object.create(null);
7
+ if (e) {
8
+ Object.keys(e).forEach(function (k) {
9
+ if (k !== 'default') {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: function () { return e[k]; }
14
+ });
15
+ }
16
+ });
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+
22
+ var StencilCore__namespace = /*#__PURE__*/_interopNamespaceDefault(StencilCore);
4
23
 
5
24
  const appendToMap = (map, propName, value) => {
6
25
  const items = map.get(propName);
@@ -39,17 +58,22 @@ const cleanupElements = debounce((map) => {
39
58
  map.set(key, map.get(key).filter(isConnected));
40
59
  }
41
60
  }, 2_000);
61
+ const core = StencilCore__namespace;
62
+ const forceUpdate = core.forceUpdate;
63
+ const getRenderingRef = core.getRenderingRef;
42
64
  const stencilSubscription = () => {
43
- if (typeof core.getRenderingRef !== 'function') {
65
+ if (typeof getRenderingRef !== 'function' || typeof forceUpdate !== 'function') {
44
66
  // If we are not in a stencil project, we do nothing.
45
67
  // This function is not really exported by @stencil/core.
46
68
  return {};
47
69
  }
70
+ const ensureForceUpdate = forceUpdate;
71
+ const ensureGetRenderingRef = getRenderingRef;
48
72
  const elmsToUpdate = new Map();
49
73
  return {
50
74
  dispose: () => elmsToUpdate.clear(),
51
75
  get: (propName) => {
52
- const elm = core.getRenderingRef();
76
+ const elm = ensureGetRenderingRef();
53
77
  if (elm) {
54
78
  appendToMap(elmsToUpdate, propName, elm);
55
79
  }
@@ -57,12 +81,12 @@ const stencilSubscription = () => {
57
81
  set: (propName) => {
58
82
  const elements = elmsToUpdate.get(propName);
59
83
  if (elements) {
60
- elmsToUpdate.set(propName, elements.filter(core.forceUpdate));
84
+ elmsToUpdate.set(propName, elements.filter(ensureForceUpdate));
61
85
  }
62
86
  cleanupElements(elmsToUpdate);
63
87
  },
64
88
  reset: () => {
65
- elmsToUpdate.forEach((elms) => elms.forEach(core.forceUpdate));
89
+ elmsToUpdate.forEach((elms) => elms.forEach(ensureForceUpdate));
66
90
  cleanupElements(elmsToUpdate);
67
91
  },
68
92
  };
@@ -70,18 +94,26 @@ const stencilSubscription = () => {
70
94
 
71
95
  const unwrap = (val) => (typeof val === 'function' ? val() : val);
72
96
  const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) => {
73
- const unwrappedState = unwrap(defaultState);
74
- let states = new Map(Object.entries(unwrappedState ?? {}));
97
+ const resolveDefaultState = () => (unwrap(defaultState) ?? {});
98
+ const initialState = resolveDefaultState();
99
+ let states = new Map(Object.entries(initialState));
100
+ const proxyAvailable = typeof Proxy !== 'undefined';
101
+ const plainState = proxyAvailable ? null : {};
75
102
  const handlers = {
76
103
  dispose: [],
77
104
  get: [],
78
105
  set: [],
79
106
  reset: [],
80
107
  };
108
+ // Track onChange listeners to enable removeListener functionality
109
+ const changeListeners = new Map();
81
110
  const reset = () => {
82
111
  // When resetting the state, the default state may be a function - unwrap it to invoke it.
83
112
  // otherwise, the state won't be properly reset
84
- states = new Map(Object.entries(unwrap(defaultState) ?? {}));
113
+ states = new Map(Object.entries(resolveDefaultState()));
114
+ if (!proxyAvailable) {
115
+ syncPlainStateKeys();
116
+ }
85
117
  handlers.reset.forEach((cb) => cb());
86
118
  };
87
119
  const dispose = () => {
@@ -98,12 +130,14 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
98
130
  const oldValue = states.get(propName);
99
131
  if (shouldUpdate(value, oldValue, propName)) {
100
132
  states.set(propName, value);
133
+ if (!proxyAvailable) {
134
+ ensurePlainProperty(propName);
135
+ }
101
136
  handlers.set.forEach((cb) => cb(propName, value, oldValue));
102
137
  }
103
138
  };
104
- const state = (typeof Proxy === 'undefined'
105
- ? {}
106
- : new Proxy(unwrappedState, {
139
+ const state = (proxyAvailable
140
+ ? new Proxy(initialState, {
107
141
  get(_, propName) {
108
142
  return get(propName);
109
143
  },
@@ -123,7 +157,11 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
123
157
  set(propName, value);
124
158
  return true;
125
159
  },
126
- }));
160
+ })
161
+ : (() => {
162
+ syncPlainStateKeys();
163
+ return plainState;
164
+ })());
127
165
  const on = (eventName, callback) => {
128
166
  handlers[eventName].push(callback);
129
167
  return () => {
@@ -131,17 +169,24 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
131
169
  };
132
170
  };
133
171
  const onChange = (propName, cb) => {
134
- const unSet = on('set', (key, newValue) => {
172
+ const setHandler = (key, newValue) => {
135
173
  if (key === propName) {
136
174
  cb(newValue);
137
175
  }
138
- });
139
- // We need to unwrap the defaultState because it might be a function.
140
- // Otherwise we might not be sending the right reset value.
141
- const unReset = on('reset', () => cb(unwrap(defaultState)[propName]));
176
+ };
177
+ const resetHandler = () => {
178
+ const snapshot = resolveDefaultState();
179
+ cb(snapshot[propName]);
180
+ };
181
+ // Register the handlers
182
+ const unSet = on('set', setHandler);
183
+ const unReset = on('reset', resetHandler);
184
+ // Track the relationship between the user callback and internal handlers
185
+ changeListeners.set(cb, { setHandler, resetHandler, propName });
142
186
  return () => {
143
187
  unSet();
144
188
  unReset();
189
+ changeListeners.delete(cb);
145
190
  };
146
191
  };
147
192
  const use = (...subscriptions) => {
@@ -166,6 +211,47 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
166
211
  const oldValue = states.get(key);
167
212
  handlers.set.forEach((cb) => cb(key, oldValue, oldValue));
168
213
  };
214
+ const removeListener = (propName, listener) => {
215
+ const listenerInfo = changeListeners.get(listener);
216
+ if (listenerInfo && listenerInfo.propName === propName) {
217
+ // Remove the specific handlers that were created for this listener
218
+ removeFromArray(handlers.set, listenerInfo.setHandler);
219
+ removeFromArray(handlers.reset, listenerInfo.resetHandler);
220
+ changeListeners.delete(listener);
221
+ }
222
+ };
223
+ function ensurePlainProperty(key) {
224
+ if (proxyAvailable || !plainState) {
225
+ return;
226
+ }
227
+ if (Object.prototype.hasOwnProperty.call(plainState, key)) {
228
+ return;
229
+ }
230
+ Object.defineProperty(plainState, key, {
231
+ configurable: true,
232
+ enumerable: true,
233
+ get() {
234
+ return get(key);
235
+ },
236
+ set(value) {
237
+ set(key, value);
238
+ },
239
+ });
240
+ }
241
+ function syncPlainStateKeys() {
242
+ if (proxyAvailable || !plainState) {
243
+ return;
244
+ }
245
+ const knownKeys = new Set(states.keys());
246
+ for (const key of Object.keys(plainState)) {
247
+ if (!knownKeys.has(key)) {
248
+ delete plainState[key];
249
+ }
250
+ }
251
+ for (const key of knownKeys) {
252
+ ensurePlainProperty(key);
253
+ }
254
+ }
169
255
  return {
170
256
  state,
171
257
  get,
@@ -176,6 +262,7 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
176
262
  dispose,
177
263
  reset,
178
264
  forceUpdate,
265
+ removeListener,
179
266
  };
180
267
  };
181
268
  const removeFromArray = (array, item) => {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getRenderingRef, forceUpdate } from '@stencil/core';
1
+ import * as StencilCore from '@stencil/core';
2
2
 
3
3
  const appendToMap = (map, propName, value) => {
4
4
  const items = map.get(propName);
@@ -37,17 +37,22 @@ const cleanupElements = debounce((map) => {
37
37
  map.set(key, map.get(key).filter(isConnected));
38
38
  }
39
39
  }, 2_000);
40
+ const core = StencilCore;
41
+ const forceUpdate = core.forceUpdate;
42
+ const getRenderingRef = core.getRenderingRef;
40
43
  const stencilSubscription = () => {
41
- if (typeof getRenderingRef !== 'function') {
44
+ if (typeof getRenderingRef !== 'function' || typeof forceUpdate !== 'function') {
42
45
  // If we are not in a stencil project, we do nothing.
43
46
  // This function is not really exported by @stencil/core.
44
47
  return {};
45
48
  }
49
+ const ensureForceUpdate = forceUpdate;
50
+ const ensureGetRenderingRef = getRenderingRef;
46
51
  const elmsToUpdate = new Map();
47
52
  return {
48
53
  dispose: () => elmsToUpdate.clear(),
49
54
  get: (propName) => {
50
- const elm = getRenderingRef();
55
+ const elm = ensureGetRenderingRef();
51
56
  if (elm) {
52
57
  appendToMap(elmsToUpdate, propName, elm);
53
58
  }
@@ -55,12 +60,12 @@ const stencilSubscription = () => {
55
60
  set: (propName) => {
56
61
  const elements = elmsToUpdate.get(propName);
57
62
  if (elements) {
58
- elmsToUpdate.set(propName, elements.filter(forceUpdate));
63
+ elmsToUpdate.set(propName, elements.filter(ensureForceUpdate));
59
64
  }
60
65
  cleanupElements(elmsToUpdate);
61
66
  },
62
67
  reset: () => {
63
- elmsToUpdate.forEach((elms) => elms.forEach(forceUpdate));
68
+ elmsToUpdate.forEach((elms) => elms.forEach(ensureForceUpdate));
64
69
  cleanupElements(elmsToUpdate);
65
70
  },
66
71
  };
@@ -68,18 +73,26 @@ const stencilSubscription = () => {
68
73
 
69
74
  const unwrap = (val) => (typeof val === 'function' ? val() : val);
70
75
  const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) => {
71
- const unwrappedState = unwrap(defaultState);
72
- let states = new Map(Object.entries(unwrappedState ?? {}));
76
+ const resolveDefaultState = () => (unwrap(defaultState) ?? {});
77
+ const initialState = resolveDefaultState();
78
+ let states = new Map(Object.entries(initialState));
79
+ const proxyAvailable = typeof Proxy !== 'undefined';
80
+ const plainState = proxyAvailable ? null : {};
73
81
  const handlers = {
74
82
  dispose: [],
75
83
  get: [],
76
84
  set: [],
77
85
  reset: [],
78
86
  };
87
+ // Track onChange listeners to enable removeListener functionality
88
+ const changeListeners = new Map();
79
89
  const reset = () => {
80
90
  // When resetting the state, the default state may be a function - unwrap it to invoke it.
81
91
  // otherwise, the state won't be properly reset
82
- states = new Map(Object.entries(unwrap(defaultState) ?? {}));
92
+ states = new Map(Object.entries(resolveDefaultState()));
93
+ if (!proxyAvailable) {
94
+ syncPlainStateKeys();
95
+ }
83
96
  handlers.reset.forEach((cb) => cb());
84
97
  };
85
98
  const dispose = () => {
@@ -96,12 +109,14 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
96
109
  const oldValue = states.get(propName);
97
110
  if (shouldUpdate(value, oldValue, propName)) {
98
111
  states.set(propName, value);
112
+ if (!proxyAvailable) {
113
+ ensurePlainProperty(propName);
114
+ }
99
115
  handlers.set.forEach((cb) => cb(propName, value, oldValue));
100
116
  }
101
117
  };
102
- const state = (typeof Proxy === 'undefined'
103
- ? {}
104
- : new Proxy(unwrappedState, {
118
+ const state = (proxyAvailable
119
+ ? new Proxy(initialState, {
105
120
  get(_, propName) {
106
121
  return get(propName);
107
122
  },
@@ -121,7 +136,11 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
121
136
  set(propName, value);
122
137
  return true;
123
138
  },
124
- }));
139
+ })
140
+ : (() => {
141
+ syncPlainStateKeys();
142
+ return plainState;
143
+ })());
125
144
  const on = (eventName, callback) => {
126
145
  handlers[eventName].push(callback);
127
146
  return () => {
@@ -129,17 +148,24 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
129
148
  };
130
149
  };
131
150
  const onChange = (propName, cb) => {
132
- const unSet = on('set', (key, newValue) => {
151
+ const setHandler = (key, newValue) => {
133
152
  if (key === propName) {
134
153
  cb(newValue);
135
154
  }
136
- });
137
- // We need to unwrap the defaultState because it might be a function.
138
- // Otherwise we might not be sending the right reset value.
139
- const unReset = on('reset', () => cb(unwrap(defaultState)[propName]));
155
+ };
156
+ const resetHandler = () => {
157
+ const snapshot = resolveDefaultState();
158
+ cb(snapshot[propName]);
159
+ };
160
+ // Register the handlers
161
+ const unSet = on('set', setHandler);
162
+ const unReset = on('reset', resetHandler);
163
+ // Track the relationship between the user callback and internal handlers
164
+ changeListeners.set(cb, { setHandler, resetHandler, propName });
140
165
  return () => {
141
166
  unSet();
142
167
  unReset();
168
+ changeListeners.delete(cb);
143
169
  };
144
170
  };
145
171
  const use = (...subscriptions) => {
@@ -164,6 +190,47 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
164
190
  const oldValue = states.get(key);
165
191
  handlers.set.forEach((cb) => cb(key, oldValue, oldValue));
166
192
  };
193
+ const removeListener = (propName, listener) => {
194
+ const listenerInfo = changeListeners.get(listener);
195
+ if (listenerInfo && listenerInfo.propName === propName) {
196
+ // Remove the specific handlers that were created for this listener
197
+ removeFromArray(handlers.set, listenerInfo.setHandler);
198
+ removeFromArray(handlers.reset, listenerInfo.resetHandler);
199
+ changeListeners.delete(listener);
200
+ }
201
+ };
202
+ function ensurePlainProperty(key) {
203
+ if (proxyAvailable || !plainState) {
204
+ return;
205
+ }
206
+ if (Object.prototype.hasOwnProperty.call(plainState, key)) {
207
+ return;
208
+ }
209
+ Object.defineProperty(plainState, key, {
210
+ configurable: true,
211
+ enumerable: true,
212
+ get() {
213
+ return get(key);
214
+ },
215
+ set(value) {
216
+ set(key, value);
217
+ },
218
+ });
219
+ }
220
+ function syncPlainStateKeys() {
221
+ if (proxyAvailable || !plainState) {
222
+ return;
223
+ }
224
+ const knownKeys = new Set(states.keys());
225
+ for (const key of Object.keys(plainState)) {
226
+ if (!knownKeys.has(key)) {
227
+ delete plainState[key];
228
+ }
229
+ }
230
+ for (const key of knownKeys) {
231
+ ensurePlainProperty(key);
232
+ }
233
+ }
167
234
  return {
168
235
  state,
169
236
  get,
@@ -174,6 +241,7 @@ const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) =>
174
241
  dispose,
175
242
  reset,
176
243
  forceUpdate,
244
+ removeListener,
177
245
  };
178
246
  };
179
247
  const removeFromArray = (array, item) => {
package/dist/types.d.ts CHANGED
@@ -40,11 +40,15 @@ export interface ObservableMap<T> {
40
40
  * Note: Proxy objects are not supported by IE11 (not even with a polyfill)
41
41
  * so you need to use the store.get and store.set methods of the API if you wish to support IE11.
42
42
  *
43
+ * @returns The proxied object
43
44
  */
44
45
  state: T;
45
46
  /**
46
47
  * Only useful if you need to support IE11.
47
48
  *
49
+ * @param propName - The property name to get
50
+ * @returns The value of the property
51
+ *
48
52
  * @example
49
53
  * const { state, get } = createStore({ hola: 'hello', adios: 'goodbye' });
50
54
  * console.log(state.hola); // If you don't need to support IE11, use this way.
@@ -54,6 +58,10 @@ export interface ObservableMap<T> {
54
58
  /**
55
59
  * Only useful if you need to support IE11.
56
60
  *
61
+ * @param propName - The property name to set
62
+ * @param value - The value to set
63
+ * @returns void
64
+ *
57
65
  * @example
58
66
  * const { state, get } = createStore({ hola: 'hello', adios: 'goodbye' });
59
67
  * state.hola = 'ola'; // If you don't need to support IE11, use this way.
@@ -63,6 +71,10 @@ export interface ObservableMap<T> {
63
71
  /**
64
72
  * Register a event listener, you can listen to `set`, `get` and `reset` events.
65
73
  *
74
+ * @param eventName - The event name to listen for
75
+ * @param callback - The callback to call when the event occurs
76
+ * @returns A function to unsubscribe from the listener
77
+ *
66
78
  * @example
67
79
  * store.on('set', (prop, value) => {
68
80
  * console.log(`Prop ${prop} changed to: ${value}`);
@@ -71,6 +83,10 @@ export interface ObservableMap<T> {
71
83
  on: OnHandler<T>;
72
84
  /**
73
85
  * Easily listen for value changes of the specified key.
86
+ *
87
+ * @param propName - The property name to listen for
88
+ * @param cb - The callback to call when the property changes
89
+ * @returns A function to unsubscribe from the listener
74
90
  */
75
91
  onChange: OnChangeHandler<T>;
76
92
  /**
@@ -79,19 +95,37 @@ export interface ObservableMap<T> {
79
95
  *
80
96
  * This method is intended for plugins to reset
81
97
  * all their internal state between tests.
98
+ *
99
+ * @returns void
82
100
  */
83
101
  dispose(): void;
84
102
  /**
85
103
  * Resets the state to its original state.
104
+ *
105
+ * @returns void
86
106
  */
87
107
  reset(): void;
88
108
  /**
89
109
  * Registers a subscription that will be called whenever the user gets, sets, or
90
110
  * resets a value.
111
+ *
112
+ * @param plugins - The plugins to use
113
+ * @returns A function to unsubscribe from the plugins
91
114
  */
92
115
  use(...plugins: Subscription<T>[]): () => void;
93
116
  /**
94
117
  * Force a rerender of the specified key, just like the value changed.
118
+ *
119
+ * @param key - The property name to force an update for
120
+ * @returns void
121
+ */
122
+ forceUpdate(key: keyof T): void;
123
+ /**
124
+ * Remove a listener
125
+ *
126
+ * @param propName - The property name to remove the listener from
127
+ * @param listener - The listener to remove
128
+ * @returns void
95
129
  */
96
- forceUpdate(key: keyof T): any;
130
+ removeListener(propName: keyof T, listener: (value: any) => void): void;
97
131
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stencil/store",
3
3
  "author": "StencilJS Team",
4
- "version": "2.1.3",
4
+ "version": "2.2.1-dev.1763338163.54383e1",
5
5
  "description": "Store is a lightweight shared state library by the StencilJS core team. Implements a simple key/value map that efficiently re-renders components when necessary.",
6
6
  "license": "MIT",
7
7
  "homepage": "https://stenciljs.com/docs/stencil-store",
@@ -33,11 +33,11 @@
33
33
  "npm": ">=6.0.0"
34
34
  },
35
35
  "scripts": {
36
- "build": "run-s build.*",
37
- "build.clean": "rm -rf dist",
36
+ "build": "run-s build.clean build.rollup",
37
+ "build.clean": "rimraf dist",
38
38
  "build.rollup": "rollup -c rollup.config.js",
39
39
  "prettier": "npm run prettier.base -- --write",
40
- "prettier.base": "prettier --cache 'src/**/*.ts'",
40
+ "prettier.base": "prettier --cache \"src/**/*.ts\"",
41
41
  "prettier.dry-run": "npm run prettier.base -- --list-different",
42
42
  "release": "np",
43
43
  "test": "run-s test.*",
@@ -53,16 +53,17 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@ionic/prettier-config": "^4.0.0",
56
- "@rollup/plugin-typescript": "^12.1.2",
57
- "@stencil/core": "^4.27.1",
58
- "@types/node": "^22.13.5",
59
- "@vitest/coverage-v8": "^3.0.7",
56
+ "@rollup/plugin-typescript": "^12.3.0",
57
+ "@stencil/core": "^4.38.2",
58
+ "@types/node": "^24.9.2",
59
+ "@vitest/coverage-v8": "^4.0.5",
60
60
  "np": "^10.2.0",
61
- "npm-run-all2": "^7.0.2",
62
- "prettier": "^3.5.2",
63
- "rollup": "^4.34.8",
64
- "typescript": "~5.8.2",
65
- "vitest": "^3.0.7"
61
+ "npm-run-all2": "^8.0.4",
62
+ "prettier": "^3.6.2",
63
+ "rimraf": "^6.0.1",
64
+ "rollup": "^4.52.5",
65
+ "typescript": "~5.9.3",
66
+ "vitest": "^4.0.5"
66
67
  },
67
68
  "prettier": "@ionic/prettier-config"
68
69
  }