├── .bundlemonrc.integration.json ├── .bundlemonrc.json ├── .bundlemonrc.plugins.json ├── .circleci └── config.yml ├── .codacy.yml ├── .codeclimate.yml ├── .commitlintrc.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── ---security-or-performance-issue-disclosure.md │ ├── --bug-report.md │ ├── --docs-or-ngxs-io-issue-report.md │ ├── --feature-request.md │ └── -support-request.md ├── PULL_REQUEST_TEMPLATE.md ├── actions │ ├── check-build-artifacts │ │ └── action.yml │ ├── download-integration-test-artifacts │ │ └── action.yml │ ├── setup │ │ └── action.yml │ └── upload-integration-test-artifact │ │ └── action.yml └── workflows │ ├── pr-validation.yml │ ├── release.yml │ └── trunk.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.mjs ├── .node-version ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── launch.json └── settings.json ├── .yarnrc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── book.json ├── build ├── collect-dts.mjs ├── copy-license.mjs ├── packages.mjs └── remove-dts.mjs ├── cypress.config.ts ├── cypress ├── e2e │ └── list-page.cy.ts ├── fixtures │ └── example.json ├── plugins │ └── index.js ├── ssr │ └── ssr.cy.ts ├── support │ ├── commands.js │ └── e2e.js └── tsconfig.json ├── docs ├── README.md ├── SUMMARY.md ├── advanced │ └── sub-states.md ├── assets │ ├── actions-fsm.drawio │ ├── actions-fsm.png │ ├── actions-life-cycle.drawio │ ├── actions-life-cycle.png │ ├── cli.gif │ ├── devtools.png │ ├── diagram.png │ ├── hmr.gif │ ├── logo-transparent.png │ ├── logo.png │ ├── ngxs-horizontal.svg │ ├── ngxs-labs.png │ ├── ngxs-logo_dark_theme.png │ ├── ngxs-logo_light_theme.png │ ├── ngxs-sentry-breadcrumbs.png │ ├── ngxs-socket-dfd.png │ ├── ngxs-vertical.svg │ ├── router.png │ └── sub-states.png ├── community-and-labs │ ├── community │ │ ├── README.md │ │ ├── contributing.md │ │ ├── contributors.md │ │ ├── developer-guide.md │ │ ├── faq.md │ │ ├── projects.md │ │ └── sponsors.md │ └── ngxs-labs │ │ └── README.md ├── concepts │ ├── actions │ │ ├── README.md │ │ ├── action-handlers.md │ │ ├── actions-life-cycle.md │ │ ├── cancellation.md │ │ ├── monitoring-unhandled-actions.md │ │ └── schematics.md │ ├── select │ │ ├── README.md │ │ ├── error-handling.md │ │ ├── mapped-sub-states.md │ │ ├── optimizing-selectors.md │ │ ├── select-decorator.md │ │ ├── selector-utils.md │ │ └── signals.md │ ├── state │ │ ├── README.md │ │ ├── composition.md │ │ ├── error-handling.md │ │ ├── immutability-helpers.md │ │ ├── lazy.md │ │ ├── life-cycle.md │ │ ├── operators-1.md │ │ ├── operators.md │ │ ├── schematics.md │ │ ├── shared-state.md │ │ ├── sub-states.md │ │ └── token.md │ └── store │ │ ├── README.md │ │ ├── error-handling.md │ │ ├── meta-reducer.md │ │ ├── options.md │ │ └── schematics.md ├── deprecations │ ├── README.md │ ├── inject-container-state-deprecation.md │ ├── select-decorator-deprecation.md │ └── sub-states-deprecation.md ├── introduction │ ├── installation.md │ ├── schematics.md │ ├── starter-kit.md │ └── why.md ├── ngxs │ └── intro.md ├── plugins │ ├── README.md │ ├── cli.md │ ├── devtools.md │ ├── form.md │ ├── hmr.md │ ├── logger.md │ ├── router.md │ ├── storage.md │ └── websocket.md ├── recipes │ ├── README.md │ ├── authentication.md │ ├── cache.md │ ├── component-events-from-ngxs.md │ ├── debouncing-actions.md │ ├── dynamic-plugins.md │ ├── integration-with-sentry.md │ ├── intregration-with-rxangular.md │ ├── module-federation.md │ ├── unit-testing.md │ └── zoneless-ssr.md └── style-guide.md ├── integration ├── .browserslistrc ├── app │ ├── app.component.html │ ├── app.component.ts │ ├── app.component[observable].spec.ts │ ├── app.component[signal].spec.ts │ ├── app.config.browser.ts │ ├── app.config.server.ts │ ├── app.config.ts │ ├── counter │ │ ├── counter.actions.ts │ │ ├── counter.component.html │ │ ├── counter.component.ts │ │ ├── counter.module.ts │ │ ├── counter.routes.ts │ │ └── counter.state.ts │ ├── detail │ │ ├── detail.component.html │ │ ├── detail.component.ts │ │ ├── detail.module.ts │ │ ├── detail.routes.ts │ │ └── detail.state.ts │ ├── list │ │ ├── list.component.html │ │ ├── list.component.ts │ │ ├── list.module.ts │ │ ├── list.resolver.ts │ │ ├── list.routes.ts │ │ └── list.state.ts │ └── store │ │ └── todos │ │ ├── todo │ │ ├── todo.actions.ts │ │ └── todo.state.ts │ │ ├── todos.actions.ts │ │ ├── todos.model.ts │ │ └── todos.state.ts ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── jest.config.js ├── main.browser.ts ├── main.server.ts ├── main.ts ├── polyfills.ts ├── project.json ├── server.ts ├── styles.scss ├── test-setup.ts ├── tsconfig.app.json ├── tsconfig.editor.json ├── tsconfig.json ├── tsconfig.server.json ├── tsconfig.spec.json └── typings.d.ts ├── integrations └── hello-world-ng19 │ ├── .gitignore │ ├── angular.json │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ └── index-page.cy.ts │ ├── fixtures │ │ └── example.json │ ├── plugins │ │ └── index.js │ └── tsconfig.json │ ├── karma.conf.js │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── app │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── standalone.spec.ts │ │ └── store │ │ │ ├── counter │ │ │ ├── counter.actions.ts │ │ │ └── counter.state.ts │ │ │ ├── index.ts │ │ │ └── ngxs-providers.ts │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── yarn.lock ├── jest.config.js ├── jest.preset.js ├── nx.json ├── package.json ├── packages ├── devtools-plugin │ ├── README.md │ ├── index.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── devtools.module.ts │ │ ├── devtools.plugin.ts │ │ ├── public_api.ts │ │ └── symbols.ts │ ├── test-setup.ts │ ├── tests │ │ ├── devtools-custom.spec.ts │ │ ├── devtools-errors.spec.ts │ │ ├── devtools.spec.ts │ │ └── utils │ │ │ ├── create-devtools.ts │ │ │ ├── redux-connector.ts │ │ │ └── symbols.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── form-plugin │ ├── README.md │ ├── index.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── actions.ts │ │ ├── directive.ts │ │ ├── form.module.ts │ │ ├── form.plugin.ts │ │ └── public_api.ts │ ├── test-setup.ts │ ├── tests │ │ ├── form.plugin.spec.ts │ │ ├── issues │ │ │ ├── issue-1567-ngxs-onchanges-hook-with-plugin.spec.ts │ │ │ ├── issue-1590-update-form-value-primitive.spec.ts │ │ │ └── issue-1822-multiple-ngxs-form-bindings.spec.ts │ │ └── property-path.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── hmr-plugin │ ├── README.md │ ├── index.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── README.md │ │ ├── actions │ │ │ ├── hmr-before-destroy.action.ts │ │ │ └── hmr-init.action.ts │ │ ├── hmr-bootstrap.ts │ │ ├── hmr-manager.ts │ │ ├── internal │ │ │ ├── hmr-lifecycle.ts │ │ │ ├── hmr-options-builder.ts │ │ │ ├── hmr-state-context-factory.ts │ │ │ └── hmr-storage.ts │ │ ├── public_api.ts │ │ ├── symbols.ts │ │ ├── tests │ │ │ ├── hmr-helpers.ts │ │ │ ├── hmr-manager.spec.ts │ │ │ └── hmr-mock.ts │ │ └── utils │ │ │ ├── externals.ts │ │ │ └── internals.ts │ ├── test-setup.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── logger-plugin │ ├── README.md │ ├── index.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── action-logger.ts │ │ ├── internals.ts │ │ ├── log-writer.ts │ │ ├── logger.module.ts │ │ ├── logger.plugin.ts │ │ ├── public_api.ts │ │ └── symbols.ts │ ├── test-setup.ts │ ├── tests │ │ ├── helpers │ │ │ ├── index.ts │ │ │ ├── logger-spy.ts │ │ │ ├── setup-with-logger.ts │ │ │ ├── symbols.ts │ │ │ └── utils.ts │ │ ├── issues │ │ │ ├── filter-function-injection-context.spec.ts │ │ │ └── issue-1551.spec.ts │ │ └── logger.plugin.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── router-plugin │ ├── README.md │ ├── index.ts │ ├── internals │ │ ├── ng-package.json │ │ └── src │ │ │ ├── index.ts │ │ │ └── symbols.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ │ ├── public_api.ts │ │ ├── router.actions.ts │ │ ├── router.module.ts │ │ ├── router.state.ts │ │ └── serializer.ts │ ├── test-setup.ts │ ├── tests │ │ ├── helpers.ts │ │ ├── issues │ │ │ ├── issue-1293.spec.ts │ │ │ ├── issue-1407.spec.ts │ │ │ ├── issue-1640-devtools-time-travel.spec.ts │ │ │ └── issue-1718-url-recognition.spec.ts │ │ ├── router-cancel.spec.ts │ │ ├── router-data-resolved.spec.ts │ │ ├── router-navigation.spec.ts │ │ ├── router-state-cleanup.spec.ts │ │ └── router.plugin.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── storage-plugin │ ├── README.md │ ├── index.ts │ ├── internals │ │ ├── ng-package.json │ │ └── src │ │ │ ├── index.ts │ │ │ ├── storage-key.ts │ │ │ └── symbols.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── schematics │ │ ├── collection.json │ │ └── src │ │ │ ├── _testing │ │ │ ├── index.ts │ │ │ └── schematics.ts │ │ │ ├── ng-generate │ │ │ ├── __snapshots__ │ │ │ │ └── keys-migration.spec.ts.snap │ │ │ ├── index.ts │ │ │ ├── keys-migration.spec.ts │ │ │ └── schema.json │ │ │ └── utils │ │ │ ├── versions.json │ │ │ └── visit-files.ts │ ├── src │ │ ├── engines.ts │ │ ├── internals.ts │ │ ├── keys-manager.ts │ │ ├── public_api.ts │ │ ├── storage.module.ts │ │ ├── storage.plugin.ts │ │ └── with-storage-feature.ts │ ├── test-setup.ts │ ├── tests │ │ ├── issues │ │ │ ├── issue-1146-invalid-rehydration.spec.ts │ │ │ ├── issue-1634-resolve-state-name.spec.ts │ │ │ ├── issue-1857-update-for-lazy-state.spec.ts │ │ │ ├── issue-1916-key-state-notation.spec.ts │ │ │ ├── issue-598-storage-engine-per-key.spec.ts │ │ │ └── issue-restore-state-only-if-key-matches.spec.ts │ │ ├── storage-for-feature.spec.ts │ │ └── storage.plugin.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.schematics.json │ └── tsconfig.spec.json ├── store │ ├── README.md │ ├── experimental │ │ ├── ng-package.json │ │ └── src │ │ │ └── index.ts │ ├── index.ts │ ├── internals │ │ ├── ng-package.json │ │ ├── src │ │ │ ├── action-registry.ts │ │ │ ├── custom-rxjs-operators.ts │ │ │ ├── custom-rxjs-subjects.ts │ │ │ ├── index.ts │ │ │ ├── initial-state.ts │ │ │ ├── internal-tokens.ts │ │ │ ├── memoize.ts │ │ │ ├── metadata.ts │ │ │ ├── ngxs-app-bootstrapped-state.ts │ │ │ ├── object-utils.ts │ │ │ ├── state-stream.ts │ │ │ ├── state-token.ts │ │ │ └── symbols.ts │ │ └── testing │ │ │ ├── ng-package.json │ │ │ ├── src │ │ │ ├── action-collector.ts │ │ │ ├── fresh-platform.ts │ │ │ ├── helpers │ │ │ │ ├── ngxs-test.component.ts │ │ │ │ └── ngxs-test.module.ts │ │ │ ├── index.ts │ │ │ ├── ngxs.setup.ts │ │ │ ├── skip-console-logging.ts │ │ │ └── symbol.ts │ │ │ └── tests │ │ │ └── ngxs.setup.spec.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── operators │ │ ├── ng-package.json │ │ ├── src │ │ │ ├── append.ts │ │ │ ├── compose.ts │ │ │ ├── iif.ts │ │ │ ├── index.ts │ │ │ ├── insert-item.ts │ │ │ ├── patch.ts │ │ │ ├── remove-item.ts │ │ │ ├── types.ts │ │ │ ├── update-item.ts │ │ │ └── utils.ts │ │ └── tests │ │ │ ├── append.spec.ts │ │ │ ├── compose.spec.ts │ │ │ ├── iif.spec.ts │ │ │ ├── insert-item.spec.ts │ │ │ ├── patch.spec.ts │ │ │ ├── remove-item.spec.ts │ │ │ └── update-item.spec.ts │ ├── package.json │ ├── plugins │ │ ├── ng-package.json │ │ └── src │ │ │ ├── actions.ts │ │ │ ├── index.ts │ │ │ ├── symbols.ts │ │ │ └── utils.ts │ ├── project.json │ ├── schematics │ │ ├── collection.json │ │ └── src │ │ │ ├── _testing │ │ │ ├── index.ts │ │ │ └── schematics.ts │ │ │ ├── actions │ │ │ ├── actions.factory.spec.ts │ │ │ ├── actions.factory.ts │ │ │ ├── actions.schema.d.ts │ │ │ ├── files │ │ │ │ └── __name__.actions.ts__template__ │ │ │ └── schema.json │ │ │ ├── ng-add │ │ │ ├── add-declaration.ts │ │ │ ├── ng-add.factory.spec.ts │ │ │ ├── ng-add.factory.ts │ │ │ ├── ng-add.schema.d.ts │ │ │ └── schema.json │ │ │ ├── starter-kit │ │ │ ├── files │ │ │ │ └── store │ │ │ │ │ ├── auth │ │ │ │ │ ├── auth.actions.ts__template__ │ │ │ │ │ ├── auth.state.spec.ts__template__ │ │ │ │ │ └── auth.state.ts__template__ │ │ │ │ │ ├── dashboard │ │ │ │ │ ├── index.ts__template__ │ │ │ │ │ └── states │ │ │ │ │ │ ├── dictionary │ │ │ │ │ │ ├── dictionary.actions.ts__template__ │ │ │ │ │ │ ├── dictionary.state.spec.ts__template__ │ │ │ │ │ │ └── dictionary.state.ts__template__ │ │ │ │ │ │ └── user │ │ │ │ │ │ ├── user.actions.ts__template__ │ │ │ │ │ │ ├── user.state.spec.ts__template__ │ │ │ │ │ │ └── user.state.ts__template__ │ │ │ │ │ ├── store.config.ts__template__ │ │ │ │ │ └── store.module.ts__template__ │ │ │ ├── schema.json │ │ │ ├── starter-kit.factory.spec.ts │ │ │ ├── starter-kit.factory.ts │ │ │ └── starter-kit.schema.d.ts │ │ │ ├── state │ │ │ ├── files │ │ │ │ ├── __name__.state.spec.ts__template__ │ │ │ │ └── __name__.state.ts__template__ │ │ │ ├── schema.json │ │ │ ├── state.factory.spec.ts │ │ │ ├── state.factory.ts │ │ │ └── state.schema.d.ts │ │ │ ├── store │ │ │ ├── files │ │ │ │ ├── __name__.actions.ts__template__ │ │ │ │ ├── __name__.state.spec.ts__template__ │ │ │ │ └── __name__.state.ts__template__ │ │ │ ├── schema.json │ │ │ ├── store.factory.spec.ts │ │ │ ├── store.factory.ts │ │ │ └── store.schema.d.ts │ │ │ └── utils │ │ │ ├── common │ │ │ ├── lib.config.ts │ │ │ ├── project-files.config.ts │ │ │ └── properties.ts │ │ │ ├── config.ts │ │ │ ├── generate-utils.ts │ │ │ ├── interfaces │ │ │ └── package.interface.ts │ │ │ ├── normalize-options.spec.ts │ │ │ ├── normalize-options.ts │ │ │ ├── project.ts │ │ │ └── versions.json │ ├── src │ │ ├── actions-stream.ts │ │ ├── actions │ │ │ └── symbols.ts │ │ ├── configs │ │ │ └── messages.config.ts │ │ ├── decorators │ │ │ ├── action.ts │ │ │ ├── select │ │ │ │ ├── select-factory.ts │ │ │ │ ├── select.ts │ │ │ │ └── symbols.ts │ │ │ ├── selector-options.ts │ │ │ ├── selector │ │ │ │ ├── selector.ts │ │ │ │ └── symbols.ts │ │ │ └── state.ts │ │ ├── dev-features │ │ │ ├── ngxs-development.module.ts │ │ │ ├── ngxs-unhandled-actions-logger.ts │ │ │ └── symbols.ts │ │ ├── execution │ │ │ └── execution-strategy.ts │ │ ├── internal │ │ │ ├── action-results.ts │ │ │ ├── dispatcher.ts │ │ │ ├── fallback-subscriber.ts │ │ │ ├── internals.ts │ │ │ ├── lifecycle-state-manager.ts │ │ │ ├── provide-internal-tokens.ts │ │ │ ├── state-context-factory.ts │ │ │ ├── state-factory.ts │ │ │ ├── state-operations.ts │ │ │ ├── state-operators.ts │ │ │ └── unhandled-rxjs-error-callback.ts │ │ ├── ivy │ │ │ └── ivy-enabled-in-dev-mode.ts │ │ ├── module.ts │ │ ├── modules │ │ │ ├── ngxs-feature.module.ts │ │ │ └── ngxs-root.module.ts │ │ ├── ngxs-unhandled-error-handler.ts │ │ ├── operators │ │ │ ├── leave-ngxs.ts │ │ │ └── of-action.ts │ │ ├── pending-tasks.ts │ │ ├── plugin-manager.ts │ │ ├── plugin_api.ts │ │ ├── private_api.ts │ │ ├── public_api.ts │ │ ├── selectors │ │ │ ├── create-model-selector.ts │ │ │ ├── create-pick-selector.ts │ │ │ ├── create-property-selectors.ts │ │ │ ├── create-selector.ts │ │ │ ├── index.ts │ │ │ ├── private_api.ts │ │ │ ├── selector-checks.util.ts │ │ │ ├── selector-metadata.ts │ │ │ ├── selector-models.ts │ │ │ ├── selector-types.util.ts │ │ │ └── selector-utils.ts │ │ ├── standalone-features │ │ │ ├── feature-providers.ts │ │ │ ├── index.ts │ │ │ ├── initializers.ts │ │ │ ├── plugin.ts │ │ │ ├── preboot.ts │ │ │ ├── provide-states.ts │ │ │ ├── provide-store.ts │ │ │ ├── root-guard.ts │ │ │ └── root-providers.ts │ │ ├── store.ts │ │ ├── symbols.ts │ │ └── utils │ │ │ ├── create-dispatch-map.ts │ │ │ ├── create-select-map.ts │ │ │ ├── dispatch.ts │ │ │ ├── freeze.ts │ │ │ ├── lazy-provider.ts │ │ │ ├── public_api.ts │ │ │ ├── select.ts │ │ │ └── store-validators.ts │ ├── test-setup.ts │ ├── tests │ │ ├── __snapshots__ │ │ │ └── preboot-wait-stable.spec.ts.snap │ │ ├── action-handler.spec.ts │ │ ├── action.spec.ts │ │ ├── actions-stream.spec.ts │ │ ├── create-maps.spec.ts │ │ ├── dev-mode.spec.ts │ │ ├── dispatch.spec.ts │ │ ├── ensure-store.spec.ts │ │ ├── execution │ │ │ └── default-execution-strategy.spec.ts │ │ ├── helpers │ │ │ ├── counter.state.ts │ │ │ ├── macrotask.ts │ │ │ ├── make-rxjs-timeout-provider-synchronous.ts │ │ │ ├── promise-test-helper.ts │ │ │ ├── simple.state.ts │ │ │ ├── todo.state.ts │ │ │ └── utils.ts │ │ ├── inheritance.spec.ts │ │ ├── internals.spec.ts │ │ ├── issues │ │ │ ├── actions-stream-causing-ticks.spec.ts │ │ │ ├── canceling-promises.spec.ts │ │ │ ├── dispatch-with-nullable-arguments.spec.ts │ │ │ ├── issue-1568-return-empty.spec.ts │ │ │ ├── issue-1603-no-type-property-on-the-action.spec.ts │ │ │ ├── issue-1687-inject-store-inside-error-handler.spec.ts │ │ │ ├── issue-1691-error-handling.spec.ts │ │ │ ├── issue-1728-dispatch-from-select-subscriber.spec.ts │ │ │ ├── issue-1880-last-select-value.spec.ts │ │ │ ├── issue-1976-select-once-after-dispatch.spec.ts │ │ │ ├── issue-2180-dispatching-in-effect.spec.ts │ │ │ ├── issue-2227-feature-plugins.spec.ts │ │ │ ├── issue-2229-memoize-nan.spec.ts │ │ │ ├── issue-2277-provide-store-twice.spec.ts │ │ │ ├── issue-759-dispatching-empty.spec.ts │ │ │ ├── issue-933-selectors-causing-ticks.spec.ts │ │ │ ├── pending-task-notifications.spec.ts │ │ │ ├── state-initialization-order.spec.ts │ │ │ └── state-stream-order.spec.ts │ │ ├── lazy-load-late-init.spec.ts │ │ ├── lazy-load.spec.ts │ │ ├── module.spec.ts │ │ ├── ngxs-onchanges.spec.ts │ │ ├── plugins.spec.ts │ │ ├── preboot-wait-stable.spec.ts │ │ ├── release-resources.spec.ts │ │ ├── select-signal.spec.ts │ │ ├── select.spec.ts │ │ ├── selector.spec.ts │ │ ├── selectors │ │ │ ├── create-model-selector.spec.ts │ │ │ ├── create-pick-selector.spec.ts │ │ │ └── create-property-selectors.spec.ts │ │ ├── standalone-features │ │ │ └── standalone-feature.spec.ts │ │ ├── state-token.spec.ts │ │ ├── state.spec.ts │ │ ├── store-isolation.spec.ts │ │ ├── store-validator.spec.ts │ │ ├── store.spec.ts │ │ ├── unhandled-actions.spec.ts │ │ ├── utils │ │ │ ├── fixtures │ │ │ │ └── lazy-provider-default-export.ts │ │ │ ├── lazy-provider.spec.ts │ │ │ └── utils.spec.ts │ │ └── zone.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.schematics.json │ ├── tsconfig.spec.json │ └── types │ │ ├── .eslintrc.js │ │ ├── index.d.ts │ │ ├── tests │ │ ├── action.lint.ts │ │ ├── create-dispatch-map.lint.ts │ │ ├── create-select-map.lint.ts │ │ ├── create-selector.lint.ts │ │ ├── dispatch.lint.ts │ │ ├── playground.ts │ │ ├── selection.lint.ts │ │ ├── selector-options.lint.ts │ │ ├── state-operator.lint.ts │ │ ├── state-operators.append.lint.ts │ │ ├── state-operators.compose.lint.ts │ │ ├── state-operators.iif.lint.ts │ │ ├── state-operators.insert-item.lint.ts │ │ ├── state-operators.remove-item.lint.ts │ │ ├── state-operators.update-item.lint.ts │ │ ├── state-token.lint.ts │ │ └── utils │ │ │ └── assert-type.ts │ │ └── tsconfig.json └── websocket-plugin │ ├── README.md │ ├── index.ts │ ├── jest.config.js │ ├── ng-package.json │ ├── package.json │ ├── project.json │ ├── src │ ├── private_api.ts │ ├── providers.ts │ ├── public_api.ts │ ├── symbols.ts │ ├── websocket-handler.ts │ └── websocket.module.ts │ ├── test-setup.ts │ ├── tests │ ├── typings.d.ts │ ├── utils.ts │ ├── websocket-handler-cleanup.spec.ts │ └── websocket.module.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── publication ├── 2019-12-11_announcing_ngxs_3_6 │ └── article.md ├── 2020-09-09_announcing_ngxs_3_7 │ └── article.md ├── 2023-03-20_announcing_ngxs_3_8 │ └── article.md ├── 2024-06-10_announcing_ngxs_18 │ └── article.md ├── announcement-tweet.txt ├── announcement_template.md └── announcing_ngxs_next │ └── article.md ├── renovate.json ├── rollup.config.mjs ├── setup-jest-preset.ts ├── tools ├── build-schematics.mjs ├── merge-reports.js ├── publish-dev-builds.ts ├── publish-tagged-builds.ts ├── set-metadata.ts └── utils.ts ├── tsconfig.base.json ├── tsconfig.build.json ├── tsconfig.lib.json ├── tsconfig.spec.json ├── tutorials └── create-app │ ├── .eslintrc.json │ ├── README.md │ ├── astro.config.ts │ ├── env.d.ts │ ├── package.json │ ├── project.json │ ├── public │ ├── favicon.svg │ ├── logo-dark.svg │ └── logo.svg │ ├── src │ ├── content │ │ ├── config.ts │ │ └── tutorial │ │ │ ├── 1-create-a-todo-app │ │ │ ├── 1-welcome │ │ │ │ ├── 1-welcome │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── todo-list │ │ │ │ │ │ │ └── todo-list.component.ts │ │ │ │ │ └── content.md │ │ │ │ └── meta.md │ │ │ ├── 2-installation │ │ │ │ ├── 1-using-schematics │ │ │ │ │ ├── _files │ │ │ │ │ │ └── package.json │ │ │ │ │ └── content.md │ │ │ │ ├── 2-create-store │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ ├── app.config.ts │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ ├── store │ │ │ │ │ │ │ ├── todo.actions.ts │ │ │ │ │ │ │ ├── todo.state.spec.ts │ │ │ │ │ │ │ └── todo.state.ts │ │ │ │ │ │ │ └── todo-list │ │ │ │ │ │ │ ├── todo-list.component.html │ │ │ │ │ │ │ └── todo-list.component.ts │ │ │ │ │ ├── _solution │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── app.config.ts │ │ │ │ │ └── content.md │ │ │ │ └── meta.md │ │ │ ├── 3-get-todo-items │ │ │ │ ├── 1-create-action │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ └── todo.actions.ts │ │ │ │ │ ├── _solution │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ └── todo.actions.ts │ │ │ │ │ └── content.md │ │ │ │ ├── 2-handle-the-action │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ ├── todo.actions.ts │ │ │ │ │ │ │ └── todo.state.ts │ │ │ │ │ ├── _solution │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ └── todo.state.ts │ │ │ │ │ └── content.md │ │ │ │ ├── 3-create-a-query │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ └── todo.queries.ts │ │ │ │ │ ├── _solution │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── store │ │ │ │ │ │ │ └── todo.queries.ts │ │ │ │ │ └── content.md │ │ │ │ ├── 4-select-query-from-component │ │ │ │ │ ├── _files │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── todo-list │ │ │ │ │ │ │ └── todo-list.component.ts │ │ │ │ │ ├── _solution │ │ │ │ │ │ └── src │ │ │ │ │ │ │ └── app │ │ │ │ │ │ │ └── todo │ │ │ │ │ │ │ └── todo-list │ │ │ │ │ │ │ └── todo-list.component.ts │ │ │ │ │ └── content.md │ │ │ │ └── meta.md │ │ │ └── meta.md │ │ │ └── meta.md │ ├── env.d.ts │ └── templates │ │ └── ngxs │ │ ├── angular.json │ │ ├── javascript.svg │ │ ├── package.json │ │ ├── src │ │ ├── app │ │ │ ├── app.component.ts │ │ │ ├── app.config.ts │ │ │ └── todo │ │ │ │ ├── components │ │ │ │ └── todo-item │ │ │ │ │ └── todo-item.component.ts │ │ │ │ ├── store │ │ │ │ ├── todo.actions.ts │ │ │ │ ├── todo.queries.ts │ │ │ │ ├── todo.state.spec.ts │ │ │ │ └── todo.state.ts │ │ │ │ ├── todo-list │ │ │ │ ├── todo-list.component.html │ │ │ │ ├── todo-list.component.scss │ │ │ │ ├── todo-list.component.spec.ts │ │ │ │ └── todo-list.component.ts │ │ │ │ ├── todo.service.ts │ │ │ │ └── types │ │ │ │ └── todo.ts │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.scss │ │ ├── tsconfig.app.json │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── uno.config.ts │ └── yarn.lock ├── yarn.lock └── yarn.lock.readme.md /.bundlemonrc.integration.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseDir": "./integrations/", 3 | "subProject": "Integration Projects", 4 | "defaultCompression": "none", 5 | "includeCommitMessage": true, 6 | "files": [ 7 | { 8 | "friendlyName": "Main bundles", 9 | "path": "./**/dist-integration/**/main{-,.}.js", 10 | "maxPercentIncrease": 1 11 | }, 12 | { 13 | "friendlyName": "Main bundles(Gzip)", 14 | "path": "./**/dist-integration/**/main{-,.}.js", 15 | "compression": "gzip", 16 | "maxPercentIncrease": 1 17 | } 18 | ], 19 | "reportOutput": [ 20 | [ 21 | "github", 22 | { 23 | "checkRun": false, 24 | "commitStatus": true, 25 | "prComment": true 26 | } 27 | ] 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.bundlemonrc.plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseDir": "./@ngxs/", 3 | "subProject": "NGXS Plugins", 4 | "defaultCompression": "none", 5 | "includeCommitMessage": true, 6 | "files": [ 7 | { 8 | "friendlyName": "Plugins(fesm2022)", 9 | "path": "./*-plugin/fesm2022/*.mjs", 10 | "maxPercentIncrease": 1 11 | }, 12 | { 13 | "friendlyName": "Plugins(fesm2022)[gzip]", 14 | "path": "./*-plugin/fesm2022/*.mjs", 15 | "maxPercentIncrease": 0.5, 16 | "compression": "gzip" 17 | } 18 | ], 19 | "groups": [ 20 | { 21 | "friendlyName": "All Plugins(fesm2022)", 22 | "path": "./*-plugin/fesm2022/*.mjs", 23 | "maxPercentIncrease": 1 24 | }, 25 | { 26 | "friendlyName": "All Plugins(fesm2022)[gzip]", 27 | "path": "./*-plugin/fesm2022/*.mjs", 28 | "maxPercentIncrease": 0.5, 29 | "compression": "gzip" 30 | } 31 | ], 32 | "reportOutput": [ 33 | [ 34 | "github", 35 | { 36 | "checkRun": false, 37 | "commitStatus": true, 38 | "prComment": true 39 | } 40 | ] 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - '**/*.md' 4 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | plugins: 3 | nodesecurity: 4 | enabled: true 5 | checks: 6 | method-complexity: 7 | config: 8 | threshold: 20 9 | method-lines: 10 | config: 11 | threshold: 50 12 | exclude_patterns: 13 | - '.github/' 14 | - 'assets/' 15 | - 'docs/' 16 | - 'packages/**/tests/' 17 | - 'packages/store/schematics/src/utils/ng-utils/' 18 | - 'integrations/' 19 | - 'integration/' 20 | - 'tools/' 21 | - 'tutorials/' 22 | ratings: 23 | paths: 24 | - 'packages/**/**.ts' 25 | -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.spec.ts 2 | packages/store/types/.eslintrc.js 3 | node_modules/ 4 | tutorials/create-app/src/env.d.ts 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | parser: '@typescript-eslint/parser', 6 | extends: ['plugin:@typescript-eslint/recommended'], 7 | rules: { 8 | '@typescript-eslint/indent': 'off', 9 | '@typescript-eslint/ban-types': 'off', 10 | '@typescript-eslint/no-namespace': 'off', 11 | '@typescript-eslint/no-var-requires': 'off', 12 | '@typescript-eslint/no-unused-vars': 'off', 13 | '@typescript-eslint/no-explicit-any': 'off', 14 | '@typescript-eslint/no-empty-function': 'off', 15 | '@typescript-eslint/no-use-before-define': 'off', 16 | '@typescript-eslint/no-non-null-assertion': 'off', 17 | '@typescript-eslint/member-delimiter-style': 'off', 18 | '@typescript-eslint/no-parameter-properties': 'off', 19 | '@typescript-eslint/explicit-member-accessibility': 'off', 20 | '@typescript-eslint/explicit-function-return-type': 'off', 21 | '@typescript-eslint/no-angle-bracket-type-assertion': 'off', 22 | '@typescript-eslint/no-object-literal-type-assertion': 'off' 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @markwhitfeld @arturovt @kuncevic @Carniatto @poloagustin @joaqcid @splincode 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/--feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680Feature request" 3 | about: Suggest a feature for NGXS 4 | title: "\U0001F680[FEATURE]: " 5 | labels: FEATURE 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Relevant Package 11 | 12 | This feature request is for @ngxs/.... 13 | 14 | 15 | ### Description 16 | A clear and concise description of the problem or missing capability... 17 | 18 | 19 | ### Describe the problem you are trying to solve 20 | Please describe the problem. If possible please substantiate it with the use cases you want to address. 21 | 22 | 23 | ### Describe the solution you'd like 24 | If you have a solution in mind, please describe it. 25 | 26 | 27 | ### Describe alternatives you've considered 28 | Have you considered any alternative solutions or workarounds? 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/-support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "❓Support request" 3 | about: Questions and requests for support 4 | title: "❓[SUPPORT]: " 5 | labels: discussion, good first issue, investigate, not an issue 6 | assignees: '' 7 | 8 | --- 9 | 10 | 🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑 11 | 12 | Please do not file questions or support requests on the GitHub issues tracker. 13 | 14 | You can get your questions answered using other communication channels. Please see: 15 | 16 | Slack: https://ngxs.slack.com 17 | Stackoverflow: https://stackoverflow.com/questions/tagged/ngxs 18 | Twitter: https://twitter.com/NgxsStore 19 | Reddit: https://www.reddit.com/r/NGXS/ 20 | 21 | 22 | Thank you! 23 | 24 | 🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑 25 | -------------------------------------------------------------------------------- /.github/actions/download-integration-test-artifacts/action.yml: -------------------------------------------------------------------------------- 1 | # For more information see: 2 | # - https://docs.github.com/en/actions/creating-actions/creating-a-composite-action 3 | # - https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputsoutput_id 4 | 5 | # The action can be referenced in workflows like: 6 | # - ngxs/store/.github/actions/download-integration-test-artifacts@master 7 | # - ./.github/actions/download-integration-test-artifacts 8 | 9 | name: download-integration-test-artifact 10 | description: Downloads all integration test artifacts with names such as 'hello-world-*'. 11 | 12 | inputs: 13 | path: 14 | description: A path to download the artifacts. 15 | required: true 16 | default: './integrations' 17 | 18 | runs: 19 | using: 'composite' 20 | steps: 21 | - name: Download hello-world-* artifacts 22 | uses: actions/download-artifact@v4 23 | with: 24 | pattern: hello-world-* 25 | path: ${{ inputs.path }} 26 | merge-multiple: true 27 | 28 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | '**/*.{ts,js,tsx,jsx,mjs,json,md,mdx,scss,html,yml}': f => 3 | `yarn nx format:write --files=${f.join(',')}`, 4 | '**/*.{ts,tsx,js,jsx}': 'eslint' 5 | }; 6 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 18.20.3 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.lint.ts 2 | **/node_modules/** 3 | .github/** 4 | .idea/** 5 | .circleci/** 6 | .cache/** 7 | .vscode/** 8 | dist*/** 9 | coverage/** 10 | @ngxs/** 11 | **/dist/** 12 | 13 | /.nx/cache 14 | 15 | /.nx/workspace-data 16 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 95, 4 | "tabWidth": 2, 5 | "arrowParens": "avoid", 6 | "trailingComma": "none" 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "typescript.tsdk": "node_modules/typescript/lib" 4 | } 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Deprecating registry.yarnpkg.com 2 | # Issue: https://github.com/yarnpkg/yarn/issues/5891 3 | --registry "https://registry.npmjs.org/" 4 | --scripts-prepend-node-path true 5 | --ignore-engines true 6 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "3.x.x", 3 | "title": "NGXS", 4 | "root": "/docs", 5 | "plugins": ["highlight", "github", "include-codeblock", "versions", "anchors", "youtube"], 6 | "pluginsConfig": { 7 | "github": { 8 | "url": "https://github.com/ngxs/store/" 9 | }, 10 | "versions": { 11 | "type": "branches" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /build/copy-license.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import url from 'node:url'; 3 | import path from 'node:path'; 4 | 5 | import { packages } from './packages.mjs'; 6 | 7 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 8 | const licensePath = path.join(__dirname, '../LICENSE'); 9 | 10 | for (const package$ of packages) { 11 | const destinationPath = path.join(__dirname, '../@ngxs', package$, 'LICENSE'); 12 | fs.copyFileSync(licensePath, destinationPath); 13 | console.log(`Copied LICENSE in ${destinationPath}.`); 14 | } 15 | -------------------------------------------------------------------------------- /build/packages.mjs: -------------------------------------------------------------------------------- 1 | export const packages = [ 2 | 'store', 3 | 'devtools-plugin', 4 | 'form-plugin', 5 | 'logger-plugin', 6 | 'router-plugin', 7 | 'storage-plugin', 8 | 'websocket-plugin' 9 | ]; 10 | -------------------------------------------------------------------------------- /build/remove-dts.mjs: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs'; 2 | 3 | import { getEntryPointsAndDtsToRemove } from './collect-dts.mjs'; 4 | 5 | console.log('Preparing to remove unnecessary `.d.ts` files now that we have flattened them.'); 6 | 7 | for (const dts of getEntryPointsAndDtsToRemove().dtsToRemove) { 8 | fs.unlinkSync(dts); 9 | } 10 | 11 | console.log('Successfully removed unnecessary `.d.ts` files.'); 12 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | const isSsr = process.env.SSR === 'true'; 4 | 5 | export default defineConfig({ 6 | video: false, 7 | screenshotOnRunFailure: false, 8 | responseTimeout: 60000, 9 | pageLoadTimeout: 120000, 10 | e2e: { 11 | supportFile: false, 12 | // We've imported your old cypress plugins here. 13 | // You may want to clean this up later by importing these. 14 | setupNodeEvents(on, config) { 15 | return require('./cypress/plugins/index.js')(on, config); 16 | }, 17 | baseUrl: 'http://localhost:4200', 18 | excludeSpecPattern: ['**/plugins/**.js', '**/tsconfig.json'], 19 | specPattern: isSsr ? './cypress/ssr/*.ts' : './cypress/e2e/*.ts' 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /cypress/e2e/list-page.cy.ts: -------------------------------------------------------------------------------- 1 | describe('List page', () => { 2 | beforeEach(() => cy.visit('/list')); 3 | 4 | it('should contain form with "h3" title', () => { 5 | cy.get('.todo-list h3') 6 | .first() 7 | .invoke('text') 8 | .should(text => expect(text).to.equal('Reactive Form')); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | }; 18 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": false, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "types": ["node", "cypress"], 7 | "typeRoots": ["../node_modules/@types"], 8 | "lib": ["es2017", "dom"] 9 | }, 10 | "include": ["e2e/*.ts", "ssr/*.ts", "support/*.ts", "../node_modules/cypress"] 11 | } 12 | -------------------------------------------------------------------------------- /docs/advanced/sub-states.md: -------------------------------------------------------------------------------- 1 | # Sub States 2 | 3 | Read the deprecation notice at [this link](../recipes/sub-states-deprecation.md). 4 | -------------------------------------------------------------------------------- /docs/assets/actions-fsm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/actions-fsm.png -------------------------------------------------------------------------------- /docs/assets/actions-life-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/actions-life-cycle.png -------------------------------------------------------------------------------- /docs/assets/cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/cli.gif -------------------------------------------------------------------------------- /docs/assets/devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/devtools.png -------------------------------------------------------------------------------- /docs/assets/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/diagram.png -------------------------------------------------------------------------------- /docs/assets/hmr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/hmr.gif -------------------------------------------------------------------------------- /docs/assets/logo-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/logo-transparent.png -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/assets/ngxs-horizontal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/ngxs-labs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/ngxs-labs.png -------------------------------------------------------------------------------- /docs/assets/ngxs-logo_dark_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/ngxs-logo_dark_theme.png -------------------------------------------------------------------------------- /docs/assets/ngxs-logo_light_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/ngxs-logo_light_theme.png -------------------------------------------------------------------------------- /docs/assets/ngxs-sentry-breadcrumbs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/ngxs-sentry-breadcrumbs.png -------------------------------------------------------------------------------- /docs/assets/ngxs-socket-dfd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/ngxs-socket-dfd.png -------------------------------------------------------------------------------- /docs/assets/ngxs-vertical.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/router.png -------------------------------------------------------------------------------- /docs/assets/sub-states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/docs/assets/sub-states.png -------------------------------------------------------------------------------- /docs/community-and-labs/community/README.md: -------------------------------------------------------------------------------- 1 | # Community 2 | -------------------------------------------------------------------------------- /docs/community-and-labs/community/contributors.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | Thanks to all our [contributors](https://github.com/ngxs/store/graphs/contributors)! 4 | 5 | ![](https://opencollective.com/ngxs/contributors.svg?width=890) 6 | -------------------------------------------------------------------------------- /docs/concepts/select/select-decorator.md: -------------------------------------------------------------------------------- 1 | # Select Decorator 2 | 3 | {% hint style="danger" %} 4 | **DEPRECATED** 5 | 6 | [Find out why we are deprecating the select decorator](../../deprecations/select-decorator-deprecation.md) 7 | {% endhint %} 8 | 9 | You can select slices of data from the store using the `@Select` decorator. It has a few different ways to get your data out, whether passing the state class, a function, a different state class or a memoized selector. 10 | 11 | ```ts 12 | import { Select } from '@ngxs/store'; 13 | import { ZooState, ZooStateModel } from './zoo.state'; 14 | 15 | @Component({ ... }) 16 | export class ZooComponent { 17 | // Reads the name of the state from the state class 18 | @Select(ZooState) animals$: Observable; 19 | 20 | // Uses the pandas memoized selector to only return pandas 21 | @Select(ZooState.pandas) pandas$: Observable; 22 | 23 | // Also accepts a function like our select method 24 | @Select(state => state.zoo.animals) animals$: Observable; 25 | 26 | // Reads the name of the state from the parameter 27 | @Select() zoo$: Observable; 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/concepts/state/composition.md: -------------------------------------------------------------------------------- 1 | # Composition 2 | 3 | You can compose multiple stores together using class inheritance. This is quite simple: 4 | 5 | ```ts 6 | @State({ 7 | name: 'zoo', 8 | defaults: { 9 | type: null 10 | } 11 | }) 12 | @Injectable() 13 | class ZooState { 14 | @Action(Eat) 15 | eat(ctx: StateContext) { 16 | ctx.setState({ type: 'eat' }); 17 | } 18 | } 19 | 20 | @State({ 21 | name: 'stlzoo' 22 | }) 23 | @Injectable() 24 | class StLouisZooState extends ZooState { 25 | @Action(Drink) 26 | drink(ctx: StateContext) { 27 | ctx.setState({ type: 'drink' }); 28 | } 29 | } 30 | ``` 31 | 32 | Now when `StLouisZooState` is invoked, it will share the actions of the `ZooState`. 33 | Also all state options are inherited. 34 | -------------------------------------------------------------------------------- /docs/concepts/state/error-handling.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | -------------------------------------------------------------------------------- /docs/deprecations/README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATIONS 2 | -------------------------------------------------------------------------------- /docs/introduction/schematics.md: -------------------------------------------------------------------------------- 1 | # Schematics 2 | 3 | This page lists all the different schematics that can be used to generate NGXS Starter-Kit, Store, Actions and State. 4 | 5 | ## Starter Kit 6 | 7 | The Starter Kit provides a pre-configured NGXS setup that includes a Store, State, Actions, and selectors. 8 | 9 | See the [Starter Kit Page](starter-kit.md) to learn more. 10 | 11 | ## Store 12 | 13 | The store is a global state manager that dispatches actions your state containers listen to and provides a way to select data slices out from the global state. 14 | 15 | See the [Store Schematics Page](../concepts/store/schematics.md) to learn more. 16 | 17 | ## Actions 18 | 19 | Actions can either be thought of as a command which should trigger something to happen, or as the resulting event of something that has already happened. 20 | 21 | See the [Action Schematics Page](../concepts/actions/schematics.md) to learn more. 22 | 23 | ## State 24 | 25 | States are classes that define a state container. 26 | 27 | See the [State Schematics Page](../concepts/state/schematics.md) to learn more. 28 | -------------------------------------------------------------------------------- /docs/ngxs/intro.md: -------------------------------------------------------------------------------- 1 | ## Concepts 2 | 3 | There are 4 major concepts to NGXS: 4 | 5 | - Store: Global state container, action dispatcher and selector 6 | - Actions: Class describing the action to take and its associated metadata 7 | - State: Class definition of the state 8 | - Selects: State slice selectors 9 | 10 | These concepts create a circular control flow traveling from a component 11 | dispatching an action, to a store reacting to the action, back to the component 12 | through a state select. 13 | 14 |

15 | 16 |

17 | -------------------------------------------------------------------------------- /docs/recipes/README.md: -------------------------------------------------------------------------------- 1 | # Recipes 2 | -------------------------------------------------------------------------------- /integration/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | 3 | # For additional information regarding the format and rule options, please see: 4 | 5 | # https://github.com/browserslist/browserslist#queries 6 | 7 | # You can see what browsers were selected by your queries by running: 8 | 9 | # npx browserslist 10 | 11 | last 2 Chrome versions 12 | -------------------------------------------------------------------------------- /integration/app/app.config.browser.ts: -------------------------------------------------------------------------------- 1 | import { mergeApplicationConfig } from '@angular/core'; 2 | import { provideAnimations } from '@angular/platform-browser/animations'; 3 | 4 | import { appConfig } from './app.config'; 5 | 6 | export const appBrowserConfig = mergeApplicationConfig(appConfig, { 7 | providers: [provideAnimations()] 8 | }); 9 | -------------------------------------------------------------------------------- /integration/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | import { mergeApplicationConfig } from '@angular/core'; 2 | import { provideServerRendering } from '@angular/platform-server'; 3 | 4 | import { appConfig } from './app.config'; 5 | 6 | export const appServerConfig = mergeApplicationConfig(appConfig, { 7 | providers: [provideServerRendering()] 8 | }); 9 | -------------------------------------------------------------------------------- /integration/app/counter/counter.actions.ts: -------------------------------------------------------------------------------- 1 | export class CounterStateChangeAction { 2 | static readonly type = '[Counter] Change'; 3 | } 4 | -------------------------------------------------------------------------------- /integration/app/counter/counter.component.html: -------------------------------------------------------------------------------- 1 |

(counter$) {{ counter$ | async | json }}

2 |

(counter() signal) {{ counter() | json }}

3 | -------------------------------------------------------------------------------- /integration/app/counter/counter.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component, OnInit, Signal } from '@angular/core'; 3 | import { Store } from '@ngxs/store'; 4 | import { Observable } from 'rxjs'; 5 | 6 | import { CounterStateChangeAction } from '@integration/counter/counter.actions'; 7 | import { CounterState, CounterStateModel } from '@integration/counter/counter.state'; 8 | 9 | @Component({ 10 | selector: 'counter', 11 | templateUrl: './counter.component.html', 12 | imports: [CommonModule] 13 | }) 14 | export class CounterComponent implements OnInit { 15 | counter$: Observable = this._store.select(CounterState.getCounterState); 16 | counter: Signal = this._store.selectSignal(CounterState.getCounterState); 17 | 18 | constructor(private _store: Store) {} 19 | 20 | ngOnInit() { 21 | this._store.dispatch(new CounterStateChangeAction()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /integration/app/counter/counter.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | 5 | import { routes } from '@integration/counter/counter.routes'; 6 | import { NgxsModule } from '@ngxs/store'; 7 | import { CounterState } from '@integration/counter/counter.state'; 8 | 9 | @NgModule({ 10 | imports: [CommonModule, RouterModule.forChild(routes), NgxsModule.forFeature([CounterState])] 11 | }) 12 | export class CounterModule {} 13 | -------------------------------------------------------------------------------- /integration/app/counter/counter.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { CounterComponent } from '@integration/counter/counter.component'; 3 | 4 | export const routes: Routes = [ 5 | { 6 | path: '', 7 | component: CounterComponent 8 | } 9 | ]; 10 | -------------------------------------------------------------------------------- /integration/app/detail/detail.component.html: -------------------------------------------------------------------------------- 1 |

(detail$) {{ detail$ | async | json }}

2 |

(detail() signal) {{ detail() | json }}

3 | -------------------------------------------------------------------------------- /integration/app/detail/detail.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component, Signal } from '@angular/core'; 3 | import { Store } from '@ngxs/store'; 4 | import { Observable } from 'rxjs'; 5 | 6 | import { DetailState, DetailStateModel } from '@integration/detail/detail.state'; 7 | 8 | @Component({ 9 | selector: 'app-detail', 10 | templateUrl: './detail.component.html', 11 | imports: [CommonModule] 12 | }) 13 | export class DetailComponent { 14 | detail$: Observable = this._store.select(DetailState.getDetailState); 15 | detail: Signal = this._store.selectSignal(DetailState.getDetailState); 16 | 17 | constructor(private _store: Store) {} 18 | } 19 | -------------------------------------------------------------------------------- /integration/app/detail/detail.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { NgxsModule } from '@ngxs/store'; 5 | 6 | import { DetailState } from '@integration/detail/detail.state'; 7 | import { routes } from '@integration/detail/detail.routes'; 8 | 9 | @NgModule({ 10 | imports: [CommonModule, RouterModule.forChild(routes), NgxsModule.forFeature([DetailState])] 11 | }) 12 | export class DetailModule {} 13 | -------------------------------------------------------------------------------- /integration/app/detail/detail.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { DetailComponent } from '@integration/detail/detail.component'; 3 | 4 | export const routes: Routes = [ 5 | { 6 | path: '', 7 | component: DetailComponent 8 | } 9 | ]; 10 | -------------------------------------------------------------------------------- /integration/app/detail/detail.state.ts: -------------------------------------------------------------------------------- 1 | import { Selector, State } from '@ngxs/store'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | export interface DetailStateModel { 5 | foo: boolean; 6 | } 7 | 8 | @State({ 9 | name: 'detail', 10 | defaults: { foo: true } 11 | }) 12 | @Injectable() 13 | export class DetailState { 14 | @Selector() 15 | static getDetailState(state: DetailStateModel) { 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /integration/app/list/list.component.html: -------------------------------------------------------------------------------- 1 |
2 |

(list$) {{ list$ | async }}

3 |

(list() signal) {{ list() }}

4 |
5 | 6 |
7 |

(hello$) {{ hello$ | async }}

8 |

(hello() signal) {{ hello() }}

9 |
10 | 11 |
12 | (router$) animals were resolved {{ router.root!.firstChild!.firstChild!.data.list }} 13 |
14 | 15 |
16 | (router() signal) animals were resolved {{ router.root!.firstChild!.firstChild!.data.list }} 17 |
18 | -------------------------------------------------------------------------------- /integration/app/list/list.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { Component, Signal } from '@angular/core'; 3 | import { RouterStateSnapshot } from '@angular/router'; 4 | import { Store } from '@ngxs/store'; 5 | import { RouterState } from '@ngxs/router-plugin'; 6 | import { Observable } from 'rxjs'; 7 | 8 | import { ListState } from '@integration/list/list.state'; 9 | 10 | @Component({ 11 | selector: 'app-list', 12 | templateUrl: './list.component.html', 13 | imports: [CommonModule] 14 | }) 15 | export class ListComponent { 16 | list$: Observable = this._store.select(ListState.getListState); 17 | list: Signal = this._store.selectSignal(ListState.getListState); 18 | 19 | hello$ = this._store.select(ListState.getHello); 20 | hello = this._store.selectSignal(ListState.getHello); 21 | 22 | router$ = this._store.select(RouterState.state()); 23 | router = this._store.selectSignal(RouterState.state()); 24 | 25 | constructor(private _store: Store) {} 26 | } 27 | -------------------------------------------------------------------------------- /integration/app/list/list.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RouterModule } from '@angular/router'; 4 | import { NgxsModule } from '@ngxs/store'; 5 | 6 | import { ListState } from '@integration/list/list.state'; 7 | import { routes } from '@integration/list/list.routes'; 8 | import { ListResolver } from '@integration/list/list.resolver'; 9 | 10 | @NgModule({ 11 | imports: [CommonModule, RouterModule.forChild(routes), NgxsModule.forFeature([ListState])], 12 | providers: [ListResolver] 13 | }) 14 | export class ListModule {} 15 | -------------------------------------------------------------------------------- /integration/app/list/list.resolver.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Resolve } from '@angular/router'; 3 | 4 | @Injectable() 5 | export class ListResolver implements Resolve { 6 | async resolve(): Promise { 7 | return ['zebras', 'pandas', 'lions', 'giraffes']; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /integration/app/list/list.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { ListComponent } from '@integration/list/list.component'; 3 | import { ListResolver } from '@integration/list/list.resolver'; 4 | 5 | export const routes: Routes = [ 6 | { 7 | path: '', 8 | component: ListComponent, 9 | resolve: { 10 | list: ListResolver 11 | } 12 | } 13 | ]; 14 | -------------------------------------------------------------------------------- /integration/app/list/list.state.ts: -------------------------------------------------------------------------------- 1 | import { State, Selector, NgxsOnInit, NgxsAfterBootstrap, StateContext } from '@ngxs/store'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | @State({ 5 | name: 'list', 6 | defaults: ['foo'] 7 | }) 8 | @Injectable() 9 | export class ListState implements NgxsOnInit, NgxsAfterBootstrap { 10 | @Selector() 11 | static getListState(state: string[]) { 12 | return state; 13 | } 14 | 15 | @Selector() 16 | static getHello(): string { 17 | return 'hello'; 18 | } 19 | 20 | ngxsOnInit({ setState, getState }: StateContext): void { 21 | setState([...getState(), 'NgxsOnInit lazy']); 22 | } 23 | 24 | ngxsAfterBootstrap({ setState, getState }: StateContext): void { 25 | setState([...getState(), 'NgxsAfterBootstrap lazy']); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /integration/app/store/todos/todo/todo.actions.ts: -------------------------------------------------------------------------------- 1 | import { Todo } from '@integration/store/todos/todos.model'; 2 | 3 | export class AddTodo { 4 | static type = 'AddTodo'; 5 | constructor(readonly payload: Todo) {} 6 | } 7 | 8 | export class RemoveTodo { 9 | static type = 'RemoveTodo'; 10 | constructor(readonly payload: number) {} 11 | } 12 | -------------------------------------------------------------------------------- /integration/app/store/todos/todos.actions.ts: -------------------------------------------------------------------------------- 1 | export class SetPrefix { 2 | static type = 'SetPrefix'; 3 | } 4 | 5 | export class LoadData { 6 | static type = 'LoadData'; 7 | } 8 | -------------------------------------------------------------------------------- /integration/app/store/todos/todos.model.ts: -------------------------------------------------------------------------------- 1 | export const TODOS_STORAGE_KEY = 'todos.todo'; 2 | export type Todo = string; 3 | 4 | export interface Pizza { 5 | model: T; 6 | } 7 | 8 | export class TodoStateModel { 9 | public pizza: Pizza; 10 | } 11 | 12 | export interface Extras { 13 | name: string; 14 | selected: boolean; 15 | } 16 | -------------------------------------------------------------------------------- /integration/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /integration/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false 3 | }; 4 | -------------------------------------------------------------------------------- /integration/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/integration/favicon.ico -------------------------------------------------------------------------------- /integration/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NGXS 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /integration/main.browser.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppBrowserModule } from './app/app.browser.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppBrowserModule); 12 | -------------------------------------------------------------------------------- /integration/main.server.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | 3 | import { AppComponent } from './app/app.component'; 4 | import { appServerConfig } from './app/app.config.server'; 5 | 6 | const bootstrap = () => bootstrapApplication(AppComponent, appServerConfig); 7 | export default bootstrap; 8 | -------------------------------------------------------------------------------- /integration/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { bootstrapApplication } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app/app.component'; 5 | import { appBrowserConfig } from './app/app.config.browser'; 6 | import { environment } from './environments/environment'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | bootstrapApplication(AppComponent, appBrowserConfig); 13 | -------------------------------------------------------------------------------- /integration/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js'; 2 | -------------------------------------------------------------------------------- /integration/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /integration/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "module": "esnext", 6 | "target": "es2022", 7 | "paths": { 8 | "@ngxs/*": ["../@ngxs/*"], 9 | "@integration/*": ["./app/*"] 10 | } 11 | }, 12 | "files": ["main.ts", "polyfills.ts"], 13 | "angularCompilerOptions": { 14 | "disableTypeScriptVersionCheck": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /integration/tsconfig.editor.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["**/*.ts"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "types": ["jest", "node"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /integration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.app.json" 8 | }, 9 | { 10 | "path": "./tsconfig.server.json" 11 | }, 12 | { 13 | "path": "./tsconfig.spec.json" 14 | }, 15 | { 16 | "path": "./tsconfig.editor.json" 17 | } 18 | ], 19 | "compilerOptions": { 20 | "forceConsistentCasingInFileNames": true, 21 | "strict": true, 22 | "noImplicitReturns": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "noUnusedLocals": false, 25 | "skipLibCheck": true 26 | }, 27 | "angularCompilerOptions": { 28 | "strictInjectionParameters": true, 29 | "strictInputAccessModifiers": true, 30 | "strictTemplates": true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /integration/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "paths": { 6 | "@ngxs/*": ["../@ngxs/*"], 7 | "@integration/*": ["./app/*"] 8 | }, 9 | "target": "es2016", 10 | "module": "es2020", 11 | "types": ["node"], 12 | "module": "esnext", 13 | "typeRoots": ["../node_modules/@types"] 14 | }, 15 | "files": ["main.server.ts", "server.ts"], 16 | "exclude": ["cypress"], 17 | "angularCompilerOptions": { 18 | "disableTypeScriptVersionCheck": true, 19 | "entryModule": "app/app.server.module#AppServerModule" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /integration/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "paths": { 6 | "@ngxs/*": ["../@ngxs/*"], 7 | "@integration/*": ["./app/*"] 8 | }, 9 | "module": "commonjs", 10 | "target": "es5", 11 | "types": ["jest", "node"] 12 | }, 13 | "files": ["polyfills.ts"], 14 | "include": ["**/*.spec.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /integration/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface NodeModule { 2 | [key: string]: any; 3 | } 4 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | video: false, 5 | responseTimeout: 60000, 6 | pageLoadTimeout: 120000, 7 | e2e: { 8 | supportFile: false, 9 | // We've imported your old cypress plugins here. 10 | // You may want to clean this up later by importing these. 11 | setupNodeEvents(on, config) { 12 | return require('./cypress/plugins/index.js')(on, config); 13 | }, 14 | baseUrl: 'http://localhost:4200', 15 | excludeSpecPattern: ['**/plugins/**.js', '**/tsconfig.json'] 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/cypress/e2e/index-page.cy.ts: -------------------------------------------------------------------------------- 1 | describe('Index page', () => { 2 | beforeEach(() => cy.visit('/')); 3 | 4 | it('should render application using the latest version', () => { 5 | // Arrange & act & assert 6 | // Expect that the running applicaiton was compiled 7 | // with the necessary Angular version! 8 | cy.get('app-root').invoke('attr', 'ng-version').should('have.string', '19'); 9 | }); 10 | 11 | it('should click on the button and increase the counter', () => { 12 | // Arrange & act & assert 13 | cy.get('button').click().click().click().get('p').should('contain.text', 'Counter is 3'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | }; 18 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": false, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "types": ["node", "cypress"], 7 | "typeRoots": ["../node_modules/@types"], 8 | "lib": ["es2017", "dom"] 9 | }, 10 | "include": ["e2e/*.ts", "support/*.ts", "../node_modules/cypress"] 11 | } 12 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/integrations/hello-world-ng19/public/favicon.ico -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

Angular 19 Integration Test

2 | 3 | 4 |

Counter is {{ counter() }}

5 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { provideNgxs } from './store'; 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | const setup = async () => { 8 | await TestBed.configureTestingModule({ 9 | imports: [AppComponent], 10 | providers: [provideNgxs()] 11 | }).compileComponents(); 12 | 13 | const fixture = TestBed.createComponent(AppComponent); 14 | const component = fixture.componentInstance; 15 | 16 | return { fixture, component }; 17 | }; 18 | 19 | it('should create the app', async () => { 20 | const { component } = await setup(); 21 | expect(component).toBeTruthy(); 22 | }); 23 | 24 | it('should render title', async () => { 25 | const { fixture } = await setup(); 26 | fixture.detectChanges(); 27 | const compiled = fixture.nativeElement; 28 | expect(compiled.querySelector('h1').textContent).toContain('Angular 19 Integration Test'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { Store } from '@ngxs/store'; 3 | import { COUNTER_STATE_TOKEN, Increment } from './store'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | standalone: true 9 | }) 10 | export class AppComponent { 11 | private store = inject(Store); 12 | 13 | readonly counter = this.store.selectSignal(COUNTER_STATE_TOKEN); 14 | 15 | increment(): void { 16 | this.store.dispatch(new Increment()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideExperimentalZonelessChangeDetection } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { provideNgxs } from './store'; 5 | 6 | export const appConfig = { 7 | providers: [provideExperimentalZonelessChangeDetection(), provideRouter([]), provideNgxs()] 8 | }; 9 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/store/counter/counter.actions.ts: -------------------------------------------------------------------------------- 1 | export class Increment { 2 | static readonly type = '[Counter] Increment'; 3 | } 4 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/store/counter/counter.state.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { State, Action, StateContext, StateToken } from '@ngxs/store'; 3 | 4 | import { Increment } from './counter.actions'; 5 | 6 | export const COUNTER_STATE_TOKEN = new StateToken('counter'); 7 | 8 | @State({ 9 | name: COUNTER_STATE_TOKEN, 10 | defaults: 0 11 | }) 12 | @Injectable() 13 | export class CounterState { 14 | @Action(Increment) 15 | increment(ctx: StateContext) { 16 | ctx.setState(counter => (counter += 1)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './counter/counter.state'; 2 | export * from './counter/counter.actions'; 3 | 4 | export * from './ngxs-providers'; 5 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/app/store/ngxs-providers.ts: -------------------------------------------------------------------------------- 1 | import { provideStore } from '@ngxs/store'; 2 | import { withNgxsReduxDevtoolsPlugin } from '@ngxs/devtools-plugin'; 3 | import { withNgxsFormPlugin } from '@ngxs/form-plugin'; 4 | import { withNgxsLoggerPlugin } from '@ngxs/logger-plugin'; 5 | import { withNgxsStoragePlugin } from '@ngxs/storage-plugin'; 6 | import { withNgxsWebSocketPlugin } from '@ngxs/websocket-plugin'; 7 | import { withNgxsRouterPlugin } from '@ngxs/router-plugin'; 8 | 9 | import { CounterState } from './counter/counter.state'; 10 | 11 | declare const ngDevMode: boolean; 12 | 13 | export function provideNgxs() { 14 | return provideStore( 15 | [CounterState], 16 | withNgxsReduxDevtoolsPlugin({ 17 | disabled: typeof ngDevMode !== 'undefined' && !ngDevMode 18 | }), 19 | withNgxsFormPlugin(), 20 | withNgxsLoggerPlugin({ 21 | disabled: typeof ngDevMode !== 'undefined' && !ngDevMode 22 | }), 23 | withNgxsStoragePlugin({ keys: '*' }), 24 | withNgxsWebSocketPlugin(), 25 | withNgxsRouterPlugin() 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ng19Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig); 6 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "./out-tsc/app", 7 | "types": [] 8 | }, 9 | "files": ["src/main.ts"], 10 | "include": ["src/**/*.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "compileOnSave": false, 5 | "compilerOptions": { 6 | "outDir": "./dist/out-tsc", 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "isolatedModules": true, 14 | "esModuleInterop": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "bundler", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022" 20 | }, 21 | "angularCompilerOptions": { 22 | "enableI18nLegacyMessageIdFormat": false, 23 | "strictInjectionParameters": true, 24 | "strictInputAccessModifiers": true, 25 | "strictTemplates": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /integrations/hello-world-ng19/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { getJestProjects } = require('@nx/jest'); 2 | 3 | globalThis.ngJest = { 4 | skipNgcc: true 5 | }; 6 | 7 | module.exports = { 8 | projects: getJestProjects() 9 | }; 10 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nx/jest/preset').default; 2 | const { pathsToModuleNameMapper } = require('ts-jest'); 3 | 4 | const { compilerOptions } = require('./tsconfig.base.json'); 5 | 6 | module.exports = { 7 | ...nxPreset, 8 | testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'], 9 | transform: { 10 | '^.+.(ts|mjs|js|html)$': 'jest-preset-angular' 11 | }, 12 | testPathIgnorePatterns: ['/node_modules/', '/types/', '/helpers/'], 13 | transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], 14 | resolver: '@nx/jest/plugins/resolver', 15 | moduleFileExtensions: ['ts', 'js', 'html'], 16 | coverageReporters: ['json', 'lcovonly', 'lcov', 'text', 'html'], 17 | moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { 18 | prefix: process.cwd() 19 | }) 20 | }; 21 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "targetDefaults": { 3 | "build": { 4 | "dependsOn": ["^build"], 5 | "cache": true 6 | }, 7 | "build-package": { 8 | "cache": true 9 | }, 10 | "@nx/jest:jest": { 11 | "cache": true, 12 | "inputs": ["default", "^default", "{workspaceRoot}/jest.preset.js"], 13 | "options": { 14 | "passWithNoTests": true 15 | }, 16 | "configurations": { 17 | "ci": { 18 | "ci": true, 19 | "codeCoverage": true 20 | } 21 | } 22 | }, 23 | "@nx/eslint:lint": { 24 | "cache": true, 25 | "inputs": [ 26 | "default", 27 | "{workspaceRoot}/.eslintrc.js", 28 | "{workspaceRoot}/tools/eslint-rules/**/*" 29 | ] 30 | } 31 | }, 32 | "workspaceLayout": { 33 | "libsDir": "packages", 34 | "appsDir": "packages" 35 | }, 36 | "nxCloudAccessToken": "MWFlODQ3YjEtZGIzOC00OTJmLWE1NTYtMDcyZmNhYjU0NmU4fHJlYWQtd3JpdGU=", 37 | "parallel": 1, 38 | "useInferencePlugins": false, 39 | "defaultBase": "master" 40 | } 41 | -------------------------------------------------------------------------------- /packages/devtools-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/devtools-plugin 2 | 3 | Devtools plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/devtools-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/devtools-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/devtools-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'devtools-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/devtools-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/devtools-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/devtools-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/devtools-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/devtools-plugin", 3 | "description": "redux devtools plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "rxjs": ">=6.5.5" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/devtools-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devtools-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/devtools-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/devtools-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/devtools-plugin/ng-package.json" 13 | } 14 | }, 15 | "test": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/devtools-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/devtools-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsReduxDevtoolsPluginModule, withNgxsReduxDevtoolsPlugin } from './devtools.module'; 2 | export { NgxsReduxDevtoolsPlugin } from './devtools.plugin'; 3 | export * from './symbols'; 4 | -------------------------------------------------------------------------------- /packages/devtools-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/devtools-plugin/tests/utils/create-devtools.ts: -------------------------------------------------------------------------------- 1 | import { NgxsDevtoolsOptions } from '@ngxs/devtools-plugin'; 2 | import { ɵdefineProperty } from '@ngxs/store/internals'; 3 | 4 | import { ReduxDevtoolsMockConnector } from './redux-connector'; 5 | 6 | export function createReduxDevtoolsExtension(connector: ReduxDevtoolsMockConnector): void { 7 | ɵdefineProperty(window, '__REDUX_DEVTOOLS_EXTENSION__', { 8 | writable: true, 9 | configurable: true, 10 | value: { 11 | connect(options: NgxsDevtoolsOptions): ReduxDevtoolsMockConnector { 12 | connector.options = options; 13 | return connector; 14 | }, 15 | disconnect() {} 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /packages/devtools-plugin/tests/utils/symbols.ts: -------------------------------------------------------------------------------- 1 | export interface MockState { 2 | [key: string]: any; 3 | } 4 | 5 | export interface DevtoolsCallStack { 6 | id: number; 7 | type: string; 8 | payload: any; 9 | state: MockState; 10 | newState: MockState; 11 | jumped: boolean; 12 | } 13 | -------------------------------------------------------------------------------- /packages/devtools-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/devtools-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/devtools-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/form-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/form-plugin 2 | 3 | Forms plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/form-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/form-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/form-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'form-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/form-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/form-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/form-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/form-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/form-plugin", 3 | "description": "form plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "@angular/forms": ">=19.0.0 <20.0.0", 10 | "rxjs": ">=6.5.5" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/form-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "form-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/form-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/form-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/form-plugin/ng-package.json" 13 | } 14 | }, 15 | "test": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/form-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/form-plugin/src/form.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ModuleWithProviders, EnvironmentProviders } from '@angular/core'; 2 | import { withNgxsPlugin } from '@ngxs/store'; 3 | 4 | import { NgxsFormPlugin } from './form.plugin'; 5 | import { NgxsFormDirective } from './directive'; 6 | 7 | @NgModule({ 8 | imports: [NgxsFormDirective], 9 | exports: [NgxsFormDirective] 10 | }) 11 | export class NgxsFormPluginModule { 12 | static forRoot(): ModuleWithProviders { 13 | return { 14 | ngModule: NgxsFormPluginModule, 15 | providers: [withNgxsPlugin(NgxsFormPlugin)] 16 | }; 17 | } 18 | } 19 | 20 | export function withNgxsFormPlugin(): EnvironmentProviders { 21 | return withNgxsPlugin(NgxsFormPlugin); 22 | } 23 | -------------------------------------------------------------------------------- /packages/form-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsFormPluginModule, withNgxsFormPlugin } from './form.module'; 2 | export { NgxsFormPlugin } from './form.plugin'; 3 | export { NgxsFormDirective } from './directive'; 4 | export * from './actions'; 5 | -------------------------------------------------------------------------------- /packages/form-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/form-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/form-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/form-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/hmr-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/hmr-plugin 2 | 3 | HMR plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/hmr-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/hmr-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/hmr-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'hmr-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | stringifyContentPathRegex: '\\.(html|svg)$' 9 | } 10 | }, 11 | coverageDirectory: '../../coverage/packages/hmr-plugin', 12 | transform: { 13 | '^.+\\.(ts|js|html)$': 'jest-preset-angular' 14 | }, 15 | snapshotSerializers: [ 16 | 'jest-preset-angular/build/serializers/no-ng-attributes', 17 | 'jest-preset-angular/build/serializers/ng-snapshot', 18 | 'jest-preset-angular/build/serializers/html-comment' 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /packages/hmr-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/hmr-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/hmr-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/hmr-plugin", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "sideEffects": true, 6 | "peerDependencies": { 7 | "@angular/core": ">=19.0.0 <20.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/hmr-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hmr-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/hmr-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/hmr-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/hmr-plugin/ng-package.json" 13 | } 14 | }, 15 | "xtest": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/hmr-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/hmr-plugin 2 | 3 | HMR plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/actions/hmr-before-destroy.action.ts: -------------------------------------------------------------------------------- 1 | import { NgxsHmrSnapshot } from '../symbols'; 2 | 3 | export class HmrBeforeDestroyAction { 4 | static get type() { 5 | // NOTE: Not necessary to declare the type in this way in your code. See https://github.com/ngxs/store/pull/644#issuecomment-436003138 6 | return '@@HMR_BEFORE_DESTROY'; 7 | } 8 | 9 | constructor(public payload: Partial) {} 10 | } 11 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/actions/hmr-init.action.ts: -------------------------------------------------------------------------------- 1 | import { NgxsHmrSnapshot } from '../symbols'; 2 | 3 | export class HmrInitAction { 4 | static get type() { 5 | // NOTE: Not necessary to declare the type in this way in your code. See https://github.com/ngxs/store/pull/644#issuecomment-436003138 6 | return '@@HMR_INIT'; 7 | } 8 | 9 | constructor(public payload: Partial) {} 10 | } 11 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/internal/hmr-options-builder.ts: -------------------------------------------------------------------------------- 1 | import { NgxsHmrOptions } from '../symbols'; 2 | 3 | export class HmrOptionBuilder { 4 | public readonly deferTime: number; 5 | public readonly autoClearLogs: boolean; 6 | 7 | constructor({ deferTime, autoClearLogs }: NgxsHmrOptions) { 8 | this.deferTime = deferTime || 100; 9 | this.autoClearLogs = autoClearLogs === undefined ? true : autoClearLogs; 10 | } 11 | 12 | public clearLogs(): void { 13 | if (this.autoClearLogs) { 14 | console.clear(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/internal/hmr-storage.ts: -------------------------------------------------------------------------------- 1 | export class HmrStorage { 2 | constructor(private _snapshot: Partial = {}) {} 3 | 4 | public hasData(): boolean { 5 | return Object.keys(this._snapshot).length > 0; 6 | } 7 | 8 | public get snapshot(): Partial { 9 | return this._snapshot; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { HmrInitAction } from './actions/hmr-init.action'; 2 | export { 3 | NgxsHmrLifeCycle, 4 | NgxsHmrOptions, 5 | WebpackModule, 6 | BootstrapModuleFn, 7 | NgxsHmrSnapshot 8 | } from './symbols'; 9 | export { HmrBeforeDestroyAction } from './actions/hmr-before-destroy.action'; 10 | export { hmr } from './hmr-bootstrap'; 11 | export { hmrIsReloaded } from './utils/externals'; 12 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/utils/externals.ts: -------------------------------------------------------------------------------- 1 | import { HmrRuntime } from '../symbols'; 2 | 3 | declare const window: any; 4 | 5 | export function hmrIsReloaded(): boolean { 6 | return !!(window[HmrRuntime.Status] && window[HmrRuntime.Status].hmrReloaded); 7 | } 8 | -------------------------------------------------------------------------------- /packages/hmr-plugin/src/utils/internals.ts: -------------------------------------------------------------------------------- 1 | import { HmrRuntime } from '../symbols'; 2 | 3 | declare const window: any; 4 | 5 | export function setHmrReloadedTo(value: boolean): void { 6 | if (window[HmrRuntime.Status]) { 7 | window[HmrRuntime.Status].hmrReloaded = value; 8 | } 9 | } 10 | 11 | export function markApplicationAsHmrReloaded(): void { 12 | window[HmrRuntime.Status] = window[HmrRuntime.Status] || { 13 | hmrReloaded: false 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/hmr-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/hmr-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/hmr-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/hmr-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/logger-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/logger-plugin 2 | 3 | Logger plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/logger-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/logger-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/logger-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'logger-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/logger-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/logger-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/logger-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/logger-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/logger-plugin", 3 | "description": "logger plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "rxjs": ">=6.5.5" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/logger-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/logger-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/logger-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/logger-plugin/ng-package.json" 13 | } 14 | }, 15 | "test": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/logger-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/logger-plugin/src/internals.ts: -------------------------------------------------------------------------------- 1 | export const repeat = (str: string, times: number) => new Array(times + 1).join(str); 2 | 3 | export const pad = (num: number, maxLength: number) => 4 | repeat('0', maxLength - num.toString().length) + num; 5 | 6 | export function formatTime(time: Date) { 7 | return ( 8 | pad(time.getHours(), 2) + 9 | `:` + 10 | pad(time.getMinutes(), 2) + 11 | `:` + 12 | pad(time.getSeconds(), 2) + 13 | `.` + 14 | pad(time.getMilliseconds(), 3) 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/logger-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsLoggerPluginModule, withNgxsLoggerPlugin } from './logger.module'; 2 | export { NgxsLoggerPlugin } from './logger.plugin'; 3 | export * from './symbols'; 4 | -------------------------------------------------------------------------------- /packages/logger-plugin/src/symbols.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | export interface NgxsLoggerPluginOptions { 4 | /** Auto expand logged actions */ 5 | collapsed?: boolean; 6 | 7 | /** Provide alternate console.log implementation */ 8 | logger?: any; 9 | 10 | /** Disable the logger. Useful for prod mode. */ 11 | disabled?: boolean; 12 | 13 | /** Predicate for actions to be the logged. Takes action and state snapshot as parameters */ 14 | filter?: (action: any, state: any) => boolean; 15 | } 16 | 17 | export const NGXS_LOGGER_PLUGIN_OPTIONS = new InjectionToken( 18 | 'NGXS_LOGGER_PLUGIN_OPTIONS' 19 | ); 20 | -------------------------------------------------------------------------------- /packages/logger-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/logger-plugin/tests/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logger-spy'; 2 | export * from './utils'; 3 | export * from './setup-with-logger'; 4 | -------------------------------------------------------------------------------- /packages/logger-plugin/tests/helpers/setup-with-logger.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler } from '@angular/core'; 2 | import { TestBed } from '@angular/core/testing'; 3 | import { Store, provideStore } from '@ngxs/store'; 4 | import { ɵStateClass } from '@ngxs/store/internals'; 5 | import { NgxsLoggerPluginOptions, withNgxsLoggerPlugin } from '@ngxs/logger-plugin'; 6 | 7 | import { LoggerSpy } from './logger-spy'; 8 | import { NoopErrorHandler } from '../../../store/tests/helpers/utils'; 9 | 10 | export function setupWithLogger(states: ɵStateClass[], opts?: NgxsLoggerPluginOptions) { 11 | const logger = new LoggerSpy(); 12 | 13 | TestBed.configureTestingModule({ 14 | providers: [ 15 | provideStore(states, withNgxsLoggerPlugin({ ...opts, logger })), 16 | { provide: ErrorHandler, useClass: NoopErrorHandler } 17 | ] 18 | }); 19 | 20 | const store: Store = TestBed.inject(Store); 21 | 22 | return { 23 | store, 24 | logger 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /packages/logger-plugin/tests/helpers/symbols.ts: -------------------------------------------------------------------------------- 1 | export type CallStack = (string | {})[][]; 2 | -------------------------------------------------------------------------------- /packages/logger-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/logger-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/logger-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/router-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/router-plugin 2 | 3 | Router plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | Inspired by @ngrx/router-store and modified to use the @ngxs/store 5 | -------------------------------------------------------------------------------- /packages/router-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/router-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/router-plugin/internals/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "src/index.ts", 4 | "flatModuleFile": "ngxs-router-plugin-internals" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/router-plugin/internals/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './symbols'; 2 | -------------------------------------------------------------------------------- /packages/router-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'router-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/router-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/router-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/router-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/router-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/router-plugin", 3 | "description": "router plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "@angular/router": ">=19.0.0 <20.0.0", 10 | "rxjs": ">=6.5.5" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/router-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "router-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/router-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/router-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/router-plugin/ng-package.json" 13 | } 14 | }, 15 | "test": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/router-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/router-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsRouterPluginModule, withNgxsRouterPlugin } from './router.module'; 2 | export { ROUTER_STATE_TOKEN, RouterState, RouterStateModel } from './router.state'; 3 | export { 4 | RouterStateSerializer, 5 | DefaultRouterStateSerializer, 6 | SerializedRouterStateSnapshot 7 | } from './serializer'; 8 | export * from './router.actions'; 9 | export { 10 | NavigationActionTiming, 11 | NgxsRouterPluginOptions 12 | } from '@ngxs/router-plugin/internals'; 13 | -------------------------------------------------------------------------------- /packages/router-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/router-plugin/tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Router } from '@angular/router'; 2 | import { Store, Actions } from '@ngxs/store'; 3 | import { Type, NgZone } from '@angular/core'; 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { skipConsoleLogging } from '@ngxs/store/internals/testing'; 6 | 7 | export async function createNgxsRouterPluginTestingPlatform(module: Type) { 8 | const { injector } = await skipConsoleLogging(() => 9 | platformBrowserDynamic().bootstrapModule(module) 10 | ); 11 | const ngZone: NgZone = injector.get(NgZone); 12 | const store: Store = injector.get(Store); 13 | const router: Router = injector.get(Router); 14 | const actions$: Actions = injector.get(Actions); 15 | return { store, router, actions$, injector, ngZone }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/router-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/router-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/router-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/storage-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/storage-plugin 2 | 3 | Storage plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/storage-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/storage-plugin 3 | */ 4 | export * from './src/public_api'; 5 | -------------------------------------------------------------------------------- /packages/storage-plugin/internals/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "src/index.ts", 4 | "flatModuleFile": "ngxs-storage-plugin-internals" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/storage-plugin/internals/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './symbols'; 2 | export * from './storage-key'; 3 | -------------------------------------------------------------------------------- /packages/storage-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'storage-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/storage-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/storage-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/storage-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/storage-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/storage-plugin", 3 | "description": "extendable storage plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "rxjs": ">=6.5.5", 10 | "ts-morph": "21.0.1" 11 | }, 12 | "schematics": "./schematics/collection.json" 13 | } 14 | -------------------------------------------------------------------------------- /packages/storage-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storage-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/storage-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build-package": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/storage-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/storage-plugin/ng-package.json" 13 | } 14 | }, 15 | "build": { 16 | "executor": "nx:run-commands", 17 | "options": { 18 | "command": "node tools/build-schematics.mjs --projectRoot=packages/storage-plugin --distPath=@ngxs/storage-plugin" 19 | }, 20 | "dependsOn": ["build-package"] 21 | }, 22 | "test": { 23 | "executor": "@nx/jest:jest", 24 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 25 | "options": { 26 | "jestConfig": "packages/storage-plugin/jest.config.js" 27 | } 28 | }, 29 | "lint": { 30 | "executor": "@nx/eslint:lint" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/storage-plugin/schematics/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@schematics/angular"], 3 | "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", 4 | "schematics": { 5 | "keys-migration": { 6 | "factory": "./src/ng-generate/index#migrateKeys", 7 | "description": "Migrates from key to keys", 8 | "aliases": ["ngxs-keys-migration"], 9 | "schema": "./src/ng-generate/schema.json" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/storage-plugin/schematics/src/_testing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schematics'; 2 | -------------------------------------------------------------------------------- /packages/storage-plugin/schematics/src/ng-generate/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "SchematicsNgxsStoragePlugin", 4 | "title": "Ngxs Storage Plugin Options Schema", 5 | "type": "object", 6 | "properties": {} 7 | } 8 | -------------------------------------------------------------------------------- /packages/storage-plugin/schematics/src/utils/versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "@ngxs/store": "19.0.0" 3 | } 4 | -------------------------------------------------------------------------------- /packages/storage-plugin/schematics/src/utils/visit-files.ts: -------------------------------------------------------------------------------- 1 | import { DirEntry, Tree } from '@angular-devkit/schematics'; 2 | 3 | export function visitTsFiles( 4 | tree: Tree, 5 | dirPath = tree.root, 6 | visitor: (path: string) => void 7 | ): void { 8 | function visit(directory: DirEntry) { 9 | for (const path of directory.subfiles) { 10 | if (path.endsWith('.ts') && !path.endsWith('.d.ts')) { 11 | const entry = directory.file(path); 12 | if (entry) { 13 | visitor(entry.path); 14 | } 15 | } 16 | } 17 | 18 | for (const path of directory.subdirs) { 19 | if (path === 'node_modules') { 20 | continue; 21 | } 22 | 23 | visit(directory.dir(path)); 24 | } 25 | } 26 | 27 | visit(dirPath); 28 | } 29 | -------------------------------------------------------------------------------- /packages/storage-plugin/src/engines.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | import { StorageEngine } from '@ngxs/storage-plugin/internals'; 3 | 4 | declare const ngDevMode: boolean; 5 | declare const ngServerMode: boolean; 6 | 7 | export const LOCAL_STORAGE_ENGINE = /* @__PURE__ */ new InjectionToken( 8 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'LOCAL_STORAGE_ENGINE' : '', 9 | { 10 | providedIn: 'root', 11 | factory: () => (typeof ngServerMode !== 'undefined' && ngServerMode ? null : localStorage) 12 | } 13 | ); 14 | 15 | export const SESSION_STORAGE_ENGINE = /* @__PURE__ */ new InjectionToken( 16 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'SESSION_STORAGE_ENGINE' : '', 17 | { 18 | providedIn: 'root', 19 | factory: () => 20 | typeof ngServerMode !== 'undefined' && ngServerMode ? null : sessionStorage 21 | } 22 | ); 23 | -------------------------------------------------------------------------------- /packages/storage-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsStoragePluginModule, withNgxsStoragePlugin } from './storage.module'; 2 | export { withStorageFeature } from './with-storage-feature'; 3 | export { NgxsStoragePlugin } from './storage.plugin'; 4 | export * from './engines'; 5 | 6 | export { 7 | StorageOption, 8 | NgxsStoragePluginOptions, 9 | STORAGE_ENGINE, 10 | StorageEngine 11 | } from '@ngxs/storage-plugin/internals'; 12 | -------------------------------------------------------------------------------- /packages/storage-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/storage-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/storage-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/storage-plugin/tsconfig.schematics.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": ["es2018", "dom"], 5 | "declaration": true, 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedParameters": true, 13 | "noUnusedLocals": true, 14 | "rootDir": "schematics", 15 | "outDir": "../../@ngxs/storage-plugin/schematics", 16 | "skipDefaultLibCheck": true, 17 | "skipLibCheck": true, 18 | "sourceMap": true, 19 | "strictNullChecks": true, 20 | "target": "es6", 21 | "types": ["jest", "node"] 22 | }, 23 | "include": ["schematics/**/*"], 24 | "exclude": [ 25 | "schematics/src/_testing/**/*", 26 | "schematics/**/*.test.ts", 27 | "schematics/**/*.spec.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /packages/storage-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/store/experimental/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "src/index.ts", 5 | "flatModuleFile": "ngxs-store-experimental" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/experimental/src/index.ts: -------------------------------------------------------------------------------- 1 | import { withNgxsPendingTasks } from '@ngxs/store'; 2 | 3 | /** 4 | * Required for correct functioning of SSR apps. 5 | * 6 | * @deprecated 7 | * This experimental export is deprecated in favour of the non-experimental export. 8 | * This is no longer an experimental feature, but is now available as 9 | * `withNgxsPendingTasks` in the main `@ngxs/store` package. 10 | */ 11 | export function withExperimentalNgxsPendingTasks() { 12 | return withNgxsPendingTasks(); 13 | } 14 | -------------------------------------------------------------------------------- /packages/store/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/store 3 | */ 4 | export * from './src/public_api'; 5 | 6 | /** 7 | * The plugin api for the stuff that a plugins needs 8 | */ 9 | export * from './src/plugin_api'; 10 | 11 | /** 12 | * Private exports required for the compilation. 13 | */ 14 | export * from './src/private_api'; 15 | -------------------------------------------------------------------------------- /packages/store/internals/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "src/index.ts", 5 | "flatModuleFile": "ngxs-store-internals" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/internals/src/custom-rxjs-operators.ts: -------------------------------------------------------------------------------- 1 | import { type MonoTypeOperatorFunction, Observable } from 'rxjs'; 2 | 3 | export function ɵwrapObserverCalls( 4 | invokeFn: (fn: () => void) => void 5 | ): MonoTypeOperatorFunction { 6 | return (source: Observable) => { 7 | return new Observable(subscriber => { 8 | return source.subscribe({ 9 | next(value) { 10 | invokeFn(() => subscriber.next(value)); 11 | }, 12 | error(error) { 13 | invokeFn(() => subscriber.error(error)); 14 | }, 15 | complete() { 16 | invokeFn(() => subscriber.complete()); 17 | } 18 | }); 19 | }); 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/internals/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './symbols'; 2 | export * from './metadata'; 3 | export { ɵmemoize } from './memoize'; 4 | export { StateToken } from './state-token'; 5 | export { ɵINITIAL_STATE_TOKEN, ɵInitialState } from './initial-state'; 6 | export { ɵNgxsAppBootstrappedState } from './ngxs-app-bootstrapped-state'; 7 | export { ɵNGXS_STATE_CONTEXT_FACTORY, ɵNGXS_STATE_FACTORY } from './internal-tokens'; 8 | export { ɵOrderedSubject, ɵOrderedBehaviorSubject } from './custom-rxjs-subjects'; 9 | export { ɵwrapObserverCalls } from './custom-rxjs-operators'; 10 | export { ɵStateStream } from './state-stream'; 11 | export { ɵhasOwnProperty, ɵdefineProperty } from './object-utils'; 12 | export { ɵNgxsActionRegistry } from './action-registry'; 13 | -------------------------------------------------------------------------------- /packages/store/internals/src/initial-state.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { ɵPlainObject } from './symbols'; 4 | 5 | declare const ngDevMode: boolean; 6 | 7 | export class ɵInitialState { 8 | private static _value: ɵPlainObject = {}; 9 | 10 | static set(state: ɵPlainObject) { 11 | this._value = state; 12 | } 13 | 14 | static pop(): ɵPlainObject { 15 | const state = this._value; 16 | this._value = {}; 17 | return state; 18 | } 19 | } 20 | 21 | export const ɵINITIAL_STATE_TOKEN = new InjectionToken<ɵPlainObject>( 22 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'INITIAL_STATE_TOKEN' : '', 23 | { 24 | providedIn: 'root', 25 | factory: () => ɵInitialState.pop() 26 | } 27 | ); 28 | -------------------------------------------------------------------------------- /packages/store/internals/src/internal-tokens.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | declare const ngDevMode: boolean; 4 | 5 | // These tokens are internal and can change at any point. 6 | 7 | export const ɵNGXS_STATE_FACTORY = /* @__PURE__ */ new InjectionToken( 8 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'ɵNGXS_STATE_FACTORY' : '' 9 | ); 10 | 11 | export const ɵNGXS_STATE_CONTEXT_FACTORY = /* @__PURE__ */ new InjectionToken( 12 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'ɵNGXS_STATE_CONTEXT_FACTORY' : '' 13 | ); 14 | -------------------------------------------------------------------------------- /packages/store/internals/src/ngxs-app-bootstrapped-state.ts: -------------------------------------------------------------------------------- 1 | import { DestroyRef, inject, Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class ɵNgxsAppBootstrappedState extends BehaviorSubject { 6 | constructor() { 7 | super(false); 8 | 9 | const destroyRef = inject(DestroyRef); 10 | // Complete the subject once the root injector is destroyed to ensure 11 | // there are no active subscribers that would receive events or perform 12 | // any actions after the application is destroyed. 13 | destroyRef.onDestroy(() => this.complete()); 14 | } 15 | 16 | bootstrap(): void { 17 | this.next(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/store/internals/src/object-utils.ts: -------------------------------------------------------------------------------- 1 | // Property reads are not minified. 2 | // It's smaller to read it once and use a function. 3 | const _hasOwnProperty = Object.prototype.hasOwnProperty; 4 | export const ɵhasOwnProperty = (target: any, key: PropertyKey) => 5 | _hasOwnProperty.call(target, key); 6 | 7 | export const ɵdefineProperty = Object.defineProperty; 8 | -------------------------------------------------------------------------------- /packages/store/internals/src/state-token.ts: -------------------------------------------------------------------------------- 1 | import { ɵensureSelectorMetadata } from './metadata'; 2 | import type { ɵTokenName, ɵSelectFromRootState, ɵRuntimeSelectorContext } from './symbols'; 3 | 4 | export class StateToken { 5 | constructor(private readonly _name: ɵTokenName) { 6 | const selectorMetadata = ɵensureSelectorMetadata(this); 7 | selectorMetadata.makeRootSelector = ( 8 | runtimeContext: ɵRuntimeSelectorContext 9 | ): ɵSelectFromRootState => { 10 | return runtimeContext.getStateGetter(this._name); 11 | }; 12 | } 13 | 14 | getName(): string { 15 | return this._name; 16 | } 17 | 18 | toString(): string { 19 | return `StateToken[${this._name}]`; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/internals/testing/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "src/index.ts", 5 | "flatModuleFile": "ngxs-store-internals-testing" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/internals/testing/src/helpers/ngxs-test.component.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: '' 6 | }) 7 | export class NgxsTestComponent implements OnInit, AfterViewInit { 8 | public ngOnInit(): void {} 9 | public ngAfterViewInit(): void {} 10 | } 11 | -------------------------------------------------------------------------------- /packages/store/internals/testing/src/helpers/ngxs-test.module.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationRef, NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { NgxsTestComponent } from './ngxs-test.component'; 5 | 6 | @NgModule({ 7 | imports: [BrowserModule, NgxsTestComponent] 8 | }) 9 | export class NgxsTestModule { 10 | public static ngDoBootstrap(app: ApplicationRef): void { 11 | app.bootstrap(NgxsTestComponent); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/store/internals/testing/src/index.ts: -------------------------------------------------------------------------------- 1 | export { freshPlatform } from './fresh-platform'; 2 | export { NgxsTestBed } from './ngxs.setup'; 3 | export { 4 | skipConsoleLogging, 5 | ConsoleRecord, 6 | ConsoleRecorder, 7 | loggedError 8 | } from './skip-console-logging'; 9 | export { NgxsTesting } from './symbol'; 10 | export { NgxsActionCollector } from './action-collector'; 11 | -------------------------------------------------------------------------------- /packages/store/internals/testing/src/symbol.ts: -------------------------------------------------------------------------------- 1 | import { NgxsModuleOptions, Store } from '@ngxs/store'; 2 | import { ModuleWithProviders } from '@angular/core'; 3 | import { TestBedStatic } from '@angular/core/testing'; 4 | import { ɵStateClass } from '@ngxs/store/internals'; 5 | 6 | export interface NgxsOptionsTesting { 7 | states?: ɵStateClass[]; 8 | ngxsOptions?: NgxsModuleOptions; 9 | imports?: ModuleWithProviders[]; 10 | before?: () => void; 11 | } 12 | 13 | export interface NgxsTesting { 14 | store: Store; 15 | getTestBed: TestBedStatic; 16 | } 17 | -------------------------------------------------------------------------------- /packages/store/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'store', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/store', 7 | coveragePathIgnorePatterns: ['/node_modules/', '/schematics/src/utils/ng-utils/'], 8 | transform: { 9 | '^.+\\.(ts|mjs|js|html)$': [ 10 | 'jest-preset-angular', 11 | { 12 | isolatedModules: true, 13 | tsconfig: '/tsconfig.spec.json', 14 | stringifyContentPathRegex: '\\.(html|svg)$' 15 | } 16 | ] 17 | }, 18 | snapshotSerializers: [ 19 | 'jest-preset-angular/build/serializers/no-ng-attributes', 20 | 'jest-preset-angular/build/serializers/ng-snapshot', 21 | 'jest-preset-angular/build/serializers/html-comment' 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /packages/store/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/store" 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/operators/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "src/index.ts", 5 | "flatModuleFile": "ngxs-store-operators" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/operators/src/append.ts: -------------------------------------------------------------------------------- 1 | import { ExistingState, NoInfer, StateOperator } from './types'; 2 | import { isArray } from './utils'; 3 | 4 | /** 5 | * @param items - Specific items to append to the end of an array 6 | */ 7 | export function append(items: NoInfer): StateOperator { 8 | return function appendOperator(existing: ExistingState): T[] { 9 | // If `items` is `undefined` or `null` or `[]` but `existing` is provided 10 | // just return `existing` 11 | const itemsNotProvidedButExistingIs = (!items || !items.length) && existing; 12 | if (itemsNotProvidedButExistingIs) { 13 | return existing as unknown as T[]; 14 | } 15 | 16 | if (isArray(existing)) { 17 | return existing.concat(items as unknown as ExistingState); 18 | } 19 | 20 | // For example if some property is added dynamically 21 | // and didn't exist before thus it's not `ArrayLike` 22 | return items as unknown as T[]; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /packages/store/operators/src/compose.ts: -------------------------------------------------------------------------------- 1 | import { ExistingState, NoInfer, StateOperator } from './types'; 2 | 3 | export function compose(...operators: NoInfer[]>): StateOperator { 4 | return function composeOperator(existing: ExistingState): T { 5 | return operators.reduce( 6 | (accumulator, operator) => operator(accumulator as ExistingState), 7 | existing as T 8 | ); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /packages/store/operators/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module 3 | * @description 4 | * Entry point for all public APIs of this package. 5 | */ 6 | export { append } from './append'; 7 | export { compose } from './compose'; 8 | export { iif } from './iif'; 9 | export { insertItem } from './insert-item'; 10 | export { patch, ɵPatchSpec } from './patch'; 11 | export { isStateOperator, isPredicate, Predicate } from './utils'; 12 | export { updateItem } from './update-item'; 13 | export { removeItem } from './remove-item'; 14 | export { ExistingState, NoInfer, StateOperator } from './types'; 15 | -------------------------------------------------------------------------------- /packages/store/operators/src/patch.ts: -------------------------------------------------------------------------------- 1 | import { ExistingState, NoInfer, StateOperator } from './types'; 2 | import { isStateOperator } from './utils'; 3 | 4 | type NotUndefined = T extends undefined ? never : T; 5 | 6 | export type ɵPatchSpec = { [P in keyof T]?: T[P] | StateOperator> }; 7 | 8 | export function patch>( 9 | patchObject: NoInfer<ɵPatchSpec> 10 | ): StateOperator { 11 | return function patchStateOperator(existing: ExistingState): T { 12 | let clone = null; 13 | for (const k in patchObject) { 14 | const newValue = patchObject[k]; 15 | const existingPropValue = existing?.[k]; 16 | const newPropValue = isStateOperator(newValue) 17 | ? newValue(existingPropValue) 18 | : newValue; 19 | if (newPropValue !== existingPropValue) { 20 | if (!clone) { 21 | clone = { ...(existing) }; 22 | } 23 | clone[k] = newPropValue; 24 | } 25 | } 26 | return clone || existing; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /packages/store/operators/src/remove-item.ts: -------------------------------------------------------------------------------- 1 | import { ExistingState, NoInfer, StateOperator } from './types'; 2 | import { isPredicate, isNumber, invalidIndex, Predicate } from './utils'; 3 | 4 | /** 5 | * @param selector - index or predicate to remove an item from an array by 6 | */ 7 | export function removeItem(selector: number | NoInfer>): StateOperator { 8 | return function removeItemOperator(existing: ExistingState): T[] { 9 | let index = -1; 10 | 11 | if (isPredicate(selector)) { 12 | index = existing.findIndex(selector); 13 | } else if (isNumber(selector)) { 14 | index = selector; 15 | } 16 | 17 | if (invalidIndex(index)) { 18 | return existing as T[]; 19 | } 20 | 21 | const clone = existing.slice(); 22 | clone.splice(index, 1); 23 | return clone; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /packages/store/operators/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { StateOperator } from './types'; 2 | 3 | export const isArray = Array.isArray; 4 | 5 | export type Predicate = (value: T | Readonly) => boolean; 6 | 7 | const isFunction = (value: unknown) => typeof value == 'function'; 8 | 9 | export const isStateOperator = isFunction as ( 10 | value: T | StateOperator 11 | ) => value is StateOperator; 12 | 13 | export const isPredicate = isFunction as ( 14 | value: Predicate | boolean | number 15 | ) => value is Predicate; 16 | 17 | export const isNumber = (value: unknown): value is number => typeof value === 'number'; 18 | 19 | export const invalidIndex = (index: number) => Number.isNaN(index) || index === -1; 20 | -------------------------------------------------------------------------------- /packages/store/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/store", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "sideEffects": false, 6 | "peerDependencies": { 7 | "@angular/core": ">=19.0.0 <20.0.0", 8 | "rxjs": ">=7.0.0" 9 | }, 10 | "schematics": "./schematics/collection.json" 11 | } 12 | -------------------------------------------------------------------------------- /packages/store/plugins/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "src/index.ts", 5 | "flatModuleFile": "ngxs-store-plugins" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/store/plugins/src/actions.ts: -------------------------------------------------------------------------------- 1 | import { ɵPlainObject } from '@ngxs/store/internals'; 2 | 3 | /** 4 | * Init action 5 | */ 6 | export class InitState { 7 | static readonly type = '@@INIT'; 8 | } 9 | 10 | /** 11 | * Update action 12 | */ 13 | export class UpdateState { 14 | static readonly type = '@@UPDATE_STATE'; 15 | 16 | constructor(readonly addedStates?: ɵPlainObject) {} 17 | } 18 | -------------------------------------------------------------------------------- /packages/store/plugins/src/index.ts: -------------------------------------------------------------------------------- 1 | export { InitState, UpdateState } from './actions'; 2 | export { 3 | NGXS_PLUGINS, 4 | NgxsPlugin, 5 | NgxsPluginFn, 6 | NgxsNextPluginFn, 7 | ɵisPluginClass 8 | } from './symbols'; 9 | export { getActionTypeFromInstance, actionMatcher, setValue, getValue } from './utils'; 10 | -------------------------------------------------------------------------------- /packages/store/schematics/src/_testing/index.ts: -------------------------------------------------------------------------------- 1 | export * from './schematics'; 2 | -------------------------------------------------------------------------------- /packages/store/schematics/src/actions/actions.factory.ts: -------------------------------------------------------------------------------- 1 | import { Rule, SchematicsException, url, Tree } from '@angular-devkit/schematics'; 2 | import { ActionsSchema } from './actions.schema'; 3 | import { generateFiles } from '../utils/generate-utils'; 4 | import { isEmpty } from '../utils/common/properties'; 5 | import { normalizeBaseOptions } from '../utils/normalize-options'; 6 | import { join } from 'path'; 7 | 8 | export function actions(options: ActionsSchema): Rule { 9 | return (host: Tree) => { 10 | if (isEmpty(options.name)) { 11 | throw new SchematicsException('Invalid options, "name" is required.'); 12 | } 13 | 14 | const normalizedOptions = normalizeBaseOptions(host, options); 15 | const path = options.flat 16 | ? normalizedOptions.path 17 | : join(normalizedOptions.path, normalizedOptions.name); 18 | 19 | return generateFiles(url('./files'), path, { 20 | name: normalizedOptions.name 21 | }); 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/store/schematics/src/actions/actions.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface ActionsSchema { 2 | /** 3 | * The name of the actions. 4 | */ 5 | name: string; 6 | /** 7 | * The path to create the actions. 8 | */ 9 | path?: string; 10 | /** 11 | * Flag to indicate if a dir is created. 12 | */ 13 | flat?: boolean; 14 | /** 15 | * The application project name to add the Ngxs module/provider. 16 | */ 17 | project?: string; 18 | } 19 | -------------------------------------------------------------------------------- /packages/store/schematics/src/actions/files/__name__.actions.ts__template__: -------------------------------------------------------------------------------- 1 | export class <%= classify(name) %>Action { 2 | static readonly type = '[<%= classify(name) %>] Add item'; 3 | constructor(readonly payload: any) { } 4 | } 5 | -------------------------------------------------------------------------------- /packages/store/schematics/src/actions/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "SchematicsNgxsActions", 4 | "title": "Ngxs Actions Options Schema", 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "description": "The name of the actions.", 9 | "type": "string", 10 | "$default": { 11 | "$source": "argv", 12 | "index": 0 13 | }, 14 | "x-prompt": "What name would you like to use for the actions?", 15 | "x-priority": "important" 16 | }, 17 | "path": { 18 | "type": "string", 19 | "format": "path", 20 | "description": "The path to create the actions. Relative to the specified (or default) project." 21 | }, 22 | "flat": { 23 | "type": "boolean", 24 | "default": false, 25 | "description": "Flag to indicate if a dir is created." 26 | }, 27 | "project": { 28 | "type": "string", 29 | "description": "The name of the project.", 30 | "aliases": ["p"], 31 | "x-dropdown": "projects" 32 | } 33 | }, 34 | "required": ["name"] 35 | } 36 | -------------------------------------------------------------------------------- /packages/store/schematics/src/ng-add/ng-add.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface NgxsPackageSchema { 2 | /** 3 | * The flag for skipping packages installation. 4 | */ 5 | skipInstall?: boolean; 6 | /** 7 | * Additonal packages to be added to the workspace. 8 | */ 9 | plugins?: string[]; 10 | /** 11 | * The application project name to add the Ngxs module import to. 12 | */ 13 | project?: string; 14 | } 15 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/auth/auth.actions.ts__template__: -------------------------------------------------------------------------------- 1 | import { AuthenticationStateModel } from './auth.state'; 2 | 3 | export class SetAuthData { 4 | static readonly type = '[Auth] Auth data'; 5 | constructor(readonly payload: AuthenticationStateModel) {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/dashboard/index.ts__template__: -------------------------------------------------------------------------------- 1 | import { DictionaryState } from './states/dictionary/dictionary.state'; 2 | import { UserState } from './states/user/user.state'; 3 | 4 | export const DashboardStates = [DictionaryState, UserState]; 5 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/dashboard/states/dictionary/dictionary.actions.ts__template__: -------------------------------------------------------------------------------- 1 | import { DictionaryStateModel } from './dictionary.state'; 2 | 3 | export class SetDictionaryData { 4 | static readonly type = '[Dictionary] Set dictionary data action'; 5 | constructor(readonly payload: DictionaryStateModel) {} 6 | } 7 | 8 | export class DictionaryReset { 9 | static readonly type = '[Dictionary] Reset dictionary action'; 10 | } 11 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/dashboard/states/user/user.actions.ts__template__: -------------------------------------------------------------------------------- 1 | import { UserStateModel } from './user.state'; 2 | 3 | export class SetUser { 4 | static readonly type = '[SetUser] action'; 5 | constructor(readonly payload: UserStateModel) {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/dashboard/states/user/user.state.ts__template__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Action, Selector, State, StateContext } from '@ngxs/store'; 3 | import { SetUser } from './user.actions'; 4 | 5 | export interface UserStateModel { 6 | userId: string; 7 | email: string; 8 | firstName: string; 9 | lastName: string; 10 | fullName: string; 11 | positionId: string; 12 | positionName: string; 13 | departmentCode: string; 14 | departmentName: string; 15 | } 16 | 17 | @State({ 18 | name: 'user', 19 | defaults: { 20 | userId: '', 21 | email: '', 22 | firstName: '', 23 | lastName: '', 24 | fullName: '', 25 | positionId: '', 26 | positionName: '', 27 | departmentCode: '', 28 | departmentName: '' 29 | } 30 | }) 31 | @Injectable() 32 | export class UserState { 33 | @Selector() 34 | static getUser(state: UserStateModel): UserStateModel { 35 | return state; 36 | } 37 | 38 | @Action(SetUser) 39 | setUser(ctx: StateContext, { payload }: SetUser) { 40 | ctx.setState(payload); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/files/store/store.module.ts__template__: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NgxsModule } from '@ngxs/store'; 4 | import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin'; 5 | import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin'; 6 | import { 7 | DEVTOOLS_REDUX_CONFIG, 8 | LOGGER_CONFIG, 9 | OPTIONS_CONFIG, 10 | STATES_MODULES 11 | } from './store.config'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | NgxsModule.forRoot(STATES_MODULES, OPTIONS_CONFIG), 17 | NgxsReduxDevtoolsPluginModule.forRoot(DEVTOOLS_REDUX_CONFIG), 18 | NgxsLoggerPluginModule.forRoot(LOGGER_CONFIG) 19 | ], 20 | exports: [NgxsModule] 21 | }) 22 | export class NgxsStoreModule {} 23 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "$id": "SchematicsNgxsStarterKit", 4 | "title": "Ngxs Starter Kit Options Schema", 5 | "type": "object", 6 | "properties": { 7 | "path": { 8 | "type": "string", 9 | "description": "The path to create the starter kit. Relative to the specified (or default) project." 10 | }, 11 | "spec": { 12 | "type": "boolean", 13 | "description": "Specifies if a spec file is generated.", 14 | "default": true 15 | }, 16 | "standalone": { 17 | "type": "boolean", 18 | "description": "Explicitly set whether should generate standalone APIs for the generated starter kit." 19 | } 20 | }, 21 | "required": ["path"] 22 | } 23 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/starter-kit.factory.ts: -------------------------------------------------------------------------------- 1 | import { Rule, Tree, url } from '@angular-devkit/schematics'; 2 | import { generateFiles } from '../utils/generate-utils'; 3 | import { isStandaloneApp } from '@schematics/angular/utility/ng-ast-utils'; 4 | import { getProjectMainFile } from '../utils/project'; 5 | import { normalizePath } from '../utils/normalize-options'; 6 | import { StarterKitSchema } from './starter-kit.schema'; 7 | 8 | export function starterKit(options: StarterKitSchema): Rule { 9 | return (host: Tree) => { 10 | let isStandalone = options.standalone; 11 | if (typeof isStandalone !== 'boolean') { 12 | const mainFile = getProjectMainFile(host, options.project); 13 | isStandalone = !!mainFile && isStandaloneApp(host, mainFile); 14 | } 15 | 16 | const normalizedPath = normalizePath(options.path); 17 | 18 | return generateFiles( 19 | url('./files'), 20 | normalizedPath, 21 | { ...options, isStandalone }, 22 | options.spec 23 | ); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /packages/store/schematics/src/starter-kit/starter-kit.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface StarterKitSchema { 2 | /** 3 | * The path to create the starter kit. 4 | */ 5 | path?: string; 6 | /** 7 | * The spec flag 8 | */ 9 | spec?: boolean; 10 | /** 11 | * The application project name to add the Ngxs module/provider. 12 | */ 13 | project?: string; 14 | /** 15 | * Explicitly set whether should generate standalone APIs for the generated starter kit. 16 | */ 17 | standalone?: boolean; 18 | } 19 | -------------------------------------------------------------------------------- /packages/store/schematics/src/state/files/__name__.state.spec.ts__template__: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { <% if (isStandalone) { %> provideStore, <% } else { %> NgxsModule, <% } %> Store } from '@ngxs/store'; 3 | import { <%= classify(name) %>State, <%= classify(name) %>StateModel } from './<%= dasherize(name) %>.state'; 4 | 5 | describe('<%= classify(name) %> state', () => { 6 | let store: Store; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | <% if (isStandalone) { %> providers: [provideStore([<%= classify(name) %>State])] 11 | <% } else { %> imports: [NgxsModule.forRoot([<%= classify(name) %>State])] <% } %> 12 | }); 13 | 14 | store = TestBed.inject(Store); 15 | }); 16 | 17 | it('should create an empty state', () => { 18 | const actual = store.selectSnapshot(<%= classify(name) %>State.getState); 19 | const expected: <%= classify(name) %>StateModel = { 20 | items: [] 21 | }; 22 | expect(actual).toEqual(expected); 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /packages/store/schematics/src/state/files/__name__.state.ts__template__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { State, Selector } from '@ngxs/store'; 3 | 4 | export interface <%= classify(name) %>StateModel { 5 | items: string[]; 6 | } 7 | 8 | @State<<%= classify(name) %>StateModel>({ 9 | name: '<%= camelize(name) %>', 10 | defaults: { 11 | items: [] 12 | } 13 | }) 14 | @Injectable() 15 | export class <%= classify(name) %>State { 16 | 17 | @Selector() 18 | static getState(state: <%= classify(name) %>StateModel) { 19 | return state; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /packages/store/schematics/src/state/state.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface StateSchema { 2 | /** 3 | * The name of the state. 4 | */ 5 | name: string; 6 | /** 7 | * The path to create the state. 8 | */ 9 | path?: string; 10 | /** 11 | * The spec flag 12 | */ 13 | spec?: boolean; 14 | /** 15 | * Flag to indicate if a dir is created. 16 | */ 17 | flat?: boolean; 18 | /** 19 | * The application project name to add the Ngxs module/provider. 20 | */ 21 | project?: string; 22 | /** 23 | * Explicitly set whether should generate standalone APIs for the generated state. 24 | */ 25 | standalone?: boolean; 26 | } 27 | -------------------------------------------------------------------------------- /packages/store/schematics/src/store/files/__name__.actions.ts__template__: -------------------------------------------------------------------------------- 1 | export class <%= classify(name) %>Action { 2 | static readonly type = '[<%= classify(name) %>] Add item'; 3 | constructor(readonly payload: string) { } 4 | } 5 | -------------------------------------------------------------------------------- /packages/store/schematics/src/store/files/__name__.state.ts__template__: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { State, Action, Selector, StateContext } from '@ngxs/store'; 3 | import { <%= classify(name) %>Action } from './<%= dasherize(name) %>.actions'; 4 | 5 | export interface <%= classify(name) %>StateModel { 6 | items: string[]; 7 | } 8 | 9 | @State<<%= classify(name) %>StateModel>({ 10 | name: '<%= camelize(name) %>', 11 | defaults: { 12 | items: [] 13 | } 14 | }) 15 | @Injectable() 16 | export class <%= classify(name) %>State { 17 | 18 | @Selector() 19 | static getState(state: <%= classify(name) %>StateModel) { 20 | return state; 21 | } 22 | 23 | @Action(<%= classify(name) %>Action) 24 | add(ctx: StateContext<<%= classify(name) %>StateModel>, { payload }: <%= classify(name) %>Action) { 25 | const stateModel = ctx.getState(); 26 | stateModel.items = [...stateModel.items, payload]; 27 | ctx.setState(stateModel); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/store/schematics/src/store/store.schema.d.ts: -------------------------------------------------------------------------------- 1 | export interface StoreSchema { 2 | /** 3 | * The name of the store. 4 | */ 5 | name: string; 6 | /** 7 | * The path to create the store. 8 | */ 9 | path?: string; 10 | /** 11 | * The spec flag 12 | */ 13 | spec?: boolean; 14 | /** 15 | * Flag to indicate if a dir is created. 16 | */ 17 | flat?: boolean; 18 | /** 19 | * The application project name to add the Ngxs module/provider. 20 | */ 21 | project?: string; 22 | /** 23 | * Explicitly set whether should generate standalone APIs for the generated store. 24 | */ 25 | standalone?: boolean; 26 | } 27 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/common/lib.config.ts: -------------------------------------------------------------------------------- 1 | export enum LIBRARIES { 2 | DEVTOOLS = '@ngxs/devtools-plugin', 3 | FORM = '@ngxs/form-plugin', 4 | HMR = '@ngxs/hmr-plugin', 5 | LOGGER = '@ngxs/logger-plugin', 6 | ROUTER = '@ngxs/router-plugin', 7 | STORAGE = '@ngxs/storage-plugin', 8 | STORE = '@ngxs/store', 9 | WEBSOCKET = '@ngxs/websocket-plugin' 10 | } 11 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/common/project-files.config.ts: -------------------------------------------------------------------------------- 1 | export const PACKAGE_JSON = 'package.json'; 2 | export const ANGULAR_JSON = 'angular.json'; 3 | export const TSCONFIG_SPEC_JSON = 'src/tsconfig.spec.json'; 4 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/common/properties.ts: -------------------------------------------------------------------------------- 1 | export function isEmpty(value: string | undefined): boolean { 2 | return typeof value !== 'undefined' && !value.trim().length; 3 | } 4 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/generate-utils.ts: -------------------------------------------------------------------------------- 1 | import { strings } from '@angular-devkit/core'; 2 | import { 3 | apply, 4 | filter, 5 | move, 6 | noop, 7 | template, 8 | Rule, 9 | mergeWith, 10 | Source 11 | } from '@angular-devkit/schematics'; 12 | 13 | export function generateFiles( 14 | srcFolder: Source, 15 | target: string, 16 | substitutions: { 17 | [k: string]: any; 18 | }, 19 | generateSpecs?: boolean 20 | ): Rule { 21 | return mergeWith( 22 | apply(srcFolder, [ 23 | generateSpecs ? noop() : filter(path => !path.includes('.spec')), 24 | template({ 25 | template: '', 26 | ...strings, 27 | ...substitutions 28 | }), 29 | move(target) 30 | ]) 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/interfaces/package.interface.ts: -------------------------------------------------------------------------------- 1 | import { Tree } from '@angular-devkit/schematics'; 2 | 3 | export interface PackageToJsonInterface { 4 | host: Tree; 5 | type: string; 6 | pkg: string; 7 | version: string; 8 | } 9 | -------------------------------------------------------------------------------- /packages/store/schematics/src/utils/versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "@ngxs/store": "19.0.0" 3 | } 4 | -------------------------------------------------------------------------------- /packages/store/src/actions/symbols.ts: -------------------------------------------------------------------------------- 1 | export interface ActionDef { 2 | type: string; 3 | 4 | new (...args: TArgs): TReturn; 5 | } 6 | 7 | export type ActionType = ActionDef | { type: string }; 8 | -------------------------------------------------------------------------------- /packages/store/src/decorators/select/select-factory.ts: -------------------------------------------------------------------------------- 1 | import { DestroyRef, inject, Injectable } from '@angular/core'; 2 | 3 | import { Store } from '../../store'; 4 | import { NgxsConfig } from '../../symbols'; 5 | 6 | /** 7 | * Allows the select decorator to get access to the DI store, this is used internally 8 | * in `@Select` decorator. 9 | */ 10 | @Injectable({ providedIn: 'root' }) 11 | export class SelectFactory { 12 | static store: Store | null = null; 13 | static config: NgxsConfig | null = null; 14 | 15 | constructor(store: Store, config: NgxsConfig) { 16 | SelectFactory.store = store; 17 | SelectFactory.config = config; 18 | 19 | inject(DestroyRef).onDestroy(() => { 20 | SelectFactory.store = null; 21 | SelectFactory.config = null; 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/store/src/decorators/select/select.ts: -------------------------------------------------------------------------------- 1 | import { createSelectObservable, createSelectorFn, PropertyType } from './symbols'; 2 | 3 | /** 4 | * Decorator for selecting a slice of state from the store. 5 | * 6 | * @deprecated 7 | * Read the deprecation notice at this link: https://ngxs.io/deprecations/select-decorator-deprecation. 8 | */ 9 | export function Select(rawSelector?: T, ...paths: string[]): PropertyDecorator { 10 | return function (target, key): void { 11 | const name: string = key.toString(); 12 | const selectorId = `__${name}__selector`; 13 | const selector = createSelectorFn(name, rawSelector, paths); 14 | 15 | Object.defineProperties(target, { 16 | [selectorId]: { 17 | writable: true, 18 | enumerable: false, 19 | configurable: true 20 | }, 21 | [name]: { 22 | enumerable: true, 23 | configurable: true, 24 | get(): PropertyType { 25 | return this[selectorId] || (this[selectorId] = createSelectObservable(selector)); 26 | } 27 | } 28 | }); 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /packages/store/src/decorators/selector-options.ts: -------------------------------------------------------------------------------- 1 | import { ɵSharedSelectorOptions } from '@ngxs/store/internals'; 2 | 3 | import { selectorOptionsMetaAccessor } from '../selectors/selector-metadata'; 4 | 5 | /** 6 | * Decorator for setting selector options at a method or class level. 7 | */ 8 | export function SelectorOptions(options: ɵSharedSelectorOptions) { 9 | return ( 10 | function decorate( 11 | target: any, 12 | methodName: string, 13 | descriptor: TypedPropertyDescriptor 14 | ) { 15 | if (methodName) { 16 | descriptor ||= Object.getOwnPropertyDescriptor(target, methodName)!; 17 | // Method Decorator 18 | const originalFn = descriptor.value || (descriptor).originalFn; 19 | if (originalFn) { 20 | selectorOptionsMetaAccessor.defineOptions(originalFn, options); 21 | } 22 | } else { 23 | // Class Decorator 24 | selectorOptionsMetaAccessor.defineOptions(target, options); 25 | } 26 | } 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /packages/store/src/dev-features/ngxs-development.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule, makeEnvironmentProviders } from '@angular/core'; 2 | 3 | import { NgxsDevelopmentOptions, NGXS_DEVELOPMENT_OPTIONS } from './symbols'; 4 | import { NgxsUnhandledActionsLogger } from './ngxs-unhandled-actions-logger'; 5 | 6 | @NgModule() 7 | export class NgxsDevelopmentModule { 8 | static forRoot(options: NgxsDevelopmentOptions): ModuleWithProviders { 9 | return { 10 | ngModule: NgxsDevelopmentModule, 11 | providers: [ 12 | NgxsUnhandledActionsLogger, 13 | { provide: NGXS_DEVELOPMENT_OPTIONS, useValue: options } 14 | ] 15 | }; 16 | } 17 | } 18 | 19 | export function withNgxsDevelopmentOptions(options: NgxsDevelopmentOptions) { 20 | return makeEnvironmentProviders([ 21 | NgxsUnhandledActionsLogger, 22 | { provide: NGXS_DEVELOPMENT_OPTIONS, useValue: options } 23 | ]); 24 | } 25 | -------------------------------------------------------------------------------- /packages/store/src/dev-features/symbols.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | import { ActionType } from '../actions/symbols'; 4 | 5 | export interface NgxsDevelopmentOptions { 6 | // This allows setting only `true` because there's no reason to set `false`. 7 | // Developers may just skip importing the development module at all. 8 | warnOnUnhandledActions: 9 | | true 10 | | { 11 | ignore: ActionType[]; 12 | }; 13 | } 14 | 15 | export const NGXS_DEVELOPMENT_OPTIONS = 16 | /* @__PURE__ */ new InjectionToken( 17 | typeof ngDevMode !== 'undefined' && ngDevMode ? 'NGXS_DEVELOPMENT_OPTIONS' : '', 18 | { 19 | providedIn: 'root', 20 | factory: () => ({ warnOnUnhandledActions: true }) 21 | } 22 | ); 23 | -------------------------------------------------------------------------------- /packages/store/src/execution/execution-strategy.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable, NgZone } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class InternalNgxsExecutionStrategy { 5 | private _ngZone = inject(NgZone); 6 | 7 | enter(func: () => T): T { 8 | if (typeof ngServerMode !== 'undefined' && ngServerMode) { 9 | return this._runInsideAngular(func); 10 | } 11 | return this._runOutsideAngular(func); 12 | } 13 | 14 | leave(func: () => T): T { 15 | return this._runInsideAngular(func); 16 | } 17 | 18 | private _runInsideAngular(func: () => T): T { 19 | if (NgZone.isInAngularZone()) { 20 | return func(); 21 | } 22 | return this._ngZone.run(func); 23 | } 24 | 25 | private _runOutsideAngular(func: () => T): T { 26 | if (NgZone.isInAngularZone()) { 27 | return this._ngZone.runOutsideAngular(func); 28 | } 29 | return func(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/store/src/internal/action-results.ts: -------------------------------------------------------------------------------- 1 | import { DestroyRef, inject, Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | 4 | import type { ActionContext } from '../actions-stream'; 5 | 6 | /** 7 | * Internal Action result stream that is emitted when an action is completed. 8 | * This is used as a method of returning the action result to the dispatcher 9 | * for the observable returned by the dispatch(...) call. 10 | * The dispatcher then asynchronously pushes the result from this stream onto the main action stream as a result. 11 | */ 12 | @Injectable({ providedIn: 'root' }) 13 | export class InternalDispatchedActionResults extends Subject { 14 | constructor() { 15 | super(); 16 | // Complete the subject once the root injector is destroyed to ensure 17 | // there are no active subscribers that would receive events or perform 18 | // any actions after the application is destroyed. 19 | inject(DestroyRef).onDestroy(() => this.complete()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/src/internal/provide-internal-tokens.ts: -------------------------------------------------------------------------------- 1 | import { makeEnvironmentProviders } from '@angular/core'; 2 | import { ɵNGXS_STATE_CONTEXT_FACTORY, ɵNGXS_STATE_FACTORY } from '@ngxs/store/internals'; 3 | 4 | import { StateFactory } from './state-factory'; 5 | import { StateContextFactory } from './state-context-factory'; 6 | 7 | // Backward compatibility is provided because these tokens are used by third-party 8 | // libraries. We expose a separate function to allow tree-shaking of these tokens 9 | // if they are not used in standard applications that do not rely on them. 10 | export function ɵprovideNgxsInternalStateTokens() { 11 | return makeEnvironmentProviders([ 12 | { 13 | provide: ɵNGXS_STATE_CONTEXT_FACTORY, 14 | useExisting: StateContextFactory 15 | }, 16 | { 17 | provide: ɵNGXS_STATE_FACTORY, 18 | useExisting: StateFactory 19 | } 20 | ]); 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/src/internal/state-operators.ts: -------------------------------------------------------------------------------- 1 | import { 2 | throwPatchingArrayError, 3 | throwPatchingPrimitiveError 4 | } from '../configs/messages.config'; 5 | import { ExistingState, StateOperator } from '@ngxs/store/operators'; 6 | 7 | export function simplePatch(value: Partial): StateOperator { 8 | return (existingState: ExistingState) => { 9 | if (typeof ngDevMode !== 'undefined' && ngDevMode) { 10 | if (Array.isArray(value)) { 11 | throwPatchingArrayError(); 12 | } else if (typeof value !== 'object') { 13 | throwPatchingPrimitiveError(); 14 | } 15 | } 16 | 17 | const newState: any = { ...(existingState as any) }; 18 | for (const key in value) { 19 | // deep clone for patch compatibility 20 | newState[key] = (value as any)[key]; 21 | } 22 | 23 | return newState as T; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /packages/store/src/module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { ɵStateClass } from '@ngxs/store/internals'; 3 | 4 | import { NgxsModuleOptions } from './symbols'; 5 | import { NgxsRootModule } from './modules/ngxs-root.module'; 6 | import { NgxsFeatureModule } from './modules/ngxs-feature.module'; 7 | import { getRootProviders } from './standalone-features/root-providers'; 8 | import { getFeatureProviders } from './standalone-features/feature-providers'; 9 | 10 | @NgModule() 11 | export class NgxsModule { 12 | static forRoot( 13 | states: ɵStateClass[] = [], 14 | options: NgxsModuleOptions = {} 15 | ): ModuleWithProviders { 16 | return { 17 | ngModule: NgxsRootModule, 18 | providers: getRootProviders(states, options) 19 | }; 20 | } 21 | 22 | static forFeature(states: ɵStateClass[] = []): ModuleWithProviders { 23 | return { 24 | ngModule: NgxsFeatureModule, 25 | providers: getFeatureProviders(states) 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/store/src/modules/ngxs-feature.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { featureStatesInitializer } from '../standalone-features/initializers'; 4 | 5 | /** 6 | * @ignore 7 | */ 8 | @NgModule() 9 | export class NgxsFeatureModule { 10 | constructor() { 11 | featureStatesInitializer(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/store/src/modules/ngxs-root.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { rootStoreInitializer } from '../standalone-features/initializers'; 4 | 5 | /** 6 | * @ignore 7 | */ 8 | @NgModule() 9 | export class NgxsRootModule { 10 | constructor() { 11 | rootStoreInitializer(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/store/src/operators/leave-ngxs.ts: -------------------------------------------------------------------------------- 1 | import { ɵwrapObserverCalls } from '@ngxs/store/internals'; 2 | 3 | import { InternalNgxsExecutionStrategy } from '../execution/execution-strategy'; 4 | 5 | /** 6 | * Returns operator that will run 7 | * `subscribe` outside of the ngxs execution context 8 | */ 9 | export function leaveNgxs(ngxsExecutionStrategy: InternalNgxsExecutionStrategy) { 10 | return ɵwrapObserverCalls(fn => ngxsExecutionStrategy.leave(fn)); 11 | } 12 | -------------------------------------------------------------------------------- /packages/store/src/plugin_api.ts: -------------------------------------------------------------------------------- 1 | export { 2 | InitState, 3 | UpdateState, 4 | getActionTypeFromInstance, 5 | setValue, 6 | getValue, 7 | NGXS_PLUGINS, 8 | NgxsPlugin, 9 | NgxsPluginFn, 10 | NgxsNextPluginFn 11 | } from '@ngxs/store/plugins'; 12 | -------------------------------------------------------------------------------- /packages/store/src/private_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsRootModule as ɵNgxsRootModule } from './modules/ngxs-root.module'; 2 | export { NgxsFeatureModule as ɵNgxsFeatureModule } from './modules/ngxs-feature.module'; 3 | 4 | export * from './selectors/private_api'; 5 | 6 | export { ɵprovideNgxsInternalStateTokens } from './internal/provide-internal-tokens'; 7 | -------------------------------------------------------------------------------- /packages/store/src/selectors/create-pick-selector.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from './create-selector'; 2 | import { ensureValidSelector } from './selector-checks.util'; 3 | import { TypedSelector } from './selector-types.util'; 4 | 5 | type KeysToValues = { 6 | [Index in keyof Keys]: Keys[Index] extends keyof T ? T[Keys[Index]] : never; 7 | }; 8 | 9 | export function createPickSelector( 10 | selector: TypedSelector, 11 | keys: [...Keys] 12 | ) { 13 | if (typeof ngDevMode !== 'undefined' && ngDevMode) { 14 | ensureValidSelector(selector, { prefix: '[createPickSelector]' }); 15 | } 16 | const validKeys = keys.filter(Boolean); 17 | const selectors = validKeys.map(key => createSelector([selector], (s: TModel) => s[key])); 18 | return createSelector([...selectors], (...props: KeysToValues) => { 19 | return validKeys.reduce( 20 | (acc, key, index) => { 21 | acc[key] = props[index]; 22 | return acc; 23 | }, 24 | {} as Pick 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /packages/store/src/selectors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-model-selector'; 2 | export * from './create-pick-selector'; 3 | export * from './create-property-selectors'; 4 | export * from './create-selector'; 5 | export * from './selector-types.util'; 6 | -------------------------------------------------------------------------------- /packages/store/src/selectors/private_api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * These types are exported privately for developers who work on advanced 3 | * utilities and may need to use these types externally. For instance, 4 | * they may have a function that accepts selector functions and returns 5 | * signals with the type corresponding to the return type of those selectors. 6 | */ 7 | export { 8 | ɵSelectorFunc, 9 | ɵStateSelector, 10 | ɵSelectorDef, 11 | ɵSelectorReturnType 12 | } from './selector-types.util'; 13 | -------------------------------------------------------------------------------- /packages/store/src/selectors/selector-models.ts: -------------------------------------------------------------------------------- 1 | import { ɵSharedSelectorOptions, ɵSelectFromRootState } from '@ngxs/store/internals'; 2 | 3 | export interface CreationMetadata { 4 | containerClass: any; 5 | selectorName: string; 6 | getSelectorOptions?: () => ɵSharedSelectorOptions; 7 | } 8 | 9 | export interface RuntimeSelectorInfo { 10 | selectorOptions: ɵSharedSelectorOptions; 11 | argumentSelectorFunctions: ɵSelectFromRootState[]; 12 | } 13 | -------------------------------------------------------------------------------- /packages/store/src/standalone-features/feature-providers.ts: -------------------------------------------------------------------------------- 1 | import { Provider } from '@angular/core'; 2 | import { ɵStateClass } from '@ngxs/store/internals'; 3 | 4 | import { FEATURE_STATE_TOKEN } from '../symbols'; 5 | import { PluginManager } from '../plugin-manager'; 6 | 7 | /** 8 | * This function provides the required providers when calling `NgxsModule.forFeature` 9 | * or `provideStates`. It is shared between the NgModule and standalone APIs. 10 | */ 11 | export function getFeatureProviders(states: ɵStateClass[]): Provider[] { 12 | return [ 13 | PluginManager, 14 | ...states, 15 | { 16 | provide: FEATURE_STATE_TOKEN, 17 | multi: true, 18 | useValue: states 19 | } 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/src/standalone-features/index.ts: -------------------------------------------------------------------------------- 1 | export { provideStore } from './provide-store'; 2 | export { provideStates } from './provide-states'; 3 | export { withNgxsPlugin } from './plugin'; 4 | export { withNgxsPreboot } from './preboot'; 5 | -------------------------------------------------------------------------------- /packages/store/src/standalone-features/root-guard.ts: -------------------------------------------------------------------------------- 1 | import { inject, InjectionToken } from '@angular/core'; 2 | 3 | export const ROOT_STORE_GUARD = /* @__PURE__ */ new InjectionToken('ROOT_STORE_GUARD', { 4 | providedIn: 'root', 5 | factory: () => ({ initialized: false }) 6 | }); 7 | 8 | export function assertRootStoreNotInitialized(): void { 9 | const rootStoreGuard = inject(ROOT_STORE_GUARD); 10 | if (rootStoreGuard.initialized) { 11 | throw new Error('provideStore() should only be called once.'); 12 | } 13 | rootStoreGuard.initialized = true; 14 | } 15 | -------------------------------------------------------------------------------- /packages/store/src/standalone-features/root-providers.ts: -------------------------------------------------------------------------------- 1 | import { APP_BOOTSTRAP_LISTENER, Provider, inject } from '@angular/core'; 2 | import { ɵStateClass, ɵNgxsAppBootstrappedState } from '@ngxs/store/internals'; 3 | 4 | import { NgxsModuleOptions, ROOT_STATE_TOKEN, NGXS_OPTIONS } from '../symbols'; 5 | 6 | /** 7 | * This function provides the required providers when invoking `NgxsModule.forRoot` 8 | * or `provideStore`. It is shared between the NgModule and standalone APIs. 9 | */ 10 | export function getRootProviders( 11 | states: ɵStateClass[], 12 | options: NgxsModuleOptions 13 | ): Provider[] { 14 | return [ 15 | ...states, 16 | { 17 | provide: ROOT_STATE_TOKEN, 18 | useValue: states 19 | }, 20 | { 21 | provide: APP_BOOTSTRAP_LISTENER, 22 | useFactory: () => { 23 | const appBootstrappedState = inject(ɵNgxsAppBootstrappedState); 24 | return () => appBootstrappedState.bootstrap(); 25 | }, 26 | multi: true 27 | }, 28 | { 29 | provide: NGXS_OPTIONS, 30 | useValue: options 31 | } 32 | ]; 33 | } 34 | -------------------------------------------------------------------------------- /packages/store/src/utils/create-dispatch-map.ts: -------------------------------------------------------------------------------- 1 | import type { Observable } from 'rxjs'; 2 | 3 | import { dispatch } from './dispatch'; 4 | 5 | import { ActionDef } from '../actions/symbols'; 6 | 7 | export type ActionMap = Record>; 8 | 9 | export function createDispatchMap(actionMap: T) { 10 | return Object.entries(actionMap).reduce((accumulator, [key, ActionType]) => { 11 | (accumulator as any)[key] = dispatch(ActionType); 12 | return accumulator; 13 | }, {}) as { 14 | // This is inlined to enhance developer experience. 15 | // If we were to switch to another type, such as 16 | // `type ActionMapReturnType = { ... }`, code editors 17 | // would display the return type simply as `ActionMapReturnType`, rather 18 | // than presenting it as an object with properties that correspond to 19 | // functions returning observables. 20 | readonly [K in keyof T]: (...args: ConstructorParameters) => Observable; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/store/src/utils/create-select-map.ts: -------------------------------------------------------------------------------- 1 | import { Signal, inject } from '@angular/core'; 2 | 3 | import { Store } from '../store'; 4 | import { TypedSelector, ɵSelectorReturnType } from '../selectors'; 5 | 6 | export type SelectorMap = Record>; 7 | 8 | export function createSelectMap(selectorMap: T) { 9 | const store = inject(Store); 10 | 11 | return Object.entries(selectorMap).reduce((accumulator, [key, selector]) => { 12 | (accumulator as any)[key] = store.selectSignal(selector); 13 | return accumulator; 14 | }, {}) as { 15 | // This is inlined to enhance developer experience. 16 | // If we were to switch to another type, such as 17 | // `type SelectorMapReturnType = { ... }`, code editors 18 | // would display the return type simply as `SelectorMapReturnType`, rather 19 | // than presenting it as an object with properties that correspond to 20 | // signals that keep store selected value. 21 | readonly [K in keyof T]: Signal<ɵSelectorReturnType>; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/store/src/utils/dispatch.ts: -------------------------------------------------------------------------------- 1 | import { inject } from '@angular/core'; 2 | 3 | import { Store } from '../store'; 4 | import { ActionDef } from '../actions/symbols'; 5 | 6 | export function dispatch(ActionType: ActionDef) { 7 | const store = inject(Store); 8 | return (...args: TArgs) => store.dispatch(new ActionType(...args)); 9 | } 10 | -------------------------------------------------------------------------------- /packages/store/src/utils/freeze.ts: -------------------------------------------------------------------------------- 1 | import { ɵhasOwnProperty } from '@ngxs/store/internals'; 2 | 3 | /** 4 | * Object freeze code 5 | * https://github.com/jsdf/deep-freeze 6 | */ 7 | export const deepFreeze = (o: any) => { 8 | Object.freeze(o); 9 | 10 | const oIsFunction = typeof o === 'function'; 11 | 12 | Object.getOwnPropertyNames(o).forEach(function (prop) { 13 | if ( 14 | ɵhasOwnProperty(o, prop) && 15 | (oIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true) && 16 | o[prop] !== null && 17 | (typeof o[prop] === 'object' || typeof o[prop] === 'function') && 18 | !Object.isFrozen(o[prop]) 19 | ) { 20 | deepFreeze(o[prop]); 21 | } 22 | }); 23 | 24 | return o; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/store/src/utils/public_api.ts: -------------------------------------------------------------------------------- 1 | export { select } from './select'; 2 | export { dispatch } from './dispatch'; 3 | export { SelectorMap, createSelectMap } from './create-select-map'; 4 | export { ActionMap, createDispatchMap } from './create-dispatch-map'; 5 | export { lazyProvider } from './lazy-provider'; 6 | -------------------------------------------------------------------------------- /packages/store/src/utils/select.ts: -------------------------------------------------------------------------------- 1 | import { Signal, inject } from '@angular/core'; 2 | 3 | import { Store } from '../store'; 4 | import { TypedSelector } from '../selectors'; 5 | 6 | /** 7 | * This function serves as a utility and has multiple purposes. 8 | * Firstly, it allows you to select properties from the state class 9 | * without having to inject the store class and use `this.store.selectSignal`, 10 | * resulting in a more concise implementation. Secondly, it can be used with 11 | * other solutions such as NgRx signal store with its `signalStoreFeature` or 12 | * `withComputed` functionalities. 13 | * 14 | * Please note that it's named `select` instead of `selectSignal` because 15 | * signals are evolving into first-class primitives in Angular, displacing other 16 | * primitives such as observables. Observables represent a stream of events, 17 | * whereas signals represent a single value changing over time. 18 | */ 19 | export function select(selector: TypedSelector): Signal { 20 | return inject(Store).selectSignal(selector); 21 | } 22 | -------------------------------------------------------------------------------- /packages/store/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/store/tests/__snapshots__/preboot-wait-stable.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`preboot feature + stable should wait for app to become stable 1`] = ` 4 | "
[ 5 | "Angola", 6 | "Namibia", 7 | "Botswana", 8 | "Zambia" 9 | ]
" 10 | `; 11 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/counter.state.ts: -------------------------------------------------------------------------------- 1 | import { State, Action, Selector, StateContext } from '../../src/public_api'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | export class Increment { 5 | static readonly type = 'INCREMENT'; 6 | } 7 | export class Decrement { 8 | static readonly type = 'DECREMENT'; 9 | } 10 | 11 | export class MathService { 12 | add(val: number, count: number) { 13 | return val + count; 14 | } 15 | 16 | subtract(val: number, count: number) { 17 | return val - count; 18 | } 19 | } 20 | 21 | @State({ 22 | name: 'counter', 23 | defaults: 0 24 | }) 25 | @Injectable() 26 | export class CounterState { 27 | @Selector() 28 | static getCounter(state: number) { 29 | return state; 30 | } 31 | 32 | constructor(private math: MathService) {} 33 | 34 | @Action(Increment) 35 | increment(ctx: StateContext) { 36 | ctx.setState(state => this.math.add(state, 1)); 37 | } 38 | 39 | @Action(Decrement) 40 | decrement(ctx: StateContext) { 41 | ctx.setState(state => this.math.subtract(state, 1)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/macrotask.ts: -------------------------------------------------------------------------------- 1 | export function macrotask() { 2 | // We explicitly provide 5 ms to wait until RxJS `SafeSubscriber` 3 | // handles the error. The `SafeSubscriber` re-throws errors asynchronously, 4 | // it's the following: `setTimeout(() => { throw error })`. 5 | return new Promise(resolve => setTimeout(resolve, 5)); 6 | } 7 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/make-rxjs-timeout-provider-synchronous.ts: -------------------------------------------------------------------------------- 1 | import { timeoutProvider } from 'rxjs/internal/scheduler/timeoutProvider'; 2 | 3 | export function makeRxJSTimeoutProviderSynchronous(): void { 4 | beforeAll(() => { 5 | // RxJS would report unhandled errors asynchronusly by default, 6 | // let's report them synchronously in unit tests. 7 | timeoutProvider.delegate = { 8 | setTimeout: handler => { 9 | handler(); 10 | return 0; 11 | }, 12 | clearTimeout: () => {} 13 | }; 14 | }); 15 | 16 | afterAll(() => { 17 | timeoutProvider.delegate = undefined; 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/promise-test-helper.ts: -------------------------------------------------------------------------------- 1 | export function createPromiseTestHelper() { 2 | type MarkResolvedFn = (result: T | PromiseLike) => void; 3 | type MarkRejectedFn = (reason?: any) => void; 4 | let resolveFn: MarkResolvedFn = () => {}; 5 | let rejectFn: MarkRejectedFn = () => {}; 6 | 7 | const promise = new Promise((resolve, reject) => { 8 | resolveFn = resolve; 9 | rejectFn = reject; 10 | }); 11 | return { 12 | promise, 13 | markPromiseResolved(...args: Parameters) { 14 | resolveFn(...args); 15 | resolveFn = () => {}; 16 | }, 17 | markPromiseRejected(reason?: any) { 18 | rejectFn(reason); 19 | rejectFn = () => {}; 20 | } 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/simple.state.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { State, Action, Selector } from '../../src/public_api'; 3 | 4 | export class UpdateValue { 5 | static readonly type = 'UPDATE_VALUE'; 6 | 7 | constructor(public readonly value: string) {} 8 | } 9 | 10 | @State({ 11 | name: 'simple', 12 | defaults: '' 13 | }) 14 | @Injectable() 15 | export class SimpleState { 16 | @Selector() 17 | static getSimple(state: string) { 18 | return state; 19 | } 20 | 21 | @Action(UpdateValue) 22 | updateValue(_: string, action: UpdateValue) { 23 | return action.value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/todo.state.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { State, Action, Selector, StateContext } from '../../src/public_api'; 3 | 4 | export class AddTodo { 5 | static readonly type = 'ADD_TODO'; 6 | 7 | constructor(public readonly todo: string) {} 8 | } 9 | export class RemoveTodo { 10 | static readonly type = 'REMOVE_TODO'; 11 | 12 | constructor(public readonly index: number) {} 13 | } 14 | 15 | @State({ 16 | name: 'todos', 17 | defaults: [] 18 | }) 19 | @Injectable() 20 | export class TodoState { 21 | @Selector() 22 | static getTodos(state: string[]) { 23 | return state; 24 | } 25 | 26 | @Action(AddTodo) 27 | addTodo(ctx: StateContext, action: AddTodo) { 28 | const state = ctx.getState(); 29 | ctx.setState([action.todo, ...state]); 30 | } 31 | 32 | @Action(RemoveTodo) 33 | removeTodo(ctx: StateContext, action: RemoveTodo) { 34 | const state = ctx.getState(); 35 | ctx.setState(state.filter((_, index) => index !== action.index)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/store/tests/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, ErrorHandler } from '@angular/core'; 2 | 3 | @Injectable() 4 | export class NoopErrorHandler implements ErrorHandler { 5 | handleError(_: any) { 6 | /* noop*/ 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/store/tests/utils/fixtures/lazy-provider-default-export.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { provideStates, State } from '@ngxs/store'; 3 | 4 | @State({ 5 | name: 'products', 6 | defaults: [] 7 | }) 8 | @Injectable() 9 | class ProductsState {} 10 | 11 | const productsStateProvider = provideStates([ProductsState]); 12 | 13 | export default productsStateProvider; 14 | -------------------------------------------------------------------------------- /packages/store/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/store/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/store/tsconfig.schematics.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": ["es2018", "dom"], 5 | "declaration": true, 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedParameters": true, 13 | "noUnusedLocals": true, 14 | "rootDir": "schematics", 15 | "outDir": "../../@ngxs/store/schematics", 16 | "skipDefaultLibCheck": true, 17 | "skipLibCheck": true, 18 | "sourceMap": true, 19 | "strictNullChecks": true, 20 | "target": "es6", 21 | "types": ["jest", "node"] 22 | }, 23 | "include": ["schematics/**/*"], 24 | "exclude": [ 25 | "schematics/templates/**/*", 26 | "schematics/**/*.test.ts", 27 | "schematics/**/*.spec.ts", 28 | "schematics/src/_testing/**/*" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /packages/store/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/store/types/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path'); 2 | 3 | /** @type {import("@types/eslint").Linter.Config} */ 4 | module.exports = { 5 | parser: '@typescript-eslint/parser', 6 | parserOptions: { 7 | project: [path.join(__dirname, 'tsconfig.json')], 8 | ecmaVersion: 2022, 9 | sourceType: 'module' 10 | }, 11 | extends: ['plugin:expect-type/recommended'], 12 | plugins: ['eslint-plugin-expect-type'], 13 | overrides: [ 14 | { 15 | files: ['*.ts'], 16 | rules: { 17 | 'prefer-const': 'off' 18 | } 19 | } 20 | ] 21 | }; 22 | -------------------------------------------------------------------------------- /packages/store/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /* file: packages/store/types/index.d.ts */ 2 | // TypeScript Version: 3.4 3 | -------------------------------------------------------------------------------- /packages/store/types/tests/utils/assert-type.ts: -------------------------------------------------------------------------------- 1 | export type Callback = (...args: any[]) => T; 2 | 3 | export function assertType(callback: Callback): T { 4 | return callback(); 5 | } 6 | -------------------------------------------------------------------------------- /packages/store/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitAny": true, 5 | "noImplicitThis": true, 6 | "strictNullChecks": true, 7 | "strictFunctionTypes": true, 8 | "noEmit": true, 9 | "lib": ["es2017", "dom"], 10 | "sourceMap": true, 11 | "declaration": false, 12 | "moduleResolution": "node", 13 | "emitDecoratorMetadata": true, 14 | "experimentalDecorators": true, 15 | "strictPropertyInitialization": false, 16 | "target": "es5", 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "typeRoots": ["node_modules/@types"], 20 | "baseUrl": ".", 21 | "paths": { 22 | "@angular/core": ["../../../node_modules/@angular/core/core.d.ts"], 23 | "@ngxs/store/operators": ["../../../@ngxs/store/operators/index.d.ts"], 24 | "@ngxs/store/internals": ["../../../@ngxs/store/internals/index.d.ts"], 25 | "@ngxs/*": ["../../../@ngxs/*/index.d.ts"] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/websocket-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @ngxs/websocket-plugin 2 | 3 | WebSocket plugin for NGXS. See [repo](https://github.com/ngxs/store) for more info. 4 | -------------------------------------------------------------------------------- /packages/websocket-plugin/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The public api for consumers of @ngxs/websocket-plugin 3 | */ 4 | export * from './src/public_api'; 5 | 6 | /** 7 | * The private api for consumers of @ngxs/websocket-plugin 8 | */ 9 | export * from './src/private_api'; 10 | -------------------------------------------------------------------------------- /packages/websocket-plugin/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: 'websocket-plugin', 3 | preset: '../../jest.preset.js', 4 | setupFilesAfterEnv: ['/test-setup.ts'], 5 | globals: {}, 6 | coverageDirectory: '../../coverage/packages/websocket-plugin', 7 | transform: { 8 | '^.+\\.(ts|mjs|js|html)$': [ 9 | 'jest-preset-angular', 10 | { 11 | isolatedModules: true, 12 | tsconfig: '/tsconfig.spec.json', 13 | stringifyContentPathRegex: '\\.(html|svg)$' 14 | } 15 | ] 16 | }, 17 | snapshotSerializers: [ 18 | 'jest-preset-angular/build/serializers/no-ng-attributes', 19 | 'jest-preset-angular/build/serializers/ng-snapshot', 20 | 'jest-preset-angular/build/serializers/html-comment' 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /packages/websocket-plugin/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "lib": { 4 | "entryFile": "index.ts" 5 | }, 6 | "dest": "../../@ngxs/websocket-plugin" 7 | } 8 | -------------------------------------------------------------------------------- /packages/websocket-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngxs/websocket-plugin", 3 | "description": "WebSocket plugin for @ngxs/store", 4 | "version": "0.0.0", 5 | "sideEffects": true, 6 | "peerDependencies": { 7 | "@ngxs/store": "^0.0.0", 8 | "@angular/core": ">=19.0.0 <20.0.0", 9 | "rxjs": ">=6.5.5" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/websocket-plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "websocket-plugin", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "packages/websocket-plugin", 5 | "projectType": "library", 6 | "targets": { 7 | "build": { 8 | "executor": "@angular-devkit/build-angular:ng-packagr", 9 | "outputs": ["{workspaceRoot}/@ngxs/websocket-plugin"], 10 | "options": { 11 | "tsConfig": "tsconfig.build.json", 12 | "project": "packages/websocket-plugin/ng-package.json" 13 | } 14 | }, 15 | "test": { 16 | "executor": "@nx/jest:jest", 17 | "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], 18 | "options": { 19 | "jestConfig": "packages/websocket-plugin/jest.config.js" 20 | } 21 | }, 22 | "lint": { 23 | "executor": "@nx/eslint:lint" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/websocket-plugin/src/private_api.ts: -------------------------------------------------------------------------------- 1 | export { NGXS_WEBSOCKET_OPTIONS as ɵNGXS_WEBSOCKET_OPTIONS } from './symbols'; 2 | -------------------------------------------------------------------------------- /packages/websocket-plugin/src/providers.ts: -------------------------------------------------------------------------------- 1 | import { APP_INITIALIZER } from '@angular/core'; 2 | 3 | import { WebSocketHandler } from './websocket-handler'; 4 | import { USER_OPTIONS, NGXS_WEBSOCKET_OPTIONS, NgxsWebSocketPluginOptions } from './symbols'; 5 | 6 | export function ɵwebsocketOptionsFactory(options: NgxsWebSocketPluginOptions) { 7 | return { 8 | reconnectInterval: 5000, 9 | reconnectAttempts: 10, 10 | typeKey: 'type', 11 | deserializer(e: MessageEvent) { 12 | return JSON.parse(e.data); 13 | }, 14 | serializer(value: any) { 15 | return JSON.stringify(value); 16 | }, 17 | ...options 18 | }; 19 | } 20 | 21 | export function ɵgetProviders(options?: NgxsWebSocketPluginOptions) { 22 | return [ 23 | { provide: USER_OPTIONS, useValue: options }, 24 | { 25 | provide: NGXS_WEBSOCKET_OPTIONS, 26 | useFactory: ɵwebsocketOptionsFactory, 27 | deps: [USER_OPTIONS] 28 | }, 29 | { 30 | provide: APP_INITIALIZER, 31 | useFactory: () => () => {}, 32 | deps: [WebSocketHandler], 33 | multi: true 34 | } 35 | ]; 36 | } 37 | -------------------------------------------------------------------------------- /packages/websocket-plugin/src/public_api.ts: -------------------------------------------------------------------------------- 1 | export { NgxsWebSocketPluginModule, withNgxsWebSocketPlugin } from './websocket.module'; 2 | export { 3 | ConnectWebSocket, 4 | WebSocketMessageError, 5 | DisconnectWebSocket, 6 | WebSocketDisconnected, 7 | SendWebSocketMessage, 8 | WebSocketConnectionUpdated, 9 | WebSocketConnected 10 | } from './symbols'; 11 | -------------------------------------------------------------------------------- /packages/websocket-plugin/src/websocket.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NgModule, 3 | ModuleWithProviders, 4 | EnvironmentProviders, 5 | makeEnvironmentProviders 6 | } from '@angular/core'; 7 | 8 | import { ɵgetProviders } from './providers'; 9 | import { NgxsWebSocketPluginOptions } from './symbols'; 10 | 11 | @NgModule() 12 | export class NgxsWebSocketPluginModule { 13 | static forRoot( 14 | options?: NgxsWebSocketPluginOptions 15 | ): ModuleWithProviders { 16 | return { 17 | ngModule: NgxsWebSocketPluginModule, 18 | providers: ɵgetProviders(options) 19 | }; 20 | } 21 | } 22 | 23 | export function withNgxsWebSocketPlugin( 24 | options?: NgxsWebSocketPluginOptions 25 | ): EnvironmentProviders { 26 | return makeEnvironmentProviders(ɵgetProviders(options)); 27 | } 28 | -------------------------------------------------------------------------------- /packages/websocket-plugin/test-setup.ts: -------------------------------------------------------------------------------- 1 | import './../../setup-jest-preset'; 2 | -------------------------------------------------------------------------------- /packages/websocket-plugin/tests/typings.d.ts: -------------------------------------------------------------------------------- 1 | type WebSocketMessage = string | Blob | ArrayBuffer | ArrayBufferView; 2 | 3 | declare module 'mock-socket' { 4 | interface WebSocketCallbackMap { 5 | close: () => void; 6 | error: (err: Error) => void; 7 | message: (message: WebSocketMessage) => void; 8 | open: () => void; 9 | } 10 | 11 | interface WebSocket { 12 | // the `WebSocket` from the `mock-socket` package actually has this method! 13 | // but they didn't add this method to the interface! 14 | // that's why we do "module augmentation" 15 | // so we don't have to use `any` type like `(socket: any)` 16 | on(type: K, callback: WebSocketCallbackMap[K]): void; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/websocket-plugin/tests/utils.ts: -------------------------------------------------------------------------------- 1 | import { WebSocket } from 'mock-socket'; 2 | 3 | export function mockWebSocket(): void { 4 | const OriginalWebSocket = window.WebSocket; 5 | 6 | beforeEach(() => { 7 | (window as any).WebSocket = WebSocket; 8 | }); 9 | 10 | afterAll(() => { 11 | window.WebSocket = OriginalWebSocket; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /packages/websocket-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | }, 9 | { 10 | "path": "./tsconfig.spec.json" 11 | } 12 | ], 13 | "compilerOptions": { 14 | "forceConsistentCasingInFileNames": true, 15 | "strict": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/websocket-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["test-setup.ts", "**/*.spec.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/websocket-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.spec.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /publication/announcement-tweet.txt: -------------------------------------------------------------------------------- 1 | ⚪️💙💙💙💙⚪️💙💙💙💙⚪️ 2 | ⚪️💙⚪️⚪️💙⚪️💙⚪️⚪️⚪️⚪️ 3 | ⚪️💙⚪️⚪️💙⚪️💙⚪️💙💙⚪️ 4 | ⚪️💙⚪️⚪️💙⚪️💙⚪️⚪️💙⚪️ 5 | ⚪️💙⚪️⚪️💙⚪️💙💙💙💙⚪️ 6 | ⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️⚪️ 7 | ⚪️💙⚪️⚪️💙⚪️💙💙💙💙⚪️ 8 | ⚪️⚪️💙💙⚪️⚪️💙⚪️⚪️⚪️⚪️ 9 | ⚪️⚪️💙💙⚪️⚪️💙💙💙💙⚪️ 10 | ⚪️⚪️💙💙⚪️⚪️⚪️⚪️⚪️💙⚪️ 11 | ⚪️💙⚪️⚪️💙⚪️💙💙💙💙⚪️ 12 | 🚀#NGXS vX.Y.Z released! 🚀 13 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "rangeStrategy": "bump", 3 | "separateMajorMinor": false, 4 | "timezone": "UTC", 5 | "baseBranches": ["master"], 6 | "ignoreDeps": ["@types/node"], 7 | "automerge": true, 8 | "packageFiles": ["package.json", "packages/**/package.json"], 9 | "major": { 10 | "devDependencies": { 11 | "enabled": false 12 | } 13 | }, 14 | "packageRules": [ 15 | { 16 | "extends": ["monorepo:angular", "monorepo:angularcli"], 17 | "groupName": "@angular", 18 | "schedule": ["before 3am on the first day of the month"] 19 | }, 20 | { 21 | "packageNames": ["typescript"], 22 | "groupName": "typescript", 23 | "updateTypes": "patch", 24 | "schedule": ["before 3am on the first day of the month"] 25 | }, 26 | { 27 | "packagePatterns": ["*"], 28 | "excludePackageNames": ["typescript"], 29 | "groupName": "all dependencies", 30 | "groupSlug": "all", 31 | "schedule": ["before 3am on the first day of the month"] 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { dts } from 'rollup-plugin-dts'; 2 | 3 | import { getEntryPointsAndDtsToRemove } from './build/collect-dts.mjs'; 4 | 5 | export default getEntryPointsAndDtsToRemove().entryPoints.map(entryPoint => ({ 6 | input: entryPoint, 7 | output: [{ file: entryPoint, format: 'es' }], 8 | plugins: [dts()] 9 | })); 10 | -------------------------------------------------------------------------------- /tools/publish-dev-builds.ts: -------------------------------------------------------------------------------- 1 | import { parse, SemVer } from 'semver'; 2 | import { execute, publishAllPackagesToNpm } from './utils'; 3 | 4 | async function main() { 5 | const json = require('../package.json'); 6 | 7 | // determine commit from either circle ci or last git commit 8 | let commit = process.env.CIRCLE_SHA1; 9 | if (!commit) { 10 | const lastCommit = await execute('git rev-parse HEAD'); 11 | commit = lastCommit.toString().trim(); 12 | } 13 | 14 | // shorten commit 15 | commit = commit!.slice(0, 7); 16 | 17 | // construct new version from base version 2.0.0 to become 2.0.0+dev.shortsha 18 | const version: SemVer = parse(json.version)!; 19 | const newVersion = `${version.major}.${version.minor}.${version.patch}-dev.master-${commit}`; 20 | 21 | console.log('publishing new version', newVersion); 22 | 23 | // run through all our packages and push them to npm 24 | await publishAllPackagesToNpm(newVersion, 'dev'); 25 | } 26 | 27 | main().catch(err => { 28 | console.error(err); 29 | process.exit(1); 30 | }); 31 | -------------------------------------------------------------------------------- /tools/publish-tagged-builds.ts: -------------------------------------------------------------------------------- 1 | import { publishAllPackagesToNpm } from './utils'; 2 | 3 | async function main() { 4 | const json = require('../package.json'); 5 | console.log('publishing new version', json.version); 6 | 7 | // run through all our packages and push them to npm 8 | await publishAllPackagesToNpm(json.version, 'latest'); 9 | } 10 | 11 | main().catch(err => { 12 | console.error(err); 13 | process.exit(1); 14 | }); 15 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "out-tsc/lib", 5 | "target": "es2022", 6 | "module": "es2022", 7 | "declaration": true, 8 | "inlineSources": true, 9 | "importHelpers": true, 10 | "skipLibCheck": true, 11 | "stripInternal": true, 12 | "paths": { 13 | "@ngxs/*": ["@ngxs/*"] 14 | } 15 | }, 16 | "angularCompilerOptions": { 17 | "compilationMode": "partial", 18 | "annotateForClosureCompiler": true, 19 | "skipTemplateCodegen": true, 20 | "strictMetadataEmit": true, 21 | "fullTemplateTypeCheck": true, 22 | "strictInjectionParameters": true, 23 | "enableResourceInlining": true 24 | }, 25 | "exclude": ["@ngxs/**", "**/*.spec.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "out-tsc/lib", 5 | "module": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "importHelpers": true 9 | }, 10 | "exclude": ["@ngxs/**", "**/*.spec.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "types": ["jest", "node"], 8 | "importHelpers": true, 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "useUnknownInCatchVariables": false 12 | }, 13 | "exclude": ["@ngxs/**", "./packages/store/types/**"], 14 | "include": ["packages/**/*.ts", "packages/**/*.spec.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /tutorials/create-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.js"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tutorials/create-app/README.md: -------------------------------------------------------------------------------- 1 | # create-app 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | -------------------------------------------------------------------------------- /tutorials/create-app/astro.config.ts: -------------------------------------------------------------------------------- 1 | import tutorialkit from '@tutorialkit/astro'; 2 | import { defineConfig } from 'astro/config'; 3 | 4 | export default defineConfig({ 5 | outDir: '../../@tutorials/create-app', 6 | devToolbar: { 7 | enabled: false 8 | }, 9 | integrations: [tutorialkit()] 10 | }); 11 | -------------------------------------------------------------------------------- /tutorials/create-app/env.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/triple-slash-reference 2 | /// 3 | /// 4 | /// 5 | -------------------------------------------------------------------------------- /tutorials/create-app/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-app", 3 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 4 | "sourceRoot": "tutorials/create-app/src", 5 | "projectType": "library", 6 | "tags": [], 7 | "targets": { 8 | "lint": { 9 | "executor": "@nx/eslint:lint" 10 | }, 11 | "serve": { 12 | "executor": "nx:run-script", 13 | "options": { 14 | "script": "dev" 15 | } 16 | }, 17 | "build": { 18 | "executor": "nx:run-script", 19 | "options": { 20 | "script": "build" 21 | } 22 | }, 23 | "check": { 24 | "executor": "nx:run-script", 25 | "options": { 26 | "script": "check" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tutorials/create-app/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { chapterSchema, lessonSchema, partSchema, tutorialSchema } from '@tutorialkit/types'; 2 | import { defineCollection } from 'astro:content'; 3 | 4 | const tutorial = defineCollection({ 5 | type: 'content', 6 | schema: tutorialSchema 7 | .strict() 8 | .or(partSchema.strict()) 9 | .or(chapterSchema.strict()) 10 | .or(lessonSchema.strict()) 11 | }); 12 | 13 | export const collections = { tutorial }; 14 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/1-welcome/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: chapter 3 | title: Welcome 4 | --- 5 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/1-using-schematics/_files/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngxs-project", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "ng serve", 8 | "build": "ng build" 9 | }, 10 | "devDependencies": { 11 | "@angular-devkit/build-angular": "^18.0.7", 12 | "@angular/animations": "^18.0.6", 13 | "@angular/cdk": "^18.0.6", 14 | "@angular/cli": "^18.0.7", 15 | "@angular/common": "^18.0.6", 16 | "@angular/compiler": "^18.0.6", 17 | "@angular/compiler-cli": "^18.0.6", 18 | "@angular/core": "^18.0.6", 19 | "@angular/material": "^18.0.6", 20 | "@angular/platform-browser": "^18.0.6", 21 | "@angular/platform-browser-dynamic": "^18.0.6", 22 | "@ngxs/store": "^18.1.1", 23 | "rxjs": "~7.8.1", 24 | "tslib": "^2.0.0", 25 | "zone.js": "~0.14.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/1-using-schematics/content.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: lesson 3 | title: Installation using schematics 4 | focus: /package.json 5 | --- 6 | 7 | # Installation 8 | 9 | In our Angular application, we should install the `@ngxs/store` using `ng-add` schematic. 10 | 11 | > The dependency of NGXS v.18 is Angular >= v.17 12 | 13 | The command is: 14 | 15 | ```bash 16 | ng add @ngxs/store 17 | ``` 18 | 19 | This command will update the `package.json` file with the `@ngxs/store` dependency 20 | 21 | --- 22 | 23 | 🎧 **Exercises** 24 | 25 | - There are no exercises in this lesson 26 | 27 | --- 28 | 29 | 👀 **Observe** 30 | 31 | - The **package.json** file has the related dependency 32 | 33 | --- 34 | 35 | 📚 **Read more** 36 | 37 | - Read more in the NGXS documentation about the installation 38 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/2-create-store/_files/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { TodoState } from './todo/store/todo.state'; 2 | import { provideHttpClient } from '@angular/common/http'; 3 | import { provideAnimations } from '@angular/platform-browser/animations'; 4 | import { ApplicationConfig } from '@angular/core'; 5 | import { provideStore } from '@ngxs/store'; 6 | 7 | export const config: ApplicationConfig = { 8 | providers: [provideAnimations(), provideHttpClient(), provideStore([])] 9 | }; 10 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/2-create-store/_files/src/app/todo/store/todo.actions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Note! This is the auto-generated action. We will replace this with our own action.*/ 3 | export class StoreAction { 4 | static readonly type = '[Store] Add item'; 5 | constructor(readonly payload: string) {} 6 | } 7 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/2-create-store/_files/src/app/todo/store/todo.state.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { provideStore, Store } from '@ngxs/store'; 3 | import { TodoState, TodoStateModel } from './todo.state'; 4 | 5 | describe('Store store', () => { 6 | let store: Store; 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | providers: [provideStore([TodoState])] 10 | }); 11 | 12 | store = TestBed.inject(Store); 13 | }); 14 | 15 | // it('should create an action and add an item', () => { 16 | // const expected: TodoStateModel = { 17 | // todos: [] 18 | // }; 19 | // store.dispatch(new StoreAction('item-1')); 20 | // const actual = store.selectSnapshot(StoreState.getState); 21 | // expect(actual).toEqual(expected); 22 | // }); 23 | }); 24 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/2-create-store/_solution/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { TodoState } from './todo/store/todo.state'; 2 | import { provideHttpClient } from '@angular/common/http'; 3 | import { provideAnimations } from '@angular/platform-browser/animations'; 4 | import { ApplicationConfig } from '@angular/core'; 5 | import { provideStore } from '@ngxs/store'; 6 | 7 | export const config: ApplicationConfig = { 8 | providers: [provideAnimations(), provideHttpClient(), provideStore([TodoState])] 9 | }; 10 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/2-installation/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: chapter 3 | title: Installation 4 | --- 5 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/1-create-action/_files/src/app/todo/store/todo.actions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Note! This is the auto-generated action. We will replace this with our own action.*/ 3 | export class StoreAction { 4 | static readonly type = '[Store] Add item'; 5 | constructor(readonly payload: string) {} 6 | } 7 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/1-create-action/_solution/src/app/todo/store/todo.actions.ts: -------------------------------------------------------------------------------- 1 | export class GetTodos { 2 | static readonly type = '[Todo] Get'; 3 | } 4 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/1-create-action/content.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: lesson 3 | title: Create action 4 | focus: /src/app/todo/store/todo.actions.ts 5 | --- 6 | 7 | # Create Action 8 | 9 | To fetch the data from the source, we have to dispatch an action which will initiate an HTTP GET method. 10 | 11 | To accomplish this, we should create and dispatch an `Action`. 12 | 13 | --- 14 | 15 | 🎧 **Exercises** 16 | 17 | 1. Create the Get Action class in the `todo.actions.ts` file. 18 | 19 | ```ts 20 | export class GetTodos { 21 | static readonly type = '[Todo] Get'; 22 | } 23 | ``` 24 | 25 | --- 26 | 27 | 👀 **Observe** 28 | 29 | - Each Action is a separate class 30 | 31 | --- 32 | 33 | 📚 **Read more** 34 | 35 | - Read more in the NGXS documentation here 36 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/2-handle-the-action/_files/src/app/todo/store/todo.actions.ts: -------------------------------------------------------------------------------- 1 | export class GetTodos { 2 | static readonly type = '[Todo] Get'; 3 | } 4 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/3-create-a-query/_files/src/app/todo/store/todo.queries.ts: -------------------------------------------------------------------------------- 1 | import { createPropertySelectors, createSelector } from '@ngxs/store'; 2 | import { TodoState, TodoStateModel } from './todo.state'; 3 | 4 | export class TodoSelectors { 5 | static getSlices = createPropertySelectors(TodoState); 6 | } 7 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/3-create-a-query/_solution/src/app/todo/store/todo.queries.ts: -------------------------------------------------------------------------------- 1 | import { createPropertySelectors, createSelector } from '@ngxs/store'; 2 | import { TodoState, TodoStateModel } from './todo.state'; 3 | 4 | export class TodoSelectors { 5 | static getSlices = createPropertySelectors(TodoState); 6 | 7 | static getCompletedTodos = createSelector([TodoSelectors.getSlices.todos], todos => 8 | todos.filter(todo => todo.completed) 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/3-get-todo-items/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: chapter 3 | title: Get todo items 4 | --- 5 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/1-create-a-todo-app/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: part 3 | title: The app 4 | # mainCommand: ['npm i @ngxs/store', 'Install NGXS'] 5 | --- 6 | -------------------------------------------------------------------------------- /tutorials/create-app/src/content/tutorial/meta.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: tutorial 3 | template: ngxs 4 | mainCommand: ['npm run start', 'Starting Angular Application'] 5 | prepareCommands: 6 | - ['npm install', 'Installing dependencies'] 7 | --- 8 | -------------------------------------------------------------------------------- /tutorials/create-app/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/javascript.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngxs-project", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "ng serve", 8 | "build": "ng build" 9 | }, 10 | "devDependencies": { 11 | "@angular-devkit/build-angular": "^18.0.7", 12 | "@angular/animations": "^18.0.6", 13 | "@angular/cdk": "^18.0.6", 14 | "@angular/cli": "^18.0.7", 15 | "@angular/common": "^18.0.6", 16 | "@angular/compiler": "^18.0.6", 17 | "@angular/compiler-cli": "^18.0.6", 18 | "@angular/core": "^18.0.6", 19 | "@angular/material": "^18.0.6", 20 | "@angular/platform-browser": "^18.0.6", 21 | "@angular/platform-browser-dynamic": "^18.0.6", 22 | "@ngxs/store": "^18.1.1", 23 | "rxjs": "~7.8.1", 24 | "tslib": "^2.0.0", 25 | "zone.js": "~0.14.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component } from '@angular/core'; 2 | import { TodoListComponent } from './todo/todo-list/todo-list.component'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | template: ` `, 7 | styles: ``, 8 | imports: [TodoListComponent], 9 | standalone: true, 10 | changeDetection: ChangeDetectionStrategy.OnPush 11 | }) 12 | export class AppComponent { 13 | title = 'Code Shots With Profanis - Like and Subscribe :)'; 14 | } 15 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient } from '@angular/common/http'; 2 | import { ApplicationConfig } from '@angular/core'; 3 | import { provideAnimations } from '@angular/platform-browser/animations'; 4 | import { provideStore } from '@ngxs/store'; 5 | import { TodoState } from './todo/store/todo.state'; 6 | 7 | export const config: ApplicationConfig = { 8 | providers: [ 9 | provideAnimations(), 10 | provideHttpClient(), 11 | // add the TodoState to the provideStore array 12 | provideStore([]) 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/store/todo.actions.ts: -------------------------------------------------------------------------------- 1 | import { TodoModel } from '../types/todo'; 2 | 3 | export class GetTodos { 4 | static readonly type = '[Todo] Get'; 5 | } 6 | 7 | // export class UpdateComplete { 8 | // static readonly type = '[Todo] Update complete'; 9 | // constructor( 10 | // public id: number, 11 | // public todo: TodoModel, 12 | // ) {} 13 | // } 14 | 15 | // export class AddTodo { 16 | // static readonly type = '[Todo] Add'; 17 | // constructor(public todoName: string) {} 18 | // } 19 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/store/todo.queries.ts: -------------------------------------------------------------------------------- 1 | import { createPropertySelectors, createSelector } from '@ngxs/store'; 2 | import { TodoState, TodoStateModel } from './todo.state'; 3 | 4 | export class TodoSelectors { 5 | static getSlices = createPropertySelectors(TodoState); 6 | 7 | static getCompletedTodos = createSelector([TodoSelectors.getSlices.todos], todos => 8 | todos.filter(todo => todo.completed) 9 | ); 10 | 11 | // static getIncompletedTodos = createSelector( 12 | // [TodoSelectors.getSlices.todos], 13 | // (todos) => todos.filter((todo) => !todo.completed), 14 | // ); 15 | } 16 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/store/todo.state.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { provideStore, Store } from '@ngxs/store'; 3 | import { TodoState, TodoStateModel } from './todo.state'; 4 | 5 | describe('Store store', () => { 6 | let store: Store; 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | providers: [provideStore([TodoState])] 10 | }); 11 | 12 | store = TestBed.inject(Store); 13 | }); 14 | 15 | // it('should create an action and add an item', () => { 16 | // const expected: TodoStateModel = { 17 | // todos: [] 18 | // }; 19 | // store.dispatch(new StoreAction('item-1')); 20 | // const actual = store.selectSnapshot(StoreState.getState); 21 | // expect(actual).toEqual(expected); 22 | // }); 23 | }); 24 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/todo-list/todo-list.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngxs/store/6b0346eca56f43fbfb1aef0a23fc289a9119b5d7/tutorials/create-app/src/templates/ngxs/src/app/todo/todo-list/todo-list.component.scss -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/todo-list/todo-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { TodoListComponent } from './todo-list.component'; 4 | 5 | describe('TodoListComponent', () => { 6 | let component: TodoListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [TodoListComponent] 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(TodoListComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/todo.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { inject, Injectable } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { TodoModel } from './types/todo'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class TodoService { 10 | private readonly _url = 'https://jsonplaceholder.typicode.com/todos'; 11 | private _http = inject(HttpClient); 12 | 13 | get(): Observable { 14 | return this._http.get(this._url); 15 | } 16 | 17 | create(todoName: string): Observable { 18 | return this._http.post(this._url, { 19 | title: todoName, 20 | completed: false 21 | }); 22 | } 23 | 24 | updateCompleted(id: number, todo: TodoModel): Observable { 25 | return this._http.put(`${this._url}/${id}`, { 26 | ...todo 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/app/todo/types/todo.ts: -------------------------------------------------------------------------------- 1 | export interface TodoModel { 2 | userId?: number; 3 | id: number; 4 | title: string; 5 | completed: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NGXS TODO Application 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { AppComponent } from './app/app.component'; 3 | import { config } from './app/app.config'; 4 | 5 | bootstrapApplication(AppComponent, config); 6 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/src/styles.scss: -------------------------------------------------------------------------------- 1 | @import '@angular/material/prebuilt-themes/indigo-pink.css'; 2 | @import url('https://fonts.googleapis.com/icon?family=Material+Icons'); 3 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts"], 9 | "include": ["src/**/*.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /tutorials/create-app/src/templates/ngxs/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "sourceMap": true, 13 | "declaration": false, 14 | "experimentalDecorators": true, 15 | "moduleResolution": "node", 16 | "importHelpers": true, 17 | "target": "ES2022", 18 | "module": "es2020", 19 | "lib": ["es2018", "dom"], 20 | "useDefineForClassFields": false 21 | }, 22 | "angularCompilerOptions": { 23 | "strictInjectionParameters": true, 24 | "strictInputAccessModifiers": true, 25 | "strictTemplates": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tutorials/create-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "baseUrl": "./", 6 | "jsxImportSource": "react", 7 | "paths": { 8 | "@*": ["src/*"] 9 | } 10 | }, 11 | "exclude": ["dist"] 12 | } 13 | --------------------------------------------------------------------------------