### Install Packages and Run Project Source: https://crystallize.com/docs/developer/accelerators/product-configurator Install project dependencies using pnpm and start the development server. ```bash pnpm install & pnpm dev ``` -------------------------------- ### Install Project Dependencies Source: https://crystallize.com/docs/developer/accelerators/astro-boilerplate Run this command in both the root and client folders to install all required packages. ```bash npm install ``` -------------------------------- ### Installation Source: https://crystallize.com/docs/developer/sdk/js-api-client Install the Crystallize JS API Client using your preferred package manager. ```APIDOC pnpm add @crystallize/js-api-client # or npm install @crystallize/js-api-client # or yarn add @crystallize/js-api-client ``` -------------------------------- ### Install JS API Client Source: https://crystallize.com/docs/developer/sdk/js-api-client Install the JS API Client using your preferred package manager. ```bash pnpm add @crystallize/js-api-client # or npm install @crystallize/js-api-client # or yarn add @crystallize/js-api-client ``` ```bash pnpm add @crystallize/js-api-client # or npm install @crystallize/js-api-client # or yarn add @crystallize/js-api-client ``` -------------------------------- ### Quick Start Source: https://crystallize.com/docs/developer/sdk/js-api-client Initialize the client and make a catalogue API call. ```APIDOC import { createClient } from '@crystallize/js-api-client'; const api = createClient({ tenantIdentifier: 'furniture', // For protected APIs, provide credentials // accessTokenId: '…', // accessTokenSecret: '…', // staticAuthToken: '…', // and more }); // Call any GraphQL you already have (string query + variables) const { catalogue } = await api.catalogueApi( `query Q($path: String!, $language: String!) { catalogue(path: $path, language: $language) { name path } }`, { path: '/shop', language: 'en' }, ); // Don't forget to close when using HTTP/2 option (see below) api.close(); ``` -------------------------------- ### Install Crystallize Boilerplate Source: https://crystallize.com/docs/developer/accelerators/nextjs-ecommerce-template Use this command to install the Furnitut Next.js Boilerplate. Follow the wizard and select the appropriate option to clone the project and initialize a tenant. ```bash crystallize boilerplate install ``` -------------------------------- ### Configure Crystallize Tenant and Tokens Source: https://crystallize.com/docs/developer/accelerators/nextjs-ecommerce-template Set your Crystallize tenant identifier, tenant ID, and access tokens in the .env file. Use the provided example as a starting point. Remember to keep access tokens secret. ```env CRYSTALLIZE_TENANT_IDENTIFIER=furnitut CRYSTALLIZE_TENANT_ID=66d6f90eeeb7a544a0d94362 CRYSTALLIZE_ACCESS_TOKEN_ID=xXx CRYSTALLIZE_ACCESS_TOKEN_SECRET=xXx ``` -------------------------------- ### Install Crystallize Boilerplate Source: https://crystallize.com/docs/developer/cli Installs a Crystallize boilerplate project. Specify the target folder, tenant identifier, and boilerplate identifier. If identifiers are omitted, an interactive wizard will guide you. ```bash ~/crystallize boilerplate install [tenant-identifier] [boilerplate-identifier] ``` ```bash ~/crystallize boilerplate install [tenant-identifier] [boilerplate-identifier] ``` -------------------------------- ### Run Development Server Source: https://crystallize.com/docs/developer/accelerators/astro-boilerplate Start the development server to view the boilerplate locally. ```bash npm run dev ``` -------------------------------- ### Install React JS Components with NPM Source: https://crystallize.com/docs/developer/sdk/react-js-components Install the Crystallize React JS components library using NPM. This is the first step to using the provided React components. ```bash npm install @crystallize/reactjs-components ``` -------------------------------- ### Image Upload Example Output Source: https://crystallize.com/docs/developer/cli This is an example of the output generated when uploading images, showing the mapping of clean names to Crystallize keys and paths. ```json { "clean-name-png": "crystallize-key", "images-crystallize-png": "my-tenant/25/1/12/3435d2d0/images/crystallize.png" } ``` -------------------------------- ### Perform Company Search with Two API Source: https://crystallize.com/docs/developer/integrations/payment-gateways/two Construct a query string with 'limit', 'offset', and 'q' parameters to search for companies via the Two Search API. This example demonstrates a GET request to the Norway endpoint. ```javascript const query = new URLSearchParams({ limit: 'string', offset: 'string', q: 'string' }).toString(); const resp = await fetch( `https://no.search.two.inc/search?${query}`, {method: 'GET'} ); const data = await resp.text(); ``` -------------------------------- ### Run Project Locally Source: https://crystallize.com/docs/developer/accelerators/nextjs-subscription-commerce-starter Navigate to your project directory and use npm to start the local development server. This command assumes you are in the 'nerd-factory-boilerplate' folder. ```bash cd nerd-factory-boilerplate npm run dev ``` -------------------------------- ### Search Query Example Source: https://crystallize.com/docs/developer/apis/discovery-api/search This example demonstrates how to perform a search query across multiple shapes, using inline fragments to handle different item types like products, categories, and stories. ```APIDOC ## Search Query ### Description Retrieves search results across multiple shapes, handling different item types using inline fragments. ### Method POST ### Endpoint /discovery/search ### Parameters #### Query Parameters - **term** (String) - Required - The search term to query. - **options** (Object) - Optional - Search options, such as fuzziness. - **fuzzy** (Object) - Optional - Enables fuzzy matching. - **fuzziness** (Double) - Optional - The fuzziness level for fuzzy matching. ### Request Body ```graphql query SearchProducts($term: String!, $options: SearchOptions) { search(term: $term, options: $options) { hits { shape ... on product { name defaultVariant { name defaultPrice } } ... on category { name } ... on story { name } } } } ``` ### Response #### Success Response (200) - **hits** (Array) - An array of search results. - **shape** (String) - The shape of the item. - **name** (String) - The name of the item (if applicable). - **defaultVariant** (Object) - The default variant of a product (if applicable). - **name** (String) - The name of the default variant. - **defaultPrice** (Float) - The default price of the variant. #### Response Example ```json { "data": { "search": { "hits": [ { "shape": "product", "name": "Green T-Shirt", "defaultVariant": { "name": "Small", "defaultPrice": 19.99 } }, { "shape": "category", "name": "Apparel" }, { "shape": "story", "name": "New Arrivals" } ] } } } ``` ``` -------------------------------- ### Start Mass Operation Bulk Task Source: https://crystallize.com/docs/developer/mass-operations Manually start a bulk task that was previously created with `autoStart` set to `false`. ```APIDOC ## StartMassOperationBulkTask ### Description Manually initiates the execution of a mass operation bulk task. This is used when the task was created with `autoStart` set to `false`. ### Method `mutation` ### Parameters #### Input - **id** (ID!) - The unique identifier of the bulk task to start. ### Response #### Success Response (BulkTaskMassOperation) - **id** (ID!) - The identifier of the bulk task. - **status** (String) - The updated status of the bulk task (e.g., `RUNNING`). #### Error Response (BasicError) - **error** (String) - A description of the error. - **errorName** (String) - The name of the error. ### Example ```graphql mutation StartMassOperationBulkTask($id: ID!) { startMassOperationBulkTask(id: $id) { ... on BulkTaskMassOperation { id status } ... on BasicError { error errorName } } } ``` ``` -------------------------------- ### Run Local Development Server Source: https://crystallize.com/docs/developer/accelerators/nextjs-ecommerce-template Navigate to your project folder and use npm to start the local development server. This command initiates the Next.js development environment. ```bash cd nextjs-furnitut npm run dev ``` -------------------------------- ### Install React JS Components with Yarn Source: https://crystallize.com/docs/developer/sdk/react-js-components Install the Crystallize React JS components library using Yarn. This is an alternative to NPM for package management. ```bash yarn add @crystallize/reactjs-components ``` -------------------------------- ### Install Crystallize CLI Source: https://crystallize.com/docs/developer/cli Installs the Crystallize CLI using a curl script. This script detects your OS and downloads the latest version from GitHub. Review the open-source script for details. ```bash curl -LSs https://crystallizeapi.github.io/cli/install.bash | bash ``` ```bash curl -LSs https://crystallizeapi.github.io/cli/install.bash | bash ``` -------------------------------- ### Core API Error Handling Example Source: https://crystallize.com/docs/developer/apis/core-api Example demonstrating how to query for specific errors in the Core API. This shows how to handle cases where an order might not belong to the tenant. ```APIDOC { order(id: "ORDER_ID") { ... on Order { id } ...on OrderDoesNotBelongToTenantError { errorName message } } } ``` -------------------------------- ### Fetch a Document using GraphQL Source: https://crystallize.com/docs/developer/apis/catalogue-api/fetching-an-item Retrieve a document by its path using a GraphQL query. This example fetches basic information for a document. ```graphql query GetDocument { catalogue(language: "en", path: "/some/document/path") { id name type path } } ``` -------------------------------- ### Autocomplete Query Example Source: https://crystallize.com/docs/developer/apis/discovery-api/autocomplete This example demonstrates how to perform an autocomplete query to find items with names that partially match the term 'Pal'. ```APIDOC ## Autocomplete Query ### Description Performs a typeahead search, returning results based on partial matches of a word in item names. ### Method POST ### Endpoint /discovery/graphql ### Parameters #### Query Parameters - **term** (String) - Required - The partial string to search for. ### Request Body ```json { "query": "query Autocomplete($term: String!) { autocomplete(term: $term) { summary { totalHits } hits { name path } } }", "variables": { "term": "Pal" } } ``` ### Response #### Success Response (200) - **summary** (Object) - Contains information about the search results. - **totalHits** (Int) - The total number of matching items. - **hits** (Array) - A list of matching items. - **name** (String) - The name of the item. - **path** (String) - The path to the item. #### Response Example ```json { "data": { "autocomplete": { "summary": { "totalHits": 150 }, "hits": [ { "name": "Pale Blue Dot", "path": "/documents/astronomy/pale-blue-dot" }, { "name": "Palm Tree", "path": "/documents/botany/palm-tree" } ] } } } ``` ``` -------------------------------- ### Basic Catalogue Query Source: https://crystallize.com/docs/developer/apis/catalogue-api/querying-the-catalogue This example demonstrates a basic GraphQL query to fetch a folder and its immediate children from the catalogue. It specifies the language and path for the query. ```APIDOC ## Basic Query To fetch any item via the Catalogue API, the **catalogue** query root is used. In the example query below, we are retrieving a folder named **shop** and the names of the subfolders within it. ```graphql query HelloCrystallize{ catalogue(language: "en", path: "/shop") { name children { name path } } } ``` ``` -------------------------------- ### GetProducts Query Source: https://crystallize.com/docs/developer/apis/discovery-api An example GraphQL query to fetch product information including name, title, description, and topics. ```APIDOC ## GetProducts Query ### Description This query retrieves a list of products, including their name, title, description, and associated topics. ### Method POST ### Endpoint https://api.crystallize.com/_tenant-name_/discovery ### Request Body ```json { "query": "query GetProducts { browse { product { hits { name title description topics } } } }" } ``` ### Response #### Success Response (200) - **data** (object) - Contains the result of the query. - **browse** (object) - **product** (object) - **hits** (array of objects) - **name** (string) - **title** (string) - **description** (string) - **topics** (array of strings) ### Response Example ```json { "data": { "browse": { "product": { "hits": [ { "name": "Example Product Name", "title": "Example Product Title", "description": "This is an example product description.", "topics": ["topic1", "topic2"] } ] } } } } ``` ``` -------------------------------- ### Example Custom View URLs Source: https://crystallize.com/docs/pim/catalogue/custom-views These are example URLs for configuring Custom Views in Crystallize. They demonstrate how to use parameters like {{itemPath}}, {{language}}, and {{itemExternalReference}} to dynamically generate preview links. ```html https://my.shop.com/{{language}}/{{itemPath}} ``` ```html https://my.shop.com?item={{itemPath}} ``` ```html https://my.shop.com/{{itemPath}}?preview=true ``` ```html https://my.shop.com/api/preview?slug={{itemPath}}  (Next.js) ``` -------------------------------- ### Start Mass Operation Bulk Task Source: https://crystallize.com/docs/developer/mass-operations If `autoStart` was not enabled during task creation, use this GraphQL mutation to explicitly start a previously registered bulk task using its ID. ```graphql mutation StartMassOperationBulkTask($id: ID!) { startMassOperationBulkTask(id: $id) { ... on BulkTaskMassOperation { id status } ... on BasicError { error errorName } } } ``` -------------------------------- ### Fetch a Folder using GraphQL Source: https://crystallize.com/docs/developer/apis/catalogue-api/fetching-an-item Query the Catalogue API to fetch a folder by its path. This example demonstrates fetching the 'plants' folder from the demo tenant. ```graphql query GetFolder { catalogue(language: "en", path: "/shop/plants") { id name type path } } ``` -------------------------------- ### Search within Browse Query Source: https://crystallize.com/docs/developer/apis/discovery-api/browse This example demonstrates how to perform a search within a browse query, including options for fuzziness to allow for minor misspellings. ```APIDOC ## Search within Browse Query ### Description Performs a search within a browse query, allowing for exact matches or fuzzy matching with configurable fuzziness levels (NONE, SINGLE, DOUBLE). ### Method POST (or relevant GraphQL method) ### Endpoint [Discovery API Endpoint] ### Parameters #### Query Parameters - **term** (String) - Required - The search term to look for. - **options.fuzzy.fuzziness** (String) - Optional - Defines the fuzziness level: NONE, SINGLE, or DOUBLE. ### Request Example ```graphql query GetProducts{ browse{ product( term: "chair", options: {fuzzy: {fuzziness: DOUBLE} } ){ hits{ name title description variants{ name defaultPrice } } } } } ``` ### Response #### Success Response (200) - **hits** (Array) - Contains the search results. - **name** (String) - The name of the product. - **title** (String) - The title of the product. - **description** (String) - The description of the product. - **variants** (Array) - List of product variants. - **name** (String) - The name of the variant. - **defaultPrice** (Number) - The default price of the variant. #### Response Example ```json { "data": { "browse": { "product": { "hits": [ { "name": "Example Chair", "title": "Comfortable Office Chair", "description": "A chair designed for maximum comfort.", "variants": [ { "name": "Standard", "defaultPrice": 199.99 } ] } ] } } } } ``` ``` -------------------------------- ### Fetch Product Variants Associated with a Price List Source: https://crystallize.com/docs/developer/apis/catalogue-api/querying-price-list-info Retrieve a price list and its associated product variants. This example demonstrates pagination using `after` and `first` cursors. ```graphql query { priceList(identifier: "price-list-for-students") { tenantId identifier name modifierType products ( language: "en" after: "eyJfaWQiOiI2MWMzMTE5YWZjZDE1MDZmODdjYmQwOWMifQ==" #first: 2 ) { pageInfo { hasPreviousPage hasNextPage startCursor endCursor totalNodes } edges { cursor node { id type name variants { sku price priceVariants { identifier name currency price priceList (identifier: "price-list-for-students") { startDate endDate identifier price } } } } } } } } ``` ```graphql query { priceList(identifier: "price-list-for-students") { tenantId identifier name modifierType products ( language: "en" after: "eyJfaWQiOiI2MWMzMTE5YWZjZDE1MDZmODdjYmQwOWMifQ==" #first: 2 ) { pageInfo { hasPreviousPage hasNextPage startCursor endCursor totalNodes } edges { cursor node { id type name variants { sku price priceVariants { identifier name currency price priceList (identifier: "price-list-for-students") { startDate endDate identifier price } } } } } } } } ``` -------------------------------- ### Crystallize Signature Payload Example Source: https://crystallize.com/docs/developer/apis/signature-verification An example of a Crystallize signature payload, demonstrating the values for registered claims and additional data. ```json { "iss": "crystallize", "iat": 1516239022, "exp": 1516239022, "aud": "webhook", "sub": "signature", "tenantIdentifier": "my-awesome-tenant", "tenantId": "123", "userId": "123", "hmac": "1101b34dac8c55e5590a37271f1c41c3d745463854613494a1624a15be24f1f8" } ``` -------------------------------- ### Initialize and Use JS API Client Source: https://crystallize.com/docs/developer/sdk/js-api-client Initialize the client with your tenant identifier and optionally credentials for protected APIs. Then, use the client to make GraphQL calls. Remember to close the client when using HTTP/2 options. ```javascript import { createClient } from '@crystallize/js-api-client'; const api = createClient({ tenantIdentifier: 'furniture', // For protected APIs, provide credentials // accessTokenId: '…', // accessTokenSecret: '…', // staticAuthToken: '…', // and more }); // Call any GraphQL you already have (string query + variables) const { catalogue } = await api.catalogueApi( `query Q($path: String!, $language: String!) { catalogue(path: $path, language: $language) { name path } }`, { path: '/shop', language: 'en' }, ); // Don't forget to close when using HTTP/2 option (see below) api.close(); ``` ```javascript import { createClient } from '@crystallize/js-api-client'; const api = createClient({ tenantIdentifier: 'furniture', // For protected APIs, provide credentials // accessTokenId: '…', // accessTokenSecret: '…', // staticAuthToken: '…', // and more }); // Call any GraphQL you already have (string query + variables) const { catalogue } = await api.catalogueApi( `query Q($path: String!, $language: String!) { catalogue(path: $path, language: $language) { name path } }`, { path: '/shop', language: 'en' }, ); // Don't forget to close when using HTTP/2 option (see below) api.close(); ``` -------------------------------- ### Create a Crystallize Tenant Source: https://crystallize.com/docs/developer/cli Use this command to create a new tenant. It can run non-interactively if credentials are provided. The CLI will notify you if the identifier is already in use. ```bash ~/crystallize tenant create ``` -------------------------------- ### Subscription Contract Manager: Create Template and Contract Source: https://crystallize.com/docs/developer/sdk/js-api-client Illustrates creating a subscription contract template based on a variant and then creating a new contract using that template. ```javascript import { createSubscriptionContractManager } from '@crystallize/js-api-client'; const scm = createSubscriptionContractManager(api); const template = await scm.createTemplateBasedOnVariantIdentity( '/shop/my-product', 'SKU-1', 'plan-identifier', 'period-id', 'default', 'en', ); // …tweak template and create const created = await scm.create({ customerIdentifier: 'customer-123', tenantId: 'tenant-id', payment: { /* … */ }, ...template, }); ``` -------------------------------- ### Verify Webhook GET Request Signature Source: https://crystallize.com/docs/developer/apis/signature-verification When using webhooks with the GET method, provide the `webhookUrl` parameter to the `guard` function. This instructs the client to extract URL parameters for HMAC verification. ```typescript guard(signatureJwt, { url: request.url, // full URL here, including https:// etc. request.href in some framework webhookUrl: 'https://webhook.site/xxx', // the URL you have setup in the Webhook method: 'GET', }), ``` -------------------------------- ### Fetch a Product using GraphQL Source: https://crystallize.com/docs/developer/apis/catalogue-api/fetching-an-item Use this GraphQL query to fetch a specific product by its path. Ensure the language code is correct for your tenant. ```graphql query GetProduct { catalogue(language: "en", path: "/shop/plants/alocasia") { id name type path } } ``` -------------------------------- ### GraphQL Search Query Example Source: https://crystallize.com/docs/developer/apis/discovery-api/search This example demonstrates a GraphQL search query across multiple shapes, including 'product', 'category', and 'story'. It utilizes inline fragments to structure the response based on the shape of the returned items. The 'search' field takes a 'term' and 'options' argument, with 'fuzzy' option enabled for approximate matching. ```graphql query SearchProducts { search( term: "green", options: {fuzzy: {fuzziness: DOUBLE}}) { hits { shape ... on product { name defaultVariant{ name defaultPrice } } ... on category { name } ... on story { name } } } } ``` -------------------------------- ### Mass Operation File Format Example Source: https://crystallize.com/docs/developer/mass-operations This JSON structure defines a single 'piece/upsert' operation for creating or updating a piece with a 'singleLine' component. ```json { "version": "0.0.1", "operations": [ { "intent": "piece/upsert", "identifier": "rating-system", "name": "Rating System", "components": [ { "id": "name", "type": "singleLine", "singleLine": { "required": true } } ] } ] } ``` -------------------------------- ### Tenant URL Example Source: https://crystallize.com/docs/configuration/tenant This is the URL format for accessing a specific tenant in the Crystallize app. Replace 'your-tenant-identifier' with the actual unique identifier for your tenant. ```url https://app.crystallize.com/@your-tenant-identifier ``` -------------------------------- ### Order Manager: Register, Set Payments, and Pipeline Stage Source: https://crystallize.com/docs/developer/sdk/js-api-client Demonstrates registering a new order, updating its payment information, and moving it to a specific pipeline stage using the Order Manager. ```javascript import { createOrderManager } from '@crystallize/js-api-client'; const om = createOrderManager(api); // Register (minimal example) const confirmation = await om.register({ cart: [{ sku: 'SKU-1', name: 'Product', quantity: 1, price: { gross: 100, net: 80, currency: 'USD' } }], customer: { identifier: 'customer-123' }, }); // Update payments only await om.setPayments('order-id', [ { provider: 'STRIPE', amount: { gross: 100, net: 80, currency: 'USD' }, method: 'card', }, ]); // Put in pipeline stage await om.putInPipelineStage({ id: 'order-id', pipelineId: 'pipeline', stageId: 'stage' }); ``` -------------------------------- ### Creating a Crystallize API Client Source: https://crystallize.com/docs/developer/sdk/js-api-client Instantiate the Crystallize API client with configuration options. This includes setting up tenant identifiers, authentication credentials, and other client-specific settings. ```APIDOC ## createClient ### Description Initializes the Crystallize API client. ### Parameters #### configuration - **tenantIdentifier** (string) - Required - The identifier for your Crystallize tenant. - **tenantId** (string) - Optional - The ID of your Crystallize tenant. - **accessTokenId** (string) - Required if not using `staticAuthToken` or `shopApiToken` - The ID of the access token. - **accessTokenSecret** (string) - Required if not using `staticAuthToken` or `shopApiToken` - The secret for the access token. - **sessionId** (string) - Alternative to `accessTokenId` and `accessTokenSecret` for authentication. - **staticAuthToken** (string) - Optional - A static token for read-only catalogue/discovery access. - **shopApiToken** (object) - Optional - Token for shop API access. Can include `doNotFetch`, `scopes`, and `expiresIn`. - **shopApiStaging** (boolean) - Optional - Set to true to use the staging Shop API. - **origin** (string) - Optional - Custom host suffix for the API. Defaults to `.crystallize.com`. #### options - **useHttp2** (boolean) - Optional - Enable HTTP/2 transport. - **profiling** (object) - Optional - Callbacks for request profiling. - **onRequest** (function) - Callback when a request is made. - **onRequestResolved** (function) - Callback when a request is resolved. - **extraHeaders** (object) - Optional - Extra request headers for all calls. ### Example ```javascript import { createClient } from '@crystallize/js-api-client'; const client = createClient({ tenantIdentifier: 'your-tenant-identifier', // other configuration options... }, { // other options... }); ``` ``` -------------------------------- ### Autocomplete Query Example Source: https://crystallize.com/docs/developer/apis/discovery-api/discovery-queries Autocomplete queries are designed for partial matching, ideal for typeahead search or auto-completion of input fields, returning a small subset of data. ```graphql ``` -------------------------------- ### Upsert Customer, Register Order, and Update Item Component Source: https://crystallize.com/docs/developer/mass-operations This example demonstrates a sequence of operations: first, it upserts a customer, then registers an order referencing that customer with specific cart details and pricing, and finally updates a component of an existing item. This showcases how to link operations using identifiers. ```json { "version": "0.0.1", "operations": [ { "intent": "customer/upsert", "identifier": "customer-for-order-123", "firstName": "John", "lastName": "Doe", "type": "individual" }, { "intent": "order/register", "customer": { "identifier": "customer-for-order-123", "type": "individual" }, "additionalInformation": "Please deliver between 9am-5pm", "cart": [ { "sku": "SP-RED-001", "name": "Sample Product", "productId": "67e5d2d12d31ee752710a74b", "quantity": 2, "price": { "currency": "USD", "gross": 1000, "net": 800, "tax": { "name": "VAT", "percent": 20 } } } ] }, { "intent": "item/updateComponent/item", "itemId": "632958a35dfc2c90cbbad20d", "language": "en", "component": { "componentId": "title", "singleLine": { "text": "Mass operation updated title" } } } ] } ``` -------------------------------- ### Create and Use Navigation Fetcher Source: https://crystallize.com/docs/developer/sdk/js-api-client Use `createNavigationFetcher` to retrieve navigation trees. It supports fetching by folders with customizable depth and additional query parameters for each level. ```javascript import { createNavigationFetcher } from '@crystallize/js-api-client'; const nav = createNavigationFetcher(api); const tree = await nav.byFolders('/', 'en', 3, /* extra root-level query */ undefined, (level) => { if (level === 1) return { shape: { identifier: true } }; return {}; }); ```