Alokai Changelog
1.3.3
Patch Changes
- FIXED
@alokai/connect: Fixedloggertype in API Client extension hooks
1.3.2
Patch Changes
- FIXED Applying
patchesto the apps when runningalokai-cli store deploycommand - FIXED
.outdir now is being removed on preinstall. It makes sure that the.outdir doesn't impact theyarn installcommand.
Migration guide
Add to your root package.json a preinstall script which removes .out:
{
"scripts": {
+ "preinstall": "rm -rf .out"
}
}
Note: The rm -rf command works on Unix-based systems (Linux, macOS). For a universal solution that works across all operating systems, consider using:
rimrafpackage:"preinstall": "rimraf .out"- A Node.js script for cross-platform compatibility
1.3.1
Patch Changes
- FIXED Mismatch warning in
alokai version upgradecommand. Now, it shows what dependencies are mismatching.
1.3.0
Minor Changes
- CHANGED
deploycommand of@alokai/clinow pushes the information about the Alokai version used in the project, to provide better visibility and tracking in the Alokai Console. - ADDED
alokai lintcommand that provides a unified way to run linting across the entire project, including both applications and stores. This command introduces graceful error handling, unified linting for all applications via Turbo, auto-fix support, flexible filtering, enhanced error reporting, and silent mode for integration use.
# Run lint for entire project
alokai lint
# Run lint with auto-fix
alokai lint --fix
# Run lint with custom filter
alokai lint --filter=packages/my-package
# Exclude multiple patterns
alokai lint --filter='!packages/excluded !apps/test'
- ADDED
alokai versioncommand that allows you to check the version of your Alokai project, update theversionfield inpackage.jsonfiles, and be informed about possible upgrades. This command provides centralized version management across the entire project.
yarn alokai version
# Example output:
# The project is running on Alokai version: 1.3.0
# Versions in package.json files updated successfully.
# You are running the latest Alokai version.
- ADDED
alokai version upgradecommand that allows users to upgrade their dependencies to the next Alokai version. This command automates the upgrade process for better version management. It only upgrades dependencies of minor and patch Alokai versions.
yarn alokai version upgrade
- CHANGED Error handling for
@alokai/clipackage installation failures has been improved to provide user-friendly error messages for package authentication and not found errors, replacing generic error messages with specific guidance for Alokai package authentication problems. - CHANGED Next.js base metadata have been moved from
app/[locale]/layout.tsxtoconfig/metadata.ts, to enable more granular control over metadata in child stores and allow metadata overrides without modifying the layout component. - ADDED Support for
--cwdflag andALOKAI_CWDenvironment variable to run Alokai CLI commands from non-root directories. This enhancement allows users to execute Alokai CLI commands from any directory by specifying the project root.
# Using --cwd flag
alokai-cli store build --cwd=./path/to/alokai/project
# Using environment variable
ALOKAI_CWD=./path/to/alokai/project alokai-cli store build
Patch Changes
- FIXED Security vulnerability fixes across multiple packages in
@alokai/connect. Previously, there were 40 vulnerabilities (23 Low, 8 Moderate, 2 High, 7 Critical). Now there are 24 vulnerabilities (22 Low, 2 Moderate, 0 High, 0 Critical), representing a 40% reduction in total vulnerabilities with all critical and high severity issues resolved. - CHANGED
--verboseflag default value fordevcommand fromfalsetotrue, to provide more detailed output during development by default and improve debugging experience. - FIXED
deployment-workflow.ymlnow properly passes verbose flag to the command. Previously, the verbose flag was not being passed correctly, causing deployment workflows to run without detailed output when expected. - ADDED Prerequisites section to integration template README instructions that includes instructions for installing required dev dependencies and enhanced documentation structure with proper AI-driven vs manual setup sections. This improves the developer experience when creating new integrations.
- FIXED Typo in integration template default value. Previously, the integration generate command used incorrect default template value
resti-api, now it correctly usesrest-api. - CHANGED Turborepo filter descriptions in store commands have been improved to provide clearer guidance on how to use Turborepo package filters, replacing vague descriptions with specific examples and use cases.
- CHANGED
@alokai/connectdependency has been removed from rootpackage.jsonfile to avoid duplication, as it's already present in theapps/**/package.jsonfiles where it's actually needed. - FIXED Single store deployment framework auto detection. Previously, calling the command without framework flag caused a TypeError with
"paths[1]" argument being undefined. Now the framework is properly auto-detected.
Migration guide
- Remove
@alokai/connectfrom the rootpackage.jsonfile
- "@alokai/connect": "^1.2.0",
- Ensure that the
@alokai/connectpackage is present in theapps/**/package.jsonfiles - Ensure that
--verboseflag is properly passed to thedeploycommand in.github/workflows/continuous-delivery.ymlfile.
yarn store deploy \
# ...
- --verbose ${{ needs.chooseStoresToDeploy.outputs.verbose }}
+ ${{ needs.chooseStoresToDeploy.outputs.verbose == 'true' && '--verbose' || '' }}
- Add
alokai version checkcommand to yourpostinstallanddevscripts.
"scripts": {
- "postinstall": "node ./scripts/symlink-node-modules.mjs && node ./scripts/init-script.mjs && mkdir -p .out && yarn build:packages",
+ "postinstall": "node ./scripts/symlink-node-modules.mjs && node ./scripts/init-script.mjs && mkdir -p .out && yarn build:packages && yarn alokai version check",
- "dev": "yarn alokai store dev --all",
+ "dev": "yarn alokai version check && yarn alokai store dev --all",
}
1.2.3
Patch Changes
- FIXED
X-Frame-Optionsheader has been set toDENYby default, which prevents the site from being embedded in iframes. This will block features like live preview in some CMS systems (Contentful).
Migration guide:
iframe Embedding
To control iframe embedding, update the Next.js middleware:
+ type Header = {
+ key: string;
+ value: string;
+ }
+
+ /**
+ * Headers to set for every response
+ */
+ const responseHeaders: Header[] = [
+ /*
+ * NOTE: The X-Frame-Options header below prevents the site from being embedded in iframes,
+ * which blocks features like live preview in CMS systems (Contentful, Amplience, etc.).
+ * To enable live preview functionality, you may need to remove or modify this header.
+ * See the CMS module documentation for specific configuration steps.
+ */
+ env('NEXT_PUBLIC_DISABLE_X_FRAME_OPTIONS_HEADER') !== 'true' && {
+ key: 'X-Frame-Options',
+ value: 'DENY',
+ },
+ ].filter((header): header is Header => !!header);
+
+ export default createAlokaiMiddleware(async (request) => {
+ // ...
+ for (const header of responseHeaders) {
+ response.headers.set(header.key, header.value);
+ }
+ //...
Now, by default the X-Frame-Options header is set to DENY, which prevents the site from being embedded in iframes. To enable live preview functionality in some CMS systems, you need to set the NEXT_PUBLIC_DISABLE_X_FRAME_OPTIONS_HEADER environment variable to true.
+ NEXT_PUBLIC_DISABLE_X_FRAME_OPTIONS_HEADER=true
Playwright Tests
If you are struggling with running integration tests with yarn store test, because the middleware app is not build before tests start, make sure to update your turbo.json file:
{
"tasks": {
"playwright-default#test:integration": {
- "dependsOn": ["^storefront-middleware:build"],
+ "dependsOn": ["^build", "^storefront-middleware:build"],
+ "passThroughEnv": ["PLAYWRIGHT_OPTIONS"]
},
"playwright-default#test:integration:ui": {
- "dependsOn": [],
+ "dependsOn": ["^storefront-middleware#build"],
+ "passThroughEnv": ["PLAYWRIGHT_OPTIONS"]
}
}
}
This ensures Playwright tests wait for a full build and the storefront-middleware build before starting.
1.2.2
Patch Changes
- FIX Fix extension generation failing with ENOENT error when creating unified-commerce extensions
Fixed an issue where generating extensions using alokai-cli extension generate command would fail with "ENOENT: no such file or directory" error when trying to rename the middleware directory during the extension creation process.
1.2.1
Patch Changes
FIX Integration generate command fails when using default template due to typo in template name
- Fixed typo in integration generate command: corrected default template value from
resti-apitorest-api - Updated README documentation to reflect the correct template name
- Improved README formatting with better table of contents structure
1.2.0
Minor Changes
- ADDED Templates for integration boilerplates. Now, the developers are able to generate boilerplates for integrations based on the OpenAPI, GraphQL, REST API, proxied SDKs with extensions to unifiy both commerce and CMS integrations. More information can be found in the docs.
- ADDED Auto-imports for the Nuxt are now fully supported and will be automatically generated when the store is synced during the dev process or when the store is composed.
- CHANGED Watch mode for dev will be now little bit more verbose, so the developer can easily tell when the process of synchronizing data is finished.
- CHANGED Enabled Typescript shim for
*.vuefiles innuxt.config.ts. - CHANGED Turned CMS Mock integration into a separate Storefront Module and moved its code to the
@/sf-modules/cms-mockdirectory in all Storefront apps. - CHANGED Changed CMS Mock module key in
storefront-middleware/middleware.config.tsfromcmstocms-mock. Updated the corresponding SDK module accordingly. - CHANGED Deprecated the
useCmsPagecomposable in favour of the newConnectCmsPagewrapper instorefront-unified-nuxt. - CHANGED Re-exported
RenderCmsContentandConnectCmsPagewrappers from@/components/cms/wrappers/index.tsfor easier maintenance of CMS-enhanced pages (e.g. PDP and PLP). - CHANGED Moved interfaces describing dynamic content of CMS-only and CMS-enhanced pages to the
@/sf-modules/<module_name>/types/index.tsfile and re-exported them from the@/types/cms.tsfile. - CHANGED Renamed Unified SDK modules registered by CMS Modules to allow for using multiple CMS modules in a single Storefront. Instead of
sdk.unifiedCms, every CMS module is now registering its own namespace:
| CMS Module | Unified SDK Namespace |
|---|---|
| cms-amplience | sdk.unifiedAmplience |
| cms-bloomreach-content | sdk.unifiedBloomreachContent |
| cms-builderio | sdk.unifiedBuilderio |
| cms-contentful | sdk.unifiedContentful |
| cms-contentstack | sdk.unifiedContentstack |
| cms-mock | sdk.unifiedCmsMock |
| cms-smartedit | sdk.unifiedSmartedit |
| cms-storyblok | sdk.unifiedStoryblok |
Unified SDK modules files added to the Storefront by CMS modules have been renamed accordingly:
| CMS Module | Unified SDK module file name |
|---|---|
| cms-amplience | unified-amplience.ts |
| cms-bloomreach-content | unified-bloomreach-content.ts |
| cms-builderio | unified-builderio.ts |
| cms-contentful | unified-contentful.ts |
| cms-contentstack | unified-contentstack.ts |
| cms-mock | unified-cms-mock.ts |
| cms-smartedit | unified-smartedit.ts |
| cms-storyblok | unified-storyblok.ts |
Patch Changes
- CHANGED AI contexts to be more framework and integration specific. Now, Next.js projects are going to include only Next-related context, and the same for Nuxt. The same rule applies to integrations.
- FIXED Random 404 errors on CMS pages using Contentstack Live Preview feature.
- FIXED Assets installation in modules for Nuxt
Migration guide
Using multiple CMS modules in a single Storefront
The changes described in this migration guide are recommended for developers intending to use multiple CMS modules in their project - either now, or in the future. For other projects, the changes should be considered optional.
- Update the root
package.jsonversion to1.2.0. - Enable Typescript shim for
*.vuefiles (optional)
No changes required.
- Replace
useCmsPagecomposable with theConnectCmsPagewrapper
No changes required.
- Re-export CMS module components from the
@/sf-modulesdirectory
If your connectCmsPage and RenderCmsContent components are located in the @/components/cms/wrappers directory, move them to the @/sf-modules/<module_name>/components directory and re-export them from there:
export { default as connectCmsPage } from '@/sf-modules/<module_name>/components/connect-cms-page';
export { default as RenderCmsContent } from '@/sf-modules/<module_name>/components/render-cms-content';
Second, ensure the components are re-exported from the @/components/cms/wrappers directory:
export * from '@/sf-modules/<module_name>/components';
Finally, ensure the components are properly imported by pages that use them:
import { connectCmsPage, RenderCmsContent } from '@/components/cms/wrappers';
- Move dynamic pages interfaces to the
@/sf-modulesdirectory
First, create the @/sf-modules/<module_name>/types/index.ts file and move there:
- all interfaces describing dynamic CMS content from your pages in the
apps/storefront-unified-nextjs/appdirectory (including PLP & PDP), - the
PropsWithCmsPagepage interface from the@/sf-modules/<module_name>/components/connect-cms-page.tsxfile
export interface DynamicPage {
componentsAboveFold: AgnosticCmsComponent[];
componentsBelowFold: AgnosticCmsComponent[];
}
export interface ProductDetailsPage {
componentsBottom: AgnosticCmsComponent[];
}
export interface CategoryPage {
componentsBottom: AgnosticCmsComponent[];
componentsTop: AgnosticCmsComponent[];
}
export interface PropsWithCmsPage<TPage = Record<string, any>> {
page: TPage;
params: {
locale: string;
};
}
Second, re-export the interfaces from the @/types/cms.ts file:
export type * from '@/sf-modules/<module_name>/types';
Finally, ensure the moved interfaces are properly imported by pages that use them:
import type { CategoryPage, PropsWithCmsPage } from '@/types/cms';
- Rename the
UnifiedCmsEndpointsinterface First, in the maintypes.tsfile, find export statements refering to your CMS module and replace them with a single export all statement:
export type { UnifiedCmsEndpoints } from '@sf-modules-middleware/<module-name>';
export type { Endpoints as CmsModuleEndpoints } from '@vsf-enterprise/<cms>-api';
export * from '@sf-modules-middleware/<module_name>';
Second, rename and re-export everything from the types.ts file of your CMS module:
export type { UnifiedEndpoints as UnifiedCmsEndpoints } from '@vsf-enterprise/<cms>-api';
export type {
Endpoints as CmsModuleNameEndpoints
UnifiedEndpoints as UnifiedCmsModuleNameEndpoints
} from '@vsf-enterprise/<cms>-api';
- Update the
unifiedCmsSDK module
import { defineSdkModule } from '@vue-storefront/next';
import type { UnifiedCmsEndpoints } from 'storefront-middleware/types'; import type { UnifiedCmsModuleNameEndpoints } from 'storefront-middleware/types'; export const unifedCms = defineSdkModule(() => { /* */ }); export const unifedCmsModuleName = defineSdkModule(() => { /* */ }); Second, update the corresponding export path in the SDK modules barrell file:
export * from "@/sdk/modules/unified-cms"export * from "@/sdk/modules/unified-cms-module-name"Contentstack: Fixing Live Preview
Now query is being stored within search params instead of cookies, which fixes a random 404 errors on CMS pages after using Contentstack's live preview. To fix live preview in Contentstack:
- Update the
@vsf-enterprise/contentstack-apiand@vsf-enterprise/contentstack-sdkfollowing the compatibility matrix. - Pass
searchParamstosdk.unifiedContentstack.getPagecall:
const { params, searchParams } = props;
//...
const page = await sdk.unifiedContentstack
.getPage({
locale: cmsLocale,
path,
searchParams,
});
1.1.0
Minor Changes
- ADDED A capability to monitor page views in the storefront apps.
- ADDED Support for ignorePaths field handling in
alokai.config.json. - CHANGED Deployable stores are now recognised by the fact they have "deployable" field instead of config at all, so that template stores can be parametrised in
alokai.config.json. - CHANGED The
@alokai/connect/config-switchernow supports using no header with the extension, defaulting to the default value when no header is provided. - ADDED Adds "userAlreadyExists" error handling to nuxt register-b2b module.
- ADDED Add
renamecommand used to change store id. Example usage:
alokai-cli store rename
--store-id sapcc-b2c
--new-store-id sapcc-my-brand
- ADDED Add
integration generatecommand for generating integration boilerplate code. Example usage:
alokai-cli integration generate myIntegration
- ADDED Added
store lintcommand. - ADDED Alokai CLI's
build,dev,startandtestcommands now support the--turbo-optionflag for passing custom options when running a Turbo task. - CHANGED Replaced relative imports with aliased imports in Storefront apps:
playwright,storefront-middlewareandstorefront-unified-[nuxt|nextjs]. - CHANGED The
preparescript in the base Nuxt app now runsalokai-cli store preparecommand afternuxt prepareby default. - CHANGED The
@vue-storefront/nuxtmodule now injects value ofNUXT_PUBLIC_ALOKAI_VERSIONenvironment variable into appConfig. - CHANGED Generation of
tsconfigfiles. Previously, apps had private configuration that was replicated in each store. Now shared configuration will be inherited from the root config, reducing duplication and improving maintainability. Example:
- import { confg as cmsConfig } from './integrations/cms-mock';
+ import { config as cmsConfig } from '@/integrations/cms-mock';
Patch Changes
- CHANGED The
store preparecommand of@alokai/clinow generates theincludearray in store-specifictsconfig.jsonfiles to comply with the stricter file inclusion requirements enforced by thecompilerOptions.composite: truesetting. - CHANGED
lintandlint:fixscripts in generated project. Now they lintstoresdirectory as well. - CHANGED add missing dist directory to instrumentation npm packages and updated those packages:
- @alokai/instrumentation-next-component@1.0.0
- @alokai/instrumentation-nuxt3-module@1.0.0
- @vue-storefront/next@6.1.0
- @vue-storefront/nuxt@9.1.0
- FIXED Removed the dotenv library from
@alokai/connect/loggerto allow using it in React Native projects. - FIXED Fixed Typescript errors in the
/apps/stores/default/storefront-unified-nuxtapp by adding properpathsconfiguration in the/apps/stores/default/storefront-unified-nuxt/tsconfig.jsonfile. To add the requiredpathsconfiguration in your stores' Nuxt apps, run Alokai CLI'sstore preparecommand from the root of your repository:
yarn store prepare --all
- FIXED Hydration warnings in the
storefront-unified-nuxtapplication. - FIXED Fixes register-b2b playwright test suite.
- FIXED Add autogenerated
tsconfig.json(create in substores) to.gitignore. - FIXED Move data-testid to
element. To migrate update the file apps/storefront-unified-nextjs/sf-modules/register-b2b/components/success-b2b-register-modal.tsx:
-<div className="relative z-50" data-testid="success-modal">
+<div className="relative z-50">
/* ... */
- <SfModal as="section" className="!relative max-w-[480px] p-6 md:p-10" open role="alertdialog">
+ <SfModal as="section" className="!relative max-w-[480px] p-6 md:p-10" open role="alertdialog" data-testid="success-modal">
- FIXED Removes duplicated email input in file
apps/storefront-unified-nuxt/components/RegisterForm/RegisterForm.vue - FIXED Add missing "prettier" devDependency to storefront-unified-nextjs app. This change will be needed for upcoming Yarn 4 migration. To migrate, add following line to
apps/storefront-unified-nextjs/package.json:
"devDependencies": {
/* ... */
"prettier: "3.3.2",
/* ... */
}
- FIXED Paths to files for AI contexts. Previously they were pointing to non-existing directories. Now they point to the proper ones.
- FIXED Adds fixes to re-order module. To apply them in an existing storefront, update
apps/storefront-unified-nuxt/components/CartPageProductCard/CartPageProductCard.vuefile:
- defineProps<{
+ const props = defineProps<{
- FIXED Adds fixes to register-b2b module. To apply them in an existing storefront, remove duplicated lines in script section of
apps/storefront-unified-nuxt/components/RegisterForm/RegisterForm.vuefile:
const messageModel = ref('');
-const messageModel = ref('');
const characterLimit = 1000;
-const characterLimit = 1000;
const charsCount = computed(() => characterLimit - messageModel.value.length);
-const charsCount = computed(() => characterLimit - messageModel.value.length);
- FIXED Added again a logic for copying
.env.exampleto.envin init script.
Migration guide
Before starting the migration, please update all the dependencies to the compatible versions based on the compatibility matrix.
- Update the root
package.jsonversion to1.1.0. - To monitor page views in the storefront apps, see Instrumentation guide.
- If you have stores with configs that you wish to keep as non templates and they don't have
deploymentfield, you can add the field to thealokai.config.jsonfile.
// alokai.config.json
{
"stores": {
...
"my-store": {
+ "deployment": {...},
}
}
}
- Turn
storefront-middlewareinto a composite project
In ./apps/storefront-middleware/tsconfig.json, add compilerOptions.composite: true:
{
"compilerOptions": {
+ "composite": true
}
}
This will allow other apps in your repository to use storefront-middleware as a Typescript Project Reference in subsequent steps.
- Configure path aliases in
storefront-middlewareandplaywrightapps
In ./apps/storefront-middleware/tsconfig.json and ./apps/playwright/tsconfig.json, add the following path aliases:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
- Add
storefront-middlewareas a Project Reference in your storefront-unified-nuxt|nextjs apps
Since storefront-middleware is a composite project, it can be added as a Typescript Project Reference by other apps in your repository. This is required for proper resolution of absolute imports within storefront-middleware in storefront-unified-nuxt|nextjs apps.
./apps/storefront-unified-nextjs/tsconfig.json
{
+ "references": [
+ {
+ "path": "../storefront-middleware"
+ }
+ ]
}
- Ensure
storefront-middlewarebuild before running in dev mode
Since storefront-unified-[nuxt|nextjs] apps now depend on storefront-middleware as a Project Reference, make sure storefront-middleware is built before running your apps in dev mode to avoid type errors.
./apps/storefront-middleware/package.json
{
"scripts": {
- "dev": "NODE_ENV=development tsx watch src/index.ts",
+ "dev": "concurrently --raw \"tspc --watch\" \"NODE_ENV=development tsx watch src/index.ts\"",
},
"devDependencies": {
+ "concurrently": "^9.0.0",
}
}
- Add
multistore()rule group to eslint config instorefront-middlewareandplaywrightapps
To force absolute imports in storefront-middleware and playwright apps, add the multistore() rule group to the following eslint config files:
./packages/eslint-config/playwright.mjs
- import { concat, ecma, playwright, style, typescript } from '@vue-storefront/eslint-config';
+ import { concat, ecma, playwright, style, typescript, multistore } from '@vue-storefront/eslint-config';
import gitignore from 'eslint-config-flat-gitignore';
export default concat(
gitignore(),
ecma(),
typescript(
{
files: '**/*.ts',
},
{
files: ['setup/**/*.ts'],
rules: {
'@typescript-eslint/no-unused-expressions': 'off',
},
},
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
},
},
),
style(),
playwright({
files: ['**/*.test.ts', 'sf-modules/**/*.test.ts'],
}),
+ multistore(),
);
./packages/eslint-config/middleware.mjs
- import { concat, ecma, style, typescript } from "@vue-storefront/eslint-config";
+ import { concat, ecma, style, typescript, multistore } from "@vue-storefront/eslint-config";
import gitignore from "eslint-config-flat-gitignore";
export default concat(
gitignore(),
ecma(),
typescript(
{},
{
files: ["**/*.{ts,tsx}"],
rules: {
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/no-magic-numbers": "off",
"import/no-unresolved": "off",
},
},
),
style(),
+ multistore(),
);
- Add
multistore()rule group to eslint config in storefront-unified-nuxt|nextjs apps
To force absolute imports in storefront-unified-nuxt|nextjs apps, add the multistore() rule group to the following eslint config files:
./packages/eslint-config/nextjs.mjs
- import { concat, ecma, nextjs, style, typescript } from '@vue-storefront/eslint-config';
+ import { concat, ecma, nextjs, style, typescript, multistore } from '@vue-storefront/eslint-config';
import gitignore from 'eslint-config-flat-gitignore';
export default concat(
gitignore(),
ecma(),
typescript(
{
files: '**/*.{ts,tsx}',
},
{
files: ['**/*.{ts,tsx}'],
rules: {
'@typescript-eslint/consistent-type-imports': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
'import/no-unresolved': 'off',
},
},
),
nextjs({
files: {
components: ['components/**/*.{js,jsx,ts,tsx}', 'app/**/components/*.{js,jsx,ts,tsx}'],
general: '**/*.{js,jsx,ts,tsx}',
hooks: '{hooks,helpers}/**/*.{js,jsx,ts,tsx}',
},
}),
style(),
+ multistore(),
);
- Update
tsconfig.jsonfiles in stores
From the root of your repository, run the alokai-cli store prepare command to automatically update tsconfig.json files in stores:
yarn store prepare --all
In a Nuxt app, this command should be run every time you run nuxt prepare. We recommend adding it to the prepare script in your Nuxt app's package.json:
{
"scripts": {
- "prepare": "nuxt prepare"
+ "prepare": "nuxt prepare && cd ../.. && yarn store prepare --all --app-type=nuxt"
}
}
And you can add tsconfig.json for Nuxt app to the root .gitignore file.
apps/**/stores/*/storefront-unified-nuxt/tsconfig.json
- Add scripts for linting apps
In the root package.json file, add the following linting scripts:
{
"scripts": {
"lint": "turbo run lint --filter=\"!./out/**\" && yarn store lint --all",
"lint:fix": "turbo run lint:fix --filter=\"!./out/**\" && yarn store lint --all --fix"
}
}
- Use eslint to auto-replace relative imports with absolute ones
The final step is allowing eslint to automatically replace relative imports in your apps with absolute ones. Lint-fix your base apps and stores by running the lint:fix script from the root of your repository:
yarn lint:fix
Make sure the script finishes successfully and commit the files automatically fixed by eslint.
If you experience issues when running the above script, try to resolve version of
eslintdependency in the rootpackage.jsonof your project. We recommend trying version9.23.0.{ "resolutions": { + "eslint": "9.23.0" }, }
- Make
#test:integrationturbo tasks dependent onstorefront-middlewarebuild
When running integration tests with playwright (either in a headless mode using the test:integration script or in a headed mode using the test:integration:ui script), make sure that the storefront-middleware build is completed first. This is now a requirement since the storefront-middleware app is a composite project referenced by storefront-unified-nuxt|nextjs apps.
To add the dependency, in the turbo.json file, update your base app tasks configuration:
{
"tasks": {
"playwright#test:integration": {
"dependsOn": ["^storefront-middleware:build"],
+ "dependsOn": ["^build", "^storefront-middleware:build"],
},
"playwright#test:integration:ui": {
"dependsOn": [],
+ "dependsOn": ["^storefront-middleware#build"],
},
}
}
as well as your stores' tasks configuration:
{
"tasks": {
"playwright-<store-id>#test:integration": {
"dependsOn": ["^build"],
+ "dependsOn": ["^build", "^storefront-middleware-sapcc#build"],
},
"playwright-<store-id>#test:integration:ui": {
"dependsOn": [],
+ "dependsOn": ["^storefront-middleware-sapcc#build"],
},
}
}
Register B2B Module
Update file apps/playwright/sf-modules/register-b2b/pageObjects/register-b2b.page.ts:
- private message = this.page.getByTestId('textarea');
- private modal = this.page.getByTestId('modal');
+ private message = this.page.getByTestId('message-input');
+ private modal = this.page.getByTestId('success-modal');
Then, update frontend application.
./apps/storefront-unified-nextjs/app/locale/(auth)/register/components/register-form-client.tsx
<SfTextarea
className="block min-h-[96px] w-full"
+ data-testid="message-input"
maxLength={characterLimit}
name="message"
onInput={onMessageChange}
placeholder={t('messagePlaceholder')}
value={messageValue}
/>
-<p className="mt-0.5 text-right text-neutral-500 typography-hint-xs">{charsCount}</p>
+<p className="typography-hint-xs mt-0.5 text-right text-neutral-500">{charsCount}</p>
{isUserAlreadyExistsError && (
- <p className="mt-0.5 font-medium text-negative-700 typography-hint-xs">{t('userAlreadyExists')}</p>
+ <p data-testid="user-already-exists-error" className="text-negative-700 typography-hint-xs mt-0.5 font-medium">{t('userAlreadyExists')}</p>
Hydration warning in Nuxt
./apps/storefront-unified-nuxt/components/CategoryFilters/FilterSize.vue
@@ -4,8 +4,9 @@
v-model="selectedProxy"
size="sm"
class="uppercase w-full md:sf-chip-sm md:h-8 h-10"
- :input-props="{ value: facetValue.value }"
+ :input-props="{ value: facetValue.value, id }"
:disabled="!facetValue.productCount"
+ :for="id"
>
<span class="leading-4 text-center" data-testid="size-chip">{{ prettify(facetValue.label) }}</span>
</SfChip>
@@ -20,6 +21,8 @@ import { prettify } from '@/utils/facets';
const emits = defineEmits<FilterItemEmits>();
const props = defineProps<FilterItemProps>();
+const id = computed(() => `${props.facetValue.value}-${props.facetValue.label}`);
+
const selectedProxy = computed({
get: () => props.selected,
set: (value: FilterItemProps['selected']) => emits('update:selected', value),
./apps/storefront-unified-nuxt/components/ProductAttributes/ProductAttributes.vue
@@ -15,8 +15,11 @@
class="min-w-[48px]"
size="sm"
:input-props="{
- onClick: (e) => value == selectedSize && e.preventDefault(),
+ onClick: (e: Event) => value == selectedSize && e.preventDefault(),
+ value,
+ id: getSizeChipId(value, valueLabel),
}"
+ :for="getSizeChipId(value, valueLabel)"
:model-value="value === selectedSize"
@update:model-value="value !== selectedSize && setAttribute(slug, sku)"
>
@@ -33,8 +36,11 @@
class="min-w-[48px]"
size="sm"
:input-props="{
- onClick: (e) => attribute.value == selectedColor && e.preventDefault(),
+ onClick: (e: Event) => attribute.value == selectedColor && e.preventDefault(),
+ value: attribute.value,
+ id: getColorChipId(attribute.value, attribute.valueLabel),
}"
+ :for="getColorChipId(attribute.value, attribute.valueLabel)"
:model-value="attribute.value === selectedColor"
@update:model-value="setAttribute(attribute.slug, attribute.sku)"
>
@@ -67,4 +73,7 @@ const selectedSize = computed(() => (sizes[0] ? getAttribute(sizes[0].name as (t
const selectedColor = computed(() =>
colors[0] ? getAttribute(colors[0].name as (typeof colorFacetNames)[number]) : null,
);
+
+const getSizeChipId = (value: string, label: string) => `size-${value}-${label}`;
+const getColorChipId = (value: string, label: string) => `color-${value}-${label}`;
</script>
./apps/storefront-unified-nuxt/components/ui/ProductCard/ProductCard.vue
@@ -37,7 +37,7 @@
{{ description }}
</p>
<div class="block font-semibold typography-text-lg" data-testid="product-card-vertical-price">
- <SkeletonGeneric :is-loaded="!isLazyLoadingData" inline class-name="my-2">
+ <SkeletonGeneric :is-loaded="!isLazyLoadingData" inline class-name="my-2 min-h-10">
<DecoratedPrice
v-if="price"
:regular="formatPrice(price.value)"
@@ -50,13 +50,6 @@
}"
/>
</SkeletonGeneric>
- <noscript>
- <DecoratedPrice
- v-if="price"
- :regular="formatPrice(price.value)"
- :special="price?.isDiscounted ? formatPrice(price.regularPrice) : undefined"
- />
- </noscript>
</div>
<SfButton
v-if="showAddToCartButton && sku"
./apps/storefront-unified-nuxt/components/ui/PurchaseCard/PurchaseCard.vue
@@ -25,18 +25,6 @@
}"
/>
</SkeletonGeneric>
- <noscript>
- <DecoratedPrice
- v-if="product.price"
- :regular="formatPrice(product.price.value)"
- :special="product.price.isDiscounted ? formatPrice(product.price.regularPrice) : undefined"
- class="mt-1"
- :class-name-variants="{
- regular: 'typography-headline-2',
- special: 'text-base',
- }"
- />
- </noscript>
<div class="inline-flex items-center mt-4 mb-2">
<SfRating size="sm" :value="product.rating?.average ?? 0" :max="5" />
<SfCounter class="ml-1" size="xs">{{ product.rating?.count ?? 0 }}</SfCounter>
./apps/storefront-unified-nuxt/composables/useLazyProduct/types.ts
@@ -17,5 +17,5 @@ export interface UseLazyLoadProduct {
loading: Readonly<Ref<boolean>>;
}
-export type LazyLoadFnReturn = Partial<SfProduct>;
-export type LazyLoadFn = (id: string, sku?: string) => Promise<LazyLoadFnReturn>;
+export type ProductLazyLoadFnReturn = Partial<SfProduct>;
+export type ProductLazyLoadFn = (id: string, sku?: string) => Promise<ProductLazyLoadFnReturn>;
./apps/storefront-unified-nuxt/composables/useLazyProduct/useLazyProduct.ts
@@ -1,8 +1,9 @@
import { toRefs } from '@vueuse/shared';
-
-import type { LazyLoadFn, UseLazyProductState } from '@/composables/useLazyProduct/types';
+import type { ProductLazyLoadFn } from '@/composables/useLazyProduct/types';
import type { FetchProduct } from '@/composables/useProduct/types';
+import type { UseLazyProductState } from './types';
+
/**
* Default lazy load function for fetching additional product data.
*/
@@ -32,7 +33,7 @@ const defaultLazyLoadProductFn = async (id: string, sku?: string) => {
* await fetchProduct();
* const productName = product.value?.name;
*/
-export const useLazyProduct = (id: string, sku?: string, lazyLoadFn: LazyLoadFn = defaultLazyLoadProductFn) => {
+export const useLazyProduct = (id: string, sku?: string, lazyLoadFn: ProductLazyLoadFn = defaultLazyLoadProductFn) => {
const parent = useProduct(id, sku);
const state = useState<UseLazyProductState>(`useLazyProduct-${id}-${sku}`, () => ({
data: parent.data.value,
./apps/storefront-unified-nuxt/composables/useLazySearchProducts/types.ts
@@ -11,5 +11,5 @@ export interface UseLazySearchProducts extends UseSearchProducts {
lazyLoading: Readonly<Ref<boolean>>;
}
-export type LazyLoadFnReturn = Partial<SfProductCatalogItem[]>;
-export type LazyLoadFn = (args: unknown) => Promise<LazyLoadFnReturn>;
+export type SearchProductsLazyLoadFnReturn = Partial<SfProductCatalogItem[]>;
+export type SearchProductsLazyLoadFn = (args: unknown) => Promise<SearchProductsLazyLoadFnReturn>;
./apps/storefront-unified-nuxt/layouts/checkout.vue
@@ -20,10 +20,12 @@
<template #suffix><SfIconChevronRight /></template>{{ backLabelDesktop }}
</SfButton>
</div>
- <span v-if="loading" class="!flex justify-center my-40 h-24">
- <SfLoaderCircular size="3xl" />
- </span>
- <slot v-else />
+ <ClientOnly>
+ <span v-if="loading" class="!flex justify-center my-40 h-24">
+ <SfLoaderCircular size="3xl" />
+ </span>
+ <slot v-else />
+ </ClientOnly>
</div>
</NarrowContainer>
</main>
1.0.2
Patch Changes
- FIXED Version field in generated project package.json. Previously, the
versionfield in generated project package.json was incorrectly set to@alokai/ecosystem@1.0.1instead of just1.0.1. Now, the version field now correctly shows just the version number (e.g.,1.0.1) as expected.
1.0.1
Patch Changes
FIXED
- Versions of dependencies in modules, now using latest stable versions instead of rc versions
1.0.0
Before starting the migration, please update all the dependencies to the compatible versions based on the compatibility matrix.
Major Changes
- CHANGED Updated the package for compatibility with Node.js 22.
- CHANGED Updated the enterprise registry to
npm.alokai.cloud. - CHANGED Updates to SDK and frontend apps. Now, every sdk request should contain
x-alokai-localeheader instead ofvsf-localecookie. This change is important in CDN support, as we cannot pass cookies to the CDN. - CHANGED Replaced core dependencies with a new
@alokai/connectpackage.@vue-storefront/middleware,@vue-storefront/sdk,vue-storefront/logger,vue-storefront/unified-data-model,@vue-storefront/multistorewere replaced with@alokai/connect. The replacement preserves the same functionality and interface as the original packages. - CHANGED The
@vue-storefront/multistorepackage has been renamed to@alokai/connect/config-switcher. If you were using the multistore functionality, you'll need to update your code to use the new package.
Minor Changes
- ADDED Module-based SDK configuration with
defineSdkModuleand enhanceddefineSdkConfigin@vue-storefront/nuxt. This new approach allows for better code organization and reusability of SDK modules. Before, all modules were defined inline in the config file. Now, you can extract them to separate files. - ADDED
createAlokaiMiddlewareto@vue-storefront/next- a wrapper for a Next.js middleware which registers the Alokai-specific logic. Example usage
// apps/storefront-unified-nextjs/middleware.ts
import { createAlokaiMiddleware } from "@vue-storefront/next";
export default createAlokaiMiddleware(async (request) => {
// your middleware logic
});
- ADDED to
@vue-storefront/nextgetPathnameFromRequestHeadersfunction which allows getting pathname from the request headers during the server side rendering. It requirescreateAlokaiMiddlewareto be used in themiddleware.tsfile. - ADDED
getConfigSwitcherHeadertodefaultRequestConfigin themiddlewareModuleof@alokai/connect/sdk. It allows setting thex-alokai-middleware-config-idheader on the Storefront side for the Config Switcher extension.
Example usage:
export function getSdkConfig() {
return defineSdkConfig(
({ buildModule, config, getRequestHeaders, middlewareModule }) => ({
commerce: buildModule(middlewareModule<CommerceEndpoints>, {
apiUrl: `${config.apiUrl}/commerce`,
cdnCacheBustingId: config.cdnCacheBustingId,
defaultRequestConfig: {
getConfigSwitcherHeader: (computedHeaders) => "some-value",
headers: getRequestHeaders(),
},
ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
}),
);
}
- ADDED New feature for the middleware app. Now, for every request, the middleware will convert the
x-alokai-localeheader intovsf-localecookie. This change is important in CDN support, as we cannot pass cookies to the CDN. The feature can also be implemented in the projects that are NOT using@alokai/connectpackage, by adding an extension to each registered integration:
- Install
@alokai/cookies-bridgepackage:
yarn add @alokai/cookies-bridge
- Add the extension to the integration configuration:
// Only in projects that are NOT using `@alokai/connect` package.
+ import { createCookiesBridgeExtension } from "@alokai/cookies-bridge";
+ const cookiesBridgeExtension = createCookiesBridgeExtension([{
+ header: 'x-alokai-locale',
+ cookie: 'vsf-locale',
+ }]);
export const config = {
configuration: {
// ...
}
extensions: (extensions: ApiClientExtension[]) => [
+ cookiesBridgeExtension,
...extensions,
unifiedApiExtension,
cdnExtension,
...(IS_MULTISTORE_ENABLED === 'true' ? [configSwitcherExtensionFactory()] : []),
],
};
Note: The extension should be added as first in the extensions list, as it should be executed before any other extension.
In some projects, the middleware configuration might be placed in middleware.config.ts file, but implementation is the same.
- ADDED Added a way to pass playwright test options in Alokai CLI through the
--playwright-options(-p) flag in thestore testcommand.
alokai-cli store test --playwright-options="--project=nuxt-desktop --headed"
- ADDED
alokai store removecommand.
Patch Changes
- CHANGED The maximum file upload size limit has been increased to 20MB.
- CHANGED Updated the
expressversion to^4.20.0to fix security vulnerabilities. See more in GitHub Vulnerability Alert. - FIXED Fixed randomly failing integration tests when running them for multiple stores using the
store testcommand. The issue was due to tests running in parallel through Turbo's pipeline. From now on, when running tests for multiple stores (using the--allflag or passing an array of store IDs via the--store-idflag), the tests will be run for each store consecutively. - FIXED Issue with
jw-paginateon cart page in Nuxt storefront. Previously, it was throwing aSyntaxError: The requested module '/node_modules/jw-paginate/lib/jw-paginate.js?v=5a45ef3c' does not provide an export named 'default'error. Now, it is fixed by addingjw-paginateto theoptimizeDeps.includeconfig innuxt.config.ts.
Migration guide
- Update the registry in your project:
- Step 1: Update the registry in your project:
+ @vsf-enterprise:registry=https://npm.alokai.cloud
+ @alokai:registry=https://npm.alokai.cloud
- @vsf-enterprise:registry=https://registrynpm.storefrontcloud.io
- @alokai:registry=https://registrynpm.storefrontcloud.io
auto-install-peers=true
- Step 2: Login to the enterprise registry:
npm login --registry=https://npm.alokai.cloud
- Update your Next application to use the new
x-alokai-localeheader instead of thevsf-localecookie.
The changes below are only relevant for Next apps. If you are using Nuxt, you can skip this section.
- Step 1: Update the
storefront-unified-nextjs/sdk/index.tsfile to pass current locale into SDK instance:
+import { getLocale as nextIntlGetLocale } from 'next-intl/server';
import { getSdk as getUniversalSdk } from './sdk.server';
/**
* A dedicated function to get the SDK instance on the server side.
* You can import it in React Server Components, Middleware, Route Handlers.
*/
-export const getSdk = () =>
+export const getSdk = async ({ getLocale = nextIntlGetLocale } = {}) => {
- getUniversalSdk({
+ const locale = await getLocale();
+ return getUniversalSdk({
+ getLocale: () => locale,
+ getRequestHeaders: () => ({
+ cookies: cookies(),
+ headers: headers(),
+ }),
+ });
+};
- Step 2: Update
apps/storefront-unified-nextjs/middleware.ts
export default async function middleware(request: NextRequest) {
// ... existing code
- const authRedirectPath = await getAuthRedirectPath(request);
+ const authRedirectPath = await getAuthRedirectPath(request, { locale });
if (authRedirectPath) {
response = NextResponse.redirect(new URL(`/${locale}${authRedirectPath}`, request.nextUrl));
}
// ...existing code
}
-async function getAuthRedirectPath(request: NextRequest) {
+async function getAuthRedirectPath(request: NextRequest, config: { locale: string }) {
- const sdk = getSdk();
+ const sdk = await getSdk({ getLocale: async () => config.locale });
- Step 3: Update every place that fetches data on the server-side to wait for the promise returned by
getSdk. For example:
- const sdk = getSdk();
+ const sdk = await getSdk();
const currencies = await sdk.unified.getCurrencies();
If you're using SDK directly in a destructuring pattern like this: const { products } = getSdk().unified.getProducts({ skus });, you should first assign the resolved sdk to a variable:
const sdk = await getSdk();
const { products } = await sdk.unified.getProducts({ skus });
- Step 4: Update
storefront-unified-nextjs/components/providers.tsx:
- sdk={getSdk()}
+ sdk={getSdk({
+ getLocale: () => locale,
+ })}
- Update your Nuxt application to use the new
x-alokai-localeheader instead of thevsf-localecookie.
The changes below are only relevant for Nuxt apps. If you are using Next.js, you can skip this section.
- Step 1: Remove unused cookie name from
storefront-unified-nuxt/nuxt.config.ts:
cookieNames: {
currency: 'vsf-currency',
- locale: 'vsf-locale',
},
- Step 2: Update
storefront-unified-nuxt/localization.tsto stop setting locale cookie:
import type { SfLocale } from '~/types';
-import { type H3Event, parseCookies, setCookie as setResponseCookie } from 'h3';
-
-function setRequestCookie(event: H3Event, name: string, value: string) {
- const { headers } = event.node.req;
- const cookiesObject = {
- ...parseCookies(event),
- [name]: value,
- };
-
- try {
- headers.cookie = Object.entries(cookiesObject)
- .map((item) => `${encodeURIComponent(item[0])}=${encodeURIComponent(item[1])}`)
- .join('; ');
- } catch {
- throw new Error(`Failed to set cookie ${name}=${value}`);
- }
-}
-function setCookie(event: H3Event, name: string, value: string) {
- setRequestCookie(event, name, value);
- setResponseCookie(event, name, value, {
- sameSite: 'strict',
- secure: true,
- });
-}
export default defineNuxtPlugin((nuxtApp) => {
if (import.meta.client) {
return;
}
const { locale: i18nLocale, locales: i18nLocales } = (nuxtApp as unknown as { $i18n: Composer }).$i18n;
- const { cookieNames } = useAppConfig();
const event = useRequestEvent();
const { locale, locales } = storeToRefs(useSfState());
if (!event) {
return;
}
- setCookie(event, cookieNames.locale, i18nLocale.value as SfLocale);
-
locale.value = i18nLocale.value as SfLocale;
// ...
});
- Step 3: Update
storefront-unified-nuxt/composables/useLocation/useLocation.tsto stop using locale cookie:
export const useLocation: UseLocationReturn = () => {
- const { cookieNames } = useAppConfig();
const { locale } = storeToRefs(useSfState());
const { locale: i18nLocale } = useI18n();
- const cookie = useCookie(cookieNames.locale, {
- default: () => i18nLocale.value,
- sameSite: 'strict',
- secure: true,
- });
whenever(i18nLocale, () => {
locale.value = i18nLocale.value as SfLocale;
- cookie.value = i18nLocale.value;
});
// ...
};
- Add
jw-paginateto theoptimizeDeps.includeconfig innuxt.config.ts:
This step is only relevant for Nuxt storefront in dev mode. If you are using Next.js, you can skip this step.
optimizeDeps: {
include: [
'@storefront-ui/vue',
'@storefront-ui/shared',
'@alokai/connect/logger',
'vue3-lazy-hydration',
+ 'jw-paginate',
],
},
- Use
@alokai/connectpackage instead of@vue-storefront/middleware,@vue-storefront/sdk,vue-storefront/logger,vue-storefront/unified-data-model,@vue-storefront/multistorepackages.
In this guide, we'll walk you through the process of migrating from the legacy packages to the new @alokai/connect package. This migration guide is designed to help you transition smoothly and take advantage of the new features and improvements in the @alokai/connect package.
- Update
packages/tailwind-config/src/nuxt.ts. Please remember to runyarn build:packagesafter modifyingpackagesdir.
import { tailwindConfig } from "@storefront-ui/react/tailwind-config";
import sfTypography from "@storefront-ui/typography";
import type { Config } from "tailwindcss";
export default {
content: [],
corePlugins: {
preflight: true,
},
plugins: [sfTypography],
presets: [tailwindConfig],
theme: {
extend: {
screens: {
"2xl": "1366px",
"2xs": "360px",
"3xl": "1536px",
"4xl": "1920px",
lg: "1024px",
md: "768px",
sm: "640px",
xl: "1280px",
xs: "376px",
},
},
},
} as Config;
Important notes:
- Interfaces of packages did not change, so you can expect the same names, methods, and behavior as before.
- The
@vue-storefront/multistorepackage has been renamed to@alokai/connect/config-switcher. You can use the new package to switch between different store configurations at runtime. - Some functions were moved to the
@alokai/connect/integration-kitpackage. Be sure to import them from the correct package. You can think aboutintegration-kitas a place for all utilities related to creating new integrations. - Functions moved from
@vue-storefront/middleware:apiClientFactory
- Functions moved from
@vue-storefront/unified-data-model:validatePasswordunifiedExtensionFactorycreateUnifiedCmsExtensiongetNormalizersassignToNormalizerContextmergeNormalizersgetApiDefinitionsdefineAddCustomFieldsFactorytoContextualizedNormalizers
- All other functions remain in their respective packages.
- All types related with moved functions are also moved to the
@alokai/connect/integration-kitpackage. - Data models and unified methods declarations are now part of the root
@alokai/connectpackage.
Migration steps:
- Step 1: Install the new package.
Navigate to your storefront-unified-<framework> and storefront-middleware apps and install the new package:
# Using yarn
yarn add @alokai/connect
# Using pnpm
pnpm add @alokai/connect
# Using npm
npm install @alokai/connect
- Step 2: Remove the legacy packages
Navigate to your storefront-unified-<framework> remove the legacy packages:
yarn remove @vue-storefront/sdk
Then, navigate to your storefront-middleware app and remove the legacy packages:
yarn remove @vue-storefront/middleware
If you are using @vue-storefront/unified-data-model or @vue-storefront/logger in your project, you need to remove them as well.
yarn remove @vue-storefront/multistore
yarn remove @vue-storefront/unified-data-model
yarn remove @vue-storefront/logger
- Step 3: Update your imports
Find all imports of the legacy packages in your project and update them to import from the new @alokai/connect package. For example:
- import { createApiClient } from "@vue-storefront/middleware";
+ import { createApiClient } from "@alokai/connect/integration-kit";
- import { createLogger } from "@vue-storefront/logger";
+ import { createLogger } from "@alokai/connect/logger";
- Step 4: Update your code
Review your codebase and update any references to the legacy packages. Make sure to test your application thoroughly to ensure that everything works as expected. Because of the functional equivalence between the legacy and new packages, you should not encounter any breaking changes.
- Migrate from
@vue-storefront/multistoreto@alokai/connect/config-switcher
This section is only relevant if you were using the @vue-storefront/multistore package in your project. If you weren't using multistore functionality, you can skip this section.
- Step 1: Update your imports
- import { createMultistoreExtension } from "@vue-storefront/multistore";
+ import { createConfigSwitcherExtension } from "@alokai/connect/config-switcher";
- Step 2: Update your configuration file
Rename the multistore.config.ts file to configSwitcher.config.ts and update the imports to import from the new @alokai/connect/config-switcher package.
+ import { createConfigSwitcherExtension } from "@alokai/connect/config-switcher";
- export const multistoreConfig = {
- async fetchConfiguration(/* { domain } */) {
- return {
- "my-apparel-domain.io": {
- baseSiteId: "apparel-uk",
- defaultCurrency: "GBP",
- // ...
- },
- "my-electronics-domain.io": {
- baseSiteId: "electronics",
- defaultCurrency: "USD",
- // ...
- },
- };
- },
- mergeConfigurations({ baseConfig, storeConfig }) {
- // ...
- },
- cacheManagerFactory() {
- // ...
- },
- };
+ export const configSwitcherExtension = createConfigSwitcherExtension({
+ // Static configuration
+ configuration: {
+ "my-apparel-domain.io": {
+ api: {
+ baseSiteId: "apparel-uk",
+ defaultCurrency: "GBP",
+ // ...
+ },
+ },
+ "my-electronics-domain.io": {
+ api: {
+ baseSiteId: "electronics",
+ defaultCurrency: "USD",
+ // ...
+ },
+ },
+ },
+ // Use domain strategy to match the previous behavior
+ // The old multistore package only supported domain-based switching
+ switchStrategy: "domain",
+ // Set cache TTL (in seconds) - default is 10
+ cacheTTL: 10,
+ });
Then move the file to the integrations/<integrationName>/extensions/configSwitcher.ts directory.
For dynamic configurations that were previously fetched in the fetchConfiguration method, you can use an async function:
export const configSwitcherExtension = createConfigSwitcherExtension({
// Asynchronously fetch configurations from an external source
// Returns a Record<ConfigId, Configuration> mapping config IDs to their settings
configuration: async () => {
const response = await fetch(`https://api.example.com/configs`);
return await response.json();
},
switchStrategy: "domain",
cacheTTL: 10,
});
- Step 3: Update your integration configuration
This example uses the SAPCC integration, but the same principles apply to all other integrations like Magento, BigCommerce, Commercetools, etc.
import { AUTH_USER_TOKEN_COOKIE_NAME, type MiddlewareConfig } from '@vsf-enterprise/sapcc-api';
import type { ApiClientExtension, Integration } from '@vue-storefront/middleware';
- import { multistoreExtensionFactory } from '../../multistore/utils';
+ import { configSwitcherExtension } from './extensions/configSwitcher';
import { cdnExtension, customExtension, unifiedApiExtension } from './extensions';
const {
- IS_MULTISTORE_ENABLED,
NODE_ENV,
SAPCC_API_URI,
SAPCC_OAUTH_CLIENT_ID,
SAPCC_OAUTH_CLIENT_SECRET,
SAPCC_OAUTH_TOKEN_ENDPOINT,
SAPCC_OAUTH_TOKEN_REVOKE_ENDPOINT,
SAPCC_OAUTH_URI,
} = process.env;
export const config = {
configuration: {
// integration config
},
extensions: (extensions: ApiClientExtension[]) => [
...extensions,
unifiedApiExtension,
cdnExtension,
- ...(IS_MULTISTORE_ENABLED === 'true' ? [multistoreExtensionFactory()] : []),
+ configSwitcherExtension,
customExtension,
],
location: '@vsf-enterprise/sapcc-api/server',
} satisfies Integration<Config>;
Configuration Merging
The new package automatically handles configuration merging, so you no longer need to implement the mergeConfigurations method. The base configuration for your integration is automatically deep-merged with the store-specific configuration.
- Step 4: Remove old files
- You can now remove the
apps/storefront-middleware/multistoredirectory, as it won't be needed anymore. - Update the app bootstrapping in
apps/storefront-middleware/src/index.ts
import {
createServer,
type CreateServerOptions,
} from "@alokai/connect/middleware";
import { config } from "../middleware.config";
const developmentCorsConfig: CreateServerOptions["cors"] = {
credentials: true,
origin: true,
};
const port = Number(process.env.API_PORT) || 4000;
runApp();
async function runApp() {
const app = await createServer(config, {
cors:
process.env.NODE_ENV === "production" ? undefined : developmentCorsConfig,
});
app.listen(port, "", () => {
console.log(`API server listening on port ${port}`);
});
}
- Remove
multistore:devscript fromapps/storefront-middleware/package.json.
- Step 5: (Optional) Set up caching
The new package includes built-in caching with a configurable TTL, so you no longer need to implement the cacheManagerFactory method. By default, configurations are cached for 10 seconds, but you can adjust this by setting the cacheTTL parameter:
createConfigSwitcherExtension({
configuration: {
/* ... */
},
cacheTTL: 60, // Cache for 60 seconds
});
- Use Node.js version 22.14.0 or higher
Use Node.js version 22.14.0 or higher for optimal performance, security, and compatibility. While Node.js 20 is technically supported, it is not recommended as it may cause compatibility issues with certain packages and has not been thoroughly tested.
- Step 1: Update your
.nvmrcor.node-versionfile to22.14.0
- 18.17.1
+ 22.14.0
- Step 2: Upgrade
@types/nodeto version^22.13.17for compatibility with the latest Node.js features.
- "@types/node": "18.x",
+ "@types/node": "22.13.17",
- Step 3: Update CI pipeline to use Node.js version
22.14.0or higher.
- (Optional) Separate SDK modules to separate files
- Step 1: Create new directory
modulesin thesdkdirectory of the storefront app. - Step 2: Create new file for each module.
// Define reusable module
const unified = defineSdkModule(
({ buildModule, config, getRequestHeaders, middlewareModule }) =>
buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: `${config.apiUrl}/commerce/unified`,
ssrApiUrl: `${config.ssrApiUrl}/commerce/unified`,
cdnCacheBustingId: config.cdnCacheBustingId,
defaultRequestConfig: {
getConfigSwitcherHeader,
headers: getRequestHeaders(),
},
methodsRequestConfig:
config.defaultMethodsRequestConfig.unifiedCommerce.middlewareModule,
}),
);
- Step 3: Create a new file
sdk/modules/index.tsthat exports all modules.
export * from "./unified";
- Step 4: Update your SDK configuration file to import the modules.
+ import * as modules from "./modules";
+ import * as modules from '@/sdk/modules';
export function getSdkConfig() {
- return defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => ({
- // ...
- }));
+ return defineSdkConfig(modules);
}
0.2.0
Minor Changes
- CHANGED In both
@vue-storefront/nextand@vue-storefront/nuxt, theconfigparameter available in the callback passed todefineSdkConfig()no longer contains computedmiddlewareUrlproperty. It now exposesapiUrlandssrApiUrlthat should be used instead. Read the migration guides for this Storefront version to see examples of usingapiUrlandssrApiUrlin your SDK configuration files. This change is necessary to support custom domain setup OOTB. - CHANGED Storefronts has been update to be compliant with the European Accessibility Act (EAA) and Web Content Accessibility Guidelines (WCAG) 2.1 AA.
- ADDED Multistore capabilities with shared configuration packages
- ADDED File-based inheritance system for efficient code sharing across stores
- ADDED Centralized configuration management for ESLint, Prettier, Tailwind, and lint-staged
- CHANGED Project structure to support multiple stores with shared packages
- CHANGED Turbo configuration updated to version 2.4.4 with new tasks format
- ADDED Alokai CLI integration for store management
- ADDED CI/CD pipeline setup for multistore deployments
Migration guide (Multistore)
To migrate your existing project to the new multistore setup, follow the multistore migration guide. Your Alokai Solution Architect can help you with the migration process.
Migration guide (Custom domains setup OOTB - Next.js)
The changes below are only relevant for Next apps. If you are using Nuxt, you can skip this section.
- Update environment variables
In the apps/storefront-unified-nextjs/sdk/options.ts file, verify the NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL environment variable exists:
const ssrApiUrl = env('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL');
+ if (!ssrApiUrl) {
+ throw new Error('NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL is required to run the app');
+ }
See our guide on initializing the SDK for a complete version of the file.
Also, make sure the environment variable is added to the apps/storefront-unified-nextjs/.env.example file and - if it exists - the apps/storefront-unified-nextjs/.env file:
+ NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL="http://localhost:4000"
- Update
@vue-storefront/nextversion
In apps/storefront-unified-nextjs/package.json, upgrade the version of @vue-storefront/next package to 5.1.0.
{
"dependencies": {
- "@vue-storefront/next": "4.3.2",
+ "@vue-storefront/next": "5.1.0",
}
}
remember to reinstall the dependencies of your project with the yarn install command afterwards.
- Update SDK configuration file
Update your SDK configuration file (apps/storefront-unified-nextjs/sdk/config.ts) so that registered modules use apiUrl and ssrApiUrl properties instead of middlewareUrl.
import { defineSdkConfig } from '@vue-storefront/next';
export function getSdkConfig() {
return defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => ({
commerce: buildModule(middlewareModule, {
- apiUrl: `${config.middlewareUrl}/commerce`,
+ apiUrl: `${config.apiUrl}/commerce`,
+ ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
}));
}
Migration guide (Custom domains setup OOTB - Nuxt)
- Update environment variables
Make sure the NUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL environment variable is added to the apps/storefront-unified-nuxt/.env.example file and - if it exists - the apps/storefront-unified-nuxt/.env file:
+ NUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL="http://localhost:4000"
- Update
@vue-storefront/nuxtversion
In apps/storefront-unified-nuxt/package.json, upgrade the version of @vue-storefront/nuxt package to 8.1.0.
{
"dependencies": {
- "@vue-storefront/nuxt": "7.0.0",
+ "@vue-storefront/nuxt": "8.1.0",
}
}
remember to reinstall the dependencies of your project with the yarn install command afterwards.
- Update SDK configuration file
Update your SDK configuration file (apps/storefront-unified-nuxt/sdk.config.ts) so that registered modules use apiUrl and ssrApiUrl properties instead of middlewareUrl.
export default defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => ({
commerce: buildModule(middlewareModule, {
- apiUrl: `${config.middlewareUrl}/commerce`,
+ apiUrl: `${config.apiUrl}/commerce`,
+ ssrApiUrl: `${config.ssrApiUrl}/commerce`,
}),
}));
0.1.0
Minor Changes
- ADDED Alokai ecosystem versioning: Each release of a core package now includes a version bump for the entire ecosystem, simplifying tracking of changes and ensuring compatibility between packages. Core packages include:
@vue-storefront/middleware@vue-storefront/multistore@vue-storefront/unified-data-model@vue-storefront/sdk@vue-storefront/nuxt@vue-storefront/nextCompatibility matrix for the entire ecosystem is available here.- CHANGED
@vue-storefront/nuxtversion updated tov7.0.0. - CHANGED from now on
getSdkis available instead ofsdkproperty on globalNuxtApp.$alokaiobject. - CHANGED You should now ensure that environment variables
NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL(for Next.js apps) andNUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL(for Nuxt apps) are set in your file both locally and in your deployed application.
Patch Changes
- FIXED Nuxt App -
useLazyProductcomposable merged product data wrongly. - FIXED Nuxt App - localization plugin need to set locale cookie directly in the plugin initialization.
- FIXED Next App - the initial page load with a locale in the URL, different from the previously set one, now correctly fetches data in the right language from the server.
Migration guide (Next.js)
The changes below are only relevant for Next apps. If you are using Nuxt, you can skip this section.
- Set the
NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URLenvironment variable in your.envfile.
+ NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL=http://localhost:4000
- Set the
NEXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URLenvironment variable tohttp://additional-app-middleware:4000in your deployed application. - Use
cookiesfromnext/headersin yourgetSdkfunction to fix the issue with the initial page load with a locale in the URL, different from the previously set one.
import {
headers,
+ cookies,
} from 'next/headers';
import { getSdk } from './sdk.server';
// ...
getSdk({
- getRequestHeaders: () => headers
+ getRequestHeaders: () => ({
+ headers: headers(),
+ cookies: cookies(),
+ }),
});
Migration guide (Nuxt)
The changes below are only relevant for Nuxt apps. If you are using Next.js, you can skip this section.
- Set the
NUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URLenvironment variable in your.envfile.
+ NUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URL=http://localhost:4000
- Set the
NUXT_PUBLIC_ALOKAI_MIDDLEWARE_SSR_API_URLenvironment variable tohttp://additional-app-middleware:4000in your deployed application. - Modify localization plugin to set locale cookie directly in the plugin initialization.
+ import type { Composer } from '#i18n';
// ...
- nuxtApp.hook('vue:setup', () => {
- const { locale: i18nLocale, locales: i18nLocales } = useI18n();
+ const { locale: i18nLocale, locales: i18nLocales } = (nuxtApp as unknown as { $i18n: Composer }).$i18n;
// ...
- });
- Update
@vue-storefront/nuxtpackage tov7.0.0.
- "@vue-storefront/nuxt": "6.x.x",
+ "@vue-storefront/nuxt": "7.0.0",
- Update the way you get the SDK instance.
// get sdk instance
- const { sdk } = useNuxtApp().$alokai;
+ const sdk = useNuxtApp().$alokai.getSdk();
// fetch some data
sdk.unified.getProducts();
Common issues
Despite following this migration guide, you might still experience issues in several areas of your project.
Nuxt Checkout module linting errors
Running the yarn lint command might result in errors caused by the Checkout module in the Nuxt Storefront. If that is the case in your project:
- In the
apps/storefront-unified-nuxt/sf-modules/checkout/components/Checkout/Checkout.vuefile disable the following eslint rule on line 1:
+ <!-- eslint-disable vue/multi-word-component-names -->
<template>
...
</template>
and the following eslint rule on line 151:
if (!termsAndConditions.value) {
validationMessages.push('validation.missingTermsAndConditions');
+ // eslint-disable-next-line vue/no-side-effects-in-computed-properties
invalidTermsAndConditions.value = true;
}
- In the
apps/storefront-unified-nuxt/sf-modules/checkout/pages/checkout.vuefile disable the following eslint rule on line 1:
+ <!-- eslint-disable vue/multi-word-component-names -->
<template>
...
</template>
Custom extension linting errors
Running the yarn lint command might result in errors caused by the custom extension in the Storefront Middleware app. If that is the case in your project, in the apps/storefront-middleware/api/custom-methods/custom.ts file disable the following eslint rule on line 1:
+ /* eslint-disable @typescript-eslint/no-unused-vars */
import { type IntegrationContext } from '../../types';
import type { CustomMethodArgs, CustomMethodResponse } from './types';
Nuxt and Middleware tsconfig.json linting errors
Running the yarn lint command might result in errors caused by the Typescript configuration files in the Nuxt Storefront and Storefront Middleware apps. If that is the case in your project, add new lines at the end of the apps/storefront-unified-nuxt/tsconfig.json and apps/storefront-middleware/tsconfig.json files.