@ngrx/signals 19.0.0 → 19.1.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.
@@ -64,13 +64,25 @@ function signalMethod(processingFn, config) {
64
64
  const sourceInjector = config?.injector ?? inject(Injector);
65
65
  const signalMethodFn = (input, config) => {
66
66
  if (isSignal(input)) {
67
- const instanceInjector = config?.injector ?? getCallerInjector() ?? sourceInjector;
68
- const watcher = effect((onCleanup) => {
67
+ const callerInjector = getCallerInjector();
68
+ if (typeof ngDevMode !== 'undefined' &&
69
+ ngDevMode &&
70
+ config?.injector === undefined &&
71
+ callerInjector === undefined) {
72
+ console.warn('@ngrx/signals: The function returned by signalMethod was called', 'outside the injection context with a signal. This may lead to', 'a memory leak. Make sure to call it within the injection context', '(e.g. in a constructor or field initializer) or pass an injector', 'explicitly via the config parameter.\n\nFor more information, see:', 'https://ngrx.io/guide/signals/signal-method#automatic-cleanup');
73
+ }
74
+ const instanceInjector = config?.injector ?? callerInjector ?? sourceInjector;
75
+ const watcher = effect(() => {
69
76
  const value = input();
70
77
  untracked(() => processingFn(value));
71
- onCleanup(() => watchers.splice(watchers.indexOf(watcher), 1));
72
78
  }, { injector: instanceInjector });
73
79
  watchers.push(watcher);
80
+ instanceInjector.get(DestroyRef).onDestroy(() => {
81
+ const ix = watchers.indexOf(watcher);
82
+ if (ix !== -1) {
83
+ watchers.splice(ix, 1);
84
+ }
85
+ });
74
86
  return watcher;
75
87
  }
76
88
  else {
@@ -86,47 +98,20 @@ function getCallerInjector() {
86
98
  return inject(Injector);
87
99
  }
88
100
  catch {
89
- return null;
101
+ return undefined;
90
102
  }
91
103
  }
92
104
 
93
- function deepFreeze(target) {
94
- Object.freeze(target);
95
- const targetIsFunction = typeof target === 'function';
96
- Object.getOwnPropertyNames(target).forEach((prop) => {
97
- // Ignore Ivy properties, ref: https://github.com/ngrx/platform/issues/2109#issuecomment-582689060
98
- if (prop.startsWith('ɵ')) {
99
- return;
100
- }
101
- if (hasOwnProperty(target, prop) &&
102
- (targetIsFunction
103
- ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments'
104
- : true)) {
105
- const propValue = target[prop];
106
- if ((isObjectLike(propValue) || typeof propValue === 'function') &&
107
- !Object.isFrozen(propValue)) {
108
- deepFreeze(propValue);
109
- }
110
- }
111
- });
112
- return target;
113
- }
114
- function freezeInDevMode(target) {
115
- return ngDevMode ? deepFreeze(target) : target;
116
- }
117
- function hasOwnProperty(target, propertyName) {
118
- return isObjectLike(target)
119
- ? Object.prototype.hasOwnProperty.call(target, propertyName)
120
- : false;
121
- }
122
- function isObjectLike(target) {
123
- return typeof target === 'object' && target !== null;
124
- }
125
-
126
105
  const STATE_WATCHERS = new WeakMap();
127
106
  const STATE_SOURCE = Symbol('STATE_SOURCE');
107
+ function isWritableStateSource(stateSource) {
108
+ return ('set' in stateSource[STATE_SOURCE] &&
109
+ 'update' in stateSource[STATE_SOURCE] &&
110
+ typeof stateSource[STATE_SOURCE].set === 'function' &&
111
+ typeof stateSource[STATE_SOURCE].update === 'function');
112
+ }
128
113
  function patchState(stateSource, ...updaters) {
129
- stateSource[STATE_SOURCE].update((currentState) => updaters.reduce((nextState, updater) => freezeInDevMode({
114
+ stateSource[STATE_SOURCE].update((currentState) => updaters.reduce((nextState, updater) => ({
130
115
  ...nextState,
131
116
  ...(typeof updater === 'function' ? updater(nextState) : updater),
132
117
  }), currentState));
@@ -167,7 +152,7 @@ function removeWatcher(stateSource, watcher) {
167
152
  }
168
153
 
169
154
  function signalState(initialState) {
170
- const stateSource = signal(freezeInDevMode(initialState));
155
+ const stateSource = signal(initialState);
171
156
  const signalState = toDeepSignal(stateSource.asReadonly());
172
157
  Object.defineProperty(signalState, STATE_SOURCE, {
173
158
  value: stateSource,
@@ -185,9 +170,13 @@ function signalStore(...args) {
185
170
  constructor() {
186
171
  const innerStore = features.reduce((store, feature) => feature(store), getInitialInnerStore());
187
172
  const { stateSignals, props, methods, hooks } = innerStore;
188
- const storeMembers = { ...stateSignals, ...props, ...methods };
173
+ const storeMembers = {
174
+ ...stateSignals,
175
+ ...props,
176
+ ...methods,
177
+ };
189
178
  this[STATE_SOURCE] = innerStore[STATE_SOURCE];
190
- for (const key in storeMembers) {
179
+ for (const key of Reflect.ownKeys(storeMembers)) {
191
180
  this[key] = storeMembers[key];
192
181
  }
193
182
  const { onInit, onDestroy } = hooks;
@@ -198,10 +187,10 @@ function signalStore(...args) {
198
187
  inject(DestroyRef).onDestroy(onDestroy);
199
188
  }
200
189
  }
201
- /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SignalStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
202
- /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SignalStore, providedIn: config.providedIn || null });
190
+ /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: SignalStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
191
+ /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: SignalStore, providedIn: config.providedIn || null });
203
192
  }
204
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: SignalStore, decorators: [{
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.3", ngImport: i0, type: SignalStore, decorators: [{
205
194
  type: Injectable,
206
195
  args: [{ providedIn: config.providedIn || null }]
207
196
  }], ctorParameters: () => [] });
@@ -228,7 +217,7 @@ function type() {
228
217
  }
229
218
 
230
219
  function assertUniqueStoreMembers(store, newMemberKeys) {
231
- if (!ngDevMode) {
220
+ if (typeof ngDevMode === 'undefined' || !ngDevMode) {
232
221
  return;
233
222
  }
234
223
  const storeMembers = {
@@ -236,9 +225,9 @@ function assertUniqueStoreMembers(store, newMemberKeys) {
236
225
  ...store.props,
237
226
  ...store.methods,
238
227
  };
239
- const overriddenKeys = Object.keys(storeMembers).filter((memberKey) => newMemberKeys.includes(memberKey));
228
+ const overriddenKeys = Reflect.ownKeys(storeMembers).filter((memberKey) => newMemberKeys.includes(memberKey));
240
229
  if (overriddenKeys.length > 0) {
241
- console.warn('@ngrx/signals: SignalStore members cannot be overridden.', 'Trying to override:', overriddenKeys.join(', '));
230
+ console.warn('@ngrx/signals: SignalStore members cannot be overridden.', 'Trying to override:', overriddenKeys.map((key) => String(key)).join(', '));
242
231
  }
243
232
  }
244
233
 
@@ -250,7 +239,7 @@ function withProps(propsFactory) {
250
239
  ...store.props,
251
240
  ...store.methods,
252
241
  });
253
- assertUniqueStoreMembers(store, Object.keys(props));
242
+ assertUniqueStoreMembers(store, Reflect.ownKeys(props));
254
243
  return {
255
244
  ...store,
256
245
  props: { ...store.props, ...props },
@@ -262,6 +251,39 @@ function withComputed(signalsFactory) {
262
251
  return withProps(signalsFactory);
263
252
  }
264
253
 
254
+ /**
255
+ * @description
256
+ * Allows passing properties, methods, or signals from a SignalStore
257
+ * to a feature.
258
+ *
259
+ * @usageNotes
260
+ * ```typescript
261
+ * signalStore(
262
+ * withMethods((store) => ({
263
+ * load(id: number): Observable<Entity> {
264
+ * return of({ id, name: 'John' });
265
+ * },
266
+ * })),
267
+ * withFeature(
268
+ * // 👇 has full access to the store
269
+ * (store) => withEntityLoader((id) => firstValueFrom(store.load(id)))
270
+ * )
271
+ * );
272
+ * ```
273
+ *
274
+ * @param featureFactory function returning the actual feature
275
+ */
276
+ function withFeature(featureFactory) {
277
+ return (store) => {
278
+ const storeForFactory = {
279
+ ...store['stateSignals'],
280
+ ...store['props'],
281
+ ...store['methods'],
282
+ };
283
+ return featureFactory(storeForFactory)(store);
284
+ };
285
+ }
286
+
265
287
  function withHooks(hooksOrFactory) {
266
288
  return (store) => {
267
289
  const storeMembers = {
@@ -303,7 +325,7 @@ function withMethods(methodsFactory) {
303
325
  ...store.props,
304
326
  ...store.methods,
305
327
  });
306
- assertUniqueStoreMembers(store, Object.keys(methods));
328
+ assertUniqueStoreMembers(store, Reflect.ownKeys(methods));
307
329
  return {
308
330
  ...store,
309
331
  methods: { ...store.methods, ...methods },
@@ -314,9 +336,9 @@ function withMethods(methodsFactory) {
314
336
  function withState(stateOrFactory) {
315
337
  return (store) => {
316
338
  const state = typeof stateOrFactory === 'function' ? stateOrFactory() : stateOrFactory;
317
- const stateKeys = Object.keys(state);
339
+ const stateKeys = Reflect.ownKeys(state);
318
340
  assertUniqueStoreMembers(store, stateKeys);
319
- store[STATE_SOURCE].update((currentState) => freezeInDevMode({
341
+ store[STATE_SOURCE].update((currentState) => ({
320
342
  ...currentState,
321
343
  ...state,
322
344
  }));
@@ -335,5 +357,5 @@ function withState(stateOrFactory) {
335
357
  * Generated bundle index. Do not edit.
336
358
  */
337
359
 
338
- export { deepComputed, getState, patchState, signalMethod, signalState, signalStore, signalStoreFeature, type, watchState, withComputed, withHooks, withMethods, withProps, withState };
360
+ export { deepComputed, getState, isWritableStateSource, patchState, signalMethod, signalState, signalStore, signalStoreFeature, type, watchState, withComputed, withFeature, withHooks, withMethods, withProps, withState };
339
361
  //# sourceMappingURL=ngrx-signals.mjs.map