Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(store): expose keys which are used to store metadata privately #2054

Merged
merged 1 commit into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/logger-plugin/tests/helpers/setup-with-logger.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ErrorHandler } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { Store, provideStore } from '@ngxs/store';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';
import { NgxsLoggerPluginOptions, withNgxsLoggerPlugin } from '@ngxs/logger-plugin';

import { LoggerSpy } from './logger-spy';
import { NoopErrorHandler } from '../../../store/tests/helpers/utils';

export function setupWithLogger(states: StateClass[], opts?: NgxsLoggerPluginOptions) {
export function setupWithLogger(states: ɵStateClass[], opts?: NgxsLoggerPluginOptions) {
const logger = new LoggerSpy();

TestBed.configureTestingModule({
Expand Down
4 changes: 2 additions & 2 deletions packages/router-plugin/tests/issues/issue-1407.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Component, Injectable, NgModule } from '@angular/core';
import { Routes } from '@angular/router';
import { BrowserModule } from '@angular/platform-browser';
import { State, Action, StateContext, provideStore, provideStates } from '@ngxs/store';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';
import { freshPlatform } from '@ngxs/store/internals/testing';

import { Navigate, RouterNavigation, RouterState, withNgxsRouterPlugin } from '../..';
Expand Down Expand Up @@ -40,7 +40,7 @@ const routes: Routes = [
}
];

function getTestModule(states: StateClass[] = []) {
function getTestModule(states: ɵStateClass[] = []) {
@NgModule({
imports: [BrowserModule, RouterTestingModule.withRoutes(routes, { enableTracing: true })],
declarations: [RootComponent, HomeComponent, DialedNumberComponent],
Expand Down
12 changes: 5 additions & 7 deletions packages/storage-plugin/internals/src/storage-key.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { InjectionToken, Type } from '@angular/core';
import { StateToken } from '@ngxs/store';
import { StateClass } from '@ngxs/store/internals';
import { ɵMETA_OPTIONS_KEY, ɵStateClass } from '@ngxs/store/internals';

import { StorageEngine } from './symbols';

/** This enables the user to provide a storage engine per individual key. */
export interface KeyWithExplicitEngine {
key: string | StateClass | StateToken<any>;
key: string | ɵStateClass | StateToken<any>;
engine: Type<StorageEngine> | InjectionToken<StorageEngine>;
}

Expand All @@ -19,10 +19,8 @@ export function ɵisKeyWithExplicitEngine(key: any): key is KeyWithExplicitEngin
* This tuples all of the possible types allowed in the `key` property.
* This is not exposed publicly and used internally only.
*/
export type StorageKey = string | StateClass | StateToken<any> | KeyWithExplicitEngine;
export type StorageKey = string | ɵStateClass | StateToken<any> | KeyWithExplicitEngine;

/** This symbol is used to store the metadata on state classes. */
const META_OPTIONS_KEY = 'NGXS_OPTIONS_META';
export function ɵextractStringKey(storageKey: StorageKey): string {
// Extract the actual key out of the `{ key, engine }` structure.
if (ɵisKeyWithExplicitEngine(storageKey)) {
Expand All @@ -32,8 +30,8 @@ export function ɵextractStringKey(storageKey: StorageKey): string {
// Given the `storageKey` is a class, for instance, `AuthState`.
// We should retrieve its metadata and the `name` property.
// The `name` property might be a string (state name) or a state token.
if (storageKey.hasOwnProperty(META_OPTIONS_KEY)) {
storageKey = (storageKey as any)[META_OPTIONS_KEY].name;
if (storageKey.hasOwnProperty(ɵMETA_OPTIONS_KEY)) {
storageKey = (storageKey as any)[ɵMETA_OPTIONS_KEY].name;
}

return storageKey instanceof StateToken ? storageKey.getName() : <string>storageKey;
Expand Down
9 changes: 8 additions & 1 deletion packages/store/internals/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
export { NgxsBootstrapper } from './ngxs-bootstrapper';
export { memoize } from './memoize';
export { INITIAL_STATE_TOKEN, InitialState } from './initial-state';
export { PlainObjectOf, PlainObject, StateClass } from './symbols';
export {
PlainObjectOf,
PlainObject,
ɵStateClass,
ɵMETA_KEY,
ɵMETA_OPTIONS_KEY,
ɵSELECTOR_META_KEY
} from './symbols';
export { ɵNGXS_STATE_CONTEXT_FACTORY, ɵNGXS_STATE_FACTORY } from './internal-tokens';
13 changes: 12 additions & 1 deletion packages/store/internals/src/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ export interface PlainObjectOf<T> {
[key: string]: T;
}

export type StateClass<T = any> = new (...args: any[]) => T;
export type ɵStateClass<T = any> = new (...args: any[]) => T;

// This key is used to store metadata on state classes,
// such as actions and other related information.
export const ɵMETA_KEY = 'NGXS_META';
// This key is used to store options on state classes
// provided through the `@State` decorator.
export const ɵMETA_OPTIONS_KEY = 'NGXS_OPTIONS_META';
// This key is used to store selector metadata on selector functions,
// such as decorated with the `@Selector` or provided through the
// `createSelector` function.
export const ɵSELECTOR_META_KEY = 'NGXS_SELECTOR_META';
4 changes: 2 additions & 2 deletions packages/store/internals/testing/src/symbol.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { NgxsModuleOptions, Store } from '@ngxs/store';
import { ModuleWithProviders } from '@angular/core';
import { TestBedStatic } from '@angular/core/testing';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';

export interface NgxsOptionsTesting {
states?: StateClass[];
states?: ɵStateClass[];
ngxsOptions?: NgxsModuleOptions;
imports?: ModuleWithProviders<any>[];
before?: () => void;
Expand Down
14 changes: 7 additions & 7 deletions packages/store/src/decorators/state.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass, ɵMETA_KEY, ɵMETA_OPTIONS_KEY } from '@ngxs/store/internals';

import { StoreOptions } from '../symbols';
import { ensureStateNameIsValid } from '../utils/store-validators';
import { META_KEY, META_OPTIONS_KEY, StoreOptions } from '../symbols';
import { ensureStoreMetadata, MetaDataModel, StateClassInternal } from '../internal/internals';

interface MutateMetaOptions<T> {
Expand All @@ -14,7 +14,7 @@ interface MutateMetaOptions<T> {
* Decorates a class with ngxs state information.
*/
export function State<T>(options: StoreOptions<T>) {
return (target: StateClass): void => {
return (target: ɵStateClass): void => {
const stateClass: StateClassInternal = target;
const meta: MetaDataModel = ensureStoreMetadata(stateClass);
const inheritedStateClass: StateClassInternal = Object.getPrototypeOf(stateClass);
Expand All @@ -23,7 +23,7 @@ export function State<T>(options: StoreOptions<T>) {
options
);
mutateMetaData<T>({ meta, inheritedStateClass, optionsWithInheritance });
stateClass[META_OPTIONS_KEY] = optionsWithInheritance;
stateClass[ɵMETA_OPTIONS_KEY] = optionsWithInheritance;
};
}

Expand All @@ -32,7 +32,7 @@ function getStateOptions<T>(
options: StoreOptions<T>
): StoreOptions<T> {
const inheritanceOptions: Partial<StoreOptions<T>> =
inheritedStateClass[META_OPTIONS_KEY] || {};
inheritedStateClass[ɵMETA_OPTIONS_KEY] || {};
return { ...inheritanceOptions, ...options } as StoreOptions<T>;
}

Expand All @@ -46,8 +46,8 @@ function mutateMetaData<T>(params: MutateMetaOptions<T>): void {
ensureStateNameIsValid(stateName);
}

if (inheritedStateClass.hasOwnProperty(META_KEY)) {
const inheritedMeta: Partial<MetaDataModel> = inheritedStateClass[META_KEY] || {};
if (inheritedStateClass.hasOwnProperty(ɵMETA_KEY)) {
const inheritedMeta: Partial<MetaDataModel> = inheritedStateClass[ɵMETA_KEY] || {};
meta.actions = { ...meta.actions, ...inheritedMeta.actions };
}

Expand Down
40 changes: 20 additions & 20 deletions packages/store/src/internal/internals.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { PlainObjectOf, StateClass } from '@ngxs/store/internals';
import {
PlainObjectOf,
ɵStateClass,
ɵMETA_KEY,
ɵMETA_OPTIONS_KEY,
ɵSELECTOR_META_KEY
} from '@ngxs/store/internals';
import { Observable } from 'rxjs';

import {
META_KEY,
META_OPTIONS_KEY,
NgxsConfig,
SELECTOR_META_KEY,
StoreOptions
} from '../symbols';
import { NgxsConfig, StoreOptions } from '../symbols';
import { ActionHandlerMetaData } from '../actions/symbols';

// inspired from https://stackoverflow.com/a/43674389
export interface StateClassInternal<T = any, U = any> extends StateClass<T> {
[META_KEY]?: MetaDataModel;
[META_OPTIONS_KEY]?: StoreOptions<U>;
export interface StateClassInternal<T = any, U = any> extends ɵStateClass<T> {
[ɵMETA_KEY]?: MetaDataModel;
[ɵMETA_OPTIONS_KEY]?: StoreOptions<U>;
}

export type StateKeyGraph = PlainObjectOf<string[]>;
Expand Down Expand Up @@ -77,7 +77,7 @@ export interface StatesAndDefaults {
* @ignore
*/
export function ensureStoreMetadata(target: StateClassInternal): MetaDataModel {
if (!target.hasOwnProperty(META_KEY)) {
if (!target.hasOwnProperty(ɵMETA_KEY)) {
const defaultMetadata: MetaDataModel = {
name: null,
actions: {},
Expand All @@ -89,7 +89,7 @@ export function ensureStoreMetadata(target: StateClassInternal): MetaDataModel {
children: []
};

Object.defineProperty(target, META_KEY, { value: defaultMetadata });
Object.defineProperty(target, ɵMETA_KEY, { value: defaultMetadata });
}
return getStoreMetadata(target);
}
Expand All @@ -100,7 +100,7 @@ export function ensureStoreMetadata(target: StateClassInternal): MetaDataModel {
* @ignore
*/
export function getStoreMetadata(target: StateClassInternal): MetaDataModel {
return target[META_KEY]!;
return target[ɵMETA_KEY]!;
}

/**
Expand All @@ -109,7 +109,7 @@ export function getStoreMetadata(target: StateClassInternal): MetaDataModel {
* @ignore
*/
export function ensureSelectorMetadata(target: Function): SelectorMetaDataModel {
if (!target.hasOwnProperty(SELECTOR_META_KEY)) {
if (!target.hasOwnProperty(ɵSELECTOR_META_KEY)) {
const defaultMetadata: SelectorMetaDataModel = {
makeRootSelector: null,
originalFn: null,
Expand All @@ -118,7 +118,7 @@ export function ensureSelectorMetadata(target: Function): SelectorMetaDataModel
getSelectorOptions: () => ({})
};

Object.defineProperty(target, SELECTOR_META_KEY, { value: defaultMetadata });
Object.defineProperty(target, ɵSELECTOR_META_KEY, { value: defaultMetadata });
}

return getSelectorMetadata(target);
Expand All @@ -130,7 +130,7 @@ export function ensureSelectorMetadata(target: Function): SelectorMetaDataModel
* @ignore
*/
export function getSelectorMetadata(target: any): SelectorMetaDataModel {
return target[SELECTOR_META_KEY];
return target[ɵSELECTOR_META_KEY];
}

/**
Expand Down Expand Up @@ -216,12 +216,12 @@ export function buildGraph(stateClasses: StateClassInternal[]): StateKeyGraph {
);
}

return meta![META_KEY]!.name!;
return meta![ɵMETA_KEY]!.name!;
};

return stateClasses.reduce<StateKeyGraph>(
(result: StateKeyGraph, stateClass: StateClassInternal) => {
const { name, children } = stateClass[META_KEY]!;
const { name, children } = stateClass[ɵMETA_KEY]!;
result[name!] = (children || []).map(findName);
return result;
},
Expand All @@ -242,7 +242,7 @@ export function buildGraph(stateClasses: StateClassInternal[]): StateKeyGraph {
export function nameToState(states: StateClassInternal[]): PlainObjectOf<StateClassInternal> {
return states.reduce<PlainObjectOf<StateClassInternal>>(
(result: PlainObjectOf<StateClassInternal>, stateClass: StateClassInternal) => {
const meta = stateClass[META_KEY]!;
const meta = stateClass[ɵMETA_KEY]!;
result[meta.name!] = stateClass;
return result;
},
Expand Down
6 changes: 3 additions & 3 deletions packages/store/src/internal/state-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import {
shareReplay,
takeUntil
} from 'rxjs/operators';
import { INITIAL_STATE_TOKEN, PlainObjectOf, memoize } from '@ngxs/store/internals';
import { INITIAL_STATE_TOKEN, PlainObjectOf, memoize, ɵMETA_KEY } from '@ngxs/store/internals';

import { META_KEY, NgxsConfig } from '../symbols';
import { NgxsConfig } from '../symbols';
import {
buildGraph,
findFullParentPath,
Expand Down Expand Up @@ -174,7 +174,7 @@ export class StateFactory implements OnDestroy {
for (const name of sortedStates) {
const stateClass: StateClassInternal = nameGraph[name];
const path: string = paths[name];
const meta: MetaDataModel = stateClass[META_KEY]!;
const meta: MetaDataModel = stateClass[ɵMETA_KEY]!;

this.addRuntimeInfoToMeta(meta, path);

Expand Down
6 changes: 3 additions & 3 deletions packages/store/src/module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';

import { NgxsModuleOptions } from './symbols';
import { NgxsRootModule } from './modules/ngxs-root.module';
Expand All @@ -10,7 +10,7 @@ import { getFeatureProviders } from './standalone-features/feature-providers';
@NgModule()
export class NgxsModule {
static forRoot(
states: StateClass[] = [],
states: ɵStateClass[] = [],
options: NgxsModuleOptions = {}
): ModuleWithProviders<NgxsRootModule> {
return {
Expand All @@ -19,7 +19,7 @@ export class NgxsModule {
};
}

static forFeature(states: StateClass[] = []): ModuleWithProviders<NgxsFeatureModule> {
static forFeature(states: ɵStateClass[] = []): ModuleWithProviders<NgxsFeatureModule> {
return {
ngModule: NgxsFeatureModule,
providers: getFeatureProviders(states)
Expand Down
6 changes: 3 additions & 3 deletions packages/store/src/selectors/selector-types.util.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';

import { StateToken } from '../state-token/state-token';

export type SelectorFunc<TModel> = (...arg: any[]) => TModel;

export type TypedSelector<TModel> = StateToken<TModel> | SelectorFunc<TModel>;

export type StateSelector = StateClass<any>;
export type StateSelector = ɵStateClass<any>;

export type SelectorDef<TModel> = StateSelector | TypedSelector<TModel>;

export type SelectorReturnType<T extends SelectorDef<any>> = T extends StateToken<infer R1>
? R1
: T extends SelectorFunc<infer R2>
? R2
: T extends StateClass<any>
: T extends ɵStateClass<any>
? any /* (Block comment to stop prettier breaking the comment below)
// If the state selector is a class then we should infer its return type to `any`, and not to `unknown`.
// Since we'll get an error that `Type 'unknown' is not assignable to type 'AuthStateModel'.`
Expand Down
4 changes: 2 additions & 2 deletions packages/store/src/standalone-features/feature-providers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Provider } from '@angular/core';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';

import { FEATURE_STATE_TOKEN } from '../symbols';
import { PluginManager } from '../plugin-manager';
Expand All @@ -9,7 +9,7 @@ import { StateFactory } from '../internal/state-factory';
* This function provides the required providers when calling `NgxsModule.forFeature`
* or `provideStates`. It is shared between the NgModule and standalone APIs.
*/
export function getFeatureProviders(states: StateClass[]): Provider[] {
export function getFeatureProviders(states: ɵStateClass[]): Provider[] {
return [
StateFactory,
PluginManager,
Expand Down
4 changes: 2 additions & 2 deletions packages/store/src/standalone-features/provide-states.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';
import { StateClass } from '@ngxs/store/internals';
import { ɵStateClass } from '@ngxs/store/internals';

import { getFeatureProviders } from './feature-providers';
import { NGXS_FEATURE_ENVIRONMENT_INITIALIZER } from './initializers';
Expand All @@ -20,7 +20,7 @@ import { NGXS_FEATURE_ENVIRONMENT_INITIALIZER } from './initializers';
* ```
*/
export function provideStates(
states: StateClass[],
states: ɵStateClass[],
...features: EnvironmentProviders[]
): EnvironmentProviders {
return makeEnvironmentProviders([
Expand Down
Loading
Loading