diff --git a/.dependabot/config.yml b/.dependabot/config.yml index c1f8c90e1e0..2c31a823f14 100644 --- a/.dependabot/config.yml +++ b/.dependabot/config.yml @@ -1,4 +1,4 @@ -version: 1 +version: 2 update_configs: - package_manager: "javascript" directory: "/" diff --git a/.env-cmdrc b/.env-cmdrc index 7c05655209c..60fdf05f15f 100644 --- a/.env-cmdrc +++ b/.env-cmdrc @@ -5,6 +5,9 @@ "local": { "CX_BASE_URL": "https://localhost:9002" }, + "local-http": { + "CX_BASE_URL": "http://localhost:9002" + }, "ci": { "CX_BASE_URL": "https://20.83.184.244:9002" }, @@ -18,7 +21,7 @@ "CX_BASE_URL": "https://api.spartacus.rocks" }, "cdc": { - "CX_BASE_URL": "https://cdc-spa-2011-2102.demo.hybris.com", + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s1-public.model-t.myhybris.cloud", "CX_CDC": "true" }, "digital-payments": { @@ -30,15 +33,62 @@ "CX_BASE_URL": "https://api.cp96avkh5f-integrati1-d1-public.model-t.cc.commerce.ondemand.com" }, "cpq": { - "CX_BASE_URL": "https://api.cpce-teamtiger1-d2-public.model-t.cc.commerce.ondemand.com", + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s1-public.model-t.myhybris.cloud/", "CX_B2B": "true", "CX_CPQ": "true" }, + "segment-refs": { + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-p7-public.model-t.myhybris.cloud", + "CX_SEGMENT_REFS": "true" + }, "b2c": { "CX_B2B": "false", "CX_CDS": "false" }, "b2b": { "CX_B2B": "true" + }, + "s4om": { + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s8-public.model-t.myhybris.cloud", + "CX_B2B": "true", + "CX_S4OM": "true", + "CX_REQUESTED_DELIVERY_DATE": "true", + "CX_PDF_INVOICES": "true" + }, + "omf":{ + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s9-public.model-t.myhybris.cloud", + "CX_OMF": "true" + }, + "requested-delivery-date": { + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s8-public.model-t.myhybris.cloud", + "CX_REQUESTED_DELIVERY_DATE": "true" + }, + "estimated-delivery-date": { + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s5-public.model-t.myhybris.cloud", + "CX_ESTIMATED_DELIVERY_DATE": "true" + }, + "pdf-invoices": { + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s5-public.model-t.myhybris.cloud", + "CX_PDF_INVOICES": "true" + }, + "my-account-v2":{ + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s5-public.model-t.myhybris.cloud", + "CX_PDF_INVOICES": "true", + "CX_MY_ACCOUNT_V2": "true" + }, + "cdp":{ + "CX_CDP": "true", + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s5-public.model-t.myhybris.cloud", + "CX_PDF_INVOICES": "true", + "CX_MY_ACCOUNT_V2": "true" + }, + "opps":{ + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s5-public.model-t.myhybris.cloud", + "CX_OPPS": "true" + }, + "s4-service":{ + "CX_BASE_URL": "https://api.cg79x9wuu9-eccommerc1-s8-public.model-t.myhybris.cloud", + "CX_S4_SERVICE": "true", + "CX_B2B": "true" } } diff --git a/.eslintrc.json b/.eslintrc.json index ab278171cf9..308ab98249a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,23 +2,18 @@ "root": true, "overrides": [ { - "files": [ - "*.ts" - ], + "files": ["*.ts"], "parserOptions": { - "project": [ - "tsconfig.eslint.json" - ], + "project": ["tsconfig.eslint.json"], "createDefaultProgram": true }, "extends": [ - "plugin:@angular-eslint/ng-cli-compat", - "plugin:@angular-eslint/ng-cli-compat--formatting-add-on", - "plugin:@angular-eslint/template/process-inline-templates" - ], - "plugins": [ - "deprecation" + "plugin:@angular-eslint/recommended", + "plugin:@angular-eslint/template/process-inline-templates", + "./tools/eslint-plugins/legacy-ng-cli-compat", + "./tools/eslint-plugins/legacy-ng-cli-compat--formatting-add-on" ], + "plugins": ["deprecation", "@typescript-eslint", "@nx"], "rules": { "@angular-eslint/no-host-metadata-property": "off", "@typescript-eslint/no-empty-interface": "off", @@ -73,24 +68,58 @@ "id-blacklist": "off", "id-match": "off", "max-len": "off", - "linebreak-style": [ + "linebreak-style": ["error", "unix"], + "no-underscore-dangle": "off", + "@nx/enforce-module-boundaries": [ "error", - "unix" + { + "allow": [], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"], + "notDependOnLibsWithTags": ["type:app"] + }, + { + "sourceTag": "type:feature", + "notDependOnLibsWithTags": ["type:integration"] + }, + { + "sourceTag": "type:ui", + "notDependOnLibsWithTags": ["type:feature"] + }, + { + "sourceTag": "scope:core", + "notDependOnLibsWithTags": ["type:ui", "type:feature"] + } + ] + } ], - "no-underscore-dangle": "off" + "no-console": "error" } }, { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended" - ], + "files": ["*.html"], + "extends": ["plugin:@angular-eslint/template/recommended"], "rules": { "@angular-eslint/template/no-negated-async": "off", "@angular-eslint/template/eqeqeq": "error" } + }, + { + "files": ["*.module.ts"], + "rules": { + "@nx/workspace/use-provide-default-config": "error", + "@nx/workspace/use-provide-default-config-factory": "error", + "@nx/workspace/use-provide-default-feature-toggles": "error", + "@nx/workspace/use-provide-default-feature-toggles-factory": "error" + } + }, + { + "files": ["*.action*.ts"], + "rules": { + "@nx/workspace/no-ngrx-fail-action-without-error-action-implementation": "error" + } } ] } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7d19a16ffb0..a0c2cddfbe6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,11 +8,6 @@ # # More info: https://help.github.com/articles/about-codeowners/ # - -# This is ONLY INITIAL CODEOWNERS file created to test and gradually introduce ownership -# of specific features on a code level. - - # ================================================ # General rules / philosophy # ================================================ @@ -22,9 +17,7 @@ # - we enforce that only approved PRs get merged to ensure that unreviewed code doesn't get accidentally merged # - we delegate approval rights as much as possible so that we can scale better # - each group must have at least one person, but several people are preferable to avoid a single point of failure issues -# - most file groups have a global approvers group as fallback: -# - @SAP/spartacus-global-owners: for approving minor changes, large-scale refactorings, and emergency situations. -# - a small number of file groups can have very limited number of reviewers because incorrect changes to the files they guard would have serious consequences (e.g. security, public api) +# - a small number of file groups may have very limited number of reviewers because incorrect changes to the files they guard would have serious consequences (e.g. security, public api) # # Configuration nuances: # @@ -32,75 +25,59 @@ # - This approval can come from an appropriate codeowner, or any repo collaborator (person with write access) if the PR is authored by a codeowner. # - Each codeowners team must have write access to the repo, otherwise their reviews won't count. - # ================================================ -# GitHub username registry -# (just to make this file easier to understand) +# All the files in the repo. # ================================================ +* @SAP/spartacus-codeowners -# Platonn - Krzysztof Platis -# znikola - Nikola Zarić -# developpeurweb - Miguel Estrada -# psmul - Patryk Smul -# plabadie - Patrick Labadie -# WeizhengSap - Weizheng Gao -# hackergil - Gilberto Alvarado Jimenez -###################################################################################################### -# -# CODEOWNERS rules -# ----------------- -# -# All the following rules are applied in the order specified in this file. -# The last rule that matches wins! -# -# See https://git-scm.com/docs/gitignore#_pattern_format for pattern syntax docs. -# -###################################################################################################### +# ================================================ +# This code owners file +# ================================================ +/.github/CODEOWNERS @SAP/spartacus-admins # ================================================ -# Default Owners -# (in case no pattern matches a path in a PR - this should be treated as a bug and result in adding the path to CODEOWNERS) +# Folders where anyone with write access to the repo can approve Pull Requests # ================================================ +/docs/ +/projects/storefrontapp-e2e-cypress/ -# * @SAP/spartacus-codeowners +# @SAP/spartacus-codeowners needs to be repeated in each subsequent rules +# to remain owner of all the sources in the repo. # ================================================ -# Core +# ASM # ================================================ -/projects/core/** @SAP/spartacus-codeowners -/projects/storefrontlib/** @SAP/spartacus-codeowners -/feature-libs/** @SAP/spartacus-codeowners -/core-libs/** @SAP/spartacus-codeowners -/ci-scripts/** @SAP/spartacus-codeowners -/scripts/** @SAP/spartacus-codeowners -/tools/** @SAP/spartacus-codeowners -/* @SAP/spartacus-codeowners - +/feature-libs/asm/ @SAP/spartacus-codeowners @SAP/spartacus-holidays # ================================================ -# Styles +# User domain # ================================================ -/projects/storefrontstyles/** @SAP/spartacus-codeowners +/feature-libs/user/ @SAP/spartacus-codeowners @SAP/spartacus-holidays +/projects/storefrontlib/cms-components/myaccount/ @SAP/spartacus-codeowners @SAP/spartacus-holidays # ================================================ -# Cart +# Product Configurator (VC and CPQ) integration # ================================================ -/projects/storefrontlib/cms-components/cart/** @SAP/spartacus-codeowners +/feature-libs/product-configurator/ @SAP/spartacus-codeowners @SAP/spartacus-CPQ # ================================================ -# Schematics +# Quotes # ================================================ -/projects/schematics/** @SAP/spartacus-codeowners +/feature-libs/quote/ @SAP/spartacus-codeowners @SAP/spartacus-mooncake # ================================================ -# CODEOWNERS and workflows +# Intelligent services (ISS) # ================================================ -/.github/** @SAP/spartacus-codeowners +/integration-libs/cds/ @SAP/spartacus-codeowners @SAP/spartacus-CDS -###################################################################################################### +# ================================================ +# (CDC) +# ================================================ +/integration-libs/cdc/ @SAP/spartacus-codeowners @SAP/spartacus-CDC # ================================================ -# Product configurator feature and CPQ integration +# BOPIS # ================================================ -/feature-libs/product-configurator/** @SAP/spartacus-codeowners @SAP/spartacus-CPQ +/feature-libs/pickup-in-store/ @SAP/spartacus-codeowners @SAP/spartacus-colossus + diff --git a/.github/ISSUE_TEMPLATE/epic_release.md b/.github/ISSUE_TEMPLATE/epic_release.md index b2ce2cbaf0b..5966f80f06f 100644 --- a/.github/ISSUE_TEMPLATE/epic_release.md +++ b/.github/ISSUE_TEMPLATE/epic_release.md @@ -18,7 +18,7 @@ The following is a checklist for new epics or features acceptance for Spartacus, - [ ] Epic complies with [Security best practices](https://sap.github.io/spartacus-docs/security-best-practices/) - [ ] Epic complies with [Accessibility best practices](https://sap.github.io/spartacus-docs/a11y-best-practices/) - [ ] Epic CSS supports directionality (if required) -- [ ] Epic code scans have been conducted (sonar, whitesource, checkmarx) +- [ ] Epic code scans have been conducted [Compliance scans for branches](https://wiki.one.int.sap/wiki/display/spar/Compliance+scans+for+branches) - [ ] Epic renders successfully in SSR ## Audits/reviews diff --git a/.github/ISSUE_TEMPLATE/new-release.md b/.github/ISSUE_TEMPLATE/new-release.md deleted file mode 100644 index 01bd3678d78..00000000000 --- a/.github/ISSUE_TEMPLATE/new-release.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -name: New Release -about: Checklist for new release -title: 'New Release: *.*.*' -labels: release-activities -assignees: '' - ---- - -## Steps before the release - -- [ ] Validate that all merged tickets were tested (QA column must be empty, except for tickets marked as `not-blocking-release`) -- [ ] If there is no maintenance branch yet: - - [ ] Create new maintenance branch (`release/major.minor.x`) - - [ ] Announce new maintenance branch (Set topic in tribe channel) - - [ ] Bump the maintenance branch for the Hosting service deployment github action (workflows/deploy-hs.yml) -- [ ] Create new release branch `release/major.minor.patch.*` from the corresponding branch (develop/maintenance) -- [ ] Follow the steps to [release update schematics](https://github.com/SAP/spartacus/blob/develop/projects/schematics/README.md#releasing-update-schematics) -- [ ] If a new maintenance branch was created, enable the branch to be deployed by the hosting service deployment github action (workflows/deploy-hs.yml). -- [ ] Trigger a Travis build and make sure: - - [ ] All e2e tests pass. - - [ ] Installation script job runs successfully - ---- - -## Steps for Spartacus sample data - -### For Mac/Linux - -- [ ] Cleanup repo, build and generate compodocs and publish on github pages, generate spartacussampledata archives (`./scripts/pre-release.sh`) - -### For Windows - -- [ ] For patch and stable releases: - - [ ] Remove old versions sample data and documentation. - - [ ] Generate compodocs (`yarn generate:docs`) - - [ ] Publish the new compodocs on github pages (`yarn publish:docs`) - - [ ] Get the spartacussampledata source code zips for versions 1905, 2005, 2011 and 2105 of CX (use `release/[version]/next` branches) - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/1905/next.zip` -> `spartacussampledataaddon.1905.zip` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/1905/next.tar.gz` -> `spartacussampledataaddon.1905.tar.gz` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2005/next.zip` -> `spartacussampledata.2005.zip` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2005/next.tar.gz` -> `spartacussampledata.2005.tar.gz` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2011/next.zip` -> `spartacussampledata.2011.zip` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2011/next.tar.gz` -> `spartacussampledata.2011.tar.gz` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2105/next.zip` -> `spartacussampledata.2105.zip` - - [ ] Download and rename in root directory `https://github.tools.sap/cx-commerce/spartacussampledata/archive/release/2105/next.tar.gz` -> `spartacussampledata.2105.tar.gz` - -### For all operative systems - -Do the following steps to keep track of spartacussampledata releases: - -- Important note: - - If the release is a 4.3.x - - make sure the downloaded sample data is using the `release/2105/4dot4` for the 2105 sample release - - If the release is a 5.x.x - - make sure the downloaded sample data is using the `release/2105/deploy` for the 2105 sample release -- [ ] Tag sample data branches for each version (1905, 2005, 2011, 2105): - - [ ] `git clone https://github.tools.sap/cx-commerce/spartacussampledata` (if already present `cd spartacussampledata && git fetch origin`) - - [ ] tag the final commit on [release/1905/next](https://github.tools.sap/cx-commerce/spartacussampledata/commits/release/1905/next) branch: `git tag 1905-*.*.* HEAD-COMMIT-HASH-FROM-release/1905/next` - - [ ] tag the final commit on [release/2005/next](https://github.tools.sap/cx-commerce/spartacussampledata/commits/release/2005/next) branch: `git tag 2005-*.*.* HEAD-COMMIT-HASH-FROM-release/2005/next` - - [ ] tag the final commit on [release/2011/next](https://github.tools.sap/cx-commerce/spartacussampledata/commits/release/2011/next) branch: `git tag 2011-*.*.* HEAD-COMMIT-HASH-FROM-release/2011/next` - - [ ] tag the final commit on [release/2105/next](https://github.tools.sap/cx-commerce/spartacussampledata/commits/release/2105/next) branch: `git tag 2105-*.*.* HEAD-COMMIT-HASH-FROM-release/2105/next` - - [ ] push created tags: `git push origin --tags` - ---- - -## Release specific steps - -- [ ] Before you release libraries, fetch all git tags from github with `git fetch origin --tags` (required to generate release notes) -- [ ] Release libraries with release scripts: - - Make sure your GITHUB_TOKEN env variable is set and valid - - Check if you are logged into npm with `npm whoami` - - If you are not logged in, then login with `npm login` - - If there are any problems, setup 2FA for npm & `npm set @spartacus:registry https://registry.npmjs.org/` - - Important note: DO NOT push or tag any of the released and committed libraries from list below. INSTEAD, push all commits together. Then wait for merging branch to maintenance one and tag release with proper version `major.minor.patch`. Also add a `core-major.minor.patch` tag as well. - - For each package select/type version when prompted: - - [ ] `npm run release:core:with-changelog` - - [ ] `npm run release:styles:with-changelog` - - [ ] `npm run release:assets:with-changelog` - - [ ] `npm run release:storefront:with-changelog` - - [ ] `npm run release:smartedit:with-changelog` (needed since `3.2.0-next.0`) - - [ ] `npm run release:qualtrics:with-changelog` (needed since `3.1.0-next.0`) - - [ ] `npm run release:tracking:with-changelog` (needed since `3.2.0-next.0`) - - [ ] `npm run release:user:with-changelog` (needed since `3.2.0-rc.0`) - - [ ] `npm run release:product:with-changelog` (needed since `3.2.0-next.1`) - - [ ] `npm run release:order:with-changelog` - - [ ] `npm run release:cart:with-changelog` (needed since `3.2.0-rc.0`) - - [ ] `npm run release:storefinder:with-changelog` (needed since `3.0.0-rc.0`) - - [ ] `npm run release:asm:with-changelog` (needed since `3.2.0-rc.0`) - - [ ] `npm run release:checkout:with-changelog` (needed since `4.0.0-rc.0`) - - [ ] `npm run release:setup:with-changelog` (needed since `3.0.0-next.1`) - - [ ] `npm run release:organization:with-changelog` (needed since `3.0.0-next.1`) - - [ ] `npm run release:product-configurator:with-changelog` (needed since `3.1.0-next.0`) - - [ ] `npm run release:cds:with-changelog` - - [ ] `npm run release:cdc:with-changelog` (needed since `3.1.0-next.0`) - - [ ] For < 3.2.0 releases ONLY, set the spartacus peerDependencies manually, then run - `npm run release:cdc:with-changelog`. - - [ ] For older versions since 2.1.0-next.0 ONLY, publish under `0..0` eg. `0.201.0-next.0` for first `2.1.0-next.0` release - - [ ] `npm run release:digital-payments:with-changelog` (needed since `4.1.0-next.0`) - - [ ] `npm run release:epd-visualization:with-changelog` (needed since `4.3.0`) - - [ ] `npm run release:punchout:with-changelog` (needed since `4.3.0`) - - [ ] Run `yarn generate:deps` and commit to make sure `dependencies.json` gets updated before releasing schematics. - - [ ] `npm run release:schematics:with-changelog` - -- [ ] Check that the release notes are populated on github (if they are not, update them) -- [ ] Check tags on npm. - - `next` tag should always reference the last non-stable version - - `latest` tag should always point to the last stable version - - You can leave `rc` tag until we release stable release. - - Use `npm view @spartacus/NAME@VERSION` (ie. `npm view @spartacus/cdc@next`) instead of clicking thru the `npmjs.org` website (which is much slower) - - Use `npm dist-tag` command for tag updates. -- [ ] Test the released libraries from a new shell app - - [ ] Change the `scripts/install/config.sh` to test npm tag (next/latest/rc) at the same time: - - ```bash - SPARTACUS_VERSION=`next` # or `latest`, `rc`; still, you can set it to a specific one, ie `*.*.*` (or leave the config file unchanged) - ``` - - - [ ] Run the installation script to make sure you can create a shell app with the latest imported libraries with no errors: - - ```bash - cd scripts/install && ./run.sh install_npm - ``` - - - [ ] Start and open the shell app locally, and do the following manual tests: - - [ ] Open the homepage. Make sure it loads correctly and there are no errors in the console - - [ ] Search for a product. Make sure search page works - - [ ] Register a new user, login and make sure you can checkout - -- [ ] Merge release branch (PR from release/*.*.*) to the maintenance branch -- [ ] Tag release version as `major.minor.patch` with reference to merge commit from the above step -- [ ] Announce the new release on tribe channel diff --git a/.github/api-extractor-action/Dockerfile b/.github/api-extractor-action/Dockerfile index 83ae84931ff..2ff3318ae95 100644 --- a/.github/api-extractor-action/Dockerfile +++ b/.github/api-extractor-action/Dockerfile @@ -1,13 +1,13 @@ -FROM node:16 +FROM node:18 COPY package.json / -COPY yarn.lock / +COPY package-lock.json / -RUN yarn +RUN npm i COPY src/*.ts / COPY tsconfig.json / -RUN ["yarn", "build"] +RUN ["npm", "run", "build"] ENTRYPOINT ["node", "/index.js"] diff --git a/.github/api-extractor-action/package-lock.json b/.github/api-extractor-action/package-lock.json new file mode 100644 index 00000000000..fe7a9f393af --- /dev/null +++ b/.github/api-extractor-action/package-lock.json @@ -0,0 +1,1462 @@ +{ + "name": "api-extractor-action", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "api-extractor-action", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@actions/cache": "^1.0.5", + "@actions/core": "^1.9.1", + "@actions/exec": "^1.0.4", + "@actions/github": "^4.0.0", + "@actions/glob": "^0.4.0", + "@actions/io": "^1.0.2", + "diff-lines": "^1.1.1", + "normalize-newline": "^4.0.0" + }, + "devDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@actions/cache": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.5.tgz", + "integrity": "sha512-TcvJOduwsPP27KLmIa5cqXsQYFK2GzILcEpnhvYmhGwi1aYx9XwhKmp6Im8X6DJMBxbvupKPsOntG6f6sSkIPA==", + "dependencies": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^1.0.9", + "@actions/io": "^1.0.1", + "@azure/ms-rest-js": "^2.0.7", + "@azure/storage-blob": "^12.1.2", + "semver": "^6.1.0", + "uuid": "^3.3.3" + } + }, + "node_modules/@actions/cache/node_modules/@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "dependencies": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, + "node_modules/@actions/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/core/node_modules/@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@actions/core/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@actions/exec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz", + "integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/github": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-4.0.0.tgz", + "integrity": "sha512-Ej/Y2E+VV6sR9X7pWL5F3VgEWrABaT292DRqRU6R4hnQjPtC/zD3nagxVdXWiRQvYDh8kHXo7IDmG42eJ/dOMA==", + "dependencies": { + "@actions/http-client": "^1.0.8", + "@octokit/core": "^3.0.0", + "@octokit/plugin-paginate-rest": "^2.2.3", + "@octokit/plugin-rest-endpoint-methods": "^4.0.0" + } + }, + "node_modules/@actions/glob": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.4.0.tgz", + "integrity": "sha512-+eKIGFhsFa4EBwaf/GMyzCdWrXWymGXfFmZU3FHQvYS8mPcHtTtZONbkcqqUMzw9mJ/pImEBFET1JNifhqGsAQ==", + "dependencies": { + "@actions/core": "^1.9.1", + "minimatch": "^3.0.4" + } + }, + "node_modules/@actions/http-client": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz", + "integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==", + "dependencies": { + "tunnel": "0.0.6" + } + }, + "node_modules/@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/abort-controller/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + }, + "node_modules/@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-auth/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/core-http": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.1.tgz", + "integrity": "sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@azure/core-http/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/core-http/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.2.tgz", + "integrity": "sha512-tucUutPhBwCPu6v16KEFYML81npEL6gnT+iwewXvK5ZD55sr0/Vw2jfQETMiKVeARRrXHB2QQ3SpxxGi1zAUWg==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-lro/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/core-paging": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz", + "integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==", + "dependencies": { + "@azure/core-asynciterator-polyfill": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-tracing/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/core-util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", + "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-util/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@azure/ms-rest-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", + "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", + "dependencies": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.7", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^8.3.2", + "xml2js": "^0.5.0" + } + }, + "node_modules/@azure/ms-rest-js/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.13.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.13.0.tgz", + "integrity": "sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/storage-blob/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/@octokit/auth-token": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz", + "integrity": "sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==", + "dependencies": { + "@octokit/types": "^6.0.0" + } + }, + "node_modules/@octokit/core": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz", + "integrity": "sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.6.tgz", + "integrity": "sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ==", + "dependencies": { + "@octokit/types": "^5.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", + "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "dependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz", + "integrity": "sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==", + "dependencies": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.0.1.tgz", + "integrity": "sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.2.tgz", + "integrity": "sha512-3Dy7/YZAwdOaRpGQoNHPeT0VU1fYLpIUdPyvR37IyFLgd6XSij4j9V/xN/+eSjF2KKvmfIulEh9LF1tRPjIiDA==", + "dependencies": { + "@octokit/types": "^6.0.1" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz", + "integrity": "sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==", + "dependencies": { + "@octokit/types": "^6.1.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz", + "integrity": "sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", + "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", + "dependencies": { + "@octokit/types": "^5.0.1", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", + "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "dependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/@octokit/types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.1.2.tgz", + "integrity": "sha512-LPCpcLbcky7fWfHCTuc7tMiSHFpFlrThJqVdaHgowBTMS0ijlZFfonQC/C1PrZOjD4xRCYgBqH9yttEATGE/nw==", + "dependencies": { + "@octokit/openapi-types": "^2.0.1", + "@types/node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@types/node": { + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz", + "integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" + }, + "node_modules/before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-lines": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diff-lines/-/diff-lines-1.1.1.tgz", + "integrity": "sha512-Oo5JzEEriF/+T0usOeRP5yOzr6SWvni2rrxvIgijMZSxPcEvf8JOvCO5GpnWwkte7fcOgnue/f5ECg1H9lMPCw==", + "dependencies": { + "diff": "^3.5.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-newline": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/normalize-newline/-/normalize-newline-4.1.0.tgz", + "integrity": "sha512-ff4jKqMI8Xl50/4Mms/9jPobzAV/UK+kXG2XJ/7AqOmxIx8mqfqTIHYxuAnEgJ2AQeBbLnlbmZ5+38Y9A0w/YA==", + "dependencies": { + "replace-buffer": "^1.2.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/replace-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/replace-buffer/-/replace-buffer-1.2.1.tgz", + "integrity": "sha512-ly3OKwKu+3T55DjP5PjIMzxgz9lFx6dQnBmAIxryZyRKl8f22juy12ShOyuq8WrQE5UlFOseZgQZDua0iF9DHw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "@actions/cache": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.5.tgz", + "integrity": "sha512-TcvJOduwsPP27KLmIa5cqXsQYFK2GzILcEpnhvYmhGwi1aYx9XwhKmp6Im8X6DJMBxbvupKPsOntG6f6sSkIPA==", + "requires": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^1.0.9", + "@actions/io": "^1.0.1", + "@azure/ms-rest-js": "^2.0.7", + "@azure/storage-blob": "^12.1.2", + "semver": "^6.1.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "requires": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + } + } + }, + "@actions/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", + "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", + "requires": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@actions/exec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz", + "integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/github": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-4.0.0.tgz", + "integrity": "sha512-Ej/Y2E+VV6sR9X7pWL5F3VgEWrABaT292DRqRU6R4hnQjPtC/zD3nagxVdXWiRQvYDh8kHXo7IDmG42eJ/dOMA==", + "requires": { + "@actions/http-client": "^1.0.8", + "@octokit/core": "^3.0.0", + "@octokit/plugin-paginate-rest": "^2.2.3", + "@octokit/plugin-rest-endpoint-methods": "^4.0.0" + } + }, + "@actions/glob": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.4.0.tgz", + "integrity": "sha512-+eKIGFhsFa4EBwaf/GMyzCdWrXWymGXfFmZU3FHQvYS8mPcHtTtZONbkcqqUMzw9mJ/pImEBFET1JNifhqGsAQ==", + "requires": { + "@actions/core": "^1.9.1", + "minimatch": "^3.0.4" + } + }, + "@actions/http-client": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz", + "integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + }, + "@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/core-http": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.1.tgz", + "integrity": "sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/core-lro": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.2.tgz", + "integrity": "sha512-tucUutPhBwCPu6v16KEFYML81npEL6gnT+iwewXvK5ZD55sr0/Vw2jfQETMiKVeARRrXHB2QQ3SpxxGi1zAUWg==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/core-paging": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz", + "integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==", + "requires": { + "@azure/core-asynciterator-polyfill": "^1.0.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "requires": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/core-util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.0.tgz", + "integrity": "sha512-ANP0Er7R2KHHHjwmKzPF9wbd0gXvOX7yRRHeYL1eNd/OaNrMLyfZH/FQasHRVAf6rMXX+EAUpvYwLMFDHDI5Gw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "requires": { + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@azure/ms-rest-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", + "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", + "requires": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.7", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^8.3.2", + "xml2js": "^0.5.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/storage-blob": { + "version": "12.13.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.13.0.tgz", + "integrity": "sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + } + } + }, + "@octokit/auth-token": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz", + "integrity": "sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q==", + "requires": { + "@octokit/types": "^6.0.0" + } + }, + "@octokit/core": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz", + "integrity": "sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.6.tgz", + "integrity": "sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ==", + "requires": { + "@octokit/types": "^5.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "@octokit/types": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", + "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/graphql": { + "version": "4.5.8", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz", + "integrity": "sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA==", + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.0.1.tgz", + "integrity": "sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.2.tgz", + "integrity": "sha512-3Dy7/YZAwdOaRpGQoNHPeT0VU1fYLpIUdPyvR37IyFLgd6XSij4j9V/xN/+eSjF2KKvmfIulEh9LF1tRPjIiDA==", + "requires": { + "@octokit/types": "^6.0.1" + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz", + "integrity": "sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA==", + "requires": { + "@octokit/types": "^6.1.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.4.12", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz", + "integrity": "sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", + "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", + "requires": { + "@octokit/types": "^5.0.1", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "dependencies": { + "@octokit/types": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz", + "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==", + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.1.2.tgz", + "integrity": "sha512-LPCpcLbcky7fWfHCTuc7tMiSHFpFlrThJqVdaHgowBTMS0ijlZFfonQC/C1PrZOjD4xRCYgBqH9yttEATGE/nw==", + "requires": { + "@octokit/openapi-types": "^2.0.1", + "@types/node": ">= 8" + } + }, + "@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" + }, + "@types/node": { + "version": "14.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", + "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==" + }, + "@types/node-fetch": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz", + "integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "requires": { + "@types/node": "*" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "diff-lines": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diff-lines/-/diff-lines-1.1.1.tgz", + "integrity": "sha512-Oo5JzEEriF/+T0usOeRP5yOzr6SWvni2rrxvIgijMZSxPcEvf8JOvCO5GpnWwkte7fcOgnue/f5ECg1H9lMPCw==", + "requires": { + "diff": "^3.5.0" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "normalize-newline": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/normalize-newline/-/normalize-newline-4.1.0.tgz", + "integrity": "sha512-ff4jKqMI8Xl50/4Mms/9jPobzAV/UK+kXG2XJ/7AqOmxIx8mqfqTIHYxuAnEgJ2AQeBbLnlbmZ5+38Y9A0w/YA==", + "requires": { + "replace-buffer": "^1.2.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "replace-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/replace-buffer/-/replace-buffer-1.2.1.tgz", + "integrity": "sha512-ly3OKwKu+3T55DjP5PjIMzxgz9lFx6dQnBmAIxryZyRKl8f22juy12ShOyuq8WrQE5UlFOseZgQZDua0iF9DHw==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/.github/api-extractor-action/package.json b/.github/api-extractor-action/package.json index c299d5b1677..6ceded1db27 100644 --- a/.github/api-extractor-action/package.json +++ b/.github/api-extractor-action/package.json @@ -10,15 +10,15 @@ "license": "ISC", "dependencies": { "@actions/cache": "^1.0.5", - "@actions/core": "^1.2.6", + "@actions/core": "^1.9.1", "@actions/exec": "^1.0.4", "@actions/github": "^4.0.0", - "@actions/glob": "^0.1.1", + "@actions/glob": "^0.4.0", "@actions/io": "^1.0.2", "diff-lines": "^1.1.1", - "normalize-newline": "^3.0.0" + "normalize-newline": "^4.0.0" }, "devDependencies": { - "typescript": "^4.1.3" + "typescript": "^5.0.0" } } diff --git a/.github/api-extractor-action/src/api-extractor.ts b/.github/api-extractor-action/src/api-extractor.ts index 81c4c7d3001..e104c89c637 100644 --- a/.github/api-extractor-action/src/api-extractor.ts +++ b/.github/api-extractor-action/src/api-extractor.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + import * as core from '@actions/core'; import * as exec from '@actions/exec'; import { ExecOptions } from '@actions/exec'; diff --git a/.github/api-extractor-action/src/comment.ts b/.github/api-extractor-action/src/comment.ts index a335ebde625..92db2952ef7 100644 --- a/.github/api-extractor-action/src/comment.ts +++ b/.github/api-extractor-action/src/comment.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + import { Context } from '@actions/github/lib/context'; import * as fs from 'fs'; import { diff --git a/.github/api-extractor-action/src/const.ts b/.github/api-extractor-action/src/const.ts index 5dd574d1fa8..50ad45c5e18 100644 --- a/.github/api-extractor-action/src/const.ts +++ b/.github/api-extractor-action/src/const.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export const BASE_BRANCH_DIR = 'branch-clone'; export const BUILD_DIR = 'dist'; export const REPORT_DIR = 'etc'; diff --git a/.github/api-extractor-action/src/index.ts b/.github/api-extractor-action/src/index.ts index 300f3c7d5c4..8e8735bdac3 100644 --- a/.github/api-extractor-action/src/index.ts +++ b/.github/api-extractor-action/src/index.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + /** * Terminology: * - head branch -> branch you are working on diff --git a/.github/api-extractor-action/src/setup.ts b/.github/api-extractor-action/src/setup.ts index 37245559fe7..3e965873d3b 100644 --- a/.github/api-extractor-action/src/setup.ts +++ b/.github/api-extractor-action/src/setup.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + import * as cache from '@actions/cache'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; @@ -17,7 +23,7 @@ export async function prepareRepositoryForApiExtractor( core.startGroup('Prepare branches for extractor'); // Install dependencies to build libraries - await exec.exec('yarn'); + await exec.exec('npm', ['i']); // Create directory for reports await io.mkdirP(`${REPORT_DIR}`); @@ -54,14 +60,14 @@ export async function prepareRepositoryForApiExtractor( ); // If we didn't restore builded libs from cache on the BASE branch, we need to also build base branch - await exec.exec('yarn', ['--cwd', BASE_BRANCH_DIR]); - await exec.exec('yarn', ['--cwd', BASE_BRANCH_DIR, BUILD_COMMAND]); + await exec.exec('npm', ['--prefix', BASE_BRANCH_DIR]); + await exec.exec('npm', ['--prefix', BASE_BRANCH_DIR, 'run', BUILD_COMMAND]); } // Build the libraries from the HEAD branch - // TODO: We can parallel these builds, when schematics builds won't trigger yarn install - await exec.exec('yarn'); - await exec.exec('yarn', [BUILD_COMMAND]); + // TODO: We can parallel these builds, when schematics builds won't trigger npm install + await exec.exec('npm', ['i']); + await exec.exec('npm', ['run', BUILD_COMMAND]); core.endGroup(); } diff --git a/.github/api-extractor-action/tsconfig.json b/.github/api-extractor-action/tsconfig.json index 08da10df538..c7ac3bead47 100644 --- a/.github/api-extractor-action/tsconfig.json +++ b/.github/api-extractor-action/tsconfig.json @@ -1,29 +1,34 @@ { "compilerOptions": { /* Basic Options */ - "target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["es2019", "esnext.bigint", "es2020.string", "es2020.symbol.wellknown"], /* Specify library files to be included in the compilation. */ - "allowJs": false, /* Allow javascript files to be compiled. */ - "checkJs": false, /* Report errors in .js files. */ - "declaration": false, /* Generates corresponding '.d.ts' file. */ - "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": false, /* Generates corresponding '.map' file. */ + "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "lib": [ + "es2019", + "esnext.bigint", + "es2022.string", + "es2022.symbol.wellknown" + ] /* Specify library files to be included in the compilation. */, + "allowJs": false /* Allow javascript files to be compiled. */, + "checkJs": false /* Report errors in .js files. */, + "declaration": false /* Generates corresponding '.d.ts' file. */, + "declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */, + "sourceMap": false /* Generates corresponding '.map' file. */, /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "esModuleInterop": false, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } } diff --git a/.github/api-extractor-action/yarn.lock b/.github/api-extractor-action/yarn.lock deleted file mode 100644 index 5b3a1b858a9..00000000000 --- a/.github/api-extractor-action/yarn.lock +++ /dev/null @@ -1,584 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@actions/cache@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@actions/cache/-/cache-1.0.5.tgz#ba98725d38611b5426fc57a2f00bd8b0d9202f36" - integrity sha512-TcvJOduwsPP27KLmIa5cqXsQYFK2GzILcEpnhvYmhGwi1aYx9XwhKmp6Im8X6DJMBxbvupKPsOntG6f6sSkIPA== - dependencies: - "@actions/core" "^1.2.6" - "@actions/exec" "^1.0.1" - "@actions/glob" "^0.1.0" - "@actions/http-client" "^1.0.9" - "@actions/io" "^1.0.1" - "@azure/ms-rest-js" "^2.0.7" - "@azure/storage-blob" "^12.1.2" - semver "^6.1.0" - uuid "^3.3.3" - -"@actions/core@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.6.tgz#a78d49f41a4def18e88ce47c2cac615d5694bf09" - integrity sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA== - -"@actions/exec@^1.0.1", "@actions/exec@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.0.4.tgz#99d75310e62e59fc37d2ee6dcff6d4bffadd3a5d" - integrity sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw== - dependencies: - "@actions/io" "^1.0.1" - -"@actions/github@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@actions/github/-/github-4.0.0.tgz#d520483151a2bf5d2dc9cd0f20f9ac3a2e458816" - integrity sha512-Ej/Y2E+VV6sR9X7pWL5F3VgEWrABaT292DRqRU6R4hnQjPtC/zD3nagxVdXWiRQvYDh8kHXo7IDmG42eJ/dOMA== - dependencies: - "@actions/http-client" "^1.0.8" - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.3" - "@octokit/plugin-rest-endpoint-methods" "^4.0.0" - -"@actions/glob@^0.1.0", "@actions/glob@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@actions/glob/-/glob-0.1.1.tgz#0af851312dfed21fdf9eac3473cfde93ffa56e40" - integrity sha512-ikM4GVZOgSGDNTjv0ECJ8AOqmDqQwtO4K1M4P465C9iikRq34+FwCjUVSwzgOYDP85qtddyWpzBw5lTub/9Xmg== - dependencies: - "@actions/core" "^1.2.6" - minimatch "^3.0.4" - -"@actions/http-client@^1.0.8", "@actions/http-client@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.9.tgz#af1947d020043dbc6a3b4c5918892095c30ffb52" - integrity sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg== - dependencies: - tunnel "0.0.6" - -"@actions/io@^1.0.1", "@actions/io@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.0.2.tgz#2f614b6e69ce14d191180451eb38e6576a6e6b27" - integrity sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg== - -"@azure/abort-controller@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.1.tgz#8510935b25ac051e58920300e9d7b511ca6e656a" - integrity sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A== - dependencies: - tslib "^1.9.3" - -"@azure/core-asynciterator-polyfill@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" - integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== - -"@azure/core-auth@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.1.3.tgz#94e7bbc207010e7a2fdba61565443e4e1cf1e131" - integrity sha512-A4xigW0YZZpkj1zK7dKuzbBpGwnhEcRk6WWuIshdHC32raR3EQ1j6VA9XZqE+RFsUgH6OAmIK5BWIz+mZjnd6Q== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-tracing" "1.0.0-preview.8" - "@opentelemetry/api" "^0.6.1" - tslib "^2.0.0" - -"@azure/core-http@^1.1.1", "@azure/core-http@^1.2.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-1.2.1.tgz#af33bc90062f72de4cd933fd8148a16a9032f8fe" - integrity sha512-vPHIQXjLVs4iin2BUaj7/sqIAfGq3MW1TLEc3yYKFNpi/sBQn2KI0g+Ow0EQYvAkkHhTHGArA7JKhcjsnJMGLw== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-auth" "^1.1.3" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - "@types/node-fetch" "^2.5.0" - "@types/tunnel" "^0.0.1" - form-data "^3.0.0" - node-fetch "^2.6.0" - process "^0.11.10" - tough-cookie "^4.0.0" - tslib "^2.0.0" - tunnel "^0.0.6" - uuid "^8.3.0" - xml2js "^0.4.19" - -"@azure/core-lro@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-1.0.2.tgz#b7b51ff7b84910b7eb152a706b0531d020864f31" - integrity sha512-Yr0JD7GKryOmbcb5wHCQoQ4KCcH5QJWRNorofid+UvudLaxnbCfvKh/cUfQsGUqRjO9L/Bw4X7FP824DcHdMxw== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.1.1" - events "^3.0.0" - tslib "^1.10.0" - -"@azure/core-paging@^1.1.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.1.3.tgz#3587c9898a0530cacb64bab216d7318468aa5efc" - integrity sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A== - dependencies: - "@azure/core-asynciterator-polyfill" "^1.0.0" - -"@azure/core-tracing@1.0.0-preview.8": - version "1.0.0-preview.8" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz#1e0ff857e855edb774ffd33476003c27b5bb2705" - integrity sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q== - dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.6.1" - tslib "^1.10.0" - -"@azure/core-tracing@1.0.0-preview.9": - version "1.0.0-preview.9" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz#84f3b85572013f9d9b85e1e5d89787aa180787eb" - integrity sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug== - dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.10.2" - tslib "^2.0.0" - -"@azure/logger@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.0.tgz#48b371dfb34288c8797e5c104f6c4fb45bf1772c" - integrity sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA== - dependencies: - tslib "^1.9.3" - -"@azure/ms-rest-js@^2.0.7": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz#41bc541984983b5242dfbcf699ea281acd045946" - integrity sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg== - dependencies: - "@types/node-fetch" "^2.3.7" - "@types/tunnel" "0.0.1" - abort-controller "^3.0.0" - form-data "^2.5.0" - node-fetch "^2.6.0" - tough-cookie "^3.0.1" - tslib "^1.10.0" - tunnel "0.0.6" - uuid "^3.3.2" - xml2js "^0.4.19" - -"@azure/storage-blob@^12.1.2": - version "12.3.0" - resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.3.0.tgz#4afda22f72c9a9bc8c01b99ea4c2d63f20779e0d" - integrity sha512-nCySzNfm782pEW3sg9GHj1zE4gBeVVMeEBdWb4MefifrCwQQOoz5cXZTNFiUJAJqAO+/72r2UjZcUwHk/QmzkA== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.2.0" - "@azure/core-lro" "^1.0.2" - "@azure/core-paging" "^1.1.1" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - events "^3.0.0" - tslib "^2.0.0" - -"@octokit/auth-token@^2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" - integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q== - dependencies: - "@octokit/types" "^6.0.0" - -"@octokit/core@^3.0.0": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106" - integrity sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.4.12" - "@octokit/types" "^6.0.3" - before-after-hook "^2.1.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.6" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.6.tgz#4f09f2b468976b444742a1d5069f6fa45826d999" - integrity sha512-7Cc8olaCoL/mtquB7j/HTbPM+sY6Ebr4k2X2y4JoXpVKQ7r5xB4iGQE0IoO58wIPsUk4AzoT65AMEpymSbWTgQ== - dependencies: - "@octokit/types" "^5.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.5.8" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" - integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-2.0.1.tgz#7453d8281ce66b8ed1607f7ac7d751c3baffd2cc" - integrity sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q== - -"@octokit/plugin-paginate-rest@^2.2.3": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.2.tgz#45d13dbf5ff8aed54f1a3716b1d57fdc62720c5f" - integrity sha512-3Dy7/YZAwdOaRpGQoNHPeT0VU1fYLpIUdPyvR37IyFLgd6XSij4j9V/xN/+eSjF2KKvmfIulEh9LF1tRPjIiDA== - dependencies: - "@octokit/types" "^6.0.1" - -"@octokit/plugin-rest-endpoint-methods@^4.0.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63" - integrity sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA== - dependencies: - "@octokit/types" "^6.1.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.2.tgz#0e76b83f5d8fdda1db99027ea5f617c2e6ba9ed0" - integrity sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw== - dependencies: - "@octokit/types" "^5.0.1" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.12" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc" - integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" - -"@octokit/types@^5.0.0", "@octokit/types@^5.0.1": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" - integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== - dependencies: - "@types/node" ">= 8" - -"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.1.0": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.1.2.tgz#2b3a6ae0b8b71c27c770b4ff3e9ad8f1f538af58" - integrity sha512-LPCpcLbcky7fWfHCTuc7tMiSHFpFlrThJqVdaHgowBTMS0ijlZFfonQC/C1PrZOjD4xRCYgBqH9yttEATGE/nw== - dependencies: - "@octokit/openapi-types" "^2.0.1" - "@types/node" ">= 8" - -"@opencensus/web-types@0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@opencensus/web-types/-/web-types-0.0.7.tgz#4426de1fe5aa8f624db395d2152b902874f0570a" - integrity sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g== - -"@opentelemetry/api@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.10.2.tgz#9647b881f3e1654089ff7ea59d587b2d35060654" - integrity sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA== - dependencies: - "@opentelemetry/context-base" "^0.10.2" - -"@opentelemetry/api@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.6.1.tgz#a00b504801f408230b9ad719716fe91ad888c642" - integrity sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q== - dependencies: - "@opentelemetry/context-base" "^0.6.1" - -"@opentelemetry/context-base@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.10.2.tgz#55bea904b2b91aa8a8675df9eaba5961bddb1def" - integrity sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw== - -"@opentelemetry/context-base@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.6.1.tgz#b260e454ee4f9635ea024fc83be225e397f15363" - integrity sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ== - -"@types/node-fetch@^2.3.7", "@types/node-fetch@^2.5.0": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" - integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*": - version "14.14.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b" - integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw== - -"@types/node@>= 8": - version "14.11.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.2.tgz#2de1ed6670439387da1c9f549a2ade2b0a799256" - integrity sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA== - -"@types/tunnel@0.0.1", "@types/tunnel@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" - integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== - dependencies: - "@types/node" "*" - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -before-after-hook@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" - integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -combined-stream@^1.0.6, combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -diff-lines@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/diff-lines/-/diff-lines-1.1.1.tgz#4f10a709b6ce2af1d6b412ada5a90cf01d782571" - integrity sha512-Oo5JzEEriF/+T0usOeRP5yOzr6SWvni2rrxvIgijMZSxPcEvf8JOvCO5GpnWwkte7fcOgnue/f5ECg1H9lMPCw== - dependencies: - diff "^3.5.0" - -diff@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -events@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" - integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - -mime-types@^2.1.12: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -node-fetch@^2.6.0, node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -normalize-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-newline/-/normalize-newline-3.0.0.tgz#1cbea804aba436001f83938ab21ec039d69ae9d3" - integrity sha1-HL6oBKukNgAfg5OKsh7AOdaa6dM= - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver@^6.1.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tslib@^1.10.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== - -tunnel@0.0.6, tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -uuid@^3.3.2, uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -xml2js@^0.4.19: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== diff --git a/.github/cache-builded-libs/Dockerfile b/.github/cache-builded-libs/Dockerfile index ac7795d8836..820c547290a 100644 --- a/.github/cache-builded-libs/Dockerfile +++ b/.github/cache-builded-libs/Dockerfile @@ -1,13 +1,13 @@ -FROM node:16 +FROM node:18 COPY package.json / -COPY yarn.lock / +COPY package-lock.json / -RUN yarn +RUN npm i COPY index.ts / COPY tsconfig.json / -RUN ["yarn", "build"] +RUN ["npm", "run", "build"] ENTRYPOINT ["node", "/index.js"] diff --git a/.github/cache-builded-libs/index.ts b/.github/cache-builded-libs/index.ts index 245d7086eb2..b1069b287e1 100644 --- a/.github/cache-builded-libs/index.ts +++ b/.github/cache-builded-libs/index.ts @@ -1,20 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + import * as cache from '@actions/cache'; import * as core from '@actions/core'; import * as exec from '@actions/exec'; import * as github from '@actions/github'; async function run() { - core.startGroup('yarn'); - let exitCode = await exec.exec('yarn', [], { + core.startGroup('npm'); + let exitCode = await exec.exec('npm', ['i'], { ignoreReturnCode: true, }); core.endGroup(); if (exitCode !== 0) { - core.setFailed(`Yarn install failed`); + core.setFailed(`NPM install failed`); } - core.startGroup('yarn build:libs'); - exitCode = await exec.exec('yarn', ['build:libs'], { + core.startGroup('npm run build:libs'); + exitCode = await exec.exec('npm', ['run', 'build:libs'], { ignoreReturnCode: true, }); core.endGroup(); diff --git a/.github/cache-builded-libs/package-lock.json b/.github/cache-builded-libs/package-lock.json new file mode 100644 index 00000000000..887ed729060 --- /dev/null +++ b/.github/cache-builded-libs/package-lock.json @@ -0,0 +1,562 @@ +{ + "name": "cache-builded-libs", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/cache": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.1.2.tgz", + "integrity": "sha512-3XeKcXIonfIbqvW7gPm/VLOhv1RHQ1dtTgSBCH6OUhCgSTii9bEVgu0PIms7UbLnXeMCKFzECfpbud8fJEvBbQ==", + "requires": { + "@actions/core": "^1.10.0", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^2.0.1", + "@actions/io": "^1.0.1", + "@azure/abort-controller": "^1.1.0", + "@azure/ms-rest-js": "^2.6.0", + "@azure/storage-blob": "^12.8.0", + "semver": "^6.1.0", + "uuid": "^3.3.3" + } + }, + "@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "requires": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "requires": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "requires": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, + "@actions/http-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", + "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@actions/io": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", + "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" + }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-lro": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.0.tgz", + "integrity": "sha512-Vsd5Sl04RG/p5ui/p0dAFMov5I/W4dmRjOrtWGXVs4vY/hNMPefiFH7cZEOr+1u0XrBKkpvt634IyUUD9bVRuQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-paging": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.4.0.tgz", + "integrity": "sha512-tabFtZTg8D9XqZKEfNUOGh63SuYeOxmvH4GDcOJN+R1bZWZ1FZskctgY9Pmuwzhn+0Xvq9rmimK9hsvtLkeBsw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "requires": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.1.1.tgz", + "integrity": "sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz", + "integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/ms-rest-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", + "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", + "requires": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.7", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^8.3.2", + "xml2js": "^0.5.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/storage-blob": { + "version": "12.13.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.13.0.tgz", + "integrity": "sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "dependencies": { + "@azure/core-http": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.1.tgz", + "integrity": "sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + } + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "@opentelemetry/api": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.0.tgz", + "integrity": "sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g==" + }, + "@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "requires": { + "@types/node": "*" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "node-fetch": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/.github/cache-builded-libs/package.json b/.github/cache-builded-libs/package.json index 2088018ef6d..8af2321cc7c 100644 --- a/.github/cache-builded-libs/package.json +++ b/.github/cache-builded-libs/package.json @@ -9,12 +9,12 @@ "author": "", "license": "ISC", "dependencies": { - "@actions/cache": "^1.0.5", - "@actions/core": "^1.2.6", - "@actions/exec": "^1.0.4", - "@actions/github": "^4.0.0" + "@actions/cache": "^3.1.2", + "@actions/core": "^1.10.0", + "@actions/exec": "^1.1.1", + "@actions/github": "^5.1.1" }, "devDependencies": { - "typescript": "^4.1.3" + "typescript": "^5.0.0" } } diff --git a/.github/cache-builded-libs/tsconfig.json b/.github/cache-builded-libs/tsconfig.json index 08da10df538..c7ac3bead47 100644 --- a/.github/cache-builded-libs/tsconfig.json +++ b/.github/cache-builded-libs/tsconfig.json @@ -1,29 +1,34 @@ { "compilerOptions": { /* Basic Options */ - "target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["es2019", "esnext.bigint", "es2020.string", "es2020.symbol.wellknown"], /* Specify library files to be included in the compilation. */ - "allowJs": false, /* Allow javascript files to be compiled. */ - "checkJs": false, /* Report errors in .js files. */ - "declaration": false, /* Generates corresponding '.d.ts' file. */ - "declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": false, /* Generates corresponding '.map' file. */ + "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "lib": [ + "es2019", + "esnext.bigint", + "es2022.string", + "es2022.symbol.wellknown" + ] /* Specify library files to be included in the compilation. */, + "allowJs": false /* Allow javascript files to be compiled. */, + "checkJs": false /* Report errors in .js files. */, + "declaration": false /* Generates corresponding '.d.ts' file. */, + "declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */, + "sourceMap": false /* Generates corresponding '.map' file. */, /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "esModuleInterop": false, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } } diff --git a/.github/cache-builded-libs/yarn.lock b/.github/cache-builded-libs/yarn.lock deleted file mode 100644 index 79ab0b26270..00000000000 --- a/.github/cache-builded-libs/yarn.lock +++ /dev/null @@ -1,555 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@actions/cache@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@actions/cache/-/cache-1.0.5.tgz#ba98725d38611b5426fc57a2f00bd8b0d9202f36" - integrity sha512-TcvJOduwsPP27KLmIa5cqXsQYFK2GzILcEpnhvYmhGwi1aYx9XwhKmp6Im8X6DJMBxbvupKPsOntG6f6sSkIPA== - dependencies: - "@actions/core" "^1.2.6" - "@actions/exec" "^1.0.1" - "@actions/glob" "^0.1.0" - "@actions/http-client" "^1.0.9" - "@actions/io" "^1.0.1" - "@azure/ms-rest-js" "^2.0.7" - "@azure/storage-blob" "^12.1.2" - semver "^6.1.0" - uuid "^3.3.3" - -"@actions/core@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.6.tgz#a78d49f41a4def18e88ce47c2cac615d5694bf09" - integrity sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA== - -"@actions/exec@^1.0.1", "@actions/exec@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.0.4.tgz#99d75310e62e59fc37d2ee6dcff6d4bffadd3a5d" - integrity sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw== - dependencies: - "@actions/io" "^1.0.1" - -"@actions/github@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@actions/github/-/github-4.0.0.tgz#d520483151a2bf5d2dc9cd0f20f9ac3a2e458816" - integrity sha512-Ej/Y2E+VV6sR9X7pWL5F3VgEWrABaT292DRqRU6R4hnQjPtC/zD3nagxVdXWiRQvYDh8kHXo7IDmG42eJ/dOMA== - dependencies: - "@actions/http-client" "^1.0.8" - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.3" - "@octokit/plugin-rest-endpoint-methods" "^4.0.0" - -"@actions/glob@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@actions/glob/-/glob-0.1.1.tgz#0af851312dfed21fdf9eac3473cfde93ffa56e40" - integrity sha512-ikM4GVZOgSGDNTjv0ECJ8AOqmDqQwtO4K1M4P465C9iikRq34+FwCjUVSwzgOYDP85qtddyWpzBw5lTub/9Xmg== - dependencies: - "@actions/core" "^1.2.6" - minimatch "^3.0.4" - -"@actions/http-client@^1.0.8", "@actions/http-client@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.9.tgz#af1947d020043dbc6a3b4c5918892095c30ffb52" - integrity sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg== - dependencies: - tunnel "0.0.6" - -"@actions/io@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.0.2.tgz#2f614b6e69ce14d191180451eb38e6576a6e6b27" - integrity sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg== - -"@azure/abort-controller@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.1.tgz#8510935b25ac051e58920300e9d7b511ca6e656a" - integrity sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A== - dependencies: - tslib "^1.9.3" - -"@azure/core-asynciterator-polyfill@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" - integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== - -"@azure/core-auth@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.1.3.tgz#94e7bbc207010e7a2fdba61565443e4e1cf1e131" - integrity sha512-A4xigW0YZZpkj1zK7dKuzbBpGwnhEcRk6WWuIshdHC32raR3EQ1j6VA9XZqE+RFsUgH6OAmIK5BWIz+mZjnd6Q== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-tracing" "1.0.0-preview.8" - "@opentelemetry/api" "^0.6.1" - tslib "^2.0.0" - -"@azure/core-http@^1.1.1", "@azure/core-http@^1.2.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-1.2.1.tgz#af33bc90062f72de4cd933fd8148a16a9032f8fe" - integrity sha512-vPHIQXjLVs4iin2BUaj7/sqIAfGq3MW1TLEc3yYKFNpi/sBQn2KI0g+Ow0EQYvAkkHhTHGArA7JKhcjsnJMGLw== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-auth" "^1.1.3" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - "@types/node-fetch" "^2.5.0" - "@types/tunnel" "^0.0.1" - form-data "^3.0.0" - node-fetch "^2.6.0" - process "^0.11.10" - tough-cookie "^4.0.0" - tslib "^2.0.0" - tunnel "^0.0.6" - uuid "^8.3.0" - xml2js "^0.4.19" - -"@azure/core-lro@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-1.0.2.tgz#b7b51ff7b84910b7eb152a706b0531d020864f31" - integrity sha512-Yr0JD7GKryOmbcb5wHCQoQ4KCcH5QJWRNorofid+UvudLaxnbCfvKh/cUfQsGUqRjO9L/Bw4X7FP824DcHdMxw== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.1.1" - events "^3.0.0" - tslib "^1.10.0" - -"@azure/core-paging@^1.1.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.1.3.tgz#3587c9898a0530cacb64bab216d7318468aa5efc" - integrity sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A== - dependencies: - "@azure/core-asynciterator-polyfill" "^1.0.0" - -"@azure/core-tracing@1.0.0-preview.8": - version "1.0.0-preview.8" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz#1e0ff857e855edb774ffd33476003c27b5bb2705" - integrity sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q== - dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.6.1" - tslib "^1.10.0" - -"@azure/core-tracing@1.0.0-preview.9": - version "1.0.0-preview.9" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz#84f3b85572013f9d9b85e1e5d89787aa180787eb" - integrity sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug== - dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.10.2" - tslib "^2.0.0" - -"@azure/logger@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.0.tgz#48b371dfb34288c8797e5c104f6c4fb45bf1772c" - integrity sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA== - dependencies: - tslib "^1.9.3" - -"@azure/ms-rest-js@^2.0.7": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz#41bc541984983b5242dfbcf699ea281acd045946" - integrity sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg== - dependencies: - "@types/node-fetch" "^2.3.7" - "@types/tunnel" "0.0.1" - abort-controller "^3.0.0" - form-data "^2.5.0" - node-fetch "^2.6.0" - tough-cookie "^3.0.1" - tslib "^1.10.0" - tunnel "0.0.6" - uuid "^3.3.2" - xml2js "^0.4.19" - -"@azure/storage-blob@^12.1.2": - version "12.3.0" - resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.3.0.tgz#4afda22f72c9a9bc8c01b99ea4c2d63f20779e0d" - integrity sha512-nCySzNfm782pEW3sg9GHj1zE4gBeVVMeEBdWb4MefifrCwQQOoz5cXZTNFiUJAJqAO+/72r2UjZcUwHk/QmzkA== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.2.0" - "@azure/core-lro" "^1.0.2" - "@azure/core-paging" "^1.1.1" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - events "^3.0.0" - tslib "^2.0.0" - -"@octokit/auth-token@^2.4.4": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" - integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q== - dependencies: - "@octokit/types" "^6.0.0" - -"@octokit/core@^3.0.0": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106" - integrity sha512-d9dTsqdePBqOn7aGkyRFe7pQpCXdibSJ5SFnrTr0axevObZrpz3qkWm7t/NjYv5a66z6vhfteriaq4FRz3e0Qg== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.4.12" - "@octokit/types" "^6.0.3" - before-after-hook "^2.1.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.10" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.10.tgz#741ce1fa2f4fb77ce8ebe0c6eaf5ce63f565f8e8" - integrity sha512-9+Xef8nT7OKZglfkOMm7IL6VwxXUQyR7DUSU0LH/F7VNqs8vyd7es5pTfz9E7DwUIx7R3pGscxu1EBhYljyu7Q== - dependencies: - "@octokit/types" "^6.0.0" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.5.8" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" - integrity sha512-WnCtNXWOrupfPJgXe+vSmprZJUr0VIu14G58PMlkWGj3cH+KLZEfKMmbUQ6C3Wwx6fdhzVW1CD5RTnBdUHxhhA== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.0" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-2.0.1.tgz#7453d8281ce66b8ed1607f7ac7d751c3baffd2cc" - integrity sha512-9AuC04PUnZrjoLiw3uPtwGh9FE4Q3rTqs51oNlQ0rkwgE8ftYsOC+lsrQyvCvWm85smBbSc0FNRKKumvGyb44Q== - -"@octokit/plugin-paginate-rest@^2.2.3": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.6.2.tgz#45d13dbf5ff8aed54f1a3716b1d57fdc62720c5f" - integrity sha512-3Dy7/YZAwdOaRpGQoNHPeT0VU1fYLpIUdPyvR37IyFLgd6XSij4j9V/xN/+eSjF2KKvmfIulEh9LF1tRPjIiDA== - dependencies: - "@octokit/types" "^6.0.1" - -"@octokit/plugin-rest-endpoint-methods@^4.0.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63" - integrity sha512-+v5PcvrUcDeFXf8hv1gnNvNLdm4C0+2EiuWt9EatjjUmfriM1pTMM+r4j1lLHxeBQ9bVDmbywb11e3KjuavieA== - dependencies: - "@octokit/types" "^6.1.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.4.tgz#07dd5c0521d2ee975201274c472a127917741262" - integrity sha512-LjkSiTbsxIErBiRh5wSZvpZqT4t0/c9+4dOe0PII+6jXR+oj/h66s7E4a/MghV7iT8W9ffoQ5Skoxzs96+gBPA== - dependencies: - "@octokit/types" "^6.0.0" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.12" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc" - integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" - -"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.1.0": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.1.2.tgz#2b3a6ae0b8b71c27c770b4ff3e9ad8f1f538af58" - integrity sha512-LPCpcLbcky7fWfHCTuc7tMiSHFpFlrThJqVdaHgowBTMS0ijlZFfonQC/C1PrZOjD4xRCYgBqH9yttEATGE/nw== - dependencies: - "@octokit/openapi-types" "^2.0.1" - "@types/node" ">= 8" - -"@opencensus/web-types@0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@opencensus/web-types/-/web-types-0.0.7.tgz#4426de1fe5aa8f624db395d2152b902874f0570a" - integrity sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g== - -"@opentelemetry/api@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.10.2.tgz#9647b881f3e1654089ff7ea59d587b2d35060654" - integrity sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA== - dependencies: - "@opentelemetry/context-base" "^0.10.2" - -"@opentelemetry/api@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.6.1.tgz#a00b504801f408230b9ad719716fe91ad888c642" - integrity sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q== - dependencies: - "@opentelemetry/context-base" "^0.6.1" - -"@opentelemetry/context-base@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.10.2.tgz#55bea904b2b91aa8a8675df9eaba5961bddb1def" - integrity sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw== - -"@opentelemetry/context-base@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.6.1.tgz#b260e454ee4f9635ea024fc83be225e397f15363" - integrity sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ== - -"@types/node-fetch@^2.3.7", "@types/node-fetch@^2.5.0": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" - integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - -"@types/node@*", "@types/node@>= 8": - version "14.14.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b" - integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw== - -"@types/tunnel@0.0.1", "@types/tunnel@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" - integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== - dependencies: - "@types/node" "*" - -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -before-after-hook@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" - integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -combined-stream@^1.0.6, combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - -events@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" - integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - -mime-types@^2.1.12: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -node-fetch@^2.6.0, node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver@^6.1.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tslib@^1.10.0, tslib@^1.9.3: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== - -tunnel@0.0.6, tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -uuid@^3.3.2, uuid@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -xml2js@^0.4.19: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== diff --git a/.github/hs-deploy-action/.gitignore b/.github/hs-deploy-action/.gitignore deleted file mode 100644 index a6c7c2852d0..00000000000 --- a/.github/hs-deploy-action/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/.github/hs-deploy-action/Dockerfile b/.github/hs-deploy-action/Dockerfile deleted file mode 100644 index 3d1e04e2f6f..00000000000 --- a/.github/hs-deploy-action/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:16 - -COPY package.json / -COPY yarn.lock / - -RUN yarn - -COPY src/*.ts / -COPY tsconfig.json / - -RUN ["yarn", "build"] - -ENTRYPOINT ["node", "/index.js"] diff --git a/.github/hs-deploy-action/README.md b/.github/hs-deploy-action/README.md deleted file mode 100644 index db247f3e85f..00000000000 --- a/.github/hs-deploy-action/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hosting service deployment bot - -Deploys Spartacus to Upscale hosting service diff --git a/.github/hs-deploy-action/action.yml b/.github/hs-deploy-action/action.yml deleted file mode 100644 index 21a4a21fc2f..00000000000 --- a/.github/hs-deploy-action/action.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: 'Hosting service deployment' -description: 'Hosting service deployment bot' -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/hs-deploy-action/package.json b/.github/hs-deploy-action/package.json deleted file mode 100644 index 3c6c10cd101..00000000000 --- a/.github/hs-deploy-action/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "deploy-action", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "tsc index.ts" - }, - "author": "", - "license": "Apache-2.0", - "dependencies": { - "@actions/exec": "^1.0.4", - "@actions/github": "^4.0.0", - "@types/node": "^12.11.1" - }, - "devDependencies": { - "typescript": "^4.1.3" - } -} diff --git a/.github/hs-deploy-action/src/deploy.ts b/.github/hs-deploy-action/src/deploy.ts deleted file mode 100644 index f28ada62a0a..00000000000 --- a/.github/hs-deploy-action/src/deploy.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as exec from '@actions/exec'; -import { ExecOptions } from '@actions/exec'; -import { addComment, getBundleId } from './functions'; - -/** - * Deploys an app to the hosting service - * @param github Github object - * @param octoKit Octokit object - * @param branch Name of the branch to deploy - */ -export async function deploy(github: any, octoKit: any, branch: string) { - const context = github.context; - - console.log(`--> Deploying branch ${branch}`); - - const bundleId = getBundleId(branch); - const command = `upp application deploy -b ${bundleId} -t spartacus --csr-dist ./dist/storefrontapp --ssr-dist ./dist/storefrontapp-server -e stage`; - - const exp = /https\:\/\/\w+\.cloudfront\.net/; - const ERROR = 'Response code: 500'; - - const options: ExecOptions = {}; - options.listeners = { - stdout: async (data: Buffer) => { - const line = data.toString(); - - if (line.includes(ERROR)) { - const body = `:exclamation: Spartacus deployment failed (${line}). Check job logs for details.)`; - console.log(`--> ${body}`); - await addComment(context, octoKit, body); - - process.exitCode = 1; - process.exit(); - } - - const match = line.match(exp); - if (match && match.length > 0) { - const body = `:rocket: Spartacus deployed to [${match}](${match})`; - console.log(`--> ${body}`); - await addComment(context, octoKit, body); - } - }, - stderr: (data: Buffer) => { - console.log(`upp deploy exited with error: ${data.toString()}`); - }, - }; - - await exec.exec(command, [], options); -} - -/** - * Undeploys an app from the Upscale hosting service - * @param branch Name of the branch to undeploy - */ -export async function undeploy(branch: any) { - const bundleId = getBundleId(branch); - const command = `upp application undeploy -b ${bundleId} -e stage`; - - const options: ExecOptions = { - input: Buffer.from('confirm\n'), - }; - options.listeners = { - stdout: (data: Buffer) => { - console.log(data.toString()); - }, - - stderr: (data: Buffer) => { - console.log(`upp deploy exited with error: ${data.toString()}`); - }, - }; - - console.log(`Starting Hosting Service Undeployment of PR branch ${branch}`); - - //run sh to get CLI and prep - await exec.exec(command, [], options); - - console.log('Application undeployed from Hosting service successfully'); -} diff --git a/.github/hs-deploy-action/src/functions.ts b/.github/hs-deploy-action/src/functions.ts deleted file mode 100755 index bdb0ca10d00..00000000000 --- a/.github/hs-deploy-action/src/functions.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as exec from '@actions/exec'; - -export async function build() { - //build libs and app - await exec.exec('yarn', ['install']); - await exec.exec('yarn', ['build:libs']); - await exec.exec('yarn', ['build']); - await exec.exec('yarn', ['build:ssr']); -} - -/** - * Adds hosting service deployment URL as a comment to the corresponding github pull request - * @param context Github context - * @param octoKit Github octokit object - * @param comment Comment body - */ -export async function addComment(context: any, octoKit: any, comment: string) { - const COMMENT_HEADER = '## Hosting service deployment'; - const issueNumber = context.payload.pull_request.number; - const owner = context.payload.repository.owner.login; - const repo = context.payload.repository.name; - const body = `${COMMENT_HEADER}\n${comment}`; - - const comments = await octoKit.issues.listComments({ - issue_number: issueNumber, - owner, - repo, - }); - - const botComment = comments.data.filter( - (comment: any) => - comment.body.includes(COMMENT_HEADER) && - comment.user.login === 'github-actions[bot]' - ); - - if (botComment && botComment.length) { - await octoKit.issues.deleteComment({ - comment_id: botComment[0].id, - owner, - repo, - }); - } - - await octoKit.issues.createComment({ - issue_number: issueNumber, - owner, - repo, - body, - }); -} - -/** - * Generates a UPP valid bundle Id based on the branch name - * replaces slashes with -s and other chars with -d - * @param branch Branch name - */ -export function getBundleId(branch: string) { - let bundleId = ''; - const regex = /(\-\d)/; - branch - .toLowerCase() - .replace(/\//g, '-s') - .replace(/\./g, '-d') - .replace(/_/g, '-') - .split(regex) - .forEach((s: string) => { - if (s.match(regex)) { - bundleId += s.substring(0, 1) + 'i' + s.substring(1, 2); - } else { - bundleId += s; - } - }); - console.log(`--> Generated bundle ID: ${bundleId}`); - return bundleId; -} diff --git a/.github/hs-deploy-action/src/index.ts b/.github/hs-deploy-action/src/index.ts deleted file mode 100644 index 6aaeef62b1b..00000000000 --- a/.github/hs-deploy-action/src/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as github from '@actions/github'; -import * as exec from '@actions/exec'; -import { build } from './functions'; -import { deploy, undeploy } from './deploy'; - -async function run() { - const GITHUB_TOKEN = process.env.GITHUB_TOKEN; - const UPP_ACTION = process.env.UPP_ACTION; - - if (!GITHUB_TOKEN) { - throw new Error('Github token missing in action'); - } - - const octoKit = github.getOctokit(GITHUB_TOKEN); - const context = github.context; - const eventName = context.eventName; - - let branch; - - if (eventName === 'push') { - branch = context.payload.ref.replace('refs/heads/', ''); - } else if (eventName === 'pull_request') { - branch = context.payload.pull_request.head.ref; - } - - console.log( - `Starting Hosting service deployment on '${eventName}' of branch '${branch}'` - ); - - //run sh to get CLI and prep - await exec.exec('sh', ['./.github/hs-deploy-action/upp-cli-setup.sh']); - - if (UPP_ACTION === 'deploy') { - await build(); - await deploy(github, octoKit, branch); - console.log('--> Hosting service deployment done!'); - } else if (UPP_ACTION === 'undeploy') { - await undeploy(branch); - } -} - -run(); diff --git a/.github/hs-deploy-action/tsconfig.json b/.github/hs-deploy-action/tsconfig.json deleted file mode 100644 index 83185e220be..00000000000 --- a/.github/hs-deploy-action/tsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, - "lib": [ - "es2019", - "esnext.bigint", - "es2020.string", - "es2020.symbol.wellknown" - ] /* Specify library files to be included in the compilation. */, - "allowJs": false /* Allow javascript files to be compiled. */, - "checkJs": false /* Report errors in .js files. */, - "declaration": false /* Generates corresponding '.d.ts' file. */, - "declarationMap": false /* Generates a sourcemap for each corresponding '.d.ts' file. */, - "sourceMap": false /* Generates corresponding '.map' file. */, - - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - - /* Additional Checks */ - "noUnusedLocals": true /* Report errors on unused locals. */, - "noUnusedParameters": true /* Report errors on unused parameters. */, - "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, - - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - "esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/.github/hs-deploy-action/upp-cli-setup.sh b/.github/hs-deploy-action/upp-cli-setup.sh deleted file mode 100755 index 5c241e2ff24..00000000000 --- a/.github/hs-deploy-action/upp-cli-setup.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -set -o errexit -set -o nounset - -APP="upp-cli" - -echo "-----" -echo "Downloading upp cli zip" - -# TODO pull fron NPM once the CLI is published. For now, we can only get it from assets on github releases -curl -u ${GHT_USER}:${GHT_TOKEN} -L -H "Accept: application/octet-stream" \ - "https://github.tools.sap/api/v3/repos/cx-commerce/upscale-partner-platform-cli/releases/assets/10192" -o ${APP}.zip - -if [ ! -s ${APP}.zip ]; then - echo "Error downloading upp CLI zip. Check url and configs" - exit 1 -fi - -echo "-----" -echo "Installing upp cli (dependencies)" -unzip -o ${APP}.zip -d ${APP} -cd ${APP} -npm install -chmod -R 777 /github/home/.npm -chmod -R 777 . - -echo "-----" -echo "Installing UPP CLI" -npm run install-cli - -cd .. - -echo "-----" -echo "Configuring cli" - -upp config -z -t ${UPP_TENANT} -c ${UPP_CLIENT} -s ${UPP_SECRET} -r us10 -i 3 -a us10.staging.upp.upscalecommerce.com - -echo "-----" -echo "UPP CLI installed and ready." \ No newline at end of file diff --git a/.github/hs-deploy-action/yarn.lock b/.github/hs-deploy-action/yarn.lock deleted file mode 100644 index 81cdbb517dd..00000000000 --- a/.github/hs-deploy-action/yarn.lock +++ /dev/null @@ -1,191 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@actions/exec@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.0.4.tgz" - integrity sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw== - dependencies: - "@actions/io" "^1.0.1" - -"@actions/github@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@actions/github/-/github-4.0.0.tgz" - integrity sha512-Ej/Y2E+VV6sR9X7pWL5F3VgEWrABaT292DRqRU6R4hnQjPtC/zD3nagxVdXWiRQvYDh8kHXo7IDmG42eJ/dOMA== - dependencies: - "@actions/http-client" "^1.0.8" - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.3" - "@octokit/plugin-rest-endpoint-methods" "^4.0.0" - -"@actions/http-client@^1.0.8": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-1.0.9.tgz" - integrity sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg== - dependencies: - tunnel "0.0.6" - -"@actions/io@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.0.2.tgz" - integrity sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg== - -"@octokit/auth-token@^2.4.4": - version "2.4.5" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz" - integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== - dependencies: - "@octokit/types" "^6.0.3" - -"@octokit/core@^3.0.0": - version "3.2.5" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.5.tgz" - integrity sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.4.12" - "@octokit/types" "^6.0.3" - before-after-hook "^2.1.0" - universal-user-agent "^6.0.0" - -"@octokit/endpoint@^6.0.1": - version "6.0.11" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz" - integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - -"@octokit/graphql@^4.5.8": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.0.tgz" - integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - -"@octokit/openapi-types@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.1.0.tgz" - integrity sha512-bodZvSYgycbUuuKrC/anCBUExvaSSWzMMFz0xl7pcJujxnmGxvqvcFHktjx1ZOSyeNKLfYF0QCgibaHUGsZTng== - -"@octokit/plugin-paginate-rest@^2.2.3": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.10.0.tgz" - integrity sha512-71OsKBSMcQEu/6lfVbhv5C5ikU1rn10rKot/WiV7do7fyfElQ2eCUQFogHPbj0ci5lnKAjvahOiMAr6lcvL8Qw== - dependencies: - "@octokit/types" "^6.10.0" - -"@octokit/plugin-rest-endpoint-methods@^4.0.0": - version "4.12.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.12.0.tgz" - integrity sha512-RgnQ1aoetdOJjZYC37LV5FNlL7GY/v1CdC5dur1Zp/UiADJlbRFbAz/xLx26ovXw67dK7EUtwCghS+6QyiI9RA== - dependencies: - "@octokit/types" "^6.10.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz" - integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": - version "5.4.14" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.14.tgz" - integrity sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.7.1" - deprecation "^2.0.0" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - once "^1.4.0" - universal-user-agent "^6.0.0" - -"@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.7.1": - version "6.10.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.10.0.tgz" - integrity sha512-aMDo10kglofejJ96edCBIgQLVuzMDyjxmhdgEcoUUD64PlHYSrNsAGqN0wZtoiX4/PCQ3JLA50IpkP1bcKD/cA== - dependencies: - "@octokit/openapi-types" "^5.1.0" - -"@types/node@^12.11.1": - version "12.20.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.4.tgz#73687043dd00fcb6962c60fbf499553a24d6bdf2" - integrity sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ== - -before-after-hook@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.1.tgz" - integrity sha512-5ekuQOvO04MDj7kYZJaMab2S8SPjGJbotVNyv7QYFCOAwrGZs/YnoDNlh1U+m5hl7H2D/+n0taaAV/tfyd3KMA== - -deprecation@^2.0.0, deprecation@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz" - integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== - -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tunnel@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -typescript@^4.1.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz" - integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.github/workflows/breaking-changes-detection-bot.yml b/.github/workflows/breaking-changes-detection-bot.yml index 3190b5581f9..89c3b25f217 100644 --- a/.github/workflows/breaking-changes-detection-bot.yml +++ b/.github/workflows/breaking-changes-detection-bot.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.8.0 + uses: styfle/cancel-workflow-action@0.12.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@master diff --git a/.github/workflows/cache-builded-libs.yml b/.github/workflows/cache-builded-libs.yml index 5db2ae99ef2..32f3e07f06b 100644 --- a/.github/workflows/cache-builded-libs.yml +++ b/.github/workflows/cache-builded-libs.yml @@ -2,8 +2,9 @@ on: push: branches: - develop + - develop-* - 'epic/**' - - 'release/**' + - release-* name: Cache libs (dist) jobs: cacheBuildedLibs: diff --git a/.github/workflows/cache-node-modules.yml b/.github/workflows/cache-node-modules.yml index c240cd2b706..4d56819d606 100644 --- a/.github/workflows/cache-node-modules.yml +++ b/.github/workflows/cache-node-modules.yml @@ -2,34 +2,36 @@ on: push: branches: - develop + - develop-* - 'epic/**' - - 'release/**' + - release-* name: Cache node modules env: - NODE_VERSION: '16' + NODE_VERSION: '20' + CONTINUUM_REGISTRY_TOKEN: ${{ secrets.CONTINUUM_REGISTRY_TOKEN }} jobs: cacheNodeModules: name: Cache node_modules runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Installing root dependencies - run: yarn --frozen-lockfile + run: npm ci - name: Installing cypress dependencies working-directory: projects/storefrontapp-e2e-cypress - run: yarn --frozen-lockfile + run: npm ci - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress - key: nodemodules-${{ github.sha }} \ No newline at end of file + key: nodemodules-${{ github.sha }} diff --git a/.github/workflows/ci-continuous-integration.yml b/.github/workflows/ci-continuous-integration.yml new file mode 100644 index 00000000000..91457b16e59 --- /dev/null +++ b/.github/workflows/ci-continuous-integration.yml @@ -0,0 +1,102 @@ +on: + pull_request: + types: [opened, synchronize] + branches: + - develop + - develop-* + - release-* + - epic/** + workflow_dispatch: + # empty as it is used only to manually trigger the workflow + +env: + NODE_VERSION: '20' + NX_BASE: origin/${{ github.event.pull_request.base.ref }} + NX_HEAD: origin/${{ github.event.pull_request.head.ref }} + +concurrency: + group: ci-continuous-integration-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +name: Continuous Integration +jobs: + unit_tests: + name: CI - Unit tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Run unit tests + run: | + ci-scripts/unit-tests.sh + sonarqube_scan: + name: CI - SonarQube Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: SonarQube Scan + uses: sonarsource/sonarqube-scan-action@master + with: + args: > + -Dsonar.qualitygate.wait=true + -Dsonar.projectKey=composable-storefront + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: https://sonar.tools.sap + if: github.event_name == 'pull_request' + linting: + name: CI - Validations and static code checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Run linting validation + run: | + ci-scripts/validate-lint.sh + ci_result: + needs: [unit_tests, sonarqube_scan, linting] + name: CI - Result + runs-on: ubuntu-latest + if: ${{ always() }} + steps: + - name: Aggregate Required Job Results + run: | + exit 1 + if: | + needs.unit_tests.result == 'failure' || needs.unit_tests.result == 'cancelled' || + needs.sonarqube_scan.result == 'failure' || needs.sonarqube_scan.result == 'cancelled' || + needs.linting.result == 'failure' || needs.linting.result == 'cancelled' diff --git a/.github/workflows/ci-merge-checks.yml b/.github/workflows/ci-merge-checks.yml new file mode 100644 index 00000000000..67dc0cc0233 --- /dev/null +++ b/.github/workflows/ci-merge-checks.yml @@ -0,0 +1,177 @@ +on: + pull_request: + types: [ready_for_review] + branches: + - develop + - develop-* + - release-* + workflow_dispatch: + # empty as it is used only to manually trigger the workflow + +env: + CYPRESS_KEY: ${{ secrets.CYPRESS_KEY }} + GH_TOKEN: ${{ github.token }} + NODE_VERSION: '20' + AMP_API_TOKEN: ${{ secrets.AMP_API_TOKEN }} + CONTINUUM_REGISTRY_TOKEN: ${{ secrets.CONTINUUM_REGISTRY_TOKEN }} + +concurrency: + group: ci-merge-checks-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +name: Merge Checks +jobs: + no_retries: + name: MC - Prevent retries + # E2Es can't be retried. Moreover, in some retry cases, they don't run + # and the job returns a success code. + runs-on: ubuntu-latest + steps: + - name: Forcefully fail build if jobs are all retried + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + if: ${{ github.run_attempt > 1 }} + validate_e2e_execution: + name: MC - Confirm e2es should run + runs-on: ubuntu-latest + outputs: + SHOULD_RUN_E2E: ${{ steps.save-e2e-output-result.outputs.SHOULD_RUN_E2E }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Determine whether to run e2es + id: save-e2e-output-result + run: | + source ci-scripts/validate-e2e-execution.sh + echo "::set-output name=SHOULD_RUN_E2E::$(echo $RUN_E2E)" + b2c_e2e_tests: + needs: [no_retries, validate_e2e_execution] + name: MC - E2E B2C core + runs-on: ubuntu-latest + strategy: + matrix: + containers: [1, 2, 3, 4, 5] + if: ${{ needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + steps: + - name: Forcefully fail build if e2e job is retried + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + if: ${{ github.run_attempt > 1 }} + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Run e2es + env: + SPA_ENV: ci,b2c + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} + run: | + ci-scripts/e2e-cypress.sh + b2c_ssr_e2e_tests: + needs: [no_retries, validate_e2e_execution] + name: MC - E2E SSR core (B2C) + runs-on: ubuntu-latest + if: ${{ needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + steps: + - name: Forcefully fail build if e2e job is retried + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + if: ${{ github.run_attempt > 1 }} + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Run e2es + env: + SPA_ENV: ci,b2c + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} + run: | + ci-scripts/e2e-cypress.sh --ssr + b2b_e2e_tests: + needs: [no_retries, validate_e2e_execution] + name: MC - E2E B2B core + runs-on: ubuntu-latest + strategy: + matrix: + containers: [1, 2, 3, 4] + if: ${{ needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + steps: + - name: Forcefully fail build if e2e job is retried + uses: actions/github-script@v7 + with: + script: | + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + if: ${{ github.run_attempt > 1 }} + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Run e2es + env: + SPA_ENV: ci,b2b + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} + run: | + ci-scripts/e2e-cypress.sh -s b2b + merge_checks_result: + needs: [b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests] + name: MC - Result + runs-on: ubuntu-latest + if: ${{ always() }} + steps: + - name: Aggregate Required Job Results + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `# Merge Checks Failed + Please push a commit to re-trigger the build. + To push an empty commit you can use \`git commit --allow-empty -m "Trigger Build"\`` + }) + core.setFailed('Please push a commit to re-trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + if: | + needs.b2c_e2e_tests.result == 'failure' || needs.b2c_e2e_tests.result == 'cancelled' || + needs.b2c_ssr_e2e_tests.result == 'failure' || needs.b2c_ssr_e2e_tests.result == 'cancelled' || + needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' diff --git a/.github/workflows/ci-pull-request-status.yml b/.github/workflows/ci-pull-request-status.yml new file mode 100644 index 00000000000..b65afe17cdb --- /dev/null +++ b/.github/workflows/ci-pull-request-status.yml @@ -0,0 +1,28 @@ +on: + pull_request: + types: [opened, synchronize] + branches: + - develop + - develop-* + - release-* + workflow_dispatch: + # empty as it is used only to manually trigger the workflow + +env: + GH_TOKEN: ${{ github.token }} + +concurrency: + group: ci-merge-checks-${{ github.head_ref || github.run_id }} + # This workflow has the same group id as the merge checks (e2e tests) workflow. + # The goal is to cancel pending expensive tests when the PR is set to to draft. + cancel-in-progress: true + +name: Pull Request +jobs: + change-to-draft: + name: PR - Set status to draft + runs-on: ubuntu-latest + steps: + - name: Run Script + run: | + gh pr ready --undo ${{github.event.number}} -R ${{ github.repository }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58ef5a5811b..a2d9f8d7d3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,123 +2,114 @@ on: push: branches: - develop - - epic/** - - release/** - - integration-libs/** - pull_request: - types: [opened, synchronize] + - develop-* + - release-* + workflow_dispatch: + # empty as it is used only to manually trigger the workflow + env: CYPRESS_KEY: ${{ secrets.CYPRESS_KEY }} - NODE_VERSION: '16' + NODE_VERSION: '20' + AMP_API_TOKEN: ${{ secrets.AMP_API_TOKEN }} + CONTINUUM_REGISTRY_TOKEN: ${{ secrets.CONTINUUM_REGISTRY_TOKEN }} -concurrency: +concurrency: group: ci-${{ github.head_ref || github.run_id }} cancel-in-progress: true name: Spartacus build pipeline jobs: - no_retries: + no_retries: name: Verify re-run of all jobs runs-on: ubuntu-latest steps: - name: Forcefully fail build if jobs are all retried - uses: actions/github-script@v5 + uses: actions/github-script@v7 with: script: | - core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') if: ${{ github.run_attempt > 1 }} validate_e2e_execution: name: Validate pull_request files runs-on: ubuntu-latest - outputs: + outputs: SHOULD_RUN_E2E: ${{ steps.save-e2e-output-result.outputs.SHOULD_RUN_E2E }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine whether to run e2es id: save-e2e-output-result - run: | + run: | source ci-scripts/validate-e2e-execution.sh echo "::set-output name=SHOULD_RUN_E2E::$(echo $RUN_E2E)" - unit_tests_core: + unit_tests: needs: no_retries - name: Unit tests for core Spartacus libs + name: Unit tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: | + path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress key: nodemodules-${{ github.event.pull_request.base.sha }} restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - - name: Install angular CLI - run: npm install -g @angular/cli@v13-lts - name: Package installation - run: yarn --frozen-lockfile - - name: Run unit tests for Spartacus libs + run: npm ci + - name: Run unit tests run: | - ci-scripts/unit-tests-core-lib.sh - unit_tests_libs: + ci-scripts/unit-tests.sh + sonarqube_scan: needs: no_retries - name: Unit tests for integration libs + name: SonarQube Scan runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup node - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 with: - node-version: ${{ env.NODE_VERSION }} - - name: Cache node_modules - id: cache-node-modules - uses: actions/cache@v2 + fetch-depth: 0 + - name: SonarQube Scan + uses: sonarsource/sonarqube-scan-action@master with: - path: | - node_modules - projects/storefrontapp-e2e-cypress/node_modules - ~/.cache/Cypress - key: nodemodules-${{ github.event.pull_request.base.sha }} - restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - - name: Install angular CLI - run: npm install -g @angular/cli@v13-lts - - name: Package installation - run: yarn --frozen-lockfile - - name: Run unit tests for integration libs - run: | - ci-scripts/unit-tests.sh + args: > + -Dsonar.qualitygate.wait=true + -Dsonar.projectKey=composable-storefront + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: https://sonar.tools.sap + if: github.event_name == 'pull_request' linting: needs: no_retries name: Validations and static code checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: | + path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress key: nodemodules-${{ github.event.pull_request.base.sha }} restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - - name: Install angular CLI - run: npm install -g @angular/cli@v13-lts - name: Package installation - run: yarn --frozen-lockfile + run: npm ci - name: Run linting validation run: | ci-scripts/validate-lint.sh @@ -129,56 +120,66 @@ jobs: strategy: matrix: containers: [1, 2, 3, 4, 5] - if: ${{ github.event_name == 'push' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} steps: - name: Forcefully fail build if e2e job is retried - uses: actions/github-script@v5 + uses: actions/github-script@v7 with: script: | - core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') if: ${{ github.run_attempt > 1 }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: | + path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress key: nodemodules-${{ github.event.pull_request.base.sha }} restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - name: Run e2es - env: + env: SPA_ENV: ci,b2c + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} run: | ci-scripts/e2e-cypress.sh b2c_ssr_e2e_tests: needs: [no_retries, validate_e2e_execution] name: E2E tests for SSR B2C runs-on: ubuntu-latest - if: ${{ github.event_name == 'push' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} steps: - name: Forcefully fail build if e2e job is retried - uses: actions/github-script@v5 + uses: actions/github-script@v7 with: script: | - core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') if: ${{ github.run_attempt > 1 }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: | + path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress key: nodemodules-${{ github.event.pull_request.base.sha }} restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - name: Run e2es - env: + env: SPA_ENV: ci,b2c + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} run: | ci-scripts/e2e-cypress.sh --ssr b2b_e2e_tests: @@ -187,21 +188,25 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - containers: [1, 2] - if: ${{ github.event_name == 'push' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} + containers: [1, 2, 3, 4] + if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.validate_e2e_execution.outputs.SHOULD_RUN_E2E == 'true' }} steps: - name: Forcefully fail build if e2e job is retried - uses: actions/github-script@v5 + uses: actions/github-script@v7 with: script: | - core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') + core.setFailed('Please push a commit to trigger the build. To push an empty commit you can use `git commit --allow-empty -m "Trigger Build"`') if: ${{ github.run_attempt > 1 }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: | + path: | node_modules projects/storefrontapp-e2e-cypress/node_modules ~/.cache/Cypress @@ -210,37 +215,69 @@ jobs: - name: Run e2es env: SPA_ENV: ci,b2b - run: | + BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} + run: | ci-scripts/e2e-cypress.sh -s b2b + ssr_tests: + needs: [no_retries, validate_e2e_execution] + name: SSR Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: | + node_modules + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Build SSR Server + run: npm run build:libs && npm run build && npm run build:ssr:local-http-backend + - name: Run SSR tests + run: npm run test:ssr:ci --verbose build_conclusion: - needs: [no_retries, unit_tests_core, unit_tests_libs, linting, b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests] + needs: [no_retries, unit_tests, linting, b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests, ssr_tests, sonarqube_scan] name: Build Conclusion runs-on: ubuntu-latest if: ${{ always() }} steps: - name: Required build failed - uses: actions/github-script@v5 + uses: actions/github-script@v7 with: script: | core.setFailed('Build failed') if: | needs.no_retries.result == 'failure' || needs.no_retries.result == 'cancelled' || - needs.unit_tests_core.result == 'failure' || needs.unit_tests_core.result == 'cancelled' || - needs.unit_tests_libs.result == 'failure' || needs.unit_tests_libs.result == 'cancelled' || + needs.unit_tests.result == 'failure' || needs.unit_tests.result == 'cancelled' || + needs.sonarqube_scan.result == 'failure' || needs.sonarqube_scan.result == 'cancelled' || needs.linting.result == 'failure' || needs.linting.result == 'cancelled' || needs.b2c_e2e_tests.result == 'failure' || needs.b2c_e2e_tests.result == 'cancelled' || - needs.b2c_ssr_e2e_tests.result == 'failure' || needs.b2c_ssr_e2e_tests.result == 'cancelled' || - needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' + needs.b2c_ssr_e2e_tests.result == 'failure' || needs.b2c_ssr_e2e_tests.result == 'cancelled' || + needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' || + needs.ssr_tests.result == 'failure' || needs.ssr_tests.result == 'cancelled' send_slack_message: needs: build_conclusion - name: Slack message for failed develop CI build in Spartacus + name: Slack message for failed CI build in Spartacus runs-on: ubuntu-latest if: ${{ always() }} steps: + - run: | + echo "Context Info:" + echo " Event = ${{ github.event_name }}" + echo " Build Result = ${{ needs.build_conclusion.result }}" + echo " Branch = ${{ github.ref_name }}" + echo " Default branch = ${{ github.event.repository.default_branch }}" - name: Notify the slack channel of when build conclusion failed env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} - uses: slackapi/slack-github-action@v1.19.0 + uses: slackapi/slack-github-action@v1.26.0 with: channel-id: ${{ secrets.SLACK_NOTIFICATION_CHANNEL }} payload: | @@ -250,7 +287,7 @@ jobs: "type": "section", "text": { "type": "mrkdwn", - "text": ":nuclear-bomb: :fireduck: *Broken build in develop*" + "text": ":nuclear-bomb: :fireduck: *Broken build on ${{ github.ref_name }}*" } }, { @@ -293,8 +330,7 @@ jobs: } ] } - if: | + if: | needs.build_conclusion.result == 'failure' && - github.event_name == 'push' && - github.ref_name == 'develop' - + github.event_name == 'push' && + github.ref_name == github.event.repository.default_branch diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..7ab1fc800ac --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,102 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "develop", "develop*", "release-*", "epic/*"] + pull_request: + branches: [ "develop", "develop*", "release-*", "epic/*" ] + schedule: + - cron: '45 2 * * *' + + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + config: | + name: Disable scan for tests + paths-ignore: + - "projects/storefrontapp-e2e-cypress/**" + - "**/*.spec.ts" + - "**/*.spec.js" + - "**/*_spec.ts" + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/config-check.yml b/.github/workflows/config-check.yml index de17bab6811..31f314f0ea0 100644 --- a/.github/workflows/config-check.yml +++ b/.github/workflows/config-check.yml @@ -8,16 +8,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.8.0 + uses: styfle/cancel-workflow-action@0.12.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@master - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: '16' - - name: Yarn - run: yarn + node-version: '20' + - name: NPM + run: npm i - name: Check configurations - run: yarn config:check + run: npm run config:check env: FORCE_COLOR: 2 # Support colors from chalk diff --git a/.github/workflows/deploy-hs.yml b/.github/workflows/deploy-hs.yml deleted file mode 100644 index c88e2c3a575..00000000000 --- a/.github/workflows/deploy-hs.yml +++ /dev/null @@ -1,35 +0,0 @@ -on: - pull_request: - types: [opened, synchronize] - branches: - - 'develop' - - 'feature/GH-**' - - 'epic/**' - - '!release/4.1.x' - push: - branches: - - develop - - release/4.1.x -name: Hosting service - Deploy -jobs: - hostingServiceDeploy: - name: Deployment bot - environment: dev - runs-on: ubuntu-latest - steps: - - name: Cancel Previous Runs - if: ${{ !env.ACT }} - uses: styfle/cancel-workflow-action@0.8.0 - with: - access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v2 - - name: Deployment bot - uses: ./.github/hs-deploy-action - env: - UPP_ACTION: 'deploy' - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GHT_USER: ${{ secrets.GHT_USER}} - GHT_TOKEN: ${{ secrets.GHT_TOKEN}} - UPP_TENANT: ${{ secrets.UPP_TENANT}} - UPP_CLIENT: ${{ secrets.UPP_CLIENT}} - UPP_SECRET: ${{ secrets.UPP_SECRET}} diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 568fcb4832a..0b928b69b8d 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -4,6 +4,7 @@ on: - 'release/4.**' - 'epic/**' - 'develop' + - 'develop-*' name: Installation jobs: install: @@ -12,16 +13,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.8.0 + uses: styfle/cancel-workflow-action@0.12.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: '16' - - name: Yarn - run: yarn --frozen-lockfile + node-version: '20' + - name: NPM + run: npm ci env: FORCE_COLOR: 2 # Support colors from chalk - name: run installation script - run: cd ./scripts/install && ./run.sh install && ./run.sh start \ No newline at end of file + run: cd ./scripts/install && ./run.sh install && ./run.sh start diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml index 7c8b898fc6d..9aa40ab64b4 100644 --- a/.github/workflows/lighthouse.yml +++ b/.github/workflows/lighthouse.yml @@ -1,15 +1,15 @@ on: - push: + pull_request: + types: [ready_for_review] branches: - develop - - epic/** - - release/** - - integration-libs/** - pull_request: - types: [opened, synchronize] + - develop-* + - release-* + workflow_dispatch: + # empty as it is used only to manually trigger the workflow env: CYPRESS_KEY: ${{ secrets.CYPRESS_KEY }} - NODE_VERSION: '16' + NODE_VERSION: '20' concurrency: group: lighthouse-${{ github.head_ref || github.run_id }} @@ -21,14 +21,14 @@ jobs: name: Lighthouse score validation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Cache node_modules id: cache-node-modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | node_modules @@ -36,10 +36,8 @@ jobs: ~/.cache/Cypress key: nodemodules-${{ github.event.pull_request.base.sha }} restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - - name: Install angular CLI - run: npm install -g @angular/cli@v13-lts - name: Package installation - run: yarn --frozen-lockfile + run: npm ci - name: Run lighthouse score validation run: | - ci-scripts/lhci.sh \ No newline at end of file + ci-scripts/lhci.sh diff --git a/.github/workflows/minor-release.yml b/.github/workflows/minor-release.yml new file mode 100644 index 00000000000..76341778afa --- /dev/null +++ b/.github/workflows/minor-release.yml @@ -0,0 +1,99 @@ +name: Minor Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 2211.20.0 or 2211.20.0-1)' + required: false + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: develop + + - name: Configure Git + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + + - name: Set Version + run: | + echo "version=${{ github.event.inputs.version }}" >> $GITHUB_ENV + + - name: Create Release Branch + run: | + version="${{ env.version }}" + if [[ $version == *-* ]]; then + IFS='-' read -ra VERSION_AND_PRERELEASE <<< "$version" + prerelease="${VERSION_AND_PRERELEASE[*]:1}" + version_part=${VERSION_AND_PRERELEASE[0]} + else + version_part=$version + prerelease="" + fi + + IFS='.' read -ra ADDR <<< "$version_part" + echo "major=${ADDR[0]}" >> $GITHUB_ENV + echo "minor=${ADDR[1]}" >> $GITHUB_ENV + echo "patch=${ADDR[2]}" >> $GITHUB_ENV + release_branch="release-${ADDR[0]}.${ADDR[1]}.${ADDR[2]}" + + if [[ -n $prerelease ]]; then + release_branch="${release_branch}-${prerelease// /-}" + fi + + echo "RELEASE_BRANCH=$release_branch" >> $GITHUB_ENV + + echo "Checking for existence of the branch: $release_branch" + + if git ls-remote --heads origin "$release_branch" | grep -q "$release_branch"; then + echo "Error: Branch $release_branch already exists. Please check the branch before proceeding." + exit 1 + fi + + git checkout -b $release_branch + + - name: Update SCSS Versioning + run: | + major=${{ env.major }} + minor=${{ env.minor }} + patch=${{ env.patch }} + sed -i "s/major: [0-9]\\{1,\\},/major: $major,/" projects/storefrontstyles/scss/_versioning.scss + sed -i "s/minor: [0-9]\\{1,\\},/minor: $minor,/" projects/storefrontstyles/scss/_versioning.scss + sed -i "s/patch: [0-9]\\{1,\\},/patch: $patch,/" projects/storefrontstyles/scss/_versioning.scss + echo "Updated versions in projects/storefrontstyles/scss/_versioning.scss." + + - name: Update Publishing Version + run: | + version="${{ env.version }}" # Use the full version + sed -i "s/export const PUBLISHING_VERSION = '.*';/export const PUBLISHING_VERSION = '$version';/" ./tools/config/const.ts + echo "Updated PUBLISHING_VERSION in ./tools/config/const.ts." + + + - name: Package installation + run: npm ci + + - name: Run Config Update + run: | + if ! npm run config:update; then + echo "Error: The configuration file is not up to date. Regenerating dependencies..." + npm run generate:deps + npm run config:update + fi + npm run generate:deps + + - name: Run Schematics Tests + run: | + npm run test:all-schematics + + - name: Push Changes + run: | + git add . + git commit -m "Release ${{ env.version }}" + git push origin "${{ env.RELEASE_BRANCH }}" + \ No newline at end of file diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 00000000000..f8a03e93ac5 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,59 @@ +on: + workflow_dispatch: + inputs: + compodoc: + description: 'Build and deploy compodoc?' + required: false + type: boolean + what_version: + description: 'What is the release version? (e.g. 5.0.1)' + required: true + +name: Pre-release for Spartacus + +jobs: + publish_compodoc: + name: Deploy compodocs to github + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Generate and publish compodocs + if: ${{ github.event.inputs.compodoc == 'true' }} + run: | + git remote set-url origin https://git:${{ secrets.GH_TEMPORARY_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git + + npm run generate:publish:docs -- -u "github-actions-bot " + tag_sampledata: + name: Tagging sample data + runs-on: ubuntu-latest + steps: + - name: Determine major version + env: + SPARTACUS_VERSION: ${{ github.event.inputs.what_version }} + id: current-run-major-version + run: echo "MAJOR_VERSION=${SPARTACUS_VERSION%%.*}" >> $GITHUB_OUTPUT + - name: Tagging the branch of release/${{ steps.current-run-major-version.outputs.MAJOR_VERSION}}.x + uses: actions/checkout@v4 + with: + github-server-url: 'https://github.tools.sap' + repository: 'cx-commerce/spartacussampledata' + token: ${{ secrets.GHT_PRIVATE_REPO_TOKEN }} + ref: 'release/${{ steps.current-run-major-version.outputs.MAJOR_VERSION}}.x' + - name: push the tag to the repository + run: | + git tag ${{ github.event.inputs.what_version }} + git push origin --tags + publish_sample_data: + name: Publishing Spartacus Sample Data + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: create release with sample data assets + env: + GHT_USER: ${{ secrets.GHT_USER }} + GHT_PRIVATE_REPO_TOKEN: ${{ secrets.GHT_PRIVATE_REPO_TOKEN }} + GH_TOKEN: ${{ secrets.GH_TEMPORARY_TOKEN }} + run: | + ci-scripts/publish-sample-data.sh diff --git a/.github/workflows/prepend-license.yml b/.github/workflows/prepend-license.yml new file mode 100644 index 00000000000..1f4944e5dfc --- /dev/null +++ b/.github/workflows/prepend-license.yml @@ -0,0 +1,36 @@ +on: + pull_request: + types: [opened, synchronize] + +name: Add license header to files +jobs: + add_license_header: + name: Add license header to files + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + token: ${{ secrets.GH_PR_TOKEN }} + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: Download reuse tool + run: pip3 install --user reuse + - name: Setup git config + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + - name: Adding license header to files + run: ci-scripts/prepend-license.sh + - name: push added headers + run: | + git pull origin ${{ github.event.pull_request.head.ref }} + + if [ -z $(git status --porcelain) ]; + then + echo "No files to commit" + else + git commit -am "Add license header" + git push + fi diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml new file mode 100644 index 00000000000..10ee53288e4 --- /dev/null +++ b/.github/workflows/repo-sync.yml @@ -0,0 +1,124 @@ +on: + schedule: + - cron: '0 0 * * 1,4' + workflow_dispatch: + inputs: + branch_to_sync: + description: Branch to sync to the private repository (repo default branch if left empty) + required: false + +name: Sync a branch to private repo + +env: + DEFAULT_BRANCH_TO_SYNC: ${{ github.event.repository.default_branch }} + +jobs: + sync_public_repo_to_private: + name: Force sync a branch to the private repository with current git history + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }} + fetch-depth: 0 + - name: Push a branch to the private repository + run: | + git push -u https://${{ secrets.GHT_USER }}:${{ secrets.GHT_PRIVATE_REPO_TOKEN }}@github.tools.sap/cx-commerce-storefronts/${{ secrets.GHT_SPARTACUS_REPO }}.git ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }} -f + - name: Include remaining pipeline files to the private remote's branch + run: | + PIPELINE_CONFIG_PATH="./.pipeline/config.yml" + AZURE_CONFIG_PATH="./azure-pipelines.yml" + SONAR_PATH="./sonar-project.properties" + RELEASE_PACKAGES_LIST_GENERATOR="./ci-scripts/release-packages-list-generator.sh" + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Clone repo" + + git clone -b ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }} https://${{ secrets.GHT_USER }}:${{ secrets.GHT_PRIVATE_REPO_TOKEN }}@github.tools.sap/cx-commerce-storefronts/${{ secrets.GHT_SPARTACUS_REPO }}.git + cd ${{ secrets.GHT_SPARTACUS_REPO }} + + git config --global user.email ${{ secrets.GHT_EMAIL }} + git config --global user.name ${{ secrets.GHT_USER }} + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Get files from other branch" + + git checkout origin/sap-pipeline-files -- . + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Configure pipeline" + + sed -i "s%productiveBranch:%productiveBranch: '${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}'%gi" $PIPELINE_CONFIG_PATH + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Verify productiveBranch has been updated with the synched branch name" + + if grep -Fq "productiveBranch: '${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}'" $PIPELINE_CONFIG_PATH + then + echo "Branch name has successfully been added to the productiveBranch" + else + echo "Error. Branch name has NOT been added to the productiveBranch" + exit 1 + fi + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Configure azure pipeline trigger" + + sed -i "s%trigger:%trigger:\n - ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}%gi" $AZURE_CONFIG_PATH + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Verify trigger has been updated with the synched branch name" + + if grep -Pzo "trigger:\n - ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}" $AZURE_CONFIG_PATH + then + echo "Branch name has successfully been added to the trigger" + else + echo "Error. Branch name has NOT been added to the trigger" + exit 1 + fi + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Configure azure pipeline build packages list" + + RELEASE_PACKAGES=$($RELEASE_PACKAGES_LIST_GENERATOR) + + FORMATTED_RELEASE_PACKAGES="" + for PACKAGE in $RELEASE_PACKAGES; do + FORMATTED_RELEASE_PACKAGES+=" - ${PACKAGE}\n" + done + + sed -i "s%buildPackages:%buildPackages:\n${FORMATTED_RELEASE_PACKAGES}%gi" $AZURE_CONFIG_PATH + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Verify buildPackages parameter has been updated with the generated release packages list" + + if grep -Pzo "buildPackages:\n${FORMATTED_RELEASE_PACKAGES}" $AZURE_CONFIG_PATH + then + echo "List of packages to release has successfully been added to the buildPackages parameter" + else + echo "Error. List of packages to release has NOT been added to the buildPackages parameter" + exit 1 + fi + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Configure sonar" + + sed -i "s%sonar.branch.name=%sonar.branch.name=${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}%gi" $SONAR_PATH + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Verify sonar.branch.name has been updated with the synched branch name" + + if grep -Fq "sonar.branch.name=${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }}" $SONAR_PATH + then + echo "Branch name has successfully been added to the sonar.branch.name" + else + echo "Error. Branch name has NOT been added to the sonar.branch.name" + exit 1 + fi + + echo "---------------------------------------------------------------------------------------------------------------------------" + echo "Include files to synced branch" + + git add . + git commit -m "include remaining pipeline files" + git push origin ${{ github.event.inputs.branch_to_sync || env.DEFAULT_BRANCH_TO_SYNC }} diff --git a/.github/workflows/undeploy-hs.yml b/.github/workflows/undeploy-hs.yml deleted file mode 100644 index 2e6135f834f..00000000000 --- a/.github/workflows/undeploy-hs.yml +++ /dev/null @@ -1,22 +0,0 @@ -on: - pull_request: - types: [closed] -name: Hosting service - Undeploy -jobs: - hostingServiceUndeploy: - name: Undeployment bot - environment: dev - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Deployment bot - uses: ./.github/hs-deploy-action - env: - UPP_ACTION: 'undeploy' - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GHT_USER: ${{ secrets.GHT_USER}} - GHT_TOKEN: ${{ secrets.GHT_TOKEN}} - UPP_TENANT: ${{ secrets.UPP_TENANT}} - UPP_CLIENT: ${{ secrets.UPP_CLIENT}} - UPP_SECRET: ${{ secrets.UPP_SECRET}} - \ No newline at end of file diff --git a/.github/workflows/update-ccv2.yml b/.github/workflows/update-ccv2.yml new file mode 100644 index 00000000000..99f3195876f --- /dev/null +++ b/.github/workflows/update-ccv2.yml @@ -0,0 +1,49 @@ +on: + workflow_dispatch: + inputs: + ccv2_branch: + description: Branch on ccv2 to update + default: spa_p4 + required: true + source_branch_to_deploy: + description: source branch to deploy on ccv2 (repo default branch if left empty) + +name: Update ccv2 repo with unreleased Spartacus + +env: + DEFAULT_BRANCH_TO_DEPLOY: ${{ github.event.repository.default_branch }} + +jobs: + deploy_to_ccv2: + name: Updating storefront of a ccv2 environment + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.source_branch_to_deploy || env.DEFAULT_BRANCH_TO_DEPLOY }} + fetch-depth: 0 + - name: Get commit hash of the branch to deploy + id: branch-to-deploy-commit-id + run: | + echo "::set-output name=commit_hash::$(git rev-parse ${{ github.event.inputs.source_branch_to_deploy || env.DEFAULT_BRANCH_TO_DEPLOY }})" + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + key: nodemodules-${{ steps.branch-to-deploy-commit-id.outputs.commit_hash }} + restore-keys: nodemodules-${{ steps.branch-to-deploy-commit-id.outputs.commit_hash }} + - name: Package installation + run: npm ci + - name: Update ccv2 environment with source + env: + CCV2_BRANCH: ${{ github.event.inputs.ccv2_branch }} + SOURCE_BRANCH_TO_DEPLOY: ${{ github.event.inputs.source_branch_to_deploy }} + GHT_USER: ${{ secrets.GHT_USER }} + GHT_PRIVATE_REPO_TOKEN: ${{ secrets.GHT_PRIVATE_REPO_TOKEN }} + GHT_REPO: ${{ secrets.GHT_CCV2_REPO }} + GHT_CCV2_EMAIL: ${{ secrets.GHT_CCV2_EMAIL }} + GHT_CCV2_USERNAME: ${{ secrets.GHT_CCV2_USERNAME }} + run: | + ci-scripts/update-ccv2.sh diff --git a/.github/workflows/update-cloud-repo.yml b/.github/workflows/update-cloud-repo.yml new file mode 100644 index 00000000000..f90f139e832 --- /dev/null +++ b/.github/workflows/update-cloud-repo.yml @@ -0,0 +1,156 @@ +name: Update Cloud Commerce Repository +on: + workflow_dispatch: + inputs: + version: + description: 'Enter the version to use' + required: true + npm_token: + description: 'Enter your npm token' + required: true + secret: true + +env: + CONFIG_DIR: scripts/install + REPO_URL: https://${{secrets.GH_TEMPORARY_TOKEN}}@github.com/SAP-samples/cloud-commerce-sample-setup.git + NPM_REPO_URL: https://73554900100900004337.dev.npmsrv.base.repositories.cloud.sap/ + OCC_BACKEND_URL: OCC_BACKEND_BASE_URL_VALUE + +jobs: + run_script: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Create storefronts + run: | + function setup_config_dir() { + [ -d "${CONFIG_DIR}" ] + cp "${CONFIG_DIR}/config.default.sh" "${CONFIG_DIR}/config.sh" + } + + function update_config() { + local storefront_type=$1 + local version=${{ github.event.inputs.version }} + local npm_token=${{ github.event.inputs.npm_token }} + + # Update configuration values + if [ "${storefront_type}" == "b2c" ]; then + sed -i 's/^BASE_SITE=.*/BASE_SITE="electronics-spa"/' "${CONFIG_DIR}/config.sh" + sed -i 's/^SSR_APP_NAME=.*/SSR_APP_NAME="spartacusstore"/' "${CONFIG_DIR}/config.sh" + elif [ "${storefront_type}" == "b2b" ]; then + sed -i 's/^BASE_SITE=.*/BASE_SITE="powertools-spa"/' "${CONFIG_DIR}/config.sh" + sed -i 's/^SSR_APP_NAME=.*/SSR_APP_NAME="b2bspastore"/' "${CONFIG_DIR}/config.sh" + sed -i '/ADD_B2B_LIBS/c\ADD_B2B_LIBS=true' "${CONFIG_DIR}/config.sh" + else + echo "Invalid storefront type. BASE_SITE and SSR_APP_NAME not set." + fi + + sed -i 's/^BACKEND_URL=.*/BACKEND_URL="'${OCC_BACKEND_URL}'"/' "${CONFIG_DIR}/config.sh" + sed -i "s/^SPARTACUS_VERSION=.*/SPARTACUS_VERSION='${version}'/" "${CONFIG_DIR}/config.sh" + sed -i "s/^NPM_TOKEN=.*/NPM_TOKEN='${npm_token}'/" "${CONFIG_DIR}/config.sh" + sed -i "s|^NPM_URL=.*|NPM_URL='${NPM_REPO_URL}'|" "${CONFIG_DIR}/config.sh" + } + + # Rename the generated spartacus folders so we can have B2C and B2B folders + function rename_spartacus_folders() { + local version=${{ github.event.inputs.version }} + + if [ "${storefront_type}" == "b2c" ]; then + mv ../spartacus-${version} ../spartacus-${version}-b2c + if [ -d "../spartacus-${version}-b2c" ]; then + echo "Renaming to ../spartacus-${version}-b2c was successful" + else + echo "Renaming to ../spartacus-${version}-b2c failed" + exit 1 + fi + elif [ "${storefront_type}" == "b2b" ]; then + mv ../spartacus-${version} ../spartacus-${version}-b2b + if [ -d "../spartacus-${version}-b2b" ]; then + echo "Renaming to ../spartacus-${version}-b2b was successful" + else + echo "Renaming to ../spartacus-${version}-b2b failed" + exit 1 + fi + fi + + } + + # Comment out the base url in the spartacus-configuration.module.ts file + function modify_file_to_remove_baseurl() { + local path + if [ "${storefront_type}" == "b2c" ]; then + path="spartacusstore" + elif [ "${storefront_type}" == "b2b" ]; then + path="b2bspastore" + else + echo "Invalid storefront type: ${storefront_type}" + exit 1 + fi + + local file_location="../spartacus-${{ github.event.inputs.version }}-${storefront_type}/apps/${path}/src/app/spartacus/spartacus-configuration.module.ts" + if [ -f "${file_location}" ]; then + sed -i 's/baseUrl/\/\/baseUrl/1' "${file_location}" + echo "Modified ${file_location}." + else + echo "${file_location} does not exist." + exit 1 + fi + } + + storefront_types=("b2c" "b2b") + + # Loop over storefront types + for storefront_type in "${storefront_types[@]}"; do + setup_config_dir + update_config $storefront_type + cd "${CONFIG_DIR}" && bash "./run.sh" install_npm + cd ../../ + rename_spartacus_folders $storefront_type + modify_file_to_remove_baseurl $storefront_type + done + - name: Set Git user + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + - name: Commit changes + run: | + function clone_and_setup_repo() { + git clone $REPO_URL + cd cloud-commerce-sample-setup + + # Get the current date and time + timestamp=$(date +%Y%m%d%H%M%S) + + # Create a new branch with a unique name + branch_name="update_storefronts_$timestamp" + git checkout -b $branch_name + + # Copy over the B2C and B2B content to the js-storefront folder and then commit the changes + rm -rf js-storefront/spartacusstore/* + cp -r ./../../spartacus-${{ github.event.inputs.version }}-b2c/apps/spartacusstore/* js-storefront/spartacusstore/ + git add . + git commit -m "Updated content of js-storefront/spartacusstore" + + rm -rf js-storefront/b2bspastore/* + cp -r ./../../spartacus-${{ github.event.inputs.version }}-b2b/apps/b2bspastore/* js-storefront/b2bspastore/ + git add . + git commit -m "Updated content of js-storefront/b2bspastore" + + git push -u $REPO_URL + } + + clone_and_setup_repo + + # Echo the branch name so it can be used in later steps + echo "branch_name=$branch_name" >> $GITHUB_ENV + - name: Create Pull Request + run: | + curl -X POST -H "Authorization: token ${{ secrets.GH_TEMPORARY_TOKEN }}" -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/SAP-samples/cloud-commerce-sample-setup/pulls \ + -d '{ + "title": "Update js-storefronts content", + "body": "This PR updates the content of the js-storefronts.", + "head": "'"$branch_name"'", + "base": "main" + }' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3a4632e3f9c..61cbf7418e3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # compiled output /dist /tmp +/temp /out-tsc /documentation @@ -19,7 +20,7 @@ docs.zip # dependencies node_modules/ -package-lock.json +yarn.lock # verdaccio storage scripts/install/storage @@ -37,6 +38,7 @@ scripts/install/config.sh *.launch .settings/ *.sublime-workspace +*.swp # IDE - VSCode .vscode/settings.json @@ -44,6 +46,9 @@ scripts/install/config.sh # Coverage reports **/coverage +# Unit test reports +**/unit-tests-reports + # misc /.angular/cache .lighthouseci/ @@ -57,6 +62,7 @@ npm-debug.log yarn-error.log testem.log debug.log +prettier.log # System Files .DS_Store @@ -64,3 +70,5 @@ Thumbs.db **/git-ignore +.nx/cache + diff --git a/.lighthouserc.yml b/.lighthouserc.yml index 7ab28af684a..702607092ad 100644 --- a/.lighthouserc.yml +++ b/.lighthouserc.yml @@ -5,7 +5,7 @@ ci: - http://localhost:4000 - http://localhost:4000/electronics-spa/en/USD/Brands/Canon/c/brand_10 - http://localhost:4000/electronics-spa/en/USD/product/832386/ef-300mm-f-2.8l-is-usm - startServerCommand: 'yarn serve:ssr' + startServerCommand: 'npm run serve:ssr' assert: preset: lighthouse:recommended assertions: diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..b7599e66bb7 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +.* +sonar-project.properties +sub-folder/ +.pipeline/ +tmp/** diff --git a/.prettierignore b/.prettierignore index 6287a6a794f..fba421ee909 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,5 @@ feature-libs/smartedit/assets/webApplicationInjector.js **/*.md **/*.yml coverage + +/.nx/cache \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 544138be456..c1a6f667131 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "singleQuote": true + "singleQuote": true, + "trailingComma": "es5" } diff --git a/.reuse/dep5 b/.reuse/dep5 index 793938dc912..f91e6cf59c0 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -28,11 +28,5 @@ Disclaimer: The code in this project may include calls to APIs (“API Calls”) # Spartacus code Files: * -Copyright: 2018-2021 SAP SE or an SAP affiliate company and Spartacus contributors +Copyright: 2018-2023 SAP SE or an SAP affiliate company and Spartacus contributors License: Apache-2.0 - -# Sample paragraph, commented out: -# -# Files: src/* -# Copyright: $YEAR $NAME <$CONTACT> -# License: ... diff --git a/.stylelintrc.json b/.stylelintrc.json index 8bf405190b0..6f1604d6a26 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,6 +1,22 @@ { - "plugins": ["stylelint-scss"], + "extends": "stylelint-config-recommended-scss", "rules": { + "block-no-empty": null, + "declaration-block-no-duplicate-properties": null, + "declaration-block-no-shorthand-property-overrides": null, + "font-family-no-missing-generic-family-keyword": null, + "no-invalid-position-at-import-rule": null, + "no-descending-specificity": null, + "no-duplicate-selectors": null, + "selector-class-pattern": null, + "scss/at-if-no-null": null, + "scss/at-import-partial-extension": null, + "scss/at-extend-no-missing-placeholder": null, + "scss/comment-no-empty": null, + "scss/load-no-partial-leading-underscore": null, + "scss/no-global-function-names": null, + "scss/operator-no-unspaced": null, + "scss/operator-no-newline-after": null, "property-disallowed-list": [ "margin-left", "margin-right", diff --git a/.vscode/README.md b/.vscode/README.md index ceaa4960a80..7cfb49b0934 100644 --- a/.vscode/README.md +++ b/.vscode/README.md @@ -18,7 +18,7 @@ The debug configurations are defined in `launch.json`. In order to debug in VSCode, do the following: -- `yarn start` +- `npm run start` - Open the "Debug view" by selecting the Debug icon in the Activity Bar on the side. - Choose a configuration from the dropdown and click on the green "Start Debugging" button. diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4e770500ea1..c8693295eac 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,6 +7,8 @@ // The ng lint command uses ESLint under the hood. "dbaeumer.vscode-eslint", // Modern CSS/SCSS/Less linter - "stylelint.vscode-stylelint" + "stylelint.vscode-stylelint", + // Nx console - GUI + "nrwl.angular-console" ] } diff --git a/.vscode/recommended-settings.json b/.vscode/recommended-settings.json index a623dee8f34..a4b0819b5e5 100644 --- a/.vscode/recommended-settings.json +++ b/.vscode/recommended-settings.json @@ -3,5 +3,14 @@ "editor.codeActionsOnSave": { "source.organizeImports": true }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d7dfd51f31d..d301c75523e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,6 @@ Note: This is a living document. Like the Spartacus code, this document will be An easy way to start is by helping others who may have questions or need support. Look for such requests here: -* Spartacus [Slack workspace](https://join.slack.com/t/spartacus-storefront/shared_invite/zt-jekftqo0-HP6xt6IF~ffVB2cGG66fcQ) * [Stack Overflow posts tagged with 'spartacus'](https://stackoverflow.com/questions/tagged/spartacus) ---- @@ -171,6 +170,10 @@ Submit the form to us through one of the following methods: The "guidelines and standards" requirement could fill entire books and still lack a 100% clear definition, but rest assured that you will receive feedback if something is not right. That being said, please consult the [Contributor's Guide](https://sap.github.io/spartacus-docs/contributors-guide/). +### Contribution of generated AI content + +To contribute generated AI content, please read the following [guide](https://github.com/SAP/.github/blob/main/CONTRIBUTING_USING_GENAI.md) + ### Contribution Process 1. Make sure the change would be welcome, as described above. diff --git a/LICENSE.txt b/LICENSE.txt index 137069b8238..65c0fbb9d10 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt index 137069b8238..65c0fbb9d10 100644 --- a/LICENSES/Apache-2.0.txt +++ b/LICENSES/Apache-2.0.txt @@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +Copyright (c) 2018 SAP SE or an SAP affiliate company. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 7381c61d9fc..2d6c8e68657 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,29 @@ [![REUSE status](https://api.reuse.software/badge/github.com/SAP/spartacus)](https://api.reuse.software/info/github.com/SAP/spartacus) +## Spartacus and Composable Storefront + +Hello and welcome! + +This README is part of the `develop` branch, which is an active development branch in the Spartacus repository. As such, the following text may contain references to work that is in progress and not yet officially released. + +Starting with version 2211.19, composable storefront has aligned its versioning with SAP Commerce Cloud. The previous release of composable storefront was version 6.8. For more information, see [Changes to Release Numbering and Update Policies for Composable Storefront Starting in February 2024](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/6c7b98dbe68f4a508cac17a207182f4c/5fea969613a341308e2519c5f2827331.html?locale=en-US&version=2211). + +Starting with version 5.0, “SAP Commerce Cloud, composable storefront” is the name for the official release of project “Spartacus” libraries published by SAP. The officially supported composable storefront is available to SAP Commerce Cloud customers. Documentation is available on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT?locale=en-US). + +Composable storefront is based off the Spartacus open source code, and is included in the SAP Commerce Cloud license at no extra cost. Composable storefront has a roll-forward update policy. + +On-premise customers may still use Spartacus open source. For more information, see [Self-Publishing Spartacus Libraries Using the Open Source Code](docs/self-publishing-spartacus-libraries.md). + ## What is Spartacus? Spartacus is a lean, Angular-based JavaScript storefront for SAP Commerce Cloud. Spartacus talks to SAP Commerce Cloud exclusively through the Commerce REST API. -- Documentation is hosted on our dedicated [Spartacus Documentation site](https://sap.github.io/spartacus-docs/). -- Try out a [sample Spartacus storefront](https://spartacus-demo.eastus.cloudapp.azure.com/electronics-spa/en/USD/) on our public demo site. +- Documentation is available on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT?locale=en-US). +- Try out a [sample Spartacus storefront](https://composable-storefront-demo.eastus.cloudapp.azure.com/electronics-spa/en/USD/ ) on our public demo site. - Technical questions? Get in touch with us on [Stack Overflow](https://stackoverflow.com/questions/tagged/spartacus-storefront). - Non-technical questions? Join our [Slack workspace](https://join.slack.com/t/spartacus-storefront/shared_invite/zt-jekftqo0-HP6xt6IF~ffVB2cGG66fcQ). -- For details on the 4.0 launch, see the [Release Information page](https://sap.github.io/spartacus-docs/release-information/) on our Spartacus documentation site. +- For details on the 4.0 launch, see the [Release Information page](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/6c7b98dbe68f4a508cac17a207182f4c/5ebbd2c8478a4874863a6b217c0aa2ba.html) on the SAP Help Portal. Spartacus is... @@ -33,15 +47,23 @@ Spartacus provides core storefront features such as: - Checkout - Order history -See the [Release documentation](https://sap.github.io/spartacus-docs/release-information/) for more information. +See the [Release documentation](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/6c7b98dbe68f4a508cac17a207182f4c/5ebbd2c8478a4874863a6b217c0aa2ba.html) on the SAP Help Portal for more information. ## Requirements +If you are working with Spartacus 2211, see the 2211 Angular development environment requirements on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/cfcf687ce2544bba9799aa6c8314ecd0/bf31098d779f4bdebb7a2d0591917363.html?locale=en-US&version=2211). + +If you are working with Spartacus 6.x, see the relevant 6.x Angular development environment requirements on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/cfcf687ce2544bba9799aa6c8314ecd0/bf31098d779f4bdebb7a2d0591917363.html?locale=en-US&version=6.0). + +If you are working with Spartacus 5.x, see the relevant 5.x Angular development environment requirements on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/cfcf687ce2544bba9799aa6c8314ecd0/bf31098d779f4bdebb7a2d0591917363.html?locale=en-US&version=5.0). + +For the back end requirements, see the [Composable Storefront Compatibility Matrix](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/6c7b98dbe68f4a508cac17a207182f4c/ba62c2c708ec4751a2612a512177419b.html?locale=en-US#loioab06abcf72954a1297910ca47b766c43). + If you are working with Spartacus 4.x, your Angular development environment should include the following: - [Angular CLI](https://angular.io/): **12.0** or later, < 13. -- node.js: 12 (12.16.1 or later) or 14.x. - yarn: v1.15 or later +- node.js: Version **14.15** is required. Version 12.x reached end-of-life on April 30, 2022, and is no longer supported by Spartacus. It is strongly recommended that you migrate any existing Spartacus storefronts to Node.js 14 as soon as possible. If there are any issues with Spartacus and Node.js 14, please upgrade to the latest releases. If you continue to experience issues with Node.js 14, create a support ticket with SAP. Spartacus also supports version 16.x of Node.js, but this version is not yet supported in Commerce Cloud in the Public Cloud builder. If you are working with Spartacus 3.x, your Angular development environment should include the following: @@ -63,7 +85,7 @@ If you are working with Spartacus 1.x, your Angular development environment shou For the back end, SAP Commerce Cloud version 1905 or higher is required, and SAP Commerce Cloud version 2005 or newer is recommended. -**Note:** Some Spartacus features require API endpoints that are only available in newer versions of SAP Commerce Cloud. For more information, see [Feature Compatibility](https://sap.github.io/spartacus-docs/feature-release-versions/) in the Spartacus documentation. +**Note:** Some Spartacus features require API endpoints that are only available in newer versions of SAP Commerce Cloud. For more information, see [Compatibility Matrix](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/6c7b98dbe68f4a508cac17a207182f4c/7f4265e58242466aba453632401cdff4.html) on the SAP Help Portal. ## Download and Installation @@ -71,9 +93,9 @@ To get up and running with Spartacus, the recommended approach is to build your Spartacus currently can only be used with a SAP Commerce Cloud instance through Commerce APIs. -To quickly add Spartacus libraries to an Angular application, you can use Spartacus schematics: `ng add @spartacus/schematics`. This will setup and install Spartacus libraries to your Angular project. Please check the [official Spartacus schematics documentation](https://sap.github.io/spartacus-docs/schematics/) for all the prerequisites and instruction on how to use Spartacus schematics. +To quickly add Spartacus libraries to an Angular application, you can use Spartacus schematics: `ng add @spartacus/schematics`. This will setup and install Spartacus libraries to your Angular project. Please check the [official Spartacus schematics documentation](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/cfcf687ce2544bba9799aa6c8314ecd0/e38d45609de04412920a7fc9c13d41e3.html) for all the prerequisites and instruction on how to use Spartacus schematics. -For complete setup instructions, see [Building the Spartacus Storefront from Libraries](https://sap.github.io/spartacus-docs/building-the-spartacus-storefront-from-libraries/). +For complete setup instructions, see [Setting Up the Composable Storefront](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/cfcf687ce2544bba9799aa6c8314ecd0/ea187052be724bbf8796d1ba86131781.html) on the SAP Help Portal. ## Customizing and Extending Spartacus @@ -87,6 +109,12 @@ The documentation for customizing and extending Spartacus is still under develop ## Spartacus application documentation +### Version 5.x and newer + +See [Composable Storefront API](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/eaef8c61b6d9477daf75bff9ac1b7eb4/0016ac17fb464e77a10b5a1a36cda36a.html?locale=en-US). + +### Version 4.x and older + The latest generated documentation for Spartacus application libraries (modules, classes, interfaces, and so on) is hosted here: [https://sap.github.io/spartacus/](https://sap.github.io/spartacus/) The application documentation is versioned and it is included on the **Assets** section of every release of each and every Spartacus library. You can download the documentation for a particular version by accessing the **Assets** section of any Spartacus library from that particular release, and then clicking on `docs.tar.gz` or `docs.zip`. To find the **Assets** folder for a particular library, access the [Released Libraries for Spartacus](https://github.com/SAP/spartacus/releases), click on the link for the library you are interested in, and scroll to the bottom of the page. @@ -111,14 +139,12 @@ Spartacus is provided "as-is" with no official lines of support. To get help from the Spartacus community: -- For more general questions, post a question in the Help chat of our [Slack workspace](https://join.slack.com/t/spartacus-storefront/shared_invite/zt-jekftqo0-HP6xt6IF~ffVB2cGG66fcQ). - For developer questions, post a question to [Stack Overflow with the 'spartacus' tag](https://stackoverflow.com/questions/tagged/spartacus). ## Contributing Team Spartacus welcomes feedback, ideas, requests, and especially code contributions. -- Post comments to our Feedback chat in our [Slack](https://join.slack.com/t/spartacus-storefront/shared_invite/zt-jekftqo0-HP6xt6IF~ffVB2cGG66fcQ) channel. - Read the [Contributing document](CONTRIBUTING.md) and learn how to: - Help others - Report an issue @@ -130,5 +156,6 @@ Many improvements are coming! All tasks will be posted to our GitHub issue track ## License -Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. +Copyright (c) 2024 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the [LICENSE](LICENSE) file. + diff --git a/angular.json b/angular.json deleted file mode 100644 index 2645f3d0f03..00000000000 --- a/angular.json +++ /dev/null @@ -1,1022 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "feature-libs", - "projects": { - "storefrontapp": { - "root": "projects/storefrontapp/", - "sourceRoot": "projects/storefrontapp/src", - "projectType": "application", - "prefix": "cx", - "schematics": {}, - "architect": { - "build": { - "builder": "@angular-builders/custom-webpack:browser", - "options": { - "customWebpackConfig": { - "path": "./extra-webpack.config.js" - }, - "aot": true, - "outputPath": "dist/storefrontapp", - "index": "projects/storefrontapp/src/index.html", - "main": "projects/storefrontapp/src/main.ts", - "polyfills": "projects/storefrontapp/src/polyfills.ts", - "tsConfig": "projects/storefrontapp/tsconfig.app.json", - "assets": [ - "projects/storefrontapp/src/favicon.ico", - "projects/storefrontapp/src/assets", - "projects/storefrontapp/src/manifest.json", - { - "glob": "**/*", - "input": "feature-libs/smartedit/assets", - "output": "assets/" - } - ], - "styles": [ - { - "input": "projects/storefrontapp/src/styles.scss", - "bundleName": "styles" - }, - { - "input": "projects/storefrontapp/src/styles/lib-asm.scss", - "bundleName": "asm" - }, - { - "input": "projects/storefrontapp/src/styles/lib-organization.scss", - "bundleName": "organization" - }, - { - "input": "projects/storefrontapp/src/styles/lib-product-configurator.scss", - "bundleName": "product-configurator" - }, - { - "input": "projects/storefrontapp/src/styles/lib-storefinder.scss", - "bundleName": "storefinder" - }, - { - "input": "projects/storefrontapp/src/styles/lib-product.scss", - "bundleName": "product" - }, - { - "input": "projects/storefrontapp/src/styles/lib-qualtrics.scss", - "bundleName": "qualtrics" - }, - { - "input": "projects/storefrontapp/src/styles/lib-cart.scss", - "bundleName": "cart" - }, - { - "input": "projects/storefrontapp/src/styles/lib-user.scss", - "bundleName": "user" - }, - { - "input": "projects/storefrontapp/src/styles/lib-checkout.scss", - "bundleName": "checkout" - }, - { - "input": "projects/storefrontapp/src/styles/lib-order.scss", - "bundleName": "order" - }, - { - "input": "projects/storefrontapp/src/styles/lib-epd-visualization.scss", - "bundleName": "epd-visualization" - } - ], - "ngswConfigPath": "projects/storefrontlib/cms-structure/pwa/ngsw-config.json", - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "anyComponentStyle", - "maximumWarning": "6kb" - } - ], - "fileReplacements": [ - { - "replace": "projects/storefrontapp/src/environments/environment.ts", - "with": "projects/storefrontapp/src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "serviceWorker": false, - "tsConfig": "projects/storefrontapp/tsconfig.app.prod.json" - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - } - }, - "serve": { - "builder": "@angular-builders/custom-webpack:dev-server", - "options": { - "browserTarget": "storefrontapp:build" - }, - "configurations": { - "production": { - "browserTarget": "storefrontapp:build:production" - }, - "development": { - "browserTarget": "storefrontapp:build:development" - } - }, - "defaultConfiguration": "development" - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/storefrontapp/src/test.ts", - "polyfills": "projects/storefrontapp/src/polyfills.ts", - "tsConfig": "projects/storefrontapp/tsconfig.spec.json", - "karmaConfig": "projects/storefrontapp/karma.conf.js", - "styles": ["projects/storefrontapp/src/styles.scss"], - "scripts": [], - "assets": [ - "projects/storefrontapp/src/favicon.ico", - "projects/storefrontapp/src/assets", - "projects/storefrontapp/src/manifest.json", - { - "glob": "**/*", - "input": "feature-libs/smartedit/assets", - "output": "assets/" - } - ], - "codeCoverageExclude": ["dist/**/*"] - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "projects/storefrontapp/**/*.ts", - "projects/storefrontapp/**/*.html" - ] - } - }, - "server": { - "builder": "@angular-builders/custom-webpack:server", - "options": { - "customWebpackConfig": { - "path": "./extra-webpack.config.js" - }, - "outputPath": "dist/storefrontapp-server", - "main": "projects/storefrontapp/server.ts", - "tsConfig": "projects/storefrontapp/tsconfig.server.json" - }, - "configurations": { - "production": { - "outputHashing": "media", - "fileReplacements": [ - { - "replace": "projects/storefrontapp/src/environments/environment.ts", - "with": "projects/storefrontapp/src/environments/environment.prod.ts" - } - ], - "sourceMap": false, - "optimization": true, - "tsConfig": "projects/storefrontapp/tsconfig.server.prod.json" - } - } - }, - "serve-ssr": { - "builder": "@nguniversal/builders:ssr-dev-server", - "options": { - "browserTarget": "storefrontapp:build", - "serverTarget": "storefrontapp:server" - }, - "configurations": { - "production": { - "browserTarget": "storefrontapp:build:production", - "serverTarget": "storefrontapp:server:production" - } - } - }, - "prerender": { - "builder": "@nguniversal/builders:prerender", - "options": { - "browserTarget": "storefrontapp:build:production", - "serverTarget": "storefrontapp:server:production", - "routes": ["/"] - }, - "configurations": { - "production": {} - } - } - } - }, - "storefrontlib": { - "root": "projects/storefrontlib", - "sourceRoot": "projects/storefrontlib", - "projectType": "library", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "projects/storefrontlib/tsconfig.lib.json", - "project": "projects/storefrontlib/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "projects/storefrontlib/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/storefrontlib/test.ts", - "tsConfig": "projects/storefrontlib/tsconfig.spec.json", - "karmaConfig": "projects/storefrontlib/karma.conf.js", - "codeCoverageExclude": ["dist/**/*"], - "stylePreprocessorOptions": { - "includePaths": ["./projects/storefrontstyles/scss"] - } - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "projects/storefrontlib/**/*.ts", - "projects/storefrontlib/**/*.html" - ] - } - } - } - }, - "core": { - "root": "projects/core", - "sourceRoot": "projects/core/src", - "projectType": "library", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "projects/core/tsconfig.lib.json", - "project": "projects/core/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "projects/core/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/core/src/test.ts", - "tsConfig": "projects/core/tsconfig.spec.json", - "karmaConfig": "projects/core/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "projects/core/**/*.ts", - "projects/core/**/*.html" - ] - } - } - } - }, - "assets": { - "root": "projects/assets", - "sourceRoot": "projects/assets/src", - "projectType": "library", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "projects/assets/tsconfig.lib.json", - "project": "projects/assets/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "projects/assets/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/assets/src/test.ts", - "tsConfig": "projects/assets/tsconfig.spec.json", - "karmaConfig": "projects/assets/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "projects/assets/**/*.ts", - "projects/assets/**/*.html" - ] - } - } - } - }, - "schematics": { - "root": "projects/schematics", - "sourceRoot": "projects/schematics/src", - "projectType": "library", - "architect": { - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": ["projects/schematics/**/*.ts"] - } - } - } - }, - "cds": { - "projectType": "library", - "root": "integration-libs/cds", - "sourceRoot": "integration-libs/cds/src", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "integration-libs/cds/tsconfig.lib.json", - "project": "integration-libs/cds/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "integration-libs/cds/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "integration-libs/cds/src/test.ts", - "tsConfig": "integration-libs/cds/tsconfig.spec.json", - "karmaConfig": "integration-libs/cds/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "integration-libs/cds/**/*.ts", - "integration-libs/cds/**/*.html" - ] - } - } - } - }, - "organization": { - "projectType": "library", - "root": "feature-libs/organization", - "sourceRoot": "feature-libs/organization", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/organization/tsconfig.lib.json", - "project": "feature-libs/organization/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/organization/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/organization/test.ts", - "tsConfig": "feature-libs/organization/tsconfig.spec.json", - "karmaConfig": "feature-libs/organization/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/organization/**/*.ts", - "feature-libs/organization/**/*.html" - ] - } - } - } - }, - "product": { - "projectType": "library", - "root": "feature-libs/product", - "sourceRoot": "feature-libs/product", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/product/tsconfig.lib.json", - "project": "feature-libs/product/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/product/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/product/test.ts", - "tsConfig": "feature-libs/product/tsconfig.spec.json", - "karmaConfig": "feature-libs/product/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/product/**/*.ts", - "feature-libs/product/**/*.html" - ] - } - } - } - }, - "product-configurator": { - "projectType": "library", - "root": "feature-libs/product-configurator", - "sourceRoot": "feature-libs/product-configurator", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/product-configurator/tsconfig.lib.json", - "project": "feature-libs/product-configurator/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/product-configurator/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/product-configurator/test.ts", - "tsConfig": "feature-libs/product-configurator/tsconfig.spec.json", - "karmaConfig": "feature-libs/product-configurator/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/product-configurator/**/*.ts", - "feature-libs/product-configurator/**/*.html" - ] - } - } - } - }, - "cdc": { - "projectType": "library", - "root": "integration-libs/cdc", - "sourceRoot": "integration-libs/cdc", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "integration-libs/cdc/tsconfig.lib.json", - "project": "integration-libs/cdc/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "integration-libs/cdc/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "integration-libs/cdc/test.ts", - "tsConfig": "integration-libs/cdc/tsconfig.spec.json", - "karmaConfig": "integration-libs/cdc/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "integration-libs/cdc/**/*.ts", - "integration-libs/cdc/**/*.html" - ] - } - } - } - }, - "digital-payments": { - "projectType": "library", - "root": "integration-libs/digital-payments", - "sourceRoot": "integration-libs/digital-payments/src", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "integration-libs/digital-payments/tsconfig.lib.json", - "project": "integration-libs/digital-payments/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "integration-libs/digital-payments/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "integration-libs/digital-payments/src/test.ts", - "tsConfig": "integration-libs/digital-payments/tsconfig.spec.json", - "karmaConfig": "integration-libs/digital-payments/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "integration-libs/digital-payments/**/*.ts", - "integration-libs/digital-payments/**/*.html" - ] - } - } - } - }, - "epd-visualization": { - "projectType": "library", - "root": "integration-libs/epd-visualization", - "sourceRoot": "integration-libs/epd-visualization", - "prefix": "cx-epd-visualization", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "integration-libs/epd-visualization/tsconfig.lib.json", - "project": "integration-libs/epd-visualization/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "integration-libs/epd-visualization/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "integration-libs/epd-visualization/test.ts", - "tsConfig": "integration-libs/epd-visualization/tsconfig.spec.json", - "karmaConfig": "integration-libs/epd-visualization/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "integration-libs/epd-visualization/**/*.ts", - "integration-libs/epd-visualization/**/*.html" - ] - } - } - } - }, - "setup": { - "projectType": "library", - "root": "core-libs/setup", - "sourceRoot": "core-libs/setup", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "core-libs/setup/tsconfig.lib.json", - "project": "core-libs/setup/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "core-libs/setup/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "core-libs/setup/test.ts", - "tsConfig": "core-libs/setup/tsconfig.spec.json", - "karmaConfig": "core-libs/setup/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "core-libs/setup/**/*.ts", - "core-libs/setup/**/*.html" - ] - } - } - } - }, - "storefinder": { - "projectType": "library", - "root": "feature-libs/storefinder", - "sourceRoot": "feature-libs/storefinder", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/storefinder/tsconfig.lib.json", - "project": "feature-libs/storefinder/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/storefinder/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/storefinder/test.ts", - "tsConfig": "feature-libs/storefinder/tsconfig.spec.json", - "karmaConfig": "feature-libs/storefinder/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/storefinder/**/*.ts", - "feature-libs/storefinder/**/*.html" - ] - } - } - } - }, - "qualtrics": { - "projectType": "library", - "root": "feature-libs/qualtrics", - "sourceRoot": "feature-libs/qualtrics", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/qualtrics/tsconfig.lib.json", - "project": "feature-libs/qualtrics/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/qualtrics/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/qualtrics/test.ts", - "tsConfig": "feature-libs/qualtrics/tsconfig.spec.json", - "karmaConfig": "feature-libs/qualtrics/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/qualtrics/**/*.ts", - "feature-libs/qualtrics/**/*.html" - ] - } - } - } - }, - "tracking": { - "projectType": "library", - "root": "feature-libs/tracking", - "sourceRoot": "feature-libs/tracking", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/tracking/tsconfig.lib.json", - "project": "feature-libs/tracking/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/tracking/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/tracking/test.ts", - "tsConfig": "feature-libs/tracking/tsconfig.spec.json", - "karmaConfig": "feature-libs/tracking/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/tracking/**/*.ts", - "feature-libs/tracking/**/*.html" - ] - } - } - } - }, - "smartedit": { - "projectType": "library", - "root": "feature-libs/smartedit", - "sourceRoot": "feature-libs/smartedit", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/smartedit/tsconfig.lib.json", - "project": "feature-libs/smartedit/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/smartedit/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/smartedit/test.ts", - "tsConfig": "feature-libs/smartedit/tsconfig.spec.json", - "karmaConfig": "feature-libs/smartedit/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/smartedit/**/*.ts", - "feature-libs/smartedit/**/*.html" - ] - } - } - } - }, - "asm": { - "projectType": "library", - "root": "feature-libs/asm", - "sourceRoot": "feature-libs/asm", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/asm/tsconfig.lib.json", - "project": "feature-libs/asm/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/asm/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/asm/test.ts", - "tsConfig": "feature-libs/asm/tsconfig.spec.json", - "karmaConfig": "feature-libs/asm/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/asm/**/*.ts", - "feature-libs/asm/**/*.html" - ] - } - } - } - }, - "checkout": { - "projectType": "library", - "root": "feature-libs/checkout", - "sourceRoot": "feature-libs/checkout", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/checkout/tsconfig.lib.json", - "project": "feature-libs/checkout/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/checkout/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/checkout/test.ts", - "tsConfig": "feature-libs/checkout/tsconfig.spec.json", - "karmaConfig": "feature-libs/checkout/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/checkout/**/*.ts", - "feature-libs/checkout/**/*.html" - ] - } - } - } - }, - "cart": { - "projectType": "library", - "root": "feature-libs/cart", - "sourceRoot": "feature-libs/cart", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/cart/tsconfig.lib.json", - "project": "feature-libs/cart/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/cart/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/cart/test.ts", - "tsConfig": "feature-libs/cart/tsconfig.spec.json", - "karmaConfig": "feature-libs/cart/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/cart/**/*.ts", - "feature-libs/cart/**/*.html" - ] - } - } - } - }, - "user": { - "projectType": "library", - "root": "feature-libs/user", - "sourceRoot": "feature-libs/user", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/user/tsconfig.lib.json", - "project": "feature-libs/user/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/user/tsconfig.lib.prod.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/user/test.ts", - "tsConfig": "feature-libs/user/tsconfig.spec.json", - "karmaConfig": "feature-libs/user/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/user/**/*.ts", - "feature-libs/user/**/*.html" - ] - } - } - } - }, - "order": { - "projectType": "library", - "root": "feature-libs/order", - "sourceRoot": "feature-libs/order", - "prefix": "cx", - "architect": { - "build": { - "builder": "./tools/build-lib:augmented-types", - "options": { - "tsConfig": "feature-libs/order/tsconfig.lib.json", - "project": "feature-libs/order/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/order/tsconfig.lib.prod.json" - }, - "development": { - "tsConfig": "feature-libs/order/tsconfig.lib.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/order/test.ts", - "tsConfig": "feature-libs/order/tsconfig.spec.json", - "karmaConfig": "feature-libs/order/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/order/**/*.ts", - "feature-libs/order/**/*.html" - ] - } - } - } - }, - "punchout": { - "projectType": "library", - "root": "feature-libs/punchout", - "sourceRoot": "feature-libs/punchout", - "prefix": "cx", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:ng-packagr", - "options": { - "tsConfig": "feature-libs/punchout/tsconfig.lib.json", - "project": "feature-libs/punchout/ng-package.json" - }, - "configurations": { - "production": { - "tsConfig": "feature-libs/punchout/tsconfig.lib.prod.json" - }, - "development": { - "tsConfig": "feature-libs/punchout/tsconfig.lib.json" - } - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "feature-libs/punchout/test.ts", - "tsConfig": "feature-libs/punchout/tsconfig.spec.json", - "karmaConfig": "feature-libs/punchout/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "feature-libs/punchout/**/*.ts", - "feature-libs/punchout/**/*.html" - ] - } - } - } - } - }, - "defaultProject": "storefrontapp" -} diff --git a/ci-scripts/e2e-cypress.sh b/ci-scripts/e2e-cypress.sh index 5ee0db8f634..4b8bf57faa4 100755 --- a/ci-scripts/e2e-cypress.sh +++ b/ci-scripts/e2e-cypress.sh @@ -57,11 +57,13 @@ fi echo '-----' echo "Building Spartacus libraries" -yarn --frozen-lockfile +export NODE_OPTIONS=--dns-result-order=ipv4first -(cd projects/storefrontapp-e2e-cypress && yarn --frozen-lockfile) +npm ci -yarn build:libs 2>&1 | tee build.log +(cd projects/storefrontapp-e2e-cypress && npm ci) + +npm run build:libs 2>&1 | tee build.log results=$(grep "Warning: Can't resolve all parameters for" build.log || true) if [[ -z "${results}" ]]; then @@ -74,28 +76,40 @@ else fi echo '-----' echo "Building Spartacus storefrontapp" -yarn build +npm run build if [[ "${SSR}" = true ]]; then echo "Building Spartacus storefrontapp (SSR PROD mode)" - yarn build:ssr:ci + npm run build:ssr:ci echo "Starting Spartacus storefrontapp in SSR mode" - (yarn serve:ssr:ci &) + (npm run serve:ssr:ci &) echo '-----' echo "Running SSR Cypress smoke test" - yarn e2e:run:ci:ssr + if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then + if [[ "${GITHUB_HEAD_REF}" == epic/* ]]; then + npm run e2e:run:ci:ssr + else + npm run e2e:run:ci:core:ssr + fi + else + npm run e2e:run:ci:ssr"${SUITE}" + fi else - yarn start:pwa & + npm run start:pwa & echo '-----' echo "Running Cypress end to end tests" if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then - yarn e2e:run:ci:core"${SUITE}" + if [[ "${GITHUB_HEAD_REF}" == epic/* ]]; then + npm run e2e:run:ci"${SUITE}" + else + npm run e2e:run:ci:core"${SUITE}" + fi else - yarn e2e:run:ci"${SUITE}" + npm run e2e:run:ci"${SUITE}" fi fi diff --git a/ci-scripts/lhci.sh b/ci-scripts/lhci.sh index 178e8118b09..172c3d1d43a 100755 --- a/ci-scripts/lhci.sh +++ b/ci-scripts/lhci.sh @@ -2,13 +2,14 @@ set -e export SPA_ENV='lighthouse' +export NODE_OPTIONS=--dns-result-order=ipv4first npm install -g @lhci/cli@0.8.x echo " --> Building Spartacus libraries" -yarn build:libs -yarn build -yarn build:ssr +npm run build:libs +npm run build +npm run build:ssr echo "--> Running lighthouse score on CI" lhci autorun diff --git a/ci-scripts/prepend-license.sh b/ci-scripts/prepend-license.sh new file mode 100755 index 00000000000..c373c87a3ad --- /dev/null +++ b/ci-scripts/prepend-license.sh @@ -0,0 +1,7 @@ +# Find .ts files that: +# - are not in node_modules +# - do not have 'spec' in their name +# (however words containing a substring 'spec' are allowed, e.g. 'inSPECtor' or 'SPECial') +FILES=`find . -not -path '**/node_modules/**' -not -regex ".*[^a-zA-Z]spec[^a-zA-Z].*" -name '*.ts' -print` + +reuse annotate --copyright="SAP Spartacus team " --license="Apache-2.0" --multi-line $FILES diff --git a/ci-scripts/publish-sample-data.sh b/ci-scripts/publish-sample-data.sh new file mode 100755 index 00000000000..d2e418599bf --- /dev/null +++ b/ci-scripts/publish-sample-data.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +TAG_NAME="sampledata" +SAMPLE_DATA_ASSETS_FOLDER="sample-data-assets" +STOREFRONT_FILE_NAME="spartacussampledata" + + +SAMPLE_DATA_UNRELEASED_BRANCH="release/2211.x" +UNRELEASED_SPARTACUS_VERSION_NAME="$STOREFRONT_FILE_NAME-version-2211-x" + +SAMPLE_DATA_UNRELEASED_BRANCH6="release/6.x" +UNRELEASED_SPARTACUS_VERSION_NAME6="$STOREFRONT_FILE_NAME-version-6-x" + +SAMPLE_DATA_CURRENT_BRANCH="release/5.x" +CURRENT_SPARTACUS_VERSION_NAME="$STOREFRONT_FILE_NAME-version-5-x" + +SAMPLE_DATA_PREVIOUS_BRANCH="release/4.x" +PREVIOUS_RELEASE_SPARTACUS_VERSION_NAME="$STOREFRONT_FILE_NAME-version-4-x" + +SAMPLE_DATA_OLD_BRANCH="release/3.x" +OLD_RELEASE_SPARTACUS_VERSION_NAME="$STOREFRONT_FILE_NAME-version-3-x" + + +function download_sample_data_from_spartacussample_repo { + curl -H "Authorization: token $GHT_PRIVATE_REPO_TOKEN" -L "https://github.tools.sap/cx-commerce/spartacussampledata/archive/$1.zip" -o "$2.zip" + curl -H "Authorization: token $GHT_PRIVATE_REPO_TOKEN" -L "https://github.tools.sap/cx-commerce/spartacussampledata/archive/$1.tar.gz" -o "$2.tar.gz" +} + +echo "-----" +echo "Downloading PREVIOUS sample data for 6.x" + +download_sample_data_from_spartacussample_repo $SAMPLE_DATA_UNRELEASED_BRANCH $UNRELEASED_SPARTACUS_VERSION_NAME + +echo "-----" +echo "Downloading PREVIOUS sample data for 6.x" + +download_sample_data_from_spartacussample_repo $SAMPLE_DATA_UNRELEASED_BRANCH6 $UNRELEASED_SPARTACUS_VERSION_NAME6 + +echo "-----" +echo "Downloading PREVIOUS sample data for 5.x" + +download_sample_data_from_spartacussample_repo $SAMPLE_DATA_CURRENT_BRANCH $CURRENT_SPARTACUS_VERSION_NAME + +echo "-----" +echo "Downloading PREVIOUS sample data for 4.x" + +download_sample_data_from_spartacussample_repo $SAMPLE_DATA_PREVIOUS_BRANCH $PREVIOUS_RELEASE_SPARTACUS_VERSION_NAME + +echo "Downloading OLD sample data for 3.x" + +download_sample_data_from_spartacussample_repo $SAMPLE_DATA_OLD_BRANCH $OLD_RELEASE_SPARTACUS_VERSION_NAME + +echo "-----" +echo "Move assets to single folder" +rm -rf $SAMPLE_DATA_ASSETS_FOLDER +mkdir $SAMPLE_DATA_ASSETS_FOLDER && mv $STOREFRONT_FILE_NAME* $SAMPLE_DATA_ASSETS_FOLDER + +echo "-----" +echo "Deleting tag on the remote repository to remove any tied releases" + +git push "https://$GH_TOKEN@github.com/SAP-samples/cloud-commerce-sample-setup.git" :refs/tags/$TAG_NAME + +echo "-----" +echo "Create a release with created tag" + +gh release create $TAG_NAME ./$SAMPLE_DATA_ASSETS_FOLDER/** --repo "https://$GH_TOKEN@github.com/SAP-samples/cloud-commerce-sample-setup.git" --title "Spartacus Sample Data" --notes "Spartacus sample data releases: +2211-x: current release +6-x: previous release +5-x: previous release +4-x: previous release +3-x: old release" \ No newline at end of file diff --git a/ci-scripts/release-packages-list-generator.sh b/ci-scripts/release-packages-list-generator.sh new file mode 100755 index 00000000000..cee21aa8957 --- /dev/null +++ b/ci-scripts/release-packages-list-generator.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# List of files for packages to be released +FILES=$(find core-libs feature-libs integration-libs projects -name package.json -not -path "*node_modules*" -not -path "*projects/eslint*") + +# Return only the names of the packages from the list of files +PACKAGE_NAMES=() +for file in ${FILES[@]}; do + PACKAGE_NAMES+=(`awk '/name/{print $2}' $file | awk -F[\/,\"] '{print $3}'`) +done + +# Format the names of packages to be delimited by a space so the parent script calling this can use them +# e.g "core schematics styles" +FORMATTED_PACKAGE_NAMES=$(printf " %s" "${PACKAGE_NAMES[@]}") +echo "$FORMATTED_PACKAGE_NAMES" diff --git a/ci-scripts/release-packer.sh b/ci-scripts/release-packer.sh new file mode 100755 index 00000000000..17583a51e24 --- /dev/null +++ b/ci-scripts/release-packer.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# The reason for this script is because piper does not support publishing multiple artifacts +# In addition, it only publishes packages that are found on the root of the project +# This script will pack every package and set it to the root level so it can be published properly +set -e +shopt -s extglob dotglob + +# Build all the libraries and generate the dist folders to use when releasing +function build_libs { + npm ci && npm run build:libs +} + +# Configure the project to move everything into a sub-folder to keep root clean for publishing +function configure_project { + mkdir sub-folder + mv !(sub-folder) sub-folder +} + +# Clear root containing the old package so the next package can be published +function clear_root { + rm -rf !(sub-folder) + cp -r sub-folder/.pipeline . + cp sub-folder/.npmignore . +} +# Append root's .nmpignore into module's .nmpignore file so it contains all paths. +function append_npmignore { + $(cd $1 && echo "\n$2" >> .npmignore); +} + +# Package is built and set at the root level +function pack { + PACKAGE=$1 + cd sub-folder + + local CONTENT="$(cat .npmignore)" + + if [[ -z "$PACKAGE" ]]; then + echo "Package cannot be empty" + exit 1 + elif [[ $PACKAGE == 'styles' ]]; then + cp -r projects/storefrontstyles/* ../. + elif [[ $PACKAGE == 'schematics' ]]; then + cp -r projects/schematics/* ../. + elif [[ $PACKAGE == 'storefront' ]]; then + append_npmignore "dist/storefrontlib" "$CONTENT" + cp -r dist/storefrontlib/* ../. + else + append_npmignore "dist/$PACKAGE/" "$CONTENT" + cp -r dist/$PACKAGE/* ../. + fi +} + +if [[ $1 == 'configure' ]]; then + configure_project +elif [[ $1 == 'build' ]]; then + build_libs +else + clear_root + pack "$1" +fi \ No newline at end of file diff --git a/ci-scripts/unit-tests-core-lib.sh b/ci-scripts/unit-tests-core-lib.sh deleted file mode 100755 index e30956c8a4f..00000000000 --- a/ci-scripts/unit-tests-core-lib.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env bash -set -e -set -o pipefail - -echo "-----" - -echo "Running unit tests and code coverage for core" -exec 5>&1 -output=$(ng test core --watch=false --sourceMap --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi - -echo "Running unit tests and code coverage for storefrontlib" -exec 5>&1 -output=$(ng test storefrontlib --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi - -echo "Running unit tests and code coverage for cart library" -exec 5>&1 -output=$(ng test cart --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for cart library" -exec 5>&1 -output=$(yarn --cwd feature-libs/cart run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for order library" -exec 5>&1 -output=$(ng test order --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for order library" -exec 5>&1 -output=$(yarn --cwd feature-libs/order run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for setup" -exec 5>&1 -output=$(ng test setup --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi - -echo "Running unit tests and code coverage for user" -exec 5>&1 -output=$(ng test user --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for user library" -exec 5>&1 -output=$(yarn --cwd feature-libs/user run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for checkout" -exec 5>&1 -output=$(ng test checkout --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for checkout library" -exec 5>&1 -output=$(yarn --cwd feature-libs/checkout run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for product library" -exec 5>&1 -output=$(ng test product --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for product library" -exec 5>&1 -output=$(yarn --cwd feature-libs/product run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for organization library" -exec 5>&1 -output=$(ng test organization --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for organization library" -exec 5>&1 -output=$(yarn --cwd feature-libs/organization run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for smartedit library" -exec 5>&1 -output=$(ng test smartedit --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for smartedit library" -exec 5>&1 -output=$(yarn --cwd feature-libs/smartedit run test:schematics --coverage=true | tee /dev/fd/5) - - -if [[ $1 == '-h' ]]; then - echo "Usage: $0 [sonar (to run sonar scan)]" - exit 1 - elif [[ $1 == 'sonar' ]]; then - - echo "Running SonarCloud scan" - sonar-scanner \ - -Dsonar.projectKey=sap_cloud-commerce-spartacus-storefront \ - -Dsonar.organization=sap \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.login=$SONAR_TOKEN -fi diff --git a/ci-scripts/unit-tests.sh b/ci-scripts/unit-tests.sh index 210a02807e0..5b18df3fd95 100755 --- a/ci-scripts/unit-tests.sh +++ b/ci-scripts/unit-tests.sh @@ -2,139 +2,33 @@ set -e set -o pipefail -echo "-----" - -echo "Running unit tests and code coverage for cds" -exec 5>&1 -output=$(ng test cds --watch=false --sourceMap --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for CDS library" -exec 5>&1 -output=$(yarn --cwd integration-libs/cds run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for product-configurator library" -exec 5>&1 -output=$(ng test product-configurator --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for product-configurator library" -exec 5>&1 -output=$(yarn --cwd feature-libs/product-configurator run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for CDC" -exec 5>&1 -output=$(ng test cdc --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for cdc library" -exec 5>&1 -output=$(yarn --cwd integration-libs/cdc run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for Digital-Payments" -exec 5>&1 -output=$(ng test digital-payments --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for Digital-Payments library" -exec 5>&1 -output=$(yarn --cwd integration-libs/digital-payments run test:schematics --coverage=true | tee /dev/fd/5) +EXCLUDE_APPLICATIONS=storefrontapp,ssr-tests +EXCLUDE_JEST=storefrontstyles,schematics,setup -echo "Running unit tests and code coverage for EPD Visualization" -exec 5>&1 -output=$(ng test epd-visualization --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for EPD Visualization library" -exec 5>&1 -output=$(yarn --cwd integration-libs/epd-visualization run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for storefinder library" -exec 5>&1 -output=$(ng test storefinder --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for storefinder library" -exec 5>&1 -output=$(yarn --cwd feature-libs/storefinder run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for qualtrics library" -exec 5>&1 -output=$(ng test qualtrics --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for qualtrics library" -exec 5>&1 -output=$(yarn --cwd feature-libs/qualtrics run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for asm library" -exec 5>&1 -output=$(ng test asm --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -echo "Running schematics unit tests and code coverage for asm library" -exec 5>&1 -output=$(yarn --cwd feature-libs/asm run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for tracking" -exec 5>&1 -output=$(ng test tracking --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi - -# echo "Running unit tests and code coverage for punchout" -# exec 5>&1 -# output=$(ng test punchout --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -# coverage=$(echo $output | grep -i "does not meet global threshold" || true) -# if [[ -n "$coverage" ]]; then -# echo "Error: Tests did not meet coverage expectations" -# exit 1 -# fi - -echo "Running schematics unit tests and code coverage for tracking library" -exec 5>&1 -output=$(yarn --cwd feature-libs/tracking run test:schematics --coverage=true | tee /dev/fd/5) - -echo "Running unit tests and code coverage for schematics library" -exec 5>&1 -output=$(yarn --cwd projects/schematics run test --runInBand --coverage=true | tee /dev/fd/5) - -if [[ $1 == '-h' ]]; then - echo "Usage: $0 [sonar (to run sonar scan)]" - exit 1 - elif [[ $1 == 'sonar' ]]; then +echo "-----" - echo "Running SonarCloud scan" - sonar-scanner \ - -Dsonar.projectKey=sap_cloud-commerce-spartacus-storefront \ - -Dsonar.organization=sap \ - -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.login=$SONAR_TOKEN +function run_affected_unit_tests { + echo "Running JASMINE unit tests and code coverage for AFFECTED libraries" + npx nx affected --target=test --exclude="$EXCLUDE_APPLICATIONS,$EXCLUDE_JEST" -- --no-watch --source-map --code-coverage --browsers ChromeHeadless + + echo "Running JEST (mostly schematics) unit tests and code coverage for AFFECTED libraries" + npx nx affected --target=test-jest --exclude="$EXCLUDE_APPLICATIONS" -- --coverage --runInBand +} + +function run_all_unit_tests { + echo "Running JASMINE unit tests and code coverage for ALL libraries" + npx nx run-many --all --target=test --exclude="$EXCLUDE_APPLICATIONS,$EXCLUDE_JEST" -- --no-watch --source-map --code-coverage --browsers ChromeHeadless + + echo "Running JEST (mostly schematics) unit tests and code coverage for ALL libraries" + npx nx run-many --all --target=test-jest --exclude="$EXCLUDE_APPLICATIONS" -- --coverage --runInBand +} + +if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then + if [[ "${GITHUB_HEAD_REF}" == epic/* ]]; then + run_all_unit_tests + else + run_affected_unit_tests + fi +else + run_all_unit_tests fi diff --git a/ci-scripts/update-ccv2.sh b/ci-scripts/update-ccv2.sh new file mode 100755 index 00000000000..edbafa0b0f0 --- /dev/null +++ b/ci-scripts/update-ccv2.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash +set -xe + +B2C_STORE="spartacusstore" +B2B_STORE="b2bspastore" +CCV2_B2C_STOREFRONT_PATH="$GHT_REPO/js-storefront/$B2C_STORE" +CCV2_B2B_STOREFRONT_PATH="$GHT_REPO/js-storefront/$B2B_STORE" +GH_BASE_URL="https://$GHT_USER:$GHT_PRIVATE_REPO_TOKEN@github.tools.sap/cx-commerce/$GHT_REPO.git" +APP_MODULE_PATH="projects/storefrontapp/src/app/app.module.ts" +B2C_CONFIG_PATH="projects/storefrontapp/src/app/spartacus/spartacus-b2c-configuration.module.ts" +B2B_CONFIG_PATH="projects/storefrontapp/src/app/spartacus/spartacus-b2b-configuration.module.ts" +SERVER_CONFIG_PATH="projects/storefrontapp/server.ts" + +function verify_branch_exist { + IS_BRANCH=$(git ls-remote --heads "$1" "$2") + + if [ -z "$IS_BRANCH" ]; then + echo "Error. Can't find the branch $2. Verify the branch name exist" + exit 1 + fi +} + +function remove_pwa_config { + sed -i '/pwa:[[:blank:]]*{/,/^[[:space:]]*}/d' "$1" + + cat "$1" + + if grep -Fq "addToHomeScreen: true" "$1" + then + echo "PWA config has NOT been removed" + exit 1 + else + echo "PWA config has SUCCESSFULLY been removed" + fi +} + +function copy_browser_and_server_files { + cp -a dist/storefrontapp/. "$1/dist/$2/browser/" + cp -a dist/storefrontapp-server/. "$1/dist/$2/server/" +} + +echo "------------------------------------------------------------------" +echo "Verify source branch exist" + +verify_branch_exist "origin" "$SOURCE_BRANCH_TO_DEPLOY" + +echo "------------------------------------------------------------------" +echo "Verify CCv2 branch exist" + +verify_branch_exist "$GH_BASE_URL" "$CCV2_BRANCH" + +echo "------------------------------------------------------------------" +echo "Comment out occBaseUrl from configration to allow index.html meta tag to set the occBaseUrl" + +sed -i 's/baseUrl: environment.occBaseUrl/\/\/ baseUrl: environment.occBaseUrl/gi' "$APP_MODULE_PATH" + +cat "$APP_MODULE_PATH" + +if grep -Fq "// baseUrl: environment.occBaseUrl" "$APP_MODULE_PATH" +then + echo "Base url has been successfully commented out from app.module.ts" +else + echo "Base url is not commented out from app.module.ts" + exit 1 +fi + +echo "------------------------------------------------------------------" +echo "Remove pwa config for B2C storefront" + +remove_pwa_config "$B2C_CONFIG_PATH" + +echo "------------------------------------------------------------------" +echo "Remove pwa config for B2B storefront" + +remove_pwa_config "$B2B_CONFIG_PATH" + +echo "------------------------------------------------------------------" +echo "Clone ccv2 repository" + +git clone -b "$CCV2_BRANCH" "$GH_BASE_URL" + +echo "------------------------------------------------------------------" +echo "Update ccv2 repo's js-storefront folder to adhere to the ccv2 dist strucutre" + +rm -rf "$CCV2_B2C_STOREFRONT_PATH" +rm -rf "$CCV2_B2B_STOREFRONT_PATH" + +# b2c +mkdir -p "$CCV2_B2C_STOREFRONT_PATH/dist/$B2C_STORE/browser" +mkdir -p "$CCV2_B2C_STOREFRONT_PATH/dist/$B2C_STORE/server" + +# b2b +mkdir -p "$CCV2_B2B_STOREFRONT_PATH/dist/$B2B_STORE/browser" +mkdir -p "$CCV2_B2B_STOREFRONT_PATH/dist/$B2B_STORE/server" + +echo "------------------------------------------------------------------" +echo "Build Spartacus libraries" +npm run build:libs + +echo "------------------------------------------------------------------" +echo "update server.ts for B2C storefront" + +sed -i "s%dist/storefrontapp%dist/$B2C_STORE/browser%gi" "$SERVER_CONFIG_PATH" + +echo "------------------------------------------------------------------" +echo "Verify server.ts has been updated for B2C dist" + +cat "$SERVER_CONFIG_PATH" + +if grep -Fq "const distFolder = join(process.cwd(), 'dist/$B2C_STORE/browser');" "$SERVER_CONFIG_PATH" +then + echo "Dist folder has been updated" +else + echo "Dist folder has NOT been updated" + exit 1 +fi + +echo "------------------------------------------------------------------" +echo "Build SSR for B2C storefront" + +npm run build:ssr:ci + +echo "------------------------------------------------------------------" +echo "Build CSR for B2C storefront" + +npm run build + +echo "------------------------------------------------------------------" +echo "Copy server and browser files to js-storefront to adhere to the ccv2 dist structure for B2C storefront" + +copy_browser_and_server_files "$CCV2_B2C_STOREFRONT_PATH" "$B2C_STORE" + +echo "------------------------------------------------------------------" +echo "update server.ts for B2B storefront" + +sed -i "s%dist/$B2C_STORE/browser%dist/$B2B_STORE/browser%gi" "$SERVER_CONFIG_PATH" + +echo "------------------------------------------------------------------" +echo "Verify server.ts has been updated for B2B dist" + +cat "$SERVER_CONFIG_PATH" + +if grep -Fq "const distFolder = join(process.cwd(), 'dist/$B2B_STORE/browser');" "$SERVER_CONFIG_PATH" +then + echo "Dist folder has been updated" +else + echo "Dist folder has NOT been updated" + exit 1 +fi + +echo "------------------------------------------------------------------" +echo "Build SSR for B2B storefront" + +export SPA_ENV='b2b' +npm run build:ssr:ci + +echo "------------------------------------------------------------------" +echo "Build CSR for B2B storefront" + +npm run build + +echo "------------------------------------------------------------------" +echo "Copy server and browser files to js-storefront to adhere to the ccv2 dist structure for B2B storefront" + +copy_browser_and_server_files "$CCV2_B2B_STOREFRONT_PATH" "$B2B_STORE" + +echo "------------------------------------------------------------------" +echo "Push to remote repository" + +cd "$GHT_REPO" + +git config --global user.email "$GHT_CCV2_EMAIL" +git config --global user.name "$GHT_CCV2_USERNAME" + +git add . +git commit --allow-empty -m "Update with $SOURCE_BRANCH_TO_DEPLOY branch from source of Spartacus" +git push diff --git a/ci-scripts/upp-cli-setup.sh b/ci-scripts/upp-cli-setup.sh deleted file mode 100755 index 2b9aa821267..00000000000 --- a/ci-scripts/upp-cli-setup.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -o errexit -set -o nounset - -APP="upp-cli" - -echo "-----" -echo "Downloading upp cli zip" - -curl -u ${GHT_USER}:${GHT_TOKEN} -L -H "Accept: application/octet-stream" \ - "https://github.tools.sap/api/v3/repos/cx-commerce/upscale-partner-platform-cli/releases/assets/7203" -o ${APP}.zip - -if [ ! -s ${APP}.zip ]; then - echo "Error downloading upp CLI zip. Check url and configs" - exit 1 -fi - -echo "-----" -echo "Installing upp cli" - -unzip -o ${APP}.zip -d ${APP} -cd ${APP} -npm install -npm run install-cli - -cd .. - -echo "-----" -echo "Configuring cli" - -upp config -z -t ${UPP_TENANT} -c ${UPP_CLIENT} -s ${UPP_SECRET} -r us10 -i 3 -a us10.stage.upp.upscalecommerce.com - -echo "-----" -echo "UPP CLI installed and setup." diff --git a/ci-scripts/validate-lint.sh b/ci-scripts/validate-lint.sh index 7c4005450ed..2a9574574b2 100755 --- a/ci-scripts/validate-lint.sh +++ b/ci-scripts/validate-lint.sh @@ -5,7 +5,7 @@ set -o pipefail function validateStylesLint { echo "----" echo "Running styleslint" - yarn lint:styles + npm run lint:styles } function validateTsConfigFile { @@ -22,8 +22,8 @@ function validateTsConfigFile { } function validateNoHardCodedText { - echo "Validating no hard-coded text (usint i18n-lint)" - yarn i18n-lint + echo "Validating no hardcoded text (usint i18n-lint)" + npm run i18n-lint } LOCAL_ENV_LIB_PATH="projects/storefrontlib/public_api" @@ -31,7 +31,7 @@ TSCONFIGFILE_TO_VALIDATE="projects/storefrontapp/tsconfig.app.prod.json" validateTsConfigFile echo "Validating that no 'fdescribe(' occurrences are present in tests..." -results=$(grep -rl --include "*spec.ts" 'fdescribe(' projects feature-libs intergration-libs core-libs || true) +results=$(grep -rl --include "*spec.ts" 'fdescribe(' projects feature-libs integration-libs core-libs || true) if [[ -z "$results" ]]; then echo "Success: No 'fdescribe(' occurrences detected in tests." else @@ -41,7 +41,7 @@ else fi echo "Validating that no 'describe.only(' occurrences are present in tests..." -results=$(grep -rl --include "*spec.ts" 'decsribe.only(' projects feature-libs intergration-libs core-libs || true) +results=$(grep -rl --include "*spec.ts" 'describe.only(' projects feature-libs integration-libs core-libs || true) if [[ -z "$results" ]]; then echo "Success: No 'describe.only(' occurrences detected in tests." else @@ -51,7 +51,7 @@ else fi echo "Validating that no 'fit(' occurrences are present in tests..." -results=$(grep -rl --include "*spec.ts" 'fit(' projects feature-libs intergration-libs core-libs || true) +results=$(grep -rl --include "*spec.ts" 'fit(' projects feature-libs integration-libs core-libs || true) if [[ -z "$results" ]]; then echo "Success: No 'fit(' occurrences detected in tests." else @@ -61,7 +61,7 @@ else fi echo "Validating that no 'it.only(' occurrences are present in tests..." -results=$(grep -rl --include "*spec.ts" 'it.only(' projects feature-libs intergration-libs core-libs || true) +results=$(grep -rl --include "*spec.ts" 'it.only(' projects feature-libs integration-libs core-libs || true) if [[ -z "$results" ]]; then echo "Success: No 'it.only(' occurrences detected in tests." else @@ -83,12 +83,12 @@ fi validateStylesLint echo "Validating code linting" -node --max_old_space_size=3584 ./node_modules/@angular/cli/bin/ng lint +node --max_old_space_size=3584 ./node_modules/nx/bin/nx run-many --all --target=lint echo "-----" echo "Validating code formatting (using prettier)" -yarn prettier 2>&1 | tee prettier.log +npm run prettier 2>&1 | tee prettier.log results=$(tail -1 prettier.log | grep projects || true) if [[ -z "$results" ]]; then echo "Success: Codebase has been prettified correctly" diff --git a/core-libs/setup/.npmignore b/core-libs/setup/.npmignore new file mode 100644 index 00000000000..3c134084ffb --- /dev/null +++ b/core-libs/setup/.npmignore @@ -0,0 +1,6 @@ +setup-jest.ts +jest.config.js +tsconfig.spec.json +**/*_spec.ts +**/*_spec.ts.snap +.eslintrc.json diff --git a/core-libs/setup/.release-it.json b/core-libs/setup/.release-it.json deleted file mode 100644 index 54f1ec35f95..00000000000 --- a/core-libs/setup/.release-it.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "git": { - "requireCleanWorkingDir": true, - "requireUpstream": false, - "tagName": "setup-${version}", - "commitMessage": "Bumping setup version to ${version}", - "tagAnnotation": "Bumping setup version to ${version}" - }, - "npm": { - "publishPath": "./../../dist/setup" - }, - "hooks": { - "after:version:bump": "cd ../.. && ng build setup --configuration production" - }, - "github": { - "release": true, - "assets": ["../../docs.tar.gz", "../../docs.zip"], - "releaseName": "@spartacus/setup@${version}", - "releaseNotes": "ts-node ../../scripts/changelog.ts --verbose --lib setup --to setup-${version}" - }, - "plugins": { - "../../scripts/release-it/bumper.js": { - "out": [ - { - "file": "package.json", - "path": [ - "peerDependencies.@spartacus/core", - "peerDependencies.@spartacus/user" - ] - } - ] - } - } -} diff --git a/core-libs/setup/jest.config.js b/core-libs/setup/jest.config.js new file mode 100644 index 00000000000..139055562b9 --- /dev/null +++ b/core-libs/setup/jest.config.js @@ -0,0 +1,32 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); +const { compilerOptions } = require('./tsconfig.spec.json'); +const { defaultTransformerOptions } = require('jest-preset-angular/presets'); + +/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ +module.exports = { + preset: 'jest-preset-angular', + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, { + prefix: '/', + }), + setupFilesAfterEnv: ['/setup-jest.ts'], + transform: { + '^.+\\.(ts|js|mjs|html|svg)$': [ + 'jest-preset-angular', + { + ...defaultTransformerOptions, + }, + ], + }, + + collectCoverage: false, + coverageReporters: ['json', 'lcov', 'text', 'clover'], + coverageDirectory: '/../../coverage/core-libs/ssr', + coverageThreshold: { + global: { + statements: 90, + branches: 80, + functions: 90, + lines: 90, + }, + }, +}; diff --git a/core-libs/setup/karma.conf.js b/core-libs/setup/karma.conf.js deleted file mode 100644 index 36864a7ba20..00000000000 --- a/core-libs/setup/karma.conf.js +++ /dev/null @@ -1,48 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-coverage'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('@angular-devkit/build-angular/plugins/karma'), - ], - client: { - clearContext: false, // leave Jasmine Spec Runner output visible in browser - }, - reporters: ['progress', 'kjhtml', 'dots'], - coverageReporter: { - dir: require('path').join(__dirname, '../../coverage/setup'), - reporters: [{ type: 'lcov', subdir: '.' }, { type: 'text-summary' }], - check: { - global: { - statements: 90, - lines: 90, - branches: 80, - functions: 90, - }, - }, - }, - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false, - restartOnFileChange: true, - buildWebpack: { - webpackConfig: { - resolve: { - fallback: { - fs: false, - }, - }, - }, - }, - }); -}; diff --git a/core-libs/setup/package.json b/core-libs/setup/package.json index 52df30a86e8..c94f2015a4d 100644 --- a/core-libs/setup/package.json +++ b/core-libs/setup/package.json @@ -1,6 +1,6 @@ { "name": "@spartacus/setup", - "version": "4.1.0-next.0", + "version": "2211.29.0-2", "description": "Includes features that makes Spartacus and it's setup easier and streamlined.", "keywords": [ "spartacus", @@ -12,19 +12,23 @@ "homepage": "https://github.com/SAP/spartacus", "repository": "https://github.com/SAP/spartacus/tree/develop/core-libs/setup", "license": "Apache-2.0", + "scripts": { + "test": "../../node_modules/.bin/jest --config ./jest.config.js" + }, "dependencies": { - "tslib": "^2.3.0" + "tslib": "^2.6.2" }, "peerDependencies": { - "@angular/core": "^13.3.0", - "@spartacus/cart": "4.1.0-next.0", - "@spartacus/core": "4.1.0-next.0", - "@spartacus/order": "4.2.0", - "@spartacus/user": "4.1.0-next.0" + "@angular/core": "^17.0.5", + "@angular/ssr": "^17.0.5", + "@spartacus/cart": "2211.29.0-2", + "@spartacus/core": "2211.29.0-2", + "@spartacus/order": "2211.29.0-2", + "@spartacus/user": "2211.29.0-2" }, "optionalDependencies": { - "@nguniversal/express-engine": "^13.1.1", - "express": "^4.15.2" + "@angular/platform-server": "^17.0.5", + "express": "^4.21.0" }, "publishConfig": { "access": "public" diff --git a/core-libs/setup/project.json b/core-libs/setup/project.json new file mode 100644 index 00000000000..ed56ba2f6f7 --- /dev/null +++ b/core-libs/setup/project.json @@ -0,0 +1,38 @@ +{ + "name": "setup", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "core-libs/setup", + "prefix": "cx", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "core-libs/setup/tsconfig.lib.json", + "project": "core-libs/setup/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "core-libs/setup/tsconfig.lib.prod.json" + } + } + }, + "test-jest": { + "executor": "nx:run-commands", + "options": { + "command": "npm run test", + "cwd": "core-libs/setup" + } + }, + "lint": { + "executor": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "core-libs/setup/**/*.ts", + "core-libs/setup/**/*.html" + ] + } + } + }, + "tags": ["type:util"] +} diff --git a/core-libs/setup/public_api.ts b/core-libs/setup/public_api.ts index 4d07be1c6b8..43ad55a8fb5 100644 --- a/core-libs/setup/public_api.ts +++ b/core-libs/setup/public_api.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './recipes/index'; diff --git a/core-libs/setup/recipes/b2b/config/default-b2b-occ-config.ts b/core-libs/setup/recipes/b2b/config/default-b2b-occ-config.ts index a412a32cd0a..5631d96e89e 100644 --- a/core-libs/setup/recipes/b2b/config/default-b2b-occ-config.ts +++ b/core-libs/setup/recipes/b2b/config/default-b2b-occ-config.ts @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + // We need this import for augmentation of OccEndpoints to pick up import { CartOccEndpoints } from '@spartacus/cart/base/occ'; import { OccConfig } from '@spartacus/core'; @@ -26,6 +32,7 @@ const defaultB2bOrderOccEndpoints: OrderOccEndpoints = { placeOrder: 'orgUsers/${userId}/orders?fields=FULL', scheduleReplenishmentOrder: 'orgUsers/${userId}/replenishmentOrders?fields=FULL,costCenter(FULL),purchaseOrderNumber,paymentType', + reorder: 'orgUsers/${userId}/cartFromOrder?orderCode=${orderCode}', }; export const defaultB2bOccConfig: OccConfig = { diff --git a/core-libs/setup/recipes/b2b/config/index.ts b/core-libs/setup/recipes/b2b/config/index.ts index da10a30382d..14033bbd1d6 100644 --- a/core-libs/setup/recipes/b2b/config/index.ts +++ b/core-libs/setup/recipes/b2b/config/index.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './default-b2b-occ-config'; diff --git a/core-libs/setup/recipes/b2b/index.ts b/core-libs/setup/recipes/b2b/index.ts index b968e6a77af..7ff4d3e3c4a 100644 --- a/core-libs/setup/recipes/b2b/index.ts +++ b/core-libs/setup/recipes/b2b/index.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './config/index'; diff --git a/core-libs/setup/recipes/index.ts b/core-libs/setup/recipes/index.ts index 1719cd4b656..ec06b08436b 100644 --- a/core-libs/setup/recipes/index.ts +++ b/core-libs/setup/recipes/index.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './b2b/index'; diff --git a/core-libs/setup/setup-jest.ts b/core-libs/setup/setup-jest.ts new file mode 100644 index 00000000000..0569a34365f --- /dev/null +++ b/core-libs/setup/setup-jest.ts @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { + platformServerTesting, + ServerTestingModule, +} from '@angular/platform-server/testing'; + +getTestBed().initTestEnvironment( + ServerTestingModule, + platformServerTesting(), + {} +); diff --git a/core-libs/setup/ssr/engine-decorator/index.ts b/core-libs/setup/ssr/engine-decorator/index.ts index 9397c12c61b..6707ccf7222 100644 --- a/core-libs/setup/ssr/engine-decorator/index.ts +++ b/core-libs/setup/ssr/engine-decorator/index.ts @@ -1 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export { NgExpressEngineDecorator } from './ng-express-engine-decorator'; diff --git a/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.spec.ts b/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.spec.ts index ef12c1144b6..0e43cdb2e1f 100644 --- a/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.spec.ts +++ b/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.spec.ts @@ -1,18 +1,26 @@ import { SERVER_REQUEST_URL } from '@spartacus/core'; +import { NgSetupOptions, RenderOptions } from '../engine/ng-express-engine'; +import { DefaultExpressServerLogger, EXPRESS_SERVER_LOGGER } from '../logger'; import { - decorateExpressEngine, NgExpressEngine, NgExpressEngineDecorator, NgExpressEngineInstance, + decorateExpressEngine, } from './ng-express-engine-decorator'; +jest.mock('fs', () => ({ + readFileSync: () => '', +})); + +jest.spyOn(console, 'log').mockImplementation(() => {}); + describe('NgExpressEngineDecorator', () => { describe('get', () => { let originalEngine: NgExpressEngine; let originalEngineInstance: NgExpressEngineInstance; - let mockEngineOptions; + let mockEngineOptions: Readonly; - let mockOptions; + let mockOptions: RenderOptions; const mockPath = 'testPath'; const mockCallback = () => {}; @@ -30,13 +38,8 @@ describe('NgExpressEngineDecorator', () => { providers: [{ provide: 'testToken', useValue: 'testValue' }], } as any; - originalEngine = jasmine - .createSpy('ngExpressEngine') - .and.callFake(() => originalEngineInstance); - - originalEngineInstance = jasmine - .createSpy('ngExpressEngineInstance') - .and.callFake(() => {}); + originalEngine = jest.fn(() => originalEngineInstance); + originalEngineInstance = jest.fn(() => {}); const engine = NgExpressEngineDecorator.get(originalEngine, null); const engineInstance = engine(mockEngineOptions); @@ -53,9 +56,9 @@ describe('NgExpressEngineDecorator', () => { it(`should pass setup options to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ + expect.objectContaining({ bootstrap: 'TestModule', - providers: jasmine.arrayContaining([ + providers: expect.arrayContaining([ { provide: 'testToken', useValue: 'testValue' }, ]), }) @@ -64,9 +67,9 @@ describe('NgExpressEngineDecorator', () => { it(`should add SERVER_REQUEST_URL to providers in the setup options passed to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ - providers: jasmine.arrayContaining([ - jasmine.objectContaining({ + expect.objectContaining({ + providers: expect.arrayContaining([ + expect.objectContaining({ provide: SERVER_REQUEST_URL, }), ]), @@ -79,12 +82,12 @@ describe('NgExpressEngineDecorator', () => { describe('decorateExpressEngine', () => { let originalEngine: NgExpressEngine; let originalEngineInstance: NgExpressEngineInstance; - let mockEngineOptions; + let mockEngineOptions: Readonly; - let mockOptions; + let mockOptions: RenderOptions; const mockPath = 'testPath'; const mockCallback = () => {}; - let engineInstance; + let engineInstance: NgExpressEngineInstance; beforeEach(() => { const app = { @@ -97,12 +100,13 @@ describe('decorateExpressEngine', () => { req: { protocol: 'https', originalUrl: '/electronics/en/USD/cart', - get: jasmine.createSpy('req.get').and.returnValue('site.com'), + get: jest.fn(() => {}), app, connection: {}, - }, - res: >{ - set: jasmine.createSpy('req.set'), + res: >{ + set: jest.fn(() => {}), + locals: {}, + }, }, } as any; @@ -111,13 +115,8 @@ describe('decorateExpressEngine', () => { providers: [{ provide: 'testToken', useValue: 'testValue' }], } as any; - originalEngine = jasmine - .createSpy('ngExpressEngine') - .and.callFake(() => originalEngineInstance); - - originalEngineInstance = jasmine - .createSpy('ngExpressEngineInstance') - .and.callFake(() => {}); + originalEngine = jest.fn(() => originalEngineInstance); + originalEngineInstance = jest.fn(() => {}); }); describe('with disabled optimizations', () => { @@ -137,9 +136,9 @@ describe('decorateExpressEngine', () => { it(`should pass setup options to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ + expect.objectContaining({ bootstrap: 'TestModule', - providers: jasmine.arrayContaining([ + providers: expect.arrayContaining([ { provide: 'testToken', useValue: 'testValue' }, ]), }) @@ -148,9 +147,9 @@ describe('decorateExpressEngine', () => { it(`should add SERVER_REQUEST_URL to providers in the setup options passed to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ - providers: jasmine.arrayContaining([ - jasmine.objectContaining({ + expect.objectContaining({ + providers: expect.arrayContaining([ + expect.objectContaining({ provide: SERVER_REQUEST_URL, }), ]), @@ -188,14 +187,98 @@ describe('decorateExpressEngine', () => { it(`should pass parameters to the original engine instance`, () => { expect(originalEngineInstance).toHaveBeenCalledWith( + mockPath, + { + ...mockOptions, + providers: [ + { + provide: EXPRESS_SERVER_LOGGER, + useValue: new DefaultExpressServerLogger(), + }, + ], + }, + expect.any(Function) + ); + }); + + it(`should apply optimization wrapper`, () => { + // we check that callback is not the original one + expect(originalEngineInstance).not.toHaveBeenCalledWith( mockPath, mockOptions, - jasmine.any(Function) + mockCallback + ); + }); + + it(`should pass setup options to the original engine`, () => { + expect(originalEngine).toHaveBeenCalledWith( + expect.objectContaining({ + bootstrap: 'TestModule', + providers: expect.arrayContaining([ + { provide: 'testToken', useValue: 'testValue' }, + ]), + }) + ); + }); + + it(`should add SERVER_REQUEST_URL to providers in the setup options passed to the original engine`, () => { + expect(originalEngine).toHaveBeenCalledWith( + expect.objectContaining({ + providers: expect.arrayContaining([ + expect.objectContaining({ + provide: SERVER_REQUEST_URL, + }), + ]), + }) + ); + }); + + it(`should be called only once per request with caching`, () => { + const mockOptions2 = { + ...mockOptions, + req: { ...mockOptions.req, originalUrl: 'aaa' }, + }; + const mockOptions3 = { + ...mockOptions, + req: { ...mockOptions.req, originalUrl: 'ccc' }, + }; + engineInstance(mockPath, mockOptions, mockCallback); + engineInstance('aaa', mockOptions2, mockCallback); + engineInstance(mockPath, mockOptions, mockCallback); + engineInstance('aaa', mockOptions2, mockCallback); + engineInstance('ccc', mockOptions3, mockCallback); + expect(originalEngineInstance).toHaveBeenCalledTimes(3); + }); + }); + + describe('with optimizations not specified on 2nd argument', () => { + beforeEach(() => { + const engine = decorateExpressEngine( + originalEngine + // 2nd argument not specified (but not explicitly "undefined"!) + ); + engineInstance = engine(mockEngineOptions); + engineInstance(mockPath, mockOptions, mockCallback); + }); + + it(`should pass parameters to the original engine instance`, () => { + expect(originalEngineInstance).toHaveBeenCalledWith( + mockPath, + { + ...mockOptions, + providers: [ + { + provide: EXPRESS_SERVER_LOGGER, + useValue: new DefaultExpressServerLogger(), + }, + ], + }, + expect.any(Function) ); }); it(`should apply optimization wrapper`, () => { - // we check, that callback is not the original one + // we check that callback is not the original one expect(originalEngineInstance).not.toHaveBeenCalledWith( mockPath, mockOptions, @@ -205,9 +288,9 @@ describe('decorateExpressEngine', () => { it(`should pass setup options to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ + expect.objectContaining({ bootstrap: 'TestModule', - providers: jasmine.arrayContaining([ + providers: expect.arrayContaining([ { provide: 'testToken', useValue: 'testValue' }, ]), }) @@ -216,9 +299,9 @@ describe('decorateExpressEngine', () => { it(`should add SERVER_REQUEST_URL to providers in the setup options passed to the original engine`, () => { expect(originalEngine).toHaveBeenCalledWith( - jasmine.objectContaining({ - providers: jasmine.arrayContaining([ - jasmine.objectContaining({ + expect.objectContaining({ + providers: expect.arrayContaining([ + expect.objectContaining({ provide: SERVER_REQUEST_URL, }), ]), diff --git a/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.ts b/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.ts index e7cd9ed706f..58a6034f923 100644 --- a/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.ts +++ b/core-libs/setup/ssr/engine-decorator/ng-express-engine-decorator.ts @@ -1,9 +1,19 @@ -import { NgSetupOptions } from '@nguniversal/express-engine'; +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr'; +import { NgSetupOptions } from '../engine/ng-express-engine'; import { OptimizedSsrEngine, SsrCallbackFn, } from '../optimized-engine/optimized-ssr-engine'; -import { SsrOptimizationOptions } from '../optimized-engine/ssr-optimization-options'; +import { + SsrOptimizationOptions, + defaultSsrOptimizationOptions, +} from '../optimized-engine/ssr-optimization-options'; import { getServerRequestProviders } from '../providers/ssr-providers'; export type NgExpressEngineInstance = ( @@ -13,7 +23,7 @@ export type NgExpressEngineInstance = ( ) => void; export type NgExpressEngine = ( - setupOptions: Readonly + setupOptions: Readonly ) => NgExpressEngineInstance; /** @@ -36,10 +46,10 @@ export class NgExpressEngineDecorator { export function decorateExpressEngine( ngExpressEngine: NgExpressEngine, - optimizationOptions: SsrOptimizationOptions | null = { - concurrency: 20, - timeout: 3000, - } + optimizationOptions: + | SsrOptimizationOptions + | null + | undefined = defaultSsrOptimizationOptions ): NgExpressEngine { return function (setupOptions: NgSetupOptions) { const engineInstance = ngExpressEngine({ diff --git a/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap b/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap new file mode 100644 index 00000000000..36a99e199f2 --- /dev/null +++ b/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CxCommonEngine should handle APP_INITIALIZER errors the standard Angular way and throw if any occurred 1`] = ` +"R3InjectorError(TokenServerModule)[InjectionToken SOME_TOKEN -> InjectionToken SOME_TOKEN]: + NullInjectorError: No provider for InjectionToken SOME_TOKEN!" +`; + +exports[`CxCommonEngine should handle errors propagated from SSR 1`] = `"test error"`; + +exports[`CxCommonEngine should not override providers passed to options 1`] = `"message:test"`; + +exports[`CxCommonEngine should return html if no errors 1`] = `"some template"`; diff --git a/core-libs/setup/ssr/engine/cx-common-engine.spec.ts b/core-libs/setup/ssr/engine/cx-common-engine.spec.ts new file mode 100644 index 00000000000..4a37596fe60 --- /dev/null +++ b/core-libs/setup/ssr/engine/cx-common-engine.spec.ts @@ -0,0 +1,130 @@ +// eslint-disable-next-line import/no-unassigned-import +import '@angular/compiler'; + +import { Component, InjectionToken, NgModule, inject } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ServerModule } from '@angular/platform-server'; +import { PROPAGATE_ERROR_TO_SERVER } from '../error-handling/error-response/propagate-error-to-server'; +import { CxCommonEngine } from './cx-common-engine'; + +// Test how the CxCommonEngine handles successful server-side rendering +@Component({ selector: 'cx-mock', template: 'some template' }) +export class SuccessComponent {} + +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [SuccessComponent], + bootstrap: [SuccessComponent], +}) +export class SuccessServerModule {} + +// Test how the CxCommonEngine handles propagated error +@Component({ + selector: 'cx-response', + template: ``, +}) +export class WithPropagatedErrorComponent { + constructor() { + inject(PROPAGATE_ERROR_TO_SERVER)(new Error('test error')); + } +} + +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [WithPropagatedErrorComponent], + bootstrap: [WithPropagatedErrorComponent], +}) +export class WithPropagatedErrorServerModule {} + +// Test that the CxCommonEngine doesn't override providers +// If SOME_TOKEN not provided, test how the CxCommonEngine handles APP_INITIALIZER errors +export const SOME_TOKEN = new InjectionToken('SOME_TOKEN'); + +@Component({ + selector: 'cx-token', + template: `message:{{ someToken }}`, +}) +export class TokenComponent { + someToken = inject(SOME_TOKEN); +} + +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [TokenComponent], + bootstrap: [TokenComponent], +}) +export class TokenServerModule {} + +describe('CxCommonEngine', () => { + let engine: CxCommonEngine; + + beforeAll(() => { + jest.spyOn(console, 'error').mockImplementation(); + jest.spyOn(console, 'log').mockImplementation(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return html if no errors', async () => { + engine = new CxCommonEngine({ + bootstrap: SuccessServerModule, + }); + + const html = await engine.render({ + url: 'http://localhost:4200', + document: '', + }); + + // Cannot use `.toMatchInlineSnapshot()` due to bug in jest: + // see: https://github.com/thymikee/jest-preset-angular/issues/1084 + expect(html).toMatchSnapshot(); + }); + + it('should not override providers passed to options', async () => { + engine = new CxCommonEngine({ + bootstrap: TokenServerModule, + }); + + const html = await engine.render({ + url: 'http://localhost:4200', + document: '', + providers: [{ provide: SOME_TOKEN, useValue: 'test' }], + }); + + // Cannot use `.toMatchInlineSnapshot()` due to bug in jest: + // see: https://github.com/thymikee/jest-preset-angular/issues/1084 + expect(html).toMatchSnapshot(); + }); + + it('should handle APP_INITIALIZER errors the standard Angular way and throw if any occurred', async () => { + engine = new CxCommonEngine({ + bootstrap: TokenServerModule, + }); + + // Cannot use `.toMatchInlineSnapshot()` due to bug in jest: + // see: https://github.com/thymikee/jest-preset-angular/issues/1084 + await expect( + engine.render({ + url: 'http://localhost:4200', + document: '', + }) + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + it('should handle errors propagated from SSR', async () => { + engine = new CxCommonEngine({ + bootstrap: WithPropagatedErrorServerModule, + }); + + // Cannot use `.toMatchInlineSnapshot()` due to bug in jest: + // see: https://github.com/thymikee/jest-preset-angular/issues/1084 + await expect( + engine.render({ + url: 'http://localhost:4200', + document: '', + }) + ).rejects.toThrowErrorMatchingSnapshot(); + }); +}); diff --git a/core-libs/setup/ssr/engine/cx-common-engine.ts b/core-libs/setup/ssr/engine/cx-common-engine.ts new file mode 100644 index 00000000000..6abbcd08a41 --- /dev/null +++ b/core-libs/setup/ssr/engine/cx-common-engine.ts @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CommonEngine, + CommonEngineOptions, + CommonEngineRenderOptions, +} from '@angular/ssr'; +import { PROPAGATE_ERROR_TO_SERVER } from '../error-handling/error-response/propagate-error-to-server'; + +/** + * The Spartacus extension of the Angular's `CommonEngine`. It is able to handle the propagated server responses caught during server-side rendering of a Spartacus app. + * For reference, see Angular's source code: https://github.com/angular/angular-cli/blob/6cf866225ab09f8b4b3803c000b632bed8448ce4/packages/angular/ssr/src/common-engine.ts#L56 + * + * @extends {CommonEngine} + */ +export class CxCommonEngine extends CommonEngine { + constructor(options?: CommonEngineOptions) { + super(options); + } + + /** + * @override + * Renders for the given options. + * If an error is populated from the rendered applications + * (via `PROPAGATE_ERROR_TO_SERVER` callback), then such an error + * will be thrown and the result promise rejected - but only AFTER the rendering is complete. + * In other words, at first an error occurs and it's captured, then we wait until the rendering completes + * and ONLY then we reject the promise with the payload being the encountered error. + * + * Note: if more errors are captured during the rendering, only the first one will be used + * as the payload of the rejected promise, others won't. + * + * @param {CommonEngineRenderOptions} options - The options to render. + * @returns {Promise} Promise which resolves with the rendered HTML as a string + * OR rejects with the error, if any is propagated from the rendered app. + */ + override async render(options: CommonEngineRenderOptions): Promise { + let error: undefined | unknown; + + return super + .render({ + ...options, + providers: [ + { + provide: PROPAGATE_ERROR_TO_SERVER, + useFactory: () => { + return (propagatedError: unknown) => { + // We're interested only the first propagated error, so we use `??=` instead of `=`: + error ??= propagatedError; + }; + }, + }, + ...(options.providers ?? []), + ], + }) + .then((html: string) => { + if (error) { + throw error; + } + return html; + }); + } +} diff --git a/core-libs/setup/ssr/engine/ng-express-engine.spec.ts b/core-libs/setup/ssr/engine/ng-express-engine.spec.ts new file mode 100644 index 00000000000..6871dd25eea --- /dev/null +++ b/core-libs/setup/ssr/engine/ng-express-engine.spec.ts @@ -0,0 +1,283 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// eslint-disable-next-line import/no-unassigned-import +import '@angular/compiler'; + +import { Component, Inject, InjectionToken, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ServerModule } from '@angular/platform-server'; +import { REQUEST, RESPONSE } from '../public_api'; +import { ngExpressEngine } from './ng-express-engine'; +//@ts-ignore + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@Component({ selector: 'cx-mock', template: 'some template' }) +export class MockComponent {} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [MockComponent], + bootstrap: [MockComponent], +}) +export class MockServerModule {} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@Component({ selector: 'cx-request', template: `url:{{ _req.url }}` }) +export class RequestComponent { + constructor(@Inject(REQUEST) public readonly _req: any) {} +} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [RequestComponent], + bootstrap: [RequestComponent], +}) +export class RequestServerModule {} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@Component({ + selector: 'cx-response', + template: `statusCode:{{ _res.statusCode }}`, +}) +export class ResponseComponent { + constructor(@Inject(RESPONSE) public readonly _res: any) {} +} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [ResponseComponent], + bootstrap: [ResponseComponent], +}) +export class ResponseServerModule {} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +export const SOME_TOKEN = new InjectionToken('SOME_TOKEN'); + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@Component({ + selector: 'cx-token', + template: `message:{{ _someToken.message }}`, +}) +export class TokenComponent { + constructor(@Inject(SOME_TOKEN) public readonly _someToken: any) {} +} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/mock.server.module.ts + * + */ +@NgModule({ + imports: [BrowserModule, ServerModule], + declarations: [TokenComponent], + bootstrap: [TokenComponent], +}) +export class TokenServerModule {} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * Inspired by tests for ngExpressEngine. + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/spec/index.spec.ts + * + */ +describe('ngExpressEngine', () => { + beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(); + }); + + it('should render a basic template', (done) => { + ngExpressEngine({ bootstrap: MockServerModule })( + null as any as string, + { + req: { get: () => 'localhost' } as any, + document: '', + }, + (err, html) => { + if (err) { + throw err; + } + expect(html).toContain('some template'); + done(); + } + ); + }); + + it('Should throw when no module is passed', () => { + ngExpressEngine({ bootstrap: null as any })( + null as any as string, + { + req: {} as any, + bootstrap: null as any, + document: '', + }, + (_err, _html) => { + expect(_err).toBeTruthy(); + } + ); + }); + + it('should be able to inject REQUEST token', (done) => { + ngExpressEngine({ bootstrap: RequestServerModule })( + null as any as string, + { + req: { + get: () => 'localhost', + url: 'http://localhost:4200', + } as any, + document: '', + }, + (err, html) => { + if (err) { + throw err; + } + expect(html).toContain('url:http://localhost:4200'); + done(); + } + ); + }); + + it('should be able to inject RESPONSE token', (done) => { + const someStatusCode = 400; + ngExpressEngine({ bootstrap: ResponseServerModule })( + null as any as string, + { + req: { + get: () => 'localhost', + res: { + statusCode: someStatusCode, + }, + } as any, + document: '', + }, + (err, html) => { + if (err) { + throw err; + } + expect(html).toContain(`statusCode:${someStatusCode}`); + done(); + } + ); + }); + + it('should be able to inject some token', (done) => { + const someValue = { message: 'value' + new Date() }; + ngExpressEngine({ + bootstrap: TokenServerModule, + providers: [{ provide: SOME_TOKEN, useValue: someValue }], + })( + null as any as string, + { + req: { + get: () => 'localhost', + url: 'http://localhost:4200', + } as any, + document: '', + }, + (err, html) => { + if (err) { + throw err; + } + expect(html).toContain(someValue.message); + done(); + } + ); + }); +}); diff --git a/core-libs/setup/ssr/engine/ng-express-engine.ts b/core-libs/setup/ssr/engine/ng-express-engine.ts new file mode 100644 index 00000000000..45b6c669dfd --- /dev/null +++ b/core-libs/setup/ssr/engine/ng-express-engine.ts @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { StaticProvider } from '@angular/core'; +import { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr'; +import { Request, Response } from 'express'; +import { REQUEST, RESPONSE } from '../tokens/express.tokens'; +import { CxCommonEngine } from './cx-common-engine'; + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/src/main.ts + */ +export type NgSetupOptions = Pick< + CommonEngineRenderOptions, + 'providers' | 'publicPath' | 'inlineCriticalCss' +> & + CommonEngineOptions; + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/src/main.ts + */ +function getReqResProviders(req: Request, res?: Response): StaticProvider[] { + const providers: StaticProvider[] = [ + { + provide: REQUEST, + useValue: req, + }, + ]; + if (res) { + providers.push({ + provide: RESPONSE, + useValue: res, + }); + } + + return providers; +} + +/** + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/src/main.ts + */ +export interface RenderOptions extends CommonEngineRenderOptions { + req: Request; + res?: Response; +} + +/** + * This is an express engine for handling Angular Applications + * + * Function `ngExpressEngine` was originally present in Angular for a long time and was removed in version Angular 17. + * However, it is needed in Spartacus for backward compatibility reasons. + * Therefore, we have copied the code from the Angular repository and included it in our Spartacus repository to avoid larger refactors. + * + * @license + * The MIT License + * Copyright (c) 2010-2023 Google LLC. http://angular.io/license + * + * See: + * - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/src/main.ts + */ +export function ngExpressEngine(setupOptions: NgSetupOptions) { + const engine = new CxCommonEngine({ + bootstrap: setupOptions.bootstrap, + providers: setupOptions.providers, + enablePerformanceProfiler: setupOptions.enablePerformanceProfiler, + }); + + return function ( + filePath: string, + options: object, + callback: (err?: Error | null, html?: string) => void + ) { + try { + const renderOptions = { ...options } as RenderOptions; + if (!setupOptions.bootstrap && !renderOptions.bootstrap) { + throw new Error('You must pass in a NgModule to be bootstrapped'); + } + + const { req } = renderOptions; + const res = renderOptions.res ?? req.res; + + renderOptions.url = + renderOptions.url ?? + `${req.protocol}://${req.get('host') || ''}${req.baseUrl}${req.url}`; + renderOptions.documentFilePath = + renderOptions.documentFilePath ?? filePath; + renderOptions.providers = [ + ...(renderOptions.providers ?? []), + getReqResProviders(req, res), + ]; + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + renderOptions.publicPath = + renderOptions.publicPath ?? + setupOptions.publicPath ?? + (options as any).settings?.views; + renderOptions.inlineCriticalCss = + renderOptions.inlineCriticalCss ?? setupOptions.inlineCriticalCss; + + engine + .render(renderOptions) + .then((html) => callback(null, html)) + .catch(callback); + } catch (err) { + err instanceof Error && callback(err); + } + }; +} diff --git a/core-libs/setup/ssr/error-handling/error-response/index.ts b/core-libs/setup/ssr/error-handling/error-response/index.ts new file mode 100644 index 00000000000..734d871cfb3 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/error-response/index.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './propagate-error-to-server'; diff --git a/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts b/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts new file mode 100644 index 00000000000..391a651b4be --- /dev/null +++ b/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { InjectionToken } from '@angular/core'; + +/** + * Propagates the given error object to the higher layer in the server. + * + * It's meant to propagate errors for example to ExpressJS layer when using SSR + * or to a Prerendering Worker when using Server Prerendering. + * Currently, it's provided OOTB only in SSR (not prerendering), in the `CxCommonEngine` class. + * + * Note: We need it until Angular implements a proper propagation of async errors + * from an app to the the higher layer in the server. + * For more, see the Angular issue https://github.com/angular/angular/issues/33642 + */ +export const PROPAGATE_ERROR_TO_SERVER = new InjectionToken< + (error: unknown) => void +>('PROPAGATE_ERROR_RESPONSE'); diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts new file mode 100644 index 00000000000..898dcc39fa9 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts @@ -0,0 +1,56 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { CmsPageNotFoundOutboundHttpError } from '@spartacus/core'; +import { defaultExpressErrorHandlers } from './express-error-handlers'; + +describe('expressErrorHandlers', () => { + let documentContent: string; + let req: any; + let res: any; + let next: any; + + beforeEach(() => { + documentContent = 'some document content'; + req = {}; + res = { + set: jest.fn(), + status: jest.fn().mockReturnThis(), + send: jest.fn(), + }; + }); + + it('should do nothing if headers are already sent', () => { + const err = new HttpErrorResponse({ + error: 'Page not found', + }); + const errorRequestHandler = defaultExpressErrorHandlers(documentContent); + res.headersSent = true; + + errorRequestHandler(err, req, res, next); + + expect(res.set).not.toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + expect(res.send).not.toHaveBeenCalled(); + }); + + it('should handle CmsPageNotFoundOutboundHttpError', () => { + const err = new CmsPageNotFoundOutboundHttpError('Page not found'); + const errorRequestHandler = defaultExpressErrorHandlers(documentContent); + + errorRequestHandler(err, req, res, next); + + expect(res.set).toHaveBeenCalledWith('Cache-Control', 'no-store'); + expect(res.status).toHaveBeenCalledWith(404); + expect(res.send).toHaveBeenCalledWith(documentContent); + }); + + it('should handle unknown error', () => { + const err = new Error('unknown error'); + const errorRequestHandler = defaultExpressErrorHandlers(documentContent); + + errorRequestHandler(err, req, res, next); + + expect(res.set).toHaveBeenCalledWith('Cache-Control', 'no-store'); + expect(res.status).toHaveBeenCalledWith(500); + expect(res.send).toHaveBeenCalledWith(documentContent); + }); +}); diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts new file mode 100644 index 00000000000..42aba5c9d49 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CmsPageNotFoundOutboundHttpError, + HttpResponseStatus, +} from '@spartacus/core'; +import { ErrorRequestHandler } from 'express'; + +/** + * Returns default handlers which results in a fallback to client side rendering. + * - If cms page not found, the document content is sent to the client with the appropriate 404 status code. + * - For rest of errors, the document content is sent to the client with the appropriate status 500 code. + * + * @param documentContent The document content to be sent to the client. + * @returns The error request handler. + */ +export const defaultExpressErrorHandlers = + (documentContent: string): ErrorRequestHandler => + (err, _req, res, _next) => { + if (!res.headersSent) { + res.set('Cache-Control', 'no-store'); + const statusCode = + err instanceof CmsPageNotFoundOutboundHttpError + ? HttpResponseStatus.NOT_FOUND + : HttpResponseStatus.INTERNAL_SERVER_ERROR; + res.status(statusCode).send(documentContent); + } + }; diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts new file mode 100644 index 00000000000..00fe9d6251f --- /dev/null +++ b/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './express-error-handlers'; diff --git a/core-libs/setup/ssr/error-handling/index.ts b/core-libs/setup/ssr/error-handling/index.ts new file mode 100644 index 00000000000..c96a4b4acde --- /dev/null +++ b/core-libs/setup/ssr/error-handling/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './error-response/index'; +export * from './express-error-handlers/index'; +export * from './multi-error-handlers/index'; diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts new file mode 100644 index 00000000000..d1c6353b9e5 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './propagating-to-server-error-handler'; diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts new file mode 100644 index 00000000000..fa7e7358767 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts @@ -0,0 +1,54 @@ +import { TestBed } from '@angular/core/testing'; +import { FeatureConfigService } from '@spartacus/core'; +import { PROPAGATE_ERROR_TO_SERVER } from '../error-response/propagate-error-to-server'; +import { PropagatingToServerErrorHandler } from './propagating-to-server-error-handler'; + +describe('PropagatingToServerErrorHandler', () => { + let propagatingToServerErrorHandler: PropagatingToServerErrorHandler; + let featureConfigService: FeatureConfigService; + let propagateErrorResponse: any; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + PropagatingToServerErrorHandler, + FeatureConfigService, + { + provide: PROPAGATE_ERROR_TO_SERVER, + useValue: jest.fn(), + }, + ], + }); + propagatingToServerErrorHandler = TestBed.inject( + PropagatingToServerErrorHandler + ); + propagateErrorResponse = TestBed.inject(PROPAGATE_ERROR_TO_SERVER); + featureConfigService = TestBed.inject(FeatureConfigService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should propagate error when propagateErrorsToServer is enabled', () => { + jest + .spyOn(featureConfigService, 'isEnabled') + .mockImplementationOnce((val) => val === 'propagateErrorsToServer'); + const error = new Error('test error'); + + propagatingToServerErrorHandler.handleError(error); + + expect(propagateErrorResponse as jest.Mock).toHaveBeenCalledWith(error); + }); + + it('should not propagate error when propagateErrorsToServer is disabled', () => { + jest + .spyOn(featureConfigService, 'isEnabled') + .mockImplementationOnce((val) => !(val === 'propagateErrorsToServer')); + const error = new Error('test error'); + + propagatingToServerErrorHandler.handleError(error); + + expect(propagateErrorResponse as jest.Mock).not.toHaveBeenCalled(); + }); +}); diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts new file mode 100644 index 00000000000..68a65aa0e96 --- /dev/null +++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable, inject } from '@angular/core'; +import { FeatureConfigService, MultiErrorHandler } from '@spartacus/core'; +import { PROPAGATE_ERROR_TO_SERVER } from '../error-response/propagate-error-to-server'; + +/** + * Propagates the given error object to the higher layer in the server. + * + * It's meant to propagate errors for example to ExpressJS layer when using SSR + * or to a Prerendering Worker when using Server Prerendering. + * Currently, it's provided OOTB only in SSR (not prerendering), in the `CxCommonEngine` class. + * + * Note: We need it until Angular implements a proper propagation of async errors + * from an app to the the higher layer in the server. + * For more, see the Angular issue https://github.com/angular/angular/issues/33642 + * + * Intended to be used as part of a multi-error handler strategy. + * + * @see MultiErrorHandler + */ +@Injectable({ + providedIn: 'root', +}) +export class PropagatingToServerErrorHandler implements MultiErrorHandler { + protected propagateErrorToServer = inject(PROPAGATE_ERROR_TO_SERVER); + private featureConfigService: FeatureConfigService = + inject(FeatureConfigService); + + handleError(error: unknown): void { + if (!this.featureConfigService.isEnabled('propagateErrorsToServer')) { + return; + } + this.propagateErrorToServer(error); + } +} diff --git a/core-libs/setup/ssr/express-utils/express-request-origin.ts b/core-libs/setup/ssr/express-utils/express-request-origin.ts new file mode 100644 index 00000000000..894ae5d8a96 --- /dev/null +++ b/core-libs/setup/ssr/express-utils/express-request-origin.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Request } from 'express'; + +export function getRequestOrigin(req: Request): string { + // If express is resolving and trusting X-Forwarded-Host, we want to take it + // into an account to properly generate request origin. + const trustProxyFn = req.app.get('trust proxy fn'); + let forwardedHost = req.get('X-Forwarded-Host'); + if (forwardedHost && trustProxyFn(req.connection.remoteAddress, 0)) { + if (forwardedHost.indexOf(',') !== -1) { + // Note: X-Forwarded-Host is normally only ever a + // single value, but this is to be safe. + forwardedHost = forwardedHost + .substring(0, forwardedHost.indexOf(',')) + .trimRight(); + } + return `${req.protocol}://${forwardedHost}`; + } else { + return `${req.protocol}://${req.get('host')}`; + } +} diff --git a/core-libs/setup/ssr/express-utils/express-request-url.ts b/core-libs/setup/ssr/express-utils/express-request-url.ts new file mode 100644 index 00000000000..ada82ccea16 --- /dev/null +++ b/core-libs/setup/ssr/express-utils/express-request-url.ts @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Request } from 'express'; +import { getRequestOrigin } from './express-request-origin'; + +export function getRequestUrl(req: Request): string { + return getRequestOrigin(req) + req.originalUrl; +} diff --git a/core-libs/setup/ssr/logger/get-logger-inspect-options.ts b/core-libs/setup/ssr/logger/get-logger-inspect-options.ts new file mode 100644 index 00000000000..6ca72294b84 --- /dev/null +++ b/core-libs/setup/ssr/logger/get-logger-inspect-options.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isDevMode } from '@angular/core'; +import { InspectOptions } from 'node:util'; + +/** + * Default options to be used for formatting log messages in NodeJS (in both prod and dev mode). + * + * They are meant to be passed to `inspect()` or `formatWithOptions()` functions from the `node:util` module + * for logging purposes. + */ +const DEFAULT_LOGGER_INSPECT_OPTIONS: InspectOptions = { + /** + * Prevent the depth of the logged object to be limited to 2 (which is a NodeJS default). + * Otherwise, the object's properties at nested levels higher than 2 would not be visible in the logs. + * e.g. `{ a: { b: { c: { d: 'e' } } } }` would be presented as `{ a: { b: { c: [Object] } } }` in the logs. + * + * The value 10 was chosen arbitrarily. It can be adjusted in the future, if needed. + */ + depth: 10, +}; + +/** + * Options to be used for formatting log messages in NodeJS only in PRODUCTION mode. + * + * They are meant to be passed to `inspect()` or `formatWithOptions()` functions from the `node:util` module + * for logging purposes. + */ +const PRODUCTION_LOGGER_INSPECT_OPTIONS: InspectOptions = { + /** + * When converting an object into a string, we don't want separate properties + * to be placed in separate lines of the output string, no matter how wide the output string would be. + * + * Otherwise, separate lines of the output string could be interpreted incorrectly + * as separate log entries, by external monitoring tools (e.g. Kibana). + */ + breakLength: Infinity, +}; + +/** + * Options to be used for formatting log messages in NodeJS. + * + * They are meant to be passed to `inspect()` or `formatWithOptions()` functions from the `node:util` module + * for logging purposes. + * + * - In dev mode, it's optimized for human-readable output (multi-line output). + * - In prod mode, it's optimized for machine-readable output (single-line output) + */ +export function getLoggerInspectOptions(): InspectOptions { + return { + ...DEFAULT_LOGGER_INSPECT_OPTIONS, + ...(isDevMode() ? {} : PRODUCTION_LOGGER_INSPECT_OPTIONS), + }; +} diff --git a/core-libs/setup/ssr/logger/index.ts b/core-libs/setup/ssr/logger/index.ts new file mode 100644 index 00000000000..4955ce207da --- /dev/null +++ b/core-libs/setup/ssr/logger/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './get-logger-inspect-options'; +export * from './loggers/index'; +export * from './services/index'; diff --git a/core-libs/setup/ssr/logger/loggers/default-express-server-logger.spec.ts b/core-libs/setup/ssr/logger/loggers/default-express-server-logger.spec.ts new file mode 100644 index 00000000000..ac799d06528 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/default-express-server-logger.spec.ts @@ -0,0 +1,468 @@ +jest.mock('@angular/core', () => { + return { + ...jest.requireActual('@angular/core'), + isDevMode: jest.fn(), + }; +}); + +import { isDevMode } from '@angular/core'; +import { Request } from 'express'; +import { DefaultExpressServerLogger } from './default-express-server-logger'; + +const request = { + originalUrl: 'test', + res: { + locals: { + cx: { + request: { + uuid: 'test', + timeReceived: new Date('2023-05-26'), + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01', + }, + }, + }, + }, + }, +} as unknown as Request; + +describe('DefaultExpressServerLogger', () => { + let logger: DefaultExpressServerLogger; + + beforeEach(() => { + logger = new DefaultExpressServerLogger(); + jest.useFakeTimers().setSystemTime(new Date('2023-05-26')); + }); + + describe('logging', () => { + it('should be defined', () => { + expect(logger).toBeDefined(); + }); + + describe('is not dev mode', () => { + beforeEach(() => { + (isDevMode as jest.Mock).mockReturnValue(false); + }); + + it('should log proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + logger.log('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "{"message":"test","context":{"timestamp":"2023-05-26T00:00:00.000Z","request":{"url":"test","uuid":"test","timeReceived":"2023-05-26T00:00:00.000Z","traceContext":{"version":"00","traceId":"d745f6735b44e81c0ae5410cb1fc8a0c","parentId":"1b527c3828976b39","traceFlags":"01"}}}}", + ] + `); + }); + + it('should warn proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'warn') + .mockImplementation(() => {}); + + logger.warn('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "{"message":"test","context":{"timestamp":"2023-05-26T00:00:00.000Z","request":{"url":"test","uuid":"test","timeReceived":"2023-05-26T00:00:00.000Z","traceContext":{"version":"00","traceId":"d745f6735b44e81c0ae5410cb1fc8a0c","parentId":"1b527c3828976b39","traceFlags":"01"}}}}", + ] + `); + }); + + it('should error proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + + logger.error('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "{"message":"test","context":{"timestamp":"2023-05-26T00:00:00.000Z","request":{"url":"test","uuid":"test","timeReceived":"2023-05-26T00:00:00.000Z","traceContext":{"version":"00","traceId":"d745f6735b44e81c0ae5410cb1fc8a0c","parentId":"1b527c3828976b39","traceFlags":"01"}}}}", + ] + `); + }); + + it('should info proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'info') + .mockImplementation(() => {}); + + logger.info('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "{"message":"test","context":{"timestamp":"2023-05-26T00:00:00.000Z","request":{"url":"test","uuid":"test","timeReceived":"2023-05-26T00:00:00.000Z","traceContext":{"version":"00","traceId":"d745f6735b44e81c0ae5410cb1fc8a0c","parentId":"1b527c3828976b39","traceFlags":"01"}}}}", + ] + `); + }); + + it('should debug proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'debug') + .mockImplementation(() => {}); + + logger.debug('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "{"message":"test","context":{"timestamp":"2023-05-26T00:00:00.000Z","request":{"url":"test","uuid":"test","timeReceived":"2023-05-26T00:00:00.000Z","traceContext":{"version":"00","traceId":"d745f6735b44e81c0ae5410cb1fc8a0c","parentId":"1b527c3828976b39","traceFlags":"01"}}}}", + ] + `); + }); + + it('should stringify Error instance passed as context', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + const error = new Error('test error'); + logger.log('test', { error }); + + expect(debugSpy.mock.lastCall?.[0]).toContain( + `"error":"Error: test error` // deliberate no closing double quote, because there goes the stacktrace + ); + }); + + it('should not abbreviate Error details nested at 10 levels', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + const error = new Error('test error', { + cause: { + level2: { + level3: { + level4: { + level5: { + level6: { + level7: { + level8: { + level9: { + level10string: 'level10string', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + logger.log('test', { error }); + + expect(debugSpy.mock.lastCall?.[0]).toContain( + `{ level10string: 'level10string' }` + ); + }); + }); + + describe('is dev mode', () => { + beforeEach(() => { + (isDevMode as jest.Mock).mockReturnValue(true); + }); + + it('should log proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + logger.log('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: 'test', + context: { + timestamp: '2023-05-26T00:00:00.000Z', + request: { + url: 'test', + uuid: 'test', + timeReceived: 2023-05-26T00:00:00.000Z, + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01' + } + } + } +}", +] +`); + }); + + it('should warn proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'warn') + .mockImplementation(() => {}); + + logger.warn('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: 'test', + context: { + timestamp: '2023-05-26T00:00:00.000Z', + request: { + url: 'test', + uuid: 'test', + timeReceived: 2023-05-26T00:00:00.000Z, + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01' + } + } + } +}", +] +`); + }); + + it('should error proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'error') + .mockImplementation(() => {}); + + logger.error('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: 'test', + context: { + timestamp: '2023-05-26T00:00:00.000Z', + request: { + url: 'test', + uuid: 'test', + timeReceived: 2023-05-26T00:00:00.000Z, + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01' + } + } + } +}", +] +`); + }); + + it('should info proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'info') + .mockImplementation(() => {}); + + logger.info('test', { request }); + + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: 'test', + context: { + timestamp: '2023-05-26T00:00:00.000Z', + request: { + url: 'test', + uuid: 'test', + timeReceived: 2023-05-26T00:00:00.000Z, + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01' + } + } + } +}", +] +`); + }); + + it('should debug proper shape of the JSON', () => { + const debugSpy = jest + .spyOn(console, 'debug') + .mockImplementation(() => {}); + + logger.debug('test', { request }); + expect(debugSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: 'test', + context: { + timestamp: '2023-05-26T00:00:00.000Z', + request: { + url: 'test', + uuid: 'test', + timeReceived: 2023-05-26T00:00:00.000Z, + traceContext: { + version: '00', + traceId: 'd745f6735b44e81c0ae5410cb1fc8a0c', + parentId: '1b527c3828976b39', + traceFlags: '01' + } + } + } +}", +] +`); + }); + + it('should stringify Error instance passed as context', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + const error = new Error('test error'); + logger.log('test', { error }); + + expect(debugSpy.mock.lastCall?.[0]).toContain( + `error: Error: test error` + ); + }); + + it('should not abbreviate Error details nested at 10 levels', () => { + const debugSpy = jest + .spyOn(console, 'log') + .mockImplementation(() => {}); + + const error = new Error('test error', { + cause: { + level2: { + level3: { + level4: { + level5: { + level6: { + level7: { + level8: { + level9: { + level10string: 'level10string', + }, + }, + }, + }, + }, + }, + }, + }, + }, + }); + logger.log('test', { error }); + + expect(debugSpy.mock.lastCall?.[0]).toContain( + // Note: The "error" property is already nested within the "context" property, + // That's why effectively 9 levels of nesting inside the Error object are shown in the output + `level9: [Object]` + ); + }); + }); + }); + + describe('create log message', () => { + it('should return message without request', () => { + const logMessage = logger['stringifyWithContext']('test', {}); + + expect(logMessage).not.toContain('request'); + }); + + it('should return message with request', () => { + const logMessage = logger['stringifyWithContext']('test', { + request, + }); + + expect(logMessage).toContain('request'); + }); + }); + + describe('map context', () => { + it('should return context without request', () => { + const context = logger['mapContext']({ + options: {}, + }); + + expect(context).toMatchInlineSnapshot(` + { + "options": {}, + "timestamp": "2023-05-26T00:00:00.000Z", + } + `); + }); + + it('should return context with request', () => { + const context = logger['mapContext']({ request }); + + expect(context).toMatchInlineSnapshot(` + { + "request": { + "timeReceived": 2023-05-26T00:00:00.000Z, + "traceContext": { + "parentId": "1b527c3828976b39", + "traceFlags": "01", + "traceId": "d745f6735b44e81c0ae5410cb1fc8a0c", + "version": "00", + }, + "url": "test", + "uuid": "test", + }, + "timestamp": "2023-05-26T00:00:00.000Z", + } + `); + }); + }); + + describe('map request', () => { + it('should return mapped request', () => { + const mappedRequest = logger['mapRequest'](request); + + expect(mappedRequest).toMatchInlineSnapshot(` + { + "timeReceived": 2023-05-26T00:00:00.000Z, + "traceContext": { + "parentId": "1b527c3828976b39", + "traceFlags": "01", + "traceId": "d745f6735b44e81c0ae5410cb1fc8a0c", + "version": "00", + }, + "url": "test", + "uuid": "test", + } + `); + }); + + it('should return mapped request without traceContext prop if traceparent is not available', () => { + const requestWithoutTraceContext = { + originalUrl: 'test', + res: { + locals: { + cx: { + request: { + uuid: 'test', + timeReceived: new Date('2023-05-26'), + }, + }, + }, + }, + } as unknown as Request; + + const mappedRequest = logger['mapRequest'](requestWithoutTraceContext); + + expect(mappedRequest).toMatchInlineSnapshot(` + { + "timeReceived": 2023-05-26T00:00:00.000Z, + "url": "test", + "uuid": "test", + } + `); + }); + }); +}); diff --git a/core-libs/setup/ssr/logger/loggers/default-express-server-logger.ts b/core-libs/setup/ssr/logger/loggers/default-express-server-logger.ts new file mode 100644 index 00000000000..35d1282d3c1 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/default-express-server-logger.ts @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isDevMode } from '@angular/core'; +import { Request } from 'express'; +import { formatWithOptions } from 'node:util'; +import { getRequestContext } from '../../optimized-engine/request-context'; +import { getLoggerInspectOptions } from '../get-logger-inspect-options'; +import { + ExpressServerLogger, + ExpressServerLoggerContext, +} from './express-server-logger'; + +/** + * + * Default logger used in SSR (ExpressJS) to enhance logs visible e.g. in monitoring tools e.g. Kibana. + * It outputs a JSON with properties "message" and "context", + * which contains a "timestamp" and details of the "request" ("url", "uuid", "timeReceived") + * + * The output "context" JSON will contain also a property "traceContext" + * with "traceId", "parentId", "version" and "traceFlags", + * if only the given request has the special header "traceparent" (specifed in + * the "W3C TraceContext" document. See https://www.w3.org/TR/trace-context/#traceparent-header ). + */ +export class DefaultExpressServerLogger implements ExpressServerLogger { + log(message: string, context: ExpressServerLoggerContext): void { + /* eslint-disable-next-line no-console */ + console.log(this.stringifyWithContext(message, context)); + } + warn(message: string, context: ExpressServerLoggerContext): void { + /* eslint-disable-next-line no-console */ + console.warn(this.stringifyWithContext(message, context)); + } + error(message: string, context: ExpressServerLoggerContext): void { + /* eslint-disable-next-line no-console */ + console.error(this.stringifyWithContext(message, context)); + } + info(message: string, context: ExpressServerLoggerContext): void { + /* eslint-disable-next-line no-console */ + console.info(this.stringifyWithContext(message, context)); + } + debug(message: string, context: ExpressServerLoggerContext): void { + /* eslint-disable-next-line no-console */ + console.debug(this.stringifyWithContext(message, context)); + } + + /** + * Converts a message and an ExpressServerLoggerContext object into a single JSON string containing both pieces of information, which can be used for logging purposes. + * + * In prod mode, it prints a single line stringified JSON that can be easily parsed by + * monitoring tools e.g. Kibana. + * + * In dev mode, it prints human readable multi-line stringified JSON with indentation. + * + * @protected + * @param message - The message to be included in the log entry. + * @param context - The context object associated with the log entry. + * @returns A JSON string containing both the message and context information, suitable for logging. + */ + protected stringifyWithContext( + message: string, + context: ExpressServerLoggerContext + ): string { + const logObject = { message, context: this.mapContext(context) }; + + if (isDevMode()) { + // In dev mode, we want a *human-readable* string representation of the log object + // that can be printed in the console. + return formatWithOptions(getLoggerInspectOptions(), logObject); + } + + // In prod mode, we want a *single-line* JSON which is machine-readable - easy to parse for monitoring tools. + // The monitoring tools can then decide how to display in their UI the JSON's properties. + return JSON.stringify( + logObject, + /** + * Replaces Error instances with their stringified representation. + */ + (_key: string, value: any) => { + return value instanceof Error ? this.stringifyError(value) : value; + } + ); + } + + /** + * Map the context for the ExpressServerLogger + * + * @protected + * @param context - The logging context object to be mapped + * @returns - The mapped context with timestamp and request (if available) + */ + protected mapContext( + context: ExpressServerLoggerContext + ): Record { + const timestamp = new Date().toISOString(); + const outputContext = { timestamp, ...context }; + + if (context.request) { + Object.assign(outputContext, { + request: this.mapRequest(context.request), + }); + } + + return outputContext; + } + + /** + * Maps a Request object into a JavaScript object with specific properties. + * + * @protected + * @param request - An Express Request object. + * @returns - A mapped request object. By default, it contains only "url", a random "uuid" and "timeReceived" of the request. + */ + protected mapRequest(request: Request): Record { + return { + url: request.originalUrl, + ...getRequestContext(request), + }; + } + + /** + * Converts an Error object into a detailed string (with message, stack, cause, etc.). + * + * Otherwise, the Error instance would not be visible in the logs after passing through `JSON.stringify()`. + * For more, see https://stackoverflow.com/a/50738205/11734692 + */ + protected stringifyError(error: unknown): string { + return formatWithOptions(getLoggerInspectOptions(), error); + } +} diff --git a/core-libs/setup/ssr/logger/loggers/express-server-logger.ts b/core-libs/setup/ssr/logger/loggers/express-server-logger.ts new file mode 100644 index 00000000000..048e9f1cd1d --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/express-server-logger.ts @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { InjectionToken } from '@angular/core'; +import { Request } from 'express'; + +/** + * ExpressServerLoggerContext is used for log message in server side rendering. + * It contains optional request object and additional properties that can be used in log message. + */ +export interface ExpressServerLoggerContext { + request?: Request; + [_key: string]: any; +} + +/** + * ExpressServerLogger is used for log message in server side rendering. + * It contains methods for logging messages with different levels. Each method accepts message and context. + * Context is an object that contains additional information about the log message. + * + * @property log - logs message with level "log" + * @property warn - logs message with level "warn" + * @property error - logs message with level "error" + * @property info - logs message with level "info" + * @property debug - logs message with level "debug" + */ +export interface ExpressServerLogger { + log(message: string, context: ExpressServerLoggerContext): void; + warn(message: string, context: ExpressServerLoggerContext): void; + error(message: string, context: ExpressServerLoggerContext): void; + info(message: string, context: ExpressServerLoggerContext): void; + debug(message: string, context: ExpressServerLoggerContext): void; +} + +/** + * Injection token for ExpressServerLogger used for log message in server side rendering. + * EXPRESS_SERVER_LOGGER is used to provide proper logger to LoggerService instance. + * + * Spartacus is providing one type of server logger: + * - DefaultExpressServerLogger - default implementation used for logging contextual messages in SSR. + * + */ +export const EXPRESS_SERVER_LOGGER = new InjectionToken( + 'EXPRESS_SERVER_LOGGER' +); diff --git a/core-libs/setup/ssr/logger/loggers/index.ts b/core-libs/setup/ssr/logger/loggers/index.ts new file mode 100644 index 00000000000..57e7bb6e640 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './default-express-server-logger'; +export * from './express-server-logger'; diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.spec.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.spec.ts new file mode 100644 index 00000000000..438c1dca904 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.spec.ts @@ -0,0 +1,8 @@ +import { InvalidTraceparentFormatError } from './invalid-traceparent-format-error'; + +describe('InvalidTraceparentFormatError', () => { + it('should be an instance of InstantiationError', () => { + const error = new InvalidTraceparentFormatError(); + expect(error).toBeInstanceOf(InvalidTraceparentFormatError); + }); +}); diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.ts new file mode 100644 index 00000000000..8cf7c21d546 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-format-error.ts @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Error thrown when the traceparent header has an invalid format. + */ +export class InvalidTraceparentFormatError extends Error { + constructor() { + super('Traceparent header has invalid format.'); + } +} diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.spec.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.spec.ts new file mode 100644 index 00000000000..c0b10ff396c --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.spec.ts @@ -0,0 +1,15 @@ +import { InvalidTraceparentLengthError } from './invalid-traceparent-length-error'; + +describe('InvalidTraceparentFormatError', () => { + it('should be an instance of InstantiationError', () => { + const error = new InvalidTraceparentLengthError(0); + expect(error).toBeInstanceOf(InvalidTraceparentLengthError); + }); + + it('should have the correct message', () => { + const error = new InvalidTraceparentLengthError(20); + expect(error.message).toBe( + `Traceparent header has invalid length: ${20}. Expected 55 characters.` + ); + }); +}); diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.ts new file mode 100644 index 00000000000..6fe41070cee --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/errors/invalid-traceparent-length-error.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Error thrown when the traceparent header has an invalid length. + * @param traceparentLength The length of the traceparent header. + */ +export class InvalidTraceparentLengthError extends Error { + constructor(traceparentLength: number) { + super( + `Traceparent header has invalid length: ${traceparentLength}. Expected 55 characters.` + ); + } +} diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.spec.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.spec.ts new file mode 100644 index 00000000000..29602dd808c --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.spec.ts @@ -0,0 +1,60 @@ +import { InvalidTraceparentFormatError } from './errors/invalid-traceparent-format-error'; +import { InvalidTraceparentLengthError } from './errors/invalid-traceparent-length-error'; +import { parseTraceparent } from './parse-traceparent'; + +describe('parseTraceparent', () => { + it('should return null if traceparent is not a string', () => { + const result = parseTraceparent({} as string); + + expect(result).toBeUndefined(); + }); + + it('should throw an error if traceparent is not 55 characters long', () => { + const shorterString = '0'.repeat(54); + const longerString = '0'.repeat(56); + + expect(() => parseTraceparent(shorterString)).toThrow( + InvalidTraceparentLengthError + ); + expect(() => parseTraceparent(longerString)).toThrow( + InvalidTraceparentLengthError + ); + }); + + it('should throw an error if traceparent does not match the W3C pattern', () => { + const additionalChar = 'x'; + const unsupportedFormats: string[] = [ + `00${additionalChar}-af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01`, + `0-${additionalChar}0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01`, + `00-0af7651916cd43dd8448eb211c80319-${additionalChar}b7ad6b7169203331-01`, + `00-0af7651916cd43dd8448eb211c80319c-b7ad6b716920333-${additionalChar}01`, + `00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331${additionalChar}-1`, + `0010af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01`, + `00-0af7651916cd43dd8448eb211c80319c1b7ad6b7169203331-01`, + `00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331101`, + `0010af7651916cd43dd8448eb211c80319c1b7ad6b7169203331101`, + ]; + + unsupportedFormats.forEach((traceparent) => { + expect(() => parseTraceparent(traceparent)).toThrow( + InvalidTraceparentFormatError + ); + }); + }); + + it('should parse traceparent correctly', () => { + const traceparent = + '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; + + const result = parseTraceparent(traceparent); + + expect(result).toMatchInlineSnapshot(` + { + "parentId": "b7ad6b7169203331", + "traceFlags": "01", + "traceId": "0af7651916cd43dd8448eb211c80319c", + "version": "00", + } + `); + }); +}); diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.ts new file mode 100644 index 00000000000..fcc44df3db0 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/parse-traceparent.ts @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { InvalidTraceparentFormatError } from './errors/invalid-traceparent-format-error'; +import { InvalidTraceparentLengthError } from './errors/invalid-traceparent-length-error'; +import { W3cTraceContext } from './w3c-trace-context.model'; + +const HEXDIGLC = '[0-9a-f]'; // https://www.w3.org/TR/trace-context/#traceparent-header-field-values +const VERSION = HEXDIGLC + '{2}'; // 2 HEXDIGLC +const TRACE_ID = HEXDIGLC + '{32}'; // 32 HEXDIGLC +const PARENT_ID = HEXDIGLC + '{16}'; // 16 HEXDIGLC +const TRACE_FLAGS = HEXDIGLC + '{2}'; // 2 HEXDIGLC +const TRACEPARENT = [VERSION, TRACE_ID, PARENT_ID, TRACE_FLAGS].join('-'); // separated by dashes +const traceparentPattern = new RegExp('^' + TRACEPARENT + '$'); + +/** + * Maps traceparent header to object with properties version, traceId, spanId and traceFlags. + * Since `traceparent` header may be not attached to the request, the function returns undefined if the header is not provided. + * If the header is provided but has invalid format or length, the function throws an error. + * + * @param traceparent + * @returns Params of the traceparent header. + * + * @see https://www.w3.org/TR/trace-context/#traceparent-header-field-values + */ +export function parseTraceparent( + traceparent: string | undefined | null +): W3cTraceContext | undefined { + if (typeof traceparent !== 'string') { + return undefined; + } + + if (traceparent.length !== 55) { + throw new InvalidTraceparentLengthError(traceparent.length); + } + + if (!traceparentPattern.test(traceparent)) { + throw new InvalidTraceparentFormatError(); + } + + const [version, traceId, parentId, traceFlags] = traceparent.split('-'); + + return { version, traceId, parentId, traceFlags }; +} diff --git a/core-libs/setup/ssr/logger/loggers/w3c-trace-context/w3c-trace-context.model.ts b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/w3c-trace-context.model.ts new file mode 100644 index 00000000000..8c325ec6832 --- /dev/null +++ b/core-libs/setup/ssr/logger/loggers/w3c-trace-context/w3c-trace-context.model.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * W3cTraceContext is used for log message in server side rendering. + * It contains values of traceparent header. + * + * @property version - version of traceparent header + * @property traceId - traceId of traceparent header + * @property parentId - spanId of traceparent header + * @property traceFlags - traceFlags of traceparent header + * + * @see https://www.w3.org/TR/trace-context/#traceparent-header-field + */ +export interface W3cTraceContext { + version: string; + traceId: string; + parentId: string; + traceFlags: string; +} diff --git a/core-libs/setup/ssr/logger/services/express-logger.service.spec.ts b/core-libs/setup/ssr/logger/services/express-logger.service.spec.ts new file mode 100644 index 00000000000..0f888d88f98 --- /dev/null +++ b/core-libs/setup/ssr/logger/services/express-logger.service.spec.ts @@ -0,0 +1,139 @@ +jest.mock('@angular/core', () => { + return { + ...jest.requireActual('@angular/core'), + isDevMode: jest.fn(), + }; +}); +import { isDevMode } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { Request } from 'express'; +import { REQUEST } from '../../tokens/express.tokens'; +import { EXPRESS_SERVER_LOGGER, ExpressServerLogger } from '../loggers'; +import { ExpressLoggerService } from './express-logger.service'; + +const mockRequest: Partial = { url: 'test/url' }; + +class MockServerLogger implements ExpressServerLogger { + log = jest.fn(); + warn = jest.fn(); + error = jest.fn(); + info = jest.fn(); + debug = jest.fn(); +} + +describe('ExpressLoggerService', () => { + let request: Request; + let logger: ExpressServerLogger; + let loggerService: ExpressLoggerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ExpressLoggerService, + { provide: REQUEST, useValue: mockRequest }, + { provide: EXPRESS_SERVER_LOGGER, useClass: MockServerLogger }, + ], + }); + + request = TestBed.inject(REQUEST); + logger = TestBed.inject(EXPRESS_SERVER_LOGGER); + loggerService = TestBed.inject(ExpressLoggerService); + }); + + describe('log', () => { + it('should log', () => { + const log = jest.spyOn(logger, 'log'); + + loggerService.log('test'); + + expect(log).toHaveBeenCalledWith('test', { request }); + }); + + it('should warn', () => { + const warn = jest.spyOn(logger, 'warn'); + + loggerService.warn('test'); + + expect(warn).toHaveBeenCalledWith('test', { request }); + }); + + it('should error', () => { + const error = jest.spyOn(logger, 'error'); + + loggerService.error('test'); + + expect(error).toHaveBeenCalledWith('test', { request }); + }); + + it('should info', () => { + const info = jest.spyOn(logger, 'info'); + + loggerService.info('test'); + + expect(info).toHaveBeenCalledWith('test', { request }); + }); + + it('should debug', () => { + const debug = jest.spyOn(logger, 'debug'); + + loggerService.debug('test'); + + expect(debug).toHaveBeenCalledWith('test', { request }); + }); + }); + + describe('formatLogMessage', () => { + it('should format log message', () => { + const result = loggerService['formatLogMessage']('some %s', 'test'); + + expect(result).toEqual('some test'); + }); + + describe('should format log message with options', () => { + describe('in PROD mode', () => { + beforeEach(() => { + (isDevMode as jest.Mock).mockReturnValue(false); + }); + + it('should NOT break properties to separate lines, even if output would exceed 80 characters', () => { + const a_81_times = 'a'.repeat(81); + const b_81_times = 'b'.repeat(81); + + const message = { + a: a_81_times, + b: b_81_times, + }; + const result = loggerService['formatLogMessage'](message); + + expect(result).toMatchInlineSnapshot( + `"{ a: '${a_81_times}', b: '${b_81_times}' }"` + ); + }); + }); + }); + + describe('in DEV mode', () => { + beforeEach(() => { + (isDevMode as jest.Mock).mockReturnValue(true); + }); + + it('should break properties to separate lines, even if output would exceed 80 characters', () => { + const a_81_times = 'a'.repeat(81); + const b_81_times = 'b'.repeat(81); + + const message = { + a: a_81_times, + b: b_81_times, + }; + const result = loggerService['formatLogMessage'](message); + + expect(result).toMatchInlineSnapshot(` +"{ + a: '${a_81_times}', + b: '${b_81_times}' +}" +`); + }); + }); + }); +}); diff --git a/core-libs/setup/ssr/logger/services/express-logger.service.ts b/core-libs/setup/ssr/logger/services/express-logger.service.ts new file mode 100644 index 00000000000..786049f983f --- /dev/null +++ b/core-libs/setup/ssr/logger/services/express-logger.service.ts @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable, inject } from '@angular/core'; +import { LoggerService } from '@spartacus/core'; +import { formatWithOptions } from 'util'; +import { REQUEST } from '../../tokens/express.tokens'; +import { getLoggerInspectOptions } from '../get-logger-inspect-options'; +import { EXPRESS_SERVER_LOGGER } from '../loggers'; + +/** + * Custom `LoggerService` used in ExpressJS. + * + * It converts the input arguments to a final string message similar as the native `console` + * does (using the native function `format` from `node:util`) and passes this message + * to a concrete server logger, used in ExpressJS. + * + * Besides the message, it also passes the current `request` of ExpressJS as an additional + * context to the concrete server logger. + */ +@Injectable({ providedIn: 'root' }) +export class ExpressLoggerService implements LoggerService { + request = inject(REQUEST); + serverLogger = inject(EXPRESS_SERVER_LOGGER); + + log(...args: Parameters): void { + this.serverLogger.log(this.formatLogMessage(...args), { + request: this.request, + }); + } + warn(...args: Parameters): void { + this.serverLogger.warn(this.formatLogMessage(...args), { + request: this.request, + }); + } + error(...args: Parameters): void { + this.serverLogger.error(this.formatLogMessage(...args), { + request: this.request, + }); + } + info(...args: Parameters): void { + this.serverLogger.info(this.formatLogMessage(...args), { + request: this.request, + }); + } + debug(...args: Parameters): void { + this.serverLogger.debug(this.formatLogMessage(...args), { + request: this.request, + }); + } + + protected formatLogMessage(message?: any, ...optionalParams: any[]): string { + return formatWithOptions( + getLoggerInspectOptions(), + message, + ...optionalParams + ); + } +} diff --git a/core-libs/setup/ssr/logger/services/index.ts b/core-libs/setup/ssr/logger/services/index.ts new file mode 100644 index 00000000000..5344e5e065c --- /dev/null +++ b/core-libs/setup/ssr/logger/services/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './express-logger.service'; +export * from './prerendering-logger.service'; +export * from './server-logger-service-factory'; diff --git a/core-libs/setup/ssr/logger/services/prerendering-logger.service.spec.ts b/core-libs/setup/ssr/logger/services/prerendering-logger.service.spec.ts new file mode 100644 index 00000000000..d1b2e1b75f8 --- /dev/null +++ b/core-libs/setup/ssr/logger/services/prerendering-logger.service.spec.ts @@ -0,0 +1,57 @@ +import { TestBed } from '@angular/core/testing'; +import { ExpressLoggerService } from './express-logger.service'; +import { PrerenderingLoggerService } from './prerendering-logger.service'; + +describe('PrerenderingLoggerService', () => { + let loggerService: PrerenderingLoggerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ExpressLoggerService], + }); + + loggerService = TestBed.inject(PrerenderingLoggerService); + }); + + describe('log', () => { + it('should log', () => { + const log = jest.spyOn(console, 'log').mockImplementation(() => {}); + + loggerService.log('test'); + + expect(log).toHaveBeenCalledWith('test'); + }); + + it('should warn', () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + + loggerService.warn('test'); + + expect(warn).toHaveBeenCalledWith('test'); + }); + + it('should error', () => { + const error = jest.spyOn(console, 'error').mockImplementation(() => {}); + + loggerService.error('test'); + + expect(error).toHaveBeenCalledWith('test'); + }); + + it('should info', () => { + const info = jest.spyOn(console, 'info').mockImplementation(() => {}); + + loggerService.info('test'); + + expect(info).toHaveBeenCalledWith('test'); + }); + + it('should debug', () => { + const debug = jest.spyOn(console, 'debug').mockImplementation(() => {}); + + loggerService.debug('test'); + + expect(debug).toHaveBeenCalledWith('test'); + }); + }); +}); diff --git a/core-libs/setup/ssr/logger/services/prerendering-logger.service.ts b/core-libs/setup/ssr/logger/services/prerendering-logger.service.ts new file mode 100644 index 00000000000..c719c8e45d1 --- /dev/null +++ b/core-libs/setup/ssr/logger/services/prerendering-logger.service.ts @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable } from '@angular/core'; +import { LoggerService } from '@spartacus/core'; + +/** + * Custom `LoggerService` used in pre-rendering in the server environment. + * + * It simply forwards the arguments to the native `console` methods. + */ +@Injectable({ + providedIn: 'root', +}) +export class PrerenderingLoggerService extends LoggerService { + log(...args: Parameters): void { + /* eslint-disable-next-line no-console */ + console.log(...args); + } + warn(...args: Parameters): void { + /* eslint-disable-next-line no-console */ + console.warn(...args); + } + error(...args: Parameters): void { + /* eslint-disable-next-line no-console */ + console.error(...args); + } + info(...args: Parameters): void { + /* eslint-disable-next-line no-console */ + console.info(...args); + } + debug(...args: Parameters): void { + /* eslint-disable-next-line no-console */ + console.debug(...args); + } +} diff --git a/core-libs/setup/ssr/logger/services/server-logger-service-factory.spec.ts b/core-libs/setup/ssr/logger/services/server-logger-service-factory.spec.ts new file mode 100644 index 00000000000..12a3ab28daf --- /dev/null +++ b/core-libs/setup/ssr/logger/services/server-logger-service-factory.spec.ts @@ -0,0 +1,51 @@ +import { TestBed } from '@angular/core/testing'; +import { LoggerService } from '@spartacus/core'; +import { REQUEST } from '../../tokens/express.tokens'; +import { EXPRESS_SERVER_LOGGER, ExpressServerLogger } from '../loggers'; +import { ExpressLoggerService } from './express-logger.service'; +import { PrerenderingLoggerService } from './prerendering-logger.service'; +import { serverLoggerServiceFactory } from './server-logger-service-factory'; + +class MockExpressServerLogger implements ExpressServerLogger { + log = jest.fn(); + warn = jest.fn(); + error = jest.fn(); + info = jest.fn(); + debug = jest.fn(); +} + +describe('serverLoggerServiceFactory', () => { + it('should return ExpressLoggerService if REQUEST is available', () => { + TestBed.configureTestingModule({ + providers: [ + { provide: REQUEST, useValue: {} }, + { + provide: EXPRESS_SERVER_LOGGER, + useValue: new MockExpressServerLogger(), + }, + { + provide: LoggerService, + useFactory: serverLoggerServiceFactory, + }, + ], + }); + expect(TestBed.inject(LoggerService)).toBeInstanceOf(ExpressLoggerService); + }); + it('should return PrerenderingLoggerService if REQUEST is not available', () => { + TestBed.configureTestingModule({ + providers: [ + { + provide: EXPRESS_SERVER_LOGGER, + useValue: new MockExpressServerLogger(), + }, + { + provide: LoggerService, + useFactory: serverLoggerServiceFactory, + }, + ], + }); + expect(TestBed.inject(LoggerService)).toBeInstanceOf( + PrerenderingLoggerService + ); + }); +}); diff --git a/core-libs/setup/ssr/logger/services/server-logger-service-factory.ts b/core-libs/setup/ssr/logger/services/server-logger-service-factory.ts new file mode 100644 index 00000000000..aa70e6934d8 --- /dev/null +++ b/core-libs/setup/ssr/logger/services/server-logger-service-factory.ts @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { inject } from '@angular/core'; +import { REQUEST } from '../../tokens/express.tokens'; +import { ExpressLoggerService } from './express-logger.service'; +import { PrerenderingLoggerService } from './prerendering-logger.service'; + +export const serverLoggerServiceFactory = () => { + const isExpress = inject(REQUEST, { optional: true }) !== null; + return isExpress + ? inject(ExpressLoggerService) + : inject(PrerenderingLoggerService); +}; diff --git a/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.spec.ts b/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.spec.ts new file mode 100644 index 00000000000..38e14228007 --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.spec.ts @@ -0,0 +1,93 @@ +import { DefaultExpressServerLogger } from '../logger'; +import { getLoggableSsrOptimizationOptions } from './get-loggable-ssr-optimization-options'; +import { SsrOptimizationOptions } from './ssr-optimization-options'; + +class MockLogger extends DefaultExpressServerLogger { + constructor() { + super(); + } +} + +describe('getLoggableSsrOptimizationOptions', () => { + const ssrOptions: SsrOptimizationOptions = { + concurrency: 10, + timeout: 3000, + forcedSsrTimeout: 60000, + maxRenderTime: 300000, + reuseCurrentRendering: true, + debug: false, + }; + + it('should return options in the same shape as provided', () => { + expect(getLoggableSsrOptimizationOptions(ssrOptions)) + .toMatchInlineSnapshot(` + { + "concurrency": 10, + "debug": false, + "forcedSsrTimeout": 60000, + "maxRenderTime": 300000, + "reuseCurrentRendering": true, + "timeout": 3000, + } + `); + }); + + it('should return constructor name if property is not a plain object', () => { + expect( + getLoggableSsrOptimizationOptions({ + ...ssrOptions, + logger: new MockLogger(), + }) + ).toMatchInlineSnapshot(` + { + "concurrency": 10, + "debug": false, + "forcedSsrTimeout": 60000, + "logger": "MockLogger", + "maxRenderTime": 300000, + "reuseCurrentRendering": true, + "timeout": 3000, + } + `); + }); + + it('should not return constructor name if property is a plain object', () => { + expect( + getLoggableSsrOptimizationOptions({ + ...ssrOptions, + somePlainObject: { config: 'value' }, + } as SsrOptimizationOptions) + ).toMatchInlineSnapshot(` + { + "concurrency": 10, + "debug": false, + "forcedSsrTimeout": 60000, + "maxRenderTime": 300000, + "reuseCurrentRendering": true, + "somePlainObject": { + "config": "value", + }, + "timeout": 3000, + } + `); + }); + + it('should change function to string if property is a function', () => { + expect( + getLoggableSsrOptimizationOptions({ + ...ssrOptions, + renderKeyResolver: (req: any) => req.url, + }) + ).toMatchInlineSnapshot(` + { + "concurrency": 10, + "debug": false, + "forcedSsrTimeout": 60000, + "maxRenderTime": 300000, + "renderKeyResolver": "(req) => req.url", + "reuseCurrentRendering": true, + "timeout": 3000, + } + `); + }); +}); diff --git a/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.ts b/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.ts new file mode 100644 index 00000000000..09e93aba420 --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/get-loggable-ssr-optimization-options.ts @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SsrOptimizationOptions } from './ssr-optimization-options'; + +/** + * Helper function that maps optimization options to primitive values. + * This is useful for logging and monitoring purposes. + * + * @param value optimization options that should be logged + * @returns options containing only primitive values that are easier to read by developers and monitoring tools + */ +export const getLoggableSsrOptimizationOptions = ( + value: SsrOptimizationOptions +) => { + const newValue: Record = { ...value }; + Object.keys(value).forEach((key) => { + if (isClassInstance(newValue[key])) { + newValue[key] = newValue[key].constructor?.name; + } + if (typeof newValue[key] === 'function') { + newValue[key] = newValue[key].toString(); + } + }); + return newValue; +}; + +/** + * Checks if the given value is a class instance, + * but not a plain Object. + * + * @private + */ +const isClassInstance = (value: any): boolean => { + return typeof value === 'object' && value.constructor !== Object; +}; diff --git a/core-libs/setup/ssr/optimized-engine/index.ts b/core-libs/setup/ssr/optimized-engine/index.ts index f8b2a4f066c..cfa41066a42 100644 --- a/core-libs/setup/ssr/optimized-engine/index.ts +++ b/core-libs/setup/ssr/optimized-engine/index.ts @@ -1,3 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './optimized-ssr-engine'; export * from './rendering-cache'; +export * from './rendering-cache.model'; +export * from './rendering-strategy-resolver'; +export * from './rendering-strategy-resolver-options'; +export { RequestContext, getRequestContext } from './request-context'; export * from './ssr-optimization-options'; diff --git a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts index 739a7fffda5..26fb3c402cd 100644 --- a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts +++ b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts @@ -1,17 +1,33 @@ +/// + import { fakeAsync, flush, tick } from '@angular/core/testing'; -import { Application, Request } from 'express'; +import { Application, Request, Response } from 'express'; import { IncomingHttpHeaders } from 'http'; import { Socket } from 'net'; import { NgExpressEngineInstance } from '../engine-decorator/ng-express-engine-decorator'; +import { ExpressServerLogger, ExpressServerLoggerContext } from '../logger'; import { OptimizedSsrEngine, SsrCallbackFn } from './optimized-ssr-engine'; import { RenderingStrategy, SsrOptimizationOptions, + defaultSsrOptimizationOptions, } from './ssr-optimization-options'; const defaultRenderTime = 100; const host = 'my.shop.com'; +jest.mock('fs', () => ({ + readFileSync: () => '', +})); +const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); +const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + +class MockExpressServerLogger implements Partial { + log(message: string, context: ExpressServerLoggerContext): void { + console.log(message, context); + } +} + /** * Helper class to easily create and test engine wrapper against mocked engine. * @@ -24,7 +40,7 @@ const host = 'my.shop.com'; */ class TestEngineRunner { /** Accumulates html output for engine runs */ - renders: string[] = []; + renders: (string | Error)[] = []; /** Accumulates response parameters for engine runs */ responseParams: object[] = []; @@ -33,7 +49,11 @@ class TestEngineRunner { optimizedSsrEngine: OptimizedSsrEngine; engineInstance: NgExpressEngineInstance; - constructor(options: SsrOptimizationOptions, renderTime?: number) { + constructor( + options: SsrOptimizationOptions, + renderTime?: number, + params?: { withError?: boolean } + ) { // mocked engine instance that will render test output in 100 milliseconds const engineInstanceMock = ( filePath: string, @@ -41,17 +61,31 @@ class TestEngineRunner { callback: SsrCallbackFn ) => { setTimeout(() => { - callback(undefined, `${filePath}-${this.renderCount++}`); + const result = `${filePath}-${this.renderCount++}`; + + if (params?.withError) { + const err = new Error(result); + callback(err, undefined); + } else { + callback(undefined, result); + } }, renderTime ?? defaultRenderTime); }; - this.optimizedSsrEngine = new OptimizedSsrEngine( - engineInstanceMock, - options - ); + this.optimizedSsrEngine = new OptimizedSsrEngine(engineInstanceMock, { + ...options, + }); this.engineInstance = this.optimizedSsrEngine.engineInstance; } + /** Create engine that results with error during render */ + static withError( + options: SsrOptimizationOptions, + renderTime = defaultRenderTime + ): TestEngineRunner { + return new TestEngineRunner(options, renderTime, { withError: true }); + } + /** Run request against the engine. The result will be stored in rendering property. */ request( url: string, @@ -80,14 +114,15 @@ class TestEngineRunner { }, app, connection: >{}, - }, - res: >{ - set: (key: string, value: any) => (response[key] = value), + res: >{ + set: (key: string, value: any) => (response[key] = value), + locals: {}, + }, }, }; - this.engineInstance(url, optionsMock, (_, html): void => { - this.renders.push(html ?? ''); + this.engineInstance(url, optionsMock, (error, html): void => { + this.renders.push(html ?? error ?? ''); this.responseParams.push(response); }); @@ -104,15 +139,137 @@ const getCurrentConcurrency = ( }; describe('OptimizedSsrEngine', () => { + describe('SsrOptimizationOptions', () => { + it('should use the defaults if an empty object is provided', () => { + const engineRunner = new TestEngineRunner({}); + expect(engineRunner.optimizedSsrEngine['ssrOptions']).toEqual( + defaultSsrOptimizationOptions + ); + }); + it('should override the defaults', () => { + const engineRunner = new TestEngineRunner({ + reuseCurrentRendering: false, + }); + expect(engineRunner.optimizedSsrEngine['ssrOptions']).toEqual({ + ...defaultSsrOptimizationOptions, + reuseCurrentRendering: false, + }); + }); + }); + + describe('logOptions', () => { + let dateSpy: jest.SpyInstance; + + beforeEach(() => { + const mockDate = new Date('2023-01-01'); + dateSpy = jest + .spyOn(global, 'Date') + .mockImplementationOnce(() => mockDate); + }); + + afterEach(() => { + dateSpy.mockReset(); + }); + + afterAll(() => { + dateSpy.mockRestore(); + }); + + it('should log the provided options', () => { + new TestEngineRunner({ + timeout: 50, + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + }); + + expect(consoleLogSpy.mock.lastCall).toMatchInlineSnapshot(` +[ + "{ + message: '[spartacus] SSR optimization engine initialized', + context: { + timestamp: '2023-01-01T00:00:00.000Z', + options: { + cacheSize: 3000, + concurrency: 10, + timeout: 50, + forcedSsrTimeout: 60000, + maxRenderTime: 300000, + reuseCurrentRendering: true, + renderingStrategyResolver: '() => ssr_optimization_options_1.RenderingStrategy.ALWAYS_SSR', + logger: 'DefaultExpressServerLogger', + shouldCacheRenderingResult: '({ options, entry }) => !(options.ssrFeatureToggles?.avoidCachingErrors === true &&\\n' + + ' Boolean(entry.err))', + ssrFeatureToggles: { avoidCachingErrors: false } + } + } +}", +] +`); + }); + }); + + describe('rendering', () => { + it('should return rendered output if no errors', fakeAsync(() => { + const originalUrl = 'a'; + const engineRunner = new TestEngineRunner({}).request('a'); + + tick(200); + expect(engineRunner.renders).toEqual(['a-0']); + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringContaining( + `Request is resolved with the SSR rendering result (${originalUrl})` + ) + ); + })); + + it('should return error if rendering fails', fakeAsync(() => { + const originalUrl = 'a'; + const engineRunner = TestEngineRunner.withError({}).request('a'); + + tick(200); + expect(engineRunner.renders).toEqual([new Error('a-0')]); + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining( + `Request is resolved with the SSR rendering error (${originalUrl})` + ) + ); + })); + }); + + describe('rendering cache', () => { + it('should be initialized with default optimization options none of the custom options are provided', () => { + const engineRunner = new TestEngineRunner({}); + expect( + engineRunner.optimizedSsrEngine['renderingCache']['options'] + ).toEqual(defaultSsrOptimizationOptions); + }); + + it('should be initialized with the provided custom options', () => { + const engineRunner = new TestEngineRunner({ + cacheSize: 100, + ttl: 200, + }); + expect(engineRunner.optimizedSsrEngine['renderingCache']).toBeDefined(); + expect( + engineRunner.optimizedSsrEngine['renderingCache']['options'] + ).toEqual({ + ...defaultSsrOptimizationOptions, + cacheSize: 100, + ttl: 200, + }); + }); + }); + describe('timeout option', () => { it('should fallback to CSR if rendering exceeds timeout', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 50 }).request('a'); + tick(200); expect(engineRunner.renders).toEqual(['']); })); it('should return timed out render in the followup request', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 50 }).request('a'); + tick(200); expect(engineRunner.renders).toEqual(['']); @@ -122,12 +279,14 @@ describe('OptimizedSsrEngine', () => { it('should return render if rendering meets timeout', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 150 }).request('a'); + tick(200); expect(engineRunner.renders).toEqual(['a-0']); })); it('should fallback instantly if is set to 0', () => { const engineRunner = new TestEngineRunner({ timeout: 0 }).request('a'); + expect(engineRunner.renders).toEqual(['']); }); @@ -154,6 +313,7 @@ describe('OptimizedSsrEngine', () => { describe('no-store cache control header', () => { it('should be applied for a fallback', () => { const engineRunner = new TestEngineRunner({ timeout: 0 }).request('a'); + expect(engineRunner.renders).toEqual(['']); expect(engineRunner.responseParams).toEqual([ { 'Cache-Control': 'no-store' }, @@ -162,13 +322,14 @@ describe('OptimizedSsrEngine', () => { it('should not be applied for a render within time limit', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 200 }).request('a'); + tick(200); expect(engineRunner.renders).toEqual(['a-0']); expect(engineRunner.responseParams).toEqual([{}]); })); - it('should not be applied for a render served with next response', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 50 }).request('a'); + tick(200); engineRunner.request('a'); expect(engineRunner.renders).toEqual(['', 'a-0']); @@ -193,7 +354,6 @@ describe('OptimizedSsrEngine', () => { tick(200); expect(engineRunner.renders).toEqual(['a-0', 'a-1', 'a-2']); })); - it('should cache requests if enabled', fakeAsync(() => { const engineRunner = new TestEngineRunner({ cache: true, @@ -223,6 +383,148 @@ describe('OptimizedSsrEngine', () => { })); }); + describe('avoidCachingErrors option', () => { + describe('when using default shouldCacheRenderingResult', () => { + it('should not cache errors if `avoidCachingErrors` is set to true', fakeAsync(() => { + const engineRunner = TestEngineRunner.withError({ + cache: true, + ssrFeatureToggles: { + avoidCachingErrors: true, + }, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual([ + new Error('a-0'), + new Error('a-1'), + new Error('a-2'), + ]); + })); + + it('should cache errors if `avoidCachingErrors` is set to false', fakeAsync(() => { + const engineRunner = TestEngineRunner.withError({ + cache: true, + ssrFeatureToggles: { + avoidCachingErrors: false, + }, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual([ + new Error('a-0'), + new Error('a-0'), + new Error('a-0'), + ]); + })); + + it('should cache HTML if `avoidCachingErrors` is set to true', fakeAsync(() => { + const engineRunner = new TestEngineRunner({ + cache: true, + ssrFeatureToggles: { + avoidCachingErrors: true, + }, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']); + })); + + it('should cache HTML if `avoidCachingErrors` is set to false', fakeAsync(() => { + const engineRunner = new TestEngineRunner({ + cache: true, + ssrFeatureToggles: { + avoidCachingErrors: true, + }, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']); + })); + }); + }); + + describe('shouldCacheRenderingResult option', () => { + it('should not cache errors if `shouldCacheRenderingResult` returns false', fakeAsync(() => { + const engineRunner = TestEngineRunner.withError({ + cache: true, + shouldCacheRenderingResult: () => false, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual([ + new Error('a-0'), + new Error('a-1'), + new Error('a-2'), + ]); + })); + + it('should cache errors if `shouldCacheRenderingResult` returns true', fakeAsync(() => { + const engineRunner = TestEngineRunner.withError({ + cache: true, + shouldCacheRenderingResult: () => true, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual([ + new Error('a-0'), + new Error('a-0'), + new Error('a-0'), + ]); + })); + + it('should not cache HTML if `shouldCacheRenderingResult` returns false', fakeAsync(() => { + const engineRunner = new TestEngineRunner({ + cache: true, + shouldCacheRenderingResult: () => false, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual(['a-0', 'a-1', 'a-2']); + })); + + it('should cache HTML if `shouldCacheRenderingResult` returns true', fakeAsync(() => { + const engineRunner = new TestEngineRunner({ + cache: true, + shouldCacheRenderingResult: () => true, + }).request('a'); + + tick(200); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']); + })); + }); + describe('concurrency option', () => { it('should limit concurrency and fallback to csr', fakeAsync(() => { const engineRunner = new TestEngineRunner({ @@ -274,7 +576,7 @@ describe('OptimizedSsrEngine', () => { describe('ttl option', () => { it('should invalidate expired renders', fakeAsync(() => { let currentDate = 100; - spyOn(Date, 'now').and.callFake(() => currentDate); + jest.spyOn(Date, 'now').mockImplementation(() => currentDate); const engineRunner = new TestEngineRunner({ cache: true, @@ -293,6 +595,27 @@ describe('OptimizedSsrEngine', () => { tick(200); expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-1']); })); + + it('should not invalidate renders if ttl is not defined', fakeAsync(() => { + let currentDate = 100; + jest.spyOn(Date, 'now').mockImplementation(() => currentDate); + + const engineRunner = new TestEngineRunner({ + cache: true, + timeout: 200, + }).request('a'); + + tick(200); + currentDate += 200; + engineRunner.request('a'); + + tick(200); + currentDate += 200; + engineRunner.request('a'); + + tick(200); + expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']); + })); }); describe('renderKeyResolver option', () => { @@ -302,11 +625,10 @@ describe('OptimizedSsrEngine', () => { timeout: 200, cache: true, }); - spyOn( + jest.spyOn( engineRunner.optimizedSsrEngine as any, 'isConcurrencyLimitExceeded' - ).and.callThrough(); - + ); const route = 'home'; engineRunner.request(route); tick(200); @@ -321,7 +643,7 @@ describe('OptimizedSsrEngine', () => { timeout: 200, cache: true, }); - spyOn( + jest.spyOn( engineRunner.optimizedSsrEngine as any, 'isConcurrencyLimitExceeded' ); @@ -343,7 +665,7 @@ describe('OptimizedSsrEngine', () => { it('should use custom render key resolver', fakeAsync(() => { const engineRunner = new TestEngineRunner({ - renderKeyResolver: (req) => req.originalUrl.substr(0, 2), + renderKeyResolver: (req) => req.originalUrl.substring(0, 2), timeout: 200, cache: true, }).request('ala'); @@ -385,21 +707,24 @@ describe('OptimizedSsrEngine', () => { renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, timeout: 0, cache: true, - }).request('a'); + }); + + engineRunner.request('a'); tick(200); expect(engineRunner.renders).toEqual(['a-0']); })); - it('should render each request separately, even if there is already a pending render for the same rendering key', fakeAsync(() => { + it('when reuseCurrentRendering is false, it should render each request separately, even if there is already a pending render for the same rendering key', fakeAsync(() => { const engineRunner = new TestEngineRunner({ renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, timeout: 200, + reuseCurrentRendering: false, }); - spyOn( + jest.spyOn( engineRunner.optimizedSsrEngine as any, 'expressEngine' // 'expressEngine' is a protected property - ).and.callThrough(); + ); engineRunner.request('a'); expect(getCurrentConcurrency(engineRunner)).toEqual({ @@ -430,7 +755,9 @@ describe('OptimizedSsrEngine', () => { renderingStrategyResolver: () => RenderingStrategy.ALWAYS_CSR, timeout: 200, cache: true, - }).request('a'); + }); + + engineRunner.request('a'); tick(200); engineRunner.request('a'); @@ -444,10 +771,10 @@ describe('OptimizedSsrEngine', () => { timeout: 200, cache: true, }); - spyOn( + jest.spyOn( engineRunner.optimizedSsrEngine as any, 'expressEngine' // 'expressEngine' is a protected property - ).and.callThrough(); + ); engineRunner.request('a'); expect(engineRunner.renders).toEqual(['']); @@ -463,17 +790,20 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({ renderingStrategyResolver: () => RenderingStrategy.DEFAULT, timeout: 50, - }).request('a'); + }); + + engineRunner.request('a'); tick(200); engineRunner.request('a'); expect(engineRunner.renders).toEqual(['', 'a-0']); })); - it('should fallback to CSR when there is already pending a render for the same rendering key', fakeAsync(() => { + it('when reuseCurrentRendering is false, it should fallback to CSR when there is already pending a render for the same rendering key', fakeAsync(() => { const engineRunner = new TestEngineRunner({ renderingStrategyResolver: () => RenderingStrategy.DEFAULT, timeout: 200, + reuseCurrentRendering: false, }).request('a'); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, @@ -520,14 +850,15 @@ describe('OptimizedSsrEngine', () => { renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, timeout: 50, forcedSsrTimeout: 80, - }).request('a'); + }); + + engineRunner.request('a'); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, }); tick(60); expect(engineRunner.renders).toEqual([]); - tick(50); expect(engineRunner.renders).toEqual(['']); expect(getCurrentConcurrency(engineRunner)).toEqual({ @@ -545,7 +876,9 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({ timeout: 50, forcedSsrTimeout: 80, - }).request('a'); + }); + + engineRunner.request('a'); tick(60); expect(engineRunner.renders).toEqual(['']); @@ -555,7 +888,6 @@ describe('OptimizedSsrEngine', () => { expect(engineRunner.renders).toEqual(['', 'a-0']); })); }); - describe('maxRenderTime option', () => { const fiveMinutes = 300000; @@ -565,7 +897,7 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({}, renderTime).request( requestUrl ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); tick(renderTime + 1); expect(engineRunner.renderCount).toEqual(1); @@ -581,13 +913,14 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({}, renderTime).request( requestUrl ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); tick(fiveMinutes); expect(engineRunner.renderCount).toEqual(0); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `Rendering of ${requestUrl} was not able to complete. This might cause memory leaks!`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); tick(101); @@ -599,16 +932,20 @@ describe('OptimizedSsrEngine', () => { const renderTime = 200; const maxRenderTime = renderTime - 50; // shorter than the predicted render time const engineRunner = new TestEngineRunner( - { maxRenderTime }, + { + maxRenderTime, + timeout: undefined, + }, renderTime ).request(requestUrl); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); tick(maxRenderTime); expect(engineRunner.renderCount).toEqual(0); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `Rendering of ${requestUrl} was not able to complete. This might cause memory leaks!`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); tick(50); @@ -625,7 +962,7 @@ describe('OptimizedSsrEngine', () => { { concurrency: 1, maxRenderTime }, renderTime ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); // issue two requests engineRunner.request(hangingRequest); @@ -637,7 +974,9 @@ describe('OptimizedSsrEngine', () => { tick(1); // while the concurrency slot is busy rendering the first hanging request, the second request gets the CSR version expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( - `CSR fallback: Concurrency limit exceeded (1)` + `CSR fallback: Concurrency limit exceeded (1)`, + true, + { request: expect.objectContaining({ originalUrl: csrRequest }) } ); expect(engineRunner.renderCount).toEqual(0); expect(getCurrentConcurrency(engineRunner)).toEqual({ @@ -647,7 +986,8 @@ describe('OptimizedSsrEngine', () => { tick(maxRenderTime); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `Rendering of ${hangingRequest} was not able to complete. This might cause memory leaks!`, - false + false, + { request: expect.objectContaining({ originalUrl: hangingRequest }) } ); expect(engineRunner.renderCount).toEqual(0); @@ -655,7 +995,9 @@ describe('OptimizedSsrEngine', () => { engineRunner.request(ssrRequest); tick(1); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( - `Rendering started (${ssrRequest})` + `Rendering started (${ssrRequest})`, + true, + { request: expect.objectContaining({ originalUrl: ssrRequest }) } ); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, @@ -674,13 +1016,14 @@ describe('OptimizedSsrEngine', () => { }, renderTime ).request(requestUrl); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); expect(engineRunner.renders).toEqual([]); tick(fiveMinutes + 101); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `Rendering of ${requestUrl} completed after the specified maxRenderTime, therefore it was ignored.`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(engineRunner.renders).toEqual(['']); @@ -696,7 +1039,6 @@ describe('OptimizedSsrEngine', () => { const getRenderingKey = (requestUrlStr: string): string => `https://${host}${requestUrlStr}`; - const getRenderCallbacksCount = ( engineRunner: TestEngineRunner, requestUrlStr: string @@ -712,8 +1054,11 @@ describe('OptimizedSsrEngine', () => { describe('when disabled', () => { it('should fallback to CSR for parallel subsequent requests for the same rendering key', fakeAsync(() => { const timeout = 300; - const engineRunner = new TestEngineRunner({ timeout }, 400); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + const engineRunner = new TestEngineRunner( + { timeout, reuseCurrentRendering: false }, + 400 + ); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); engineRunner.request(requestUrl); expect(getRenderCallbacksCount(engineRunner, requestUrl)).toEqual({ @@ -727,12 +1072,16 @@ describe('OptimizedSsrEngine', () => { }); tick(100); + expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( - `CSR fallback: rendering in progress (${requestUrl})` + `CSR fallback: rendering in progress (${requestUrl})`, + true, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(engineRunner.renders).toEqual(['', '']); @@ -748,7 +1097,7 @@ describe('OptimizedSsrEngine', () => { { timeout, reuseCurrentRendering: true }, 400 ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); engineRunner.request(requestUrl); tick(200); @@ -758,7 +1107,8 @@ describe('OptimizedSsrEngine', () => { tick(100); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); tick(100); @@ -768,15 +1118,13 @@ describe('OptimizedSsrEngine', () => { })); it('and honour the timeout option', fakeAsync(() => { + const logSpy = jest.fn(); const timeout = 300; const engineRunner = new TestEngineRunner( { timeout, reuseCurrentRendering: true }, 1000 ); - const logSpy = spyOn( - engineRunner.optimizedSsrEngine, - 'log' - ).and.callThrough(); + engineRunner.optimizedSsrEngine['log'] = logSpy; engineRunner.request(requestUrl); @@ -790,15 +1138,15 @@ describe('OptimizedSsrEngine', () => { tick(200); let renderExceedMessageCount = 0; - logSpy.calls.allArgs().forEach((args: unknown[]) => { - args.forEach((message: unknown) => { - if ( - message === - `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}` - ) { - renderExceedMessageCount++; - } - }); + + logSpy.mock.calls.forEach((call) => { + const messageArg = call[0]; + if ( + messageArg === + `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}` + ) { + renderExceedMessageCount++; + } }); expect(renderExceedMessageCount).toBe(2); @@ -852,7 +1200,7 @@ describe('OptimizedSsrEngine', () => { { timeout, reuseCurrentRendering: true, concurrency: 2 }, 400 ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); // start 1st request engineRunner.request(requestUrl); @@ -886,7 +1234,8 @@ describe('OptimizedSsrEngine', () => { tick(100); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(engineRunner.renders).toEqual(['']); // the first request fallback to CSR due to timeout expect(getCurrentConcurrency(engineRunner)).toEqual({ @@ -917,7 +1266,7 @@ describe('OptimizedSsrEngine', () => { timeout: 200, concurrency: 1, }); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); engineRunner.request('a'); engineRunner.request('a'); @@ -937,6 +1286,7 @@ describe('OptimizedSsrEngine', () => { { timeout, reuseCurrentRendering: true, concurrency: 2 }, 200 ); + engineRunner .request(requestUrl) .request(requestUrl) @@ -995,7 +1345,7 @@ describe('OptimizedSsrEngine', () => { { concurrency: 2, maxRenderTime, reuseCurrentRendering: true }, renderTime ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); engineRunner.request(hangingRequest); engineRunner.request(hangingRequest); @@ -1015,7 +1365,8 @@ describe('OptimizedSsrEngine', () => { tick(maxRenderTime); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `Rendering of ${hangingRequest} was not able to complete. This might cause memory leaks!`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 0, @@ -1030,7 +1381,9 @@ describe('OptimizedSsrEngine', () => { engineRunner.request(ssrRequest); tick(1); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( - `Rendering started (${ssrRequest})` + `Rendering started (${ssrRequest})`, + true, + { request: expect.objectContaining({ originalUrl: ssrRequest }) } ); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, @@ -1056,7 +1409,7 @@ describe('OptimizedSsrEngine', () => { { timeout, reuseCurrentRendering: true }, 400 ); - spyOn(engineRunner.optimizedSsrEngine, 'log').and.callThrough(); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); engineRunner.request(requestUrl); tick(200); @@ -1066,11 +1419,13 @@ describe('OptimizedSsrEngine', () => { expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}`, - false + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } ); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${differentUrl}`, - false + false, + { request: expect.objectContaining({ originalUrl: differentUrl }) } ); expect(engineRunner.renderCount).toEqual(1); @@ -1080,4 +1435,58 @@ describe('OptimizedSsrEngine', () => { })); }); }); + + describe('logger option', () => { + let dateSpy: jest.SpyInstance; + + beforeEach(() => { + const mockDate = new Date('2023-01-01'); + dateSpy = jest + .spyOn(global, 'Date') + .mockImplementationOnce(() => mockDate); + }); + + afterAll(() => { + dateSpy.mockRestore(); + }); + + it('should use the default server logger, if custom logger is not specified', () => { + new TestEngineRunner({}); + expect(consoleLogSpy).toHaveBeenCalled(); + }); + it('should use the provided logger', () => { + new TestEngineRunner({ + logger: new MockExpressServerLogger() as ExpressServerLogger, + }); + expect(consoleLogSpy.mock.lastCall).toMatchInlineSnapshot(` + [ + "[spartacus] SSR optimization engine initialized", + { + "options": { + "cacheSize": 3000, + "concurrency": 10, + "forcedSsrTimeout": 60000, + "logger": "MockExpressServerLogger", + "maxRenderTime": 300000, + "renderingStrategyResolver": "(request) => { + if (hasExcludedUrl(request, defaultAlwaysCsrOptions.excludedUrls)) { + return ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR; + } + return shouldFallbackToCsr(request, options) + ? ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR + : ssr_optimization_options_1.RenderingStrategy.DEFAULT; + }", + "reuseCurrentRendering": true, + "shouldCacheRenderingResult": "({ options, entry }) => !(options.ssrFeatureToggles?.avoidCachingErrors === true && + Boolean(entry.err))", + "ssrFeatureToggles": { + "avoidCachingErrors": false, + }, + "timeout": 3000, + }, + }, + ] + `); + }); + }); }); diff --git a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts index f4a993ce8cc..148373f0313 100644 --- a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts +++ b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts @@ -1,12 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + /* webpackIgnore: true */ import { Request, Response } from 'express'; import * as fs from 'fs'; import { NgExpressEngineInstance } from '../engine-decorator/ng-express-engine-decorator'; -import { getRequestUrl } from '../util/request-url'; +import { getRequestUrl } from '../express-utils/express-request-url'; +import { + EXPRESS_SERVER_LOGGER, + ExpressServerLogger, + ExpressServerLoggerContext, +} from '../logger'; +import { getLoggableSsrOptimizationOptions } from './get-loggable-ssr-optimization-options'; import { RenderingCache } from './rendering-cache'; +import { preprocessRequestForLogger } from './request-context'; import { RenderingStrategy, SsrOptimizationOptions, + defaultSsrOptimizationOptions, } from './ssr-optimization-options'; /** @@ -31,7 +45,8 @@ export type SsrCallbackFn = ( */ export class OptimizedSsrEngine { protected currentConcurrency = 0; - protected renderingCache = new RenderingCache(this.ssrOptions); + protected renderingCache: RenderingCache; + private logger: ExpressServerLogger; private templateCache = new Map(); /** @@ -53,7 +68,41 @@ export class OptimizedSsrEngine { constructor( protected expressEngine: NgExpressEngineInstance, protected ssrOptions?: SsrOptimizationOptions - ) {} + ) { + this.ssrOptions = ssrOptions + ? { + ...defaultSsrOptimizationOptions, + // overrides the default options + ...ssrOptions, + // merge feature toggles + ssrFeatureToggles: { + ...defaultSsrOptimizationOptions.ssrFeatureToggles, + ...ssrOptions.ssrFeatureToggles, + }, + } + : undefined; + + if (!this.ssrOptions?.logger) { + throw new Error('`SsrOptimizationOptions.logger` is not defined'); + } + this.logger = this.ssrOptions?.logger; + this.renderingCache = new RenderingCache(this.ssrOptions); + this.logOptions(); + } + + protected logOptions(): void { + if (!this.ssrOptions) { + return; + } + + const loggableSsrOptions = getLoggableSsrOptimizationOptions( + this.ssrOptions + ); + + this.log(`[spartacus] SSR optimization engine initialized`, true, { + options: loggableSsrOptions, + }); + } /** * When SSR page can not be returned in time, we're returning index.html of @@ -99,10 +148,16 @@ export class OptimizedSsrEngine { !this.ssrOptions?.reuseCurrentRendering; if (fallBack) { - this.log(`CSR fallback: rendering in progress (${request?.originalUrl})`); + this.log( + `CSR fallback: rendering in progress (${request?.originalUrl})`, + true, + { request } + ); } else if (concurrencyLimitExceeded) { this.log( - `CSR fallback: Concurrency limit exceeded (${this.ssrOptions?.concurrency})` + `CSR fallback: Concurrency limit exceeded (${this.ssrOptions?.concurrency})`, + true, + { request } ); } @@ -155,8 +210,8 @@ export class OptimizedSsrEngine { */ protected getTimeout(request: Request): number { return this.getRenderingStrategy(request) === RenderingStrategy.ALWAYS_SSR - ? this.ssrOptions?.forcedSsrTimeout ?? 60000 - : this.ssrOptions?.timeout ?? 0; + ? (this.ssrOptions?.forcedSsrTimeout ?? 60000) + : (this.ssrOptions?.timeout ?? 0); } /** @@ -198,11 +253,15 @@ export class OptimizedSsrEngine { options: any, callback: SsrCallbackFn ): void { + preprocessRequestForLogger(options.req, this.logger); + const request: Request = options.req; - const response: Response = options.res || options.req.res; + const response: Response = options.req.res; if (this.returnCachedRender(request, callback)) { - this.log(`Render from cache (${request?.originalUrl})`); + this.log(`Render from cache (${request?.originalUrl})`, true, { + request, + }); return; } if (!this.shouldRender(request)) { @@ -210,7 +269,7 @@ export class OptimizedSsrEngine { return; } - let requestTimeout: NodeJS.Timeout | undefined; + let requestTimeout: ReturnType | undefined; if (this.shouldTimeout(request)) { // establish timeout for rendering const timeout = this.getTimeout(request); @@ -219,12 +278,14 @@ export class OptimizedSsrEngine { this.fallbackToCsr(response, filePath, callback); this.log( `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${request?.originalUrl}`, - false + false, + { request } ); }, timeout); } else { // Here we respond with the fallback to CSR, but we don't `return`. // We let the actual rendering task to happen in the background + // to eventually store the rendered result in the cache. this.fallbackToCsr(response, filePath, callback); } @@ -236,9 +297,7 @@ export class OptimizedSsrEngine { clearTimeout(requestTimeout); callback(err, html); - this.log( - `Request is resolved with the SSR rendering result (${request?.originalUrl})` - ); + this.logForRenderResult(err, html, request); // store the render only if caching is enabled if (this.ssrOptions?.cache) { @@ -260,10 +319,15 @@ export class OptimizedSsrEngine { }); } - protected log(message: string, debug = true): void { - if (!debug || this.ssrOptions?.debug) { - console.log(message); - } + /** + * @deprecated since v2211.27 - This method will be private in the future. + */ + protected log( + message: string, + _ignoredLegacyDebugParameter = true, + context: ExpressServerLoggerContext + ): void { + this.logger.log(message, context || {}); } /** Retrieve the document from the cache or the filesystem */ @@ -271,9 +335,7 @@ export class OptimizedSsrEngine { let doc = this.templateCache.get(filePath); if (!doc) { - // fs.readFileSync could be missing in a browser, specifically - // in a unit tests with { node: { fs: 'empty' } } webpack configuration - doc = fs?.readFileSync ? fs.readFileSync(filePath, 'utf-8') : ''; + doc = fs.readFileSync(filePath, 'utf-8'); this.templateCache.set(filePath, doc); } @@ -333,7 +395,9 @@ export class OptimizedSsrEngine { } this.log( - `Request is waiting for the SSR rendering to complete (${request?.originalUrl})` + `Request is waiting for the SSR rendering to complete (${request?.originalUrl})`, + true, + { request } ); } @@ -362,38 +426,77 @@ export class OptimizedSsrEngine { // Setting the timeout for hanging renders that might not ever finish due to various reasons. // After the configured `maxRenderTime` passes, we consider the rendering task as hanging, // and release the concurrency slot and forget all callbacks waiting for the render's result. - let maxRenderTimeout: NodeJS.Timeout | undefined = setTimeout(() => { - this.renderingCache.clear(renderingKey); - maxRenderTimeout = undefined; - this.currentConcurrency--; - if (this.ssrOptions?.reuseCurrentRendering) { - this.renderCallbacks.delete(renderingKey); - } - this.log( - `Rendering of ${request?.originalUrl} was not able to complete. This might cause memory leaks!`, - false - ); - }, this.ssrOptions?.maxRenderTime ?? 300000); // 300000ms == 5 minutes + let maxRenderTimeout: ReturnType | undefined = + setTimeout(() => { + this.renderingCache.clear(renderingKey); + maxRenderTimeout = undefined; + this.currentConcurrency--; + if (this.ssrOptions?.reuseCurrentRendering) { + this.renderCallbacks.delete(renderingKey); + } + this.log( + `Rendering of ${request?.originalUrl} was not able to complete. This might cause memory leaks!`, + false, + { request } + ); + }, this.ssrOptions?.maxRenderTime ?? 300000); // 300000ms == 5 minutes - this.log(`Rendering started (${request?.originalUrl})`); + this.log(`Rendering started (${request?.originalUrl})`, true, { request }); this.renderingCache.setAsRendering(renderingKey); this.currentConcurrency++; + options = { + ...options, + providers: [ + { + provide: EXPRESS_SERVER_LOGGER, + useValue: this.logger, + }, + ...(options?.providers ?? []), + ], + }; + this.expressEngine(filePath, options, (err, html) => { if (!maxRenderTimeout) { // ignore this render's result because it exceeded maxRenderTimeout this.log( `Rendering of ${request.originalUrl} completed after the specified maxRenderTime, therefore it was ignored.`, - false + false, + { request } ); return; } clearTimeout(maxRenderTimeout); - this.log(`Rendering completed (${request?.originalUrl})`); + this.log(`Rendering completed (${request?.originalUrl})`, true, { + request, + }); this.currentConcurrency--; renderCallback(err, html); }); } + + /** + * Logs the result of the render. + */ + private logForRenderResult( + err: unknown | undefined, + html: string | undefined, + request: Request + ): void { + if (html) { + this.log( + `Request is resolved with the SSR rendering result (${request?.originalUrl})`, + true, + { request } + ); + } + if (err) { + this.logger.error( + `Request is resolved with the SSR rendering error (${request?.originalUrl})`, + { request, error: err } + ); + } + } } diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts new file mode 100644 index 00000000000..d95a53c8f0b --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Represents a rendering entry in the rendering cache. + */ +export interface RenderingEntry { + html?: any; + err?: any; + time?: number; + rendering?: boolean; +} diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts index 1ed682d3f98..81592e76823 100644 --- a/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts +++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts @@ -1,10 +1,21 @@ +/// + import { RenderingCache } from './rendering-cache'; +import { + SsrOptimizationOptions, + defaultSsrOptimizationOptions, +} from './ssr-optimization-options'; + +const options: SsrOptimizationOptions = { + shouldCacheRenderingResult: + defaultSsrOptimizationOptions.shouldCacheRenderingResult, +}; describe('RenderingCache', () => { let renderingCache: RenderingCache; beforeEach(() => { - renderingCache = new RenderingCache({}); + renderingCache = new RenderingCache(options); }); it('should create rendering cache instance', () => { @@ -56,11 +67,11 @@ describe('RenderingCache', () => { }); it('should return true if there is rendering', () => { renderingCache.store('test', undefined, 'testHtml'); - expect(renderingCache.isReady('test')).toBeTrue(); + expect(renderingCache.isReady('test')).toBeTruthy(); }); it('should return true if there is an error', () => { renderingCache.store('test', {} as any); - expect(renderingCache.isReady('test')).toBeTrue(); + expect(renderingCache.isReady('test')).toBeTruthy(); }); }); @@ -75,13 +86,13 @@ describe('RenderingCache with ttl', () => { let renderingCache: RenderingCache; beforeEach(() => { - renderingCache = new RenderingCache({ ttl: 100 }); + renderingCache = new RenderingCache({ ...options, ttl: 100 }); }); describe('get', () => { it('should return timestamp', () => { renderingCache.store('test', null, 'testHtml'); - expect(renderingCache.get('test').time).toBeTruthy(); + expect(renderingCache.get('test')?.time).toBeTruthy(); }); }); @@ -90,7 +101,7 @@ describe('RenderingCache with ttl', () => { beforeEach(() => { mockedTime = 100; - spyOn(Date, 'now').and.callFake(() => mockedTime); + jest.spyOn(Date, 'now').mockImplementation(() => mockedTime); }); it('should return false for non-existent renders', () => { @@ -116,7 +127,7 @@ describe('RenderingCache with cacheSize', () => { let renderingCache: RenderingCache; beforeEach(() => { - renderingCache = new RenderingCache({ cacheSize: 2 }); + renderingCache = new RenderingCache({ ...options, cacheSize: 2 }); }); describe('get', () => { @@ -149,4 +160,75 @@ describe('RenderingCache with cacheSize', () => { expect(renderingCache.get('c')).toBeTruthy(); }); }); + + describe('RenderingCache and shouldCacheRenderingResult', () => { + let renderingCache: RenderingCache; + + describe('if default shouldCacheRenderingResult', () => { + it('should cache HTML if avoidCachingErrors is false', () => { + renderingCache = new RenderingCache({ + ...options, + ssrFeatureToggles: { + avoidCachingErrors: false, + }, + }); + renderingCache.store('a', undefined, 'a'); + expect(renderingCache.get('a')).toEqual({ html: 'a', err: undefined }); + }); + + it('should cache HTML if avoidCachingErrors is true', () => { + renderingCache = new RenderingCache({ + ...options, + ssrFeatureToggles: { + avoidCachingErrors: false, + }, + }); + renderingCache.store('a', undefined, 'a'); + expect(renderingCache.get('a')).toEqual({ html: 'a', err: undefined }); + }); + + it('should cache errors if avoidCachingErrors is false', () => { + renderingCache = new RenderingCache({ + ...options, + ssrFeatureToggles: { + avoidCachingErrors: false, + }, + }); + renderingCache.store('a', new Error('err'), 'a'); + expect(renderingCache.get('a')).toEqual({ + html: 'a', + err: new Error('err'), + }); + }); + + it('should not cache errors if avoidCachingErrors is true', () => { + renderingCache = new RenderingCache({ + ...options, + ssrFeatureToggles: { + avoidCachingErrors: true, + }, + }); + renderingCache.store('a', new Error('err'), 'a'); + expect(renderingCache.get('a')).toBeFalsy(); + }); + }); + + describe('if shouldCacheRenderingResult is not defined', () => { + beforeEach(() => { + renderingCache = new RenderingCache({ + ...options, + shouldCacheRenderingResult: undefined, + }); + }); + it('should not cache a html', () => { + renderingCache.store('a', undefined, 'a'); + expect(renderingCache.get('a')).toBeFalsy(); + }); + + it('should not cache an error', () => { + renderingCache.store('a', new Error('err'), 'a'); + expect(renderingCache.get('a')).toBeFalsy(); + }); + }); + }); }); diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.ts index 5c444acc8fa..f7700687fb7 100644 --- a/core-libs/setup/ssr/optimized-engine/rendering-cache.ts +++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.ts @@ -1,11 +1,11 @@ -import { SsrOptimizationOptions } from './ssr-optimization-options'; +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ -export interface RenderingEntry { - html?: any; - err?: any; - time?: number; - rendering?: boolean; -} +import { RenderingEntry } from './rendering-cache.model'; +import { SsrOptimizationOptions } from './ssr-optimization-options'; export class RenderingCache { protected renders = new Map(); @@ -31,7 +31,15 @@ export class RenderingCache { this.renders.delete(this.renders.keys().next().value); } } - this.renders.set(key, entry); + // cache only if shouldCacheRenderingResult return true + if ( + this.options?.shouldCacheRenderingResult?.({ + options: this.options, + entry, + }) + ) { + this.renders.set(key, entry); + } } get(key: string): RenderingEntry | undefined { diff --git a/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver-options.ts b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver-options.ts new file mode 100644 index 00000000000..20212f4ea45 --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver-options.ts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface RenderingStrategyResolverOptions { + excludedUrls?: string[]; + excludedParams?: string[]; +} + +export const defaultRenderingStrategyResolverOptions: RenderingStrategyResolverOptions = + { + excludedUrls: ['checkout', 'my-account'], + excludedParams: ['asm'], + }; diff --git a/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.spec.ts b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.spec.ts new file mode 100644 index 00000000000..b7627ac2675 --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.spec.ts @@ -0,0 +1,73 @@ +/// + +import { RenderingStrategy } from './ssr-optimization-options'; +import { Request } from 'express'; + +import { defaultRenderingStrategyResolver } from './rendering-strategy-resolver'; + +describe('RenderingStrategyResolver', () => { + let resolver: (req: Request) => RenderingStrategy = + defaultRenderingStrategyResolver({ + excludedUrls: ['checkout', 'my-account'], + excludedParams: ['asm'], + }); + + it('should return DEFAULT rendering strategy if no excluded parameters or URLs match', () => { + const request: Partial = { + query: {}, + url: '/some-page', + }; + + const strategy = resolver(request as Request); + + expect(strategy).toBe(RenderingStrategy.DEFAULT); + }); + + it('should return ALWAYS_CSR rendering strategy if an excluded parameter matches', () => { + const request: Partial = { + query: { + asm: 'true', + }, + url: '/some-page', + }; + + const strategy = resolver(request as Request); + + expect(strategy).toBe(RenderingStrategy.ALWAYS_CSR); + }); + + it('should return ALWAYS_CSR rendering strategy if the URL matches an excluded URL', () => { + const request: Partial = { + query: {}, + url: '/checkout/confirm', + }; + + const strategy = resolver(request as Request); + + expect(strategy).toBe(RenderingStrategy.ALWAYS_CSR); + }); + + it('should return ALWAYS_CSR rendering strategy if both excluded parameters and URLs match', () => { + const request: Partial = { + query: { + asm: 'true', + }, + url: '/checkout/confirm', + }; + + const strategy = resolver(request as Request); + + expect(strategy).toBe(RenderingStrategy.ALWAYS_CSR); + }); + + it('should return ALWAYS_CSR rendering strategy if the URL matches SmartEdit url', () => { + const request: Partial = { + query: {}, + url: 'cx-preview', + }; + + const strategy = resolver(request as Request); + + expect(strategy).toBe(RenderingStrategy.ALWAYS_CSR); + }); +}); diff --git a/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.ts b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.ts new file mode 100644 index 00000000000..277e085696b --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/rendering-strategy-resolver.ts @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Request } from 'express'; +import { RenderingStrategy } from './ssr-optimization-options'; +import { RenderingStrategyResolverOptions } from './rendering-strategy-resolver-options'; + +const defaultAlwaysCsrOptions = { + excludedUrls: ['cx-preview'], +}; + +const hasExcludedParams = ( + request: Request, + excludedParams: string[] | undefined +): boolean => { + const params: string[] = request.query + ? Object.getOwnPropertyNames(request.query) + : []; + + if (!excludedParams) { + return false; + } + + return excludedParams.some((excludedParam: string) => + params.some((param: string): boolean => excludedParam === param) + ); +}; + +const hasExcludedUrl = ( + request: Request, + excludedUrls: string[] | undefined +) => { + return request.url && excludedUrls + ? excludedUrls.some((url) => request.url.search(url) > -1) + : false; +}; + +const shouldFallbackToCsr = ( + request: Request, + { excludedParams, excludedUrls }: RenderingStrategyResolverOptions +) => { + return ( + hasExcludedParams(request, excludedParams) || + hasExcludedUrl(request, excludedUrls) + ); +}; + +/** + * Creates a rendering strategy resolver function with the specified options. + * + * @function + * @param options - The options to configure the rendering strategy resolver. + * @param [options.excludedUrls] - An optional array of URLs for which server-side rendering (SSR) should be disabled. + * @param [options.excludedParams] - An optional array of Query parameters for which SSR should be disabled. + * @returns A rendering strategy resolver function that takes a Request object + * as a parameter and returns the rendering strategy to be applied for the request, which can be either + * `RenderingStrategy.ALWAYS_CSR` or `RenderingStrategy.DEFAULT`. + */ +export const defaultRenderingStrategyResolver = + (options: RenderingStrategyResolverOptions) => + (request: Request): RenderingStrategy => { + if (hasExcludedUrl(request, defaultAlwaysCsrOptions.excludedUrls)) { + return RenderingStrategy.ALWAYS_CSR; + } + + return shouldFallbackToCsr(request, options) + ? RenderingStrategy.ALWAYS_CSR + : RenderingStrategy.DEFAULT; + }; diff --git a/core-libs/setup/ssr/optimized-engine/request-context.spec.ts b/core-libs/setup/ssr/optimized-engine/request-context.spec.ts new file mode 100644 index 00000000000..1fb767e1a5d --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/request-context.spec.ts @@ -0,0 +1,118 @@ +import crypto from 'crypto'; +import { Request } from 'express'; +import { IncomingHttpHeaders } from 'http'; +import { + DefaultExpressServerLogger, + ExpressServerLogger, + ExpressServerLoggerContext, +} from '../logger'; +import * as parser from '../logger/loggers/w3c-trace-context/parse-traceparent'; +import { preprocessRequestForLogger } from './request-context'; + +describe('RequestContext', () => { + let request: Request; + let headers: IncomingHttpHeaders; + let logger: ExpressServerLogger; + let dateSpy: jest.SpyInstance; + let randomUUIDSpy: jest.SpyInstance; + const mockDate = new Date('2023-09-07'); + + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(); + dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); + randomUUIDSpy = jest + .spyOn(crypto, 'randomUUID') + .mockReturnValue('ad90db04-a501-4dc5-9b4e-2cc2ab10d49c'); + + headers = {}; + request = { + res: { + locals: { + cx: { request: {} as ExpressServerLoggerContext }, + }, + }, + get: (header: string): string | string[] | undefined => { + return headers[header]; + }, + headers, + } as unknown as Request; + logger = new DefaultExpressServerLogger(); + }); + + afterEach(() => { + dateSpy.mockRestore(); + randomUUIDSpy.mockRestore(); + }); + + it('should add initial request context', () => { + preprocessRequestForLogger(request, logger); + expect(request.res?.locals.cx.request).toMatchInlineSnapshot(` + { + "timeReceived": "2023-09-07T00:00:00.000Z", + "traceContext": undefined, + "uuid": "ad90db04-a501-4dc5-9b4e-2cc2ab10d49c", + } + `); + }); + + it('should add trace context', () => { + request.headers.traceparent = + '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01'; + + preprocessRequestForLogger(request, logger); + expect(request.res?.locals.cx.request).toMatchInlineSnapshot(` + { + "timeReceived": "2023-09-07T00:00:00.000Z", + "traceContext": { + "parentId": "b7ad6b7169203331", + "traceFlags": "01", + "traceId": "0af7651916cd43dd8448eb211c80319c", + "version": "00", + }, + "uuid": "ad90db04-a501-4dc5-9b4e-2cc2ab10d49c", + } + `); + }); + + it('should add only initial context and log an error if traceparent is invalid', () => { + const invalidTraceparent = 'invalid'; + const loggerErrorSpy = jest.spyOn(logger, 'error'); + + request.headers.traceparent = invalidTraceparent; + preprocessRequestForLogger(request, logger); + expect(request.res?.locals.cx.request).toMatchInlineSnapshot(` + { + "timeReceived": "2023-09-07T00:00:00.000Z", + "traceContext": undefined, + "uuid": "ad90db04-a501-4dc5-9b4e-2cc2ab10d49c", + } + `); + expect(loggerErrorSpy).toHaveBeenCalledWith( + `Traceparent header has invalid length: ${invalidTraceparent.length}. Expected 55 characters.`, + { request } + ); + }); + + it('should add only initial context and log unexpected error if parsing failed for unknown reason', () => { + const invalidTraceparent = 'invalid'; + const loggerErrorSpy = jest.spyOn(logger, 'error'); + + jest.spyOn(parser, 'parseTraceparent').mockImplementationOnce(() => { + // eslint-disable-next-line no-throw-literal + throw { message: 'Unexpected error' }; + }); + request.headers.traceparent = invalidTraceparent; + preprocessRequestForLogger(request, logger); + expect(request.res?.locals.cx.request).toMatchInlineSnapshot(` + { + "timeReceived": "2023-09-07T00:00:00.000Z", + "traceContext": undefined, + "uuid": "ad90db04-a501-4dc5-9b4e-2cc2ab10d49c", + } + `); + expect(loggerErrorSpy).toHaveBeenCalledWith( + 'Unexpected error during parsing traceparent header', + { request } + ); + }); +}); diff --git a/core-libs/setup/ssr/optimized-engine/request-context.ts b/core-libs/setup/ssr/optimized-engine/request-context.ts new file mode 100644 index 00000000000..e56ec35cdf3 --- /dev/null +++ b/core-libs/setup/ssr/optimized-engine/request-context.ts @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { randomUUID } from 'crypto'; +import { Request } from 'express'; +import { ExpressServerLogger } from '../logger'; +import { parseTraceparent } from '../logger/loggers/w3c-trace-context/parse-traceparent'; +import { W3cTraceContext } from '../logger/loggers/w3c-trace-context/w3c-trace-context.model'; + +/** + * RequestContext is used for log message in server side rendering. + * It contains request's UUID, time of receiving the request and the W3C Trace Context if `traceparent` header is available and valid. + */ +export interface RequestContext { + uuid: string; + timeReceived: string; + traceContext?: W3cTraceContext; +} + +/** + * Returns the request context from the request object. + * @param request - the request object + * @returns the context of the request + */ +export const getRequestContext = (request: Request): RequestContext => { + return request.res?.locals?.cx?.request; +}; + +/** + * Prepares and updates a request with the context object, which is used to enrich the logs. + * It contains the random request's UUID, time of receiving the context and the W3C Trace Context (if available). + * The trace context is parsed from the `traceparent` header, which is specified in + * the "W3C TraceContext" document. See https://www.w3.org/TR/trace-context/#traceparent-header + * for more details. + * @param request - the request object + * @param logger - the ExpressServerLogger object. It is used to log the error if occurred during parsing traceparent header + * @returns the context of the request and error if occurred during parsing traceparent header + */ +export const preprocessRequestForLogger = ( + request: Request, + logger: ExpressServerLogger +) => { + const requestContext: RequestContext = { + ...createInitialRequestContext(), + traceContext: getTraceContext(request, logger), + }; + setRequestContext(request, requestContext); +}; + +/** + * Updates the request object with the request context. + * @param request - the request object + * @param context - the context of the request + */ +const setRequestContext = (request: Request, context: RequestContext) => { + if (request.res) { + request.res.locals = { + ...request.res.locals, + cx: { + ...request.res.locals.cx, + request: context, + }, + }; + } +}; + +/** + * Creates the initial request context to the request object. + * @param request - the request object + * @returns object with a randomly generated UUID and the current time + */ +const createInitialRequestContext = (): RequestContext => { + const requestContext: RequestContext = { + uuid: randomUUID(), + timeReceived: new Date().toISOString(), + }; + return requestContext; +}; + +/** + * Parses the `traceparent` header and returns an object with the W3C TraceContext. + * In case when the `traceparent` header is absent or invalid, `undefined` value is returned. + * @param request - the request object + * @param logger - the logger object + * + */ +const getTraceContext = ( + request: Request, + logger: ExpressServerLogger +): W3cTraceContext | undefined => { + try { + return parseTraceparent(request.get('traceparent')) ?? undefined; + } catch (e) { + const error = + e instanceof Error + ? e + : new Error('Unexpected error during parsing traceparent header'); + logger.error(error.message, { request }); + } +}; + +declare module 'express' { + export interface Locals { + cx: { + request: RequestContext; + }; + } +} diff --git a/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts b/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts index 30e38e7e5ab..9f3610ef5da 100644 --- a/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts +++ b/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts @@ -1,4 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + import { Request } from 'express'; +import { DefaultExpressServerLogger, ExpressServerLogger } from '../logger'; +import { RenderingEntry } from './rendering-cache.model'; +import { defaultRenderingStrategyResolver } from './rendering-strategy-resolver'; +import { defaultRenderingStrategyResolverOptions } from './rendering-strategy-resolver-options'; export interface SsrOptimizationOptions { /** @@ -24,6 +34,8 @@ export interface SsrOptimizationOptions { * Can also be use when `cache` option is set to false. It will then limit the * number of renders that timeouts and are kept in temporary cache, waiting * to be served with next request. + * + * Default value is set to 3000. */ cacheSize?: number; @@ -47,7 +59,10 @@ export interface SsrOptimizationOptions { renderKeyResolver?: (req: Request) => string; /** - * Allows defining custom rendering strategy per request + * This function allows for the definition of a custom rendering strategy on a per-request basis. + * By default, we provide a defaultRenderingStrategyResolver, + * which has a default parameter defaultRenderingStrategyResolverOptions. + * This default option disables server-side rendering (SSR) on pages such as 'checkout' and 'my-account'. * * @param req */ @@ -100,9 +115,64 @@ export interface SsrOptimizationOptions { reuseCurrentRendering?: boolean; /** - * Enable detailed logs for troubleshooting problems + * @deprecated - This flag is not used anymore since v2211.27. + * + * Now all the information about the traffic and rendering is logged unconditionally: + * - receiving requests + * - responding to requests (either with HTML result, error or fallback to CSR) + * - start and end of renders + * - timeout of renders (due to passing `maxRenderTime`) */ debug?: boolean; + + /** + * Config for improving logged messages with context and JSON structure. + * + * It enhances the logs in SSR by adding context, including the request's details, + * and structuring them as JSON. + * + * The `logger` property is optional and accepts: + * - `ExpressServerLogger`: Interprets the given `ExpressServerLogger` as a custom logger + * + * By default, the DefaultExpressServerLogger is used. + */ + logger?: ExpressServerLogger; + + /** + * When caching is enabled, this function tells whether the given rendering result + * (html or error) should be cached. + * + * By default, all html rendering results are cached. By default, also all errors are cached + * unless the separate option `avoidCachingErrors` is enabled. + */ + shouldCacheRenderingResult?: ({ + options, + entry, + }: { + options: SsrOptimizationOptions; + entry: Pick; + }) => boolean; + + /** + * Toggles providing granular adaptation to breaking changes in OptimizedSsrEngine. + * They are temporary and will be removed in the future. + * Each toggle has its own lifespan. + * + * Note: They are related only to the `OptimizedSsrEngine`. In particular, they + * are different from Spartacus's regular feature toggles provided in the Angular app. + */ + ssrFeatureToggles?: { + /** + * Determines if rendering errors should be skipped from caching. + * + * It's recommended to set to `true` (i.e. errors are skipped from caching), + * which will become the default behavior, when this feature toggle is removed. + * + * It only affects the default `shouldCacheRenderingResult`. + * Custom implementations of `shouldCacheRenderingResult` may ignore this setting. + */ + avoidCachingErrors?: boolean; + }; } export enum RenderingStrategy { @@ -110,3 +180,33 @@ export enum RenderingStrategy { DEFAULT = 0, ALWAYS_SSR = 1, } + +/** + * Deeply required type for `featureToggles` property. + */ +type DeepRequired = { + [P in keyof T]-?: DeepRequired; +}; + +export const defaultSsrOptimizationOptions: SsrOptimizationOptions & + // To not forget adding default values, when adding new feature toggles in the type in the future + DeepRequired> = { + cacheSize: 3000, + concurrency: 10, + timeout: 3_000, + forcedSsrTimeout: 60_000, + maxRenderTime: 300_000, + reuseCurrentRendering: true, + renderingStrategyResolver: defaultRenderingStrategyResolver( + defaultRenderingStrategyResolverOptions + ), + logger: new DefaultExpressServerLogger(), + shouldCacheRenderingResult: ({ options, entry }) => + !( + options.ssrFeatureToggles?.avoidCachingErrors === true && + Boolean(entry.err) + ), + ssrFeatureToggles: { + avoidCachingErrors: false, + }, +}; diff --git a/core-libs/setup/ssr/providers/index.ts b/core-libs/setup/ssr/providers/index.ts index 0e8d15d51d9..8bc24f5415e 100644 --- a/core-libs/setup/ssr/providers/index.ts +++ b/core-libs/setup/ssr/providers/index.ts @@ -1 +1,8 @@ -export { getServerRequestProviders } from './ssr-providers'; +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './model'; +export * from './ssr-providers'; diff --git a/core-libs/setup/ssr/providers/model.ts b/core-libs/setup/ssr/providers/model.ts new file mode 100644 index 00000000000..962a1095df8 --- /dev/null +++ b/core-libs/setup/ssr/providers/model.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Server options + */ +export interface ServerOptions { + /** + * Specify a domain (origin) from which the HTTP requests are being made. + * Should be without the trailing slash, e.g. "https://my.domain.com". + * + * In SSR mode, it will be automatically resolved from the express server, + * therefore it doesn't have to be set via this option. + * If explicitly set, this option will take precedence over the express server. + * + * It is _mandatory_ to provide it for the prerendering, as it can not be + * automatically resolved. + */ + serverRequestOrigin?: string; +} diff --git a/core-libs/setup/ssr/providers/server-request-origin.spec.ts b/core-libs/setup/ssr/providers/server-request-origin.spec.ts new file mode 100644 index 00000000000..64407fcd487 --- /dev/null +++ b/core-libs/setup/ssr/providers/server-request-origin.spec.ts @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ +import { serverRequestOriginFactory } from './server-request-origin'; + +jest.mock('@angular/core', () => { + return { + ...jest.requireActual('@angular/core'), + inject: jest.fn(), + }; +}); + +import { inject } from '@angular/core'; + +describe('serverRequestOriginFactory', () => { + describe('when SERVER_REQUEST_ORIGIN is present', () => { + it('should return SERVER_REQUEST_ORIGIN', () => { + const mockOrigin = 'https://express.origin.com'; + (inject as jest.Mock).mockReturnValue(mockOrigin); + + const result = serverRequestOriginFactory()(); + + expect(result).toEqual(mockOrigin); + }); + }); + describe('when SERVER_REQUEST_ORIGIN is NOT present', () => { + beforeEach(() => { + (inject as jest.Mock).mockReturnValue(null); + }); + + describe('and when options.serverRequestOrigin is present', () => { + it('should return options.serverRequestOrigin', (done) => { + const optionsOrigin = 'https://express.origin.com'; + const result = serverRequestOriginFactory({ + serverRequestOrigin: optionsOrigin, + })(); + done(); + expect(result).toEqual(optionsOrigin); + }); + }); + describe('and when options.serverRequestOrigin is NOT present', () => { + it('should throw an error if ', () => { + expect(() => { + serverRequestOriginFactory()(); + }).toThrowError(''); + }); + }); + }); +}); diff --git a/core-libs/setup/ssr/providers/server-request-origin.ts b/core-libs/setup/ssr/providers/server-request-origin.ts new file mode 100644 index 00000000000..1ac32737fee --- /dev/null +++ b/core-libs/setup/ssr/providers/server-request-origin.ts @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { inject } from '@angular/core'; +import { SERVER_REQUEST_ORIGIN } from '@spartacus/core'; +import { ServerOptions } from './model'; + +/** + * Returns a factory function which resolves the server request origin. + */ +export function serverRequestOriginFactory(options?: ServerOptions): Function { + return (): string => { + const serverRequestOrigin = inject(SERVER_REQUEST_ORIGIN, { + optional: true, + skipSelf: true, + }); + + // usually prerendering mode, but can be SSR + if (options?.serverRequestOrigin) { + return options.serverRequestOrigin; + } + + // SSR mode, from express engine + if (serverRequestOrigin) { + return serverRequestOrigin; + } + + throw new Error( + `The request origin is not set. + If you are using the default environment variable, please specify it when initiating the process. + + E.g. + > SERVER_REQUEST_ORIGIN=https://my.domain.com yarn prerender + > SERVER_REQUEST_ORIGIN=http://localhost:4200 yarn serve:ssr + + + Alternatively, you can pass it as an argument to provideServer + function, but beware it will be used for server-side rendering as well. + + E.g. + @NgModule({ + // ... + providers: [ + provideServer({ + serverRequestOrigin: 'https://my.domain.com', + }), + ], + }) + export class AppServerModule {}` + ); + }; +} diff --git a/core-libs/setup/ssr/providers/server-request-url.spec.ts b/core-libs/setup/ssr/providers/server-request-url.spec.ts new file mode 100644 index 00000000000..ddf29effbdd --- /dev/null +++ b/core-libs/setup/ssr/providers/server-request-url.spec.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ +jest.mock('@angular/core', () => { + return { + ...jest.requireActual('@angular/core'), + inject: jest.fn(), + }; +}); + +import { inject } from '@angular/core'; +import { INITIAL_CONFIG } from '@angular/platform-server'; +import { SERVER_REQUEST_ORIGIN, SERVER_REQUEST_URL } from '@spartacus/core'; +import { serverRequestUrlFactory } from './server-request-url'; + +describe('serverRequestUrlFactory', () => { + describe('when SERVER_REQUEST_URL is present', () => { + it('should return SERVER_REQUEST_URL', () => { + const mockOrigin = 'https://express.origin.com'; + (inject as jest.Mock).mockImplementation((token) => { + if (token.toString() === SERVER_REQUEST_URL.toString()) { + return mockOrigin; + } + }); + + const result = serverRequestUrlFactory()(); + expect(result).toEqual(mockOrigin); + }); + }); + describe('when SERVER_REQUEST_URL is NOT present', () => { + it('should use SERVER_REQUEST_ORIGIN and INITIAL_CONFIG to build the url', () => { + const mockOrigin = 'https://express.origin.com'; + const mockUrl = '/home'; + + (inject as jest.Mock).mockImplementation((token) => { + if (token.toString() === SERVER_REQUEST_ORIGIN.toString()) { + return mockOrigin; + } + if (token.toString() === INITIAL_CONFIG.toString()) { + return { + url: mockUrl, + }; + } + }); + + const result = serverRequestUrlFactory()(); + expect(result).toEqual(mockOrigin + mockUrl); + }); + }); +}); diff --git a/core-libs/setup/ssr/providers/server-request-url.ts b/core-libs/setup/ssr/providers/server-request-url.ts new file mode 100644 index 00000000000..58e631f659f --- /dev/null +++ b/core-libs/setup/ssr/providers/server-request-url.ts @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { inject } from '@angular/core'; +import { INITIAL_CONFIG } from '@angular/platform-server'; +import { SERVER_REQUEST_ORIGIN, SERVER_REQUEST_URL } from '@spartacus/core'; +import { ServerOptions } from './model'; + +/** + * Returns a factory function which resolves the server request URL. + */ +export function serverRequestUrlFactory(options?: ServerOptions): Function { + return (): string => { + const platformConfig = inject(INITIAL_CONFIG); + const serverRequestOrigin = inject(SERVER_REQUEST_ORIGIN); + const serverRequestUrl = inject(SERVER_REQUEST_URL, { + optional: true, + skipSelf: true, + }); + + // SSR mode + if (serverRequestUrl) { + // should override the automatically recognized origin + if (options?.serverRequestOrigin) { + return serverRequestUrl.replace( + serverRequestOrigin, + options.serverRequestOrigin + ); + } + + return serverRequestUrl; + } + + // prerendering mode (no express server) + return serverRequestOrigin + platformConfig.url; + }; +} diff --git a/core-libs/setup/ssr/providers/ssr-providers.ts b/core-libs/setup/ssr/providers/ssr-providers.ts index 73f038a90eb..b6af3698cef 100644 --- a/core-libs/setup/ssr/providers/ssr-providers.ts +++ b/core-libs/setup/ssr/providers/ssr-providers.ts @@ -1,9 +1,50 @@ -import { StaticProvider } from '@angular/core'; -import { REQUEST } from '@nguniversal/express-engine/tokens'; -import { SERVER_REQUEST_ORIGIN, SERVER_REQUEST_URL } from '@spartacus/core'; -import { getRequestOrigin } from '../util/request-origin'; -import { getRequestUrl } from '../util/request-url'; +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Provider, StaticProvider } from '@angular/core'; +import { + LoggerService, + MULTI_ERROR_HANDLER, + SERVER_REQUEST_ORIGIN, + SERVER_REQUEST_URL, +} from '@spartacus/core'; + +import { PropagatingToServerErrorHandler } from '../error-handling/multi-error-handlers'; +import { getRequestOrigin } from '../express-utils/express-request-origin'; +import { getRequestUrl } from '../express-utils/express-request-url'; +import { serverLoggerServiceFactory } from '../logger'; +import { REQUEST } from '../tokens/express.tokens'; +import { ServerOptions } from './model'; +import { serverRequestOriginFactory } from './server-request-origin'; +import { serverRequestUrlFactory } from './server-request-url'; +/** + * Returns the providers used for SSR and pre-rendering processes. + */ +export function provideServer(options?: ServerOptions): Provider[] { + return [ + { + provide: SERVER_REQUEST_ORIGIN, + useFactory: serverRequestOriginFactory(options), + }, + { + provide: SERVER_REQUEST_URL, + useFactory: serverRequestUrlFactory(options), + }, + { + provide: LoggerService, + useFactory: serverLoggerServiceFactory, + }, + { + provide: MULTI_ERROR_HANDLER, + useExisting: PropagatingToServerErrorHandler, + multi: true, + }, + ]; +} /** * Returns Spartacus providers to be passed to the Angular express engine (in SSR) * @@ -12,13 +53,13 @@ import { getRequestUrl } from '../util/request-url'; export function getServerRequestProviders(): StaticProvider[] { return [ { - provide: SERVER_REQUEST_URL, - useFactory: getRequestUrl, + provide: SERVER_REQUEST_ORIGIN, + useFactory: getRequestOrigin, deps: [REQUEST], }, { - provide: SERVER_REQUEST_ORIGIN, - useFactory: getRequestOrigin, + provide: SERVER_REQUEST_URL, + useFactory: getRequestUrl, deps: [REQUEST], }, ]; diff --git a/core-libs/setup/ssr/public_api.ts b/core-libs/setup/ssr/public_api.ts index 6a9627e1e38..81b6bfd277f 100644 --- a/core-libs/setup/ssr/public_api.ts +++ b/core-libs/setup/ssr/public_api.ts @@ -1,3 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + export * from './engine-decorator/index'; +export * from './engine/cx-common-engine'; +export * from './engine/ng-express-engine'; +export * from './error-handling/index'; +export * from './logger/index'; export * from './optimized-engine/index'; export * from './providers/index'; +export * from './testing/index'; +export * from './tokens/express.tokens'; diff --git a/core-libs/setup/ssr/testing/index.ts b/core-libs/setup/ssr/testing/index.ts new file mode 100644 index 00000000000..deb3a36ff8c --- /dev/null +++ b/core-libs/setup/ssr/testing/index.ts @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + // exporting only deliberate public api members: + TestConfigServerModule, +} from './test-config-server.module'; diff --git a/core-libs/setup/ssr/testing/test-config-server.module.ts b/core-libs/setup/ssr/testing/test-config-server.module.ts new file mode 100644 index 00000000000..4bd49041577 --- /dev/null +++ b/core-libs/setup/ssr/testing/test-config-server.module.ts @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ModuleWithProviders, NgModule, inject } from '@angular/core'; +import { TEST_CONFIG, TEST_CONFIG_COOKIE_NAME } from '@spartacus/core'; +import { REQUEST } from '../public_api'; + +export function getCookie(cookie: string, name: string) { + const regExp = new RegExp('(?:^|;\\s*)' + name + '=([^;]*)', 'g'); + const result: RegExpExecArray | null = regExp.exec(cookie); + + return (result && decodeURIComponent(result[1])) || ''; +} + +export function parseConfigJSON(config: string) { + try { + return JSON.parse(decodeURIComponent(config)); + } catch (_) { + return {}; + } +} + +/** + * A counterpart of the `TestConfigModule` from `@spartacus/core`, + * but for the Server platform. + * @see {@link TestConfigModule} + * + * - It uses the cookie from the REQUEST object (but not from `document.cookie`). + * - The `TestConfigModule` must be imported in the app module anyway. + * + * CAUTION: DON'T USE IT IN PRODUCTION! IT HASN'T BEEN REVIEWED FOR SECURITY ISSUES. + */ +@NgModule({}) +export class TestConfigServerModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: TestConfigServerModule, + providers: [ + { + provide: TEST_CONFIG, + useFactory: () => { + const cookieName: string = inject(TEST_CONFIG_COOKIE_NAME); + const request = inject(REQUEST); + if (request && cookieName) { + const cookie = request.get('Cookie') ?? ''; + const config = getCookie(cookie, cookieName); + return parseConfigJSON(config); + } + return {}; + }, + }, + ], + }; + } +} diff --git a/core-libs/setup/ssr/tokens/express.tokens.ts b/core-libs/setup/ssr/tokens/express.tokens.ts new file mode 100644 index 00000000000..3e96e30aa19 --- /dev/null +++ b/core-libs/setup/ssr/tokens/express.tokens.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { InjectionToken } from '@angular/core'; +import { Request, Response } from 'express'; + +/** + * Represents an injection token for the Request object of ExpressJS in server-side rendering. + * This token is used to provide the request object to components and services. + * It's a replacement for the `REQUEST` token from `@nguniversal/express-engine` that was removed only in Angular 17. Now this token is provided by Spartacus. + */ +export const REQUEST: InjectionToken = new InjectionToken( + 'REQUEST' +); + +/** + * Represents an injection token for the Response object of ExpressJS in server-side rendering. + * This token is used to provide the response object to components and services. + * It's a replacement for the `RESPONSE` token from `@nguniversal/express-engine` that was removed only in Angular 17. Now this token is provided by Spartacus. + */ +export const RESPONSE: InjectionToken = new InjectionToken( + 'RESPONSE' +); diff --git a/core-libs/setup/ssr/util/request-origin.ts b/core-libs/setup/ssr/util/request-origin.ts deleted file mode 100644 index ca88e68656b..00000000000 --- a/core-libs/setup/ssr/util/request-origin.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Request } from 'express'; - -export function getRequestOrigin(req: Request): string { - // If express is resolving and trusting X-Forwarded-Host, we want to take it - // into an account to properly generate request origin. - const trustProxyFn = req.app.get('trust proxy fn'); - let forwardedHost = req.get('X-Forwarded-Host'); - if (forwardedHost && trustProxyFn(req.connection.remoteAddress, 0)) { - if (forwardedHost.indexOf(',') !== -1) { - // Note: X-Forwarded-Host is normally only ever a - // single value, but this is to be safe. - forwardedHost = forwardedHost - .substring(0, forwardedHost.indexOf(',')) - .trimRight(); - } - return req.protocol + '://' + forwardedHost; - } else { - return req.protocol + '://' + req.get('host'); - } -} diff --git a/core-libs/setup/ssr/util/request-url.ts b/core-libs/setup/ssr/util/request-url.ts deleted file mode 100644 index da6f6e73cdd..00000000000 --- a/core-libs/setup/ssr/util/request-url.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Request } from 'express'; -import { getRequestOrigin } from './request-origin'; - -export function getRequestUrl(req: Request): string { - return getRequestOrigin(req) + req.originalUrl; -} diff --git a/core-libs/setup/test.ts b/core-libs/setup/test.ts deleted file mode 100644 index 46bbd26ac78..00000000000 --- a/core-libs/setup/test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -// Zone.js and zone.js/testing should be imported as FIRST and in this ORDER: -import 'zone.js'; -import 'zone.js/testing'; - -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting, -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: { - context( - path: string, - deep?: boolean, - filter?: RegExp - ): { - keys(): string[]; - (id: string): T; - }; -}; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), - { - teardown: { destroyAfterEach: false }, - } -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().forEach(context); diff --git a/core-libs/setup/tsconfig.lib.json b/core-libs/setup/tsconfig.lib.json index db88dbae316..efc1fb1c51d 100644 --- a/core-libs/setup/tsconfig.lib.json +++ b/core-libs/setup/tsconfig.lib.json @@ -3,16 +3,16 @@ "compilerOptions": { "outDir": "../../out-tsc/lib", "declarationMap": true, - "target": "es2015", - "module": "es2020", + "module": "es2022", "moduleResolution": "node", "declaration": true, "sourceMap": true, "inlineSources": true, + "strict": true, "experimentalDecorators": true, "importHelpers": true, "types": [], - "lib": ["es2020", "dom"], + "lib": ["es2022", "dom"], "paths": { "@spartacus/cart/base/assets": ["dist/cart/base/assets"], "@spartacus/cart/base/components/add-to-cart": [ @@ -81,7 +81,13 @@ "@spartacus/user/profile": ["dist/user/profile"], "@spartacus/user/profile/occ": ["dist/user/profile/occ"], "@spartacus/user/profile/root": ["dist/user/profile/root"], - "@spartacus/storefront": ["dist/storefrontlib"] + "@spartacus/storefront": ["dist/storefrontlib"], + "@spartacus/pdf-invoices/assets": ["dist/pdf-invoices/assets"], + "@spartacus/pdf-invoices/components": ["dist/pdf-invoices/components"], + "@spartacus/pdf-invoices/core": ["dist/pdf-invoices/core"], + "@spartacus/pdf-invoices": ["dist/pdf-invoices"], + "@spartacus/pdf-invoices/occ": ["dist/pdf-invoices/occ"], + "@spartacus/pdf-invoices/root": ["dist/pdf-invoices/root"] } }, "angularCompilerOptions": { @@ -91,5 +97,5 @@ "strictInjectionParameters": true, "enableResourceInlining": true }, - "exclude": ["test.ts", "**/*.spec.ts"] + "exclude": ["setup-jest.ts", "**/*.spec.ts"] } diff --git a/core-libs/setup/tsconfig.spec.json b/core-libs/setup/tsconfig.spec.json index 6bee84b186d..cc1fdb41f4f 100644 --- a/core-libs/setup/tsconfig.spec.json +++ b/core-libs/setup/tsconfig.spec.json @@ -1,12 +1,650 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "baseUrl": "./", "outDir": "../../out-tsc/spec", "strict": false, - "target": "es2015", - "module": "es2020", - "types": ["jasmine", "node"] + "module": "CommonJs", + "types": ["jest"], + "paths": { + "@spartacus/schematics": ["../../projects/schematics/index"], + "@spartacus/setup": ["../../core-libs/setup/public_api"], + "@spartacus/setup/ssr": ["../../core-libs/setup/ssr/public_api"], + "@spartacus/asm/assets": ["../../feature-libs/asm/assets/public_api"], + "@spartacus/asm/components": [ + "../../feature-libs/asm/components/public_api" + ], + "@spartacus/asm/core": ["../../feature-libs/asm/core/public_api"], + "@spartacus/asm/customer-360/assets": [ + "../../feature-libs/asm/customer-360/assets/public_api" + ], + "@spartacus/asm/customer-360/components": [ + "../../feature-libs/asm/customer-360/components/public_api" + ], + "@spartacus/asm/customer-360/core": [ + "../../feature-libs/asm/customer-360/core/public_api" + ], + "@spartacus/asm/customer-360": [ + "../../feature-libs/asm/customer-360/public_api" + ], + "@spartacus/asm/customer-360/occ": [ + "../../feature-libs/asm/customer-360/occ/public_api" + ], + "@spartacus/asm/customer-360/root": [ + "../../feature-libs/asm/customer-360/root/public_api" + ], + "@spartacus/asm": ["../../feature-libs/asm/public_api"], + "@spartacus/asm/occ": ["../../feature-libs/asm/occ/public_api"], + "@spartacus/asm/root": ["../../feature-libs/asm/root/public_api"], + "@spartacus/cart/base/assets": [ + "../../feature-libs/cart/base/assets/public_api" + ], + "@spartacus/cart/base/components/add-to-cart": [ + "../../feature-libs/cart/base/components/add-to-cart/public_api" + ], + "@spartacus/cart/base/components/mini-cart": [ + "../../feature-libs/cart/base/components/mini-cart/public_api" + ], + "@spartacus/cart/base/components": [ + "../../feature-libs/cart/base/components/public_api" + ], + "@spartacus/cart/base/core": [ + "../../feature-libs/cart/base/core/public_api" + ], + "@spartacus/cart/base": ["../../feature-libs/cart/base/public_api"], + "@spartacus/cart/base/occ": [ + "../../feature-libs/cart/base/occ/public_api" + ], + "@spartacus/cart/base/root": [ + "../../feature-libs/cart/base/root/public_api" + ], + "@spartacus/cart/import-export/assets": [ + "../../feature-libs/cart/import-export/assets/public_api" + ], + "@spartacus/cart/import-export/components": [ + "../../feature-libs/cart/import-export/components/public_api" + ], + "@spartacus/cart/import-export/core": [ + "../../feature-libs/cart/import-export/core/public_api" + ], + "@spartacus/cart/import-export": [ + "../../feature-libs/cart/import-export/public_api" + ], + "@spartacus/cart/import-export/root": [ + "../../feature-libs/cart/import-export/root/public_api" + ], + "@spartacus/cart": ["../../feature-libs/cart/public_api"], + "@spartacus/cart/quick-order/assets": [ + "../../feature-libs/cart/quick-order/assets/public_api" + ], + "@spartacus/cart/quick-order/components": [ + "../../feature-libs/cart/quick-order/components/public_api" + ], + "@spartacus/cart/quick-order/core": [ + "../../feature-libs/cart/quick-order/core/public_api" + ], + "@spartacus/cart/quick-order": [ + "../../feature-libs/cart/quick-order/public_api" + ], + "@spartacus/cart/quick-order/root": [ + "../../feature-libs/cart/quick-order/root/public_api" + ], + "@spartacus/cart/saved-cart/assets": [ + "../../feature-libs/cart/saved-cart/assets/public_api" + ], + "@spartacus/cart/saved-cart/components": [ + "../../feature-libs/cart/saved-cart/components/public_api" + ], + "@spartacus/cart/saved-cart/core": [ + "../../feature-libs/cart/saved-cart/core/public_api" + ], + "@spartacus/cart/saved-cart": [ + "../../feature-libs/cart/saved-cart/public_api" + ], + "@spartacus/cart/saved-cart/occ": [ + "../../feature-libs/cart/saved-cart/occ/public_api" + ], + "@spartacus/cart/saved-cart/root": [ + "../../feature-libs/cart/saved-cart/root/public_api" + ], + "@spartacus/cart/wish-list/assets": [ + "../../feature-libs/cart/wish-list/assets/public_api" + ], + "@spartacus/cart/wish-list/components/add-to-wishlist": [ + "../../feature-libs/cart/wish-list/components/add-to-wishlist/public_api" + ], + "@spartacus/cart/wish-list/components": [ + "../../feature-libs/cart/wish-list/components/public_api" + ], + "@spartacus/cart/wish-list/core": [ + "../../feature-libs/cart/wish-list/core/public_api" + ], + "@spartacus/cart/wish-list": [ + "../../feature-libs/cart/wish-list/public_api" + ], + "@spartacus/cart/wish-list/root": [ + "../../feature-libs/cart/wish-list/root/public_api" + ], + "@spartacus/checkout/b2b/assets": [ + "../../feature-libs/checkout/b2b/assets/public_api" + ], + "@spartacus/checkout/b2b/components": [ + "../../feature-libs/checkout/b2b/components/public_api" + ], + "@spartacus/checkout/b2b/core": [ + "../../feature-libs/checkout/b2b/core/public_api" + ], + "@spartacus/checkout/b2b": ["../../feature-libs/checkout/b2b/public_api"], + "@spartacus/checkout/b2b/occ": [ + "../../feature-libs/checkout/b2b/occ/public_api" + ], + "@spartacus/checkout/b2b/root": [ + "../../feature-libs/checkout/b2b/root/public_api" + ], + "@spartacus/checkout/base/assets": [ + "../../feature-libs/checkout/base/assets/public_api" + ], + "@spartacus/checkout/base/components": [ + "../../feature-libs/checkout/base/components/public_api" + ], + "@spartacus/checkout/base/core": [ + "../../feature-libs/checkout/base/core/public_api" + ], + "@spartacus/checkout/base": [ + "../../feature-libs/checkout/base/public_api" + ], + "@spartacus/checkout/base/occ": [ + "../../feature-libs/checkout/base/occ/public_api" + ], + "@spartacus/checkout/base/root": [ + "../../feature-libs/checkout/base/root/public_api" + ], + "@spartacus/checkout": ["../../feature-libs/checkout/public_api"], + "@spartacus/checkout/scheduled-replenishment/assets": [ + "../../feature-libs/checkout/scheduled-replenishment/assets/public_api" + ], + "@spartacus/checkout/scheduled-replenishment/components": [ + "../../feature-libs/checkout/scheduled-replenishment/components/public_api" + ], + "@spartacus/checkout/scheduled-replenishment": [ + "../../feature-libs/checkout/scheduled-replenishment/public_api" + ], + "@spartacus/checkout/scheduled-replenishment/root": [ + "../../feature-libs/checkout/scheduled-replenishment/root/public_api" + ], + "@spartacus/customer-ticketing/assets": [ + "../../feature-libs/customer-ticketing/assets/public_api" + ], + "@spartacus/customer-ticketing/components": [ + "../../feature-libs/customer-ticketing/components/public_api" + ], + "@spartacus/customer-ticketing/core": [ + "../../feature-libs/customer-ticketing/core/public_api" + ], + "@spartacus/customer-ticketing": [ + "../../feature-libs/customer-ticketing/public_api" + ], + "@spartacus/customer-ticketing/occ": [ + "../../feature-libs/customer-ticketing/occ/public_api" + ], + "@spartacus/customer-ticketing/root": [ + "../../feature-libs/customer-ticketing/root/public_api" + ], + "@spartacus/estimated-delivery-date/assets": [ + "../../feature-libs/estimated-delivery-date/assets/public_api" + ], + "@spartacus/estimated-delivery-date": [ + "../../feature-libs/estimated-delivery-date/public_api" + ], + "@spartacus/estimated-delivery-date/root": [ + "../../feature-libs/estimated-delivery-date/root/public_api" + ], + "@spartacus/estimated-delivery-date/show-estimated-delivery-date": [ + "../../feature-libs/estimated-delivery-date/show-estimated-delivery-date/public_api" + ], + "@spartacus/order/assets": ["../../feature-libs/order/assets/public_api"], + "@spartacus/order/components": [ + "../../feature-libs/order/components/public_api" + ], + "@spartacus/order/core": ["../../feature-libs/order/core/public_api"], + "@spartacus/order": ["../../feature-libs/order/public_api"], + "@spartacus/order/occ": ["../../feature-libs/order/occ/public_api"], + "@spartacus/order/root": ["../../feature-libs/order/root/public_api"], + "@spartacus/organization/account-summary/assets": [ + "../../feature-libs/organization/account-summary/assets/public_api" + ], + "@spartacus/organization/account-summary/components": [ + "../../feature-libs/organization/account-summary/components/public_api" + ], + "@spartacus/organization/account-summary/core": [ + "../../feature-libs/organization/account-summary/core/public_api" + ], + "@spartacus/organization/account-summary": [ + "../../feature-libs/organization/account-summary/public_api" + ], + "@spartacus/organization/account-summary/occ": [ + "../../feature-libs/organization/account-summary/occ/public_api" + ], + "@spartacus/organization/account-summary/root": [ + "../../feature-libs/organization/account-summary/root/public_api" + ], + "@spartacus/organization/administration/assets": [ + "../../feature-libs/organization/administration/assets/public_api" + ], + "@spartacus/organization/administration/components": [ + "../../feature-libs/organization/administration/components/public_api" + ], + "@spartacus/organization/administration/core": [ + "../../feature-libs/organization/administration/core/public_api" + ], + "@spartacus/organization/administration": [ + "../../feature-libs/organization/administration/public_api" + ], + "@spartacus/organization/administration/occ": [ + "../../feature-libs/organization/administration/occ/public_api" + ], + "@spartacus/organization/administration/root": [ + "../../feature-libs/organization/administration/root/public_api" + ], + "@spartacus/organization": ["../../feature-libs/organization/public_api"], + "@spartacus/organization/order-approval/assets": [ + "../../feature-libs/organization/order-approval/assets/public_api" + ], + "@spartacus/organization/order-approval": [ + "../../feature-libs/organization/order-approval/public_api" + ], + "@spartacus/organization/order-approval/root": [ + "../../feature-libs/organization/order-approval/root/public_api" + ], + "@spartacus/organization/unit-order/assets": [ + "../../feature-libs/organization/unit-order/assets/public_api" + ], + "@spartacus/organization/unit-order/components": [ + "../../feature-libs/organization/unit-order/components/public_api" + ], + "@spartacus/organization/unit-order/core": [ + "../../feature-libs/organization/unit-order/core/public_api" + ], + "@spartacus/organization/unit-order": [ + "../../feature-libs/organization/unit-order/public_api" + ], + "@spartacus/organization/unit-order/occ": [ + "../../feature-libs/organization/unit-order/occ/public_api" + ], + "@spartacus/organization/unit-order/root": [ + "../../feature-libs/organization/unit-order/root/public_api" + ], + "@spartacus/organization/user-registration/assets": [ + "../../feature-libs/organization/user-registration/assets/public_api" + ], + "@spartacus/organization/user-registration/components": [ + "../../feature-libs/organization/user-registration/components/public_api" + ], + "@spartacus/organization/user-registration/core": [ + "../../feature-libs/organization/user-registration/core/public_api" + ], + "@spartacus/organization/user-registration": [ + "../../feature-libs/organization/user-registration/public_api" + ], + "@spartacus/organization/user-registration/occ": [ + "../../feature-libs/organization/user-registration/occ/public_api" + ], + "@spartacus/organization/user-registration/root": [ + "../../feature-libs/organization/user-registration/root/public_api" + ], + "@spartacus/pdf-invoices/assets": [ + "../../feature-libs/pdf-invoices/assets/public_api" + ], + "@spartacus/pdf-invoices/components": [ + "../../feature-libs/pdf-invoices/components/public_api" + ], + "@spartacus/pdf-invoices/core": [ + "../../feature-libs/pdf-invoices/core/public_api" + ], + "@spartacus/pdf-invoices": ["../../feature-libs/pdf-invoices/public_api"], + "@spartacus/pdf-invoices/occ": [ + "../../feature-libs/pdf-invoices/occ/public_api" + ], + "@spartacus/pdf-invoices/root": [ + "../../feature-libs/pdf-invoices/root/public_api" + ], + "@spartacus/pickup-in-store/assets": [ + "../../feature-libs/pickup-in-store/assets/public_api" + ], + "@spartacus/pickup-in-store/components": [ + "../../feature-libs/pickup-in-store/components/public_api" + ], + "@spartacus/pickup-in-store/core": [ + "../../feature-libs/pickup-in-store/core/public_api" + ], + "@spartacus/pickup-in-store": [ + "../../feature-libs/pickup-in-store/public_api" + ], + "@spartacus/pickup-in-store/occ": [ + "../../feature-libs/pickup-in-store/occ/public_api" + ], + "@spartacus/pickup-in-store/root": [ + "../../feature-libs/pickup-in-store/root/public_api" + ], + "@spartacus/product-configurator/common/assets": [ + "../../feature-libs/product-configurator/common/assets/public_api" + ], + "@spartacus/product-configurator/common": [ + "../../feature-libs/product-configurator/common/public_api" + ], + "@spartacus/product-configurator": [ + "../../feature-libs/product-configurator/public_api" + ], + "@spartacus/product-configurator/rulebased/cpq": [ + "../../feature-libs/product-configurator/rulebased/cpq/public_api" + ], + "@spartacus/product-configurator/rulebased": [ + "../../feature-libs/product-configurator/rulebased/public_api" + ], + "@spartacus/product-configurator/rulebased/root": [ + "../../feature-libs/product-configurator/rulebased/root/public_api" + ], + "@spartacus/product-configurator/textfield": [ + "../../feature-libs/product-configurator/textfield/public_api" + ], + "@spartacus/product-configurator/textfield/root": [ + "../../feature-libs/product-configurator/textfield/root/public_api" + ], + "@spartacus/product-multi-dimensional/list": [ + "../../feature-libs/product-multi-dimensional/list/public_api" + ], + "@spartacus/product-multi-dimensional/list/root": [ + "../../feature-libs/product-multi-dimensional/list/root/public_api" + ], + "@spartacus/product-multi-dimensional": [ + "../../feature-libs/product-multi-dimensional/public_api" + ], + "@spartacus/product-multi-dimensional/selector/assets": [ + "../../feature-libs/product-multi-dimensional/selector/assets/public_api" + ], + "@spartacus/product-multi-dimensional/selector/components": [ + "../../feature-libs/product-multi-dimensional/selector/components/public_api" + ], + "@spartacus/product-multi-dimensional/selector/core": [ + "../../feature-libs/product-multi-dimensional/selector/core/public_api" + ], + "@spartacus/product-multi-dimensional/selector": [ + "../../feature-libs/product-multi-dimensional/selector/public_api" + ], + "@spartacus/product-multi-dimensional/selector/occ": [ + "../../feature-libs/product-multi-dimensional/selector/occ/public_api" + ], + "@spartacus/product-multi-dimensional/selector/root": [ + "../../feature-libs/product-multi-dimensional/selector/root/public_api" + ], + "@spartacus/product/bulk-pricing/assets": [ + "../../feature-libs/product/bulk-pricing/assets/public_api" + ], + "@spartacus/product/bulk-pricing/components": [ + "../../feature-libs/product/bulk-pricing/components/public_api" + ], + "@spartacus/product/bulk-pricing/core": [ + "../../feature-libs/product/bulk-pricing/core/public_api" + ], + "@spartacus/product/bulk-pricing": [ + "../../feature-libs/product/bulk-pricing/public_api" + ], + "@spartacus/product/bulk-pricing/occ": [ + "../../feature-libs/product/bulk-pricing/occ/public_api" + ], + "@spartacus/product/bulk-pricing/root": [ + "../../feature-libs/product/bulk-pricing/root/public_api" + ], + "@spartacus/product/future-stock/assets": [ + "../../feature-libs/product/future-stock/assets/public_api" + ], + "@spartacus/product/future-stock/components": [ + "../../feature-libs/product/future-stock/components/public_api" + ], + "@spartacus/product/future-stock/core": [ + "../../feature-libs/product/future-stock/core/public_api" + ], + "@spartacus/product/future-stock": [ + "../../feature-libs/product/future-stock/public_api" + ], + "@spartacus/product/future-stock/occ": [ + "../../feature-libs/product/future-stock/occ/public_api" + ], + "@spartacus/product/future-stock/root": [ + "../../feature-libs/product/future-stock/root/public_api" + ], + "@spartacus/product/image-zoom/assets": [ + "../../feature-libs/product/image-zoom/assets/public_api" + ], + "@spartacus/product/image-zoom/components": [ + "../../feature-libs/product/image-zoom/components/public_api" + ], + "@spartacus/product/image-zoom": [ + "../../feature-libs/product/image-zoom/public_api" + ], + "@spartacus/product/image-zoom/root": [ + "../../feature-libs/product/image-zoom/root/public_api" + ], + "@spartacus/product": ["../../feature-libs/product/public_api"], + "@spartacus/product/variants/assets": [ + "../../feature-libs/product/variants/assets/public_api" + ], + "@spartacus/product/variants/components": [ + "../../feature-libs/product/variants/components/public_api" + ], + "@spartacus/product/variants": [ + "../../feature-libs/product/variants/public_api" + ], + "@spartacus/product/variants/occ": [ + "../../feature-libs/product/variants/occ/public_api" + ], + "@spartacus/product/variants/root": [ + "../../feature-libs/product/variants/root/public_api" + ], + "@spartacus/qualtrics/components": [ + "../../feature-libs/qualtrics/components/public_api" + ], + "@spartacus/qualtrics": ["../../feature-libs/qualtrics/public_api"], + "@spartacus/qualtrics/root": [ + "../../feature-libs/qualtrics/root/public_api" + ], + "@spartacus/quote/assets": ["../../feature-libs/quote/assets/public_api"], + "@spartacus/quote/components/cart-guard": [ + "../../feature-libs/quote/components/cart-guard/public_api" + ], + "@spartacus/quote/components": [ + "../../feature-libs/quote/components/public_api" + ], + "@spartacus/quote/components/request-button": [ + "../../feature-libs/quote/components/request-button/public_api" + ], + "@spartacus/quote/core": ["../../feature-libs/quote/core/public_api"], + "@spartacus/quote": ["../../feature-libs/quote/public_api"], + "@spartacus/quote/occ": ["../../feature-libs/quote/occ/public_api"], + "@spartacus/quote/root": ["../../feature-libs/quote/root/public_api"], + "@spartacus/requested-delivery-date/assets": [ + "../../feature-libs/requested-delivery-date/assets/public_api" + ], + "@spartacus/requested-delivery-date/core": [ + "../../feature-libs/requested-delivery-date/core/public_api" + ], + "@spartacus/requested-delivery-date": [ + "../../feature-libs/requested-delivery-date/public_api" + ], + "@spartacus/requested-delivery-date/occ": [ + "../../feature-libs/requested-delivery-date/occ/public_api" + ], + "@spartacus/requested-delivery-date/root": [ + "../../feature-libs/requested-delivery-date/root/public_api" + ], + "@spartacus/smartedit/core": [ + "../../feature-libs/smartedit/core/public_api" + ], + "@spartacus/smartedit": ["../../feature-libs/smartedit/public_api"], + "@spartacus/smartedit/root": [ + "../../feature-libs/smartedit/root/public_api" + ], + "@spartacus/storefinder/assets": [ + "../../feature-libs/storefinder/assets/public_api" + ], + "@spartacus/storefinder/components": [ + "../../feature-libs/storefinder/components/public_api" + ], + "@spartacus/storefinder/core": [ + "../../feature-libs/storefinder/core/public_api" + ], + "@spartacus/storefinder": ["../../feature-libs/storefinder/public_api"], + "@spartacus/storefinder/occ": [ + "../../feature-libs/storefinder/occ/public_api" + ], + "@spartacus/storefinder/root": [ + "../../feature-libs/storefinder/root/public_api" + ], + "@spartacus/tracking": ["../../feature-libs/tracking/public_api"], + "@spartacus/tracking/personalization/core": [ + "../../feature-libs/tracking/personalization/core/public_api" + ], + "@spartacus/tracking/personalization": [ + "../../feature-libs/tracking/personalization/public_api" + ], + "@spartacus/tracking/personalization/root": [ + "../../feature-libs/tracking/personalization/root/public_api" + ], + "@spartacus/tracking/tms/aep": [ + "../../feature-libs/tracking/tms/aep/public_api" + ], + "@spartacus/tracking/tms/core": [ + "../../feature-libs/tracking/tms/core/public_api" + ], + "@spartacus/tracking/tms/gtm": [ + "../../feature-libs/tracking/tms/gtm/public_api" + ], + "@spartacus/tracking/tms": ["../../feature-libs/tracking/tms/public_api"], + "@spartacus/user/account/assets": [ + "../../feature-libs/user/account/assets/public_api" + ], + "@spartacus/user/account/components": [ + "../../feature-libs/user/account/components/public_api" + ], + "@spartacus/user/account/core": [ + "../../feature-libs/user/account/core/public_api" + ], + "@spartacus/user/account": ["../../feature-libs/user/account/public_api"], + "@spartacus/user/account/occ": [ + "../../feature-libs/user/account/occ/public_api" + ], + "@spartacus/user/account/root": [ + "../../feature-libs/user/account/root/public_api" + ], + "@spartacus/user": ["../../feature-libs/user/public_api"], + "@spartacus/user/profile/assets": [ + "../../feature-libs/user/profile/assets/public_api" + ], + "@spartacus/user/profile/components": [ + "../../feature-libs/user/profile/components/public_api" + ], + "@spartacus/user/profile/core": [ + "../../feature-libs/user/profile/core/public_api" + ], + "@spartacus/user/profile": ["../../feature-libs/user/profile/public_api"], + "@spartacus/user/profile/occ": [ + "../../feature-libs/user/profile/occ/public_api" + ], + "@spartacus/user/profile/root": [ + "../../feature-libs/user/profile/root/public_api" + ], + "@spartacus/cdc/assets": ["../../integration-libs/cdc/assets/public_api"], + "@spartacus/cdc/components": [ + "../../integration-libs/cdc/components/public_api" + ], + "@spartacus/cdc/core": ["../../integration-libs/cdc/core/public_api"], + "@spartacus/cdc": ["../../integration-libs/cdc/public_api"], + "@spartacus/cdc/organization/administration": [ + "../../integration-libs/cdc/organization/administration/public_api" + ], + "@spartacus/cdc/organization/user-registration": [ + "../../integration-libs/cdc/organization/user-registration/public_api" + ], + "@spartacus/cdc/root": ["../../integration-libs/cdc/root/public_api"], + "@spartacus/cdc/user-account": [ + "../../integration-libs/cdc/user-account/public_api" + ], + "@spartacus/cdc/user-profile": [ + "../../integration-libs/cdc/user-profile/public_api" + ], + "@spartacus/cdp/customer-ticketing": [ + "../../integration-libs/cdp/customer-ticketing/public_api" + ], + "@spartacus/cdp": ["../../integration-libs/cdp/public_api"], + "@spartacus/cds/assets": ["../../integration-libs/cds/assets/public_api"], + "@spartacus/cds": ["../../integration-libs/cds/public_api"], + "@spartacus/cpq-quote/assets": [ + "../../integration-libs/cpq-quote/assets/public_api" + ], + "@spartacus/cpq-quote/cpq-quote-discount": [ + "../../integration-libs/cpq-quote/cpq-quote-discount/public_api" + ], + "@spartacus/cpq-quote": ["../../integration-libs/cpq-quote/public_api"], + "@spartacus/cpq-quote/root": [ + "../../integration-libs/cpq-quote/root/public_api" + ], + "@spartacus/digital-payments/assets": [ + "../../integration-libs/digital-payments/assets/public_api" + ], + "@spartacus/digital-payments": [ + "../../integration-libs/digital-payments/public_api" + ], + "@spartacus/epd-visualization/assets": [ + "../../integration-libs/epd-visualization/assets/public_api" + ], + "@spartacus/epd-visualization/components": [ + "../../integration-libs/epd-visualization/components/public_api" + ], + "@spartacus/epd-visualization/core": [ + "../../integration-libs/epd-visualization/core/public_api" + ], + "@spartacus/epd-visualization/epd-visualization-api": [ + "../../integration-libs/epd-visualization/epd-visualization-api/public_api" + ], + "@spartacus/epd-visualization": [ + "../../integration-libs/epd-visualization/public_api" + ], + "@spartacus/epd-visualization/root": [ + "../../integration-libs/epd-visualization/root/public_api" + ], + "@spartacus/omf": ["../../integration-libs/omf/public_api"], + "@spartacus/omf/order": ["../../integration-libs/omf/order/public_api"], + "@spartacus/omf/root": ["../../integration-libs/omf/root/public_api"], + "@spartacus/opps": ["../../integration-libs/opps/public_api"], + "@spartacus/opps/root": ["../../integration-libs/opps/root/public_api"], + "@spartacus/s4-service/assets": [ + "../../integration-libs/s4-service/assets/public_api" + ], + "@spartacus/s4-service/checkout": [ + "../../integration-libs/s4-service/checkout/public_api" + ], + "@spartacus/s4-service": ["../../integration-libs/s4-service/public_api"], + "@spartacus/s4-service/order": [ + "../../integration-libs/s4-service/order/public_api" + ], + "@spartacus/s4-service/root": [ + "../../integration-libs/s4-service/root/public_api" + ], + "@spartacus/s4om/assets": [ + "../../integration-libs/s4om/assets/public_api" + ], + "@spartacus/s4om": ["../../integration-libs/s4om/public_api"], + "@spartacus/s4om/root": ["../../integration-libs/s4om/root/public_api"], + "@spartacus/segment-refs": [ + "../../integration-libs/segment-refs/public_api" + ], + "@spartacus/segment-refs/root": [ + "../../integration-libs/segment-refs/root/public_api" + ], + "@spartacus/assets": ["../../projects/assets/src/public_api"], + "@spartacus/core": ["../../projects/core/public_api"], + "@spartacus/storefront": ["../../projects/storefrontlib/public_api"] + } }, - "files": ["test.ts"], - "include": ["**/*.spec.ts", "**/*.d.ts"] + "include": ["ssr/**/*.spec.ts", "ssr/**/*.d.ts"] } diff --git a/cumulus-configuration.json b/cumulus-configuration.json new file mode 100644 index 00000000000..262de4b84cf --- /dev/null +++ b/cumulus-configuration.json @@ -0,0 +1,3 @@ +{ + "backlogIds": ["CXSPA"] +} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 3c05fbc2b55..af504b93f28 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,7 @@ # Spartacus Documentation -All Spartacus documentation is published on our dedicated [Spartacus Documentation site](https://sap.github.io/spartacus-docs/). +Starting with version 5.0, “SAP Commerce Cloud, composable storefront” is the name for the official release of project “Spartacus” libraries published by SAP. The officially supported composable storefront is available to SAP Commerce Cloud customers. Documentation is available on the [SAP Help Portal](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT?locale=en-US). -The installation instructions provided in the `archived_installation_docs` directory allow you to set up a Spartacus-based project with an older version of SAP Commerce Cloud. However, some features and bug fixes require the latest version of SAP Commerce Cloud. It is always recommended to run your Spartacus-based storefront with the latest version of SAP Commerce Cloud. +Composable storefront is based off the Spartacus open source code, and is included in the SAP Commerce Cloud license at no extra cost. Composable storefront has a roll-forward update policy. + +On-premise customers may still use Spartacus open source. For more information, see [Self-Publishing Spartacus Libraries Using the Open Source Code](self-publishing-spartacus-libraries.md). diff --git a/docs/archived_installation_docs/README.md b/docs/archived_installation_docs/README.md deleted file mode 100644 index 3eff69837ee..00000000000 --- a/docs/archived_installation_docs/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Working with Older Versions of SAP Commerce Cloud - -**Note:** It is recommended to run your Spartacus-based storefront with the latest version of SAP Commerce Cloud. - -The installation instructions provided here allow you to set up a Spartacus-based project with an older version of SAP Commerce Cloud. Please note that some features and bug fixes require the latest version of SAP Commerce Cloud. - -The installation instructions provided here are organized according to the available releases of SAP Commerce Cloud that work with Spartacus. For example, if you are using SAP Commerce Cloud 1808 for your back end, then you will find all the relevant setup information in the `back_end_installation/1808` and `spartacus_installation/1808` directories. \ No newline at end of file diff --git a/docs/archived_installation_docs/back_end_installation/1808/back_end_installation.md b/docs/archived_installation_docs/back_end_installation/1808/back_end_installation.md deleted file mode 100644 index e9028be2dbb..00000000000 --- a/docs/archived_installation_docs/back_end_installation/1808/back_end_installation.md +++ /dev/null @@ -1,23 +0,0 @@ -The Spartacus JavaScript Storefront uses SAP Commerce Cloud for its back end, and makes use of the sample data from the B2C Accelerator electronics storefront in particular. - -Note: The latest release of SAP Commerce Cloud is recommended. These instructions are based on a setup with SAP Commerce Cloud Release 1808 as the back end. - -Perform the following steps to set up your back end: - -* Install a new instance of SAP Commerce Cloud using the `b2c_acc_plus` recipe, as follows: - - 1. In the `installer/recipes` folder of SAP Commerce Cloud, make a copy of `b2c_acc_plus` and call it `b2c_for_spartacus`. - - 2. Delete the existing `build.gradle` file in the `b2c_for_spartacus` recipe folder. - - 3. Add this [build.gradle](build.gradle) file to your `b2c_for_spartacus` recipe folder. - - 4. Follow the instructions in https://help.hybris.com/1808/hcd/8c46c266866910149666a0fe4caeee4e.html to install, initialize and start a new instance of SAP Commerce 1808, using `b2c_for_spartacus` as the recipe name. - -* Import `spartacus_sample_data.impex`, which you can download here: https://help.hybris.com/1808/api/spartacus/spartacus_sample_data.impex - - For more information on importing ImpEx, see https://help.hybris.com/1808/hcd/2f095d195c0740aab4b0bbdf0f0a2d12.html. - -* Configure your OCC client, as described here: https://help.hybris.com/1808/hcd/627c92db29ce4fce8b01ffbe478a8b3b.html#loio4079b4327ac243b6b3bd507cda6d74ff - - diff --git a/docs/archived_installation_docs/back_end_installation/1808/build.gradle b/docs/archived_installation_docs/back_end_installation/1808/build.gradle deleted file mode 100644 index 956637980fa..00000000000 --- a/docs/archived_installation_docs/back_end_installation/1808/build.gradle +++ /dev/null @@ -1,164 +0,0 @@ -def config = { - localProperties { - property 'recaptcha.publickey', '' - property 'recaptcha.privatekey', '' - property 'googleApiKey', '' - property 'apiregistryservices.events.exporting', 'false' - property 'csrf.allowed.url.patterns', '/[^/]+(/[^?]*)+(sop/response)$,/[^/]+(/[^?]*)+(merchant_callback)$,/[^/]+(/[^?]*)+(hop/response)$,/[^/]+(/[^?]*)+(language)$,/[^/]+(/[^?]*)+(currency)$,/(events)$' - - } - - extensions { - extName 'mcc' - extName 'adaptivesearchsolr' - extName 'adaptivesearchbackoffice' - extName 'adaptivesearchsamplesaddon' - extName 'adaptivesearchwebservices' - extName 'commerceservicesbackoffice' - extName 'solrfacetsearchbackoffice' - extName 'solrserver' - -// extName 'yacceleratorcockpits' - extName 'yacceleratorbackoffice' - extName 'yacceleratorinitialdata' - extName 'yacceleratorfulfilmentprocess' - extName 'yacceleratorstorefront' - extName 'ycommercewebservices' - extName 'ycommercewebservicestest' - - extName 'electronicsstore' - extName 'apparelstore' - - extName 'captchaaddon' - extName 'acceleratorwebservicesaddon' - extName 'commerceorgsamplesaddon' - extName 'orderselfserviceaddon' - - extName 'rulebuilderbackoffice' - extName 'couponbackoffice' - extName 'droolsruleengineservices' - extName 'couponfacades' - extName 'promotionenginesamplesaddon' - - extName 'assistedservicestorefront' - extName 'assistedservicewebservices' - extName 'assistedserviceyprofileaddon' - extName 'assistedservicecustomerinterestsaddon' - extName 'assistedservicepromotionaddon' - - extName 'customerticketingaddon' - extName 'customersupportbackoffice' - - extName 'profiletagaddon' - extName 'profileservices' - extName 'yaasconfigurationbackoffice' - - extName 'textfieldconfiguratortemplatebackoffice' - extName 'textfieldconfiguratortemplateaddon' - -// extName 'cmswebservices' -// extName 'smarteditwebservices' -// extName 'cmssmarteditwebservices' - extName 'permissionswebservices' - extName 'smarteditaddon' - extName 'cmssmartedit' - extName 'cmsbackoffice' - extName 'cmsoccaddon' - - extName 'consignmenttrackingaddon' - extName 'consignmenttrackingmock' - extName 'consignmenttrackingbackoffice' - - extName 'notificationaddon' - extName 'customerinterestsaddon' - extName 'stocknotificationaddon' - extName 'configurablebundleaddon' - -// extName 'previewpersonalizationweb' -// extName 'personalizationcmsweb' -// extName 'personalizationsmartedit' -// extName 'personalizationpromotionssmartedit' -// extName 'personalizationsampledataaddon' -// extName 'personalizationpromotionssampledataaddon' -// extName 'personalizationyprofile' -// extName 'personalizationpromotions' -// extName 'personalizationpromotionsweb' -// extName 'personalizationservicesbackoffice' -// extName 'personalizationpromotionsbackoffice' -// extName 'personalizationcmsbackoffice' -// extName 'personalizationintegrationbackoffice' -// extName 'personalizationsearchbackoffice' -// extName 'personalizationsearchsmartedit' -// extName 'personalizationsearchweb' -// extName 'personalizationsearchsamplesaddon' -// extName 'personalizationyprofilesampledataaddon' -// extName 'personalizationaddon' - - // extName 'assistedserviceatddtests' - // extName 'promotionengineatddtests' - // extName 'textfieldconfiguratortemplateatddtests' - // extName 'configurablebundleatddtests' - - extName 'configurablebundlecockpits' - - extName 'pcmbackofficesamplesaddon' - - extName 'b2cangularaddon' - - extName 'xyformscockpits' - extName 'xyformsbackoffice' - extName 'xyformsstorefrontcommons' - extName 'xyformssamples' - extName 'xyformsweb' - extName 'platformbackoffice' - // extName 'xyformsatddtests' - extName 'orbeonweb' - extName 'consignmenttrackingoccaddon' - extName 'customerinterestsoccaddon' - extName 'notificationoccaddon' - extName 'stocknotificationoccaddon' - - extName 'kymaintegrationbackoffice' - extName 'kymaintegrationsampledata' - - extName 'odata2webservices' - extName 'odata2webservicesfeaturetests' - extName 'integrationbackoffice' - } -} - -task setup << { - apply plugin: 'installer-platform-plugin' - apply plugin: 'installer-addon-plugin' - def pl = platform(config) - pl.setup() - - pl.project.addons { - names "captchaaddon,commerceorgsamplesaddon,promotionenginesamplesaddon,assistedservicestorefront,assistedservicecustomerinterestsaddon,assistedserviceyprofileaddon,assistedservicepromotionaddon,customerticketingaddon,textfieldconfiguratortemplateaddon,smarteditaddon,consignmenttrackingaddon,notificationaddon,customerinterestsaddon,stocknotificationaddon,orderselfserviceaddon,adaptivesearchsamplesaddon,configurablebundleaddon,"+ - "pcmbackofficesamplesaddon,b2cangularaddon,xyformssamples,xyformsstorefrontcommons,profiletagaddon" - template "yacceleratorstorefront" - storeFronts "yacceleratorstorefront" - platform pl - } - - pl.project.addons { - names "acceleratorwebservicesaddon,consignmenttrackingoccaddon,customerinterestsoccaddon,notificationoccaddon,stocknotificationoccaddon,cmsoccaddon" - template "ycommercewebservices" - storeFronts "ycommercewebservices" - platform pl - } -} - -task initialize << { - apply plugin: 'installer-platform-plugin' - def pl = platform(config) - pl.build() - pl.initialize() -} - - -task start << { - apply plugin: 'installer-platform-plugin' - def pl = platform(config) - pl.start() -} diff --git a/docs/archived_installation_docs/back_end_installation/1811/back_end_installation.md b/docs/archived_installation_docs/back_end_installation/1811/back_end_installation.md deleted file mode 100644 index ea655572ea0..00000000000 --- a/docs/archived_installation_docs/back_end_installation/1811/back_end_installation.md +++ /dev/null @@ -1,21 +0,0 @@ -The Spartacus JavaScript Storefront uses SAP Commerce Cloud for its back end, and makes use of the sample data from the B2C Accelerator electronics storefront in particular. - -Note: The latest release of SAP Commerce Cloud is recommended. These instructions are based on a setup with SAP Commerce Cloud Release 1811 as the back end. - -Perform the following steps to set up your back end: - -* Install a new instance of SAP Commerce Cloud using the `b2c_acc_plus` recipe, as follows: - - 1. In the `installer/recipes` folder of SAP Commerce Cloud, make a copy of `b2c_acc_plus` and call it `b2c_for_spartacus`. - - 2. Delete the existing `build.gradle` file in the `b2c_for_spartacus` recipe folder. - - 3. Add this [build.gradle](build.gradle) file to your `b2c_for_spartacus` recipe folder. - - 4. Follow the instructions in https://help.hybris.com/1811/hcd/8c46c266866910149666a0fe4caeee4e.html to install, initialize and start a new instance of SAP Commerce 1811, using `b2c_for_spartacus` as the recipe name. - -* Import `spartacus_sample_data.impex`, which you can download here: https://help.hybris.com/1808/api/spartacus/spartacus_sample_data.impex - - For more information on importing ImpEx, see https://help.hybris.com/1811/hcd/2f095d195c0740aab4b0bbdf0f0a2d12.html. - -* Configure your OCC client, as described here: https://help.hybris.com/1811/hcd/627c92db29ce4fce8b01ffbe478a8b3b.html#loio4079b4327ac243b6b3bd507cda6d74ff \ No newline at end of file diff --git a/docs/archived_installation_docs/back_end_installation/1811/build.gradle b/docs/archived_installation_docs/back_end_installation/1811/build.gradle deleted file mode 100644 index f22cccd5def..00000000000 --- a/docs/archived_installation_docs/back_end_installation/1811/build.gradle +++ /dev/null @@ -1,169 +0,0 @@ -def config = { - localProperties { - property 'recaptcha.publickey', '' - property 'recaptcha.privatekey', '' - property 'googleApiKey', '' - property 'apiregistryservices.events.exporting', 'false' - property 'csrf.allowed.url.patterns', '/[^/]+(/[^?]*)+(sop/response)$,/[^/]+(/[^?]*)+(merchant_callback)$,/[^/]+(/[^?]*)+(hop/response)$,/[^/]+(/[^?]*)+(language)$,/[^/]+(/[^?]*)+(currency)$,/(events)$' - - } - - extensions { - extName 'adaptivesearchsolr' - extName 'adaptivesearchbackoffice' - extName 'adaptivesearchsamplesaddon' - extName 'adaptivesearchwebservices' - extName 'commerceservicesbackoffice' - extName 'solrfacetsearchbackoffice' - extName 'solrserver' - - extName 'yacceleratorbackoffice' - extName 'yacceleratorinitialdata' - extName 'yacceleratorfulfilmentprocess' - extName 'yacceleratorstorefront' - extName 'ycommercewebservices' - extName 'ycommercewebservicestest' - - extName 'electronicsstore' - extName 'apparelstore' - - extName 'captchaaddon' - extName 'acceleratorwebservicesaddon' - extName 'commerceorgsamplesaddon' - extName 'orderselfserviceaddon' - - extName 'rulebuilderbackoffice' - extName 'couponbackoffice' - extName 'droolsruleengineservices' - extName 'couponfacades' - extName 'couponservices' - extName 'promotionenginesamplesaddon' - - extName 'assistedservicestorefront' - extName 'assistedservicewebservices' - extName 'assistedserviceyprofileaddon' - extName 'assistedservicecustomerinterestsaddon' - extName 'assistedservicepromotionaddon' - - extName 'customerticketingaddon' - extName 'customersupportbackoffice' - - extName 'profiletagaddon' - extName 'profileservices' - extName 'yaasconfigurationbackoffice' - - extName 'textfieldconfiguratortemplatebackoffice' - extName 'textfieldconfiguratortemplateaddon' - -// extName 'cmswebservices' -// extName 'smarteditwebservices' -// extName 'cmssmarteditwebservices' - extName 'permissionswebservices' - extName 'smarteditaddon' - extName 'cmssmartedit' - extName 'cmsbackoffice' - extName 'cmsoccaddon' - - extName 'consignmenttrackingaddon' - extName 'consignmenttrackingmock' - extName 'consignmenttrackingbackoffice' - - extName 'notificationaddon' - extName 'customerinterestsaddon' - extName 'stocknotificationaddon' - extName 'configurablebundleaddon' - -// extName 'previewpersonalizationweb' -// extName 'personalizationcmsweb' -// extName 'personalizationsmartedit' -// extName 'personalizationpromotionssmartedit' -// extName 'personalizationsampledataaddon' -// extName 'personalizationpromotionssampledataaddon' -// extName 'personalizationyprofile' -// extName 'personalizationpromotions' -// extName 'personalizationpromotionsweb' -// extName 'personalizationservicesbackoffice' -// extName 'personalizationpromotionsbackoffice' -// extName 'personalizationcmsbackoffice' -// extName 'personalizationintegrationbackoffice' -// extName 'personalizationsearchbackoffice' -// extName 'personalizationsearchsmartedit' -// extName 'personalizationsearchweb' -// extName 'personalizationsearchsamplesaddon' -// extName 'personalizationyprofilesampledataaddon' -// extName 'personalizationaddon' - - - extName 'pcmbackofficesamplesaddon' - - - extName 'xyformsbackoffice' - extName 'xyformsstorefrontcommons' - extName 'xyformssamples' - extName 'xyformsweb' - extName 'platformbackoffice' - extName 'orbeonweb' - extName 'consignmenttrackingoccaddon' - extName 'customerinterestsoccaddon' - extName 'notificationoccaddon' - extName 'stocknotificationoccaddon' - - extName 'kymaintegrationbackoffice' - extName 'kymaintegrationsampledata' - - /* integration-apis */ - extName 'outboundservices' - extName 'odata2webservices' - extName 'odata2webservicesfeaturetests' - extName 'integrationbackoffice' - -// extName 'merchandisingaddon' -// extName 'merchandisingcmswebservices' -// extName 'merchandisingsmartedit' -// extName 'merchandisingservices' -// extName 'merchandisingservicesbackoffice' -// extName 'merchandisingstorefrontsampledataaddon' - } -} - -task setup { - doLast { - apply plugin: 'installer-platform-plugin' - apply plugin: 'installer-addon-plugin' - def pl = platform(config) - pl.setup() - - pl.project.addons { - names "captchaaddon,commerceorgsamplesaddon,promotionenginesamplesaddon,assistedservicestorefront,assistedservicecustomerinterestsaddon,assistedserviceyprofileaddon,assistedservicepromotionaddon,customerticketingaddon,textfieldconfiguratortemplateaddon,smarteditaddon,consignmenttrackingaddon,notificationaddon,customerinterestsaddon,stocknotificationaddon,orderselfserviceaddon,adaptivesearchsamplesaddon,configurablebundleaddon," + - "pcmbackofficesamplesaddon,xyformssamples,xyformsstorefrontcommons,profiletagaddon" - template "yacceleratorstorefront" - storeFronts "yacceleratorstorefront" - platform pl - } - - pl.project.addons { - names "acceleratorwebservicesaddon,consignmenttrackingoccaddon,customerinterestsoccaddon,notificationoccaddon,stocknotificationoccaddon,cmsoccaddon" - template "ycommercewebservices" - storeFronts "ycommercewebservices" - platform pl - } - } -} - -task initialize { - doLast { - apply plugin: 'installer-platform-plugin' - def pl = platform(config) - pl.build() - pl.initialize() - } -} - - -task start { - doLast { - apply plugin: 'installer-platform-plugin' - def pl = platform(config) - pl.start() - } -} diff --git a/docs/archived_installation_docs/back_end_installation/README.md b/docs/archived_installation_docs/back_end_installation/README.md deleted file mode 100644 index cda4c3fc781..00000000000 --- a/docs/archived_installation_docs/back_end_installation/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Installing SAP Commerce Cloud - -The SAP Commerce Cloud installation instructions provided here are organized according to the available releases of SAP Commerce Cloud that work with Spartacus. For example, if you are using SAP Commerce Cloud 1808 for your back end, then you will find all the relevant setup information in the `1808` directory. - -**Note:** It is recommended to run your Spartacus-based storefront with the latest version of SAP Commerce Cloud. Also note that some features and bug fixes require the latest version of SAP Commerce Cloud. diff --git a/docs/archived_installation_docs/impex_sample_data/1808/spartacus_sample_data.impex b/docs/archived_installation_docs/impex_sample_data/1808/spartacus_sample_data.impex deleted file mode 100644 index 90f2c601d4c..00000000000 --- a/docs/archived_installation_docs/impex_sample_data/1808/spartacus_sample_data.impex +++ /dev/null @@ -1,78 +0,0 @@ -$contentCatalog=electronicsContentCatalog -$contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),CatalogVersion.version[default=Online])[default=$contentCatalog:Online] -$lang=en - -# Update MiniCartSlot -UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];cmsComponents(uid, $contentCV) -;;MiniCartSlot;MiniCart - -###### StoreFinder ###### -# Create ContentSlots -INSERT_UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];name;active;cmsComponents(uid,$contentCV) -;;StoreFinderSlot;Find a Store Link;true;StoreFinderLink - -# Create ContentSlotName for each page template -INSERT_UPDATE ContentSlotName;name[unique=true];template(uid,$contentCV)[unique=true];validComponentTypes(code);compTypeGroup(code) -;StoreFinder;ProductDetailsPageTemplate;CMSLinkComponent;; -;StoreFinder;ErrorPageTemplate;CMSLinkComponent;; -;StoreFinder;ProductListPageTemplate;CMSLinkComponent;; -;StoreFinder;ProductGridPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsListPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsGridPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsEmptyPageTemplate;CMSLinkComponent;; -;StoreFinder;CategoryPageTemplate;CMSLinkComponent;; -;StoreFinder;LandingPage1Template;CMSLinkComponent;; -;StoreFinder;LandingPage2Template;CMSLinkComponent;; -;StoreFinder;LandingPage3Template;CMSLinkComponent;; -;StoreFinder;LandingPage4Template;CMSLinkComponent;; -;StoreFinder;LandingPage5Template;CMSLinkComponent;; -;StoreFinder;LandingPage6Template;CMSLinkComponent;; -;StoreFinder;ContentPage1Template;CMSLinkComponent;; -;StoreFinder;CartPageTemplate;CMSLinkComponent;; -;StoreFinder;LoginPageTemplate;CMSLinkComponent;; -;StoreFinder;CheckoutLoginPageTemplate;CMSLinkComponent;; -;StoreFinder;MultiStepCheckoutSummaryPageTemplate;CMSLinkComponent;; -;StoreFinder;OrderConfirmationPageTemplate;CMSLinkComponent;; -;StoreFinder;AccountPageTemplate;CMSLinkComponent;; -;StoreFinder;StoreFinderPageTemplate;CMSLinkComponent;; - -# Bind Content Slots to Page Templates -INSERT_UPDATE ContentSlotForTemplate;$contentCV[unique=true];uid[unique=true];position[unique=true];pageTemplate(uid,$contentCV)[unique=true];contentSlot(uid,$contentCV)[unique=true];allowOverwrite -;;StoreFinderSlot-ProductDetailsPage;StoreFinder;ProductDetailsPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ErrorPage;StoreFinder;ErrorPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ProductListPage;StoreFinder;ProductListPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ProductGridPage;StoreFinder;ProductGridPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsListPage;StoreFinder;SearchResultsListPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsGridPage;StoreFinder;SearchResultsGridPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsEmpytPage;StoreFinder;SearchResultsEmptyPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-CategoryPage;StoreFinder;CategoryPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage1;StoreFinder;LandingPage1Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage2;StoreFinder;LandingPage2Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage3;StoreFinder;LandingPage3Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage4;StoreFinder;LandingPage4Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage5;StoreFinder;LandingPage5Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage6;StoreFinder;LandingPage6Template;StoreFinderSlot;true -;;StoreFinderSlot-ContentPage1;StoreFinder;ContentPage1Template;StoreFinderSlot;true -;;StoreFinderSlot-CartPage;StoreFinder;CartPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-LoginPage;StoreFinder;LoginPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-CheckoutLoginPage;StoreFinder;CheckoutLoginPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-MultiStepCheckoutSummaryPage;StoreFinder;MultiStepCheckoutSummaryPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-OrderConfirmationPage;StoreFinder;OrderConfirmationPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-AccountPage;StoreFinder;AccountPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-StoreFinderPage;StoreFinder;StoreFinderPageTemplate;StoreFinderSlot;true - -###### Sign out ###### -INSERT_UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];name;url;&linkRef;target(code)[default='sameWindow'] -;;SignOutLink;SignOutLink;;SignOutLink; - -INSERT_UPDATE CMSNavigationNode;uid[unique=true];$contentCV[unique=true];name;parent(uid, $contentCV);links(&linkRef);&nodeRef -;SignOutNavNode;;Sign Out;MyAccountNavNode;;SignOutNavNode - -# create cms navigation entry for nvaigation child nodes -INSERT_UPDATE CMSNavigationEntry;uid[unique=true];$contentCV[unique=true];name;navigationNode(&nodeRef);item(&linkRef); -;SignOutNavNodeEntry;;SignOutNavNodeEntry;SignOutNavNode;SignOutLink; - -###### CMS link name in English ###### -UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];linkName[lang=en] - ;;SignOutLink;"Sign Out" - ;;StoreFinderLink;"Find a Store" diff --git a/docs/archived_installation_docs/impex_sample_data/1811/spartacus_sample_data.impex b/docs/archived_installation_docs/impex_sample_data/1811/spartacus_sample_data.impex deleted file mode 100644 index 90f2c601d4c..00000000000 --- a/docs/archived_installation_docs/impex_sample_data/1811/spartacus_sample_data.impex +++ /dev/null @@ -1,78 +0,0 @@ -$contentCatalog=electronicsContentCatalog -$contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),CatalogVersion.version[default=Online])[default=$contentCatalog:Online] -$lang=en - -# Update MiniCartSlot -UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];cmsComponents(uid, $contentCV) -;;MiniCartSlot;MiniCart - -###### StoreFinder ###### -# Create ContentSlots -INSERT_UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];name;active;cmsComponents(uid,$contentCV) -;;StoreFinderSlot;Find a Store Link;true;StoreFinderLink - -# Create ContentSlotName for each page template -INSERT_UPDATE ContentSlotName;name[unique=true];template(uid,$contentCV)[unique=true];validComponentTypes(code);compTypeGroup(code) -;StoreFinder;ProductDetailsPageTemplate;CMSLinkComponent;; -;StoreFinder;ErrorPageTemplate;CMSLinkComponent;; -;StoreFinder;ProductListPageTemplate;CMSLinkComponent;; -;StoreFinder;ProductGridPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsListPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsGridPageTemplate;CMSLinkComponent;; -;StoreFinder;SearchResultsEmptyPageTemplate;CMSLinkComponent;; -;StoreFinder;CategoryPageTemplate;CMSLinkComponent;; -;StoreFinder;LandingPage1Template;CMSLinkComponent;; -;StoreFinder;LandingPage2Template;CMSLinkComponent;; -;StoreFinder;LandingPage3Template;CMSLinkComponent;; -;StoreFinder;LandingPage4Template;CMSLinkComponent;; -;StoreFinder;LandingPage5Template;CMSLinkComponent;; -;StoreFinder;LandingPage6Template;CMSLinkComponent;; -;StoreFinder;ContentPage1Template;CMSLinkComponent;; -;StoreFinder;CartPageTemplate;CMSLinkComponent;; -;StoreFinder;LoginPageTemplate;CMSLinkComponent;; -;StoreFinder;CheckoutLoginPageTemplate;CMSLinkComponent;; -;StoreFinder;MultiStepCheckoutSummaryPageTemplate;CMSLinkComponent;; -;StoreFinder;OrderConfirmationPageTemplate;CMSLinkComponent;; -;StoreFinder;AccountPageTemplate;CMSLinkComponent;; -;StoreFinder;StoreFinderPageTemplate;CMSLinkComponent;; - -# Bind Content Slots to Page Templates -INSERT_UPDATE ContentSlotForTemplate;$contentCV[unique=true];uid[unique=true];position[unique=true];pageTemplate(uid,$contentCV)[unique=true];contentSlot(uid,$contentCV)[unique=true];allowOverwrite -;;StoreFinderSlot-ProductDetailsPage;StoreFinder;ProductDetailsPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ErrorPage;StoreFinder;ErrorPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ProductListPage;StoreFinder;ProductListPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-ProductGridPage;StoreFinder;ProductGridPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsListPage;StoreFinder;SearchResultsListPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsGridPage;StoreFinder;SearchResultsGridPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-SearchResultsEmpytPage;StoreFinder;SearchResultsEmptyPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-CategoryPage;StoreFinder;CategoryPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage1;StoreFinder;LandingPage1Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage2;StoreFinder;LandingPage2Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage3;StoreFinder;LandingPage3Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage4;StoreFinder;LandingPage4Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage5;StoreFinder;LandingPage5Template;StoreFinderSlot;true -;;StoreFinderSlot-LandingPage6;StoreFinder;LandingPage6Template;StoreFinderSlot;true -;;StoreFinderSlot-ContentPage1;StoreFinder;ContentPage1Template;StoreFinderSlot;true -;;StoreFinderSlot-CartPage;StoreFinder;CartPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-LoginPage;StoreFinder;LoginPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-CheckoutLoginPage;StoreFinder;CheckoutLoginPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-MultiStepCheckoutSummaryPage;StoreFinder;MultiStepCheckoutSummaryPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-OrderConfirmationPage;StoreFinder;OrderConfirmationPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-AccountPage;StoreFinder;AccountPageTemplate;StoreFinderSlot;true -;;StoreFinderSlot-StoreFinderPage;StoreFinder;StoreFinderPageTemplate;StoreFinderSlot;true - -###### Sign out ###### -INSERT_UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];name;url;&linkRef;target(code)[default='sameWindow'] -;;SignOutLink;SignOutLink;;SignOutLink; - -INSERT_UPDATE CMSNavigationNode;uid[unique=true];$contentCV[unique=true];name;parent(uid, $contentCV);links(&linkRef);&nodeRef -;SignOutNavNode;;Sign Out;MyAccountNavNode;;SignOutNavNode - -# create cms navigation entry for nvaigation child nodes -INSERT_UPDATE CMSNavigationEntry;uid[unique=true];$contentCV[unique=true];name;navigationNode(&nodeRef);item(&linkRef); -;SignOutNavNodeEntry;;SignOutNavNodeEntry;SignOutNavNode;SignOutLink; - -###### CMS link name in English ###### -UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];linkName[lang=en] - ;;SignOutLink;"Sign Out" - ;;StoreFinderLink;"Find a Store" diff --git a/docs/archived_installation_docs/impex_sample_data/README.md b/docs/archived_installation_docs/impex_sample_data/README.md deleted file mode 100644 index c7a424efc03..00000000000 --- a/docs/archived_installation_docs/impex_sample_data/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Impex Sample Data - -Each folder in this directory contains sample data that is customized for a specific release of SAP Commerce Cloud. Choose the folder that corresponds to the version of SAP Commerce Cloud that you are using for your back end. \ No newline at end of file diff --git a/docs/archived_installation_docs/spartacus_installation/1808/README.md b/docs/archived_installation_docs/spartacus_installation/1808/README.md deleted file mode 100644 index 3781c98a539..00000000000 --- a/docs/archived_installation_docs/spartacus_installation/1808/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Working with Older Versions of SAP Commerce Cloud - -The installation instructions provided here allow you to set up a Spartacus-based project with an older version of SAP Commerce Cloud. - -If you are building a storefront application from published Spartacus libraries, see the [Setup and Installation](setupandinstallation.md) instructions. - -If you are building the Spartacus project from source, see the [Contributor Setup](contributorsetup.md) instructions. \ No newline at end of file diff --git a/docs/archived_installation_docs/spartacus_installation/1808/contributorsetup.md b/docs/archived_installation_docs/spartacus_installation/1808/contributorsetup.md deleted file mode 100644 index 183e2add9cb..00000000000 --- a/docs/archived_installation_docs/spartacus_installation/1808/contributorsetup.md +++ /dev/null @@ -1,151 +0,0 @@ -# Contributor Setup - -To contribute to the Spartacus project, the first steps are to clone the Spartacus library sources, build, and then run the storefront from the library development workspace. - -This guide shows how to build and run both in development mode and in production mode. - -# Prerequisites - -Before carrying out the procedures below, please ensure the following front end and back end requirements are in place. - -## Front end Requirements - -Your Angular development environment should include the following: - -- node.js >= 10 and < 12 -- yarn >= 1.15 - -## Back end Requirements - -The Spartacus JavaScript Storefront uses SAP Commerce Cloud for its back end, and makes use of the sample data from the B2C Accelerator electronics storefront in particular. - -To install SAP Commerce Cloud, refer to the 1808 [installation instructions](../../back_end_installation). - -Note: The latest release of SAP Commerce Cloud is recommended. - - -# Cloning the Sources - -The first step is to clone the Spartacus GitHub repository on your local system. - -# Installing the Dependencies. - -Install the dependencies by running the following yarn command: - -``` -$ yarn install -``` - -# Building and Running in Development Mode - -The simplest way to build and run from the source code is to work in development mode. - -## Configuring Your Back end URL - -Carry out the following steps before you build and launch. - -1. Configure your back end URL in the `projects/storefrontapp/environments/environment.ts` file. - - The `environment.ts` file contains properties that are applied when the app is run in development mode. - -2. Add your back end base URL to the `occBaseUrl` property, as follows: - - ``` - export const environment = { - occBaseUrl: 'https://custom-backend-url' - }; - ``` - -## Launching the Storefront - -Lauch the storefront with the following command: - -``` -$ yarn start -``` - -This is the most convenient way for a developer to run the storefront. It allows for hot-reloading of the library code as the code changes. - -# Building and Running in Production Mode - -Building in production mode has more retrictive rules about what kind of code is allowed, but it also allows you to generate a build that is optimized for production. Use this mode as your development cycle nears completion. - -## Building the @spartacus/storefront Library - -Contrary to development mode, in production mode you need to package and build a standalone storefront library. This is done with the following command: - -```bash -yarn build:libs -``` - -## Configuring Your Back end URL - -1. Configure your back end URL in the `projects/storefrontapp/environments/environment.prod.ts` file. - -2. Add your back end base URL to the `occBaseUrl` property, as follows: - - ``` - export const environment = { - occBaseUrl: 'https://custom-backend-url' - }; - ``` - -## Launching the Storefront - -Launch the server with ng serve, as follows: - -``` -$ yarn start:prod -``` - -# Additional Storefront Configuration - -In both development mode and production mode, the Spartacus storefront has default values for all of its configurations. However, you may need to override these values. - -To configure the storfront, use the `withConfig` method on the StorefrontModule. The following is an example: - -``` -@NgModule({ - imports: [ - BrowserModule, - StorefrontModule.withConfig({ - server: { - baseUrl: environment.occBaseUrl, - occPrefix: '/rest/v2/' - }, - authentication: { - client_id: 'mobile_android', - client_secret: 'secret' - } - }), - ...devImports - ], - bootstrap: [StorefrontComponent] -}) -export class AppModule {} -``` - -Note: *SAP Commerce cloud 2005 and above* needs to use the occ prefix `/occ/v2/`. Anything below will be be using the default value of `/rest/v2/`. - -The server `baseUrl` is pulled from the `environment.*.ts` file, but the rest of the properties in this example use the default values for the configs. You do not have to specify a config if you do not need to override the default value. - -For example, if you only need to override the `baseUrl` and the `client_secret`, and want to use the default values for other properties, you can use the following config: - -``` -@NgModule({ - imports: [ - BrowserModule, - StorefrontModule.withConfig({ - server: { - baseUrl: environment.occBaseUrl, - }, - authentication: { - client_secret: 'secret' - } - }), - ...devImports - ], - bootstrap: [StorefrontComponent] -}) -export class AppModule {} -``` diff --git a/docs/archived_installation_docs/spartacus_installation/1808/setupandinstallation.md b/docs/archived_installation_docs/spartacus_installation/1808/setupandinstallation.md deleted file mode 100644 index 9d578ddee7c..00000000000 --- a/docs/archived_installation_docs/spartacus_installation/1808/setupandinstallation.md +++ /dev/null @@ -1,218 +0,0 @@ -# Setup and Installation - -The following instructions describe how to build a storefront application from published Spartacus libraries. - -To build the Spartacus project from source, see [Contributor Setup](contributorsetup.md). - -# Prerequisites - -Before carrying out the procedures below, please ensure the following front end and back end requirements are in place. - -## Front end Requirements - -Your Angular development environment should include the following: - -- Angular cli v8 -- node.js >= 10 and < 12 -- yarn >= 1.15 - -## Back end Requirements - -The Spartacus JavaScript Storefront uses SAP Commerce Cloud for its back end, and makes use of the sample data from the B2C Accelerator electronics storefront in particular. - -To install SAP Commerce Cloud, refer to the 1808 [installation instructions](../../back_end_installation). - -Note: The latest release of SAP Commerce Cloud is recommended. - -# Creating a New Angular Application - -In the following procedure, we create a new Angular application with the name `mystore`. - -1. Generate a new Angular application using the Angular CLI, as follows: - ``` - $ ng new {mystore} --style=scss - ``` -2. Access the newly created directory: - ``` - $ cd {mystore} - ``` - -# Adding Peer Dependencies to the Storefront - -The dependencies in this procedure are required by the Spartacus storefront. - -1. Add the following dependencies to the `dependencies` section of `{mystore}/package.json`: - - ```json - "@angular/pwa": "^0.6.8", - "@angular/service-worker": "^6.0.0", - "@ng-bootstrap/ng-bootstrap": "^3.2.2", - "@ng-select/ng-select": "^2.9.1", - "@ngrx/effects": "^8.0.0", - "@ngrx/router-store": "^8.0.0", - "@ngrx/store": "^8.0.0", - "bootstrap": "^4.1.3", - ``` - -2. Install the dependencies. The following is an example using yarn: - - ```shell - yarn install - ``` - -# Adding the Storefront Dependencies - -There are several libraries you must add to your storefront application. You can do so with yarn, as follows: - -``` -$ yarn add @spartacus/core@next -$ yarn add @spartacus/storefront@next -$ yarn add @spartacus/styles@next -``` - -The storefront libraries are not yet released, so we suggest using the `@next` tag to install the latest pre-alpha version that is available. - -# Importing the Storefront Module into Your Application - -1. Open `{mystore}/src/app/app.module.ts` and add the following lines: - - ``` - import { StorefrontModule } from '@spartacus/storefront'; - ``` - -2. Add the `StorefrontModule` to the import section of the `NgModule` decorator: - - ``` - imports: [BrowserModule, StorefrontModule], - ``` - -Your file should look like this: - -``` -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; -import { StorefrontModule } from '@spartacus/storefront'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, StorefrontModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } -``` - -# Configuring the Storefront - -The Spartacus storefront has default values for all of its configurations. However, you may need to override these values. An example use case would be so that your storefront can communicate with your SAP Commerce back end. - -To configure the storefront, use the `withConfig` method on the StorefrontModule. The following is an example: - -``` - imports: [ - BrowserModule, StorefrontModule.withConfig({ - server: { - baseUrl: 'https://electronics.local:9002', - occPrefix: '/rest/v2/' - }, - authentication: { - client_id: 'mobile_android', - client_secret: 'secret' - } - }) - ], -``` - -Note: *SAP Commerce cloud 2005 and above* needs to use the occ prefix `/occ/v2/`. Anything below will be be using the default value of `/rest/v2/`. - -This example uses the default values for the configs. You do not have to specify a config if you do not need to override its value. For example, if you only need to override the back end base URL, you can use this config: - -``` -imports: [BrowserModule, StorefrontModule.withConfig({ - server: { - baseUrl: 'https://my-custom-backend-url:8080', - } -})] -``` - -# Adding the Storefront Component - -This procedure adds the storefront component in the UI. - -1. Open `{approot}/src/app/app.component.html` and replace the entire contents of the file with the following line: - - ``` - Loading... - ``` - -2. Import the styles from the `@spartacus/styles` library by opening `{approot}/src/styles.scss` and adding the following line: - - ``` - @import "~@spartacus/styles/index"; - ``` - -# Building and Starting - -This section describes how to validate your back end installation, and then start the application with the storefront enabled. - -## Validating the Back end - -1. Use a web browser (Chrome is highly recommended) to access the CMS OCC endpoint of your back end. - - The default is available at: `{server-base-url}/rest/v2/electronics/cms/pages`. - - For example, with a back end instance running from `https://localhost:9002`, you would access: https://localhost:9002/rest/v2/electronics/cms/pages. - - Note: *SAP Commerce cloud 2005 and above* needs to use the occ prefix `/occ/v2/`. Anything below will be be using the default value of `/rest/v2/`. - -2. Accept the security exception in your browser if you are running a development instance with a self-signed HTTPS certificate. - - When the request works, you see an XML response in your browser. - -## Starting the Storefront Application - -1. Start the application with the storefront enabled, as follows: - - ``` - $ ng serve - ``` - -2. When the app server is properly started, point your browser to http://localhost:4200, as instructed from the terminal output of `ng serve`. - -# Known Issues - -The following are known issues with the current release of Spartacus JavaScript Storefront: - -- When using SAP Commerce 1808 for your back end, you are currently not able to add payment details or address details in the Spartacus storefront, which prevents successful checkout. However, if you add payment and address details through the Accelerator electronics storefront, they will then appear in the Spartacus storefront, and you will be able to check out. - -- The Spartacus storefront is currently missing all categories. - -- The Spartacus storefront is currently missing the footer. - -- Certain AddOns may cause the Spartacus storefront to not work properly. - -- Spartacus relies on the `cmsoccaddon` for CMS information. However, this extension is currently not fully compatible with SmartEdit. As a result, the categories may not appear in Spartacus. To avoid this problem, remove the SmartEdit CMS web services and personalization extensions. - -- You may notice that the logo is very small. This can be fixed as follows: - - 1. Log in to SAP Commerce Backoffice. - - 2. Select `WCMS` in the left-hand navigation pane, then select the `Component` child node that appears below. - - 3. Search for the term `SiteLogoComponent` in the Search box in the top-center panel. - - You can modify the component directly in the Online Catalog, or you can modify it in the Staged Catalog and then perform a sync. - - 4. Open the `Administration` tab of the SiteLogoComponent, and remove the `Media` value. - - 5. Click the button labelled `...` next to the `Media` field. - - 6. In the pop-up search box that appears, search for the desired media file in your system and select it. - - 7. Save your changes diff --git a/docs/libs/creating-lib.md b/docs/libs/creating-lib.md index 38a0f38bc08..6a0c6ec307d 100644 --- a/docs/libs/creating-lib.md +++ b/docs/libs/creating-lib.md @@ -1,8 +1,8 @@ # Creating a Spartacus library -An easy way to create a new Spartacus library is to run: `ng g library ` where the `lib-name` is the name of the new library. +An easy way to create a new Spartacus library is to run: `nx g @schematics/angular:library --prefix=cx` where the `lib-name` is the name of the new library. -The library will be generated in the `feature-libs` folder by default. You need to manually move the generated files to an appropriate directory (if necessary) and modify `angular.json` to reflect the new location. +The library will be generated in the `root of the project` folder by default. You need to manually move the generated folder, which is under the `` you entered to the appropriate directory (if necessary). The entire generated folder should be moved either under `feature-libs` or `integration-libs`. This document can also serve as the guideline for the future schematic that can automate this process. @@ -15,12 +15,14 @@ This document can also serve as the guideline for the future schematic that can - [Aligning with the other libs](#aligning-with-the-other-libs) - [Modifying the generated files](#modifying-the-generated-files) - [Additional changes to existing files](#additional-changes-to-existing-files) + - [Sample data release entry ONLY if applicable](#sample-data-release-entry-only-if-applicable) - [Multi-entry point library](#multi-entry-point-library) - [Process](#process) - [Testing](#testing) - [Schematics](#schematics) - [Configuring Schematics](#configuring-schematics) - [Testing Schematics](#testing-schematics) + - [Installation script](#installation-script) ## Naming conventions @@ -28,15 +30,18 @@ These are some naming guidelines for libraries: - library names should be abbreviated, if possible (e.g. _cds_) - library names should use kebab-case (e.g. `my-account`) -- the scripts added to `package.json` should _not_ use kebab-case (e.g. `yarn build:myaccount`) +- the scripts added to `package.json` should _not_ use kebab-case (e.g. `npm run build:myaccount`) ## Generating a library -Run `ng g library ` and commit. +Run `nx g @schematics/angular:library --prefix=cx`, move it to the appropriate directory (`feature-libs` or `integration-libs`), and commit. ## Aligning with the other libs -In order to be 100% aligned with the existing Spartacus library there are some generated files that should be updated and there are some files that need to be additionally created +In order to be 100% aligned with the existing Spartacus library there are some generated files that should be updated and there are some files that need to be additionally created. Make sure that the `src` folder would not exist in the newly generated library. + +If you are generating a library, which purpose is to be a `single-entry point library`, then you can follow the file structure as done in `feature-libs/customer-ticketing` +If you are generating a library, which purpose is to be a `multi-entry point library`, then you can follow the file structure as done in `feature-libs/checkout` ### Modifying the generated files @@ -44,8 +49,6 @@ The list of the files that need to modified: - `README.md` - replace the default content with some relevant information about the library. -- `angular.json` - change the `prefix` property to `cx`. - - `karma.conf.js` Just copy paste the following and and make sure to rename `TODO:` to you lib's name: @@ -64,20 +67,26 @@ module.exports = function (config) { require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('@angular-devkit/build-angular/plugins/karma'), + require('karma-junit-reporter'), ], client: { - clearContext: false, // leave Jasmine Spec Runner output visible in browser + clearContext: true, // close Jasmine Spec Runner output in browser to avoid 'Some of your tests did a full page reload!' error when '--no-watch' is active + }, + reporters: ['progress', 'kjhtml', 'dots', 'junit'], + junitReporter: { + outputFile: 'unit-test-.xml', + outputDir: require('path').join(__dirname, '../../unit-tests-reports'), + useBrowserName: false, }, - reporters: ['progress', 'kjhtml', 'dots'], coverageReporter: { - dir: require('path').join(__dirname, '../../coverage/TODO:'), + dir: require('path').join(__dirname, '../../coverage/TODO'), reporters: [{ type: 'lcov', subdir: '.' }, { type: 'text-summary' }], check: { global: { - statements: 80, - lines: 80, - branches: 70, - functions: 80, + statements: 90, + lines: 90, + branches: 75, + functions: 85, }, }, }, @@ -92,6 +101,69 @@ module.exports = function (config) { }; ``` +- `project.json` + + - add the lint `targets > lint` + + replace the TODO: with the library name. Please remember it can either be a feature-libs or integration-libs. + + ```json + "lint": { + "executor": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "integration-libs/TODO:/**/*.ts", + "integration-libs/TODO:/**/*.html" + ] + } + } + ``` + - add the test-jest `targets > test-jest` + + replace the TODO: with the library name. Please remember it can either be a feature-libs or integration-libs. + + ```json + "test-jest": { + "executor": "nx:run-commands",, + "options": { + "command": "npm run test:schematics", + "cwd": "feature-libs/TODO: + } + } + ``` + + - add the tag(s) + - type:feature + - type:integration `IF AND ONLY IF` it is an integration lib. + + ```json + { + "name": "some-library-name", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "feature-libs/some-library-name", + "prefix": "cx", + "targets": { + ... + ... + ... + "lint": { + "executor": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "integration-libs/some-library-name/**/*.ts", + "integration-libs/some-library-name/**/*.html" + ] + } + } + ... + ... + ... + }, + "tags": ["type:feature"] + } + ``` + - `public-api.ts` - rename this file to `public_api.ts` (with the underscore instead of the dash) @@ -136,14 +208,14 @@ Use the following template: "publishConfig": { "access": "public" }, - "repository": "https://github.com/SAP/spartacus", + "repository": "https://github.com/SAP/spartacus/tree/develop/feature-libs/TODO", "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@angular/common": "^10.1.0", "@angular/core": "^10.1.0", - "rxjs": "^6.6.0", + "rxjs": "^7.8.0", "@spartacus/core": "3.0.0-next.0", "@spartacus/storefront": "3.0.0-next.0" } @@ -158,11 +230,25 @@ If your library doesn't expose any SCSS styles, remove the section `exports`/`sa - `test.ts` - - in order to run the tests for _all_ the entry points, the `test.ts` file has to be moved one level up from `lib-name/src/test.ts` to `lib-name/test.ts`. + - in order to run the tests for _all_ the entry points, you can to create the `test.ts` file in `lib-name/test.ts`. This change requires an update in: - 1. `angular.json` - change the `projects -> lib-name -> architect -> test -> options -> main` value to reflect the new file path + 1. `project.json` - change the `targets -> test -> options -> main` value to reflect the new file path + Just copy paste the following and and make sure to rename `TODO:` to you lib's name: + + ```json + "test": { + "executor": "@angular-devkit/build-angular:karma", + "options": { + "main": "feature-libs/TODO:/test.ts", + "tsConfig": "feature-libs/TODO:/tsconfig.spec.json", + "polyfills": ["zone.js", "zone.js/testing"], + "karmaConfig": "feature-libs/TODO:/karma.conf.js" + } + }, + ``` + 2. `feature-libs//tsconfig.lib.json` - update the path in `exclude` 3. `feature-libs//tsconfig.spec.json` - update the path in `files` @@ -176,10 +262,12 @@ Use the following template: "compilerOptions": { "outDir": "../../out-tsc/lib", "forceConsistentCasingInFileNames": true, - "target": "es2015", - "module": "es2020", + "target": "es2022", + "module": "es2022", "moduleResolution": "node", "declaration": true, + "declarationMap": true, + "strict": true, "sourceMap": true, "inlineSources": true, "experimentalDecorators": true, @@ -204,20 +292,20 @@ Use the following template: } ``` -- `tsconfig.spec.json` - add `"target": "es2015", "module": "es2020"` in `"compilerOptions"`: +- `tsconfig.spec.json` - add `"target": "es2022", "module": "es2022"` in `"compilerOptions"`: ```json { /* ... */ "compilerOptions": { /* ... */ - "target": "es2015", - "module": "es2020" + "target": "es2022", + "module": "es2022" } } ``` -- run `yarn config:update` script to update `compilerOptions.path` property in tsconfig files +- run `npm run config:update` script to update `compilerOptions.path` property in tsconfig files - `tsconfig.lib.prod.json` - save to re-format it. Make sure that Ivy is off (for the time being, this will change in the future) - `tslint.json` - remove - the rest of the generated files should be removed @@ -230,7 +318,7 @@ Use the following template: } }, ``` - and then run `yarn config:update` (to fix the formatting) + and then run `npm run config:update` (to fix the formatting) ### Additional changes to existing files @@ -248,97 +336,27 @@ The following files should be modified: Add the following scripts: ```json -"build:asm": "yarn --cwd feature-libs/asm run build:schematics && ng build asm --configuration production", -"release:asm:with-changelog": "cd feature-libs/asm && release-it && cd ../..", +"build:asm": "npm --prefix feature-libs/asm run build:schematics && npx nx build asm --configuration production" ``` And replace `asm` instances with the name of yours lib. Also, add the new lib to the `build:libs` and `test:libs` scripts. -- `.github/ISSUE_TEMPLATE/new-release.md` - -Add `- [ ] `npm run release:TODO::with-changelog`(needed since`x.x.x`)` under the `For each package select/type version when prompted:` section, and replace `TODO:` to match the `package.json`'s release script name. - -- `.release-it.json` - -```json -{ - "git": { - "requireCleanWorkingDir": true, - "requireUpstream": false, - "tagName": "TODO:-${version}", - "commitMessage": "Bumping TODO: version to ${version}", - "tagAnnotation": "Bumping TODO: version to ${version}" - }, - "npm": { - "publishPath": "./../../dist/TODO:" - }, - "hooks": { - "after:version:bump": "cd ../.. && ng build TODO: --configuration production" - }, - "github": { - "release": true, - "assets": ["../../docs.tar.gz", "../../docs.zip"], - "releaseName": "@spartacus/TODO:@${version}", - "releaseNotes": "ts-node ../../scripts/changelog.ts --verbose --lib TODO: --to TODO:-${version}" - }, - "plugins": { - "../../scripts/release-it/bumper.js": { - "out": [ - { - "file": "package.json", - "path": [ - "peerDependencies.@spartacus/core", - "peerDependencies.@spartacus/storefront" - ] - } - ] - } - } -} -``` - -Replace `TODO:` with the appropriate name. -Optionally, adjust the `path` property with the `peerDependencies` to match the peer dependencies defined in the `package.json`. - -- `scripts/changelog.ts` - -In the `const libraryPaths` object, add the following (and replace the `my-account` with your lib's name): - -```ts -const libraryPaths = { - ..., - '@spartacus/my-account': 'feature-libs/my-account', -}; -``` - -Also make sure to add the lib to the `switch` statement at the end of the file. - -- `scripts/packages.ts` - just add your lib to the `const packageJsonPaths` array. - -- `sonar-project.properties` - list your library to this file - - `projects/schematics/package.json` - add the library to the package group -- `scripts/templates/changelog.ejs` - add the library to `const CUSTOM_SORT_ORDER` +- `ci-scripts/unit-tests.sh` -- `ci-scripts/unit-tests-sonar.sh` -Add the library unit tests with code coverage +### Sample data release entry ONLY if applicable -```sh -echo "Running unit tests and code coverage for TODO:" -exec 5>&1 -output=$(ng test TODO: --sourceMap --watch=false --code-coverage --browsers=ChromeHeadless | tee /dev/fd/5) -coverage=$(echo $output | grep -i "does not meet global threshold" || true) -if [[ -n "$coverage" ]]; then - echo "Error: Tests did not meet coverage expectations" - exit 1 -fi -``` +If you have your own sample data that derives from our spartacussampledata, such as epdvisualizationspartacussampledata, then the following is applicable to you. -Replace `TODO:` with the appropriate name. +1. `publish-sample-data.yml` - add an input entry and env entry to pass the input to the publish-sample-data script. This input is the target branch that we would want to release. +2. `publish-sample-data.sh`: + 1. create a variable at the top to use $STOREFRONT_FILE_NAME as a prefix, which is used to name the zip/tar. + 2. create one function that utilize downloading the assets (zip/tar) of your sample data like the `download_sample_data` function. + 3. add a note for the `gh release` that mentions what that zip is. For example, if the zip is called spartacussampledata-TODO.zip, then make sure it mentions what that TODO is. ## Multi-entry point library @@ -350,22 +368,22 @@ Sources: If adding multiple entry points to the generated library, make sure to do the following changes: -- `angular.json` - change the `projects -> lib-name -> sourceRoot` to have the same value as the `root` property. This will enable code coverage report to be properly generated for all the entry points. +- `project.json` - make sure `sourceRoot` does not contain `src`, and just the library name - make sure to follow the general folder structure, as seen in e.g. `feature-libs/product` library - add `ng-package.json` to each of the feature folders -- run `yarn config:update` script to update `compilerOptions.path` property in tsconfig files +- run `npm run config:update` script to update `compilerOptions.path` property in tsconfig files ## Testing Don't forget to: -- run the tests for the generated library - `ng test --code-coverage`. In case of a library with multiple entry points, make sure to check the code-coverage report generated in the `coverage/my-account/lcov-report/index.html` -- build the generated library _with Ivy enabled_ - `ng build ` -- build the generated library (without Ivy) - `ng build --configuration production` +- run the tests for the generated library - `npx nx test --code-coverage`. In case of a library with multiple entry points, make sure to check the code-coverage report generated in the `coverage/my-account/lcov-report/index.html` +- build the generated library _with Ivy enabled_ - `npx nx build ` +- build the generated library (without Ivy) - `npx nx build --configuration production` - build the production-ready shell app with the included generated library (import a dummy service from the generated service): - - `yarn build:libs` (build all the libs) - - `yarn build` + - `npm run build:libs` (build all the libs) + - `npm run build` ## Schematics @@ -383,20 +401,78 @@ There are couple of required changes to make sure schematics will work properly - `projects/storefrontapp/tsconfig.server.prod.json`, - `projects/storefrontapp/tsconfig.server.json`, - `projects/storefrontapp/tsconfig.app.prod.json` -- add new feature lib consts in schematics folder - `feature-libs\\schematics\constants.ts` where the `lib-name` is the name of the new library - add new feature lib schema.json elements in schematics folder - `feature-libs\\schematics\add-\schema.json` where the `lib-name` is the name of the new library - add new feature chain method to 'shouldAddFeature' and function to add it - `feature-libs\\schematics\add-\index.ts` where the `lib-name` is the name of the new library - create new feature lib module in - `projects/storefrontapp/src/app/spartacus/features` -- create your schematics configuration in e.g. `projects/schematics/src/shared/lib-configs/asm-schematics-config.ts` and add it to the `projects/schematics/src/shared/schematics-config-mappings.ts` file. - +- create your schematics configuration in e.g. `projects/schematics/src/shared/lib-configs/asm-schematics-config.ts` and add it to the `projects/schematics/src/shared/schematics-config-mappings.ts` file. ### Testing Schematics +IMPORTANT : DO NOT PUSH any changed done under this step. + - Install verdaccio locally `$ npm i -g verdaccio@latest` (only for the first time) - Run it: `$ verdaccio` - Create an npm user: `$ npm adduser --registry http://localhost:4873`. After completing the registration of a new user, stop the verdaccio. This setup is only required to do once -- Create new angular project `ng new schematics-test --style=scss` +- Create new angular project `ng new schematics-test --style scss --routing=false` - Run verdaccio script `ts-node ./tools/schematics/testing.ts` (or `./node_modules/ts-node/dist/bin.js ./tools/schematics/testing.ts` in case you don't have _ts-node_ installed globally) in main spartacus core folder -- Build all libs (if it is first time, if not just build your new lib) +- Build all libs (if it is first time, if not just build your new lib) or run a command `npm run build:libs` - Publish -- Add spartacus to new angular project `ng add @spartacus/schematics@latest --baseUrl https://spartacus-demo.eastus.cloudapp.azure.com:8443/ --baseSite=electronics-spa \ No newline at end of file +- Add spartacus to new angular project `ng add @spartacus/schematics@latest --base-url https://spartacus-demo.eastus.cloudapp.azure.com:8443/ --base-site=electronics-spa + +## Installation script + +[Installation Script for Spartacus](https://github.com/SAP/spartacus/blob/develop/scripts/install/README.md) + +If your library is an integration library (that requires a separate integration servers), a separate toggle flag should be implemented in the Installation Script. + +In the following examples please replace `TODO` and `todo` with your appropriate library name: + +- In `scripts/install/config.default.ts` add a new flag `ADD_TODO=false` (similar to `ADD_CDC=false`) + +In `scripts/install/functions.ts`: + +- add a switch-case inside the `function parseInstallArgs` (similar to the case `cdc)`): + + ```bash + function parseInstallArgs { + ... + + todo) + ADD_TODO=true + echo "➖ Added TODO" + shift + ;; + ``` + +- create a new function `add_todo` for installing your library (similar to `function add_cdc`): + + ```bash + function add_todo { + if [ "$ADD_TODO" = true ] ; then + ng add @spartacus/todo@${SPARTACUS_VERSION} --skip-confirmation --no-interactive + fi + } + ``` + +- invoke your installation function `add_todo` in 3 other functions (similar to `add_cdc`): + - CSR installation: + ```bash + function install_spartacus_csr { + ... + add_todo + } + ``` + - SSR installation: + ```bash + function install_spartacus_ssr { + ... + add_todo + } + ``` + - SSR PWA installation: + ```bash + function add_spartacus_ssr_pwa { + ... + add_todo + } + ``` diff --git a/docs/libs/major-release-checklist.md b/docs/libs/major-release-checklist.md index 18ff9c03c69..444f27c7bd8 100644 --- a/docs/libs/major-release-checklist.md +++ b/docs/libs/major-release-checklist.md @@ -3,7 +3,7 @@ - Update [installation docs](https://sap.github.io/spartacus-docs/building-the-spartacus-storefront-from-libraries/): - update angular (cli) version - update `node` version - - update `yarn` version if needed + - update `npm` version if needed - add new requirements if any - review the rest of document for missing updates/new steps - Update installation script: diff --git a/docs/migration/2211_19/2211-html.md b/docs/migration/2211_19/2211-html.md new file mode 100644 index 00000000000..9190dedc644 --- /dev/null +++ b/docs/migration/2211_19/2211-html.md @@ -0,0 +1,70 @@ +# Changes in feature lib product-configurator + +## configurator-add-to-cart-button.html + +A quantity control has been added for specifiying the products quantity when adding to the cart. + +## configurator-attribute-footer.component.html + +The view now renders different error messages depending on the attribute type. +In case of free input types, the resource key is `configurator.attribute.defaultRequiredMessage`. In case of +drop-down attributes, the key is `configurator.attribute.singleSelectRequiredMessage` + +## configurator-attribute-drop-down.component.html + +New styling `cx-required-error-msg` is active in case the attribute is required but not provided. + +## configurator-attribute-input-field.component.html + +New styling `cx-required-error-msg` is active in case the attribute is required but not provided. + +## configurator-attribute-numeric-input-field.component.html + +New styling `cx-required-error-msg` is active in case the attribute is required but not provided. +Issues with respect to the numeric format of the input are rendered with `ICON_TYPE.ERROR` instead of `ICON_TYPE.WARNING`. + +## configurator-attribute-single-selection-bundle-dropdown.component.html + +New styling `cx-required-error-msg` is active in case the attribute is required but not provided. + +## configurator-price-summary.component.html + +Label for total price uses translation key `configurator.priceSummary.totalPricePerItem` instead of `configurator.priceSummary.totalPrice`. + +# Changes in feature lib ASM + +## asm-main-ui.component.html + +Three new `cx-message` elements have been incorporated into the view to exhibit distinct confirmation messages based on specific events. Specifically: + +Following the successful creation of a customer, the confirmation message is determined by the `showCreateCustomerSuccessfullyAlert` property being set to true. + +When initiating customer emulation, the confirmation message is contingent upon the `showCustomerEmulationInfoAlert` property being set to true. + +When managing either an inactive or active cart through a deep link, the confirmation message is dictated by the `showDeeplinkCartInfoAlert` property being set to true. + +## customer-list.component.html + +The close button has been relocated from the right top corner to the left bottom corner. + +A new button for creating a customer has been introduced in the top right corner. + +Additionally, a new customer search text box has been incorporated. + +## customer-selection.component.html + +A new element, adorned with the style class `linkStyleLabel` has been introduced within the `searchResultItem` button. This particular is designed to be visible in scenarios where the search result yields no matches. Importantly, this serves as a clickable element facilitating the creation of a new customer. + +# Changes in feature lib checkout + +## checkout-review-submit.component.html + +Added a new to the delivery mode section for displaying the `CheckoutDeliveryModeComponent` + +## checkout-delivery-mode.component.html + +Added a new to display `CheckoutDeliveryModeComponent` + +## order-overview.component.html + +Added a new to display the `OrderOverviewComponent` in three different cases of orders. diff --git a/docs/migration/2211_19/2211-installation.md b/docs/migration/2211_19/2211-installation.md new file mode 100644 index 00000000000..f2a35228eda --- /dev/null +++ b/docs/migration/2211_19/2211-installation.md @@ -0,0 +1,25 @@ +# Creating a new app using Spartacus v2211 + +### Appendix A: How to run SSR dev server + +Run in _2 separate windows_ of terminal: +```bash +# Terminal 1: +npm run watch # builds the app in watch mode. It compiles `server.ts` file as well and produces an output compiled file `dist/my-app-name/server/server.mjs` +``` +and +```bash +# Terminal 2: +node --watch dist/my-app-name/server/server.mjs # run the compiled server.mjs in watch mode +``` + +Note: Please mind to replace `my-app-name` with the real name of your app. + +### Appendix B: How to run Prerendering + +Run in terminal `ng build` with the explicit flag `--prerender=true` and passing a custom Node Env Variable `SERVER_REQUEST_ORIGIN` which is required by Spartacus Prerendering. + +```bash +SERVER_REQUEST_ORIGIN="http://localhost:4200" ng build --prerender=true +``` +Note: Please mind to replace `"http://localhost:4200"` with a real target domain where you want to deploy your your Prerendered pages, especially if you deploy for production. Otherwise, some of SEO features of Spartacus might be not work properly, e.g. [Canonical URLs](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/eaef8c61b6d9477daf75bff9ac1b7eb4/e712f36722c543359ed699aed9873075.html#loio98befe9ef9ae4957a4ae34669c175fd5) might point to a wrong domain or [Automatic Multi-Site Configuration](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/eaef8c61b6d9477daf75bff9ac1b7eb4/9d2e339c2b094e4f99df1c2d7cc999a8.html) might not recognize base-side correctly (e.g. if some regexes configured in CMS for base-site recognition depend on the domain name). diff --git a/docs/migration/2211_19/2211-migration.md b/docs/migration/2211_19/2211-migration.md new file mode 100644 index 00000000000..aec2117b81e --- /dev/null +++ b/docs/migration/2211_19/2211-migration.md @@ -0,0 +1,61 @@ +# Migrating a custom app to use Spartacus v2211 + +Before upgrading Spartacus to the new major version v2211, you need to first: +- upgrade to the latest minor 6.x of Spartacus +- upgrade Angular to version v16 and then to v17 + +## Update Angular to 16 and 17 + +You cannot jump 2 Angular major versions, but need to upgrade one by one, first to v16 and then to v17. + +### Update Angular to 16 and 3rd party deps to be compatible with Angular 16 + +Follow the [Angular guidelines for upgrading from v15 to v16](https://update.angular.io/?l=3&v=15.0-16.0) and bump the Angular version locally, and update other 3rd party dependencies from Angular ecosystem to versions compatible with Angular 16 (e.g. `@ng-select/ng-select@11`, `@ngrx/store@16`, `ngx-infinite-scroll@16`): + +```bash +ng update @angular/core@16 @angular/cli@16 @ng-select/ng-select@11 @ngrx/store@16 ngx-infinite-scroll@16 --force +git add . +git commit -m "update angular 16 and 3rd party deps angular 16 compatible" +``` + +Note: the flag `--force` might be needed, e.g. if you get an `npm` installation error: `Package "@nguniversal/builders" has an incompatible peer dependency to "@angular-devkit/build-angular" (requires "^15.0.0", would install "16.2.10").` + +### Update Angular to 17 and 3rd party deps to be compatible with Angular 17 + +Follow the [Angular guidelines for upgrading from v16 to v17](https://update.angular.io/?l=3&v=16.0-17.0) and bump the Angular version locally. + +Please also update other 3rd part dependencies from Angular ecosystem to versions compatible with Angular 17, e.g. `@ng-select/ng-select@12`, `@ngrx/store@17`, `ngx-infinite-scroll@17`: + +```bash +ng update @angular/core@17 @angular/cli@17 @ng-select/ng-select@12 @ngrx/store@17 ngx-infinite-scroll@17 rxjs@7.8.1 --force +git add . +git commit -m "update angular 17 and 3rd party deps angular 17 compatible" +``` + +Note: the flag `--force` might be needed, e.g. if you get an `npm` installation error: +```error +Package "@nguniversal/express-engine" has an incompatible peer dependency to "@angular/common" (requires "^15.0.0" (extended), would install "17.0.5") +Package "@nguniversal/express-engine" has an incompatible peer dependency to "@angular/core" (requires "^15.0.0" (extended), would install "17.0.5"). +Package "@nguniversal/express-engine" has an incompatible peer dependency to "@angular/platform-server" (requires "^15.0.0" (extended), would install "17.0.5"). +``` + +If `@angular-devkit/schematics` is not listed under the `devDependencies` in the `package.json` file, please execute the following commands: +```bash +npm i @angular-devkit/schematics@17 --save-dev --force +git add . +git commit -m "add @angular-devkit/schematics@17 to dev dependencies" +``` + +If `@angular-devkit/core` is not listed under the `devDependencies` in the `package.json` file, please execute the following commands: +```bash +npm i @angular-devkit/core@17 --save-dev --force +git add . +git commit -m "add @angular-devkit/core@17 to dev dependencies" +``` +### Run Spartacus update + +After successfully updating the application to Angular 17, execute this command to initiate the Spartacus update process. + +```bash +ng update @spartacus/schematics@2211.19 +``` \ No newline at end of file diff --git a/docs/migration/2211_19/2211-styling.md b/docs/migration/2211_19/2211-styling.md new file mode 100644 index 00000000000..8010bcfd551 --- /dev/null +++ b/docs/migration/2211_19/2211-styling.md @@ -0,0 +1,16 @@ +# Changes in feature lib product-configurator + +## _configurator-add-to-cart-button.scss + +`.cx-add-to-cart-btn-container` is enhanced with new styling information in order to display the quantity that +is used for adding configurable products to the cart. + +## _configurator-form-group.scss + +New styling for `.cx-required-error-msg ` has been added. + +# Changes in feature lib pickup-in-store + +## _index.scss +Removed `$useLatestStyles: true;`. +This led to the disregard of the configured feature level for the pickup-in-store library. diff --git a/docs/migration/2211_19/2211-typescript-manual.doc.md b/docs/migration/2211_19/2211-typescript-manual.doc.md new file mode 100644 index 00000000000..a1d3050fe44 --- /dev/null +++ b/docs/migration/2211_19/2211-typescript-manual.doc.md @@ -0,0 +1,177 @@ +# Changes in feature lib checkout + +## CheckoutPaymentTypeComponent + +- `GlobalMessageService` is now a required constructor dependency. + +# Changes in feature lib product-configurator + +## Changes related to the CPQ configurator + +The provisioning of the CPQ normalizers and serializers has been moved from `CpqConfiguratorRestModule` to `CpqConfiguratorCommonModule`. + +## Changes related to configurator components + +### ConfiguratorAddToCartButtonComponent + +Constructor has been extended with a new dependency to `ConfiguratorQuantityService`. + +### ConfiguratorFormComponent + +Constructor has been extended with a new dependency to `GlobalMessageService`. + +### ConfiguratorAttributeProductCardComponent + +Method `get attributeName` has been removed. Instead directly use `productCardOptions.attributeName`, which has been turned to a mandatory instead of an optional attribute. + +### ConfiguratorAttributeFooterComponent + +This component now shows error messages also for drop-down attribute types. +Method `needsUserInputMessage` has been removed since it is no longer used. Instead use new method `needsUserInputMsg`. + +### ConfiguratorAttributeHeaderComponent + +This component no longer shows error messages for drop-down attribute types. As a consequence, method `isRequiredAttributeWithDomain` has been removed since it was not used since 6.2. Instead use new method `isRequiredAttributeWithoutErrorMsg`. + +Method `isAttributeWithDomain` has been removed since it was not used since 6.2. A replacement is not available and not +needed, since its caller was deleted method `isRequiredAttributeWithDomain`. + +### ConfiguratorAttributeSingleSelectionBaseComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorAttributeDropDownComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorAttributeInputFieldComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorAttributeNumericInputFieldComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorAttributeRadioButtonComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorAttributeSingleSelectionBundleDropdownComponent + +Constructor has been extended with a new dependency to `ConfiguratorStorefrontUtilsService`. + +### ConfiguratorGroupMenuComponent + +Method `isConflictGroupTypeAllowingUndefined` has been removed. Instead directly use `isConflictGroupType`, which now also accepts its argument as +undefined. + +### ConfiguratorOverviewFilterButtonComponent + +Member `config$` has been removed. It was no longer used, the view takes its data from `configurationWithOv$`. + +### ConfiguratorOverviewSidebarComponent + +Member `config$` has been removed. It was no longer used, the view takes its data from `configurationWithOv$`. + +## Changes related to configurator services + +### RulebasedConfiguratorConnector + +Constructor has been extended with a new dependency to `ConfiguratorCoreConfig`. + +### ConfiguratorRouterListener + +Constructor has been extended with a new dependency to `ConfiguratorQuantityService`. + +# Changes in feature lib asm + +## CsAgentAuthService + +- `UserProfileFacade` has been removed a required constructor dependency. + +## AsmBindCartComponent + +- `AsmComponentService` and `RoutingService` are now required constructor dependencies. + +## CustomerEmulationComponent + +- `LaunchDialogService` and `FeatureModulesService` are now required constructor dependencies. + +## CustomerListComponent + +- `OccConfig` is now a required constructor dependency. + +## AsmComponentService + +- `AsmEnablerService` and `AsmDeepLinkService` are now required constructor dependencies. + +## CustomerSelectionComponent + +- `launchDialogService` is now a required constructor dependency. + +# Changes in feature lib cart + +## ActiveCartService + +- `WindowRef` is now a required constructor dependency. + +# Changes in the core library + +## Changes related to the Optimized SSR Engine + +- `defaultSsrOptimizationOptions` represents the default configuration for the Optimized SSR Engine. This configuration includes various parameters, among which is the `cacheSize` property. The `cacheSize` property has been set to 3000 by default. This parameter determines the maximum number of entries allowed in the cache, serving to regulate memory usage effectively. + +- The Standardized SSR logging is enabled by default and the default value of the `logger` property of `SsrOptimizationOptions` has been set to `DefaultExpressServerLogger()`. The default logger takes care of proper formatting and recognizes whether the output should be human-readable, or read by monitoring tools. The logger not only logs the messages, it also provides information about the related request that initiated the rendering process. + + The following example shows how the logger creates logs in development mode by producing a multiline JSON output: + + ```json + { + "message": "Rendering completed (/electronics-spa/en/USD/)", + "context": { + "timestamp": "2023-09-13T12:14:21.377Z", + "request": { + "url": "/electronics-spa/en/USD/", + "uuid": "a076a5ba-7889-4c19-840e-395e89fde4b5", + "timeReceived": "2023-09-13T12:14:13.450Z", + "traceContext": { + "version": "00", + "traceId": "0af7651916cd43dd8448eb211c80319c", + "parentId": "b7ad6b7169203331", + "traceFlags": "01" + } + } + } + } + ``` + The following is an example of a log created for production purposes. It is a single line of JSON that can be read by monitoring tools: + + ```text + {"message":"Rendering started (/electronics-spa/en/USD/)","context":{"timestamp":"2023-09-13T12:14:21.377Z","request":{"url":"/electronics-spa/en/USD/","uuid":"a076a5ba-7889-4c19-840e-395e89fde4b5","timeReceived":"2023-09-13T12:14:13.450Z","traceContext":{"version":"00","traceId":"0af7651916cd43dd8448eb211c80319c","parentId":"b7ad6b7169203331","traceFlags":"01"}}}} + ``` + + For more, see [Standardized SSR Logging](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/eaef8c61b6d9477daf75bff9ac1b7eb4/a54ac5aff3f6434aa1ed08a68e25084b.html?locale=en-US). + +## ClientAuthStoreModule + +- `HttpClientModule` has been removed from the `imports` property inside `NgModule` metadata. This should not be a problem, as long as you import `HttpClientModule` in your `app.module.ts`. Alternatively to _importing_ `HttpClientModule` in your `app.module.ts` you can _provide_ in `app.module.ts` the `provideHttpClient(withInterceptorsFromDi())` (optionally with `withFetch()`). For more, see https://angular.io/api/common/http/provideHttpClient + +## AuthHttpHeaderService + +- The variable name `refreshTokenTrigger$` has undergone a modification in its type, transitioning from being exclusively of type AuthToken to now encompassing the union type AuthToken | undefined. + +## BaseCoreModule + +- `ErrorHandlingModule` has been added to the `imports` property inside `NgModule` metadata. It provides `CxErrorHandler` which extends the default Angular `ErrorHandler`. As a result, all errors that occur during server-side rendering are passed to the `LoggerService`, and these errors are logged with an appropriate context. For more information about the `LoggerService`, see [Using the LoggerService](https://help.sap.com/docs/SAP_COMMERCE_COMPOSABLE_STOREFRONT/eaef8c61b6d9477daf75bff9ac1b7eb4/a54ac5aff3f6434aa1ed08a68e25084b.html?locale=en-US#using-the-loggerservice). + +# Changes in feature lib organization + +## MessageService + +- The variable name `data$` has undergone a modification in its type, transitioning from being exclusively of type T to now encompassing the union type T | undefined. + +# Changes in the storefront library + +## AddressBookModule + +- `AddressBookModule` previously housed within the storefront library has been relocated to the `UserProfileModule` within the user library. This transition integrates components, styles, and assets pertinent to the `AddressBookModule`. Such a relocation facilitates improved accessibility to essential services within the user library's domain and offers the potential to address constraints imposed by separate library boundaries. diff --git a/docs/migration/2211_19/generated-typescript-changes-doc.md b/docs/migration/2211_19/generated-typescript-changes-doc.md new file mode 100644 index 00000000000..464fe541265 --- /dev/null +++ b/docs/migration/2211_19/generated-typescript-changes-doc.md @@ -0,0 +1,1849 @@ + + +# 2211.0 Typescript Breaking Changes + +This document contains a list of breaking changes or potentially breaking changes for Spartacus 2211.19. + + + +# Class AsmBindCartComponent +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + globalMessageService: GlobalMessageService, + activeCartFacade: ActiveCartFacade, + multiCartFacade: MultiCartFacade, + asmBindCartFacade: AsmBindCartFacade, + launchDialogService: LaunchDialogService, + savedCartFacade: SavedCartFacade, + asmComponentService: AsmComponentService, + routing: RoutingService, + featureConfig: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + globalMessageService: GlobalMessageService, + activeCartFacade: ActiveCartFacade, + multiCartFacade: MultiCartFacade, + asmBindCartFacade: AsmBindCartFacade, + launchDialogService: LaunchDialogService, + savedCartFacade: SavedCartFacade, + asmComponentService?: AsmComponentService | undefined, + routing?: RoutingService | undefined +) + +``` + + +### Property featureConfig is removed. + + + +### Method goToActiveCartDetail is removed. + + + +### Method isDeepLinkActiveCart is removed. + + + +### Method isDeepLinkInactiveCart is removed. + + + +### Method onDeeplinkCart is removed. + + + + + +# Class AsmComponentService +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + authService: AuthService, + csAgentAuthService: CsAgentAuthService, + winRef: WindowRef, + asmEnablerService: AsmEnablerService, + asmDeepLinkService: AsmDeepLinkService +) + +``` + + +Current version: + +``` + +constructor( + authService: AuthService, + csAgentAuthService: CsAgentAuthService, + winRef: WindowRef, + asmEnablerService?: AsmEnablerService | undefined, + asmDeepLinkService?: AsmDeepLinkService | undefined +) + +``` + + + + +# Class AsmMainUiComponent +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + authService: AuthService, + csAgentAuthService: CsAgentAuthService, + asmComponentService: AsmComponentService, + globalMessageService: GlobalMessageService, + routingService: RoutingService, + asmService: AsmService, + userAccountFacade: UserAccountFacade, + launchDialogService: LaunchDialogService, + featureConfig: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + authService: AuthService, + csAgentAuthService: CsAgentAuthService, + asmComponentService: AsmComponentService, + globalMessageService: GlobalMessageService, + routingService: RoutingService, + asmService: AsmService, + userAccountFacade: UserAccountFacade, + launchDialogService: LaunchDialogService +) + +``` + + +### Property featureConfig is removed. + + + +### Method handleDeepLinkParamsAfterStartSession is removed. + + + + + +# Class CustomerEmulationComponent +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + asmComponentService: AsmComponentService, + userAccountFacade: UserAccountFacade, + launchDialogService: LaunchDialogService, + featureModules: FeatureModulesService +) + +``` + + +Current version: + +``` + +constructor( + asmComponentService: AsmComponentService, + userAccountFacade: UserAccountFacade, + launchDialogService?: LaunchDialogService | undefined, + featureModules?: FeatureModulesService | undefined +) + +``` + + + + +# Class CustomerListComponent +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + launchDialogService: LaunchDialogService, + breakpointService: BreakpointService, + asmConfig: AsmConfig, + translation: TranslationService, + asmCustomerListFacade: AsmCustomerListFacade, + featureConfig?: FeatureConfigService, + occConfig?: OccConfig +) + +``` + + +Current version: + +``` + +constructor( + launchDialogService: LaunchDialogService, + breakpointService: BreakpointService, + asmConfig: AsmConfig, + translation: TranslationService, + asmCustomerListFacade: AsmCustomerListFacade, + occConfig?: OccConfig | undefined +) + +``` + + +### Property featureConfig is removed. + + + + + +# Class CustomerSelectionComponent +## @spartacus/asm/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + fb: UntypedFormBuilder, + asmService: AsmService, + config: AsmConfig, + directionService: DirectionService +) + +``` + + +Current version: + +``` + +constructor( + fb: UntypedFormBuilder, + asmService: AsmService, + config: AsmConfig, + directionService: DirectionService, + launchDialogService: LaunchDialogService +) + +``` + + +### Property launchDialogService changed. + + +Previous version: + +``` +launchDialogService: LaunchDialogService | undefined +``` + + +Current version: + +``` +launchDialogService: LaunchDialogService +``` + + + + +# Function property +## @spartacus/asm/core + + +Function property changed. + +Previous version: + +``` + +property( + prop1: P1, + prop2: P2, + comparator: Comparator +): Comparator + +``` + + +Current version: + +``` + +property( + prop: P, + comparator: Comparator +): Comparator + +``` + + + + +# Class CsAgentAuthService +## @spartacus/asm/root + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + authService: AuthService, + authStorageService: AsmAuthStorageService, + userIdService: UserIdService, + oAuthLibWrapperService: OAuthLibWrapperService, + store: Store, + _userProfileFacade: UserProfileFacade, + userAccountFacade: UserAccountFacade +) + +``` + + +Current version: + +``` + +constructor( + authService: AuthService, + authStorageService: AsmAuthStorageService, + userIdService: UserIdService, + oAuthLibWrapperService: OAuthLibWrapperService, + store: Store, + userAccountFacade: UserAccountFacade +) + +``` + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + authService: AuthService, + authStorageService: AsmAuthStorageService, + userIdService: UserIdService, + oAuthLibWrapperService: OAuthLibWrapperService, + store: Store, + userProfileFacade: UserProfileFacade, + userAccountFacade: UserAccountFacade, + featureConfig: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + authService: AuthService, + authStorageService: AsmAuthStorageService, + userIdService: UserIdService, + oAuthLibWrapperService: OAuthLibWrapperService, + store: Store, + userAccountFacade: UserAccountFacade +) + +``` + + +### Property featureConfig is removed. + + + +### Property userProfileFacade is removed. + + + + + +# Class ActiveCartService +## @spartacus/cart/base/core + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + multiCartFacade: MultiCartFacade, + userIdService: UserIdService +) + +``` + + +Current version: + +``` + +constructor( + multiCartFacade: MultiCartFacade, + userIdService: UserIdService, + winRef: WindowRef +) + +``` + + +### Property winRef changed. + + +Previous version: + +``` +winRef: WindowRef | undefined +``` + + +Current version: + +``` +winRef: WindowRef +``` + + + + +# Interface CardType +## @spartacus/cart/base/root + +moved to @spartacus/core + + + + +# Interface PaymentDetails +## @spartacus/cart/base/root + +moved to @spartacus/core + + + + +# Class ProfileTagEventService +## @spartacus/cds + + +### Method getConsentReference changed. + + +Previous version: + +``` + +getConsentReference(): Observable + +``` + + +Current version: + +``` + +getConsentReference(): Observable | null + +``` + + +### Method getProfileTagEvents changed. + + +Previous version: + +``` + +getProfileTagEvents(): Observable + +``` + + +Current version: + +``` + +getProfileTagEvents(): Observable + +``` + + + + +# Class CheckoutPaymentTypeComponent +## @spartacus/checkout/b2b/components + + +### Property globalMessageService changed. + + +Previous version: + +``` +globalMessageService: GlobalMessageService | undefined +``` + + +Current version: + +``` +globalMessageService: GlobalMessageService +``` + + + + +# Class CheckoutDeliveryAddressComponent +## @spartacus/checkout/base/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + userAddressService: UserAddressService, + checkoutDeliveryAddressFacade: CheckoutDeliveryAddressFacade, + activatedRoute: ActivatedRoute, + translationService: TranslationService, + activeCartFacade: ActiveCartFacade, + checkoutStepService: CheckoutStepService, + checkoutDeliveryModesFacade: CheckoutDeliveryModesFacade, + globalMessageService: GlobalMessageService, + checkoutConfigService: CheckoutConfigService +) + +``` + + +Current version: + +``` + +constructor( + userAddressService: UserAddressService, + checkoutDeliveryAddressFacade: CheckoutDeliveryAddressFacade, + activatedRoute: ActivatedRoute, + translationService: TranslationService, + activeCartFacade: ActiveCartFacade, + checkoutStepService: CheckoutStepService, + checkoutDeliveryModesFacade: CheckoutDeliveryModesFacade, + globalMessageService: GlobalMessageService +) + +``` + + +### Property checkoutConfigService changed. + + +Previous version: + +``` +checkoutConfigService: CheckoutConfigService | undefined +``` + + +Current version: + +``` +checkoutConfigService: CheckoutConfigService +``` + + + + +# Class CheckoutDeliveryModeComponent +## @spartacus/checkout/base/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + fb: UntypedFormBuilder, + checkoutConfigService: CheckoutConfigService, + activatedRoute: ActivatedRoute, + checkoutStepService: CheckoutStepService, + checkoutDeliveryModesFacade: CheckoutDeliveryModesFacade, + activeCartFacade: ActiveCartFacade, + globalMessageService: GlobalMessageService +) + +``` + + +Current version: + +``` + +constructor( + fb: UntypedFormBuilder, + checkoutConfigService: CheckoutConfigService, + activatedRoute: ActivatedRoute, + checkoutStepService: CheckoutStepService, + checkoutDeliveryModesFacade: CheckoutDeliveryModesFacade, + activeCartFacade: ActiveCartFacade +) + +``` + + +### Property globalMessageService changed. + + +Previous version: + +``` +globalMessageService: GlobalMessageService | undefined +``` + + +Current version: + +``` +globalMessageService: GlobalMessageService +``` + + + + +# Class AuthHttpHeaderService +## @spartacus/core + + +### Property refreshToken$ changed. + + +Previous version: + +``` +refreshToken$: Observable<[AuthToken, boolean, boolean]> +``` + + +Current version: + +``` +refreshToken$: Observable<[AuthToken | undefined, boolean, boolean]> +``` + + +### Property refreshTokenTrigger$ changed. + + +Previous version: + +``` +refreshTokenTrigger$: Subject +``` + + +Current version: + +``` +refreshTokenTrigger$: Subject +``` + + + + +# Variable I18NEXT_HTTP_BACKEND_CLIENT +## @spartacus/core + + +Variable I18NEXT_HTTP_BACKEND_CLIENT changed. + +Previous version: + +``` +I18NEXT_HTTP_BACKEND_CLIENT: InjectionToken<((options: BackendOptions, url: string, payload: string | {}, callback: RequestCallback) => void) | undefined> +``` + + +Current version: + +``` +I18NEXT_HTTP_BACKEND_CLIENT: InjectionToken<((options: import("i18next-http-backend").HttpBackendOptions, url: string, payload: string | {}, callback: import("i18next-http-backend").RequestCallback) => void) | undefined> +``` + + + + +# TypeAlias I18nextHttpBackendClient +## @spartacus/core + + +TypeAlias I18nextHttpBackendClient changed. + +Previous version: + +``` +BackendOptions, +['request'] +``` + + +Current version: + +``` +HttpBackendOptions, +['request'] +``` + + + + +# Class I18nextHttpBackendInitializer +## @spartacus/core + + +### Method getBackendConfig changed. + + +Previous version: + +``` + +getBackendConfig(): BackendOptions + +``` + + +Current version: + +``` + +getBackendConfig(): HttpBackendOptions + +``` + + +### Method initialize changed. + + +Previous version: + +``` + +initialize(): InitOptions + +``` + + +Current version: + +``` + +initialize(): InitOptions + +``` + + + + +# Class UserService +## @spartacus/core + + +Class UserService has been removed and is no longer part of the public API. + + + + +# Class MessageService +## @spartacus/organization/administration/components + + +### Property data$ changed. + + +Previous version: + +``` +data$: ReplaySubject +``` + + +Current version: + +``` +data$: ReplaySubject +``` + + +### Method get changed. + + +Previous version: + +``` + +get(): Observable + +``` + + +Current version: + +``` + +get(): Observable + +``` + + + + +# Class UserFormService +## @spartacus/organization/administration/components + + +### Property featureConfigService is removed. + + + + + +# Class ConfiguratorAddToCartButtonComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + routingService: RoutingService, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorCartService: ConfiguratorCartService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + globalMessageService: GlobalMessageService, + orderHistoryFacade: OrderHistoryFacade, + commonConfiguratorUtilsService: CommonConfiguratorUtilsService, + configUtils: ConfiguratorStorefrontUtilsService, + intersectionService: IntersectionService +) + +``` + + +Current version: + +``` + +constructor( + routingService: RoutingService, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorCartService: ConfiguratorCartService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + globalMessageService: GlobalMessageService, + orderHistoryFacade: OrderHistoryFacade, + commonConfiguratorUtilsService: CommonConfiguratorUtilsService, + configUtils: ConfiguratorStorefrontUtilsService, + intersectionService: IntersectionService, + configuratorQuantityService: ConfiguratorQuantityService +) + +``` + + +### Property configuratorQuantityService changed. + + +Previous version: + +``` +configuratorQuantityService: ConfiguratorQuantityService | undefined +``` + + +Current version: + +``` +configuratorQuantityService: ConfiguratorQuantityService +``` + + + + +# Class ConfiguratorAttributeDropDownComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + + + +# Class ConfiguratorAttributeFooterComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configUtils: ConfiguratorStorefrontUtilsService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + featureConfigService?: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + configUtils: ConfiguratorStorefrontUtilsService, + attributeComponentContext: ConfiguratorAttributeCompositionContext +) + +``` + + +### Property featureConfigService is removed. + + + +### Method needsUserInputMessage is removed. + +Use method needsUserInputMsg instead. + + + +# Class ConfiguratorAttributeHeaderComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configUtils: ConfiguratorStorefrontUtilsService, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configuratorUiSettings: ConfiguratorUISettingsConfig, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + featureConfigService?: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + configUtils: ConfiguratorStorefrontUtilsService, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configuratorUiSettings: ConfiguratorUISettingsConfig, + attributeComponentContext: ConfiguratorAttributeCompositionContext +) + +``` + + +### Property featureConfigService is removed. + + + +### Method isAttributeWithDomain is removed. + +A replacement is not available and not needed, since its caller was deleted method `isRequiredAttributeWithDomain`. + +### Method isRequiredAttributeWithDomain is removed. + +Use method isRequiredAttributeWithoutErrorMsg instead. + + + +# Class ConfiguratorAttributeInputFieldComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + config: ConfiguratorUISettingsConfig, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + config: ConfiguratorUISettingsConfig, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + + + +# Class ConfiguratorAttributeNumericInputFieldComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configAttributeNumericInputFieldService: ConfiguratorAttributeNumericInputFieldService, + config: ConfiguratorUISettingsConfig, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService, + featureConfigService: FeatureConfigService +) + +``` + + +Current version: + +``` + +constructor( + configAttributeNumericInputFieldService: ConfiguratorAttributeNumericInputFieldService, + config: ConfiguratorUISettingsConfig, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configAttributeNumericInputFieldService: ConfiguratorAttributeNumericInputFieldService, + config: ConfiguratorUISettingsConfig, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + configAttributeNumericInputFieldService: ConfiguratorAttributeNumericInputFieldService, + config: ConfiguratorUISettingsConfig, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + +### Property featureConfigService is removed. + + + + + +# Class ConfiguratorAttributeProductCardComponent +## @spartacus/product-configurator/rulebased + + +### Property attributeName is removed. + + + + + +# Class ConfiguratorAttributeRadioButtonComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + + + +# Class ConfiguratorAttributeSingleSelectionBaseComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + + + +# Class ConfiguratorAttributeSingleSelectionBundleDropdownComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService +) + +``` + + +Current version: + +``` + +constructor( + quantityService: ConfiguratorAttributeQuantityService, + translation: TranslationService, + attributeComponentContext: ConfiguratorAttributeCompositionContext, + configuratorCommonsService: ConfiguratorCommonsService, + configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +) + +``` + + +### Property configuratorStorefrontUtilsService changed. + + +Previous version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService | undefined +``` + + +Current version: + +``` +configuratorStorefrontUtilsService: ConfiguratorStorefrontUtilsService +``` + + + + +# Class ConfiguratorFormComponent +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + configExpertModeService: ConfiguratorExpertModeService, + launchDialogService: LaunchDialogService, + featureConfigService: FeatureConfigService, + globalMessageService: GlobalMessageService +) + +``` + + +Current version: + +``` + +constructor( + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + configExpertModeService: ConfiguratorExpertModeService, + launchDialogService: LaunchDialogService, + globalMessageService: GlobalMessageService +) + +``` + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + configExpertModeService: ConfiguratorExpertModeService, + launchDialogService: LaunchDialogService +) + +``` + + +Current version: + +``` + +constructor( + configuratorCommonsService: ConfiguratorCommonsService, + configuratorGroupsService: ConfiguratorGroupsService, + configRouterExtractorService: ConfiguratorRouterExtractorService, + configExpertModeService: ConfiguratorExpertModeService, + launchDialogService: LaunchDialogService, + globalMessageService: GlobalMessageService +) + +``` + + +### Property featureConfigservice is removed. + + + +### Property globalMessageService changed. + + +Previous version: + +``` +globalMessageService: GlobalMessageService | undefined +``` + + +Current version: + +``` +globalMessageService: GlobalMessageService +``` + + + + +# Class ConfiguratorGroupMenuComponent +## @spartacus/product-configurator/rulebased + + +### Method isConflictGroupType changed. + + +Previous version: + +``` + +isConflictGroupType( + groupType: Configurator.GroupType +): boolean + +``` + + +Current version: + +``` + +isConflictGroupType( + groupType: Configurator.GroupType | undefined +): boolean + +``` + + +### Method isConflictGroupTypeAllowingUndefined is removed. + +Use method isConflictGroupType instead. + + + +# Class ConfiguratorOverviewFilterButtonComponent +## @spartacus/product-configurator/rulebased + + +### Property config$ is removed. + + + + + +# Class ConfiguratorOverviewSidebarComponent +## @spartacus/product-configurator/rulebased + + +### Property config$ is removed. + + + + + +# Class ConfiguratorRouterListener +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + configuratorCartService: ConfiguratorCartService, + routingService: RoutingService +) + +``` + + +Current version: + +``` + +constructor( + configuratorCartService: ConfiguratorCartService, + routingService: RoutingService, + configuratorQuantityService: ConfiguratorQuantityService +) + +``` + + +### Property configuratorQuantityService changed. + + +Previous version: + +``` +configuratorQuantityService: ConfiguratorQuantityService | undefined +``` + + +Current version: + +``` +configuratorQuantityService: ConfiguratorQuantityService +``` + + + + +# Class RulebasedConfiguratorConnector +## @spartacus/product-configurator/rulebased + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + adapters: RulebasedConfiguratorAdapter[], + configUtilsService: CommonConfiguratorUtilsService +) + +``` + + +Current version: + +``` + +constructor( + adapters: RulebasedConfiguratorAdapter[], + configUtilsService: CommonConfiguratorUtilsService, + config: ConfiguratorCoreConfig +) + +``` + + +### Property config changed. + + +Previous version: + +``` +config: ConfiguratorCoreConfig | undefined +``` + + +Current version: + +``` +config: ConfiguratorCoreConfig +``` + + + + +# Class LegacyExpressServerLogger +## @spartacus/setup/ssr + + +Class LegacyExpressServerLogger has been removed and is no longer part of the public API. + + + + +# Class OptimizedSsrEngine +## @spartacus/setup/ssr + + +### Method log changed. + + +Previous version: + +``` + +log( + message: string, + debug?: boolean, + context?: ExpressServerLoggerContext +): void + +``` + + +Current version: + +``` + +log( + message: string, + debug: boolean | undefined, + context: ExpressServerLoggerContext +): void + +``` + + + + +# Interface SsrOptimizationOptions +## @spartacus/setup/ssr + + +### PropertySignature logger changed. + + +Previous version: + +``` +logger: true | ExpressServerLogger +``` + + +Current version: + +``` +logger: ExpressServerLogger +``` + + + + +# Class CmsTicketInterceptor +## @spartacus/smartedit/root + + +### Property featureConfig is removed. + + + + + +# Class AddressBookComponent +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + + + +# Class AddressBookComponentService +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + + + +# Class AddressBookModule +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + + + +# Class AddressFormComponent +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + +### Constructor changed. + + +Previous version: + +``` + +constructor( + fb: UntypedFormBuilder, + userService: UserService, + userAddressService: UserAddressService, + globalMessageService: GlobalMessageService, + translation: TranslationService, + launchDialogService: LaunchDialogService +) + +``` + + +Current version: + +``` + +constructor( + fb: UntypedFormBuilder, + userAddressService: UserAddressService, + globalMessageService: GlobalMessageService, + translation: TranslationService, + launchDialogService: LaunchDialogService, + userProfileFacade: UserProfileFacade +) + +``` + + +### Property userService is removed. + +Use UserProfileFacade instead. + + + +# Class AddressFormModule +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + + + +# Class ProductListComponentService +## @spartacus/storefront + + +### Property featureConfigService is removed. + + + + + +# Class SuggestedAddressDialogComponent +## @spartacus/storefront + +moved to @spartacus/user/profile/components + + diff --git a/docs/migration/2211_19/migration-comments-api-elements.json b/docs/migration/2211_19/migration-comments-api-elements.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/docs/migration/2211_19/migration-comments-api-elements.json @@ -0,0 +1 @@ +[] diff --git a/docs/migration/2211_19/migration-comments-members.json b/docs/migration/2211_19/migration-comments-members.json new file mode 100644 index 00000000000..4c73f5479b9 --- /dev/null +++ b/docs/migration/2211_19/migration-comments-members.json @@ -0,0 +1,32 @@ +[ + { + "apiElementName": "ConfiguratorAttributeFooterComponent", + "entryPoint": "@spartacus/product-configurator/rulebased", + "memberName": "needsUserInputMessage", + "migrationComment": "Use method needsUserInputMsg instead." + }, + { + "apiElementName": "ConfiguratorAttributeHeaderComponent", + "entryPoint": "@spartacus/product-configurator/rulebased", + "memberName": "isRequiredAttributeWithDomain", + "migrationComment": "Use method isRequiredAttributeWithoutErrorMsg instead." + }, + { + "apiElementName": "ConfiguratorAttributeHeaderComponent", + "entryPoint": "@spartacus/product-configurator/rulebased", + "memberName": "isAttributeWithDomain", + "migrationComment": "A replacement is not available and not needed, since its caller was deleted method `isRequiredAttributeWithDomain`." + }, + { + "apiElementName": "ConfiguratorGroupMenuComponent", + "entryPoint": "@spartacus/product-configurator/rulebased", + "memberName": "isConflictGroupTypeAllowingUndefined", + "migrationComment": "Use method isConflictGroupType instead." + }, + { + "apiElementName": "AddressFormComponent", + "entryPoint": "@spartacus/storefront", + "memberName": "userService", + "migrationComment": "Use UserProfileFacade instead." + } +] diff --git a/docs/migration/2211_19/renamed-api-mappings.json b/docs/migration/2211_19/renamed-api-mappings.json new file mode 100644 index 00000000000..fe51488c706 --- /dev/null +++ b/docs/migration/2211_19/renamed-api-mappings.json @@ -0,0 +1 @@ +[] diff --git a/docs/migration/2_0.md b/docs/migration/2_0.md deleted file mode 100644 index 1b8c71c7f11..00000000000 --- a/docs/migration/2_0.md +++ /dev/null @@ -1,3 +0,0 @@ -# Technical Changes in Spartacus 2.0 - -The contents of this page have been moved to the Spartacus documentation repo: [https://sap.github.io/spartacus-docs/technical-changes-version-2/](https://sap.github.io/spartacus-docs/technical-changes-version-2/). diff --git a/docs/migration/3_0.md b/docs/migration/3_0.md deleted file mode 100644 index 9d54ccfd3c6..00000000000 --- a/docs/migration/3_0.md +++ /dev/null @@ -1,3 +0,0 @@ -# Technical Changes in Spartacus 3.0 - -The contents of this page have been moved to the Spartacus documentation repo: [https://sap.github.io/spartacus-docs/technical-changes-version-3/](https://sap.github.io/spartacus-docs/technical-changes-version-3/). diff --git a/docs/migration/4_0.md b/docs/migration/4_0.md deleted file mode 100644 index f9e34b3a5ca..00000000000 --- a/docs/migration/4_0.md +++ /dev/null @@ -1,1904 +0,0 @@ -# Technical Changes in Spartacus 4.0 - -## Before migrating to Spartacus 4.0 - -Before you migrate to version 4.0 of libraries we highly recommend to switch to new app structure and new feature libraries. It's easier to do migration in multiple small steps (migrating to new app structure, switching to extracted feature libs and then migrating to 4.0), where you can make sure that everything still works as before after every step. Read the next chapter if you need more insights why we introduce this change. - -### Reasons for migration to new app structure - -Before the 3.0 release we started to separate libraries based on it's responsibility. With 3.0 we already released few libraries in separate packages (eg. @spartacus/organization, @spartacus/storefinder). We kept moving more libraries in the minor 3.x releases as well. We tried to do that in a no breaking-changes manner. However with each major release we want to pay off tech debt we accumulated during minor releases. Extracted libraries are huge contributor to tech debt, as we keep the same functionality in 2 places. With 4.0 release we will remove the functionality from core libraries (@spartacus/core, @spartacus/storefront, @spartacus/assets and @spartacus/styles) that was already extracted to separate libraries in minor releases. - -Along the way we discovered that we had to change few of the bigger modules to accommodate these changes (eg. `B2cStoreFrontModule`, `StorefrontModule` or `CmsLibModule`). - -So that's why we recommend to switch to new app structure that is not using these modules and to switch to new feature libraries if they exists for the features you are using. Below you can find generic guide on how to do it. After that migration to 4.0 should be easier. - -### Migrating to new, reference app structure - -Before you start to migrate to new app structure read the reasoning behind the change [here](https://sap.github.io/spartacus-docs/reference-app-structure/). - -So let's migrate to the new structure step by step. - -1. Create `SpartacusModule` under `app/spartacus/spartacus.module.ts` path and add it to `imports` in `AppModule`. -2. Add `BaseStorefrontModule` to imports and exports of `SpartacusModule`. This modules is exported from `@spartacus/storefront` library. -3. Create `SpartacusFeaturesModule` under `app/spartacus/spartacus-features.module.ts` path and add it to `imports` in `SpartacusModule`. -4. Create `SpartacusConfigurationModule` under `app/spartacus/spartacus-configuration.module.ts` path and add it to `imports` in `SpartacusModule`. -5. Move spartacus configuration to `SpartacusConfigurationModule`. That would be the configurations you pass with `provideConfig`, `provideConfigFactory` or with `withConfig` methods from some of the modules (eg. `B2cStorefrontModule`, `ConfigModule`). We recommend to use `provideConfig` or `provideConfigFactory` in module providers to configure spartacus. -6. Configure `AppRoutingModule`. If you don't have this module, first create it under `app/app-routing.module.ts`. Don't forget to import this module in `AppModule`. In `AppRoutingModule` imports configure `RouterModule` with these 3 options: - - ```ts - RouterModule.forRoot([], { - anchorScrolling: 'enabled', - relativeLinkResolution: 'corrected', - initialNavigation: 'enabled', - }), - ``` - - Previously this module was configured in `StorefrontModule`, which is now deprecated and removed in version 4.0. - -7. Configure ngrx modules in `AppModule`. They were part of the `StorefrontModule`, but similarly like `RouterModule` we require this configuration to be present in application. You need to add to `imports` 2 modules: `StoreModule.forRoot({})` and `EffectsModule.forRoot([])`. Import these modules from `@ngrx/store` and from `@ngrx/effects`. - - ```ts - @NgModule({ - imports: [ - AppRoutingModule, - StoreModule.forRoot({}), - EffectsModule.forRoot([]), - SpartacusModule, - - // then the rest of your custom modules... - ], - ... - ``` - -8. Now let's focus on replacing deprecated Spartacus grouping modules - - 1. Migrating `B2cStorefrontModule`. - - config from `B2cStorefrontModule.withConfig` should be already moved to `SpartacusConfigurationModule` and provided with `provideConfig` - - first add `HttpClientModule` to imports in `AppModule` if it's not present there - - now add in `SpartacusFeaturesModule` module imports two modules from `@spartacus/storefront`: `StorefrontModule` and `CmsLibModule` (those modules are removed in 4.0, but we want to migrate the modules step by step. Migration of those modules will be covered in next steps). - - this module provided few default configs, so add them to your `SpartacusConfigurationModule` if you rely on them - - ```ts - provideConfig({ - pwa: { - enabled: true, - addToHomeScreen: true, - }, - }), - provideConfig(layoutConfig), - provideConfig(mediaConfig), - ...defaultCmsContentProviders, - ``` - - - remove usage of `B2cStorefrontModule` from your app - - 2. Migrating `B2bStorefrontModule`. - - config from `B2bStorefrontModule.withConfig` should be already moved to `SpartacusConfigurationModule` and provided with `provideConfig` - - add `HttpClientModule` to imports in `AppModule` if it's not present there - - add in `SpartacusFeaturesModule` imports few modules: `StorefrontModule`, `CmsLibModule` from `@spartacus/storefront` (those modules are removed in 4.0, but we want to migrate the modules step by step. Migration of those modules will be covered in next steps) and `CostCenterModule.forRoot()` from `@spartacus/core` - - this module provided few default configs, so add them to your `SpartacusConfigurationModule` if you rely on them - - ```ts - provideConfig(layoutConfig), - provideConfig(mediaConfig), - provideConfig(defaultB2bOccConfig), - provideConfig(defaultB2bCheckoutConfig), - ...defaultCmsContentProviders, - ``` - - - remove usage of `B2bStorefrontModule` from your app - - 3. Migrating `StorefrontModule` - - you should have configured `RouterModule` in `AppRoutingModule` - - in `AppModule` you should already have `StoreModule.forRoot({})` and `EffectsModule.forRoot([])` present in `imports` - - add `AsmModule` from `@spartacus/storefront` to imports in `SpartacusFeaturesModule` - - add `StorefrontFoundationModule` to `SpartacusFeaturesModule` imports. - - Add `MainModule` to `SpartacusFeaturesModule` imports - - Add `SmartEditModule.forRoot()`, `PersonalizationModule.forRoot()` and `OccModule.forRoot()` from `@spartacus/core` to imports in `SpartacusFeaturesModule` - - Add `ProductDetailsPageModule` and `ProductListingPageModule` from `@spartacus/storefront` to imports in `SpartacusFeaturesModule` - - Add `ExternalRoutesModule.forRoot()` from `@spartacus/core` to imports in `SpartacusFeaturesModule` - - remove usage of `StorefrontModule` from you app - - 4. Migrating `CmsLibModule` - - add imports listed below from `@spartacus/storefront` directly to imports in `SpartacusFeaturesModule`: - - ```ts - AnonymousConsentManagementBannerModule, - AsmModule, // remove if it's already present - HamburgerMenuModule, - CmsParagraphModule, - LinkModule, - BannerModule, - CategoryNavigationModule, - NavigationModule, - FooterNavigationModule, - BreadcrumbModule, - SearchBoxModule, - SiteContextSelectorModule, - QualtricsModule, - AddressBookModule, - OrderHistoryModule, - OrderCancellationModule, - OrderReturnModule, - ReturnRequestListModule, - ReturnRequestDetailModule, - ProductListModule, - ProductFacetNavigationModule, - ProductTabsModule, - ProductCarouselModule, - ProductReferencesModule, - OrderDetailsModule, - PaymentMethodsModule, - ConsentManagementModule, - CartComponentModule, - TabParagraphContainerModule, - OrderConfirmationModule, - ProductImagesModule, - ProductSummaryModule, - ProductVariantsModule, - ProductIntroModule, - BannerCarouselModule, - MyCouponsModule, - WishListModule, - NotificationPreferenceModule, - MyInterestsModule, - StockNotificationModule, - ReplenishmentOrderHistoryModule, - ReplenishmentOrderConfirmationModule, - ReplenishmentOrderDetailsModule, - UserComponentModule, - CloseAccountModule, - UpdateEmailModule, - UpdatePasswordModule, - UpdateProfileModule, - ForgotPasswordModule, - ResetPasswordModule, - ``` - - - remove usage of `CmsLibModule` from your app - - 5. Migrating `MainModule` - - add `AnonymousConsentsDialogModule` from `@spartacus/storefront` into imports in `SpartacusFeaturesModule` - - remove usage of `MainModule` from you app - - 6. Migrating `StorefrontFoundationModule` - - add `AuthModule.forRoot()`, `AnonymousConsentsModule.forRoot()`, `CartModule.forRoot()`, `CheckoutModule.forRoot()`, `UserModule.forRoot()` and `ProductModule.forRoot()` from `@spartacus/core` into imports in `SpartacusFeaturesModule` - - add `CartPageEventModule`, `PageEventModule` and `ProductPageEventModule` from `@spartacus/storefront` into imports in `SpartacusFeaturesModule` - - remove usage of `StorefrontFoundationModule` from your app - - 7. Migrating `OccModule` - - add `AsmOccModule`, `CartOccModule`, `CheckoutOccModule`, `ProductOccModule`, `UserOccModule`, `CostCenterOccModule` from `@spartacus/core` to imports in `SpartacusFeaturesModule` - - remove usage of `OccModule` from your app - - 8. Migrating `EventsModule` - - add `CartPageEventModule`, `PageEventModule` and `ProductPageEventModule` from `@spartacus/storefront` to imports in `SpartacusFeaturesModule` - - remove usage of `EventsModule` from your app - - 9. That's it. You migrated to new app structure. However there is one more thing that we recommend to do. All these huge modules that we just migrated were created to make it easy to create complete spartacus application. What we mean by "complete" is the spartacus with a lot of features enabled out of the box. You might've not even used those features and yet they landed in your application files making your app bigger and by that increasing your application initial load time. As we just replaced these bootstrap modules with granular modules it gives you a great opportunity to remove feature modules that you don't need. - - Here is a list of common features that you might not use: - - if you don't use ASM feature you can remove from `SpartacusFeaturesModule` imports of `AsmModule` and `AsmOccModule` - - if you don't use Smartedit you can remove from `SpartacusFeaturesModule` import of `SmartEditModule` - - if you don't use Qualtrics you can remove from `SpartacusFeaturesModule` import of `QualtricsModule` - - if you don't use product variants you can remove from `SpartacusFeaturesModule` imports of `ProductVariantsModule` - - if you don't support replenishments order you can remove from `SpartacusFeaturesModule` imports of `ReplenishmentOrderHistoryModule`, `ReplenishmentOrderConfirmationModule` and `ReplenishmentOrderDetailsModule` - - There are many more modules that you might not need, so we recommend just going through all the imports in `SpartacusFeaturesModule` and verifying if you use it or not. If not just remove it and make your application smaller. - -### Upgrade Angular libraries first -Before upgrading Spartacus to 4.0, you need first to upgrade Angular to the version 12 and upgrade Angular 3rd party dependencies like `@ng-bootstrap/ng-bootstrap` or `@ng-select/ng-select` to the versions compatible with Angular 12. - -```bash -ng update @ng-bootstrap/ng-bootstrap@10 @ng-select/ng-select@7 @angular/core@12 @angular/cli@12 -``` - -For more, see [the official Angular upgrade guide](https://update.angular.io/). - -### Upgrade Spartacus to 3.4.x first -You must first upgrade all of your `@spartacus` libraries to the latest 3.4.x release before you begin upgrading to Spartacus 4.0. For more information, see [Upgrading Spartacus Libraries to a New Minor Version](https://sap.github.io/spartacus-docs/release-information/#upgrading-spartacus-libraries-to-a-new-minor-version). - -### Finally, upgrade Spartacus to 4.0 -Spartacus 4.0 includes many new features and fixes. Since this update is a major release, some of the updates may also be breaking changes for your application. In this case, additional work on your side may be required to fix issues that result from upgrading from 3.4.x to 4.0. - -To update to version 4.0 of Spartacus, run the following command in the workspace of your Angular application: - -```bash -ng update @spartacus/schematics@4 -``` - -When the update has finished running, inspect your code for comments that begin with `// TODO:Spartacus`. For detailed information about each added comment, see [Detailed List of Changes](#detailed-list-of-changes) below. - -## Augmentable Config interface - -In spartacus we expose quite some number of methods that accepts configuration. Up until now we didn't do a great job of typing those methods. You might notice that usually when we provide configuration we use type assertions (eg. `provideConfig({i18n: {...}})`) to improve type safety and autocomplete functionality. - -In version 4.0 we changed the way we work with `Config`. Now each feature contributes to this interface thanks to module augmentation TS feature. Thanks to that `Config` now correctly describe all configuration options you can pass to spartacus. - -With that changed we are able to change the type of all the methods that accept configuration from `any` to `Config`. You no longer have to use type assertion to benefit from better type safety and DX. - -We still keep the individual configs (eg. `I18nConfig`, `AsmConfig`, `AuthConfig`, etc.), but all those interfaces also contribute to `Config` interface. - -When you need to access configuration object you can still in constructor use following syntax: `protected config: AsmConfig` (this will only hint for you `AsmConfig` properties), but you now have the option to do it with `protected config: Config`. Using the latter is recommended when you want to access complete configuration with type safety (eg. `FeatureConfig` and `MediaConfig` at the same time). - -This change should be for painless for most of the users, but it will affect you if you have custom configuration in your application. - -Let's show it on an example with special configuration for theme: - -```ts -// existing code -@Injectable({ - providedIn: 'root', - useExisting: Config, -}) -export abstract class ThemeConfig { - theme?: { - dark?: boolean; - }; -} - -// required changes - -// You need to augment `Config` interface from `@spartacus/core` to be able to provide this config with `provideConfig` method -declare module '@spartacus/core' { - interface Config extends ThemeConfig {} -} -``` - -You don't need to change anything in a places where you use this config, but in a place where you declare you custom config you have to instruct Typescript that `Config` interface also have `theme` property with `dark` option. Without it Typescript will complain that you try to pass properties which are not part of `Config`. - -We still recommend making top-level configuration properties optional, so you can pass the configuration in multiple chunks and not in a single place. - -## Detailed List of Changes - -#### Config providers - -- first parameter of function `provideConfig` changed from type `any` to `Config` -- first parameter of function `provideDefaultConfig` changed from type `any` to `Config` - -#### ConfigModule - -- parameter of method `withConfig` changed type from `object` to `Config` -- parameter of method `forRoot` changed type from `any` to `Config` - -#### Config injection tokens - -- `Config` injection token was replaced with injectable `Config` abstract class. -- `ConfigChunk` injection token is now of type `Config[]` (from `object[]`) -- `DefaultConfigChunk` injection token is now of type `Config[]` (from `object[]`) - -#### StorefrontConfig - -This type was removed, as it's purpose is now covered with augmentable `Config` interface. Replace usage of `StorefrontConfig` with `Config`. - -#### ConfigInitializerService - -- Constructor changed from: - - ```ts - constructor( - @Inject(Config) protected config: any, - @Optional() - @Inject(CONFIG_INITIALIZER_FORROOT_GUARD) - protected initializerGuard, - @Inject(RootConfig) protected rootConfig: any - ) {} - ``` - - to - - ```ts - constructor( - protected config: Config, - @Optional() - @Inject(CONFIG_INITIALIZER_FORROOT_GUARD) - protected initializerGuard: any, - @Inject(RootConfig) protected rootConfig: Config - ) {} - ``` - -- Method `getStable` return signature changed from `Observable` to `Observable`. -- Method `getStableConfig` return signature changed from `Promise` to `Promise`. - -#### Config validators - -- type `ConfigValidator` changed from `(config: any) => string | void` to `(config: Config) => string | void` -- first parameter of function `validateConfig` changed from `any` to `Config` - -#### ConfigInitializer - -- `ConfigInitializer.configFactory` signature changed from `() => Promise` to `() => Promise`. - -#### ConfigurationService - -- `unifiedConfig$` type changed from `Observable` to `Observable` -- `config` type changed from `any` to `Config` -- constructor changed from - - ```ts - constructor( - @Inject(RootConfig) protected rootConfig: any, - @Inject(DefaultConfig) protected defaultConfig: any, - protected unifiedInjector: UnifiedInjector, - @Inject(Config) config: any - ) - ``` - - to - - ```ts - constructor( - @Inject(RootConfig) protected rootConfig: Config, - @Inject(DefaultConfig) protected defaultConfig: Config, - protected unifiedInjector: UnifiedInjector, - config: Config - ) - ``` - -#### Feature config utils - -- `isFeatureLevel` first parameter type changed from `unknown` to `Config` -- `isFeatureEnabled` first parameter type changed from `unknown` to `Config` - -#### MediaService - -- constructor changed from - - ```ts - constructor( - @Inject(Config) protected config: StorefrontConfig, - protected breakpointService: BreakpointService - ) {} - ``` - - to - - ```ts - constructor( - protected config: Config - ) {} - ``` -- `getMedia` now supports `role` attribute - - - -### ModalService -- `ModalService` no longer depends on `FeatureConfigService`. But `ApplicationRef` is now a new required dependency. - - -#### Product configurator configuration - -- `productConfigurator` configuration option is now optional (properties `updateDebounceTime` and `cpq` from this options are now also optional) -- `backend.cpq` configuration option is now optional - -#### SmartEditConfig - -- `smartEdit` property is optional -- `smartEdit.storefrontPreviewRoute` property is optional -- `smartEdit.allowOrigin` property is optional - -#### PersonalizationConfig - -- `personalization` property is optional - -#### CmsStructureConfig - -- `cmsStructure` property is optional - -#### PageComponentModule - -- Exposes `forRoot()` method, to minimize side-effects of frequent import in dependant modules. `PageComponentModule.forRoot()` is now imported in `BaseStorefrontModule`. - -## Breaking Changes Introduced in 4.0 - -### StoreFinderService - -- Added `platformId` injection to constructor. -- Methods `getStoreLatitude()` and `getStoreLongitude()` have been moved to this service from removed `StoreDataService`. - -### StoreDataService - -- Service has been removed and functions moved to `StoreFinderService`. - -### AbstractStoreItemComponent - -- `StoreDataService` has been replaced with `StoreFinderService`. - -### StoreFinderListItemComponent - -- `StoreDataService` has been replaced with `StoreFinderService`. - -### StoreFinderListComponent - -- `StoreDataService` has been replaced with `StoreFinderService`. - -### StoreFinderStoreDescriptionComponent - -- `StoreDataService` has been replaced with `StoreFinderService`. - -### GoogleMapRendererService - -- `StoreDataService` has been replaced with `StoreFinderService`. -- `ExternalJsFileLoader` has been replaced with `ScriptLoader`. - -### ScheduleComponent - -- `ngOnChanges()` has been changed to `ngOnInit()` along with corresponding class implementations (ie. implements `OnChanges` to `OnInit`). -- `displayDays` variable has been removed. Use `weekDays` instead. -- `StoreDataService` has been removed`. -- Methods `getStoreOpeningTime()`, `getStoreClosingTime()`, `getInitialDate()` have been removed. Use `weekDayOpeningList` from `location` instead. -### PromotionService -PromotionService is deleted. The promotions can directly be found on the order or cart. Use other existing services to retrieve the Order or cart. - -The order promotions are in the order/cart attributes `appliedOrderPromotions` and `potentialOrderPromotions` - -The product promotions for order/cart entries are now available via the attribute `entries[].promotions` - - -### SavedCartDetailsActionComponent - - Removed `ClearCheckoutService` from constructor. - -### SavedCartListComponent -- Removed `ClearCheckoutService` from constructor. - -### SavedCartFormDialogComponent -- Removed `ClearCheckoutService` from constructor. - -### AddressBookComponentService -Lib: @spartacus/core -Class: AddressBookComponentService -Change: constructor parameter `checkoutDeliveryService: CheckoutDeliveryService` is removed. -Instead, the `CheckoutEventListener` from the checkout lib listens for the new address change events and resets the checkout delivery accordingly. - -### AddressBookComponent -Lib: @spartacus/core -Class: AddressBookComponent -Change: Two constructor parameters are removed. First the constructor parameter `checkoutDeliveryService: CheckoutDeliveryService` is removed. -AddressBookComponent does not call `CheckoutDeliveryService.clearCheckoutDeliveryDetails()` anymore when an address is changed. Instead, `AddressBookComponentService` fires events. See `AddressBookComponentService` migration doc. - -The second constructor parameters removed is `userAddressService: UserAddressService`. `UserAddressService` interactions are now encapsulated in `AddressBookComponentService`. - - -### AddressFormComponent -Lib: @spartacus/core -Change: constructor parameter `checkoutDeliveryService: CheckoutDeliveryService` is removed. -AddressFormComponent now uses the new address verification function from `UserAddressService` called `verifyAddress` instead of the `verifyAddress` function from `CheckoutDeliveryService`. `UserAddressService.verifyAddress` does not use the ngrx store under the hood. - -Change: `TranslationService` is a new, required constructor dependency. -AddressFormComponent now uses translations to show a configurable default title option instead of a hard coded title. - -### UserAddressService -Lib: @spartacus/core -Change: Two new required constructor parameters `userAddressConnector: UserAddressConnector` and `command: CommandService` - -### CheckoutDetailsLoadedGuard -Lib: @spartacus/storefront -CheckoutDetailsLoadedGuard was not used and is now removed. - -### PaymentDetailsSetGuard -Lib: @spartacus/storefront -PaymentDetailsSetGuard was not used and is now removed. - -### ShippingAddressSetGuard -Lib: @spartacus/storefront -ShippingAddressSetGuard was not used and is now removed. - -### DeliveryModeSetGuard -Lib: @spartacus/storefront -DeliveryModeSetGuard was not used and is now removed. - - -## New Checkout Library - -Spartacus 4.0 introduces the checkout library. The checkout related code is moved out of `@spartacus/core` and `@spartacus/storefrontlib` into one of the checkout lib's entry points. The checkout library is split into these entry points: - -``` -@spartacus/checkout/assets -The checkout related i18n keys are moved here. - -@spartacus/checkout/components -Checkout related UI code is moved here. This includes components, guards and ui services. - -@spartacus/checkout/core -The checkout facade API implementation are moved here, as well as connectors, event builder, event listener, models, other services, and state management. - -@spartacus/checkout/occ -The checkout related OCC code is moved here. This includes the checkout related adapters and converters. - -@spartacus/checkout/root -The root entry point is, by convention, meant to always be eager loaded. It contains the config, events, facades, http interceptors and models. - -@spartacus/checkout/styles -The checkout related scss styles are moved here. -``` - -Most of the code is moved unchanged, but some classes required changes after they were moved. See the section below for the list: - -## (start) Changes in the classes carried over to the @spartacus/checkout lib - -### Use facades instead of services - -Some services are now available through facades. Facades should be used instead. The main advantage to use facades instead of their service implementation is that the facades support lazy loading. Facades are imported from `@spartacus/checkout/root`. - -- `CheckoutCostCenterFacade` should be used instead of `CheckoutCostCenterService` - -- `CheckoutDeliveryFacade` should be used instead of `CheckoutDeliveryService` - -- `CheckoutPaymentFacade` should be used instead of `CheckoutPaymentService` - -- `CheckoutFacade` should be used instead of `CheckoutService` - -- `PaymentTypeFacade` should be used instead of `PaymentTypeService` - -- `ClearCheckoutFacade` should be used instead of `ClearCheckoutService` - -#### ExpressCheckoutService - -- Service moved from `@spartacus/storefront` entry point to `@spartacus/checkout/components`. -- `CheckoutDeliveryService` constructor parameter replaced with `CheckoutDeliveryFacade` from `@spartacus/checkout/root`. -- `CheckoutPaymentService` constructor parameter replaced with `CheckoutPaymentFacade` from `@spartacus/checkout/root`. -- `CheckoutDetailsService` constructor parameter is now imported from `@spartacus/checkout/components`. -- `CheckoutConfigService` constructor parameter is now imported from `@spartacus/checkout/components`. -- `ClearCheckoutService` constructor parameter replaced with required `ClearCheckoutFacade` from `@spartacus/checkout/root`. -- Method `resetCheckoutProcesses` was removed, use method `resetCheckoutProcesses` from `ClearCheckoutFacade` instead. - -### CostCenterComponent -constructor parameter of type `CheckoutCostCenterService` is now of type `CheckoutCostCenterFacade` -constructor parameter of type `PaymentTypeService` is now of type `PaymentTypeFacade` - -### DeliveryModeComponent -constructor parameter of type `CheckoutDeliveryService` is now of type `CheckoutDeliveryFacade` - -### PaymentMethodComponent -constructor parameter of type `CheckoutService` is now of type `CheckoutFacade` -constructor parameter of type `CheckoutDeliveryService` is now of type `CheckoutDeliveryFacade` -constructor parameter of type `CheckoutPaymentService` is now of type `CheckoutPaymentFacade` - -### PaymentFormComponent -constructor parameter of type `CheckoutPaymentService` is now of type `CheckoutPaymentFacade` -constructor parameter of type `CheckoutDeliveryService` is now of type `CheckoutDeliveryFacade` -PaymentFormComponent does not implement `OnDestroy` anymore -method `ngOnDestroy()` removed. -Address verification uses new `UserAddressService.verifyAddress` function instead of `CheckoutDeliveryService.verifyAddress`. -expiration date has been wrapped to `fieldset` instead of `label`. `span` has been replaced with `legend` and there are new `label` instead of `div` per every form control (expiration month, expiration year). - -### PaymentTypeComponent -constructor parameter of type `PaymentTypeService` is now of type `PaymentTypeFacade` - -### PlaceOrderComponent -constructor parameter of type `CheckoutService` is now of type `CheckoutFacade` - -### ReviewSubmitComponent -constructor parameter of type `CheckoutDeliveryService` is now of type `CheckoutDeliveryFacade` -constructor parameter of type `CheckoutPaymentService` is now of type `CheckoutPaymentFacade` -constructor parameter of type `PaymentTypeService` is now of type `PaymentTypeFacade` -constructor parameter of type `CheckoutCostCenterService` is now of type `CheckoutCostCenterFacade` -Removed constructor parameter `PromotionService` - -Removed the attribute orderPromotions$ -The component gets promotions directly from the cart in the html template. - -### ScheduleReplenishmentOrderComponent -constructor parameter of type `CheckoutService` is now of type `CheckoutFacade` - -### ShippingAddressComponent -constructor parameter of type `CheckoutDeliveryService` is now of type `CheckoutDeliveryFacade` -constructor parameter of type `PaymentTypeService` is now of type `PaymentTypeFacade` -constructor parameter of type `CheckoutCostCenterService` is now of type `CheckoutCostCenterFacade` - -### CheckoutEventModule -Change: One new required constructor parameters `_checkoutEventListener: CheckoutEventListener` - -To split out the checkout code in the checkout lib, the address verification functionality -was moved in `UserAddressService` in @spartacus/core. The address verification related functions in `CheckoutDeliveryService` and ngrx supporting classes are not present in the checkout lib. - -### CheckoutDeliveryService: -These functions are not present in the checkout lib: -- `getAddressVerificationResults(): Observable` -- `verifyAddress(address: Address): void` -- `clearAddressVerificationResults(): void` - -These functions are also not present in the corresponding facade `CheckoutDeliveryFacade` - -### CheckoutState -Property `addressVerification: AddressVerificationState` is removed from the `CheckoutState` class in the checkout lib. - -### AddressVerificationState -The `AddressVerificationState` class is not carried over to the checkout lib. - -### CheckoutDeliveryService -New property `processStateStore: Store` is added into the constructor. - -### CheckoutPaymentService -New property `processStateStore: Store` is added into the constructor. - -### CheckoutService -New property `processStateStore: Store` is added into the constructor. - -### PaymentTypeService -New property `processStateStore: Store` is added into the constructor. - -### OrderConfirmationItemsComponent -Removed constructor parameter `PromotionService` -Removed the attribute orderPromotions$ -The component gets promotions directly from the order in the html template. - -### OccCheckoutAdapter -Protected method `getEndpoint` has been removed. There are new methods: `getPlaceOrderEndpoint`, `getRemoveDeliveryAddressEndpoint`, `getClearDeliveryModeEndpoint`, `getLoadCheckoutDetailsEndpoint`. - -### OccCheckoutPaymentAdapter -Protected method`getCartEndpoint` has been removed. There are new methods: `getCardTypesEndpoint`, `getCreatePaymentDetailsEndpoint`, `getPaymentProviderSubInfoEndpoint`, `getSetPaymentDetailsEndpoint`. - -### OccCheckoutCostCenterAdapter -Protected method`getCartEndpoint` has been removed. There is a new method: `getSetCartCostCenterEndpoint`. - -### OccCheckoutDeliveryAdapter -Protected method`getCartEndpoint` has been removed. There are new methods: `getCreateDeliveryAddressEndpoint`, `getDeliveryModeEndpoint`, `getDeliveryModesEndpoint`, `getSetDeliveryAddressEndpoint`, `getSetDeliveryModeEndpoint`. - -### OccCheckoutPaymentTypeAdapter -Protected method`getCartEndpoint` has been removed. There are new methods: `getPaymentTypesEndpoint`, `getSetCartPaymentTypeEndpoint`. - -### OccCheckoutReplenishmentOrderAdapter -There is a new method: `getScheduleReplenishmentOrderEndpoint`. - -### CheckoutComponentModule -`CheckoutComponentModule` was and renamed to `CheckoutComponentsModule`. It was moved to `@spartacus/checkout/components`. The new module is, not exactly the same as the previous one, but the new one should essentially be a superset of the previous one. - -### CheckoutModule -The `CheckoutModule` from `@spartacus/core` became `CheckoutCoreModule` in `@spartacus/checkout/core`. `CheckoutCoreModule` from `@spartacus/checkout/core` fills a similar role as the previous `CheckoutModule` from `@spartacus/core`. One exception is providing the `CheckoutCartInterceptor`, which is now done in `CheckoutRootModule` from `@spartacus/checkout/root` instead. -(Note: The new `CheckoutCoreModule` doesn't have the method `forRoot()` - please just import `CheckoutCoreModule` instead.) - -There is still a `CheckoutModule` in `@spartacus/checkout`. While its name is the same as the previous module from `@spartacus/core`, his role is different. It imports other new checkout lib modules: `CheckoutComponentsModule`, `CheckoutCoreModule`, `CheckoutOccModule`. The new module naming changes might seem confusing at first sight, but the choice of the new names were made to align with the feature libs module naming convention in Spartacus. - -## Complete list of symbols (class, interface, const) moved to the checkout lib - -Imports for these symbols must be updated in custom code. - -| Name | Moved From | Moved To | Renamed To -| ------------- | ------------- | ------------- | ------------- | -| CheckoutLoginComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutLoginModule | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationModule | @spartacus/storefront | @spartacus/checkout/components | - | -| ReplenishmentOrderConfirmationModule | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationGuard | @spartacus/storefront | @spartacus/checkout/components | - | -| GuestRegisterFormComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationItemsComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationOverviewComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationThankYouMessageComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| OrderConfirmationTotalsComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutComponentModule | @spartacus/storefront | @spartacus/checkout/components | CheckoutComponentsModule | -| CheckoutOrchestratorComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutOrchestratorModule | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutOrderSummaryComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutOrderSummaryModule | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressModule | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressMobileBottomComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressMobileBottomModule | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressMobileTopComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutProgressMobileTopModule | @spartacus/storefront | @spartacus/checkout/components | - | -| DeliveryModeComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| DeliveryModeModule | @spartacus/storefront | @spartacus/checkout/components | - | -| PaymentMethodComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| PaymentMethodModule | @spartacus/storefront | @spartacus/checkout/components | - | -| PaymentFormComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| PaymentFormModule | @spartacus/storefront | @spartacus/checkout/components | - | -| PlaceOrderComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| PlaceOrderModule | @spartacus/storefront | @spartacus/checkout/components | - | -| ReviewSubmitComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| ReviewSubmitModule | @spartacus/storefront | @spartacus/checkout/components | - | -| ScheduleReplenishmentOrderComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| ScheduleReplenishmentOrderModule | @spartacus/storefront | @spartacus/checkout/components | - | -| CardWithAddress | @spartacus/storefront | @spartacus/checkout/components | - | -| ShippingAddressComponent | @spartacus/storefront | @spartacus/checkout/components | - | -| ShippingAddressModule | @spartacus/storefront | @spartacus/checkout/components | - | -| DeliveryModePreferences | @spartacus/storefront | @spartacus/checkout/root | - | -| CheckoutConfig | @spartacus/storefront | @spartacus/checkout/root | - | -| CheckoutAuthGuard | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutStepsSetGuard | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutGuard | @spartacus/storefront | @spartacus/checkout/components | - | -| NotCheckoutAuthGuard | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutStepType | @spartacus/storefront | @spartacus/checkout/root | - | -| checkoutShippingSteps | @spartacus/storefront | @spartacus/checkout/root | - | -| checkoutPaymentSteps | @spartacus/storefront | @spartacus/checkout/root | - | -| CheckoutStep | @spartacus/storefront | @spartacus/checkout/root | - | -| CheckoutConfigService | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutDetailsService | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutReplenishmentFormService | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutStepService | @spartacus/storefront | @spartacus/checkout/components | - | -| ExpressCheckoutService | @spartacus/storefront | @spartacus/checkout/components | - | -| CheckoutOccModule | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutCostCenterAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutDeliveryAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutPaymentTypeAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutPaymentAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutReplenishmentOrderAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccCheckoutAdapter | @spartacus/core | @spartacus/checkout/occ | - | -| OccReplenishmentOrderFormSerializer | @spartacus/core | @spartacus/checkout/occ | - | -| CheckoutModule | @spartacus/core | @spartacus/checkout/core | CheckoutCoreModule | -| CheckoutAdapter | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutConnector | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutCostCenterAdapter | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutCostCenterConnector | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutDeliveryAdapter | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutDeliveryConnector | @spartacus/core | @spartacus/checkout/core | - | -| DELIVERY_MODE_NORMALIZER | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutPaymentAdapter | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutPaymentConnector | @spartacus/core | @spartacus/checkout/core | - | -| PAYMENT_DETAILS_SERIALIZER | @spartacus/core | @spartacus/checkout/core | - | -| CARD_TYPE_NORMALIZER | @spartacus/core | @spartacus/checkout/core | - | -| PAYMENT_TYPE_NORMALIZER | @spartacus/core | @spartacus/checkout/core | - | -| PaymentTypeAdapter | @spartacus/core | @spartacus/checkout/core | - | -| PaymentTypeConnector | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutReplenishmentOrderAdapter | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutReplenishmentOrderConnector | @spartacus/core | @spartacus/checkout/core | - | -| REPLENISHMENT_ORDER_FORM_SERIALIZER | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutEventBuilder | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutEventModule | @spartacus/core | @spartacus/checkout/core | - | -| OrderPlacedEvent | @spartacus/core | @spartacus/checkout/root | - | -| CheckoutCostCenterService | @spartacus/core | @spartacus/checkout/root | CheckoutCostCenterFacade | -| CheckoutDeliveryService | @spartacus/core | @spartacus/checkout/root | CheckoutDeliveryFacade | -| CheckoutPaymentService | @spartacus/core | @spartacus/checkout/root | CheckoutPaymentFacade | -| CheckoutService | @spartacus/core | @spartacus/checkout/root | CheckoutFacade | -| PaymentTypeService | @spartacus/core | @spartacus/checkout/root | PaymentTypeFacade | -| ClearCheckoutService | @spartacus/core | @spartacus/checkout/root | ClearCheckoutFacade | -| CheckoutDetails | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutPageMetaResolver | @spartacus/core | @spartacus/checkout/core | - | -| CHECKOUT_FEATURE | @spartacus/core | @spartacus/checkout/core | - | -| CHECKOUT_DETAILS | @spartacus/core | @spartacus/checkout/core | - | -| SET_DELIVERY_ADDRESS_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| SET_DELIVERY_MODE_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| SET_SUPPORTED_DELIVERY_MODE_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| SET_PAYMENT_DETAILS_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| GET_PAYMENT_TYPES_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| SET_COST_CENTER_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| PLACED_ORDER_PROCESS_ID | @spartacus/core | @spartacus/checkout/core | - | -| StateWithCheckout | @spartacus/core | @spartacus/checkout/core | - | -| CardTypesState | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutStepsState | @spartacus/core | @spartacus/checkout/core | - | -| PaymentTypesState | @spartacus/core | @spartacus/checkout/core | - | -| OrderTypesState | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutState | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutActions | @spartacus/core | @spartacus/checkout/core | - | -| CheckoutSelectors | @spartacus/core | @spartacus/checkout/core | - | - -## (end) Changes in the classes carried over to the @spartacus/checkout lib - - - -### AddedToCartDialogComponent -- Removed constructor parameter `PromotionService` -- Removed the attribute orderPromotions$ -- The component gets promotions directly from the cart in the html template. -- The `increment` property was removed. Use property `numberOfEntriesBeforeAdd` instead. - - -### CartDetailsComponent -- Removed constructor parameter `PromotionService` -- Removed the attributes `orderPromotions$` and `promotions$`. -- The component gets promotions directly from the cart in the html template. - - - -### CartItemComponent -- Removed constructor parameter `PromotionService` -- The component gets product promotions directly from the cart entry data. -- Removed the `ngOnInit()` method -- Removed the attribute `appliedProductPromotions$` - - -### CartItemListComponent -- removed constructor dependency `FeatureConfigService` -- added new required constructor dependencies `UserIdService` and `MultiCartService` - -### OrderDetailItemsComponent -Removed constructor parameter `PromotionService` -Removed the attribute orderPromotions$ -The component gets promotions directly from the order in the html template. - -### SuggestedAddressDialogComponent - -SuggestedAddressDialogComponent uses different translation label keys: - -- Key `checkoutAddress.verifyYourAddress` is replaced by `addressSuggestion.verifyYourAddress`. -- Key `checkoutAddress.ensureAccuracySuggestChange` is replaced by `addressSuggestion.ensureAccuracySuggestChange`. -- Key `checkoutAddress.chooseAddressToUse` is replaced by `addressSuggestion.chooseAddressToUse`. -- Key `checkoutAddress.suggestedAddress` is replaced by `addressSuggestion.suggestedAddress`. -- Key `checkoutAddress.enteredAddress` is replaced by `addressSuggestion.enteredAddress`. -- Key `checkoutAddress.editAddress` is replaced by `addressSuggestion.editAddress`. -- Key `checkoutAddress.saveAddress` is replaced by `addressSuggestion.saveAddress`. - -### OrderOverviewComponent - -OrderOverviewComponent uses different translation label keys: - -- Key `checkoutOrderConfirmation.replenishmentNumber` is replaced by `orderDetails.replenishmentId`. -- Key `checkoutOrderConfirmation.status` is replaced by `orderDetails.status`. -- Key `checkoutOrderConfirmation.active` is replaced by `orderDetails.active`. -- Key `checkoutOrderConfirmation.cancelled` is replaced by `orderDetails.cancelled`. -- Key `checkoutReview.startOn` is replaced by `orderDetails.startOn`. -- Key `checkoutOrderConfirmation.frequency` is replaced by `orderDetails.frequency`. -- Key `checkoutOrderConfirmation.nextOrderDate` is replaced by `orderDetails.nextOrderDate`. -- Key `checkoutOrderConfirmation.orderNumber` is replaced by `orderDetails.orderNumber`. -- Key `checkoutOrderConfirmation.placedOn` is replaced by `orderDetails.placedOn`. -- Key `checkoutReview.poNumber` is replaced by `orderDetails.purchaseOrderNumber`. -- Key `checkoutPO.noPoNumber` is replaced by `orderDetails.emptyPurchaseOrderId`. -- Key `checkoutProgress.methodOfPayment` is replaced by `orderDetails.methodOfPayment`. -- Key `checkoutPO.costCenter` is replaced by `orderDetails.costCenter`. -- Key `checkoutShipping.shippingMethod` is replaced by `orderDetails.shippingMethod`. -- The `getOrderCurrentDateCardContent` method requires `isoDate` parameter. It is no longer optional. - -### Qualtrics changes - -- `QualtricsConfig` was removed from storefrontlib. Use @spartacus/qualtrics/components instead. -- `QUALTRICS_EVENT_NAME` was removed from storefrontlib. Use @spartacus/qualtrics/components instead. -- `QualtricsLoaderService` was removed from storefrontlib. Use @spartacus/qualtrics/components instead. -- `QualtricsLoaderService` from the feature-lib no longer requires `RendererFactory2`. It has been replaced with `ScriptLoader`. -- `QualtricsComponent` was removed from storefrontlib. Use @spartacus/qualtrics/components instead. -- `QualtricsModule` was removed from storefrontlib, and renamed QualtricsComponentsModule. Use @spartacus/qualtrics/components instead. - -### OccConfigLoaderModule -- `OccConfigLoaderModule` was removed. Please use `SiteContextConfigInitializer` and `I18nConfigInitializer` instead. - -### OccConfigLoaderService -- `OccConfigLoaderService` was removed. Please use `SiteContextConfigInitializer` and `I18nConfigInitializer` instead. - -### OccLoadedConfigConverter -- `OccLoadedConfigConverter` was removed. Please use `SiteContextConfigInitializer` and `I18nConfigInitializer` instead. - -### OccLoadedConfig -- `OccLoadedConfig` was removed. Please use `SiteContextConfigInitializer` and `I18nConfigInitializer` instead. - -### OccSitesConfigLoader -- `OccSitesConfigLoader`was removed. Please use `SiteContextConfigInitializer` and `I18nConfigInitializer` instead. - -### OccEndpoints -The config property `backend.occ.endpoints.baseSitesForConfig` has been removed. Please use `backend.occ.endpoints.baseSites` instead. - -#### NavigationUIComponent - -- Added `HamburgerMenuService` to constructor. - - -### Changes to Styles in 4.0 - -#### Changes in storefrontstyles components - -`$page-template-blacklist` scss variable name has been renamed to `$page-template-blocklist` in `_page-template.scss`. - -`$cart-components-whitelist` scss variable name has been renamed to `$cart-components-allowlist` in `cart/_index.scss`. - -`$cds-components-whitelist` scss variable name has been renamed to `$cds-components-allowlist` in `cds/index.scss`. - -`$store-finder-components-whitelist` scss variable name has been renamed to `$store-finder-components-allowlist` in `store-finder/index.scss`. - -`$checkout-components-whitelist` scss variable name has been renamed to `$checkout-components-allowlist` in `checkout/_index.scss`. - -`$content-components-whitelist` scss variable name has been renamed to `$content-components-allowlist` in `content/_index.scss`. - -`$layout-components-whitelist` scss variable name has been renamed to `$layout-components-allowlist` in `layout/_index.scss`. - -`$misc-components-whitelist` scss variable name has been renamed to `$misc-components-allowlist` in `misc/_index.scss `. - -`$myaccount-components-whitelist` scss variable name has been renamed to `$myaccount-components-allowlist` in `myaccount/_index.scss`. - -`$product-components-whitelist` scss variable name has been renamed to `$product-components-allowlist` in `product/index.scss`. - -`$product-list-whitelist` scss variable name has been renamed to `$product-list-allowlist` in `product/list/_index.scss`. - -`$pwa-components-whitelist` scss variable name has been renamed to `$pwa-components-allowlist` in `pwa/add-to-home-screen-banner/_index.scss`. - -`$user-components-whitelist` scss variable name has been renamed to `$user-components-allowlist` in `user/_index.scss`. - -`$wish-list-components-whitelist` scss variable name has been renamed to `$wish-list-components-allowlist` in `wish-list/index.scss`. - -#### Changes in Organization feature library - -`$page-template-blacklist-organization` scss variable name has been renamed to `$page-template-blocklist-organization`. - -`$page-template-whitelist-organization` scss variable name has been renamed to `$page-template-allowlist-organization`. - -#### Changes in Storefinder feature library - -`$page-template-blacklist-store-finder` scss variable name has been renamed to `$page-template-blocklist-store-finder`. - -`$page-template-whitelist-store-finder` scss variable name has been renamed to `$page-template-allowlist-store-finder`. - -### Removal of grouping modules - -In 4.0 release we removed few modules that were grouping feature modules and provided some default configuration. - -To migrate them check this section: `Before migrating to Spartacus 4.0` - -- removed `B2cStorefrontModule` from `@spartacus/storefront` -- removed `B2bStorefrontModule` from `@spartacus/setup` -- removed `StorefrontModule` from `@spartacus/storefront` -- removed `CmsLibModule` from `@spartacus/storefront` -- removed `MainModule` from `@spartacus/storefront` -- removed `StorefrontFoundationModule` from `@spartacus/storefront` -- removed `OccModule` from `@spartacus/core` -- removed `EventsModule` from `@spartacus/storefront` - -### ViewConfigModule removed - -This module only provided empty default configuration that is not needed. This module was pretty much useless. - -### EventService - -- `EventService` will now register event's parent as an event. For more, see [this docs page](https://sap.github.io/spartacus-docs/event-service/#event-type-inheritance). - -### Changes in product configurator feature library - -#### MessageConfig - -Has been renamed to `ConfiguratorMessageConfig` - -#### ConfiguratorAttributeDropDownComponent - -Method `onSelect` has been removed, it is no longer used. Use `onSelect` from super class which takes the value as argument - -#### ConfiguratorAttributeNumericInputFieldComponent - -Method `createEventFromInput` has been removed, it is no longer used - -#### ConfiguratorAttributeRadioButtonComponent - -Method `onDeselect` has been removed, it is no longer used - -#### ConfiguratorGroupMenuComponent - -Method `preventScrollingOnSpace`, `navigateUpOnEnter` and `clickOnEnter` have been removed, they are no longer used - -#### ConfiguratorProductTitleComponent - -Methods `getProductImageURL`, `getProductImageAlt` and `clickOnEnter` have been removed, they are no longer used - -#### ConfiguratorCartService - -Change: The type of constructor parameter `checkoutService: CheckoutService` is changed to `checkoutFacade: CheckoutFacade` to adapt to the new checkout lib. -The constructor parameter `cartStore: Store` has been removed. - -In addition, `ConfiguratorCartService` now also requires `ConfiguratorUtilsService`. - -#### CommonConfiguratorUtilsService - -Method `getCartId` changes its signature from `getCartId(cart: Cart): string` to `getCartId(cart?: Cart): string` - -#### ConfiguratorGroupsService - -Method `getFirstConflictGroup` changes return parameter from `Configurator.Group` to `Configurator.Group | undefined` -Method `getMenuParentGroup` changes return parameter from `Configurator.Group` to `Configurator.Group | undefined` -Method `getParentGroup` changes return parameter from `Configurator.Group` to `Configurator.Group | undefined` -Method `getNextGroupId` changes return parameter from `Observable` to `Observable` -Method `getPreviousGroupId` changes return parameter from `Observable` to `Observable` -Method `getNeighboringGroupId` changes return parameter from `Observable` to `Observable` - -#### ConfiguratorUtilsService - -Method `getParentGroup` changes return parameter from `Configurator.Group` to `Configurator.Group | undefined` - -#### New and renamed dependencies - -`ConfiguratorCartEntryInfoComponent` now also requires `CommonConfiguratorUtilsService`. -`ConfiguratorAttributeCheckboxListComponent` now also requires `ConfiguratorAttributeQuantityService`. -`ConfiguratorAttributeDropDownComponent` now also requires `ConfiguratorAttributeQuantityService`. -`ConfiguratorAttributeInputFieldComponent` now also requires `ConfiguratorUISettingsConfig`. -`ConfiguratorAttributeNumericInputFieldComponent` now also requires `ConfiguratorUISettingsConfig`. -`ConfiguratorAttributeRadiButtonComponent` now also requires `ConfiguratorAttributeQuantityService`. -`ConfiguratorGroupMenuComponent` now also requires `ConfiguratorGroupMenuService` and `DirectionService`. -`ConfiguratorStorefrontUtilsService` now also requires `WindowRef` and `KeyboardFocusService`. -`ConfiguratorFormComponent` now also requires `ConfiguratorStorefrontUtilsService`. -`ConfiguratorIssuesNotificationComponent` now also requires `CartItemContext`. -`ConfiguratorOverviewAttributeComponent` now also requires `BreakpointService`. -`ConfiguratorUpdateMessageComponent` now requires `ConfiguratorMessageConfig` instead of `MessageConfig`. - -### LoginRegisterComponent - -Lib: @spartacus/user -Class: LoginRegisterComponent - -Change: constructor parameter `checkoutConfigService: CheckoutConfigService` is removed. -The display of the guest checkout button relies on the presence of the `forced` query param only. - -### NgbTabsetModule - -#### StoreFinderComponentsModule - -- Deprecated import `NgbTabsetModule` from `@ng-bootstrap/ng-bootstrap` has been replaced with `NgbNavModule` to support version 8 of the library. - -#### StoreFinderListComponent - -- Mobile template has been updated to use nav instead of tabs. [See changes.](https://github.com/SAP/spartacus/pull/12398/files#diff-1db586698a503ea500917fe4d734f84d0729f585aa7c4b56705d9171a38e7f55L64-L120) -- Added styles for `ul.nav` to keep the same appearance. - -### ASM changes - -- `AsmModule` was removed from storefrontlib, and renamed AsmComponentsModule. Use @spartacus/asm/components instead. -- `AsmModule` was removed from core, and renamed AsmCoreModule. Use @spartacus/asm/core. instead. -- `AsmOccModule` was removed from core. Use @spartacus/asm/occ instead. -- `OccAsmAdapter` was removed from core. Use @spartacus/asm/occ instead. -- `AsmConfig` was removed from core. Use @spartacus/asm/core. -- `AsmAdapter` was removed. Use @spartacus/asm/core instead. -- `AsmConnector` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH_PAGE_NORMALIZER` was removed. Use @spartacus/asm/core instead. -- `AsmService` was removed. Use @spartacus/asm/core instead. -- `CsAgentAuthService` was removed. Use @spartacus/asm/root instead. -- `CustomerSearchPage` was removed. Use @spartacus/asm/core instead. -- `CustomerSearchOptions` was removed. Use @spartacus/asm/core instead. -- `AsmUi` was removed. Use @spartacus/asm/core instead. -- `AsmAuthHttpHeaderService` was removed. Use @spartacus/asm/root instead. -- `TOKEN_TARGET` was removed. Use @spartacus/asm/root instead. -- `AsmAuthService` was removed. Use @spartacus/asm/root instead. -- `AsmAuthStorageService` was removed. Use @spartacus/asm/root instead. -- `SYNCED_ASM_STATE` was removed. Use @spartacus/asm/core instead. -- `AsmStatePersistenceService` was removed. Use @spartacus/asm/core instead. -- `ASM_UI_UPDATE` was removed. Use @spartacus/asm/core instead. -- `AsmUiUpdate` was removed. Use @spartacus/asm/core instead. -- `AsmUiAction` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH_FAIL` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH_SUCCESS` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH_RESET` was removed. Use @spartacus/asm/core instead. -- `CustomerSearch` was removed. Use @spartacus/asm/core instead. -- `CustomerSearchFail` was removed. Use @spartacus/asm/core instead. -- `CustomerSearchSuccess` was removed. Use @spartacus/asm/core instead. -- `CustomerSearchReset` was removed. Use @spartacus/asm/core instead. -- `CustomerAction` was removed. Use @spartacus/asm/core instead. -- `LOGOUT_CUSTOMER_SUPPORT_AGENT` was removed. Use @spartacus/asm/core instead. -- `LogoutCustomerSupportAgent` was removed. Use @spartacus/asm/core instead. -- `ASM_FEATURE` was removed. Use @spartacus/asm/core instead. -- `CUSTOMER_SEARCH_DATA` was removed. Use @spartacus/asm/core instead. -- `StateWithAsm` was removed. Use @spartacus/asm/core instead. -- `AsmState` was removed. Use @spartacus/asm/core instead. -- `getAsmUi` was removed. Use @spartacus/asm/core instead. -- `getCustomerSearchResultsLoaderState` was removed. Use @spartacus/asm/core instead. -- `getCustomerSearchResults` was removed. Use @spartacus/asm/core instead. -- `getCustomerSearchResultsLoading` was removed. Use @spartacus/asm/core instead. -- `getAsmState` was removed. Use @spartacus/asm/core instead. - -### LaunchDialogService - -#### SavedCartFormLaunchDialogService - -- Service has been removed. `openDialog` method is part of LaunchDialogService now. - -#### AddToSavedCartComponent - -- Removed `SavedCartFormLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### SavedCartDetailsActionComponent - -- Removed `SavedCartFormLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### SavedCartDetailsOverviewComponent - -- Removed `SavedCartFormLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### AnonymousConsentLaunchDialogService - -- Service has been removed. `openDialog` method is part of `LaunchDialogService` now. - -#### AnonymousConsentManagementBannerComponent - -- Removed `AnonymousConsentLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### AnonymousConsentOpenDialogComponent - -- Removed `AnonymousConsentLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### ReplenishmentOrderCancellationLaunchDialogService - -- Service has been removed. `openDialog` method is part of LaunchDialogService now. - -#### ReplenishmentOrderCancellationComponent - -- Removed `ReplenishmentOrderCancellationLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. - -#### OrderHistoryComponent - -- `div` which wrapped `cx-sorting` component has been changed to `label` and added `span` before field. - -#### OrderReturnRequestListComponent - -- `div` which wrapped `cx-sorting` component has been changed to `label` and added `span` before field. - -#### ReplenishmentOrderHistoryComponent - -- Removed `ReplenishmentOrderCancellationLaunchDialogService` from constructor. -- Added `LaunchDialogService` to constructor. -- `div` which wrapped `cx-sorting` component has been changed to `label` and added `span` before field. - -#### ProductListComponent - -- Two elements `div` which wrapped `cx-sorting` component has been merged to one `label` and added `span` before field. - -### SmartEdit - -- `SmartEditModule` was removed. Use `@spartacus/smartedit` instead. -- `SmartEditService` was moved to `@spartacus/smartedit/core`. - -### Personalization - -- `PersonalizationModule` was removed. Use `@spartacus/tracking/personalization` instead. -- `PersonalizationConfig` was moved to `@spartacus/tracking/personalization/root`. -- `PersonalizationContextService` was moved to `@spartacus/tracking/personalization/core`. -- `PersonalizationAction` was moved to `@spartacus/tracking/personalization/core`. -- `PersonalizationContext` was moved to `@spartacus/tracking/personalization/core`. - -### WindowRef - -- `platformId` is now required constructor dependency. - -### ProductListComponentService - -- `ProductListComponentService` now also requires `ViewConfig`. -- The `defaultPageSize` property was removed. To modify default page size use `provideConfig({ view: { defaultPageSize: import('@spartacus/product-configurator/rulebased').then( - (m) => m.RulebasedConfiguratorModule - ), - }, - } -``` - -needs to look like that in 4.0 - -``` - featureModules: { - productConfiguratorRulebased: { - module: () => import('@spartacus/product-configurator/rulebased').then( - (m) => m.RulebasedConfiguratorModule - ), - }, - } -``` - -### Translations (i18n) changed - -- Key `asm.standardSessionInProgress` was removed. -- Key `pageMetaResolver.checkout.title_plurar` has been removed. -- Value of `pageMetaResolver.checkout.title` has been changed to `Checkout`. - -### Storage Sync mechanism - -I version 4.0 we removed deprecated in version 3.0 storage sync mechanism. In previous major release we provided more powerful mechanism based on `StatePersistenceService` which can cover all use cases for synchronizing data to and from browser storage (eg. `localStorage`, `sessionStorage`) better than the removed storage sync. - -What was removed: - -- core of the mechanism (reducer) -- configuration (`storageSync` from `StateConfig`) -- default config and default keys (`defaultStateConfig`, `DEFAULT_LOCAL_STORAGE_KEY` and `DEFAULT_SESSION_STORAGE_KEY`) - -### DefaultScrollConfig - -- `defaultScrollConfig` was renamed to `defaultViewConfig` - -### BaseSiteService - -- The method `BaseSiteService.initialize` was removed, please use `BaseSiteServiceInitializer.initialize` instead. A method `BaseSiteService.isInitialized` was added. - -### LanguageService - -- The method `LanguageService.initialize` was removed, please use `LanguageInitializer.initialize` instead. A method `LanguageService.isInitialized` was added. -- `LanguageService` no longer uses `WindowRef`. The language initialization from the state was moved to `LanguageInitializer`. -- `LanguageService` now validate the value passed to the method `setActive()` against the iso codes listed in the Spartacus `context` config, before setting the actual value in the ngrx store. -- The initialization of the site context is scheduled a bit earlier than in before (now it's run in an observable stream instead of a Promise's callback). It's a very slight change, but might have side-effects in some custom implementations. -- The active language is now persisted in the Local Storage instead of the Session Storage - -### CurrencyService - -- The method `CurrencyService.initialize` was removed, please use `CurrencyInitializer.initialize` instead. A method `CurrencyService.isInitialized` was added. -- `CurrencyService` no longer uses `WindowRef`. The currency initialization from the state was moved to `CurrencyInitializer`. -- `CurrencyService` now validate the value passed to the method `setActive()` against the iso codes listed in the Spartacus `context` config, before setting the actual value in the ngrx store. -- The initialization of the site context is scheduled a bit earlier than in before (now it's run in an observable stream instead of a Promise's callback). It's a very slight change, but might have side-effects in some custom implementations. -- The active currency is now persisted in the LocalStorage instead of the Session Storage. - -### Page resolvers - -In 3.1 and 3.2 we've introduced a few changes on how the page meta data is collected. The resolvers are now configurable and whether they render on the client or server (SSR) is also configurable (by default description, image, robots and canonical url are resolved only in SSR). A few resolvers are changed or added, since most data is now configurable in the backend (description, robots). The robot information that was previously hardcoded in some resolvers, are now driven by backend data. - -The feature of resolving the canonical URL has been enabled by default in 4.0. In case you want to opt-out, please change the Spartacus configuration of `pageMeta.resolvers`. - -A new feature has landed in the product and category resolvers for the canonical URL. - -The `BasePageMetaResolver` is leveraged to compose most of the generic page meta data. Most page resolvers now use the page data to create the description and robot tags. The changes have affected the following resolver classes: - -- The `PageMetaService` has a dependency on `UnifiedInjector`, `PageMetaConfig` and the `platformId`. -- The `ContentPageMetaResolver` depends only on the `BasePageMetaResolver`. -- The `BasePageMetaResolver` requires the `Router` and `PageLinkService` to add canonical links to the page meta. -- The `ProductPageMetaResolver` requires the `BasePageMetaResolver` and `PageLinkService`. -- The `CategoryPageMetaResolver` requires the `BasePageMetaResolver`. -- The `SearchPageMetaResolver` requires the `BasePageMetaResolver`. -- The `CheckoutPageMetaResolver` uses the `BasePageMetaResolver`. -- The `OrganizationPageMetaResolver` no longer uses the `BasePageMetaResolver`. -- The `RoutingService` uses the `Router` to resolve the full URL (used to resolve the canonical URL in the page meta resolvers) - -The `CmsPageTitleModule` is renamed to `PageMetaModule`. -The `CartPageMetaResolver` is removed since all content is resolved by the generic `ContentPageMetaResolver`. - -The following properties where removed in 4.0: - -- The `ContentPageMetaResolver` no longer supports the `homeBreadcrumb$`,`breadcrumb$`,`title$` and `cms$` as the content is resolved by the `BasePageMetaResolver`. -- The `resolverMethods` property on the `PageMetaService` has changed to `resolvers$` since the resolvers are read from the configuration stream. - -### SelectiveCartService - -- The `getLoaded` method was removed. Use `isStable` method instead. - -### DynamicAttributeService - -- `DynamicAttributeService` doesn't depend anymore on the `SmartEditService`, but only on the `UnifiedInjector`. -- The method `addDynamicAttributes` was removed. Please use functions `addAttributesToComponent` or `addAttributesToSlot` instead. - -### SearchBoxComponentService - -- `SearchBoxComponentService` now also requires `EventService`. - -### CartListItemComponent - -- Removed `FeatureConfigService` from constructor and added `UserIdService` and `MultiCartService` to constructor. -- Property `form: FormGroup` is now initialized on component creation instead of in the `createForm()` method. -- `ngOnInit()` method has been modified to fix an issue with rendering items. -- `[class.is-changed]` template attribute on `
` tag now depends on method `control.get('quantity').disabled`. - -### Models - -- The `Item` interface was removed. Use `OrderEntry` instead. -- `sortCode` was removed from interface `TableHeader`. - -### SearchBoxComponent - -- `RoutingService` is a new, required constructor dependency. -- `cx-icon[type.iconTypes.RESET]` has been changed to `button>cx-icon[type.iconTypes.Reset]` -- `cx-icon[type.iconstypes.SEARCH]` has been changed to `div>cx-icon[type.iconstypes.SEARCH]` with rest of the attributes removed. The button is for presentation purpose only. -- `div.suggestions` has been changed to `ul>li>a` for better a11y support -- `div.products` has been changed to `ul>li>a` for better a11y support -- `winRef.document.querySelectorAll('.products > a, .suggestions > a')` has been changed to `winRef.document.querySelectorAll('.products > li a, .suggestions > li a')` - -### Organization Administration breaking changes - -#### CardComponent (Administration) - -- In attribute `cxPopoverOptions` of element `button.hint-popover` property `displayCloseButton` has been set to `true`. - -#### ListComponent - -- In attribute `cxPopoverOptions` of element `button.hint-popover` property `displayCloseButton` has been set to `true`. -- `ng-select` for sort has been wrapped by `label` and added `span` before. - -#### UserGroupUserListComponent - -- Removed `MessageService` from constructor. - -#### ToggleStatusComponent - -- Removed `FeatureConfigService` from constructor. -- Added `DisableInfoService` to constructor. - -#### DeleteItemComponent - -- Removed `FeatureConfigService` from constructor. - -#### UnitChildrenComponent - -- `CurrentUnitService` is now required parameter in component constructor. - -#### UnitCostCenterListComponent - -- `CurrentUnitService` is now required parameter in component constructor. - -#### UnitUserListComponent - -- `CurrentUnitService` is now required parameter in component constructor. - -#### UnitFormComponent - -- Renamed property `formGroup` to `form`. -- Removed property `form$`. - -#### OrganizationTableType - -- Removed unused `UNIT_ASSIGNED_ROLES` property from enum. - -#### HttpErrorModel - -- Removed `error` property from interface. - -#### Organization related Translations (i18n) changes - -- Change contents of: - `orgBudget.messages.deactivate`, - `orgCostCenter.messages.deactivate`, - `orgPurchaseLimit.messages.deactivate`, - `orgUnit.messages.deactivate`, - `orgUnitAddress.messages.delete`, - `orgUserGroup.messages.delete`, - `orgUser.messages.deactivate` -- Removed unused keys: - `orgBudget.messages.deactivateBody`, - `orgBudget.byName`, - `orgBudget.byUnitName`, - `orgBudget.byCode`, - `orgBudget.byValue`, - `orgCostCenter.messages.deactivateBody`, - `orgCostCenter.byName`, - `orgCostCenter.byCode`, - `orgCostCenter.byUnitName`, - `orgPurchaseLimit.messages.deactivateBody`, - `orgPurchaseLimit.byName`, - `orgPurchaseLimit.byUnitName`, - `orgUnit.messages.deactivateBody`, - `orgUnitAddress.messages.deleteBody`, - `orgUserGroup.messages.deleteBody`, - `orgUserGroup.byName`, - `orgUserGroup.byUnitName`, - `orgUserGroup.byGroupID`, - `orgUser.messages.deactivateBody`, - `orgUser.byName`, - `orgUser.byUnitName`, - -### Dependencies changes - -- The peer dependency package `i18next-xhr-backend` was replaced with `i18next-http-backend`. -- The peer dependency package `i18next` was upgraded to the version `20.2.2` - -### CmsFeaturesService - -- `CmsComponentsService` constructor is now using `CmsFeaturesService` (replacing `FeatureModulesService`) and `ConfigInitializerService`. -- `FeatureModulesService` was removed. Was replaced by `CmsFeaturesService`. - -### ConfigInitializerService - -- `getStableConfig` method was removed. Use the new equivalent method instead: `getStable`. - -### CartItemComponent - -`CartItemComponent` now also requires `CartItemContextSource`. Moreover, a customized version of this component now also should provide locally `CartItemContextSource` and `CartItemContext` in the following way: -```typescript -@Component({ - providers: [ - CartItemContextSource, - { provide: CartItemContext, useExisting: CartItemContextSource }, - ], - /* ... */ -}) -``` - -### ProductListItemComponent and ProductGridItemComponent -`ProductListItemComponent` and `ProductGridItemComponent` now require `ProductListItemContextSource`. Moreover, customized versions of those components now also should provide locally `ProductListItemContextSource` and `ProductListItemContext` in the following way: -```typescript -@Component({ - providers: [ - ProductListItemContextSource, - { provide: ProductListItemContext, useExisting: ProductListItemContextSource }, - ], - /* ... */ -}) -``` - - -### CartItemContext and CartItemContextSource -- the property `promotionLocation$` has been removed, please use `location$` instead - - -### User lib changes - -#### CMS Components - -- Following modules `CloseAccountModule`, `ForgotPasswordModule`, `RegisterComponentModule`, `ResetPasswordModule`, `UpdateEmailModule`, `UpdatePasswordModule`, `UpdateProfileModule` were moved to `@spartacus/user/profile/components`. -- Following modules `LoginModule`, `LoginFormModule`, `LoginRegisterModule` were moved to `@spartacus/user/account/components`. -- Component `ResetPasswordFormComponent` was renamed to `ResetPasswordComponent` and now can be used from `@spartacus/user/profile/components`. Also logic for this component was changed. For details look on sections below. -- Component `UpdateEmailFormComponent` was removed. For replacement `UpdateEmailComponent` from `@spartacus/user/profile/components` can be used. -- Component `UpdatePasswordFormComponent` was removed. For replacement `UpdatePasswordComponent` from `@spartacus/user/profile/components` can be used. -- Component `UpdateProfileFormComponent` was removed. For replacement `UpdateProfileComponent` from `@spartacus/user/profile/components` can be used. -- Components `CloseAccountComponent`, `CloseAccountModalComponent`, `ForgotPasswordComponent`, `RegisterComponent`, `UpdateEmailComponent`, `UpdatePasswordComponent`, `UpdateProfileComponent` were moved to `@spartacus/user/profile/components`. Logic for those components was changed. For details look on sections below. -- Components `LoginComponent`, `LoginFormComponent` were moved to `@spartacus/user/account/components`. Logic for those components was changed. For details look on sections below. -- Component `LoginRegisterComponent` were moved to `@spartacus/user/account/components`. - -#### CloseAccountModalComponent - -- All services used in constructor have been changed to be `protected`. -- Component is not using `UserService` anymore, `UserProfileFacade` was introduced. -- Component is no longer using `Subscription` property, also `ngOnDestroy` method was removed. - -#### ForgotPasswordComponent - -- Property `forgotPasswordForm` was renamed to `form`. -- New observable property `isUpdating$` was added. -- Methods `ngOnInit`, `requestForgotPasswordEmail` were removed. New method `onSubmit` was added. -- Services `FormBuilder`, `UserService`, `RoutingService`, `AuthConfigService` are no longer used directly in component file. New service `ForgotPasswordComponentService` was introduced and used in constructor. -- Change detection strategy for this component was set to `OnPush`. -- There were slight changes in component template. Spinner component was added which relys on `isUpdating$` property, also form now is using `onSubmit` method on form submit event. - -#### RegisterComponent - -- Component is not using `UserService` anymore, `UserRegisterFacade` was introduced. -- Property `loading$` was changed to `isLoading$` and type was change to `BehaviorSubject` instead of `Observable`. -- Method `registerUserProcessInit` was removed. - -#### ResetPasswordComponent (previously ResetPasswordFormComponent) - -- Property `subscription` was removed. -- Type of `token` property was changed from `string` to `Observable`. -- Property `resetPasswordForm` was renamed to `form`. -- New observable property `isUpdating$` was added. -- Methods `ngOnInit`, `resetPassword`, `ngOnDestroy` were removed. New method `onSubmit` was added. -- Services `FormBuilder`, `UserService`, `RoutingService`, are no longer used directly in component file. New service `ResetPasswordComponentService` was introduced and used in constructor. -- Change detection strategy for this component was set to `OnPush`. -- Component template was adapted to the new logic. -- Spinner component was added which relys on `isUpdating$` property. - -#### LoginComponent - -- Component is not using `UserService` anymore, `UserAccountFacade` was introduced. -- Type of `user` property was changed to `Observable` instead of `Observable`. - -#### LoginFormComponent - -- Services `AuthService`, `GlobalMessageService`, `FormBuilder`, `WindowRef` are no longer used directly in component file. New service `LoginFormComponentService` was introduced and used in constructor. -- Methods `ngOnInit`, `submitForm`, `loginUser` were removed from component file. -- New properties `form`, `isUpdating$` were added. -- New method `onSubmit` was added. -- Change detection strategy for this component was set to `OnPush`. -- Spinner component was added to the template which relys on `isUpdating$` property. - -#### UpdateEmailComponent - -- Properties `subscription`, `newUid`, `isLoading$` were removed. -- Methods `ngOnInit`, `onCancel`, `onSuccess`, `ngOnDestroy` were removed from component file. -- Services `GlobalMessageService`, `UserService`, `RoutingService`, `AuthService` are no longer used directly in component file. New service `UpdateEmailComponentService` was introduced and used in constructor. -- Logic for `onSubmit` method was changed. Now this method has no parameters. -- New properties `form`, `isUpdating$` were added. -- There were important change in component template. Since `UpdateEmailFormComponent` was removed `UpdateEmailComponent` contains now the template for update email form itself. -- Change detection strategy for this component was set to `OnPush`. -- Spinner component was added to the template which relays on `isUpdating$` property. - -#### UpdateEmailComponentService - -- `UpdateEmailComponentService` now also requires `AuthRedirectService` - -#### UpdatePasswordComponent - -- Properties `subscription`, `loading$` were removed. -- Methods `ngOnInit`, `onCancel`, `onSuccess`, `ngOnDestroy` were removed from component file. -- Services `RoutingService`, `UserService`, `GlobalMessageService`, are no longer used directly in component file. New service `UpdatePasswordComponentService` was introduced and used in constructor. -- Logic for `onSubmit` method was changed. Now this method has no parameters. -- New properties `form`, `isUpdating$` were added. -- There were important change in component template. Since `UpdatePasswordFormComponent` was removed `UpdatePasswordComponent` contains now the template for update password form itself. -- Change detection strategy for this component was set to `OnPush`. -- Spinner component was added to the template which relys on `isUpdating$` property. - -#### UpdateProfileComponent - -- Properties `user$`, `loading$` were removed. -- Methods `ngOnInit`, `onCancel`, `onSuccess`, `ngOnDestroy` were removed from component file. -- Services `RoutingService`, `UserService`, `GlobalMessageService`, are no longer used directly in component file. New service `UpdateProfileComponentService` was introduced and used in constructor. -- Logic for `onSubmit` method was changed. Now this method has no parameters. -- New properties `form`, `isUpdating$` were added. -- There were important change in component template. Since `UpdateProfileFormComponent` was removed `UpdateProfileComponent` contains now the template for update profile form itself. -- Change detection strategy for this component was set to `OnPush`. -- Spinner component was added to the template which relys on `isUpdating$` property. - -### MyCouponsComponent - -- `div` which wrapped `cx-sorting` component has been changed to `label` and added `span` before. -#### OccUserAdapter - -- `OccUserAdapter` was removed. Instead please use `OccUserAccountAdapter` from `@spartacus/user/account/occ` and `OccUserProfileAdapter` from `@spartacus/user/profile/occ`. -- The `remove` method was removed. Use `close` method instead. - -#### UserAdapter - -- `UserAdapter` was removed. Instead please use `UserAccountAdapter` from `@spartacus/user/account/core` and `UserProfileAdapter` from `@spartacus/user/profile/core`. -- The `remove` method was removed. Use `close` method instead. - -#### UserConnector - -- `UserConnector` was removed. Instead please use equivalents: `UserAccountConnector` from `@spartacus/user/account/core` and `UserProfileConnector` from `@spartacus/user/profile/core` . -- The `remove` method now returns `close` method from adapter (name change). - -#### OccEndpoints - -- Endpoint `user` was removed from the declaration in `@spartacus/core`. It's now provided with module augmentation from `@spartacus/user/account/occ`. Default value is also provided from this new entry point. -- Endpoints `titles`, `userRegister`, `userForgotPassword`, `userResetPassword`, `userUpdateLoginId`, `userUpdatePassword`, `userUpdateProfile`, `userCloseAccount` were removed from the declaration in `@spartacus/core`. Those endpoints are now provided with module augmentation from `@spartacus/user/profile`. Default values are also provided from this new entry point. - -#### UserService - -- `get` method was changed, now fully relys on `UserAccountFacade.get()` from `@spartacus/user`. -- `load` method was removed, instead please use `UserAccountFacade.get()` from `@spartacus/user`. -- `register` method was removed, instead please use `UserRegisterFacade.register()` from `@spartacus/user`. -- `registerGuest` method was removed, instead please use `UserRegisterFacade.registerGuest()` from `@spartacus/user`. -- `getRegisterUserResultLoading` method was removed, instead please subscribe to `UserRegisterFacade.register()` from `@spartacus/user` to get the loading state. -- `getRegisterUserResultSuccess` method was removed, instead please subscribe to `UserRegisterFacade.register()` from `@spartacus/user` to get the success state. -- `getRegisterUserResultError` method was removed, instead please subscribe to `UserRegisterFacade.register()` from `@spartacus/user` to get the error state. -- `resetRegisterUserProcessState` method was removed and no longer needed if `UserRegisterFacade.register()`from `@spartacus/user` was used. -- `remove` method was removed, instead please use `UserProfileFacade.close()` from `@spartacus/user`. -- `loadTitles` method was removed, instead please use `UserProfileFacade.getTitles()` from `@spartacus/user`. -- `getRemoveUserResultLoading` method was removed, instead please subscribe to `UserProfileFacade.close()` from `@spartacus/user` to get the loading state. -- `getRemoveUserResultSuccess` method was removed, instead please subscribe to `UserProfileFacade.close()` from `@spartacus/user` to get the success state. -- `getRemoveUserResultError` method was removed, instead please subscribe to `UserProfileFacade.close()` from `@spartacus/user` to get the error state. -- `resetRemoveUserProcessState` method was removed and no longer needed if `UserProfileFacade.close()`from `@spartacus/user` was used. -- `isPasswordReset` method was removed, instead please subscribe to `UserPasswordFacade.reset()` from `@spartacus/user` to get the success state. -- `updatePersonalDetails` method was removed, instead please use `UserProfileFacade.update()` from `@spartacus/user`. -- `getUpdatePersonalDetailsResultLoading` method was removed, instead please subscribe to `UserProfileFacade.update()` from `@spartacus/user`to get the loading state. -- `getUpdatePersonalDetailsResultError` method was removed, instead please subscribe to `UserProfileFacade.update()` from `@spartacus/user`to get the error state. -- `getUpdatePersonalDetailsResultSuccess` method was removed, instead please subscribe to `UserProfileFacade.update()` from `@spartacus/user`to get the success state. -- `resetUpdatePersonalDetailsProcessingState` method was removed and no longer needed if `UserProfileFacade.update()` from `@spartacus/user` was used. -- `resetPassword` method was removed, instead please use `UserPasswordFacade.reset()` from `@spartacus/user`. -- `requestForgotPasswordEmail` method was removed, instead please use `UserPasswordFacade.requestForgotPasswordEmail()` from `@spartacus/user`. -- `updateEmail` method was removed, instead please use `UserEmailFacade.update()` from `@spartacus/user`. -- `getUpdateEmailResultLoading` method was removed, instead please subscribe to `UserEmailFacade.update()` from `@spartacus/user`to get the loading state. -- `getUpdateEmailResultSuccess` method was removed, instead please subscribe to `UserEmailFacade.update()` from `@spartacus/user`to get the success state. -- `getUpdateEmailResultError` method was removed, instead please subscribe to `UserEmailFacade.update()` from `@spartacus/user`to get the error state. -- `resetUpdateEmailResultState` method was removed and no longer needed if `UserEmailFacade.update()` from `@spartacus/user` was used. -- `updatePassword` method was removed, instead please use `UserPasswordFacade.update()` from `@spartacus/user`. -- `getUpdatePasswordResultLoading` method was removed, instead please subscribe to `UserPasswordFacade.update()` from `@spartacus/user`to get the loading state. -- `getUpdatePasswordResultError` method was removed, instead please subscribe to `UserPasswordFacade.update()` from `@spartacus/user`to get the error state. -- `getUpdatePasswordResultSuccess` method was removed, instead please subscribe to `UserPasswordFacade.update()` from `@spartacus/user`to get the success state. -- `resetUpdatePasswordProcessState` method was removed and no longer needed if `UserPasswordFacade.update()` from `@spartacus/user` was used. - -#### UserModule - -- `UserModule` was removed. Main modules currently are `UserAccountModule` in `@spartacus/user/account` and `UserProfileModule` in `@spartacus/user/profile`. - -#### Occ Endpoint Models - -- `UserSignUp` model was moved to `@spartacus/user/profile` lib. - -#### Ngrx state of the User feature - -Some branches of the ngrx state for the User feature were removed: `'account'`, `'titles'`, and `'resetPassword'`. Please use the new approach with Queries and Commands defined in the new facades in the library `@spartacus/user`: -`UserAccountFacade` from '@spartacus/user/account/root' -`UserEmailFacade` from '@spartacus/user/profile/root' -`UserPasswordFacade` from '@spartacus/user/profile/root' -`UserProfileFacade` from '@spartacus/user/profile/root' -`UserRegisterFacade` from '@spartacus/user/profile/root' - -The following items related to the ngrx state were removed from `@spartacus/core`: -- Following actions `ForgotPasswordEmailRequest`, `ForgotPasswordEmailRequestFail`, `ForgotPasswordEmailRequestSuccess`, `ResetPassword`, `ResetPasswordFail`, `ResetPasswordSuccess`, `LoadTitles`, `LoadTitlesFail`, `LoadTitlesSuccess`, `UpdateEmailAction`, `UpdateEmailSuccessAction`, `UpdateEmailErrorAction`, `ResetUpdateEmailAction`, `UpdatePassword`, `UpdatePasswordFail`, `UpdatePasswordSuccess`, `UpdatePasswordReset`, `LoadUserDetails`, `LoadUserDetailsFail`, `LoadUserDetailsSuccess`, `UpdateUserDetails`, `UpdateUserDetailsFail`, `UpdateUserDetailsSuccess`, `ResetUpdateUserDetails`, `RegisterUser`, `RegisterUserFail`, `RegisterUserSuccess`, `ResetRegisterUserProcess`, `RegisterGuest`, `RegisterGuestFail`, `RegisterGuestSuccess`, `RemoveUser`, `RemoveUserFail`, `RemoveUserSuccess`, `RemoveUserReset` were removed. -- Following effects `ForgotPasswordEffects`, `ResetPasswordEffects`, `TitlesEffects`, `UpdateEmailEffects`, `UpdatePasswordEffects`, `UserDetailsEffects`, `UserRegisterEffects` were removed. -- Following selectors `getResetPassword`, `getDetailsState`, `getDetails`, `getTitlesState`, `getTitlesEntites`, `getAllTitles`, `titleSelectorFactory` were removed. -- Reducers for following states `account`, `titles`, `resetPassword` were removed. - -#### Connectors - -- `TITLE_NORMALIZER` was moved to `@spartacus/user/profile`. -- `USER_SIGN_UP_SERIALIZER` was moved to `@spartacus/user/profile`. -- `USER_SERIALIZER` was removed. For replacement please use `USER_ACCOUNT_SERIALIZER` from `@spartacus/user/account` and `USER_PROFILE_SERIALIZER` from `@spartacus/user/profile`. -- `USER_NORMALIZER` was removed. For replacement please use `USER_ACCOUNT_NORMALIZER` from `@spartacus/user/account` and `USER_PROFILE_NORMALIZER` from `@spartacus/user/profile`. - -#### StoreFinderListItemComponent - -- `div[class.cx-store-name]` element has been changed to `h2[class.cx-store-name]`. - -#### StoreFinderStoresCountComponent - -- `div[class.cx-title]` element has been changed to `h2[class.cx-title]` - -#### StoreFinderListItemComponent - -- `div[class.cx-total]` element has been changed to `h4[class.cx-total]` - -#### CartItemComponent - -- `{{item.product.name}}` element has been changed to `

{{item.product.name}}

` - -#### OrderSummaryComponent - -- `

{{ 'orderCost.orderSummary' | cxTranslate }}

` element has changed to `

{{ 'orderCost.orderSummary' | cxTranslate }}

` - -#### DeliveryModeComponent - -- `h3[class.cx-checkout-title]` element has changed to `h2[class.cx-checkout-title]` - -#### PaymentMethodComponent - -- `h3[class.cx-checkout-title]` element has changed to `h2[class.cx-checkout-title]` - -#### ReviewSubmitComponent - -- `h3[class.cx-review-title]` element changed to `h2[class.cx-review-title]` -- `div[class.cx-review-cart-total]` element changed to `h4[class.cx-review-cart-total]` - -#### ShippingAddressComponent - -- `h3[class.cx-checkout-title]` element changed to `h2[class.cx-checkout-title]` - -#### CmsPageTitleComponent - -- New interface has been created - -#### CmsBreadcrumbsComponent -- `CmsBreadcrumbsComponent` extends `CmsPageTitleComponent` now -- `container` property has been moved to `CmsPageTitleComponent` - -#### BreadcrumbComponent - -- `BreadcrumbComponent` extends `PageTitleComponent` now -- `setTitle()` function has been moved to `PageTitleComponent` - -#### PageTitleComponent - -- New component that sets page title if there is not one set by default - -#### NavigationUiComponent - -- `
` element was changed to `` -- `h5[attr.aria-label]="node.title"` element was was changed to `span[attr.aria-label]="node.title"` - -#### ProductCarouselComponent - -- `

{{item.name}}

` element changed to `

{{item.name}}

` - -#### WishListItemComponent - -- `{{ cartEntry.product.name }}` element changed to `

{{ cartEntry.product.name }}

` - -#### CardComponent - -- `h4[class.cx-card-title]` element changed to `h3[class.cx-card-title]` - -#### CarouselComponent - -- `

{{ title }}

` element changed to `

{{ title }}

` - -#### AddedToCartDialogComponent - -- `[attr.aria-label]="'common.close' | cxTranslate"` element changed to `attr.aria-label="{{ 'addToCart.closeModal' | cxTranslate }}"` - -#### Translations (i18n) changes - -- Key `miniLogin.userGreeting` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `miniLogin.signInRegister` was moved to separated `@spartacus/user` lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.signIn` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.register` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.dontHaveAccount` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.guestCheckout` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.emailAddress.label` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.emailAddress.placeholder` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.password.label` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.password.placeholder` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.wrongEmailFormat` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `loginForm.forgotPassword` was moved to separated lib. Now is a part of `userAccountTranslations`, `userAccountTranslationChunksConfig` from `@spartacus/user/account/assets`. -- Key `updateEmailForm.newEmailAddress.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.newEmailAddress.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.confirmNewEmailAddress.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.confirmNewEmailAddress.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.enterValidEmail` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.bothEmailMustMatch` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.password.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.password.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.pleaseInputPassword` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `updateEmailForm.emailUpdateSuccess` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.resetPassword` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.enterEmailAddressAssociatedWithYourAccount` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.emailAddress.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.emailAddress.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.enterValidEmail` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.passwordResetEmailSent` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `forgottenPassword.passwordResetSuccess` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.confirmPassword.action` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.confirmPassword.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.confirmPassword.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.managementInMyAccount` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.termsAndConditions` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.signIn` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.register` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.confirmNewPassword` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.resetPassword` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.createAccount` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.title` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.firstName.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.firstName.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.lastName.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.lastName.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.emailAddress.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.emailAddress.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.password.label` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.password.placeholder` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.newPassword` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.emailMarketing` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.confirmThatRead` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.selectTitle` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.passwordMinRequirements` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.bothPasswordMustMatch` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.titleRequired` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `register.postRegisterMessage` was moved to separated lib. Now is a part of `userProfileTranslations`, `userProfileTranslationChunksConfig` from `@spartacus/user/profile/assets`. -- Key `myCoupons.sortByMostRecent` has been removed. We use `myCoupons.sortBy` now. -- Key `myInterests.sortByMostRecent` has been removed. We use `myInterests.sortBy` now. -- Key `orderHistory.sortByMostRecent` has been removed. We use `orderHistory.sortBy` now. -- Key `returnRequestList.sortByMostRecent` has been removed. We use `returnRequestList.sortBy` now. -- Key `productList.sortByMostRecent` has been removed. We use `productList.sortBy` now. - -### Default routing config for the My Company feature -The default routing config for the My Company pages was moved from various modules in `@spartacus/organization/administration/components` to `OrganizationAdministrationRootModule` in `@spartacus/organization/administration/root`. Therefore the default config is now provided eagerly, so it's available early on app start (but not only after the feature is lazy loaded). Also, the following objects were renamed and moved to `@spartacus/organization/administration/root`: -- `budgetRoutingConfig` -> `defaultBudgetRoutingConfig` -- `costCenterRoutingConfig` -> `defaultCostCenterRoutingConfig` -- `permissionRoutingConfig` -> `defaultPermissionRoutingConfig` -- `unitsRoutingConfig` -> `defaultUnitsRoutingConfig` -- `userGroupRoutingConfig` -> `defaultUserGroupRoutingConfig` -- `userRoutingConfig` -> `defaultUserRoutingConfig` - -#### Checkout Related Translations (i18n) changes - -The checkout related translation keys were moved to the `@spartacus/checkout/assets`. If you use some checkout -related labels outside of checkout and the checkout lib is not used, you will need to use alternate translation labels. - -- Key `checkoutAddress.verifyYourAddress` is moved to `addressSuggestion.verifyYourAddress`. -- Key `checkoutAddress.ensureAccuracySuggestChange` is moved to `addressSuggestion.ensureAccuracySuggestChange`. -- Key `checkoutAddress.chooseAddressToUse` is moved to `addressSuggestion.chooseAddressToUse`. -- Key `checkoutAddress.suggestedAddress` is moved to `addressSuggestion.suggestedAddress`. -- Key `checkoutAddress.enteredAddress` is moved to `addressSuggestion.enteredAddress`. -- Key `checkoutAddress.editAddress` is moved to `addressSuggestion.editAddress`. -- Key `checkoutAddress.saveAddress` is moved to `addressSuggestion.saveAddress`. - -- Key `checkoutOrderConfirmation.replenishmentNumber` is removed. You can use instead ``. -- Key `checkoutOrderConfirmation.placedOn` is removed. You can use instead `orderDetails.placedOn`. -- Key `checkoutOrderConfirmation.status` is removed. You can use instead `orderDetails.status`. -- Key `checkoutOrderConfirmation.active` is removed. You can use instead `orderDetails.active`. -- Key `checkoutOrderConfirmation.cancelled` is removed. You can use instead `orderDetails.cancelled`. -- Key `checkoutOrderConfirmation.frequency` is removed. You can use instead `orderDetails.frequency`. -- Key `checkoutOrderConfirmation.nextOrderDate` is removed. You can use instead `orderDetails.nextOrderDate`. -- Key `checkoutOrderConfirmation.orderNumber` is removed. You can use instead `orderDetails.orderNumber`. - -#### Changes in styles - -- Styles for following selectors `cx-close-account-modal`, `cx-close-account`, `cx-forgot-password`, `cx-register`, `cx-update-password-form`, `cx-user-form` were moved from `@spartacus/styles` to `@spartacus/user/profile/styles`. -- Styles for following selectors `cx-login`, `cx-login-form` were moved from `@spartacus/styles` to `@spartacus/user/account/styles`. - -### LogoutGuard - -`AuthRedirectService` is a new, required constructor dependency. - -### RoutingService -- `RoutingService.go` - changed signature. Before 4.0, the object with query params could be passed in the 2nd argument. Now the 2nd argument is Angular `NavigationExtras` object (with `'queryParams'` property). -- `RoutingService.navigate` - changed signature. Before 4.0, the object with query params could be passed in the 2nd argument. Now the 2nd argument is Angular `NavigationExtras` object (with `'queryParams'` property). - -### RoutingActions ngrx -The following ngrx actions have been removed: -- `RoutingActions.RouteGo` (and `RoutingActions.ROUTER_GO`) -- `RoutingActions.RouteGoByUrlAction` (and `RoutingActions.ROUTER_GO_BY_URL`) -- `RoutingActions.RouteBackAction` (and `RoutingActions.ROUTER_BACK`) -- `RoutingActions.RouteForwardAction` (and `RoutingActions.ROUTER_FORWARD`). - -Please use instead the methods of the `RoutingService`, respectively: `go()`, `goByUrl()`, `back()`, `forward()`. - -### AuthRedirectService - -- `#reportNotAuthGuard` - method not needed anymore. Every visited URL is now remembered automatically as redirect URL on `NavigationEnd` event. -- `#reportAuthGuard` - method deprecated; use the new equivalent method instead: `#saveCurrentNavigationUrl`. It remembers the anticipated page, when being invoked during the navigation. - -### OccEndpointsService - -- The `getEndpoint` method was removed. Use `buildUrl` instead with the `endpoint` string and the `propertiesToOmit` matching your desired URL. -- The `getOccEndpoint` method was removed. Use `buildUrl` instead with the `endpoint` string and the `propertiesToOmit` matching your desired URL. -- The `getBaseEndpoint` method was removed. Use `buildUrl` method instead with configurable endpoint or the `getBaseUrl` method. -- The `getUrl` method was removed. Use `buildUrl` method instead. The `buildUrl` method has the same first parameter as `getUrl`. The 2nd, 3rd and 4th parameters of `getUrl`, are merged into the second argument object of `buildUrl` with properties: `urlParams`, `queryParams` and `scope`. -- The `getRawEndpoint` method was removed. Use `buildUrl` with configurable endpoints or `getRawEndpointValue` method instead. - -### Modal Service - -- Removed `FeatureConfigService` from constructor. -- `ApplicationRef` is a new, required constructor dependency. - -### ExternalJsFileLoader - -- The service was removed from core. Please use `ScriptLoader` instead. - -### CxApi -Removed public members of `CxApi`: `CheckoutService`, `CheckoutDeliveryService`, `CheckoutPaymentService`. - -### TabParagraphContainerComponent - -- `WindowRef` and `BreakpointService` are now required parameters in the constructor. -- All services used in constructor have been changed to be `protected`. - -### b2cLayoutConfig - -- `b2cLayoutConfig` was removed from @spartacus/storefront, please use corresponding feature-lib specific layout. - -### UnitAddressFormService - -- Param `UserService` imported from `@spartacus/core` was removed from constructor. Instead new param `UserAddressService` from `@spartacus/user/profile/root` was added. - -### GuestRegisterFormComponent - -- Param `UserService` imported from `@spartacus/core` was removed from constructor. Instead new param `UserRegisterFacade` from `@spartacus/user/profile/root` was added. - -### UserIdService - -- `invokeWithUserId` method was removed. Use `takeUserId` instead. - -### PopoverDirective - -- `PopoverDirective` class now implements `ngOnInit`. -- Removed `PositioningService` from constructor. -- Event emitters `openPopover` and `closePopover` are no longer optional. -- New property: `eventSubject: Subject`. -- Removed methods: `handleOpen`, `toggle`. -- Added new methods: `handleEscape`, `handleClick`, `handlePress`, `handleTab`. - -### PopoverService - -- Argument `event` of `getFocusConfig` method is now type `PopoverEvent` instead of `Event` - -### PopoverComponent - -- Removed property`insideClicked`. -- `button.close` has been moved into a `div.cx-close-row` - -### OnNavigateFocusService - -- Removed `document` injection param from constructor. -- Added `WindowRef` param into constructor. - -### Facade factories are now inlined - -Facade factories are now inlined into `@Injectable` decorator, the following symbols are not needed anymore and were removed: `userAccountFacadeFactory`, `UserEmailFacadeFactory`, `UserPasswordFacadeFactory`, `UserProfileFacadeFactory`, `UserRegisterFacadeFactory`, `savedCartFacadeFactory`, `cdcAuthFacadeFactory`. diff --git a/docs/migration/5_0-checkout.md b/docs/migration/5_0-checkout.md deleted file mode 100644 index 88a07541dd9..00000000000 --- a/docs/migration/5_0-checkout.md +++ /dev/null @@ -1,35 +0,0 @@ -## Checkout - -Checkout refactoring + code splitting was done as part of the initiative of removing ngrx dependencies from Spartacus by replacing it with [commands and queries](https://sap.github.io/spartacus-docs/commands-and-queries/#page-title). - -As we want to reduce the bundle size in Spartacus, the checkout refactoring also consisted of decoupling the checkout logic into three different entry points: - -- `@spartacus/checkout/base` - offers a basic checkout functionality, such as b2c. -- `@spartacus/checkout/b2b` - offers a b2b checkout flow. -- `@spartacus/checkout/scheduled-replenishment` - offers a scheduled replenishment checkout flow. - -Note: Although the checkout libraries might still have some reference to some ngrx actions within the library, it is not possible to fully separate ngrx from checkout until the other libraries from Spartacus get refactored, such as Cart or User libraries. - -Benefits of converting to `command and queries`: - - as all functionality is in classes, it is easier to extend, as opposed to ngrx where you can not really extend a reducer or an effect (without dismantling Spartacus' ngrx modules) - - commands are built in a more reactive way, and return the execution result as part of the same method call. - - similarly to the commands, listening to loading, error, and data state changes are as simple as calling one method and getting all the results in one call. - -For more about commands and queries, please see [this](https://sap.github.io/spartacus-docs/commands-and-queries/#page-title) - -### General changes - -- Majority of old checkout imports (`@spartacus/checkout`) are now spread across the new entry points, and its secondary entry points. -- `normalizeHttpError()` is moved from the effects to the adapter layer - -### Setup library - -We are moving away from our initial idea of having recipes contained in the setup library. For this reason we've move our existing configurations to either library's root, or to the app itself. - -- `defaultB2bCheckoutConfig` - moved to `@spartacus/checkout/b2b/root`. -- `defaultB2bOccConfig` - moved to `@spartacus/checkout/b2b/root`. - -### New functionality - -- `backOff` pattern is now added to the OCC adapters, and configured to retry on OCC-specific JALO errors. -- validation and error handling - the commands now perform various precondition checks. At the same time, commands will throw errors if the execution fails. Previously, effects were dispatching Fail actions, which usually were't handled. \ No newline at end of file diff --git a/docs/migration/5_0.md b/docs/migration/5_0.md deleted file mode 100644 index 53c7637886a..00000000000 --- a/docs/migration/5_0.md +++ /dev/null @@ -1,776 +0,0 @@ -# Technical Changes in Spartacus 5.0 - -## Removed incubator library - -Incubator library is not being published anymore. - -## Breaking changes in product-configurator library - -### Reduced number of page header slots and introduction of exit button - -In case the rulebased product configurator is launched from product details, product catalog, or cart, the number of slots displayed in -the page header has been reduced compared to 4.0. We only show slots `SiteLogo`,`VariantConfigOverviewExitButton` and `MiniCart`. -For details see `CpqConfiguratorLayoutModule`, `VariantConfiguratorInteractiveLayoutModule` and `VariantConfiguratorOverviewLayoutModule`. -These modules are new in 5.0. The layout configuration was removed from `CpqConfiguratorInteractiveModule`, `VariantConfiguratorInteractiveModule` -and `VariantConfiguratorOverviewModule`. - -Note that `VariantConfigOverviewExitButton` is new, and allows to leave the configuration. Clicking it directs the user to the product detail -page, configuration changes are not taken over to the cart. - -### Specific page header slots in case configurator is launched in displayOnly mode - -In case the rulebased product configurator is launched from the checkout review order page, from the order confirmation page or from the order -history page, the page header shows the standard Spartacus header slots (not the reduced set of header slots listed in the previous section). -Specifically, `VariantConfigOverviewExitButton` is not offered then. -For details, see `CpqConfiguratorPageLayoutHandler` and `VariantConfiguratorPageLayoutHandler`. -The page header slots used in case of the displayOnly mode can be configured in `CpqConfiguratorLayoutModule` and `VariantConfiguratorOverviewLayoutModule`, -under the section `headerDisplayOnly`. - -### ConfiguratorCommonsService - -The method `removeObsoleteProductBoundConfiguration` has been removed, as it's no longer needed. An obsolete product bound configuration is handled -within action `RemoveCartBoundConfigurations`. So in case you called `removeObsoleteProductBoundConfiguration` before, consider to raise that action, which will -clear all cart bound configurations, and in addition delete the obsolete product bound configuration that is predecessor of a cart bound configuration. - -### ConfiguratorGroupTitleComponent - -The member `configuration$` was not used and has been removed. In case you use it in a sub component, consider to declare it there via -`configuration$: Observable = this.configRouterExtractorService .extractRouterData() .pipe( switchMap((routerData) => this.configuratorCommonsService.getConfiguration(routerData.owner) ) );` - -### RulebasedConfiguratorEventListener - -This service has been removed. It was responsible for deleting cart bound configurations when an order was submitted. This is now handled by `ConfiguratorRouterListener`, which checks on cart bound -configurations on every navigation that is not configurator related, and deletes cart bound configurations if needed. - -### ConfiguratorAddToCartButtonComponent - -- `#displayOnly` template was added -- `` was added to the `#displayOnly` template -- Now also uses `configurator.addToCart.buttonDisplayOnly` translation label key -- Added `OrderFacade` to constructor. -- Added `CommonConfiguratorUtilsService` to constructor. -- Added `ConfiguratorStorefrontUtilsService` to constructor. -- Added `IntersectionService` to constructor. - -### ConfiguratorOverviewBundleAttributeComponent - -- Added `TranslationService` to constructor. - -### ConfiguratorGroupMenuComponent - -- `configurator.addToCart.buttonDisplayOnly` was added to `configurator-common.ts` - -### AuthRedirectService - -- `reportAuthGuard` - method was removed. Use `saveCurrentNavigationUrl` method instead. -- `reportNotAuthGuard` - method not needed anymore. Every visited URL is now remembered automatically as redirect URL on `NavigationEnd` event. - -### NotAuthGuard - -- Removed `AuthRedirectService` from constructor. - -### LogoutGuard - -- Removed `AuthRedirectService` from constructor. - -### LoginGuard - -- Removed `AuthRedirectService` from constructor. - -### CdcLogoutGuard - -- Removed `AuthRedirectService` from constructor. -### ConfiguratorAttributeHeaderComponent - -- Added `ConfiguratorCommonsService` to constructor. -- Added `ConfiguratorGroupsService` to constructor. -- Added `ConfiguratorUISettingsConfig` to constructor. -- Changed signature of `isAttributeGroup`, removed `groupType` parameter -- Changed signature of `getConflictMessageKey`, removed `groupType` parameter - -### ConfiguratorAttributeSingleSelectionBaseComponent - -- Added `TranslationService` to constructor. -- Got 2 new input fields: `language` and `ownerType`, both of type `string` - -### ConfiguratorAttributeNumericInputFieldComponent - -- Added `TranslationService` to constructor. - -### ConfiguratorAttributeProductCardComponent - -- Added `TranslationService` to constructor. - -### ConfiguratorAttributeProductCardComponentOptions - -- Added new attributes `attributeLabel`, `attributeName`, `itemCount` and `itemIndex` to interface. - -### ConfiguratorCartEntryBundleInfoComponent - -- `TranslationService` was added to constructor - -### ConfiguratorExitButton - -- `container$ | async as container` was added to the button to determine what the current owner type is. The button will be named accordingly - -### ConfiguratorAttributeProductCardComponent - -- `span` added (visually hidden) for screen-reader to read explanation for hidden button (in case option .hideRemoveButton is true - -### ConfiguratorAttributeDropDownComponent - -- Added `TranslationService` to constructor. -- Added conditional includes of `` and `` because of the support for attributes with additional values -- Added styling `.cx-configurator-attribute-additional-value { margin-inline-start: 0px; }` in order to render an additional attribute value properly -- Added `label` (visually hidden) for screen-reader to read explanation of drop-down listbox (number of entries) - -### ConfiguratorAttributeRadioButtonComponent - -- Added `TranslationService` to constructor. -- Added conditional includes of `` and `` because of the support for attributes with additional values -- Added styling `.cx-configurator-attribute-additional-value { margin-inline-start: 0px; }` in order to render an additional attribute value properly - -### ConfiguratorAttributeReadOnlyComponent - -#### for staticDomain part: - -- `span` added (visually hidden) for screen-reader to read `value x of attribute y` (in order to better distinguish between text of a value and text of an attribute for screen-reader users) -- `span` added with `aria-hidden=true` around visible read-only value to avoid double reading by screen-reader - -#### for #noStaticDomain template: - -- `span` added (visually hidden) for screen-reader to read `value x of attribute y` (in order to better distinguish between text of a value and text of an attribute for screen-reader users) -- `span` added with `aria-hidden=true` around visible read-only value to avoid double reading by screen-reader - -#### for userInput part: - -- `span` added (visually hidden) for screen-reader to read `value x of attribute y` (in order to better distinguish between text of a value and text of an attribute for screen-reader users) -- `span` added with `aria-hidden=true` around visible read-only value to avoid double reading by screen-reader - -### ConfiguratorAttributeSingleSelectionBundleDropdownComponent - -- `label` added (visually hidden) for screen-reader to read explanation of drop-down listbox (number of entries) - -### ConfiguratorAttributeSingleSelectionImageComponent - -- enclosing `div` added with attribute `role=listbox` to make clear for screen-reader that the enclosed divs belong to a list selection - -### ConfiguratorConflictSuggestionComponent - -- `span`-tags added around suggestion title and texts with attribute `aria-hidden=true` as the text for screen-reader is taken over by new attribute aria-label at `div`-tag to provide consistent screen-reader text for different browsers - -### ConfiguratorAttributeSingleSelectionImageComponent - -- `span`-tags added (visually hidden) to provide extra information for screen-reader users - -### ConfiguratorOverviewAttributeComponent - -- `span` added (visually hidden) for screen-reader to read `value x of attribute y` or `value x of attribute y surcharge z` if a price is present (in order to better distinguish between text of a value and text of an attribute for screen-reader users) - -### ConfiguratorOverviewBundleAttributeComponent - -- `span` added (visually hidden) for screen-reader to read `item x of attribute y`, `item x of attribute y, surcharge z`, `item x of attribute y, quantity 123` or `item x of attribute y, quantity 123, surcharge z` depending on availability of price and quantity - -### ConfiguratorOverviewFormComponent - -- `span`-tags added (visually hidden) to provide extra information for screen-reader users - -### ConfiguratorProductTitleComponent - -- `span` with attribute `aria-hidden=true` around visible link text to avoid double reading by screen-reader (text is covered by new `aria-label` attribute of surrounding `div`) - -### ConfiguratorUpdateMessageComponent - -- `div` with attributes `aria-live=polite` and `aria-atomic=false` added around update message div in order to monitor changes in this area. As soon as update message becomes visible it will be read by the screen-reader (in polite mode). - -### OccConfiguratorVariantNormalizer - -- Method `convertAttributeType` changes its signature from `convertAttributeType(type: OccConfigurator.UiType): Configurator.UiType` to `convertAttributeType(sourceAttribute: OccConfigurator.Attribute): Configurator.UiType`. - -### ConfiguratorStorefrontUtilsService - -The method `isInViewport` has been removed. It is no longer needed as scrolling is always executed on navigation regardless of position of element. - -### TabParagraphContainerComponent - -- `BreakpointService` service has been removed, accordion view is now displayed for all screen sizes. -- `ariaLabel` has been added to fill the container aria-label based on displayed components. - -#### Template changes - -- Second ng-container, `` has been wrapped in a `div` for screen reader improvements. -- `span` with class `accordion-icon` added as icon placeholder for screen reader improvements. - -### ProductImageZoomProductImagesComponent - -- Exposes field `product$: Observable` from parent component `ProductImagesComponent` - -#### Template changes - -- Element `` has been wrapped in a new container `` for screen reader improvements. - -### CartTotalsComponent - -- The `entries$` property was removed. -- The `cartValidationInProgress` property was removed. -- Removed `Router` from constructor. -- The `ngOnDestroy` method was removed. -- The `disableButtonWhileNavigation` method was removed. - -#### Template changes - -- Removed progress button component in favor for decoupling the cart-totals business logic and the button to proceed to checkout. The new button component which is CMS-driven is tied to a cms component called `CartProceedToCheckoutComponent` that is mapped to the Spartacus component `CartProceedToCheckoutComponent`. - -### ProgressButtonComponent - -- The Output property `clikEvent` has been renamed to `clickEvent` (typo). - -### FacetListComponent - -#### Template changes - -- Top element `
` has been changed to `` to make accesibility navigation consistent. - -### Translation (i18n) changes related to accessibility in variant configuration - -The following keys have been added to `configurator-common.ts`: - -- `configurator.a11y.xx` -- `configurator.a11y.configureProduct` -- `configurator.a11y.cartEntryBundleInfo` -- `configurator.a11y.cartEntryBundleInfo_other` -- `configurator.a11y.cartEntryBundleName` -- `configurator.a11y.cartEntryBundleNameWithQuantity` -- `configurator.a11y.cartEntryBundleNameWithPrice` -- `configurator.a11y.cartEntryBundle` -- `configurator.a11y.cartEntryInfoIntro` -- `configurator.a11y.cartEntryInfo` -- `configurator.a11y.nameOfAttribute` -- `configurator.a11y.valueOfAttribute` -- `configurator.a11y.forAttribute` -- `configurator.a11y.valueOfAttributeFull` -- `configurator.a11y.valueOfAttributeFullWithPrice` -- `configurator.a11y.selectedValueOfAttributeFull` -- `configurator.a11y.selectedValueOfAttributeFullWithPrice` -- `configurator.a11y.readOnlyValueOfAttributeFull` -- `configurator.a11y.valueOfAttributeBlank` -- `configurator.a11y.value` -- `configurator.a11y.attribute` -- `configurator.a11y.requiredAttribute` -- `configurator.a11y.listOfAttributesAndValues` -- `configurator.a11y.editAttributesAndValues` -- `configurator.a11y.group` -- `configurator.a11y.itemOfAttributeSelected` -- `configurator.a11y.itemOfAttributeSelectedWithPrice` -- `configurator.a11y.itemOfAttributeSelectedPressToUnselect` -- `configurator.a11y.itemOfAttributeSelectedPressToUnselectWithPrice` -- `configurator.a11y.itemOfAttributeUnselected` -- `configurator.a11y.itemOfAttributeUnselectedWithPrice` -- `configurator.a11y.selectNoItemOfAttribute` -- `configurator.a11y.itemOfAttribute` -- `configurator.a11y.itemOfAttributeFull` -- `configurator.a11y.itemOfAttributeFullWithPrice` -- `configurator.a11y.itemOfAttributeFullWithQuantity` -- `configurator.a11y.itemOfAttributeFullWithPriceAndQuantity` -- `configurator.a11y.itemDescription` -- `configurator.a11y.listbox` -- `configurator.a11y.valueSurcharge` -- `configurator.a11y.conflictDetected` -- `configurator.a11y.conflictsInConfiguration` -- `configurator.a11y.listOfGroups` -- `configurator.a11y.inListOfGroups` -- `configurator.a11y.groupName` -- `configurator.a11y.groupBack` -- `configurator.a11y.conflictBack` -- `configurator.a11y.iconConflict` -- `configurator.a11y.iconIncomplete` -- `configurator.a11y.iconComplete` -- `configurator.a11y.iconSubGroup` -- `configurator.a11y.next` -- `configurator.a11y.previous` -- `configurator.a11y.showMoreProductInfo` -- `configurator.a11y.showLessProductInfo` -- `configurator.a11y.productName` -- `configurator.a11y.productCode` -- `configurator.a11y.productDescription` -- `configurator.a11y.configurationPage` -- `configurator.a11y.configurationPageLink` -- `configurator.a11y.overviewPage` -- `configurator.a11y.overviewPageLink` -- `configurator.attribute.singleSelectAdditionalRequiredMessage` - -## Breaking Changes Introduced in 5.0 - -### DeliveryAddressComponent - -- Added `GlobalMessageService` to constructor. - -### Translation (i18n) changes - -- `configurator.conflict.viewConfigurationDetails` was added to `configurator-common.ts` -- `quickOrderCartForm.entriesWasAdded` changed to `quickOrderCartForm.entriesWereAdded` -- `quickOrder.addToCart` changed to `quickOrder.add` -- `payment.paymentForm.setAsDefault` changed from `Set as default` to `Set as default payment method` for screen reader improvements. -- `storeFinder.storeFinder.searchBox` changed from `Enter postal code, town or address` to `Postal code, town or address` -- `common.searchBox.placeholder` changed from `Search here...` to `Enter product name or SKU` -- Translation for key `address.addressForm.setAsDefault` changed from `Set as default` to `Set as default shipping address` -- `quickOrderForm.searchBoxLabel` changed from `Enter Product name or SKU for quick order` to `Enter Product name or SKU for quick order. You can add up to {{ limit }} products per order.` for screen reader improvements. -- `AccountOrderHistoryTabContainer.tabs.tabPanelContainerRegion` was added to `order.i18n.ts` - -### OrderHistoryComponent - -- Changed `

` to `

` -- Added `TranslationService` to constructor. - -### ShippingAddressComponent - -- Added `GlobalMessageService` to constructor. - -### AddressBookComponent - -- Added `GlobalMessageService` to constructor. -- Changed `setAddressAsDefault` method parameter type`string` to `Address`. - -### OrderConfirmationThankYouMessageComponent - -- Added `GlobalMessageService` to constructor. -- Added `TranslationService` to constructor. - -#### Template Changes - -- Component template was modified to display `span` instead of `h1` for page title. - -### PromotionsComponent - -- Component template was modified to display single promotions using the `p` tag and multiple promotions using `ul` list elements - -### CartDetailsComponent - -- Component template was modified to display `h2` instead of `h4` for cart name. -- Button in `#saveForLaterBtn` is no longer wrapped by div element. - -### SaveForLaterComponent - -- Button in `#moveToCartBtn` is no longer wrapped by div element. - -### CartOutlets - -- New enum values available: `LIST_ITEM` and `ITEM_CONFIGURATOR_ISSUES`; - -### CartItemComponent - -- Component template was modified to display `h3` instead of `h2` for product name. -- Component template was modified, `tabindex` for `a` element inside `
` has been changed from `-1` to `0` -- Added separated `[cxOutlet]="CartOutlets.ITEM_CONFIGURATOR_ISSUES"` inside `ITEM` outlet. - -### CartItemListComponent - -- Component template was modified to display native table instead of nested divs. The structure has been simplified and bootstrap classes has been removed. -- It doesn't use `cx-cart-item` anymore. Instead, it uses new `cx-cart-item-list-row` component. - -### ConfiguratorIssuesNotificationComponent - -- Default config of outlet has been changed from `{id: CartOutlets.ITEM, position: OutletPosition.BEFORE}` to `{id: CartOutlets.ITEM_CONFIGURATOR_ISSUES, position: OutletPosition.REPLACE,}`. - -### OrderSummaryComponent - -- Component template was modified to display `h2` instead of `h3` for order summary. -- Markup template changed in `order-summary.component.html`. `h2` tag has been converted to `div.cx-summary-heading` and `h4.cx-review-cart-heading` has been converted to `div.cx-review-cart-heading` in two places for screen reader improvements. - -### ProductAttributesComponent - -- Component template was modified to display `h2` instead of `h3` for classification name. - -### CartCouponComponent - -- Component template was modified to display coupon as a `label` inside a form. Previously it was in a `div` before `form` tag. - -### CheckoutProgressComponent - -- `section` element changed to `nav`. - -### CardComponent - -- Component template was modified to display `span` instead of `h3` for card title. -- `{{action.name}}` changed to `` -- `cx-card-link card-link btn-link` classes have been replaced with a single class `link` - -### StoreFinderSearchComponent - -- Component template modified, `input[attr.aria-label]="'common.search' | cxTranslate"` has been changed to `input[attr.aria-label]="'storeFinder.searchBoxLabel' | cxTranslate"` -- Component template modified, `cx-icon[attr.aria-label]="'common.search' | cxTranslate"` has been changed to `cx-icon[attr.aria-label]="'storeFinder.searchNearestStores' | cxTranslate"` - -### SearchBoxComponent - -- Component template modified, `