From b3d548dd1f9e74fb520c9fcc619dd99ade1a7b22 Mon Sep 17 00:00:00 2001 From: Menelaos Nasies <38861573+MenKNas@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:09:24 +0200 Subject: [PATCH 01/32] Add atom schema utilities for CSDK (#407) * Add atom schema utilities * Simplify and trim down comments * Address comments * Change type import for schema utils * Add stricter types for customEvents * Improve typing * Adjust atom types to avoid errors --- .../content/api/content-sdk-content.api.md | 10 +- packages/core/api/content-sdk-core.api.md | 4 +- packages/nextjs/api/content-sdk-nextjs.api.md | 10 +- packages/nextjs/src/index.ts | 15 +- packages/react/api/content-sdk-react.api.md | 76 +- packages/react/package.json | 3 +- packages/react/src/atoms/createAtom.test.ts | 211 +++ packages/react/src/atoms/createAtom.ts | 80 + packages/react/src/atoms/index.ts | 13 + packages/react/src/atoms/schema-utils.ts | 62 + packages/react/src/atoms/types.ts | 59 + packages/react/src/index.ts | 15 + yarn.lock | 1432 +++++++++-------- 13 files changed, 1283 insertions(+), 707 deletions(-) create mode 100644 packages/react/src/atoms/createAtom.test.ts create mode 100644 packages/react/src/atoms/createAtom.ts create mode 100644 packages/react/src/atoms/index.ts create mode 100644 packages/react/src/atoms/schema-utils.ts create mode 100644 packages/react/src/atoms/types.ts diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 98951beead..7b2baa1528 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -373,7 +373,7 @@ export interface EditingRenderQueryParams { // @public export class EditingService { constructor(serviceConfig: EditingServiceConfig); - fetchEditingData({ itemId, language, version, layoutKind, mode }: EditingOptions, fetchOptions?: FetchOptions): Promise<{ + fetchEditingData(input: EditingOptions, fetchOptions?: FetchOptions): Promise<{ layoutData: LayoutServiceData; }>; protected getGraphQLClient(): GraphQLClient; @@ -512,7 +512,7 @@ export type GenerateMapArgs = { export type GenerateMapFunction = (args: GenerateMapArgs) => void; // @public -export const generateSites: ({ destinationPath }?: GenerateSitesConfig) => ((args: { +export const generateSites: (input?: GenerateSitesConfig) => ((args: { scConfig: SitecoreConfig; }) => Promise); @@ -540,10 +540,10 @@ export let getComponentList: typeof _getComponentList; // Warning: (ae-forgotten-export) The symbol "ComponentSpec" needs to be exported by the entry point api-surface.d.ts // // @internal -export const getComponentSpec: ({ componentId, edgeUrl, targetPath, token, }: GetComponentSpecParams) => Promise; +export const getComponentSpec: (input: GetComponentSpecParams) => Promise; // @internal -export const getComponentSpecUrl: ({ componentId, edgeUrl, targetPath, token, }: GetComponentSpecParams) => string; +export const getComponentSpecUrl: (input: GetComponentSpecParams) => string; // @internal export const getContentSdkPagesClientData: () => Record>; @@ -1361,7 +1361,7 @@ export const VARIANT_PREFIX = "_variantId_"; // Warning: (ae-incompatible-release-tags) The symbol "writeImportMap" is marked as @public, but its signature references "WriteImportMapArgsInternal" which is marked as @internal // // @public -export const writeImportMap: (args: WriteImportMapArgsInternal) => ({ scConfig }: { +export const writeImportMap: (args: WriteImportMapArgsInternal) => (input: { scConfig: SitecoreConfig; }) => Promise; diff --git a/packages/core/api/content-sdk-core.api.md b/packages/core/api/content-sdk-core.api.md index 4c241be854..ed2434c473 100644 --- a/packages/core/api/content-sdk-core.api.md +++ b/packages/core/api/content-sdk-core.api.md @@ -182,7 +182,7 @@ export function getCacheAndClean(key: string): T | undefined; export function getCoreContext(): CoreContext; // @public -export const getEnforcedCorsHeaders: ({ requestMethod, headers, presetCorsHeader, allowedOrigins, }: { +export const getEnforcedCorsHeaders: (input: { requestMethod: string | undefined; headers: IncomingHttpHeaders | Headers; presetCorsHeader?: string | string[]; @@ -204,7 +204,7 @@ export type GraphQLClientError = Partial & GenericGraphQLClientErro // @public export class GraphQLRequestClient implements GraphQLClient { constructor(endpoint: string, clientConfig?: GraphQLRequestClientConfig); - static createClientFactory({ endpoint, apiKey, contextId, }: GraphQLRequestClientFactoryConfig): GraphQLRequestClientFactory; + static createClientFactory(input: GraphQLRequestClientFactoryConfig): GraphQLRequestClientFactory; request(query: string | DocumentNode, variables?: { [key: string]: unknown; }, options?: FetchOptions): Promise; diff --git a/packages/nextjs/api/content-sdk-nextjs.api.md b/packages/nextjs/api/content-sdk-nextjs.api.md index 8cf3ea31d4..f9fb17f33b 100644 --- a/packages/nextjs/api/content-sdk-nextjs.api.md +++ b/packages/nextjs/api/content-sdk-nextjs.api.md @@ -269,7 +269,7 @@ export type ComponentPropsCollection = { }; // @public -export const ComponentPropsContext: ({ children, value, }: ComponentPropsContextProps) => JSX_2.Element; +export const ComponentPropsContext: (input: ComponentPropsContextProps) => JSX_2.Element; // @public export type ComponentPropsContextProps = { @@ -375,7 +375,7 @@ export { DesignLibrary } // Warning: (ae-forgotten-export) The symbol "DesingLibraryAppProps" needs to be exported by the entry point api-surface.d.ts // // @public -export const DesignLibraryApp: ({ page, componentMap, loadServerImportMap, }: DesingLibraryAppProps) => React_2.JSX.Element | null; +export const DesignLibraryApp: (input: DesingLibraryAppProps) => React_2.JSX.Element | null; export { DictionaryPhrases } @@ -708,14 +708,14 @@ export class PersonalizeProxy extends ProxyBase { // (undocumented) handle: (req: NextRequest, res: NextResponse) => Promise; // (undocumented) - protected initPersonalizeServer({ hostname, siteName, request, response, }: { + protected initPersonalizeServer(input: { hostname: string; siteName: string; request: NextRequest; response: NextResponse; }): Promise; // (undocumented) - protected personalize({ params, friendlyId, language, timeout, variantIds, geo, }: { + protected personalize(input: { params: ExperienceParams; friendlyId: string; language: string; @@ -973,7 +973,7 @@ export { withPlaceholder } export { withSitecore } // @public -export const writeImportMap: (args: WriteImportMapArgs) => ({ scConfig }: { +export const writeImportMap: (args: WriteImportMapArgs) => (input: { scConfig: SitecoreConfig_2; }) => Promise; diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 65524fe089..32768435b9 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -1,4 +1,4 @@ -export { default as debug } from './debug'; +export { default as debug } from './debug'; export { constants, @@ -163,6 +163,19 @@ export { AppPlaceholder, AppPlaceholderProps, renderEmptyPlaceholder, + createAtom, + withPropMeta, + withArgMeta, + getFieldMeta, + type AtomMetadata, + type AtomChild, + type DefaultChild, + type EditableComponentProps, + type CallbackPropKeys, + type CallbackArgZodTuple, + type PropMeta, + type ArgMeta, + type AtomSchemaInput, } from '@sitecore-content-sdk/react'; export { initContentSdk } from '@sitecore-content-sdk/core'; diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 3331ef010e..284aca0121 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -9,6 +9,7 @@ import { CacheOptions } from '@sitecore-content-sdk/core'; import { ClientError } from '@sitecore-content-sdk/core'; import { ComponentFields } from '@sitecore-content-sdk/content/layout'; import { ComponentParams } from '@sitecore-content-sdk/content/layout'; +import type { ComponentPropsWithoutRef } from 'react'; import { ComponentRendering } from '@sitecore-content-sdk/content/layout'; import { ComponentType } from 'react'; import { constants } from '@sitecore-content-sdk/core'; @@ -58,6 +59,7 @@ import { SearchParameters } from '@sitecore-content-sdk/search'; import { SitecoreConfig } from '@sitecore-content-sdk/content/config'; import { SitePathService } from '@sitecore-content-sdk/content/site'; import { SitePathServiceConfig } from '@sitecore-content-sdk/content/site'; +import { z } from 'zod'; // @public export const AppPlaceholder: (props: AppPlaceholderProps) => React_2.JSX.Element; @@ -65,6 +67,45 @@ export const AppPlaceholder: (props: AppPlaceholderProps) => React_2.JSX.Element // @public export type AppPlaceholderProps = Omit & Required>; +// @public +export type ArgMeta = { + argName: string; +}; + +// @public +export type AtomChild = AtomMetadata | 'text' | 'atom'; + +// @public +export type AtomMetadata = { + name: string; + version?: number; + type: 'atom' | 'atom-child'; + description: string; + props: z.ZodObject; + component: (props: unknown) => React.ReactNode; + htmlEvents?: string[]; + customEvents?: Record; + allowedChildren?: AtomChild[]; + defaultChildren?: DefaultChild[]; +}; + +// @public +export type AtomSchemaInput> = { + name: string; + description: string; + type?: 'atom' | 'atom-child'; + version?: number; + props: { + [K in keyof EditableComponentProps]?: z.ZodType[K]>; + }; + htmlEvents?: CallbackPropKeys>[]; + customEvents?: { + [K in CallbackPropKeys>]?: z.ZodType[]; + }; + allowedChildren?: AtomChild[]; + defaultChildren?: DefaultChild[]; +}; + // @public export class BYOCComponent extends React_2.Component { constructor(props: BYOCComponentProps); @@ -111,6 +152,11 @@ export { CacheClient } export { CacheOptions } +// @public +export type CallbackPropKeys = { + [K in keyof T & string]: NonNullable extends (...args: unknown[]) => unknown ? K : never; +}[keyof T & string]; + // @public export const ClientEditingChromesUpdate: () => JSX_2.Element; @@ -127,6 +173,9 @@ export { ComponentRendering } export { constants } +// @public +export function createAtom>(component: C, schema: AtomSchemaInput): AtomMetadata; + // @public export const DateField: React_2.FC; @@ -146,6 +195,12 @@ export interface DateFieldProps extends EditableFieldProps { export { debug_2 as debug } +// @public +export type DefaultChild = AtomMetadata | { + atom: AtomMetadata; + props?: Record; +}; + // @public export const DefaultEmptyFieldEditingComponentImage: React_2.FC<{ [key: string]: unknown; @@ -194,6 +249,9 @@ export type DynamicComponent = React.ComponentType<{ params?: ComponentParams; }>; +// @public +export type EditableComponentProps> = Omit, 'children'>; + // @public export const EditingScripts: () => React_2.JSX.Element; @@ -272,7 +330,7 @@ export interface FileField { // Warning: (ae-forgotten-export) The symbol "FormProps" needs to be exported by the entry point api-surface.d.ts // // @public -export const Form: ({ params, rendering }: FormProps) => React_2.JSX.Element; +export const Form: (input: FormProps) => React_2.JSX.Element; export { getChildPlaceholder } @@ -280,6 +338,9 @@ export { getContentStylesheetLink } export { getDesignLibraryStylesheetLinks } +// @public +export function getFieldMeta(schemaOrJsonSchema: z.ZodType | Record): Record | undefined; + export { getFieldValue } export { GraphQLClientError } @@ -410,7 +471,7 @@ export const Placeholder: (props: PlaceholderProps) => React_2.JSX.Element; // Warning: (ae-forgotten-export) The symbol "PlaceholderMetadataProps" needs to be exported by the entry point api-surface.d.ts // // @internal -export const PlaceholderMetadata: ({ rendering, placeholderName, children, componentRuntime, }: PlaceholderMetadataProps) => JSX_2.Element; +export const PlaceholderMetadata: (input: PlaceholderMetadataProps) => JSX_2.Element; // @public interface PlaceholderProps { @@ -443,6 +504,11 @@ interface PlaceholderProps { export { PlaceholderProps as PlaceholderComponentProps } export { PlaceholderProps } +// @public +export type PropMeta = { + control?: string; +}; + // @public export type ReactContentSdkComponent = (ComponentType | ReactModule) & { componentType?: 'server' | 'client' | 'universal'; @@ -580,6 +646,9 @@ export function useSitecore(options?: UseSitecoreOptions): SitecoreProviderState // @public export const withAppPlaceholder: (Component: ComponentType) => (props: W) => React_2.JSX.Element; +// @public +export function withArgMeta(schema: T, meta: ArgMeta): T; + // Warning: (ae-forgotten-export) The symbol "WithDatasourceCheckOptions" needs to be exported by the entry point api-surface.d.ts // Warning: (ae-forgotten-export) The symbol "WithDatasourceCheckProps" needs to be exported by the entry point api-surface.d.ts // @@ -612,6 +681,9 @@ export function withFieldMetadata(Component: ComponentType) => (props: W) => React_2.JSX.Element; +// @public +export function withPropMeta(schema: T, meta: PropMeta): T; + // Warning: (ae-forgotten-export) The symbol "WithSitecoreHocProps" needs to be exported by the entry point api-surface.d.ts // // @public @deprecated (undocumented) diff --git a/packages/react/package.json b/packages/react/package.json index 0b166572f2..74edcb13d6 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -80,7 +80,8 @@ "@sitecore-content-sdk/content": "2.0.0-canary.20", "@sitecore-content-sdk/core": "2.0.0-canary.20", "@sitecore-content-sdk/search": "0.2.0-canary.30", - "fast-deep-equal": "^3.1.3" + "fast-deep-equal": "^3.1.3", + "zod": "^4.0.0" }, "description": "", "types": "types/index.d.ts", diff --git a/packages/react/src/atoms/createAtom.test.ts b/packages/react/src/atoms/createAtom.test.ts new file mode 100644 index 0000000000..f59501b8df --- /dev/null +++ b/packages/react/src/atoms/createAtom.test.ts @@ -0,0 +1,211 @@ +import { expect } from 'chai'; +import { z } from 'zod'; +import { createAtom, withPropMeta, getFieldMeta } from './index'; + +describe('createAtom', () => { + const DummyComponent = (props: { variant?: string; size?: string }) => { + void props; + return null; + }; + + it('returns AtomMetadata with type "atom" when schema.type is omitted', () => { + const meta = createAtom(DummyComponent, { + name: 'Dummy', + description: 'A dummy atom', + props: { + variant: z.enum(['a', 'b']).optional().default('a'), + size: z.string().optional(), + }, + }); + expect(meta.type).to.equal('atom'); + expect(meta.name).to.equal('Dummy'); + expect(meta.description).to.equal('A dummy atom'); + expect(meta.props).to.be.instanceOf(z.ZodObject); + expect(meta.component).to.equal(DummyComponent); + }); + + it('returns AtomMetadata with type "atom-child" when schema.type is "atom-child"', () => { + const meta = createAtom(DummyComponent, { + name: 'DummyChild', + description: 'A dummy child', + type: 'atom-child', + props: {}, + }); + expect(meta.type).to.equal('atom-child'); + expect(meta.name).to.equal('DummyChild'); + }); + + it('includes htmlEvents and allowedChildren when provided', () => { + const Clickable = (props: { onClick?: () => void }) => { + void props; + return null; + }; + const meta = createAtom(Clickable, { + name: 'Clickable', + description: 'Clickable', + props: {}, + htmlEvents: ['onClick'], + allowedChildren: ['text'], + }); + expect(meta.htmlEvents).to.deep.equal(['onClick']); + expect(meta.allowedChildren).to.deep.equal(['text']); + }); + + it('builds props schema from schema.props', () => { + const meta = createAtom(DummyComponent, { + name: 'WithProps', + description: 'With props', + props: { + variant: z.enum(['x', 'y']).default('x'), + }, + }); + const parsed = meta.props.safeParse({ variant: 'y' }); + expect(parsed.success).to.equal(true); + if (parsed.success) { + expect(parsed.data.variant).to.equal('y'); + } + }); + + it('accepts withPropMeta in props', () => { + const meta = createAtom(DummyComponent, { + name: 'WithMeta', + description: 'With meta', + props: { + variant: withPropMeta(z.string().optional(), { control: 'text' }), + }, + }); + expect(meta.props).to.be.instanceOf(z.ZodObject); + }); + + it('getFieldMeta returns meta from Zod schema', () => { + const schema = withPropMeta(z.string(), { control: 'color' }); + const meta = getFieldMeta(schema); + expect(meta).to.deep.equal({ control: 'color' }); + }); + + describe('component scenarios (props only, no callbacks)', () => { + it('accepts component with only required props', () => { + const OnlyProps = (props: { title: string; count: number }) => null; + const meta = createAtom(OnlyProps, { + name: 'OnlyProps', + description: 'Props only', + props: { + title: z.string(), + count: z.number(), + }, + }); + expect(meta.name).to.equal('OnlyProps'); + const parsed = meta.props.safeParse({ title: 'Hi', count: 1 }); + expect(parsed.success).to.equal(true); + }); + + it('accepts component with optional props only', () => { + const OptionalOnly = (props: { tag?: string }) => null; + const meta = createAtom(OptionalOnly, { + name: 'OptionalOnly', + description: 'Optional', + props: { tag: z.string().optional() }, + }); + expect(meta.props.safeParse({})).to.have.property('success', true); + expect(meta.props.safeParse({ tag: 'x' }).success).to.equal(true); + }); + }); + + describe('customEvents (typed to callback parameters)', () => { + it('accepts component with explicit props (no ComponentType cast)', () => { + const Test = ({ + customEvent, + prop1, + prop2, + }: { + customEvent: (x: string, y: number) => void; + prop1: string; + prop2: number; + }) => null; + const meta = createAtom(Test, { + name: 'Test', + description: 'Test', + props: { + prop1: z.string(), + prop2: z.number(), + }, + customEvents: { + customEvent: [z.string(), z.number()], + }, + }); + expect(meta.name).to.equal('Test'); + expect(meta.customEvents?.customEvent).to.have.lengthOf(2); + }); + + it('accepts customEvents with tuple matching callback params (two args)', () => { + const WithSubmit = (props: { + onSubmit?: (name: string, count: number) => void; + }) => { + void props; + return null; + }; + const onSubmitSchemas = [z.string(), z.number()]; + const meta = createAtom(WithSubmit, { + name: 'WithSubmit', + description: 'With submit', + props: {}, + customEvents: { onSubmit: onSubmitSchemas }, + }); + expect(meta.customEvents?.onSubmit).to.equal(onSubmitSchemas); + }); + + it('accepts customEvents with no-arg callback (empty tuple)', () => { + const WithClick = (props: { onClick?: () => void }) => { + void props; + return null; + }; + const meta = createAtom(WithClick, { + name: 'WithClick', + description: 'With click', + props: {}, + customEvents: { + onClick: [], + }, + }); + expect(meta.customEvents).to.deep.equal({ onClick: [] }); + }); + + it('accepts customEvents with optional param (union with undefined)', () => { + const WithChange = (props: { + onChange?: (value: string, extra?: number) => void; + }) => { + void props; + return null; + }; + const meta = createAtom(WithChange, { + name: 'WithChange', + description: 'With change', + props: {}, + customEvents: { + onChange: [z.string(), z.number().optional()], + }, + }); + expect(meta.customEvents?.onChange).to.have.lengthOf(2); + }); + + it('passes through multiple customEvents', () => { + const Multi = (props: { + onA?: (x: string) => void; + onB?: (y: number) => void; + }) => { + void props; + return null; + }; + const onASchemas = [z.string()]; + const onBSchemas = [z.number()]; + const meta = createAtom(Multi, { + name: 'Multi', + description: 'Multi', + props: {}, + customEvents: { onA: onASchemas, onB: onBSchemas }, + }); + expect(meta.customEvents?.onA).to.equal(onASchemas); + expect(meta.customEvents?.onB).to.equal(onBSchemas); + }); + }); +}); diff --git a/packages/react/src/atoms/createAtom.ts b/packages/react/src/atoms/createAtom.ts new file mode 100644 index 0000000000..7f5dc6ea57 --- /dev/null +++ b/packages/react/src/atoms/createAtom.ts @@ -0,0 +1,80 @@ +/** Component-first atom/atom-child definition; schema.type differentiates. */ +import { z } from 'zod'; +import type { + AtomMetadata, + AtomChild, + DefaultChild, + EditableComponentProps, + CallbackPropKeys, + CallbackArgZodTuple, +} from './types'; + +/** + * Schema input for createAtom. Prop keys are restricted to the component's props excluding + * children and ref; event keys are restricted to callback props of the component. + * @public + */ +export type AtomSchemaInput = { + /** Unique identifier used as the element type in the DSL */ + name: string; + /** Human-readable summary for the component palette */ + description: string; + /** 'atom' (default) for top-level, 'atom-child' for scoped children */ + type?: 'atom' | 'atom-child'; + /** Optional version for schema evolution */ + version?: number; + /** Zod schemas for editable props (keys must be component props excluding children/ref) */ + props: { + [K in keyof EditableComponentProps]?: z.ZodType[K]>; + }; + /** DOM event handler prop names (e.g. onClick). Must be callback props. */ + htmlEvents?: CallbackPropKeys>[]; + /** Custom callback prop names to tuple of Zod types matching that callback's parameters. */ + customEvents?: { + [K in CallbackPropKeys>]?: CallbackArgZodTuple< + NonNullable[K]> + >; + }; + /** Allowed child types (atom-child metadata, 'text', or 'atom') */ + allowedChildren?: AtomChild[]; + /** Default children to insert when the atom is added */ + defaultChildren?: DefaultChild[]; +}; + +/** + * Create an atom or atom-child descriptor. The component is the first argument, the schema the + * second; schema.type selects 'atom' (default) or 'atom-child'. + * @param component - The React component that renders this atom + * @param schema - Name, description, type, props, events, and children rules + * @returns AtomMetadata with type taken from schema.type (default 'atom') + * @public + */ +export function createAtom( + component: C, + schema: AtomSchemaInput +): AtomMetadata { + const atomType = schema.type ?? 'atom'; + const propsShape = schema.props as Record; + const propsSchema = z.object(propsShape); + + const customEvents = + schema.customEvents && + Object.keys(schema.customEvents).length > 0 + ? (Object.fromEntries( + Object.entries(schema.customEvents).filter(([, v]) => v !== undefined) + ) as Record) + : undefined; + + return { + name: schema.name, + version: schema.version, + type: atomType, + description: schema.description, + props: propsSchema, + component: component as (props: unknown) => React.ReactNode, + htmlEvents: schema.htmlEvents, + customEvents, + allowedChildren: schema.allowedChildren, + defaultChildren: schema.defaultChildren, + }; +} diff --git a/packages/react/src/atoms/index.ts b/packages/react/src/atoms/index.ts new file mode 100644 index 0000000000..2f2e4bb7e2 --- /dev/null +++ b/packages/react/src/atoms/index.ts @@ -0,0 +1,13 @@ +/** Atom schema utilities. */ +export type { + AtomMetadata, + AtomChild, + DefaultChild, + EditableComponentProps, + CallbackPropKeys, + CallbackArgZodTuple, + PropMeta, + ArgMeta, +} from './types'; +export { withPropMeta, withArgMeta, getFieldMeta } from './schema-utils'; +export { createAtom, type AtomSchemaInput } from './createAtom'; diff --git a/packages/react/src/atoms/schema-utils.ts b/packages/react/src/atoms/schema-utils.ts new file mode 100644 index 0000000000..f70336bf76 --- /dev/null +++ b/packages/react/src/atoms/schema-utils.ts @@ -0,0 +1,62 @@ +/** Schema metadata for atom props/events. */ +import { z } from 'zod'; +import type { PropMeta, ArgMeta } from './types'; + +const META_KEY = 'meta'; + +/** + * Attach editor hint (e.g. control type) to a prop schema. Metadata is stored under a key that + * survives JSON Schema conversion for Design Studio. + * @param schema - Zod type for the prop + * @param meta - Editor metadata (e.g. control) + * @returns The same Zod type with meta attached (or schema unchanged if .meta is not callable) + * @public + */ +export function withPropMeta(schema: T, meta: PropMeta): T { + const s = schema as unknown as { meta?: (m: Record) => T }; + if (typeof s.meta === 'function') { + return s.meta({ [META_KEY]: meta }); + } + return schema; +} + +/** + * Attach display metadata to a custom event argument (e.g. argName for DS). Stored under a key + * that survives JSON Schema conversion. + * @param schema - Zod type for the argument + * @param meta - Argument metadata + * @returns The same Zod type with meta attached (or schema unchanged if .meta is not callable) + * @public + */ +export function withArgMeta(schema: T, meta: ArgMeta): T { + const s = schema as unknown as { meta?: (m: Record) => T }; + if (typeof s.meta === 'function') { + return s.meta({ [META_KEY]: meta }); + } + return schema; +} + +/** + * Get field metadata from a Zod type or a plain JSON Schema object. Uses _zod to detect Zod + * schemas; otherwise reads the meta key from the object. For internal use by the renderer / DS. + * @param schemaOrJsonSchema - Live Zod type or plain JSON Schema object + * @returns The meta object or undefined + * @internal + */ +export function getFieldMeta( + schemaOrJsonSchema: z.ZodType | Record +): Record | undefined { + if (typeof schemaOrJsonSchema !== 'object' || schemaOrJsonSchema === null) { + return undefined; + } + if ('_zod' in schemaOrJsonSchema) { + const obj = schemaOrJsonSchema as Record & { + meta?: () => Record; + }; + const m = typeof obj.meta === 'function' ? obj.meta() : undefined; + return m?.[META_KEY] as Record | undefined; + } + return (schemaOrJsonSchema as Record)[META_KEY] as + | Record + | undefined; +} diff --git a/packages/react/src/atoms/types.ts b/packages/react/src/atoms/types.ts new file mode 100644 index 0000000000..a6614460cf --- /dev/null +++ b/packages/react/src/atoms/types.ts @@ -0,0 +1,59 @@ +/** Atom schema types. @public */ +import type { z } from 'zod'; +import type { ComponentType } from 'react'; + +/** Metadata for an atom or atom-child; type differentiates. @public */ +export type AtomMetadata = { + name: string; + version?: number; + type: 'atom' | 'atom-child'; + description: string; + props: z.ZodObject; + component: (props: unknown) => React.ReactNode; + htmlEvents?: string[]; + customEvents?: Record; + allowedChildren?: AtomChild[]; + defaultChildren?: DefaultChild[]; +}; + +/** Allowed child: atom-child metadata, 'text', or 'atom'. @public */ +export type AtomChild = AtomMetadata | 'text' | 'atom'; + +/** Default child: metadata or { atom, props? }. @public */ +export type DefaultChild = AtomMetadata | { atom: AtomMetadata; props?: Record }; + +/** + * Extracts props from a component type without requiring ComponentType, so function + * components with specific props (e.g. (props: { x: string }) => JSX.Element) are accepted. + */ +type PropsOfComponent = C extends (props: infer P) => unknown + ? P + : C extends ComponentType + ? C extends ComponentType + ? P + : never + : never; + +/** Component props excluding children and ref. @public */ +export type EditableComponentProps = Omit, 'children' | 'ref'>; + +/** Keys of T that are callback (function) props. @public */ +export type CallbackPropKeys = { + [K in keyof T & string]: NonNullable extends (...args: any[]) => unknown ? K : never; +}[keyof T & string]; + +/** + * Tuple of Zod types matching a function's parameter list. Used to type customEvents so each + * callback's schemas match its parameters. Keys are strict; tuple length/element strictness + * is partial (optional params y?: number infer as undefined and need z.number().optional()). + * @public + */ +export type CallbackArgZodTuple = F extends (...args: infer A) => unknown + ? { [I in keyof A]: z.ZodType } + : never; + +/** Prop metadata (e.g. control hint for DS). @public */ +export type PropMeta = { control?: string }; + +/** Event argument metadata (e.g. argName). @public */ +export type ArgMeta = { argName: string }; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 2fb0890c7d..3eb1eb2da5 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -109,3 +109,18 @@ export { } from './components/DefaultEmptyFieldEditingComponents'; export { ClientEditingChromesUpdate } from './components/ClientEditingChromesUpdate'; export { SitePathService, SitePathServiceConfig } from '@sitecore-content-sdk/content/site'; +export { + createAtom, + withPropMeta, + withArgMeta, + getFieldMeta, + type AtomMetadata, + type AtomChild, + type DefaultChild, + type EditableComponentProps, + type CallbackPropKeys, + type CallbackArgZodTuple, + type PropMeta, + type ArgMeta, + type AtomSchemaInput, +} from './atoms'; diff --git a/yarn.lock b/yarn.lock index 45e5cc6b5b..bc6c43dc90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,57 +18,57 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/code-frame@npm:7.28.6" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/code-frame@npm:7.29.0" dependencies: "@babel/helper-validator-identifier": "npm:^7.28.5" js-tokens: "npm:^4.0.0" picocolors: "npm:^1.1.1" - checksum: 10/93e7ed9e039e3cb661bdb97c26feebafacc6ec13d745881dae5c7e2708f579475daebe7a3b5d23b183bb940b30744f52f4a5bcb65b4df03b79d82fcb38495784 + checksum: 10/199e15ff89007dd30675655eec52481cb245c9fdf4f81e4dc1f866603b0217b57aff25f5ffa0a95bbc8e31eb861695330cd7869ad52cc211aa63016320ef72c5 languageName: node linkType: hard "@babel/compat-data@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/compat-data@npm:7.28.6" - checksum: 10/dc17dfb55711a15f006e34c4610c49b7335fc11b23e192f9e5f625e8ea0f48805e61a57b6b4f5550879332782c93af0b5d6952825fffbb8d4e604b14d698249f + version: 7.29.0 + resolution: "@babel/compat-data@npm:7.29.0" + checksum: 10/7f21beedb930ed8fbf7eabafc60e6e6521c1d905646bf1317a61b2163339157fe797efeb85962bf55136e166b01fd1a6b526a15974b92a8b877d564dcb6c9580 languageName: node linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9, @babel/core@npm:^7.27.4": - version: 7.28.6 - resolution: "@babel/core@npm:7.28.6" + version: 7.29.0 + resolution: "@babel/core@npm:7.29.0" dependencies: - "@babel/code-frame": "npm:^7.28.6" - "@babel/generator": "npm:^7.28.6" + "@babel/code-frame": "npm:^7.29.0" + "@babel/generator": "npm:^7.29.0" "@babel/helper-compilation-targets": "npm:^7.28.6" "@babel/helper-module-transforms": "npm:^7.28.6" "@babel/helpers": "npm:^7.28.6" - "@babel/parser": "npm:^7.28.6" + "@babel/parser": "npm:^7.29.0" "@babel/template": "npm:^7.28.6" - "@babel/traverse": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" "@jridgewell/remapping": "npm:^2.3.5" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/1a150a69c547daf13c457be1fdaf1a0935d02b94605e777e049537ec2f279b4bb442ffbe1c2d8ff62c688878b1d5530a5784daf72ece950d1917fb78717f51d2 + checksum: 10/25f4e91688cdfbaf1365831f4f245b436cdaabe63d59389b75752013b8d61819ee4257101b52fc328b0546159fd7d0e74457ed7cf12c365fea54be4fb0a40229 languageName: node linkType: hard -"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.28.6, @babel/generator@npm:^7.7.2": - version: 7.28.6 - resolution: "@babel/generator@npm:7.28.6" +"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.0, @babel/generator@npm:^7.7.2": + version: 7.29.1 + resolution: "@babel/generator@npm:7.29.1" dependencies: - "@babel/parser": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" "@jridgewell/gen-mapping": "npm:^0.3.12" "@jridgewell/trace-mapping": "npm:^0.3.28" jsesc: "npm:^3.0.2" - checksum: 10/ef2af927e8e0985d02ec4321a242da761a934e927539147c59fdd544034dc7f0e9846f6bf86209aca7a28aee2243ed0fad668adccd48f96d7d6866215173f9af + checksum: 10/61fe4ddd6e817aa312a14963ccdbb5c9a8c57e8b97b98d19a8a99ccab2215fda1a5f52bc8dd8d2e3c064497ddeb3ab8ceb55c76fa0f58f8169c34679d2256fe0 languageName: node linkType: hard @@ -153,14 +153,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/parser@npm:7.28.6" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/parser@npm:7.29.0" dependencies: - "@babel/types": "npm:^7.28.6" + "@babel/types": "npm:^7.29.0" bin: parser: ./bin/babel-parser.js - checksum: 10/483a6fb5f9876ec9cbbb98816f2c94f39ae4d1158d35f87e1c4bf19a1f56027c96a1a3962ff0c8c46e8322a6d9e1c80d26b7f9668410df13d5b5769d9447b010 + checksum: 10/b1576dca41074997a33ee740d87b330ae2e647f4b7da9e8d2abd3772b18385d303b0cee962b9b88425e0f30d58358dbb8d63792c1a2d005c823d335f6a029747 languageName: node linkType: hard @@ -369,28 +369,28 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/traverse@npm:7.28.6" +"@babel/traverse@npm:^7.28.6, @babel/traverse@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/traverse@npm:7.29.0" dependencies: - "@babel/code-frame": "npm:^7.28.6" - "@babel/generator": "npm:^7.28.6" + "@babel/code-frame": "npm:^7.29.0" + "@babel/generator": "npm:^7.29.0" "@babel/helper-globals": "npm:^7.28.0" - "@babel/parser": "npm:^7.28.6" + "@babel/parser": "npm:^7.29.0" "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" + "@babel/types": "npm:^7.29.0" debug: "npm:^4.3.1" - checksum: 10/dd71efe9412433169b805d5c346a6473e539ce30f605752a0d40a0733feba37259bd72bb4ad2ab591e2eaff1ee56633de160c1e98efdc8f373cf33a4a8660275 + checksum: 10/3a0d0438f1ba9fed4fbe1706ea598a865f9af655a16ca9517ab57bda526e224569ca1b980b473fb68feea5e08deafbbf2cf9febb941f92f2d2533310c3fc4abc languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6, @babel/types@npm:^7.3.3": - version: 7.28.6 - resolution: "@babel/types@npm:7.28.6" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0, @babel/types@npm:^7.3.3": + version: 7.29.0 + resolution: "@babel/types@npm:7.29.0" dependencies: "@babel/helper-string-parser": "npm:^7.27.1" "@babel/helper-validator-identifier": "npm:^7.28.5" - checksum: 10/f9c6e52b451065aae5654686ecfc7de2d27dd0fbbc204ee2bd912a71daa359521a32f378981b1cf333ace6c8f86928814452cb9f388a7da59ad468038deb6b5f + checksum: 10/bfc2b211210f3894dcd7e6a33b2d1c32c93495dc1e36b547376aa33441abe551ab4bc1640d4154ee2acd8e46d3bbc925c7224caae02fcaf0e6a771e97fccc661 languageName: node linkType: hard @@ -457,30 +457,30 @@ __metadata: linkType: hard "@emnapi/core@npm:^1.4.3": - version: 1.8.1 - resolution: "@emnapi/core@npm:1.8.1" + version: 1.9.0 + resolution: "@emnapi/core@npm:1.9.0" dependencies: - "@emnapi/wasi-threads": "npm:1.1.0" + "@emnapi/wasi-threads": "npm:1.2.0" tslib: "npm:^2.4.0" - checksum: 10/904ea60c91fc7d8aeb4a8f2c433b8cfb47c50618f2b6f37429fc5093c857c6381c60628a5cfbc3a7b0d75b0a288f21d4ed2d4533e82f92c043801ef255fd6a5c + checksum: 10/52d8dc5ba0d6814c5061686b8286d84cc5349c8fc09de3a9c4175bc2369c2890b335f7b03e55bc19ce3033158962cd817522fcb3bdeb1feb9ba7a060d61b69ab languageName: node linkType: hard "@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.0": - version: 1.8.1 - resolution: "@emnapi/runtime@npm:1.8.1" + version: 1.9.0 + resolution: "@emnapi/runtime@npm:1.9.0" dependencies: tslib: "npm:^2.4.0" - checksum: 10/26725e202d4baefdc4a6ba770f703dfc80825a27c27a08c22bac1e1ce6f8f75c47b4fe9424d9b63239463c33ef20b650f08d710da18dfa1164a95e5acb865dba + checksum: 10/d04a7e67676c2560d5394a01d63532af943760cf19cc8f375390a345aeab2b19e9ee35485b06b5c211df18f947fb14ac50658fca5c4067946f1e50af3490b3b5 languageName: node linkType: hard -"@emnapi/wasi-threads@npm:1.1.0": - version: 1.1.0 - resolution: "@emnapi/wasi-threads@npm:1.1.0" +"@emnapi/wasi-threads@npm:1.2.0": + version: 1.2.0 + resolution: "@emnapi/wasi-threads@npm:1.2.0" dependencies: tslib: "npm:^2.4.0" - checksum: 10/0d557e75262d2f4c95cb2a456ba0785ef61f919ce488c1d76e5e3acfd26e00c753ef928cd80068363e0c166ba8cc0141305daf0f81aad5afcd421f38f11e0f4e + checksum: 10/c8e48c7200530744dc58170d2e25933b61433e4a0c50b4f192f5d8d4b065c7023dbfc48dac0afadbc29bd239013f2ae454c6e54e0ca6e8248402bf95c9e77e22 languageName: node linkType: hard @@ -517,184 +517,184 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/aix-ppc64@npm:0.27.2" +"@esbuild/aix-ppc64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/aix-ppc64@npm:0.27.4" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm64@npm:0.27.2" +"@esbuild/android-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-arm64@npm:0.27.4" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-arm@npm:0.27.2" +"@esbuild/android-arm@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-arm@npm:0.27.4" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/android-x64@npm:0.27.2" +"@esbuild/android-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/android-x64@npm:0.27.4" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-arm64@npm:0.27.2" +"@esbuild/darwin-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/darwin-arm64@npm:0.27.4" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/darwin-x64@npm:0.27.2" +"@esbuild/darwin-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/darwin-x64@npm:0.27.4" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-arm64@npm:0.27.2" +"@esbuild/freebsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/freebsd-arm64@npm:0.27.4" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/freebsd-x64@npm:0.27.2" +"@esbuild/freebsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/freebsd-x64@npm:0.27.4" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm64@npm:0.27.2" +"@esbuild/linux-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-arm64@npm:0.27.4" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-arm@npm:0.27.2" +"@esbuild/linux-arm@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-arm@npm:0.27.4" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ia32@npm:0.27.2" +"@esbuild/linux-ia32@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-ia32@npm:0.27.4" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-loong64@npm:0.27.2" +"@esbuild/linux-loong64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-loong64@npm:0.27.4" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-mips64el@npm:0.27.2" +"@esbuild/linux-mips64el@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-mips64el@npm:0.27.4" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-ppc64@npm:0.27.2" +"@esbuild/linux-ppc64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-ppc64@npm:0.27.4" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-riscv64@npm:0.27.2" +"@esbuild/linux-riscv64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-riscv64@npm:0.27.4" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-s390x@npm:0.27.2" +"@esbuild/linux-s390x@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-s390x@npm:0.27.4" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/linux-x64@npm:0.27.2" +"@esbuild/linux-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/linux-x64@npm:0.27.4" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-arm64@npm:0.27.2" +"@esbuild/netbsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/netbsd-arm64@npm:0.27.4" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/netbsd-x64@npm:0.27.2" +"@esbuild/netbsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/netbsd-x64@npm:0.27.4" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-arm64@npm:0.27.2" +"@esbuild/openbsd-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openbsd-arm64@npm:0.27.4" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openbsd-x64@npm:0.27.2" +"@esbuild/openbsd-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openbsd-x64@npm:0.27.4" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/openharmony-arm64@npm:0.27.2" +"@esbuild/openharmony-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/openharmony-arm64@npm:0.27.4" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/sunos-x64@npm:0.27.2" +"@esbuild/sunos-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/sunos-x64@npm:0.27.4" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-arm64@npm:0.27.2" +"@esbuild/win32-arm64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-arm64@npm:0.27.4" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-ia32@npm:0.27.2" +"@esbuild/win32-ia32@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-ia32@npm:0.27.4" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.27.2": - version: 0.27.2 - resolution: "@esbuild/win32-x64@npm:0.27.2" +"@esbuild/win32-x64@npm:0.27.4": + version: 0.27.4 + resolution: "@esbuild/win32-x64@npm:0.27.4" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -717,14 +717,14 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" +"@eslint/config-array@npm:^0.21.2": + version: 0.21.2 + resolution: "@eslint/config-array@npm:0.21.2" dependencies: "@eslint/object-schema": "npm:^2.1.7" debug: "npm:^4.3.1" - minimatch: "npm:^3.1.2" - checksum: 10/6eaa0435972f735ce52d581f355a0b616e50a9b8a73304a7015398096e252798b9b3b968a67b524eefb0fdeacc57c4d960f0ec6432abe1c1e24be815b88c5d18 + minimatch: "npm:^3.1.5" + checksum: 10/148477ba995cf57fc725601916d5a7914aa249112d8bec2c3ac9122e2b2f540e6ef013ff4f6785346a4b565f09b20db127fa6f7322f5ffbdb3f1f8d2078a531c languageName: node linkType: hard @@ -746,27 +746,27 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.1": - version: 3.3.3 - resolution: "@eslint/eslintrc@npm:3.3.3" +"@eslint/eslintrc@npm:^3.3.5": + version: 3.3.5 + resolution: "@eslint/eslintrc@npm:3.3.5" dependencies: - ajv: "npm:^6.12.4" + ajv: "npm:^6.14.0" debug: "npm:^4.3.2" espree: "npm:^10.0.1" globals: "npm:^14.0.0" ignore: "npm:^5.2.0" import-fresh: "npm:^3.2.1" js-yaml: "npm:^4.1.1" - minimatch: "npm:^3.1.2" + minimatch: "npm:^3.1.5" strip-json-comments: "npm:^3.1.1" - checksum: 10/b586a364ff15ce1b68993aefc051ca330b1fece15fb5baf4a708d00113f9a14895cffd84a5f24c5a97bd4b4321130ab2314f90aa462a250f6b859c2da2cba1f3 + checksum: 10/edabb65693d82a88cac3b2cf932a0f825e986b5e0a21ef08782d07e3a61ad87d39db67cfd5aeb146fd5053e5e24e389dbe5649ab22936a71d633c7b32a7e6d86 languageName: node linkType: hard -"@eslint/js@npm:9.39.2": - version: 9.39.2 - resolution: "@eslint/js@npm:9.39.2" - checksum: 10/6b7f676746f3111b5d1b23715319212ab9297868a0fa9980d483c3da8965d5841673aada2d5653e85a3f7156edee0893a7ae7035211b4efdcb2848154bb947f2 +"@eslint/js@npm:9.39.4": + version: 9.39.4 + resolution: "@eslint/js@npm:9.39.4" + checksum: 10/0a7ab4c4108cf2cadf66849ebd20f5957cc53052b88d8807d0b54e489dbf6ffcaf741e144e7f9b187c395499ce2e6ddc565dbfa4f60c6df455cf2b30bcbdc5a3 languageName: node linkType: hard @@ -787,6 +787,15 @@ __metadata: languageName: node linkType: hard +"@gar/promise-retry@npm:^1.0.0": + version: 1.0.2 + resolution: "@gar/promise-retry@npm:1.0.2" + dependencies: + retry: "npm:^0.13.1" + checksum: 10/b91326999ce94677cbe91973079eabc689761a93a045f6a2d34d4070e9305b27f6c54e4021688c7080cb14caf89eafa0c0f300af741b94c20d18608bdb66ca46 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -795,15 +804,15 @@ __metadata: linkType: hard "@gerrit0/mini-shiki@npm:^3.17.0": - version: 3.21.0 - resolution: "@gerrit0/mini-shiki@npm:3.21.0" + version: 3.23.0 + resolution: "@gerrit0/mini-shiki@npm:3.23.0" dependencies: - "@shikijs/engine-oniguruma": "npm:^3.21.0" - "@shikijs/langs": "npm:^3.21.0" - "@shikijs/themes": "npm:^3.21.0" - "@shikijs/types": "npm:^3.21.0" + "@shikijs/engine-oniguruma": "npm:^3.23.0" + "@shikijs/langs": "npm:^3.23.0" + "@shikijs/themes": "npm:^3.23.0" + "@shikijs/types": "npm:^3.23.0" "@shikijs/vscode-textmate": "npm:^10.0.2" - checksum: 10/e3f997e18ff3e0654a951f3be2c8fa1deba02afa77865861fbedd62775ef06d38333c76b67ecab4cc38b07d4fe39720f8fe2170ac685323991b4ed7500079b11 + checksum: 10/d44dac7c3f58ba136285b3c15c8a89eba120f7a493dddcf6178601ab8c0a5286da6a19515100297934288c41ae225f21013a4c395fe2f656b58f812dd13ccc1e languageName: node linkType: hard @@ -855,9 +864,9 @@ __metadata: linkType: hard "@img/colour@npm:^1.0.0": - version: 1.0.0 - resolution: "@img/colour@npm:1.0.0" - checksum: 10/bd248d7c4b8ba99a72b22a005a63f1d3309ee8343a74b6d0d1314bae300a3096919991a09e9a9243cf6ca50e393b4c5a7e065488ed616c3b58d052473240b812 + version: 1.1.0 + resolution: "@img/colour@npm:1.1.0" + checksum: 10/2a29be7b06b046bd33c80ffa0f3493b7535b0841a69f54af81bf5e2d8867f21704fab42e9cf24ec30c089de34f790bae4dad1fc617c3163fbf264998dc316f0a languageName: node linkType: hard @@ -1328,22 +1337,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/balanced-match@npm:^4.0.1": - version: 4.0.1 - resolution: "@isaacs/balanced-match@npm:4.0.1" - checksum: 10/102fbc6d2c0d5edf8f6dbf2b3feb21695a21bc850f11bc47c4f06aa83bd8884fde3fe9d6d797d619901d96865fdcb4569ac2a54c937992c48885c5e3d9967fe8 - languageName: node - linkType: hard - -"@isaacs/brace-expansion@npm:^5.0.0": - version: 5.0.0 - resolution: "@isaacs/brace-expansion@npm:5.0.0" - dependencies: - "@isaacs/balanced-match": "npm:^4.0.1" - checksum: 10/cf3b7f206aff12128214a1df764ac8cdbc517c110db85249b945282407e3dfc5c6e66286383a7c9391a059fc8e6e6a8ca82262fc9d2590bd615376141fbebd2d - languageName: node - linkType: hard - "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -1358,6 +1351,13 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^9.0.0": + version: 9.0.0 + resolution: "@isaacs/cliui@npm:9.0.0" + checksum: 10/8ea3d1009fd29071419209bb91ede20cf27e6e2a1630c5e0702d8b3f47f9e1a3f1c5a587fa2cb96d22d18219790327df49db1bcced573346bbaf4577cf46b643 + languageName: node + linkType: hard + "@isaacs/fs-minipass@npm:^4.0.0": version: 4.0.1 resolution: "@isaacs/fs-minipass@npm:4.0.1" @@ -1449,22 +1449,22 @@ __metadata: languageName: node linkType: hard -"@jest/diff-sequences@npm:30.0.1": - version: 30.0.1 - resolution: "@jest/diff-sequences@npm:30.0.1" - checksum: 10/0ddb7c7ba92d6057a2ee51a9cfc2155b77cca707fe959167466ea02dcb0687018cc3c22b9622f25f3a417d6ad370e2d4dcfedf9f1410dc9c02954a7484423cc7 +"@jest/diff-sequences@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/diff-sequences@npm:30.3.0" + checksum: 10/0d5b6e1599c5e0bb702f0804e7f93bbe4911b5929c40fd6a77c06105711eae24d709c8964e8d623cc70c34b7dc7262d76a115a6eb05f1576336cdb6c46593e7c languageName: node linkType: hard -"@jest/environment@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/environment@npm:30.2.0" +"@jest/environment@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/environment@npm:30.3.0" dependencies: - "@jest/fake-timers": "npm:30.2.0" - "@jest/types": "npm:30.2.0" + "@jest/fake-timers": "npm:30.3.0" + "@jest/types": "npm:30.3.0" "@types/node": "npm:*" - jest-mock: "npm:30.2.0" - checksum: 10/e168a4ff328980eb9fde5e43aea80807fd0b2dbd4579ae8f68a03415a1e58adf5661db298054fa2351c7cb2b5a74bf67b8ab996656cf5927d0b0d0b6e2c2966b + jest-mock: "npm:30.3.0" + checksum: 10/9b64add2e5430411ca997aed23cd34786d0e87562f5930ad0d4160df51435ae061809fcaa6bbc6c0ff9f0ba5f1241a5ce9a32ec772fa1d7c6b022f0169b622a4 languageName: node linkType: hard @@ -1480,12 +1480,12 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/expect-utils@npm:30.2.0" +"@jest/expect-utils@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/expect-utils@npm:30.3.0" dependencies: "@jest/get-type": "npm:30.1.0" - checksum: 10/f2442f1bceb3411240d0f16fd0074377211b4373d3b8b2dc28929e861b6527a6deb403a362c25afa511d933cda4dfbdc98d4a08eeb51ee4968f7cb0299562349 + checksum: 10/766fd24f527a13004c542c2642b68b9142270801ab20bd448a559d9c2f40af079d0eb9ec9520a47f97b4d6c7d0837ba46e86284f53c939f11d9fcbda73a11e19 languageName: node linkType: hard @@ -1498,13 +1498,13 @@ __metadata: languageName: node linkType: hard -"@jest/expect@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/expect@npm:30.2.0" +"@jest/expect@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/expect@npm:30.3.0" dependencies: - expect: "npm:30.2.0" - jest-snapshot: "npm:30.2.0" - checksum: 10/d950d95a64d5c6a39d56171dabb8dbe59423096231bb4f21d8ee0019878e6626701ac9d782803dc2589e2799ed39704031f818533f8a3e571b57032eafa85d12 + expect: "npm:30.3.0" + jest-snapshot: "npm:30.3.0" + checksum: 10/74832945a2b18c7b962b27e0ca4d25d19a29d1c3ca6fe4a9c23946025b4146799e62a81d50060ac7bcaf7036fb477aa350ddf300e215333b42d013a3d9f8ba2b languageName: node linkType: hard @@ -1518,17 +1518,17 @@ __metadata: languageName: node linkType: hard -"@jest/fake-timers@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/fake-timers@npm:30.2.0" +"@jest/fake-timers@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/fake-timers@npm:30.3.0" dependencies: - "@jest/types": "npm:30.2.0" - "@sinonjs/fake-timers": "npm:^13.0.0" + "@jest/types": "npm:30.3.0" + "@sinonjs/fake-timers": "npm:^15.0.0" "@types/node": "npm:*" - jest-message-util: "npm:30.2.0" - jest-mock: "npm:30.2.0" - jest-util: "npm:30.2.0" - checksum: 10/c2df66576ba8049b07d5f239777243e21fcdaa09a446be1e55fac709d6273e2a926c1562e0372c3013142557ed9d386381624023549267a667b6e1b656e37fe6 + jest-message-util: "npm:30.3.0" + jest-mock: "npm:30.3.0" + jest-util: "npm:30.3.0" + checksum: 10/e39d30b61ae85485bfa0b1d86d62d866d33964bf0b95b8b4f45d2f1f1baa94fd7e134c7729370a58cb67b58d2b860fb396290b5c271782ed4d3728341027549b languageName: node linkType: hard @@ -1566,14 +1566,14 @@ __metadata: linkType: hard "@jest/globals@npm:^30.2.0": - version: 30.2.0 - resolution: "@jest/globals@npm:30.2.0" + version: 30.3.0 + resolution: "@jest/globals@npm:30.3.0" dependencies: - "@jest/environment": "npm:30.2.0" - "@jest/expect": "npm:30.2.0" - "@jest/types": "npm:30.2.0" - jest-mock: "npm:30.2.0" - checksum: 10/d4a331d3847cebb3acefe120350d8a6bb5517c1403de7cd2b4dc67be425f37ba0511beee77d6837b4da2d93a25a06d6f829ad7837da365fae45e1da57523525c + "@jest/environment": "npm:30.3.0" + "@jest/expect": "npm:30.3.0" + "@jest/types": "npm:30.3.0" + jest-mock: "npm:30.3.0" + checksum: 10/485bdc0f35faf3e76cb451b75e16892d87f7ab5757e290b1a9e849a3af0ef81c47abddb188fbc0442a4689514cf0551e34d13970c9cf03610a269c39f800ff46 languageName: node linkType: hard @@ -1642,15 +1642,15 @@ __metadata: languageName: node linkType: hard -"@jest/snapshot-utils@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/snapshot-utils@npm:30.2.0" +"@jest/snapshot-utils@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/snapshot-utils@npm:30.3.0" dependencies: - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" chalk: "npm:^4.1.2" graceful-fs: "npm:^4.2.11" natural-compare: "npm:^1.4.0" - checksum: 10/6b30ab2b0682117e3ce775e70b5be1eb01e1ea53a74f12ac7090cd1a5f37e9b795cd8de83853afa7b4b799c96b1c482499aa993ca2034ea0679525d32b7f9625 + checksum: 10/2214d4f0f33d2363a0785c0ba75066bf4ed4beefd5b2d2a5c3124d66ab92f91163f03696be625223bdb0527f1e6360c4b306ba9ae421aeb966d4a57d6d972099 languageName: node linkType: hard @@ -1689,26 +1689,25 @@ __metadata: languageName: node linkType: hard -"@jest/transform@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/transform@npm:30.2.0" +"@jest/transform@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/transform@npm:30.3.0" dependencies: "@babel/core": "npm:^7.27.4" - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" "@jridgewell/trace-mapping": "npm:^0.3.25" babel-plugin-istanbul: "npm:^7.0.1" chalk: "npm:^4.1.2" convert-source-map: "npm:^2.0.0" fast-json-stable-stringify: "npm:^2.1.0" graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.2.0" + jest-haste-map: "npm:30.3.0" jest-regex-util: "npm:30.0.1" - jest-util: "npm:30.2.0" - micromatch: "npm:^4.0.8" + jest-util: "npm:30.3.0" pirates: "npm:^4.0.7" slash: "npm:^3.0.0" write-file-atomic: "npm:^5.0.1" - checksum: 10/c75d72d524c2a50ea6c05778a9b76a6e48bc228a3390896a6edd4416f7b4954ee0a07e229ed7b4949ce8889324b70034c784751e3fc455a25648bd8dcad17d0d + checksum: 10/279b6b73f59c274d7011febcbc0a1fa8939e8f677801a0a9bd95b9cf49244957267f3769c8cd541ae8026d8176089cd5e55f0f8d5361ec7788970978f4f394b4 languageName: node linkType: hard @@ -1735,9 +1734,9 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:30.2.0": - version: 30.2.0 - resolution: "@jest/types@npm:30.2.0" +"@jest/types@npm:30.3.0": + version: 30.3.0 + resolution: "@jest/types@npm:30.3.0" dependencies: "@jest/pattern": "npm:30.0.1" "@jest/schemas": "npm:30.0.5" @@ -1746,7 +1745,7 @@ __metadata: "@types/node": "npm:*" "@types/yargs": "npm:^17.0.33" chalk: "npm:^4.1.2" - checksum: 10/f50fcaea56f873a51d19254ab16762f2ea8ca88e3e08da2e496af5da2b67c322915a4fcd0153803cc05063ffe87ebef2ab4330e0a1b06ab984a26c916cbfc26b + checksum: 10/d6943cc270f07c7bc1ee6f3bb9ad1263ce7897d1a282221bf1d27499d77f2a68cfa6625ca73c193d3f81fe22a8e00635cd7acb5e73a546965c172219c81ec12c languageName: node linkType: hard @@ -2622,50 +2621,50 @@ __metadata: languageName: node linkType: hard -"@microsoft/api-extractor-model@npm:7.32.2": - version: 7.32.2 - resolution: "@microsoft/api-extractor-model@npm:7.32.2" +"@microsoft/api-extractor-model@npm:7.33.4": + version: 7.33.4 + resolution: "@microsoft/api-extractor-model@npm:7.33.4" dependencies: "@microsoft/tsdoc": "npm:~0.16.0" - "@microsoft/tsdoc-config": "npm:~0.18.0" - "@rushstack/node-core-library": "npm:5.19.1" - checksum: 10/89760055c7d3074cd903e3694c411eafa8704f048781bb2db0f0ba6e6a9a1bb12a722419ddd99141562cc8d13c98e2deee6eba118853eed630918705b223107b + "@microsoft/tsdoc-config": "npm:~0.18.1" + "@rushstack/node-core-library": "npm:5.20.3" + checksum: 10/60888f94ee2b121aac3a1e443c6922080b130226d956c98eb44de5deba80c887d0382cd9ade8ccd46d8a55af27182fb937f7ef0a17dccac286253eb35cc8111d languageName: node linkType: hard "@microsoft/api-extractor@npm:^7.55.0": - version: 7.55.5 - resolution: "@microsoft/api-extractor@npm:7.55.5" + version: 7.57.7 + resolution: "@microsoft/api-extractor@npm:7.57.7" dependencies: - "@microsoft/api-extractor-model": "npm:7.32.2" + "@microsoft/api-extractor-model": "npm:7.33.4" "@microsoft/tsdoc": "npm:~0.16.0" - "@microsoft/tsdoc-config": "npm:~0.18.0" - "@rushstack/node-core-library": "npm:5.19.1" - "@rushstack/rig-package": "npm:0.6.0" - "@rushstack/terminal": "npm:0.21.0" - "@rushstack/ts-command-line": "npm:5.1.7" + "@microsoft/tsdoc-config": "npm:~0.18.1" + "@rushstack/node-core-library": "npm:5.20.3" + "@rushstack/rig-package": "npm:0.7.2" + "@rushstack/terminal": "npm:0.22.3" + "@rushstack/ts-command-line": "npm:5.3.3" diff: "npm:~8.0.2" - lodash: "npm:~4.17.15" - minimatch: "npm:10.0.3" + lodash: "npm:~4.17.23" + minimatch: "npm:10.2.3" resolve: "npm:~1.22.1" semver: "npm:~7.5.4" source-map: "npm:~0.6.1" typescript: "npm:5.8.2" bin: api-extractor: bin/api-extractor - checksum: 10/7017f71b225df339d9b27e1243d4720d29c3e590555710daa8ce350f4920227de8d7b6b80827e7efa0abf2e0945f46eb1b703e4b02e5138a52434a5f1bd2e29e + checksum: 10/052a137ad8b9b81034b14c9225429c517783112a550a4bff3d8588f4626d269790df163eb7dd55573a44de607051571e2bf03eb6384bf2dc505a41e98de94153 languageName: node linkType: hard -"@microsoft/tsdoc-config@npm:~0.18.0": - version: 0.18.0 - resolution: "@microsoft/tsdoc-config@npm:0.18.0" +"@microsoft/tsdoc-config@npm:~0.18.1": + version: 0.18.1 + resolution: "@microsoft/tsdoc-config@npm:0.18.1" dependencies: "@microsoft/tsdoc": "npm:0.16.0" - ajv: "npm:~8.12.0" + ajv: "npm:~8.18.0" jju: "npm:~1.4.0" resolve: "npm:~1.22.2" - checksum: 10/0470df5326181d876faba51617d011a632b2e3b420953e521bb865715d0db2fb0b8bfb3ccbcaef267356a1a7150d2ffba40022db5930db6b15fcb348bf846a4b + checksum: 10/1912c4d80af10c548897dafd2b76127a53d5154001fb63029d4414d57022bf0f0aced325d8a3a0970454bf651d506236b4d26c1c473a933ea77382bce67b1236 languageName: node linkType: hard @@ -2676,9 +2675,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.39.5": - version: 0.39.8 - resolution: "@mswjs/interceptors@npm:0.39.8" +"@mswjs/interceptors@npm:^0.41.0": + version: 0.41.3 + resolution: "@mswjs/interceptors@npm:0.41.3" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -2686,7 +2685,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10/d92546cf9bf670ddb927c53f5fa19f0554b7475a264ead4e1ae2339874f4312fe4ada5d42588f27eea3577bee29fa8f46889d398f0e7ecb3f7a4c1d3e0b71bdc + checksum: 10/96b6c535fd27c8aed57f2dad380ea31e09026bf6ef960420bc0e30a2ccff269c8121f21f20423f4edd2ef1ed7db6173295950a3c4529693e6bca12eca1be4347 languageName: node linkType: hard @@ -3291,18 +3290,18 @@ __metadata: linkType: hard "@rjsf/utils@npm:*": - version: 6.2.5 - resolution: "@rjsf/utils@npm:6.2.5" + version: 6.4.1 + resolution: "@rjsf/utils@npm:6.4.1" dependencies: "@x0k/json-schema-merge": "npm:^1.0.2" fast-uri: "npm:^3.1.0" jsonpointer: "npm:^5.0.1" - lodash: "npm:^4.17.21" - lodash-es: "npm:^4.17.21" + lodash: "npm:^4.17.23" + lodash-es: "npm:^4.17.23" react-is: "npm:^18.3.1" peerDependencies: react: ">=18" - checksum: 10/5a167d860407c30faa3bc4a987a1242dd70edea067b4062196d50a8421e5177f0603e4e0a6b067abc0650452c6633aa26329a7a61eda565dc8469fa03cf31d46 + checksum: 10/b8967b731f00b312b213d7265ee2542ab6adea86bef71c02c8c3465b2e992e557b2a36631f0fbadbcc4b4b311c5e3d2be08dc509ce53c34aa691ee01e6bdf620 languageName: node linkType: hard @@ -3320,11 +3319,11 @@ __metadata: languageName: node linkType: hard -"@rushstack/node-core-library@npm:5.19.1": - version: 5.19.1 - resolution: "@rushstack/node-core-library@npm:5.19.1" +"@rushstack/node-core-library@npm:5.20.3": + version: 5.20.3 + resolution: "@rushstack/node-core-library@npm:5.20.3" dependencies: - ajv: "npm:~8.13.0" + ajv: "npm:~8.18.0" ajv-draft-04: "npm:~1.0.0" ajv-formats: "npm:~3.0.1" fs-extra: "npm:~11.3.0" @@ -3337,95 +3336,95 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10/a674c4ed4cf3c863ab6bfff0e3615e7f791d0312fa5942027b6e8070b4453464c0e77741a1ed13bc01fab66ddc89e8c068c6c58ce7d81c014966628b75223bd6 + checksum: 10/16479e853e3c15fca644a05b50734e1356f0180a88a0d164fe7a7c75b90da9e7da44befe455361ed8ef786ad1b00b6c3662f40e08cb06dc081bf5fb574c74642 languageName: node linkType: hard -"@rushstack/problem-matcher@npm:0.1.1": - version: 0.1.1 - resolution: "@rushstack/problem-matcher@npm:0.1.1" +"@rushstack/problem-matcher@npm:0.2.1": + version: 0.2.1 + resolution: "@rushstack/problem-matcher@npm:0.2.1" peerDependencies: "@types/node": "*" peerDependenciesMeta: "@types/node": optional: true - checksum: 10/a47c2d5fd0e3bbe7336f06c29ef91061e36ab8dafd04c861392806e60a3366fd8c3921be217adc71d039c8749f6b70a06c874ff314501eed5b7f8fb7b42c7a39 + checksum: 10/62fda91629577a2f57de19be357cd0990da145ff4933f4d2cd48f423cc03b92fca06dd8916dcbaf1d307a201c104847c77066d45d79fd3c323c4949f0c99bf44 languageName: node linkType: hard -"@rushstack/rig-package@npm:0.6.0": - version: 0.6.0 - resolution: "@rushstack/rig-package@npm:0.6.0" +"@rushstack/rig-package@npm:0.7.2": + version: 0.7.2 + resolution: "@rushstack/rig-package@npm:0.7.2" dependencies: resolve: "npm:~1.22.1" strip-json-comments: "npm:~3.1.1" - checksum: 10/6ca5d6615365dfe4d78fdc52a1a145bec92bba79d8692db91d05c774b4ec4d9dc6c41b31949708d0312896b9c1c205a0f0eaa32f51ac7b1780415ac51c76af71 + checksum: 10/d500714d77792797aaf98afaa7c6133b6886626096f738e1cfd9c18a15ac05fbb3ac6b2cc0798a21e9e9836ebae7f4542c951e4541cfdbee3a280f3f072cf93e languageName: node linkType: hard -"@rushstack/terminal@npm:0.21.0": - version: 0.21.0 - resolution: "@rushstack/terminal@npm:0.21.0" +"@rushstack/terminal@npm:0.22.3": + version: 0.22.3 + resolution: "@rushstack/terminal@npm:0.22.3" dependencies: - "@rushstack/node-core-library": "npm:5.19.1" - "@rushstack/problem-matcher": "npm:0.1.1" + "@rushstack/node-core-library": "npm:5.20.3" + "@rushstack/problem-matcher": "npm:0.2.1" supports-color: "npm:~8.1.1" peerDependencies: "@types/node": "*" peerDependenciesMeta: "@types/node": optional: true - checksum: 10/c9874ee6bcb27e4f38237060a50747baa331b86ca48adac2bcd293cb0f3b54c98834415862e4dc03e456dd93ba634a0c1b97f7013f2393195f6629090254fbf0 + checksum: 10/d4a8a2b1743d5e8a45f318be4665bf1fcb4bfbe9013a0d48f6c9f89960142da1bcad3544bbcd38fc29a1d061ff4664cb0b4ab87c8e9cb0d0f7479b6be70fa47b languageName: node linkType: hard -"@rushstack/ts-command-line@npm:5.1.7": - version: 5.1.7 - resolution: "@rushstack/ts-command-line@npm:5.1.7" +"@rushstack/ts-command-line@npm:5.3.3": + version: 5.3.3 + resolution: "@rushstack/ts-command-line@npm:5.3.3" dependencies: - "@rushstack/terminal": "npm:0.21.0" + "@rushstack/terminal": "npm:0.22.3" "@types/argparse": "npm:1.0.38" argparse: "npm:~1.0.9" string-argv: "npm:~0.3.1" - checksum: 10/fee27c1a3717bdc7b5ce33d9e5ae12ee918359ffee10434bf0a5d7fcecfd709f8b083e1a6dc0f5af455779fca7d85ca499c4837d643d4724797a019e23dbdb51 + checksum: 10/a0266554ef6abc710f3dfbcb91006be1c3593760b4142bd6357349c79f096b3a7f7a5ec723c4f60293dc4357c54d820cf90e6f351c269d0a0591c6654432a0a4 languageName: node linkType: hard -"@shikijs/engine-oniguruma@npm:^3.21.0": - version: 3.21.0 - resolution: "@shikijs/engine-oniguruma@npm:3.21.0" +"@shikijs/engine-oniguruma@npm:^3.23.0": + version: 3.23.0 + resolution: "@shikijs/engine-oniguruma@npm:3.23.0" dependencies: - "@shikijs/types": "npm:3.21.0" + "@shikijs/types": "npm:3.23.0" "@shikijs/vscode-textmate": "npm:^10.0.2" - checksum: 10/670dcb10195b7aabe7965921e7ff5e315182ca15287a61867f16df3046d8bb81b53416803f2fe6b12b5656be61e7a9990da3a5caf4d7d75b1386884b0e9568d1 + checksum: 10/edd8983be86f6b055793813e80ecf31c3cefdd896f3742797ae03f2cbb9f467ac736c4b152d4a5c42dbd17da17d24ff798a15d37bcf6205d7f8cd8f44e2a11e0 languageName: node linkType: hard -"@shikijs/langs@npm:^3.21.0": - version: 3.21.0 - resolution: "@shikijs/langs@npm:3.21.0" +"@shikijs/langs@npm:^3.23.0": + version: 3.23.0 + resolution: "@shikijs/langs@npm:3.23.0" dependencies: - "@shikijs/types": "npm:3.21.0" - checksum: 10/37c56fbfdacd3b74a4943cb8d481e3c3288eba72e55c76e34b1a56dd528f728d636c1691433d8bdc96cc5c0b63789754b9515995f28943580b6ef72abc5fd8d9 + "@shikijs/types": "npm:3.23.0" + checksum: 10/115b1afb9eb4eb300eb68b146e442f7e6d196878798c5b9d75dd53a6ef1e1c3b27e325353a10973e3fa47d88630e937b8a3cf3b37b6eef80bf2aec3d51657bb3 languageName: node linkType: hard -"@shikijs/themes@npm:^3.21.0": - version: 3.21.0 - resolution: "@shikijs/themes@npm:3.21.0" +"@shikijs/themes@npm:^3.23.0": + version: 3.23.0 + resolution: "@shikijs/themes@npm:3.23.0" dependencies: - "@shikijs/types": "npm:3.21.0" - checksum: 10/dd3d333f85da79b0904f0ca33e66fc6b01984d161c3ac0e8373df865dabb73f4a7fe5bb7425475aa041082d364c657a756b246d85de4735557902be408e079d4 + "@shikijs/types": "npm:3.23.0" + checksum: 10/741987380445b788aea6cc1e03492bc06950f1a0461edf45083bd226889c5ba78c7ed1af9724afacab7d6471777f0c0e8871577183dfb2cfbceb255663374835 languageName: node linkType: hard -"@shikijs/types@npm:3.21.0, @shikijs/types@npm:^3.21.0": - version: 3.21.0 - resolution: "@shikijs/types@npm:3.21.0" +"@shikijs/types@npm:3.23.0, @shikijs/types@npm:^3.23.0": + version: 3.23.0 + resolution: "@shikijs/types@npm:3.23.0" dependencies: "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - checksum: 10/814dfbbaae55b9d1d86874ed870e80da0cba521a96fb799c73c8439f88b2218fb9e84f64485a50258508d0f912bdd19d0fb8494c2717a9eed2a809e243a81a49 + checksum: 10/18b5703d445d53dd6782a3e2c7332009302c6c046f301d9cd99f1a26b9c8329b3e502b096894bffac61f78835c533827bab2ce78a39666ab87b78f8d7ca11f7b languageName: node linkType: hard @@ -3437,9 +3436,9 @@ __metadata: linkType: hard "@sinclair/typebox@npm:^0.27.8": - version: 0.27.8 - resolution: "@sinclair/typebox@npm:0.27.8" - checksum: 10/297f95ff77c82c54de8c9907f186076e715ff2621c5222ba50b8d40a170661c0c5242c763cba2a4791f0f91cb1d8ffa53ea1d7294570cf8cd4694c0e383e484d + version: 0.27.10 + resolution: "@sinclair/typebox@npm:0.27.10" + checksum: 10/1498c5ef1375787e6272528615d5c262afb60873191d2441316359817b1c411917063c8be102ef15b0b5c62243a9daa7aefc8426f20eb406b67038b3eaa0695a languageName: node linkType: hard @@ -3482,7 +3481,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^13.0.0, @sinonjs/fake-timers@npm:^13.0.5": +"@sinonjs/fake-timers@npm:^13.0.5": version: 13.0.5 resolution: "@sinonjs/fake-timers@npm:13.0.5" dependencies: @@ -3491,6 +3490,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/fake-timers@npm:^15.0.0": + version: 15.1.1 + resolution: "@sinonjs/fake-timers@npm:15.1.1" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + checksum: 10/f262d613ea7f7cdb1b5d90c0cae01b7c6b797d6d0f1ca0fe30b7b69012e3076bb8a0f69d735bc69d2824b9bb1efb8554ca9765b4a6bb22defdec9ce79e7cd8a4 + languageName: node + linkType: hard + "@sinonjs/samsam@npm:^8.0.1": version: 8.0.3 resolution: "@sinonjs/samsam@npm:8.0.3" @@ -3829,6 +3837,7 @@ __metadata: sinon-chai: "npm:^3.7.0" ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" + zod: "npm:^4.0.0" peerDependencies: "@sitecore-content-sdk/analytics-core": ^2.0.0-canary "@sitecore-content-sdk/events": ^2.0.0-canary @@ -3881,18 +3890,18 @@ __metadata: linkType: hard "@stylistic/eslint-plugin@npm:^5.2.2": - version: 5.7.1 - resolution: "@stylistic/eslint-plugin@npm:5.7.1" + version: 5.10.0 + resolution: "@stylistic/eslint-plugin@npm:5.10.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/types": "npm:^8.53.1" + "@typescript-eslint/types": "npm:^8.56.0" eslint-visitor-keys: "npm:^4.2.1" espree: "npm:^10.4.0" estraverse: "npm:^5.3.0" picomatch: "npm:^4.0.3" peerDependencies: - eslint: ">=9.0.0" - checksum: 10/df50c60e111e79342c2a677c05fff13b02206a0480e3b3fe17e1faf901147e1a2339994b1106a38cc835d20b7b313f1e3e6c9b111b117128ad15cecc9b107c1c + eslint: ^9.0.0 || ^10.0.0 + checksum: 10/d55706b09a55defbc5afbfc251c4ecc0acfcbc3e741c34bec461365b399d2c9d89a6cafac069356ea03e231f3b12d5cc9550e66603ac343c29e07f2b2cd30464 languageName: node linkType: hard @@ -4278,20 +4287,20 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 25.1.0 - resolution: "@types/node@npm:25.1.0" + version: 25.5.0 + resolution: "@types/node@npm:25.5.0" dependencies: - undici-types: "npm:~7.16.0" - checksum: 10/ad15db75cfaec9e6311a1db7f3a91799be4c2698e1cfb0f74cc4d6e4398361550d00a4070921e1de7d9a10db83e41fd29f3a640dfacdabbd1c74ae16d8ce943a + undici-types: "npm:~7.18.0" + checksum: 10/b1e8116bd8c9ff62e458b76d28a59cf7631537bb17e8961464bf754dd5b07b46f1620f568b2f89970505af9eef478dd74c614651b454c1ea95949ec472c64fcb languageName: node linkType: hard "@types/node@npm:^24.10.4": - version: 24.10.9 - resolution: "@types/node@npm:24.10.9" + version: 24.12.0 + resolution: "@types/node@npm:24.12.0" dependencies: undici-types: "npm:~7.16.0" - checksum: 10/c831395567689bdd59d0f3826332ff15c3f3f8e6acf160d7f75ce45e5476427c10ac484031c5c8d3480ae8d1729d95f3354ef4cc359364107e22da5a22e3797c + checksum: 10/e9dcf8a378af5a636353b6d88a6fae018504bab776410ac6b5411e29afbe601ba9d7957356556fc27268a62814ca4085974f785613482c18f739686efcd49655 languageName: node linkType: hard @@ -4326,11 +4335,11 @@ __metadata: linkType: hard "@types/react@npm:^19.2.7": - version: 19.2.10 - resolution: "@types/react@npm:19.2.10" + version: 19.2.14 + resolution: "@types/react@npm:19.2.14" dependencies: csstype: "npm:^3.2.2" - checksum: 10/0e34a0e42db02f4b3f4bed446128c555446c2854b833d71d597e6c8b08800dd90519a03a226ad250cccc4a0c9e51bd3f9c54cdcb79ee1219f4d5b318fb3a3813 + checksum: 10/fbff239089ee64b6bd9b00543594db498278b06de527ef1b0f71bb0eb09cc4445a71b5dd3c0d3d0257255c4eed94406be40a74ad4a987ade8a8d5dd65c82bc5f languageName: node linkType: hard @@ -4468,22 +4477,22 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": - version: 8.54.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.54.0" + version: 8.57.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.57.0" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.54.0" - "@typescript-eslint/type-utils": "npm:8.54.0" - "@typescript-eslint/utils": "npm:8.54.0" - "@typescript-eslint/visitor-keys": "npm:8.54.0" + "@typescript-eslint/scope-manager": "npm:8.57.0" + "@typescript-eslint/type-utils": "npm:8.57.0" + "@typescript-eslint/utils": "npm:8.57.0" + "@typescript-eslint/visitor-keys": "npm:8.57.0" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.4.0" peerDependencies: - "@typescript-eslint/parser": ^8.54.0 - eslint: ^8.57.0 || ^9.0.0 + "@typescript-eslint/parser": ^8.57.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/8f1c74ac77d7a84ae3f201bb09cb67271662befed036266af1eaa0653d09b545353441640516c1c86e0a94939887d32f0473c61a642488b14d46533742bfbd1b + checksum: 10/515ed019b16ff2ed4dacb1c2f1cd94227f16f93a8fe086d2bd60f78e6a36ffb20a048d55ddafdac4359d88d16f727c31b36814dba7479c4998f6ad0cc1da2c77 languageName: node linkType: hard @@ -4504,18 +4513,18 @@ __metadata: linkType: hard "@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/parser@npm:^8.39.0": - version: 8.54.0 - resolution: "@typescript-eslint/parser@npm:8.54.0" + version: 8.57.0 + resolution: "@typescript-eslint/parser@npm:8.57.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.54.0" - "@typescript-eslint/types": "npm:8.54.0" - "@typescript-eslint/typescript-estree": "npm:8.54.0" - "@typescript-eslint/visitor-keys": "npm:8.54.0" + "@typescript-eslint/scope-manager": "npm:8.57.0" + "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/typescript-estree": "npm:8.57.0" + "@typescript-eslint/visitor-keys": "npm:8.57.0" debug: "npm:^4.4.3" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/d2e09462c9966ef3deeba71d9e41d1d4876c61eea65888c93a3db6fba48b89a2165459c6519741d40e969da05ed98d3f4c87a7f56c5521ab5699743cc315f6cb + checksum: 10/9f51f8d8a81475d9870f380d9d737b9b59d89a0b7c8f9dce87e23b566d2b95986980717104dc87e2aa207de7ea0880f83963675fbe703c5531016dcacbc4c389 languageName: node linkType: hard @@ -4532,16 +4541,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/project-service@npm:8.54.0" +"@typescript-eslint/project-service@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/project-service@npm:8.57.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.54.0" - "@typescript-eslint/types": "npm:^8.54.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.57.0" + "@typescript-eslint/types": "npm:^8.57.0" debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/93f0483f6bbcf7cf776a53a130f7606f597fba67cf111e1897873bf1531efaa96e4851cfd461da0f0cc93afbdb51e47bcce11cf7dd4fb68b7030c7f9f240b92f + checksum: 10/4333c1ac52490926c780b2556d903b3d679d280e60b425d38ae851efa457ebe65b8aa9e1e88651e035527926a368cb52099f4bc395de7ec70f848430576c5db4 languageName: node linkType: hard @@ -4555,13 +4564,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/scope-manager@npm:8.54.0" +"@typescript-eslint/scope-manager@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/scope-manager@npm:8.57.0" dependencies: - "@typescript-eslint/types": "npm:8.54.0" - "@typescript-eslint/visitor-keys": "npm:8.54.0" - checksum: 10/3474f3197e8647754393dee62b3145c9de71eaa66c8a68f61c8283aa332141803885db9c96caa6a51f78128ad9ef92f774a90361655e57bd951d5b57eb76f914 + "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/visitor-keys": "npm:8.57.0" + checksum: 10/72a7086b1605f55dea36909d74e21b023ebd438b393e6ceb736ecc711f487d0add6d4f3648c1fc6c1a01faecd2a7a1f8839f92d8e7fa27f3937000f1fece2e33 languageName: node linkType: hard @@ -4574,12 +4583,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.54.0, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.54.0" +"@typescript-eslint/tsconfig-utils@npm:8.57.0, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/e9d6b29538716f007919bfcee94f09b7f8e7d2b684ad43d1a3c8d43afb9f0539c7707f84a34f42054e31c8c056b0ccf06575d89e860b4d34632ffefaefafe1fc + checksum: 10/cd451a0d1b19faa16314986bcb5aeb4bd98a77f23d4d627304434fc423689a675d6c009f19316006cdca4b83183951fcd8b56d721e595bb6b0d9d52ad0f43c5b languageName: node linkType: hard @@ -4599,19 +4608,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/type-utils@npm:8.54.0" +"@typescript-eslint/type-utils@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/type-utils@npm:8.57.0" dependencies: - "@typescript-eslint/types": "npm:8.54.0" - "@typescript-eslint/typescript-estree": "npm:8.54.0" - "@typescript-eslint/utils": "npm:8.54.0" + "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/typescript-estree": "npm:8.57.0" + "@typescript-eslint/utils": "npm:8.57.0" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.4.0" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/60e92fb32274abd70165ce6f4187e4cffa55416374c63731d7de8fdcfb7a558b4dd48909ff1ad38ac39d2ea1248ec54d6ce38dbc065fd34529a217fc2450d5b1 + checksum: 10/7ee7ca9090b973f77754e83aebf80c8263f02150109b844ccebb8f5db130b90b95af38343e875ade23fc520a197754107f3706fa0432ae2c32a32e95f1399350 languageName: node linkType: hard @@ -4622,10 +4631,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.54.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.53.1, @typescript-eslint/types@npm:^8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/types@npm:8.54.0" - checksum: 10/c25cc0bdf90fb150cf6ce498897f43fe3adf9e872562159118f34bd91a9bfab5f720cb1a41f3cdf253b2e840145d7d372089b7cef5156624ef31e98d34f91b31 +"@typescript-eslint/types@npm:8.57.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/types@npm:8.57.0" + checksum: 10/ba23a4deeb5a89b9b99fee35f58d662901f236000d0f6bcada5143a2ef5ec831c7909e9192def8a48d18f8c3327b78bf3e9c02d770b4a4d721a0422b97ca1e29 languageName: node linkType: hard @@ -4649,22 +4658,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.54.0" +"@typescript-eslint/typescript-estree@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.57.0" dependencies: - "@typescript-eslint/project-service": "npm:8.54.0" - "@typescript-eslint/tsconfig-utils": "npm:8.54.0" - "@typescript-eslint/types": "npm:8.54.0" - "@typescript-eslint/visitor-keys": "npm:8.54.0" + "@typescript-eslint/project-service": "npm:8.57.0" + "@typescript-eslint/tsconfig-utils": "npm:8.57.0" + "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/visitor-keys": "npm:8.57.0" debug: "npm:^4.4.3" - minimatch: "npm:^9.0.5" + minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" tinyglobby: "npm:^0.2.15" ts-api-utils: "npm:^2.4.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/3a545037c6f9319251d3ba44cf7a3216b1372422469e27f7ed3415244ebf42553da1ab4644da42d3f0ae2706a8cad12529ffebcb2e75406f74e3b30b812d010d + checksum: 10/eae6027de9b8e0d5c443ad77219689c59dd02085867ea34c0613c93d625cbb9c517fe514fcc38061d49bd39422ca1f170764473b21db178e1db39deeeca6458b languageName: node linkType: hard @@ -4683,18 +4692,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/utils@npm:8.54.0" +"@typescript-eslint/utils@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/utils@npm:8.57.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.54.0" - "@typescript-eslint/types": "npm:8.54.0" - "@typescript-eslint/typescript-estree": "npm:8.54.0" + "@typescript-eslint/scope-manager": "npm:8.57.0" + "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/typescript-estree": "npm:8.57.0" peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/9f88a2a7ab3e11aa0ff7f99c0e66a0cf2cba10b640def4c64a4f4ef427fecfb22f28dbe5697535915eb01f6507515ac43e45e0ff384bf82856e3420194d9ffdd + checksum: 10/76e3c8eb9f6e28e4cf1359a1b32facaa7523464baeeba8f00a8d68a5a40b3d5d79cfffe48e85d365b06637b6ea6474f63f08a5b5844b2595c2e552e067dc9449 languageName: node linkType: hard @@ -4708,13 +4717,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.54.0": - version: 8.54.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.54.0" +"@typescript-eslint/visitor-keys@npm:8.57.0": + version: 8.57.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.57.0" dependencies: - "@typescript-eslint/types": "npm:8.54.0" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10/cca5380ee30250302ee1459e5a0a38de8c16213026dbbff3d167fa7d71d012f31d60ac4483ad45ebd13f2ac963d1ca52dd5f22759a68d4ee57626e421769187a + "@typescript-eslint/types": "npm:8.57.0" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10/049edd9e6a5e919bed84bffeefa3d3d944295183feaeb175119c17bcbefa051f10e0e135e4a4dc545c5aa781bd11a276ec5e62fd1211f6692c06a84036b8c4c5 languageName: node linkType: hard @@ -4950,20 +4959,20 @@ __metadata: linkType: hard "acorn-walk@npm:^8.0.2, acorn-walk@npm:^8.1.1": - version: 8.3.4 - resolution: "acorn-walk@npm:8.3.4" + version: 8.3.5 + resolution: "acorn-walk@npm:8.3.5" dependencies: acorn: "npm:^8.11.0" - checksum: 10/871386764e1451c637bb8ab9f76f4995d408057e9909be6fb5ad68537ae3375d85e6a6f170b98989f44ab3ff6c74ad120bc2779a3d577606e7a0cd2b4efcaf77 + checksum: 10/f52a158a1c1f00c82702c7eb9b8ae8aad79748a7689241dcc2d797dce680f1dcb15c78f312f687eeacdfb3a4cac4b87d04af470f0201bd56c6661fca6f94b195 languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1": - version: 8.15.0 - resolution: "acorn@npm:8.15.0" +"acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1": + version: 8.16.0 + resolution: "acorn@npm:8.16.0" bin: acorn: bin/acorn - checksum: 10/77f2de5051a631cf1729c090e5759148459cdb76b5f5c70f890503d629cf5052357b0ce783c0f976dd8a93c5150f59f6d18df1def3f502396a20f81282482fa4 + checksum: 10/690c673bb4d61b38ef82795fab58526471ad7f7e67c0e40c4ff1e10ecd80ce5312554ef633c9995bfc4e6d170cef165711f9ca9e49040b62c0c66fbf2dd3df2b languageName: node linkType: hard @@ -5035,51 +5044,27 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.4": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" +"ajv@npm:^6.14.0": + version: 6.14.0 + resolution: "ajv@npm:6.14.0" dependencies: fast-deep-equal: "npm:^3.1.1" fast-json-stable-stringify: "npm:^2.0.0" json-schema-traverse: "npm:^0.4.1" uri-js: "npm:^4.2.2" - checksum: 10/48d6ad21138d12eb4d16d878d630079a2bda25a04e745c07846a4ad768319533031e28872a9b3c5790fa1ec41aabdf2abed30a56e5a03ebc2cf92184b8ee306c + checksum: 10/c71f14dd2b6f2535d043f74019c8169f7aeb1106bafbb741af96f34fdbf932255c919ddd46344043d03b62ea0ccb319f83667ec5eedf612393f29054fe5ce4a5 languageName: node linkType: hard -"ajv@npm:^8.0.0": - version: 8.17.1 - resolution: "ajv@npm:8.17.1" +"ajv@npm:^8.0.0, ajv@npm:~8.18.0": + version: 8.18.0 + resolution: "ajv@npm:8.18.0" dependencies: fast-deep-equal: "npm:^3.1.3" fast-uri: "npm:^3.0.1" json-schema-traverse: "npm:^1.0.0" require-from-string: "npm:^2.0.2" - checksum: 10/ee3c62162c953e91986c838f004132b6a253d700f1e51253b99791e2dbfdb39161bc950ebdc2f156f8568035bb5ed8be7bd78289cd9ecbf3381fe8f5b82e3f33 - languageName: node - linkType: hard - -"ajv@npm:~8.12.0": - version: 8.12.0 - resolution: "ajv@npm:8.12.0" - dependencies: - fast-deep-equal: "npm:^3.1.1" - json-schema-traverse: "npm:^1.0.0" - require-from-string: "npm:^2.0.2" - uri-js: "npm:^4.2.2" - checksum: 10/b406f3b79b5756ac53bfe2c20852471b08e122bc1ee4cde08ae4d6a800574d9cd78d60c81c69c63ff81e4da7cd0b638fafbb2303ae580d49cf1600b9059efb85 - languageName: node - linkType: hard - -"ajv@npm:~8.13.0": - version: 8.13.0 - resolution: "ajv@npm:8.13.0" - dependencies: - fast-deep-equal: "npm:^3.1.3" - json-schema-traverse: "npm:^1.0.0" - require-from-string: "npm:^2.0.2" - uri-js: "npm:^4.4.1" - checksum: 10/4ada268c9a6e44be87fd295df0f0a91267a7bae8dbc8a67a2d5799c3cb459232839c99d18b035597bb6e3ffe88af6979f7daece854f590a81ebbbc2dfa80002c + checksum: 10/bfed9de827a2b27c6d4084324eda76a4e32bdde27410b3e9b81d06e6f8f5c78370fc6b93fe1d869f1939ff1d7c4ae8896960995acb8425e3e9288c8884247c48 languageName: node linkType: hard @@ -5106,7 +5091,7 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.0.1": +"ansi-regex@npm:^6.2.2": version: 6.2.2 resolution: "ansi-regex@npm:6.2.2" checksum: 10/9b17ce2c6daecc75bcd5966b9ad672c23b184dc3ed9bf3c98a0702f0d2f736c15c10d461913568f2cf527a5e64291c7473358885dd493305c84a1cfed66ba94f @@ -5456,13 +5441,13 @@ __metadata: linkType: hard "axios@npm:^1.0.0": - version: 1.13.4 - resolution: "axios@npm:1.13.4" + version: 1.13.6 + resolution: "axios@npm:1.13.6" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.4" + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" proxy-from-env: "npm:^1.1.0" - checksum: 10/54b7ef71c64837f9d52475832337f520cf6fa85c94612e03a3a2aad7082804a2544741267122696662147e90e6d2746601346984cf531ae715ecdb56d586a04c + checksum: 10/a7ed83c2af3ef21d64609df0f85e76893a915a864c5934df69241001d0578082d6521a0c730bf37518ee458821b5695957cb10db9fc705f2a8996c8686ea7a89 languageName: node linkType: hard @@ -5572,6 +5557,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10/fb07bb66a0959c2843fc055838047e2a95ccebb837c519614afb067ebfdf2fa967ca8d712c35ced07f2cd26fc6f07964230b094891315ad74f11eba3d53178a0 + languageName: node + linkType: hard + "base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -5580,11 +5572,11 @@ __metadata: linkType: hard "baseline-browser-mapping@npm:^2.8.3, baseline-browser-mapping@npm:^2.9.0": - version: 2.9.19 - resolution: "baseline-browser-mapping@npm:2.9.19" + version: 2.10.8 + resolution: "baseline-browser-mapping@npm:2.10.8" bin: - baseline-browser-mapping: dist/cli.js - checksum: 10/8d7bbb7fe3d1ad50e04b127c819ba6d059c01ed0d2a7a5fc3327e23a8c42855fa3a8b510550c1fe1e37916147e6a390243566d3ef85bf6130c8ddfe5cc3db530 + baseline-browser-mapping: dist/cli.cjs + checksum: 10/820972372c87c65c2e665134d70aa44d5722492fb907aa79170fec84086a75de4675f6a7b717cf0a31b4c4f71cd0289b056b71e32007de97a37973a501d31dcb languageName: node linkType: hard @@ -5630,7 +5622,7 @@ __metadata: languageName: node linkType: hard -"brace-expansion@npm:^2.0.1": +"brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2": version: 2.0.2 resolution: "brace-expansion@npm:2.0.2" dependencies: @@ -5639,6 +5631,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.2": + version: 5.0.4 + resolution: "brace-expansion@npm:5.0.4" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10/cfd57e20d8ded9578149e47ae4d3fff2b2f78d06b54a32a73057bddff65c8e9b930613f0cbcfefedf12dd117151e19d4da16367d5127c54f3bff02d8a4479bb2 + languageName: node + linkType: hard + "braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -5850,9 +5851,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001766 - resolution: "caniuse-lite@npm:1.0.30001766" - checksum: 10/0edeb69bdb741c98deda609b75e35e5c31e944537e9873ea89a4cf9e9baa3e158675b224951a6f3a212d1f0c328a8494ace323c551ca1fa58ce4915562c4e4d3 + version: 1.0.30001779 + resolution: "caniuse-lite@npm:1.0.30001779" + checksum: 10/025b74d98d6bb05d3f95038ad7f9d621fab980768228c166f72bc012ee2f62e65dfc793fa64abab5de079bbfe7f2f831c30cfd22b82335e15107ef4deb27208e languageName: node linkType: hard @@ -5967,9 +5968,9 @@ __metadata: linkType: hard "ci-info@npm:^4.2.0": - version: 4.3.1 - resolution: "ci-info@npm:4.3.1" - checksum: 10/9dc952bef67e665ccde2e7a552d42d5d095529d21829ece060a00925ede2dfa136160c70ef2471ea6ed6c9b133218b47c007f56955c0f1734a2e57f240aa7445 + version: 4.4.0 + resolution: "ci-info@npm:4.4.0" + checksum: 10/dfded0c630267d89660c8abb988ac8395a382bdfefedcc03e3e2858523312c5207db777c239c34774e3fcff11f015477c19d2ac8a58ea58aa476614a2e64f434 languageName: node linkType: hard @@ -6634,14 +6635,14 @@ __metadata: linkType: hard "dedent@npm:^1.0.0": - version: 1.7.1 - resolution: "dedent@npm:1.7.1" + version: 1.7.2 + resolution: "dedent@npm:1.7.2" peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: babel-plugin-macros: optional: true - checksum: 10/78785ef592e37e0b1ca7a7a5964c8f3dee1abdff46c5bb49864168579c122328f6bb55c769bc7e005046a7381c3372d3859f0f78ab083950fa146e1c24873f4f + checksum: 10/30b9062290dca72b0f5a6cd3667633448cef8cd0dec602eab61015741269ad49df90cabf0521f9a32d134ceab4e21aa7f097258c55cc3baadef94874686d6480 languageName: node linkType: hard @@ -6963,9 +6964,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.263": - version: 1.5.282 - resolution: "electron-to-chromium@npm:1.5.282" - checksum: 10/5d32107084ab72199088f2b67ddd632d3c69671ee1ae958cf16f7b9bc1f664cd4c587bb8c45cbd019e15ad49fcc80711c55119040c961c2e48dcf7ddae4961dd + version: 1.5.313 + resolution: "electron-to-chromium@npm:1.5.313" + checksum: 10/cf23275f15a0bb53ed7811fe75715a4c4bea33cb552caf80f3caf28cb1b46b7299bbc014afc2d4d89d14254723625f1e94be7f4c5a584285f967124c44dcdea4 languageName: node linkType: hard @@ -7140,8 +7141,8 @@ __metadata: linkType: hard "es-iterator-helpers@npm:^1.2.1": - version: 1.2.2 - resolution: "es-iterator-helpers@npm:1.2.2" + version: 1.3.1 + resolution: "es-iterator-helpers@npm:1.3.1" dependencies: call-bind: "npm:^1.0.8" call-bound: "npm:^1.0.4" @@ -7158,8 +7159,9 @@ __metadata: has-symbols: "npm:^1.1.0" internal-slot: "npm:^1.1.0" iterator.prototype: "npm:^1.1.5" + math-intrinsics: "npm:^1.1.0" safe-array-concat: "npm:^1.1.3" - checksum: 10/17b5b2834c4f5719d6ce0e837a4d11c6ba4640bee28290d22ec4daf7106ec3d5fe0ff4f7e5dbaa2b4612e8335934360e964a8f08608d43f2889da106b25481ee + checksum: 10/38106c081426faa6a8c27f44ee653d81350944b449fad81caa032cc02c31280be11fd302d065da3b062534390040c58e8aab55ff897b5ef1ddf060079570c70d languageName: node linkType: hard @@ -7212,35 +7214,35 @@ __metadata: linkType: hard "esbuild@npm:~0.27.0": - version: 0.27.2 - resolution: "esbuild@npm:0.27.2" - dependencies: - "@esbuild/aix-ppc64": "npm:0.27.2" - "@esbuild/android-arm": "npm:0.27.2" - "@esbuild/android-arm64": "npm:0.27.2" - "@esbuild/android-x64": "npm:0.27.2" - "@esbuild/darwin-arm64": "npm:0.27.2" - "@esbuild/darwin-x64": "npm:0.27.2" - "@esbuild/freebsd-arm64": "npm:0.27.2" - "@esbuild/freebsd-x64": "npm:0.27.2" - "@esbuild/linux-arm": "npm:0.27.2" - "@esbuild/linux-arm64": "npm:0.27.2" - "@esbuild/linux-ia32": "npm:0.27.2" - "@esbuild/linux-loong64": "npm:0.27.2" - "@esbuild/linux-mips64el": "npm:0.27.2" - "@esbuild/linux-ppc64": "npm:0.27.2" - "@esbuild/linux-riscv64": "npm:0.27.2" - "@esbuild/linux-s390x": "npm:0.27.2" - "@esbuild/linux-x64": "npm:0.27.2" - "@esbuild/netbsd-arm64": "npm:0.27.2" - "@esbuild/netbsd-x64": "npm:0.27.2" - "@esbuild/openbsd-arm64": "npm:0.27.2" - "@esbuild/openbsd-x64": "npm:0.27.2" - "@esbuild/openharmony-arm64": "npm:0.27.2" - "@esbuild/sunos-x64": "npm:0.27.2" - "@esbuild/win32-arm64": "npm:0.27.2" - "@esbuild/win32-ia32": "npm:0.27.2" - "@esbuild/win32-x64": "npm:0.27.2" + version: 0.27.4 + resolution: "esbuild@npm:0.27.4" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.4" + "@esbuild/android-arm": "npm:0.27.4" + "@esbuild/android-arm64": "npm:0.27.4" + "@esbuild/android-x64": "npm:0.27.4" + "@esbuild/darwin-arm64": "npm:0.27.4" + "@esbuild/darwin-x64": "npm:0.27.4" + "@esbuild/freebsd-arm64": "npm:0.27.4" + "@esbuild/freebsd-x64": "npm:0.27.4" + "@esbuild/linux-arm": "npm:0.27.4" + "@esbuild/linux-arm64": "npm:0.27.4" + "@esbuild/linux-ia32": "npm:0.27.4" + "@esbuild/linux-loong64": "npm:0.27.4" + "@esbuild/linux-mips64el": "npm:0.27.4" + "@esbuild/linux-ppc64": "npm:0.27.4" + "@esbuild/linux-riscv64": "npm:0.27.4" + "@esbuild/linux-s390x": "npm:0.27.4" + "@esbuild/linux-x64": "npm:0.27.4" + "@esbuild/netbsd-arm64": "npm:0.27.4" + "@esbuild/netbsd-x64": "npm:0.27.4" + "@esbuild/openbsd-arm64": "npm:0.27.4" + "@esbuild/openbsd-x64": "npm:0.27.4" + "@esbuild/openharmony-arm64": "npm:0.27.4" + "@esbuild/sunos-x64": "npm:0.27.4" + "@esbuild/win32-arm64": "npm:0.27.4" + "@esbuild/win32-ia32": "npm:0.27.4" + "@esbuild/win32-x64": "npm:0.27.4" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7296,7 +7298,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/7f1229328b0efc63c4184a61a7eb303df1e99818cc1d9e309fb92600703008e69821e8e984e9e9f54a627da14e0960d561db3a93029482ef96dc82dd267a60c2 + checksum: 10/32b46ec22ef78bae6cc141145022a4c0209852c07151f037fbefccc2033ca54e7f33705f8fca198eb7026f400142f64c2dbc9f0d0ce9c0a638ebc472a04abc4a languageName: node linkType: hard @@ -7622,30 +7624,30 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^5.0.0": - version: 5.0.0 - resolution: "eslint-visitor-keys@npm:5.0.0" - checksum: 10/05334d637c73d02f644b8dbfd6f555f049a229654b543b4b701944051072808d944368164c8b291cecb60e157a54d05f221eb45945a1bdd06c3e0e298ddb4678 +"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10/f9cc1a57b75e0ef949545cac33d01e8367e302de4c1483266ed4d8646ee5c306376660196bbb38b004e767b7043d1e661cb4336b49eff634a1bbe75c1db709ec languageName: node linkType: hard "eslint@npm:^9.32.0, eslint@npm:^9.39.1": - version: 9.39.2 - resolution: "eslint@npm:9.39.2" + version: 9.39.4 + resolution: "eslint@npm:9.39.4" dependencies: "@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.1" + "@eslint/config-array": "npm:^0.21.2" "@eslint/config-helpers": "npm:^0.4.2" "@eslint/core": "npm:^0.17.0" - "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.39.2" + "@eslint/eslintrc": "npm:^3.3.5" + "@eslint/js": "npm:9.39.4" "@eslint/plugin-kit": "npm:^0.4.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/retry": "npm:^0.4.2" "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.12.4" + ajv: "npm:^6.14.0" chalk: "npm:^4.0.0" cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" @@ -7664,7 +7666,7 @@ __metadata: is-glob: "npm:^4.0.0" json-stable-stringify-without-jsonify: "npm:^1.0.1" lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" + minimatch: "npm:^3.1.5" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" peerDependencies: @@ -7674,7 +7676,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/53ff0e9c8264e7e8d40d50fdc0c0df0b701cfc5289beedfb686c214e3e7b199702f894bbd1bb48653727bb1ecbd1147cf5f555a4ae71e1daf35020cdc9072d9f + checksum: 10/de95093d710e62e8c7e753220d985587c40f4a05247ed4393ffb6e6cb43a60e825a47fc5b4263151bb2fc5871a206a31d563ccbabdceee1711072ae12db90adf languageName: node linkType: hard @@ -7690,13 +7692,13 @@ __metadata: linkType: hard "espree@npm:^11.0.0": - version: 11.1.0 - resolution: "espree@npm:11.1.0" + version: 11.2.0 + resolution: "espree@npm:11.2.0" dependencies: - acorn: "npm:^8.15.0" + acorn: "npm:^8.16.0" acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^5.0.0" - checksum: 10/00215a8ed1faa7caf18f9b67a9ff266643d07e49b97056d90be7323c69fec90c887e478282578243a5cfc33b39a414ec598a9300f9f1f536ef1dbaafa2da7e09 + eslint-visitor-keys: "npm:^5.0.1" + checksum: 10/5cc4233b8f150010c70713669ef8231f07fe9b6391870cfa0a292a07f723ed5c2922064b978e627f7cabf7753280e64c5bde41d3840caaa40946989df7009a51 languageName: node linkType: hard @@ -7773,17 +7775,17 @@ __metadata: languageName: node linkType: hard -"expect@npm:30.2.0": - version: 30.2.0 - resolution: "expect@npm:30.2.0" +"expect@npm:30.3.0": + version: 30.3.0 + resolution: "expect@npm:30.3.0" dependencies: - "@jest/expect-utils": "npm:30.2.0" + "@jest/expect-utils": "npm:30.3.0" "@jest/get-type": "npm:30.1.0" - jest-matcher-utils: "npm:30.2.0" - jest-message-util: "npm:30.2.0" - jest-mock: "npm:30.2.0" - jest-util: "npm:30.2.0" - checksum: 10/cf98ab45ab2e9f2fb9943a3ae0097f72d63a94be179a19fd2818d8fdc3b7681d31cc8ef540606eb8dd967d9c44d73fef263a614e9de260c22943ffb122ad66fd + jest-matcher-utils: "npm:30.3.0" + jest-message-util: "npm:30.3.0" + jest-mock: "npm:30.3.0" + jest-util: "npm:30.3.0" + checksum: 10/607748963fd2cf2b95ec848d59086afdff5e6b690d1ddd907f84514687f32a787896281ba49a5fda2af819238bec7fdeaf258814997d2b08eedc0968de57f3bd languageName: node linkType: hard @@ -7930,11 +7932,11 @@ __metadata: linkType: hard "filelist@npm:^1.0.4": - version: 1.0.4 - resolution: "filelist@npm:1.0.4" + version: 1.0.6 + resolution: "filelist@npm:1.0.6" dependencies: minimatch: "npm:^5.0.1" - checksum: 10/4b436fa944b1508b95cffdfc8176ae6947b92825483639ef1b9a89b27d82f3f8aa22b21eed471993f92709b431670d4e015b39c087d435a61e1bb04564cf51de + checksum: 10/84a0be69efe6724c105f18c34e8a772370d9c45e53a1ba8ced7eecf4addd2c5a357347d94bfd8bfa9cbc36b09392cad70d82206305263e26bba184eea4ca8042 languageName: node linkType: hard @@ -8017,13 +8019,13 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.3.3 - resolution: "flatted@npm:3.3.3" - checksum: 10/8c96c02fbeadcf4e8ffd0fa24983241e27698b0781295622591fc13585e2f226609d95e422bcf2ef044146ffacb6b68b1f20871454eddf75ab3caa6ee5f4a1fe + version: 3.4.1 + resolution: "flatted@npm:3.4.1" + checksum: 10/39a308e2ef82d2d8c80ebc74b67d4ff3f668be168137b649440b6735eb9c115d1e0c13ab0d9958b3d2ea9c85087ab7e14c14aa6f625a22b2916d89bbd91bc4a0 languageName: node linkType: hard -"follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.15.11": version: 1.15.11 resolution: "follow-redirects@npm:1.15.11" peerDependenciesMeta: @@ -8062,7 +8064,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0, form-data@npm:^4.0.4": +"form-data@npm:^4.0.0, form-data@npm:^4.0.5": version: 4.0.5 resolution: "form-data@npm:4.0.5" dependencies: @@ -8090,13 +8092,13 @@ __metadata: linkType: hard "fs-extra@npm:^11.1.0, fs-extra@npm:^11.3.0, fs-extra@npm:~11.3.0": - version: 11.3.3 - resolution: "fs-extra@npm:11.3.3" + version: 11.3.4 + resolution: "fs-extra@npm:11.3.4" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/daeaefafbebe8fa6efd2fb96fc926f2c952be5877811f00a6794f0d64e0128e3d0d93368cd328f8f063b45deacf385c40e3d931aa46014245431cd2f4f89c67a + checksum: 10/1b8deea9c540a2efe63c750bc9e1ba6238115579d1571d67fe8fb58e3fb6df19aba29fd4ebb81217cf0bf5bce0df30ca68dbc3e06f6652b856edd385ce0ff649 languageName: node linkType: hard @@ -8306,11 +8308,11 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.10.0, get-tsconfig@npm:^4.7.5": - version: 4.13.0 - resolution: "get-tsconfig@npm:4.13.0" + version: 4.13.6 + resolution: "get-tsconfig@npm:4.13.6" dependencies: resolve-pkg-maps: "npm:^1.0.0" - checksum: 10/3603c6da30e312636e4c20461e779114c9126601d1eca70ee4e36e3e3c00e3c21892d2d920027333afa2cc9e20998a436b14abe03a53cde40742581cb0e9ceb2 + checksum: 10/5cd1c1f273e9f1cd9f1ebeaaea281a3b7b71562fc9614ee0cf0575463b0435de68831354434a5a1a564e1049062d597d0dae8ef33f489a6d12afccee032f6784 languageName: node linkType: hard @@ -8444,13 +8446,13 @@ __metadata: linkType: hard "glob@npm:^13.0.0": - version: 13.0.0 - resolution: "glob@npm:13.0.0" + version: 13.0.6 + resolution: "glob@npm:13.0.6" dependencies: - minimatch: "npm:^10.1.1" - minipass: "npm:^7.1.2" - path-scurry: "npm:^2.0.0" - checksum: 10/de390721d29ee1c9ea41e40ec2aa0de2cabafa68022e237dc4297665a5e4d650776f2573191984ea1640aba1bf0ea34eddef2d8cbfbfc2ad24b5fb0af41d8846 + minimatch: "npm:^10.2.2" + minipass: "npm:^7.1.3" + path-scurry: "npm:^2.0.2" + checksum: 10/201ad69e5f0aa74e1d8c00a481581f8b8c804b6a4fbfabeeb8541f5d756932800331daeba99b58fb9e4cd67e12ba5a7eba5b82fb476691588418060b84353214 languageName: node linkType: hard @@ -8560,9 +8562,9 @@ __metadata: linkType: hard "graphql@npm:^16.11.0": - version: 16.12.0 - resolution: "graphql@npm:16.12.0" - checksum: 10/e299bc97cca178e549c8c1ed4cb164f631f07be987d3657f76cdf18c0250040cc0d456d4b6d41c87b855cac97b15a62ed345557527efcb0546492895a893bb87 + version: 16.13.1 + resolution: "graphql@npm:16.13.1" + checksum: 10/a42f857f60351e1f4665aa5bc5524796d3f45bf81793e6db932f902aff769b0488dafaa1d9c07bddda7122a1f6d0b0105fab12d38992f6b6fb81fc3d7ced1afc languageName: node linkType: hard @@ -8822,7 +8824,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.7.0": +"iconv-lite@npm:^0.7.0, iconv-lite@npm:^0.7.2": version: 0.7.2 resolution: "iconv-lite@npm:0.7.2" dependencies: @@ -9463,10 +9465,10 @@ __metadata: languageName: node linkType: hard -"isexe@npm:^3.1.1": - version: 3.1.1 - resolution: "isexe@npm:3.1.1" - checksum: 10/7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10/2ead327ef596042ef9c9ec5f236b316acfaedb87f4bb61b3c3d574fb2e9c8a04b67305e04733bde52c24d9622fdebd3270aadb632adfbf9cadef88fe30f479e5 languageName: node linkType: hard @@ -9593,11 +9595,11 @@ __metadata: linkType: hard "jackspeak@npm:^4.1.1": - version: 4.1.1 - resolution: "jackspeak@npm:4.1.1" + version: 4.2.3 + resolution: "jackspeak@npm:4.2.3" dependencies: - "@isaacs/cliui": "npm:^8.0.2" - checksum: 10/ffceb270ec286841f48413bfb4a50b188662dfd599378ce142b6540f3f0a66821dc9dcb1e9ebc55c6c3b24dc2226c96e5819ba9bd7a241bd29031b61911718c7 + "@isaacs/cliui": "npm:^9.0.0" + checksum: 10/b88e3fe5fa04d34f0f939a15b7cef4a8589999b7a366ef89a3e0f2c45d2a7666066b67cbf46d57c3a4796a76d27b9d869b23d96a803dd834200d222c2a70de7e languageName: node linkType: hard @@ -9717,15 +9719,15 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:30.2.0": - version: 30.2.0 - resolution: "jest-diff@npm:30.2.0" +"jest-diff@npm:30.3.0": + version: 30.3.0 + resolution: "jest-diff@npm:30.3.0" dependencies: - "@jest/diff-sequences": "npm:30.0.1" + "@jest/diff-sequences": "npm:30.3.0" "@jest/get-type": "npm:30.1.0" chalk: "npm:^4.1.2" - pretty-format: "npm:30.2.0" - checksum: 10/1fb9e4fb7dff81814b4f69eaa7db28e184d62306a3a8ea2447d02ca53d2cfa771e83ede513f67ec5239dffacfaac32ff2b49866d211e4c7516f51c1fc06ede42 + pretty-format: "npm:30.3.0" + checksum: 10/9f566259085e6badd525dc48ee6de3792cfae080abd66e170ac230359cf32c4334d92f0f48b577a31ad2a6aed4aefde81f5f4366ab44a96f78bcde975e5cc26e languageName: node linkType: hard @@ -9805,25 +9807,25 @@ __metadata: languageName: node linkType: hard -"jest-haste-map@npm:30.2.0": - version: 30.2.0 - resolution: "jest-haste-map@npm:30.2.0" +"jest-haste-map@npm:30.3.0": + version: 30.3.0 + resolution: "jest-haste-map@npm:30.3.0" dependencies: - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" "@types/node": "npm:*" anymatch: "npm:^3.1.3" fb-watchman: "npm:^2.0.2" fsevents: "npm:^2.3.3" graceful-fs: "npm:^4.2.11" jest-regex-util: "npm:30.0.1" - jest-util: "npm:30.2.0" - jest-worker: "npm:30.2.0" - micromatch: "npm:^4.0.8" + jest-util: "npm:30.3.0" + jest-worker: "npm:30.3.0" + picomatch: "npm:^4.0.3" walker: "npm:^1.0.8" dependenciesMeta: fsevents: optional: true - checksum: 10/a88be6b0b672144aa30fe2d72e630d639c8d8729ee2cef84d0f830eac2005ac021cd8354f8ed8ecd74223f6a8b281efb62f466f5c9e01ed17650e38761051f4c + checksum: 10/0e0cc449d57414ac2d1f9ece64a98ffc4b4041fe3fba7cf9aaeb71089f7101583b1752e88aa4440d6fa71f86ef50d630be4f31f922cdf404d78655cb9811493b languageName: node linkType: hard @@ -9860,15 +9862,15 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:30.2.0": - version: 30.2.0 - resolution: "jest-matcher-utils@npm:30.2.0" +"jest-matcher-utils@npm:30.3.0": + version: 30.3.0 + resolution: "jest-matcher-utils@npm:30.3.0" dependencies: "@jest/get-type": "npm:30.1.0" chalk: "npm:^4.1.2" - jest-diff: "npm:30.2.0" - pretty-format: "npm:30.2.0" - checksum: 10/f3f1ecf68ca63c9d1d80a175637a8fc655edfd1ee83220f6e3f6bd464ecbe2f93148fdd440a5a5e5a2b0b2cc8ee84ddc3dcef58a6dbc66821c792f48d260c6d4 + jest-diff: "npm:30.3.0" + pretty-format: "npm:30.3.0" + checksum: 10/8aeef24fe2a21a3a22eb26a805c0a4c8ca8961aa1ebc07d680bf55b260f593814467bdfb60b271a3c239a411b2468f352c279cef466e35fd024d901ffa6cc942 languageName: node linkType: hard @@ -9884,20 +9886,20 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:30.2.0": - version: 30.2.0 - resolution: "jest-message-util@npm:30.2.0" +"jest-message-util@npm:30.3.0": + version: 30.3.0 + resolution: "jest-message-util@npm:30.3.0" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" "@types/stack-utils": "npm:^2.0.3" chalk: "npm:^4.1.2" graceful-fs: "npm:^4.2.11" - micromatch: "npm:^4.0.8" - pretty-format: "npm:30.2.0" + picomatch: "npm:^4.0.3" + pretty-format: "npm:30.3.0" slash: "npm:^3.0.0" stack-utils: "npm:^2.0.6" - checksum: 10/e29ec76e8c8e4da5f5b25198be247535626ccf3a940e93fdd51fc6a6bcf70feaa2921baae3806182a090431d90b08c939eb13fb64249b171d2e9ae3a452a8fd2 + checksum: 10/886577543ec60b421d21987190c5e393ff3652f4f2f2b504776d73f932518827b026ab8e6ffdb1f21ff5142ddf160ba4794e56d96143baeb4ae6939e040a10bd languageName: node linkType: hard @@ -9918,14 +9920,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:30.2.0": - version: 30.2.0 - resolution: "jest-mock@npm:30.2.0" +"jest-mock@npm:30.3.0": + version: 30.3.0 + resolution: "jest-mock@npm:30.3.0" dependencies: - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" "@types/node": "npm:*" - jest-util: "npm:30.2.0" - checksum: 10/cde9b56805f90bf811a9231873ee88a0fb83bf4bf50972ae76960725da65220fcb119688f2e90e1ef33fbfd662194858d7f43809d881f1c41bb55d94e62adeab + jest-util: "npm:30.3.0" + checksum: 10/9d2a9e52c2aebc486e9accaf641efa5c6589666e883b5ac1987261d0e2c105a06b885c22aeeb1cd7582e421970c95e34fe0b41bc4a8c06d7e3e4c27651e76ad1 languageName: node linkType: hard @@ -10052,32 +10054,32 @@ __metadata: languageName: node linkType: hard -"jest-snapshot@npm:30.2.0": - version: 30.2.0 - resolution: "jest-snapshot@npm:30.2.0" +"jest-snapshot@npm:30.3.0": + version: 30.3.0 + resolution: "jest-snapshot@npm:30.3.0" dependencies: "@babel/core": "npm:^7.27.4" "@babel/generator": "npm:^7.27.5" "@babel/plugin-syntax-jsx": "npm:^7.27.1" "@babel/plugin-syntax-typescript": "npm:^7.27.1" "@babel/types": "npm:^7.27.3" - "@jest/expect-utils": "npm:30.2.0" + "@jest/expect-utils": "npm:30.3.0" "@jest/get-type": "npm:30.1.0" - "@jest/snapshot-utils": "npm:30.2.0" - "@jest/transform": "npm:30.2.0" - "@jest/types": "npm:30.2.0" + "@jest/snapshot-utils": "npm:30.3.0" + "@jest/transform": "npm:30.3.0" + "@jest/types": "npm:30.3.0" babel-preset-current-node-syntax: "npm:^1.2.0" chalk: "npm:^4.1.2" - expect: "npm:30.2.0" + expect: "npm:30.3.0" graceful-fs: "npm:^4.2.11" - jest-diff: "npm:30.2.0" - jest-matcher-utils: "npm:30.2.0" - jest-message-util: "npm:30.2.0" - jest-util: "npm:30.2.0" - pretty-format: "npm:30.2.0" + jest-diff: "npm:30.3.0" + jest-matcher-utils: "npm:30.3.0" + jest-message-util: "npm:30.3.0" + jest-util: "npm:30.3.0" + pretty-format: "npm:30.3.0" semver: "npm:^7.7.2" synckit: "npm:^0.11.8" - checksum: 10/119390b49f397ed622ba7c375fc15f97af67c4fc49a34cf829c86ee732be2b06ad3c7171c76bb842a0e84a234783f1a4c721909aa316fbe00c6abc7c5962dfbc + checksum: 10/d9f75c436587410cc8170a710d53a632e148a648ec82476ef9e618d8067246e48af7c460773304ad53eecf748b118619a6afd87212f86d680d3439787b4fec39 languageName: node linkType: hard @@ -10109,17 +10111,17 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:30.2.0": - version: 30.2.0 - resolution: "jest-util@npm:30.2.0" +"jest-util@npm:30.3.0": + version: 30.3.0 + resolution: "jest-util@npm:30.3.0" dependencies: - "@jest/types": "npm:30.2.0" + "@jest/types": "npm:30.3.0" "@types/node": "npm:*" chalk: "npm:^4.1.2" ci-info: "npm:^4.2.0" graceful-fs: "npm:^4.2.11" - picomatch: "npm:^4.0.2" - checksum: 10/cf2f2fb83417ea69f9992121561c95cf4e9aad7946819b771b8b52addf78811101b33b51d0a39fa0c305f2751dab262feed7699de052659ff03d51827c8862f5 + picomatch: "npm:^4.0.3" + checksum: 10/4b016004637f6a53d6f54c993dc8904a4d6abe93acb8dd70622dc2ca80290a03692e834af1068969b486426e87d411144705edd4d772bb715a826d7e15b5a4b3 languageName: node linkType: hard @@ -10167,16 +10169,16 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:30.2.0": - version: 30.2.0 - resolution: "jest-worker@npm:30.2.0" +"jest-worker@npm:30.3.0": + version: 30.3.0 + resolution: "jest-worker@npm:30.3.0" dependencies: "@types/node": "npm:*" "@ungap/structured-clone": "npm:^1.3.0" - jest-util: "npm:30.2.0" + jest-util: "npm:30.3.0" merge-stream: "npm:^2.0.0" supports-color: "npm:^8.1.1" - checksum: 10/9354b0c71c80173f673da6bbc0ddaad26e4395b06532f7332e0c1e93e855b873b10139b040e01eda77f3dc5a0b67613e2bd7c56c4947ee771acfc3611de2ca29 + checksum: 10/6198e7462617e8f544b1ba593970fb7656e990aa87a2259f693edde106b5aecf63bae692e8d6adc4313efcaba283b15fc25f6834cacca12cf241da0ece722060 languageName: node linkType: hard @@ -10686,7 +10688,7 @@ __metadata: languageName: node linkType: hard -"lodash-es@npm:^4.17.21": +"lodash-es@npm:^4.17.23": version: 4.17.23 resolution: "lodash-es@npm:4.17.23" checksum: 10/1feae200df22eb0bd93ca86d485e77784b8a9fb1d13e91b66e9baa7a7e5e04be088c12a7e20c2250fc0bd3db1bc0ef0affc7d9e3810b6af2455a3c6bf6dde59e @@ -10721,7 +10723,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:~4.17.15": +"lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:^4.17.23, lodash@npm:~4.17.23": version: 4.17.23 resolution: "lodash@npm:4.17.23" checksum: 10/82504c88250f58da7a5a4289f57a4f759c44946c005dd232821c7688b5fcfbf4a6268f6a6cdde4b792c91edd2f3b5398c1d2a0998274432cff76def48735e233 @@ -10766,9 +10768,9 @@ __metadata: linkType: hard "lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.5 - resolution: "lru-cache@npm:11.2.5" - checksum: 10/be50f66c6e23afeaab9c7eefafa06344dd13cde7b3528809c2660c4ad70d93b9ba537366634623cbb2eb411671f526b5a4af2c602507b9258aead0fa8d713f6c + version: 11.2.7 + resolution: "lru-cache@npm:11.2.7" + checksum: 10/fbff4b8dee8189dde9b52cdfb3ea89b4c9cec094c1538cd30d1f47299477ff312efdb35f7994477ec72328f8e754e232b26a143feda1bd1f79ff22da6664d2c5 languageName: node linkType: hard @@ -10873,9 +10875,10 @@ __metadata: linkType: hard "make-fetch-happen@npm:^15.0.0": - version: 15.0.3 - resolution: "make-fetch-happen@npm:15.0.3" + version: 15.0.4 + resolution: "make-fetch-happen@npm:15.0.4" dependencies: + "@gar/promise-retry": "npm:^1.0.0" "@npmcli/agent": "npm:^4.0.0" cacache: "npm:^20.0.1" http-cache-semantics: "npm:^4.1.1" @@ -10885,9 +10888,8 @@ __metadata: minipass-pipeline: "npm:^1.2.4" negotiator: "npm:^1.0.0" proc-log: "npm:^6.0.0" - promise-retry: "npm:^2.0.1" ssri: "npm:^13.0.0" - checksum: 10/78da4fc1df83cb596e2bae25aa0653b8a9c6cbdd6674a104894e03be3acfcd08c70b78f06ef6407fbd6b173f6a60672480d78641e693d05eb71c09c13ee35278 + checksum: 10/4aa75baab500eff4259f2e1a3e76cf01ab3a3cd750037e4bd7b5e22bc5a60f12cc766b3c45e6288accb5ab609e88de5019a8014e0f96f6594b7b03cb504f4b81 languageName: node linkType: hard @@ -10915,8 +10917,8 @@ __metadata: linkType: hard "markdown-it@npm:^14.1.0": - version: 14.1.0 - resolution: "markdown-it@npm:14.1.0" + version: 14.1.1 + resolution: "markdown-it@npm:14.1.1" dependencies: argparse: "npm:^2.0.1" entities: "npm:^4.4.0" @@ -10926,7 +10928,7 @@ __metadata: uc.micro: "npm:^2.1.0" bin: markdown-it: bin/markdown-it.mjs - checksum: 10/f34f921be178ed0607ba9e3e27c733642be445e9bb6b1dba88da7aafe8ba1bc5d2f1c3aa8f3fc33b49a902da4e4c08c2feadfafb290b8c7dda766208bb6483a9 + checksum: 10/088822c8aa9346ba4af6a205f6ee0f4baae55e3314f040dc5c28c897d57d0f979840c71872b3582a6a6e572d8c851c54e323c82f4559011dfa2e96224fc20fc2 languageName: node linkType: hard @@ -10959,9 +10961,9 @@ __metadata: linkType: hard "meow@npm:^14.0.0": - version: 14.0.0 - resolution: "meow@npm:14.0.0" - checksum: 10/133e45ba802ac4d50a35f730a84631b8a98e15babf98abadb34768eb8a04e9336e39c92318137f16a99ada3984b5bcc29a4d5f811d9a22f1327fc056875d8e52 + version: 14.1.0 + resolution: "meow@npm:14.1.0" + checksum: 10/c6a22b3912a6bc849dee0d6475cd8bb63b9307e26919ca3ace28dc1aaf3d30257071de32bba496f7b5eec3e62b03a6b7731e3d04d18efb3c3103b829aad52ca5 languageName: node linkType: hard @@ -11045,12 +11047,12 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:10.0.3": - version: 10.0.3 - resolution: "minimatch@npm:10.0.3" +"minimatch@npm:10.2.3": + version: 10.2.3 + resolution: "minimatch@npm:10.2.3" dependencies: - "@isaacs/brace-expansion": "npm:^5.0.0" - checksum: 10/d5b8b2538b367f2cfd4aeef27539fddeee58d1efb692102b848e4a968a09780a302c530eb5aacfa8c57f7299155fb4b4e85219ad82664dcef5c66f657111d9b8 + brace-expansion: "npm:^5.0.2" + checksum: 10/186c6a6ce9f7a79ae7776efc799c32d1a6670ebbcc2a8756e6cb6ec4aab7439a6ca6e592e1a6aac5f21674eefae5b19821b8fa95072a4f4567da1ae40eb6075d languageName: node linkType: hard @@ -11063,39 +11065,39 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.1.1": - version: 10.1.1 - resolution: "minimatch@npm:10.1.1" +"minimatch@npm:^10.1.1, minimatch@npm:^10.2.2": + version: 10.2.4 + resolution: "minimatch@npm:10.2.4" dependencies: - "@isaacs/brace-expansion": "npm:^5.0.0" - checksum: 10/110f38921ea527022e90f7a5f43721838ac740d0a0c26881c03b57c261354fb9a0430e40b2c56dfcea2ef3c773768f27210d1106f1f2be19cde3eea93f26f45e + brace-expansion: "npm:^5.0.2" + checksum: 10/aea4874e521c55bb60744685bbffe3d152e5460f84efac3ea936e6bbe2ceba7deb93345fec3f9bb17f7b6946776073a64d40ae32bf5f298ad690308121068a1f languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2, minimatch@npm:^3.1.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" dependencies: brace-expansion: "npm:^1.1.7" - checksum: 10/e0b25b04cd4ec6732830344e5739b13f8690f8a012d73445a4a19fbc623f5dd481ef7a5827fde25954cd6026fede7574cc54dc4643c99d6c6b653d6203f94634 + checksum: 10/b11a7ee5773cd34c1a0c8436cdbe910901018fb4b6cb47aa508a18d567f6efd2148507959e35fba798389b161b8604a2d704ccef751ea36bd4582f9852b7d63f languageName: node linkType: hard "minimatch@npm:^5.0.1": - version: 5.1.6 - resolution: "minimatch@npm:5.1.6" + version: 5.1.9 + resolution: "minimatch@npm:5.1.9" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 10/126b36485b821daf96d33b5c821dac600cc1ab36c87e7a532594f9b1652b1fa89a1eebcaad4dff17c764dce1a7ac1531327f190fed5f97d8f6e5f889c116c429 + checksum: 10/23b4feb64dcb77ba93b70a72be551eb2e2677ac02178cf1ed3d38836cc4cd84802d90b77f60ef87f2bac64d270d2d8eba242e428f0554ea4e36bfdb7e9d25d0c languageName: node linkType: hard "minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": - version: 9.0.5 - resolution: "minimatch@npm:9.0.5" + version: 9.0.9 + resolution: "minimatch@npm:9.0.9" dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + brace-expansion: "npm:^2.0.2" + checksum: 10/b91fad937deaffb68a45a2cb731ff3cff1c3baf9b6469c879477ed16f15c8f4ce39d63a3f75c2455107c2fdff0f3ab597d97dc09e2e93b883aafcf926ef0c8f9 languageName: node linkType: hard @@ -11151,17 +11153,17 @@ __metadata: linkType: hard "minipass-fetch@npm:^5.0.0": - version: 5.0.0 - resolution: "minipass-fetch@npm:5.0.0" + version: 5.0.2 + resolution: "minipass-fetch@npm:5.0.2" dependencies: - encoding: "npm:^0.1.13" + iconv-lite: "npm:^0.7.2" minipass: "npm:^7.0.3" - minipass-sized: "npm:^1.0.3" + minipass-sized: "npm:^2.0.0" minizlib: "npm:^3.0.1" dependenciesMeta: - encoding: + iconv-lite: optional: true - checksum: 10/4fb7dca630a64e6970a8211dade505bfe260d0b8d60beb348dcdfb95fe35ef91d977b29963929c9017ae0805686aa3f413107dc6bc5deac9b9e26b0b41c3b86c + checksum: 10/4f3f65ea5b20a3a287765ebf21cc73e62031f754944272df2a3039296cc75a8fc2dc50b8a3c4f39ce3ac6e5cc583e8dc664d12c6ab98e0883d263e49f344bc86 languageName: node linkType: hard @@ -11202,6 +11204,15 @@ __metadata: languageName: node linkType: hard +"minipass-sized@npm:^2.0.0": + version: 2.0.0 + resolution: "minipass-sized@npm:2.0.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10/3b89adf64ca705662f77481e278eff5ec0a57aeffb5feba7cc8843722b1e7770efc880f2a17d1d4877b2d7bf227873cd46afb4da44c0fd18088b601ea50f96bb + languageName: node + linkType: hard + "minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": version: 3.3.6 resolution: "minipass@npm:3.3.6" @@ -11218,10 +11229,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": - version: 7.1.2 - resolution: "minipass@npm:7.1.2" - checksum: 10/c25f0ee8196d8e6036661104bacd743785b2599a21de5c516b32b3fa2b83113ac89a2358465bc04956baab37ffb956ae43be679b2262bf7be15fce467ccd7950 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10/175e4d5e20980c3cd316ae82d2c031c42f6c746467d8b1905b51060a0ba4461441a0c25bb67c025fd9617f9a3873e152c7b543c6b5ac83a1846be8ade80dffd6 languageName: node linkType: hard @@ -11472,13 +11483,13 @@ __metadata: linkType: hard "nock@npm:^14.0.10": - version: 14.0.10 - resolution: "nock@npm:14.0.10" + version: 14.0.11 + resolution: "nock@npm:14.0.11" dependencies: - "@mswjs/interceptors": "npm:^0.39.5" + "@mswjs/interceptors": "npm:^0.41.0" json-stringify-safe: "npm:^5.0.1" propagate: "npm:^2.0.0" - checksum: 10/89195d798117a371d0146008f1d32eb3951035cc7ccaf12fe93c7ffce6316172094c7ca0a7746b507397aeb06635904af235bbae8b645f95f24b5e8a812b07ab + checksum: 10/2fc579f3bee5148ebfacdfc7588a1f45205b139dcc6e32a5bef436f74f479383c7445a9812f433908600f62a0e142ff4bbbe7da7d5e9ed1781bad278b792cf98 languageName: node linkType: hard @@ -11491,6 +11502,18 @@ __metadata: languageName: node linkType: hard +"node-exports-info@npm:^1.6.0": + version: 1.6.0 + resolution: "node-exports-info@npm:1.6.0" + dependencies: + array.prototype.flatmap: "npm:^1.3.3" + es-errors: "npm:^1.3.0" + object.entries: "npm:^1.1.9" + semver: "npm:^6.3.1" + checksum: 10/0a1667d535f499ac1fe6c6d22f8146bc8b68abc76fa355856219202f6cf5f386027e0ff054e66a22d08be02acbc63fcdc9f98d0fbc97993f5eabc66408fdadad + languageName: node + linkType: hard + "node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -11574,9 +11597,9 @@ __metadata: linkType: hard "node-releases@npm:^2.0.27": - version: 2.0.27 - resolution: "node-releases@npm:2.0.27" - checksum: 10/f6c78ddb392ae500719644afcbe68a9ea533242c02312eb6a34e8478506eb7482a3fb709c70235b01c32fe65625b68dfa9665113f816d87f163bc3819b62b106 + version: 2.0.36 + resolution: "node-releases@npm:2.0.36" + checksum: 10/b31ead96e328b1775f07cad80c17b0601d0ee2894650b737e7ab5cbeb14e284e82dbc37ef38f1d915fa46dd7909781bd933d19b79cfe31b352573fac6da377aa languageName: node linkType: hard @@ -12403,13 +12426,13 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^2.0.0": - version: 2.0.1 - resolution: "path-scurry@npm:2.0.1" +"path-scurry@npm:^2.0.0, path-scurry@npm:^2.0.2": + version: 2.0.2 + resolution: "path-scurry@npm:2.0.2" dependencies: lru-cache: "npm:^11.0.0" minipass: "npm:^7.1.2" - checksum: 10/1e9c74e9ccf94d7c16056a5cb2dba9fa23eec1bc221ab15c44765486b9b9975b4cd9a4d55da15b96eadf67d5202e9a2f1cec9023fbb35fe7d9ccd0ff1891f88b + checksum: 10/2b4257422bcb870a4c2d205b3acdbb213a72f5e2250f61c80f79c9d014d010f82bdf8584441612c8e1fa4eb098678f5704a66fa8377d72646bad4be38e57a2c3 languageName: node linkType: hard @@ -12457,7 +12480,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3": +"picomatch@npm:^4.0.3": version: 4.0.3 resolution: "picomatch@npm:4.0.3" checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5 @@ -12558,14 +12581,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:30.2.0": - version: 30.2.0 - resolution: "pretty-format@npm:30.2.0" +"pretty-format@npm:30.3.0": + version: 30.3.0 + resolution: "pretty-format@npm:30.3.0" dependencies: "@jest/schemas": "npm:30.0.5" ansi-styles: "npm:^5.2.0" react-is: "npm:^18.3.1" - checksum: 10/725890d648e3400575eebc99a334a4cd1498e0d36746313913706bbeea20ada27e17c184a3cd45c50f705c16111afa829f3450233fc0fda5eed293c69757e926 + checksum: 10/b288db630841f2464554c5cfa7d7faf519ad7b5c05c3818e764c7cb486bcf59f240ea5576c748f8ca6625623c5856a8906642255bbe89d6cfa1a9090b0fbc6b9 languageName: node linkType: hard @@ -13109,15 +13132,18 @@ __metadata: linkType: hard "resolve@npm:^2.0.0-next.5": - version: 2.0.0-next.5 - resolution: "resolve@npm:2.0.0-next.5" + version: 2.0.0-next.6 + resolution: "resolve@npm:2.0.0-next.6" dependencies: - is-core-module: "npm:^2.13.0" + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + node-exports-info: "npm:^1.6.0" + object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10/2d6fd28699f901744368e6f2032b4268b4c7b9185fd8beb64f68c93ac6b22e52ae13560ceefc96241a665b985edf9ffd393ae26d2946a7d3a07b7007b7d51e79 + checksum: 10/c95cb98b8d3f9e2a979e6eb8b7e0b0e13f08da62607a45207275f151d640152244568a9a9cd01662a21e3746792177cbf9be1dacb88f7355edf4db49d9ee27e5 languageName: node linkType: hard @@ -13135,15 +13161,18 @@ __metadata: linkType: hard "resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": - version: 2.0.0-next.5 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d" + version: 2.0.0-next.6 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.6#optional!builtin::version=2.0.0-next.6&hash=c3c19d" dependencies: - is-core-module: "npm:^2.13.0" + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + node-exports-info: "npm:^1.6.0" + object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10/05fa778de9d0347c8b889eb7a18f1f06bf0f801b0eb4610b4871a4b2f22e220900cf0ad525e94f990bb8d8921c07754ab2122c0c225ab4cdcea98f36e64fa4c2 + checksum: 10/1b26738af76c80b341075e6bf4b202ef85f85f4a2cbf2934246c3b5f20c682cf352833fc6e32579c6967419226d3ab63e8d321328da052c87a31eaad91e3571a languageName: node linkType: hard @@ -13164,6 +13193,13 @@ __metadata: languageName: node linkType: hard +"retry@npm:^0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d + languageName: node + linkType: hard + "reusify@npm:^1.0.4": version: 1.1.0 resolution: "reusify@npm:1.1.0" @@ -13354,11 +13390,11 @@ __metadata: linkType: hard "semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.1, semver@npm:^7.7.2, semver@npm:^7.7.3": - version: 7.7.3 - resolution: "semver@npm:7.7.3" + version: 7.7.4 + resolution: "semver@npm:7.7.4" bin: semver: bin/semver.js - checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + checksum: 10/26bdc6d58b29528f4142d29afb8526bc335f4fc04c4a10f2b98b217f277a031c66736bf82d3d3bb354a2f6a3ae50f18fd62b053c4ac3f294a3d10a61f5075b75 languageName: node linkType: hard @@ -13773,9 +13809,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.22 - resolution: "spdx-license-ids@npm:3.0.22" - checksum: 10/a2f214aaf74c21a0172232367ce785157cef45d78617ee4d12aa1246350af566968e28b511e2096b707611566ac3959b85d8bf2d53a65bc6b66580735d3e1965 + version: 3.0.23 + resolution: "spdx-license-ids@npm:3.0.23" + checksum: 10/fead6be44478e4dd73a0721ae569f4a04f358846d8d82e8d92efae64aca928592e380cf17e8b84c25c948f3a7d8a0b4fc781a1830f3911ca87d52733265176b5 languageName: node linkType: hard @@ -13805,11 +13841,11 @@ __metadata: linkType: hard "ssri@npm:^13.0.0": - version: 13.0.0 - resolution: "ssri@npm:13.0.0" + version: 13.0.1 + resolution: "ssri@npm:13.0.1" dependencies: minipass: "npm:^7.0.3" - checksum: 10/fd59bfedf0659c1b83f6e15459162da021f08ec0f5834dd9163296f8b77ee82f9656aa1d415c3d3848484293e0e6aefdd482e863e52ddb53d520bb73da1eeec1 + checksum: 10/ae560d0378d074006a71b06af71bfbe84a3fe1ac6e16c1f07575f69e670d40170507fe52b21bcc23399429bc6a15f4bc3ea8d9bc88e9dfd7e87de564e6da6a72 languageName: node linkType: hard @@ -14002,11 +14038,11 @@ __metadata: linkType: hard "strip-ansi@npm:^7.0.1": - version: 7.1.2 - resolution: "strip-ansi@npm:7.1.2" + version: 7.2.0 + resolution: "strip-ansi@npm:7.2.0" dependencies: - ansi-regex: "npm:^6.0.1" - checksum: 10/db0e3f9654e519c8a33c50fc9304d07df5649388e7da06d3aabf66d29e5ad65d5e6315d8519d409c15b32fa82c1df7e11ed6f8cd50b0e4404463f0c9d77c8d0b + ansi-regex: "npm:^6.2.2" + checksum: 10/96da3bc6d73cfba1218625a3d66cf7d37a69bf0920d8735b28f9eeaafcdb6c1fe8440e1ae9eb1ba0ca355dbe8702da872e105e2e939fa93e7851b3cb5dd7d316 languageName: node linkType: hard @@ -14158,15 +14194,15 @@ __metadata: linkType: hard "tar@npm:^7.5.4": - version: 7.5.7 - resolution: "tar@npm:7.5.7" + version: 7.5.11 + resolution: "tar@npm:7.5.11" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/0d6938dd32fe5c0f17c8098d92bd9889ee0ed9d11f12381b8146b6e8c87bb5aa49feec7abc42463f0597503d8e89e4c4c0b42bff1a5a38444e918b4878b7fd21 + checksum: 10/fb2e77ee858a73936c68e066f4a602d428d6f812e6da0cc1e14a41f99498e4f7fd3535e355fa15157240a5538aa416026cfa6306bb0d1d1c1abf314b1f878e9a languageName: node linkType: hard @@ -14617,17 +14653,17 @@ __metadata: linkType: hard "typedoc-plugin-markdown@npm:^4.6.3": - version: 4.9.0 - resolution: "typedoc-plugin-markdown@npm:4.9.0" + version: 4.10.0 + resolution: "typedoc-plugin-markdown@npm:4.10.0" peerDependencies: typedoc: 0.28.x - checksum: 10/69293e9cd1a5cecd9d2f92872472dbc48b3912c658d0a2d07b6d7bd96bd1efa9617e9bea6a4e6181482610733533b1b058dc1fc505cac7931875f081f0023a0c + checksum: 10/d16bb8efdc5fc7553f8f90802866a94040a8f19e37ddc0d430c19dcff6dd2cca9af7e55dd2144103ab44efcc36022a55f8161ffefb0bb0a4a3c9f48a93545828 languageName: node linkType: hard "typedoc@npm:^0.28.4": - version: 0.28.16 - resolution: "typedoc@npm:0.28.16" + version: 0.28.17 + resolution: "typedoc@npm:0.28.17" dependencies: "@gerrit0/mini-shiki": "npm:^3.17.0" lunr: "npm:^2.3.9" @@ -14638,7 +14674,7 @@ __metadata: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x bin: typedoc: bin/typedoc - checksum: 10/657afd087d88a14cf77ad941de8913041a6a025e87c78833a5ee58a52b8252b685730394ab7920781b0b2faa0507b1c9d87fcf82dd86d1b788420716fc17ded3 + checksum: 10/09bf15a30c51c7e16533865fb7988cece90c9eb10f7fa239b7db0ec8000c69b63c6a9ee999f40e4783311a6b0d641fb384b76c273a12fcd09f3ef5e9315902f6 languageName: node linkType: hard @@ -14737,6 +14773,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.18.0": + version: 7.18.2 + resolution: "undici-types@npm:7.18.2" + checksum: 10/e61a5918f624d68420c3ca9d301e9f15b61cba6e97be39fe2ce266dd6151e4afe424d679372638826cb506be33952774e0424141200111a9857e464216c009af + languageName: node + linkType: hard + "unicorn-magic@npm:^0.3.0": version: 0.3.0 resolution: "unicorn-magic@npm:0.3.0" @@ -14889,7 +14932,7 @@ __metadata: languageName: node linkType: hard -"uri-js@npm:^4.2.2, uri-js@npm:^4.4.1": +"uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" dependencies: @@ -15183,13 +15226,13 @@ __metadata: linkType: hard "which@npm:^6.0.0": - version: 6.0.0 - resolution: "which@npm:6.0.0" + version: 6.0.1 + resolution: "which@npm:6.0.1" dependencies: - isexe: "npm:^3.1.1" + isexe: "npm:^4.0.0" bin: node-which: bin/which.js - checksum: 10/df19b2cd8aac94b333fa29b42e8e371a21e634a742a3b156716f7752a5afe1d73fb5d8bce9b89326f453d96879e8fe626eb421e0117eb1a3ce9fd8c97f6b7db9 + checksum: 10/dbea77c7d3058bf6c78bf9659d2dce4d2b57d39a15b826b2af6ac2e5a219b99dc8a831b79fdbc453c0598adb4f3f84cf9c2491fd52beb9f5d2dececcad117f68 languageName: node linkType: hard @@ -15551,3 +15594,10 @@ __metadata: checksum: 10/b2144b38807673a4254dae06fe1a212729550609e606289c305e45c585b36fab1dbba44fe6cde90db9b28be465ec63f4c2a50867aeec6672f6bc36b6c9a361a0 languageName: node linkType: hard + +"zod@npm:^4.0.0": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 10/25fc0f62e01b557b4644bf0b393bbaf47542ab30877c37837ea8caf314a8713d220c7d7fe51f68ffa72f0e1018ddfa34d96f1973d23033f5a2a5a9b6b9d9da01 + languageName: node + linkType: hard From 1274b62f7463750968b2961a8c262287423217b5 Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:56:28 +0200 Subject: [PATCH 02/32] [content] [react] [atoms] Introduce Atom Registry utils (#408) * introduce atom registry utils, for unfolding and serializing atom metadata; introduce AtomRenderer and send atom registry event on mount * post merge fixes, regenerate yarn lock * update unit tests * fix describe block only * add DesignLibraryAtoms component and atoms mode for design library * update atoms index name * do not use enum for atom type * rename children to allowedChildren in AtomsInfo * remove unfold atoms function --- .../content/src/client/sitecore-client.ts | 16 +- .../atoms-builder/atoms-builder.test.ts | 85 +++++ .../editing/atoms-builder/atoms-builder.ts | 85 +++++ .../src/editing/atoms-builder/index.ts | 7 + packages/content/src/editing/index.ts | 5 + .../templates/nextjs-app-router/package.json | 3 +- .../nextjs-app-router/src/Providers.tsx | 2 + .../src/components/atoms/index.ts | 1 + .../DesignLibrary/DesignLibraryApp.test.tsx | 33 +- .../DesignLibrary/DesignLibraryApp.tsx | 8 +- packages/nextjs/src/index.ts | 2 + packages/react/package.json | 2 +- .../src/atoms/atom-registry-utils.test.ts | 235 ++++++++++++ .../react/src/atoms/atom-registry-utils.ts | 69 ++++ packages/react/src/atoms/createAtom.ts | 13 +- packages/react/src/atoms/types.ts | 3 +- .../components/AtomRenderer/AtomRenderer.tsx | 11 + .../src/components/AtomRenderer/index.ts | 1 + .../DesignLibrary/DesignLibraryAtoms.tsx | 35 ++ .../src/components/DesignLibrary/index.ts | 1 + .../react/src/components/SitecoreProvider.tsx | 16 +- packages/react/src/index.ts | 8 +- yarn.lock | 352 +++++++++--------- 23 files changed, 788 insertions(+), 205 deletions(-) create mode 100644 packages/content/src/editing/atoms-builder/atoms-builder.test.ts create mode 100644 packages/content/src/editing/atoms-builder/atoms-builder.ts create mode 100644 packages/content/src/editing/atoms-builder/index.ts create mode 100644 packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts create mode 100644 packages/react/src/atoms/atom-registry-utils.test.ts create mode 100644 packages/react/src/atoms/atom-registry-utils.ts create mode 100644 packages/react/src/components/AtomRenderer/AtomRenderer.tsx create mode 100644 packages/react/src/components/AtomRenderer/index.ts create mode 100644 packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx diff --git a/packages/content/src/client/sitecore-client.ts b/packages/content/src/client/sitecore-client.ts index 6bcdb88362..1f2d5bd93e 100644 --- a/packages/content/src/client/sitecore-client.ts +++ b/packages/content/src/client/sitecore-client.ts @@ -74,6 +74,10 @@ export type PageMode = { * Whether the page is in variant generation mode */ isVariantGeneration: boolean; + /** + * Whether the page is in atoms editing mode + */ + isAtomsMode?: boolean; }; /** * Whether the page is in normal mode @@ -482,7 +486,11 @@ export class SitecoreClient implements BaseSitecoreClient { ); if (!data) { - throw new Error(`Unable to fetch editing data for preview ${JSON.stringify(previewData)}. ${ERROR_MESSAGES.CONTACT_SUPPORT}`); + throw new Error( + `Unable to fetch editing data for preview ${JSON.stringify(previewData)}. ${ + ERROR_MESSAGES.CONTACT_SUPPORT + }` + ); } let layout = data.layoutData; const personalizeData = getGroomedVariantIds(variantIds); @@ -536,7 +544,11 @@ export class SitecoreClient implements BaseSitecoreClient { ); if (!componentData) { - throw new Error(`Unable to fetch editing data for preview ${JSON.stringify(designLibData)}. ${ERROR_MESSAGES.CONTACT_SUPPORT}`); + throw new Error( + `Unable to fetch editing data for preview ${JSON.stringify(designLibData)}. ${ + ERROR_MESSAGES.CONTACT_SUPPORT + }` + ); } const layout = this.applyContentRewrite(componentData); const page: Page = { diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.test.ts b/packages/content/src/editing/atoms-builder/atoms-builder.test.ts new file mode 100644 index 0000000000..c635ab13cd --- /dev/null +++ b/packages/content/src/editing/atoms-builder/atoms-builder.test.ts @@ -0,0 +1,85 @@ +import { expect } from 'chai'; +import { AtomInfo, AtomType, getDesignLibraryAtomsRegistryEvent } from './atoms-builder'; + +describe('atoms-builder', () => { + describe('getDesignLibraryAtomsRegistryEvent', () => { + it('should return an event with name "atom:registry"', () => { + const event = getDesignLibraryAtomsRegistryEvent([]); + + expect(event.name).to.equal('atom:registry'); + }); + + it('should return an event with an empty atoms registry when given an empty array', () => { + const event = getDesignLibraryAtomsRegistryEvent([]); + + expect(event.message.atomsRegistry).to.deep.equal([]); + }); + + it('should return an event containing the provided atoms registry', () => { + const atomsRegistry: AtomInfo[] = [ + { + name: 'Button', + type: AtomType.ATOM, + description: 'A button atom', + props: { label: 'string' }, + allowedChildren: [], + }, + ]; + + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + + expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); + }); + + it('should return an event containing multiple atoms', () => { + const atomsRegistry: AtomInfo[] = [ + { + name: 'Button', + type: AtomType.ATOM, + description: 'A button atom', + props: { label: 'string' }, + allowedChildren: ['ButtonIcon'], + }, + { + name: 'ButtonIcon', + type: AtomType.ATOM_CHILD, + description: 'An icon child of Button', + props: { src: 'string' }, + allowedChildren: [], + }, + ]; + + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + + expect(event.message.atomsRegistry).to.have.length(2); + expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); + }); + + it('should include optional fields when provided', () => { + const atomsRegistry: AtomInfo[] = [ + { + name: 'Card', + version: 2, + type: AtomType.ATOM, + description: 'A card atom with optional fields', + props: { title: 'string' }, + allowedChildren: ['CardBody'], + defaultChildren: ['CardBody', { atom: 'CardFooter', props: { cta: 'Learn more' } }], + htmlEvents: ['click', 'focus'], + customEvents: { onExpand: 'CustomExpandEvent' }, + }, + ]; + + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + + const [atom] = event.message.atomsRegistry; + expect(atom.version).to.equal(2); + expect(atom.defaultChildren).to.deep.equal([ + 'CardBody', + { atom: 'CardFooter', props: { cta: 'Learn more' } }, + ]); + expect(atom.htmlEvents).to.deep.equal(['click', 'focus']); + expect(atom.customEvents).to.deep.equal({ onExpand: 'CustomExpandEvent' }); + }); + }); +}); diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.ts b/packages/content/src/editing/atoms-builder/atoms-builder.ts new file mode 100644 index 0000000000..f324268995 --- /dev/null +++ b/packages/content/src/editing/atoms-builder/atoms-builder.ts @@ -0,0 +1,85 @@ +import { DesignLibraryEvent } from '../design-library'; + +/** + * Event to send import map to design library + */ +const DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME = 'atom:registry'; + +/** + * Represents the type of atom, which can be either a top-level atom or an atom child + * @internal + */ +export const AtomType = { ATOM: 'atom', ATOM_CHILD: 'atom-child' } as const; + +export type SerializedDefaultChild = string | { atom: string; props?: Record }; + +/** + * Represents the serialized atom metadata information to be sent to design library + * @internal + */ +export type AtomInfo = { + /** + * The name of the atom, which should be unique across the registry and is used to identify the atom in the atom registry. + */ + name: string; + /** + * The optional version of the atom. + */ + version?: number; + /** + * The type of the atom, which can be either a top-level 'atom' or an 'atom-child'. + */ + type: (typeof AtomType)[keyof typeof AtomType]; + /** + * A description of the atom. + */ + description: string; + /** + * The properties of the atom. + */ + props: Record; + /** + * The allowed children of the atom, which define the nested structure of the atom. + */ + allowedChildren: string[]; + /** + * The default children of the atom. + */ + defaultChildren?: SerializedDefaultChild[]; + /** + * The HTML events of the atom, which define the standard DOM events the atom can handle. + */ + htmlEvents?: string[]; + /** + * The custom events of the atom, which define the non-standard events the atom can handle. + */ + customEvents?: Record; +}; + +/** + * Represents an event indicating the atom registry to be sent to design library + * @internal + */ +export interface DesignLibraryAtomsRegistryEvent extends DesignLibraryEvent { + name: typeof DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME; + message: { + atomsRegistry: AtomInfo[]; + }; +} + +/** + * Creates a DesignLibraryAtomsRegistryEvent with the given atoms registry. + * @param {AtomInfo[]} atomsRegistry - the atoms registry to be sent in the event + * @returns {DesignLibraryAtomsRegistryEvent} the created event with the atoms registry + * @internal + */ +export function getDesignLibraryAtomsRegistryEvent( + atomsRegistry: AtomInfo[] +): DesignLibraryAtomsRegistryEvent { + return { + name: DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME, + message: { + atomsRegistry, + }, + }; +} diff --git a/packages/content/src/editing/atoms-builder/index.ts b/packages/content/src/editing/atoms-builder/index.ts new file mode 100644 index 0000000000..0498960ac4 --- /dev/null +++ b/packages/content/src/editing/atoms-builder/index.ts @@ -0,0 +1,7 @@ +export { + AtomInfo, + AtomType, + SerializedDefaultChild, + DesignLibraryAtomsRegistryEvent, + getDesignLibraryAtomsRegistryEvent, +} from './atoms-builder'; diff --git a/packages/content/src/editing/index.ts b/packages/content/src/editing/index.ts index c27b43ce44..d86547fb72 100644 --- a/packages/content/src/editing/index.ts +++ b/packages/content/src/editing/index.ts @@ -41,3 +41,8 @@ export { COMPONENT_PREVIEW_CACHE_KEY_PREFIX, updateComponent, } from './design-library'; +export { + AtomInfo, + AtomType, + getDesignLibraryAtomsRegistryEvent, +} from './atoms-builder/atoms-builder'; diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json b/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json index c1b4f86561..40a123106e 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json @@ -37,7 +37,8 @@ "next": "^16.1.1", "react": "^19.2.1", "react-dom": "^19.2.1", - "next-intl": "^4.3.5" + "next-intl": "^4.3.5", + "zod": "^4.3.6" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx index 2bc2eed055..1c5682d358 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { Page, SitecoreProvider } from '@sitecore-content-sdk/nextjs'; import scConfig from 'sitecore.config'; import components from '.sitecore/component-map.client'; +import { atoms } from 'src/components/atoms'; export default function Providers({ children, page }: { children: React.ReactNode; page: Page }) { return ( @@ -11,6 +12,7 @@ export default function Providers({ children, page }: { children: React.ReactNod componentMap={components} page={page} loadImportMap={() => import('.sitecore/import-map.client')} + atoms={atoms} > {children} diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts new file mode 100644 index 0000000000..a826d81029 --- /dev/null +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts @@ -0,0 +1 @@ +export const atoms = []; diff --git a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx index 3ef371b322..32cf3a4f5c 100644 --- a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx +++ b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx @@ -18,6 +18,7 @@ use(sinonChai); describe('', () => { let DesignLibraryStub: sinon.SinonStub; + let DesignLibraryAtomsStub: sinon.SinonStub; let DesignLibraryServerStub: sinon.SinonStub; let DesignLibraryApp: any; const sandbox = sinon.createSandbox(); @@ -27,6 +28,10 @@ describe('', () => { .stub() .returns(
Client Component Rendered
); + DesignLibraryAtomsStub = sandbox + .stub() + .returns(
Atoms Component Rendered
); + DesignLibraryServerStub = sandbox .stub() .resolves(
Server Component Rendered
); @@ -35,6 +40,7 @@ describe('', () => { const module = proxyquire('./DesignLibraryApp', { '@sitecore-content-sdk/react': { DesignLibrary: DesignLibraryStub, + DesignLibraryAtoms: DesignLibraryAtomsStub, }, './DesignLibraryServer': { DesignLibraryServer: DesignLibraryServerStub, @@ -51,12 +57,17 @@ describe('', () => { const modeLibrary: PageMode = { name: DesignLibraryMode.Normal, isDesignLibrary: true, - designLibrary: { isVariantGeneration: false }, + designLibrary: { isVariantGeneration: false, isAtomsMode: false }, isNormal: false, isPreview: false, isEditing: true, }; + const modeLibraryAtoms: PageMode = { + ...modeLibrary, + designLibrary: { ...modeLibrary.designLibrary, isAtomsMode: true }, + }; + const ClientComponent: React.FC<{ [prop: string]: unknown }> = () =>
Client Component
; const ServerComponent: React.FC<{ [prop: string]: unknown }> = () =>
Server Component
; @@ -115,6 +126,26 @@ describe('', () => { render(awaitedDesignLibraryServer); expect(DesignLibraryStub).to.have.been.calledOnce; + expect(DesignLibraryAtomsStub).to.not.have.been.called; + expect(DesignLibraryServerStub).to.not.have.been.called; + }); + + it('should render DesignLibraryAtoms when isAtomsMode is true', () => { + const layoutData: LayoutServiceData = getTestLayoutData().layoutData; + const page = getPage(layoutData, modeLibraryAtoms); + const componentMap = createComponentMap('ContentBlock', 'client'); + + const awaitedDesignLibraryServer = DesignLibraryApp({ + page, + rendering: layoutData.sitecore.route as any, + componentMap, + loadServerImportMap: sinon.stub(), + }); + + render(awaitedDesignLibraryServer); + + expect(DesignLibraryAtomsStub).to.have.been.calledOnce; + expect(DesignLibraryStub).to.not.have.been.called; expect(DesignLibraryServerStub).to.not.have.been.called; }); diff --git a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx index 445befc264..0172a17b89 100644 --- a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx +++ b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { EDITING_COMPONENT_PLACEHOLDER } from '@sitecore-content-sdk/content/layout'; import { DesingLibraryAppProps } from './models'; import { DesignLibraryServer } from './DesignLibraryServer'; -import { DesignLibrary } from '@sitecore-content-sdk/react'; +import { DesignLibrary, DesignLibraryAtoms } from '@sitecore-content-sdk/react'; /** * Design Library component intended to be used by the NextJs app router application @@ -11,6 +11,7 @@ import { DesignLibrary } from '@sitecore-content-sdk/react'; * delegates to the appropriate rendering implementation: * - Client components are rendered using the `DesignLibrary` component * - Server components are rendered using the `DesignLibraryServer` component + * - Atoms mode components are rendered using the `DesignLibraryAtoms` component * @param {DesingLibraryAppProps} props - The properties for the Design Library App. * @public */ @@ -22,13 +23,16 @@ export const DesignLibraryApp = ({ const { route } = page.layout.sitecore; if (!route) return null; + const isAtomsMode = page.mode.designLibrary.isAtomsMode; const rendering = route?.placeholders[EDITING_COMPONENT_PLACEHOLDER]?.[0]; const component = componentMap.get(rendering?.componentName || ''); const isClient = component && component.componentType === 'client'; return ( <> - {isClient ? ( + {isAtomsMode ? ( + + ) : isClient ? ( ) : ( ({ + name, + type: AtomType.ATOM, + description: `${name} atom`, + props: z.object({}), + component: () => null, + allowedChildren, +}); + +describe('serializeAtoms', () => { + it('should serialize atom props to JSON schema', () => { + const propsSchema = z.object({ + label: z.string(), + count: z.number(), + }); + const atomA = createAtom('A'); + atomA.props = propsSchema; + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(1); + expect(result[0]).to.have.property('props'); + expect(result[0].props).to.be.an('object'); + expect(result[0].props.type).to.equal('object'); + }); + + it('should serialize allowedChildren as string array', () => { + const atomB = createAtom('B'); + const atomA = createAtom('A', [atomB, 'text', 'atom']); + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(2); + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.allowedChildren).to.deep.equal(['B', 'text', 'atom']); + }); + + it('should serialize defaultChildren with atom names', () => { + const defaultAtom = createAtom('DefaultChild'); + const atomA = createAtom('A'); + atomA.defaultChildren = [defaultAtom, { atom: defaultAtom, props: { key: 'value' } }]; + + const result = serializeAtoms([atomA]); + + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.defaultChildren).to.be.an('array'); + expect(serializedA.defaultChildren![0]).to.equal('DefaultChild'); + expect(serializedA.defaultChildren![1]).to.deep.equal({ + atom: 'DefaultChild', + props: { key: 'value' }, + }); + }); + + it('should serialize customEvents to JSON schema', () => { + const atomA = createAtom('A'); + atomA.customEvents = { + onClick: [z.string(), z.number()], + onHover: [z.boolean()], + }; + + const result = serializeAtoms([atomA]); + + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.customEvents).to.be.an('object'); + expect(serializedA.customEvents?.type).to.equal('object'); + expect(serializedA.customEvents?.properties).to.have.property('onClick'); + expect(serializedA.customEvents?.properties).to.have.property('onHover'); + }); + + it('should serialize atom without customEvents when not provided', () => { + const atomA = createAtom('A'); + + const result = serializeAtoms([atomA]); + + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.customEvents).to.equal(undefined); + }); + + it('should serialize multiple atoms with all properties', () => { + const propsSchema = z.object({ title: z.string() }); + const atomB = createAtom('B'); + const atomA = createAtom('A', [atomB]); + atomA.props = propsSchema; + atomA.customEvents = { onEvent: [z.string()] }; + atomA.htmlEvents = ['onClick', 'onHover']; + atomA.defaultChildren = [atomB]; + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(2); + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.props).to.be.an('object'); + expect(serializedA.allowedChildren).to.include('B'); + expect(serializedA.customEvents).to.be.an('object'); + expect(serializedA.htmlEvents).to.deep.equal(['onClick', 'onHover']); + expect(serializedA.defaultChildren).to.deep.equal(['B']); + }); + + it('should serialize atom props with provided metadata', () => { + const propsSchema = z.object({ + name: z.string().meta({ description: 'The name of the atom', default: 'Unnamed' }), + isActive: z.boolean().meta({ description: 'Whether the atom is active', default: false }), + }); + const atomA = createAtom('A'); + atomA.props = propsSchema; + + const result = serializeAtoms([atomA]); + + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.props).to.be.an('object'); + expect(serializedA.props?.properties).to.have.property('name'); + expect(serializedA.props?.properties).to.have.property('isActive'); + expect(serializedA.props?.properties?.name?.description).to.equal('The name of the atom'); + expect(serializedA.props?.properties?.name?.default).to.equal('Unnamed'); + expect(serializedA.props?.properties?.isActive?.description).to.equal( + 'Whether the atom is active' + ); + expect(serializedA.props?.properties?.isActive?.default).to.equal(false); + }); + + it('should serialize atom custom events with provided arguiment metadata', () => { + const atomA = createAtom('A'); + atomA.customEvents = { + onSubmit: [ + z.string().meta({ firstArgName: 'First argument' }), + z.number().meta({ secondArgName: 'Second argument' }), + ], + }; + + const result = serializeAtoms([atomA]); + + const serializedA = result.find((info) => info.name === 'A'); + if (!serializedA) throw new Error('serializedA should be defined'); + expect(serializedA.customEvents).to.be.an('object'); + expect(serializedA.customEvents?.properties).to.have.property('onSubmit'); + expect(serializedA.customEvents?.properties?.onSubmit?.items).to.have.length(2); + expect(serializedA.customEvents?.properties?.onSubmit?.items?.[0]?.firstArgName).to.equal( + 'First argument' + ); + expect(serializedA.customEvents?.properties?.onSubmit?.items?.[1]?.secondArgName).to.equal( + 'Second argument' + ); + }); + + it('should flatten three levels of nested allowedChildren', () => { + // A → B → C + const atomC = createAtom('C'); + const atomB = createAtom('B', [atomC]); + const atomA = createAtom('A', [atomB]); + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(3); + const names = result.map((r) => r.name); + expect(names).to.include.members(['A', 'B', 'C']); + + const serializedA = result.find((r) => r.name === 'A')!; + const serializedB = result.find((r) => r.name === 'B')!; + const serializedC = result.find((r) => r.name === 'C')!; + + // Each level's allowedChildren should be string names, not objects + expect(serializedA.allowedChildren).to.deep.equal(['B']); + expect(serializedB.allowedChildren).to.deep.equal(['C']); + expect(serializedC.allowedChildren).to.deep.equal([]); + }); + + it('should not duplicate atoms shared across multiple levels (diamond pattern)', () => { + // A allows B and C; B also allows C → C should appear only once + const atomC = createAtom('C'); + const atomB = createAtom('B', [atomC]); + const atomA = createAtom('A', [atomB, atomC]); + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(3); + const names = result.map((r) => r.name); + expect(names).to.include.members(['A', 'B', 'C']); + // Ensure no duplicates + expect(new Set(names).size).to.equal(3); + + const serializedA = result.find((r) => r.name === 'A')!; + expect(serializedA.allowedChildren).to.deep.equal(['B', 'C']); + }); + + it('should serialize defaultChildren at multiple levels of nesting', () => { + // A has defaultChildren referencing B; B has defaultChildren referencing C + const atomC = createAtom('C'); + const atomB = createAtom('B', [atomC]); + atomB.defaultChildren = [atomC, { atom: atomC, props: { key: 'val' } }]; + const atomA = createAtom('A', [atomB]); + atomA.defaultChildren = [atomB]; + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(3); + + const serializedA = result.find((r) => r.name === 'A')!; + const serializedB = result.find((r) => r.name === 'B')!; + + // A's defaultChildren → B serialized as name string + expect(serializedA.defaultChildren).to.deep.equal(['B']); + + // B's defaultChildren → C serialized as name string and { atom: 'C', props } + expect(serializedB.defaultChildren).to.deep.equal(['C', { atom: 'C', props: { key: 'val' } }]); + }); + + it('should handle mixed string and object allowedChildren at multiple levels', () => { + // B allows 'text' and 'atom' (strings) and a concrete atom C + const atomC = createAtom('C'); + const atomB = createAtom('B', ['text', 'atom', atomC]); + const atomA = createAtom('A', [atomB, 'text']); + + const result = serializeAtoms([atomA]); + + expect(result).to.have.length(3); + + const serializedA = result.find((r) => r.name === 'A')!; + const serializedB = result.find((r) => r.name === 'B')!; + + expect(serializedA.allowedChildren).to.deep.equal(['B', 'text']); + expect(serializedB.allowedChildren).to.deep.equal(['text', 'atom', 'C']); + }); +}); diff --git a/packages/react/src/atoms/atom-registry-utils.ts b/packages/react/src/atoms/atom-registry-utils.ts new file mode 100644 index 0000000000..0e3008b544 --- /dev/null +++ b/packages/react/src/atoms/atom-registry-utils.ts @@ -0,0 +1,69 @@ +import { z } from 'zod'; +import { AtomMetadata } from './types'; +import { AtomInfo } from '@sitecore-content-sdk/content/editing'; + +const isAtomMetadata = (value: unknown): value is AtomMetadata => { + return typeof value !== 'string'; +}; + +/** + * Serializes the provided atoms metadata array into a format suitable for broadcasting via postMessage to the host application + * @param {AtomMetadata[]} atoms - the atoms to be serialized + * @returns {AtomInfo[]} the serialized atoms + * @internal + */ +export const serializeAtoms = (atoms: AtomMetadata[]): AtomInfo[] => { + const atomInfos: AtomInfo[] = []; + const visited = new Set(); + + const visit = (atom: AtomMetadata): void => { + if (visited.has(atom.name)) return; + + visited.add(atom.name); + + // process allowed children + const allowedChildren: string[] = []; + for (const child of atom.allowedChildren ?? []) { + if (isAtomMetadata(child)) { + allowedChildren.push(child.name); + } else { + allowedChildren.push(child); + } + } + + // process default children + const defaultChildren = atom.defaultChildren?.map((child) => + 'name' in child ? child.name : { atom: child.atom.name, props: child.props } + ); + + // process custom events + let customEvents: Record | undefined = undefined; + if (atom.customEvents && Object.keys(atom.customEvents).length > 0) { + const eventsShape: Record = {}; + for (const [name, args] of Object.entries(atom.customEvents)) { + eventsShape[name] = z.tuple(args as [z.ZodType, ...z.ZodType[]]); + } + customEvents = z.toJSONSchema(z.object(eventsShape), { target: 'draft-7' }); + } + + atomInfos.push({ + name: atom.name, + version: atom.version, + type: atom.type, + description: atom.description, + props: z.toJSONSchema(atom.props, { target: 'draft-7' }), + allowedChildren, + defaultChildren, + htmlEvents: atom.htmlEvents, + customEvents, + }); + + for (const ch of atom.allowedChildren ?? []) { + if (typeof ch === 'object') visit(ch); + } + }; + + atoms.forEach(visit); + + return atomInfos; +}; diff --git a/packages/react/src/atoms/createAtom.ts b/packages/react/src/atoms/createAtom.ts index 7f5dc6ea57..b9dd8266ed 100644 --- a/packages/react/src/atoms/createAtom.ts +++ b/packages/react/src/atoms/createAtom.ts @@ -1,4 +1,5 @@ /** Component-first atom/atom-child definition; schema.type differentiates. */ +import { AtomType } from '@sitecore-content-sdk/content/editing'; import { z } from 'zod'; import type { AtomMetadata, @@ -20,7 +21,7 @@ export type AtomSchemaInput = { /** Human-readable summary for the component palette */ description: string; /** 'atom' (default) for top-level, 'atom-child' for scoped children */ - type?: 'atom' | 'atom-child'; + type?: (typeof AtomType)[keyof typeof AtomType]; /** Optional version for schema evolution */ version?: number; /** Zod schemas for editable props (keys must be component props excluding children/ref) */ @@ -49,17 +50,13 @@ export type AtomSchemaInput = { * @returns AtomMetadata with type taken from schema.type (default 'atom') * @public */ -export function createAtom( - component: C, - schema: AtomSchemaInput -): AtomMetadata { - const atomType = schema.type ?? 'atom'; +export function createAtom(component: C, schema: AtomSchemaInput): AtomMetadata { + const atomType = schema.type ?? AtomType.ATOM; const propsShape = schema.props as Record; const propsSchema = z.object(propsShape); const customEvents = - schema.customEvents && - Object.keys(schema.customEvents).length > 0 + schema.customEvents && Object.keys(schema.customEvents).length > 0 ? (Object.fromEntries( Object.entries(schema.customEvents).filter(([, v]) => v !== undefined) ) as Record) diff --git a/packages/react/src/atoms/types.ts b/packages/react/src/atoms/types.ts index a6614460cf..8b13141c05 100644 --- a/packages/react/src/atoms/types.ts +++ b/packages/react/src/atoms/types.ts @@ -1,4 +1,5 @@ /** Atom schema types. @public */ +import type { AtomType } from '@sitecore-content-sdk/content/editing'; import type { z } from 'zod'; import type { ComponentType } from 'react'; @@ -6,7 +7,7 @@ import type { ComponentType } from 'react'; export type AtomMetadata = { name: string; version?: number; - type: 'atom' | 'atom-child'; + type: (typeof AtomType)[keyof typeof AtomType]; description: string; props: z.ZodObject; component: (props: unknown) => React.ReactNode; diff --git a/packages/react/src/components/AtomRenderer/AtomRenderer.tsx b/packages/react/src/components/AtomRenderer/AtomRenderer.tsx new file mode 100644 index 0000000000..752be0652f --- /dev/null +++ b/packages/react/src/components/AtomRenderer/AtomRenderer.tsx @@ -0,0 +1,11 @@ +'use client'; +import React, { useEffect } from 'react'; +import { AtomMetadata } from '../../atoms'; + +export const AtomRenderer = ({ atoms }: { atoms?: AtomMetadata[] }) => { + useEffect(() => { + console.log('AtomRenderer, available atoms:', atoms); + }, [atoms]); + + return
Atoms Renderer
; +}; diff --git a/packages/react/src/components/AtomRenderer/index.ts b/packages/react/src/components/AtomRenderer/index.ts new file mode 100644 index 0000000000..0957e9ae25 --- /dev/null +++ b/packages/react/src/components/AtomRenderer/index.ts @@ -0,0 +1 @@ +export { AtomRenderer } from './AtomRenderer'; diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx new file mode 100644 index 0000000000..b86b0b5864 --- /dev/null +++ b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx @@ -0,0 +1,35 @@ +'use client'; +import React, { useEffect } from 'react'; +import { useSitecore } from '../SitecoreProvider'; +import { serializeAtoms } from '../../atoms/atom-registry-utils'; +import { + postToDesignLibrary, + getDesignLibraryAtomsRegistryEvent, +} from '@sitecore-content-sdk/content/editing'; +import { AtomRenderer } from '../AtomRenderer/AtomRenderer'; + +/** + * Design Library Atoms component. + * + * Facilitates the communication between the Design Studio and the Rendering Host when in atom rendering mode. + * - On mount, it unfolds and serializes the atoms registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. + * - Fetches Component model data, and passes it to the `AtomRenderer` which is responsible for rendering the low code component + * based on component model data and the available atoms. + * @internal + */ +export const DesignLibraryAtoms = () => { + const { atoms } = useSitecore(); + + useEffect(() => { + if (atoms) { + const serializedAtoms = serializeAtoms(atoms); + console.log('Serialized Atoms:', serializedAtoms); + + postToDesignLibrary(getDesignLibraryAtomsRegistryEvent(serializedAtoms)); + } + + console.log('Design Library Atoms mounted'); + }, [atoms]); + + return ; +}; diff --git a/packages/react/src/components/DesignLibrary/index.ts b/packages/react/src/components/DesignLibrary/index.ts index 6c9aef016c..bf0a4177d8 100644 --- a/packages/react/src/components/DesignLibrary/index.ts +++ b/packages/react/src/components/DesignLibrary/index.ts @@ -1,3 +1,4 @@ export { DesignLibrary } from './DesignLibrary'; +export { DesignLibraryAtoms } from './DesignLibraryAtoms'; export { DesignLibraryErrorBoundary } from './DesignLibraryErrorBoundary'; export { DynamicComponent, ImportMapImport } from './models'; diff --git a/packages/react/src/components/SitecoreProvider.tsx b/packages/react/src/components/SitecoreProvider.tsx index f3a529b7df..bbefe8380d 100644 --- a/packages/react/src/components/SitecoreProvider.tsx +++ b/packages/react/src/components/SitecoreProvider.tsx @@ -5,6 +5,7 @@ import { Page } from '@sitecore-content-sdk/content/client'; import { SitecoreConfig } from '@sitecore-content-sdk/content/config'; import { ComponentMap } from './sharedTypes'; import { ImportMapImport } from './DesignLibrary/models'; +import { AtomMetadata } from '../atoms/types'; export interface SitecoreProviderProps { /** @@ -24,6 +25,11 @@ export interface SitecoreProviderProps { */ loadImportMap: () => Promise; + /** + * The atoms metadata to be used in the Design Library. + */ + atoms?: AtomMetadata[]; + children: React.ReactNode; } @@ -46,6 +52,10 @@ export interface SitecoreProviderState { * The dynamic import for import map to be used in variant generation mode. */ loadImportMap: () => Promise; + /** + * The atoms metadata to be used in the Design Library. + */ + atoms?: AtomMetadata[]; /** * The component map to use for rendering components. */ @@ -88,12 +98,13 @@ export const ImportMapReactContext = React.createContext< * @param {SitecoreProviderProps['page']} props.page - The page data. * @param {SitecoreProviderProps['componentMap']} props.componentMap - The component map. * @param {SitecoreProviderProps['loadImportMap']} props.loadImportMap - The function to load the import map. + * @param {SitecoreProviderProps['atoms']} props.atoms - The atoms metadata. * @param {React.ReactNode} props.children - The children to render. * @returns {React.ReactNode} The SitecoreProvider component. * @public */ export const SitecoreProvider = (props: SitecoreProviderProps) => { - const { api, page: propsPage, componentMap, loadImportMap, children } = props; + const { api, page: propsPage, componentMap, loadImportMap, atoms, children } = props; const [page, setPageInternal] = useState(propsPage); @@ -117,8 +128,9 @@ export const SitecoreProvider = (props: SitecoreProviderProps) => { api, componentMap, loadImportMap, + atoms, }), - [page, setPage, api, componentMap, loadImportMap] + [page, setPage, api, componentMap, loadImportMap, atoms] ); return ( diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 3eb1eb2da5..d14fadde95 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -10,7 +10,11 @@ export { NativeDataFetcherConfig, } from '@sitecore-content-sdk/core'; export { EnhancedOmit } from '@sitecore-content-sdk/core/tools'; -export { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/content/editing'; +export { + isEditorActive, + resetEditorChromes, + AtomType, +} from '@sitecore-content-sdk/content/editing'; export { getContentStylesheetLink, getDesignLibraryStylesheetLinks, @@ -81,10 +85,12 @@ export { } from './components/FEaaS'; export { DesignLibrary, + DesignLibraryAtoms, DesignLibraryErrorBoundary, DynamicComponent, ImportMapImport, } from './components/DesignLibrary'; +export { AtomRenderer } from './components/AtomRenderer'; export {} from './components/FEaaS/BYOCComponent'; export { Link, LinkField, LinkFieldValue, LinkProps } from './components/Link'; export { File, FileField } from './components/File'; diff --git a/yarn.lock b/yarn.lock index bc6c43dc90..062c7224e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -144,23 +144,23 @@ __metadata: linkType: hard "@babel/helpers@npm:^7.28.6": - version: 7.28.6 - resolution: "@babel/helpers@npm:7.28.6" + version: 7.29.2 + resolution: "@babel/helpers@npm:7.29.2" dependencies: "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.28.6" - checksum: 10/213485cdfffc4deb81fc1bf2cefed61bc825049322590ef69690e223faa300a2a4d1e7d806c723bb1f1f538226b9b1b6c356ca94eb47fa7c6d9e9f251ee425e6 + "@babel/types": "npm:^7.29.0" + checksum: 10/ad77706f3f917bd224e037fd0fbc67c45b240d2a45981321b093f70b7c535bee9bbddb0a19e34c362cb000ae21cdd8638f8a87a5f305a5bd7547e93fdcc524fe languageName: node linkType: hard "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/parser@npm:7.29.0" + version: 7.29.2 + resolution: "@babel/parser@npm:7.29.2" dependencies: "@babel/types": "npm:^7.29.0" bin: parser: ./bin/babel-parser.js - checksum: 10/b1576dca41074997a33ee740d87b330ae2e647f4b7da9e8d2abd3772b18385d303b0cee962b9b88425e0f30d58358dbb8d63792c1a2d005c823d335f6a029747 + checksum: 10/45d050bf75aa5194b3255f156173e8553d615ff5a2434674cc4a10cdc7c261931befb8618c996a1c449b87f0ef32a3407879af2ac967d95dc7b4fdbae7037efa languageName: node linkType: hard @@ -352,9 +352,9 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.12.5": - version: 7.28.6 - resolution: "@babel/runtime@npm:7.28.6" - checksum: 10/fbcd439cb74d4a681958eb064c509829e3f46d8a4bfaaf441baa81bb6733d1e680bccc676c813883d7741bcaada1d0d04b15aa320ef280b5734e2192b50decf9 + version: 7.29.2 + resolution: "@babel/runtime@npm:7.29.2" + checksum: 10/f55ba4052aa0255055b34371a145fbe69c29b37b49eaea14805b095bfb4153701486416e89392fd27ec8abafa53868be86e960b9f8f959fff91f2c8ac2a14b02 languageName: node linkType: hard @@ -457,21 +457,21 @@ __metadata: linkType: hard "@emnapi/core@npm:^1.4.3": - version: 1.9.0 - resolution: "@emnapi/core@npm:1.9.0" + version: 1.9.1 + resolution: "@emnapi/core@npm:1.9.1" dependencies: "@emnapi/wasi-threads": "npm:1.2.0" tslib: "npm:^2.4.0" - checksum: 10/52d8dc5ba0d6814c5061686b8286d84cc5349c8fc09de3a9c4175bc2369c2890b335f7b03e55bc19ce3033158962cd817522fcb3bdeb1feb9ba7a060d61b69ab + checksum: 10/c44cfe471702b43306b84d0f4f2f1506dac0065dbd73dc5a41bd99a2c39802ca7e2d7ebfbfae8997468d1ff0420603596bf35b19eabd5951bad1eb630d2d4574 languageName: node linkType: hard "@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.0": - version: 1.9.0 - resolution: "@emnapi/runtime@npm:1.9.0" + version: 1.9.1 + resolution: "@emnapi/runtime@npm:1.9.1" dependencies: tslib: "npm:^2.4.0" - checksum: 10/d04a7e67676c2560d5394a01d63532af943760cf19cc8f375390a345aeab2b19e9ee35485b06b5c211df18f947fb14ac50658fca5c4067946f1e50af3490b3b5 + checksum: 10/337767fa44ec1f6277494342664be8773f16aad4086e9e49423a9f06c5eee7495e2e1b0b50dcd764c5a5cc4c15c9d80c13fba2da6763a97c06a48115cd7ccd14 languageName: node linkType: hard @@ -788,11 +788,9 @@ __metadata: linkType: hard "@gar/promise-retry@npm:^1.0.0": - version: 1.0.2 - resolution: "@gar/promise-retry@npm:1.0.2" - dependencies: - retry: "npm:^0.13.1" - checksum: 10/b91326999ce94677cbe91973079eabc689761a93a045f6a2d34d4070e9305b27f6c54e4021688c7080cb14caf89eafa0c0f300af741b94c20d18608bdb66ca46 + version: 1.0.3 + resolution: "@gar/promise-retry@npm:1.0.3" + checksum: 10/0d13ea3bb1025755e055648f6e290d2a7e0c87affaf552218f09f66b3fcd9ea9d5c9cc5fe2aa6e285e1530437768e40f9448fe9a86f4f3417b216dcf488d3d1a languageName: node linkType: hard @@ -2700,10 +2698,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:16.1.6": - version: 16.1.6 - resolution: "@next/env@npm:16.1.6" - checksum: 10/52f17ead23b3dc42e613be469b3a179c9c199f43ab42dfcba1f1bc25adba3d7818b4159ab1c797ddfa64a081c21cc1f753a997c14e164d8a88873e0774395d37 +"@next/env@npm:16.2.0": + version: 16.2.0 + resolution: "@next/env@npm:16.2.0" + checksum: 10/3b0e6c178f225a489767b04e2cfe66e9f278e0b9f900ec27882e8dcc00ba418f0c2af06bf224c92d81f82bcf3a1656946d3f39d75c863439869b0594958b194d languageName: node linkType: hard @@ -2716,58 +2714,58 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-darwin-arm64@npm:16.1.6" +"@next/swc-darwin-arm64@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-darwin-arm64@npm:16.2.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-darwin-x64@npm:16.1.6" +"@next/swc-darwin-x64@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-darwin-x64@npm:16.2.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" +"@next/swc-linux-arm64-gnu@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-linux-arm64-gnu@npm:16.2.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" +"@next/swc-linux-arm64-musl@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-linux-arm64-musl@npm:16.2.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" +"@next/swc-linux-x64-gnu@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-linux-x64-gnu@npm:16.2.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-linux-x64-musl@npm:16.1.6" +"@next/swc-linux-x64-musl@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-linux-x64-musl@npm:16.2.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" +"@next/swc-win32-arm64-msvc@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-win32-arm64-msvc@npm:16.2.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:16.1.6": - version: 16.1.6 - resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" +"@next/swc-win32-x64-msvc@npm:16.2.0": + version: 16.2.0 + resolution: "@next/swc-win32-x64-msvc@npm:16.2.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2977,6 +2975,13 @@ __metadata: languageName: node linkType: hard +"@npmcli/redact@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/redact@npm:4.0.0" + checksum: 10/5d52df2b5267f4369c97a2b2f7c427e3d7aa4b6a83e7a1b522e196f6e9d50024c620bd0cb2052067c74d1aaa0c330d9bc04e1d335bfb46180e705bb33423e74c + languageName: node + linkType: hard + "@npmcli/run-script@npm:^4.1.0, @npmcli/run-script@npm:^4.1.3, @npmcli/run-script@npm:^4.1.7": version: 4.2.1 resolution: "@npmcli/run-script@npm:4.2.1" @@ -3837,7 +3842,7 @@ __metadata: sinon-chai: "npm:^3.7.0" ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" - zod: "npm:^4.0.0" + zod: "npm:^4.3.6" peerDependencies: "@sitecore-content-sdk/analytics-core": ^2.0.0-canary "@sitecore-content-sdk/events": ^2.0.0-canary @@ -4087,11 +4092,11 @@ __metadata: linkType: hard "@types/debug@npm:^4.1.12": - version: 4.1.12 - resolution: "@types/debug@npm:4.1.12" + version: 4.1.13 + resolution: "@types/debug@npm:4.1.13" dependencies: "@types/ms": "npm:*" - checksum: 10/47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053 + checksum: 10/5091d4ebda85236e6f4a6ecea552860e521e11d1d388d3f6255b40726f5a4a7cf1baa0d09f60853838e4cac6c12a13b14114d5f422ccecaee4d1d07dab349900 languageName: node linkType: hard @@ -4477,22 +4482,22 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": - version: 8.57.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.57.0" + version: 8.57.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.57.1" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.57.0" - "@typescript-eslint/type-utils": "npm:8.57.0" - "@typescript-eslint/utils": "npm:8.57.0" - "@typescript-eslint/visitor-keys": "npm:8.57.0" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/type-utils": "npm:8.57.1" + "@typescript-eslint/utils": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.4.0" peerDependencies: - "@typescript-eslint/parser": ^8.57.0 + "@typescript-eslint/parser": ^8.57.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/515ed019b16ff2ed4dacb1c2f1cd94227f16f93a8fe086d2bd60f78e6a36ffb20a048d55ddafdac4359d88d16f727c31b36814dba7479c4998f6ad0cc1da2c77 + checksum: 10/339a1c3204d45b41cdfab210de624cdd621c6c3928beea018e9deca95aa62fd6bd38b380f858e4f548498bf5fc8b26375286e6f8e7be12e92db7e42161bf31a3 languageName: node linkType: hard @@ -4513,18 +4518,18 @@ __metadata: linkType: hard "@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/parser@npm:^8.39.0": - version: 8.57.0 - resolution: "@typescript-eslint/parser@npm:8.57.0" + version: 8.57.1 + resolution: "@typescript-eslint/parser@npm:8.57.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.57.0" - "@typescript-eslint/types": "npm:8.57.0" - "@typescript-eslint/typescript-estree": "npm:8.57.0" - "@typescript-eslint/visitor-keys": "npm:8.57.0" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" debug: "npm:^4.4.3" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/9f51f8d8a81475d9870f380d9d737b9b59d89a0b7c8f9dce87e23b566d2b95986980717104dc87e2aa207de7ea0880f83963675fbe703c5531016dcacbc4c389 + checksum: 10/b6b84c15f99221d23f2e1c1dc9db5eae0a7a6ff6eaeb4daa8ed8f1ee464c79dfd70041c2239f9387f778ee171c9c832cc3be7e9ba1d993cbe1b421cdecd2a51f languageName: node linkType: hard @@ -4541,16 +4546,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/project-service@npm:8.57.0" +"@typescript-eslint/project-service@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/project-service@npm:8.57.1" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.57.0" - "@typescript-eslint/types": "npm:^8.57.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.57.1" + "@typescript-eslint/types": "npm:^8.57.1" debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/4333c1ac52490926c780b2556d903b3d679d280e60b425d38ae851efa457ebe65b8aa9e1e88651e035527926a368cb52099f4bc395de7ec70f848430576c5db4 + checksum: 10/f3186b1bd68ea9dfaa73482575cad89c0c0c995354b4729911a1a11fd3a11882a8b8c7c2f331a6f7d45061150bb9babb86271c7d2cb463b6d25c8444fad18b84 languageName: node linkType: hard @@ -4564,13 +4569,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/scope-manager@npm:8.57.0" +"@typescript-eslint/scope-manager@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/scope-manager@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.57.0" - "@typescript-eslint/visitor-keys": "npm:8.57.0" - checksum: 10/72a7086b1605f55dea36909d74e21b023ebd438b393e6ceb736ecc711f487d0add6d4f3648c1fc6c1a01faecd2a7a1f8839f92d8e7fa27f3937000f1fece2e33 + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" + checksum: 10/633c13461f31c3d15ef017d73f9f6e6fcf84573205d2863635928f69a75fce48547697c8aac3cbe4c727f754c5c8fbfcc1838fbfeeec1179f3789e7a0798ca0d languageName: node linkType: hard @@ -4583,12 +4588,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.57.0, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.0" +"@typescript-eslint/tsconfig-utils@npm:8.57.1, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.1" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/cd451a0d1b19faa16314986bcb5aeb4bd98a77f23d4d627304434fc423689a675d6c009f19316006cdca4b83183951fcd8b56d721e595bb6b0d9d52ad0f43c5b + checksum: 10/432966db5b031a2b1a43a96d64b78e2a7965952c5902f04bf8b6b2b8e91abcd137e0f1a6d9787653c69b1ae2fb6b0a41525ebcf39c401104225224536373a41d languageName: node linkType: hard @@ -4608,19 +4613,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/type-utils@npm:8.57.0" +"@typescript-eslint/type-utils@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/type-utils@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.57.0" - "@typescript-eslint/typescript-estree": "npm:8.57.0" - "@typescript-eslint/utils": "npm:8.57.0" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" + "@typescript-eslint/utils": "npm:8.57.1" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.4.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/7ee7ca9090b973f77754e83aebf80c8263f02150109b844ccebb8f5db130b90b95af38343e875ade23fc520a197754107f3706fa0432ae2c32a32e95f1399350 + checksum: 10/3796ae162a00a74984085bef47c5b664fd19769e701a1d46aa2f2efc0e8d46759d781dfd258d763a044dc754485f9af5a6d79b04853ad7cd25134a8073cab58f languageName: node linkType: hard @@ -4631,10 +4636,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.57.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/types@npm:8.57.0" - checksum: 10/ba23a4deeb5a89b9b99fee35f58d662901f236000d0f6bcada5143a2ef5ec831c7909e9192def8a48d18f8c3327b78bf3e9c02d770b4a4d721a0422b97ca1e29 +"@typescript-eslint/types@npm:8.57.1, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/types@npm:8.57.1" + checksum: 10/3480785a7e833061579cf39f1f0e742925897ffb6510f5acbf4784f11eb60a5e737fc26e63b229c7cd1db5c39a1bf2f17d4c663be6c33b1a7db0ff0874f4fe35 languageName: node linkType: hard @@ -4658,14 +4663,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.57.0" +"@typescript-eslint/typescript-estree@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.57.1" dependencies: - "@typescript-eslint/project-service": "npm:8.57.0" - "@typescript-eslint/tsconfig-utils": "npm:8.57.0" - "@typescript-eslint/types": "npm:8.57.0" - "@typescript-eslint/visitor-keys": "npm:8.57.0" + "@typescript-eslint/project-service": "npm:8.57.1" + "@typescript-eslint/tsconfig-utils": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/visitor-keys": "npm:8.57.1" debug: "npm:^4.4.3" minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" @@ -4673,7 +4678,7 @@ __metadata: ts-api-utils: "npm:^2.4.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/eae6027de9b8e0d5c443ad77219689c59dd02085867ea34c0613c93d625cbb9c517fe514fcc38061d49bd39422ca1f170764473b21db178e1db39deeeca6458b + checksum: 10/9c2f7f7f42f1abcb9000e70bb8df08afbae162959a57e12d67f32d351a542b68e85a7ead5898de3ffac525a7a01b3ffb0d29006d0d19c97f79a5c31915bb62fb languageName: node linkType: hard @@ -4692,18 +4697,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/utils@npm:8.57.0" +"@typescript-eslint/utils@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/utils@npm:8.57.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.57.0" - "@typescript-eslint/types": "npm:8.57.0" - "@typescript-eslint/typescript-estree": "npm:8.57.0" + "@typescript-eslint/scope-manager": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/typescript-estree": "npm:8.57.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/76e3c8eb9f6e28e4cf1359a1b32facaa7523464baeeba8f00a8d68a5a40b3d5d79cfffe48e85d365b06637b6ea6474f63f08a5b5844b2595c2e552e067dc9449 + checksum: 10/456481b16627552f20c8c2cfe1d406d06abc64fa32b550bc6337d13975e548ce56922895751103190a62c56cf3ae11ca975383282f8dc11de1876c09e84f8606 languageName: node linkType: hard @@ -4717,13 +4722,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.57.0": - version: 8.57.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.57.0" +"@typescript-eslint/visitor-keys@npm:8.57.1": + version: 8.57.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.57.1" dependencies: - "@typescript-eslint/types": "npm:8.57.0" + "@typescript-eslint/types": "npm:8.57.1" eslint-visitor-keys: "npm:^5.0.0" - checksum: 10/049edd9e6a5e919bed84bffeefa3d3d944295183feaeb175119c17bcbefa051f10e0e135e4a4dc545c5aa781bd11a276ec5e62fd1211f6692c06a84036b8c4c5 + checksum: 10/692b95d204a35a00b3dd8d6a3ba86cce2f9ee1bf64691ca3be0fee2f393d6466560c8742fe3246fee63292165c5a682d98952c7ac4515062b5ac6eaf000b795a languageName: node linkType: hard @@ -5571,12 +5576,12 @@ __metadata: languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.8.3, baseline-browser-mapping@npm:^2.9.0": - version: 2.10.8 - resolution: "baseline-browser-mapping@npm:2.10.8" +"baseline-browser-mapping@npm:^2.9.0, baseline-browser-mapping@npm:^2.9.19": + version: 2.10.9 + resolution: "baseline-browser-mapping@npm:2.10.9" bin: baseline-browser-mapping: dist/cli.cjs - checksum: 10/820972372c87c65c2e665134d70aa44d5722492fb907aa79170fec84086a75de4675f6a7b717cf0a31b4c4f71cd0289b056b71e32007de97a37973a501d31dcb + checksum: 10/40ab7b9294b9b865d51e9c62c630c1416b15326bc7cb98040a3f4619676e1c024b6fc7c20c4169b7f4593769918461fab7bb3a3ce862c7833be72a9dc4161ade languageName: node linkType: hard @@ -5756,8 +5761,8 @@ __metadata: linkType: hard "cacache@npm:^20.0.1": - version: 20.0.3 - resolution: "cacache@npm:20.0.3" + version: 20.0.4 + resolution: "cacache@npm:20.0.4" dependencies: "@npmcli/fs": "npm:^5.0.0" fs-minipass: "npm:^3.0.0" @@ -5769,8 +5774,7 @@ __metadata: minipass-pipeline: "npm:^1.2.4" p-map: "npm:^7.0.2" ssri: "npm:^13.0.0" - unique-filename: "npm:^5.0.0" - checksum: 10/388a0169970df9d051da30437f93f81b7e91efb570ad0ff2b8fde33279fbe726c1bc8e8e2b9c05053ffb4f563854c73db395e8712e3b62347a1bc4f7fb8899ff + checksum: 10/02c1b4c57dc2473e6f4654220c9405b73ae5fcdb392f82a7cf535468a52b842690cdb3694861d13bbe4dc067d5f8abe9697b4f791ae5b65cd73d62abad1e3e54 languageName: node linkType: hard @@ -5851,9 +5855,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001779 - resolution: "caniuse-lite@npm:1.0.30001779" - checksum: 10/025b74d98d6bb05d3f95038ad7f9d621fab980768228c166f72bc012ee2f62e65dfc793fa64abab5de079bbfe7f2f831c30cfd22b82335e15107ef4deb27208e + version: 1.0.30001780 + resolution: "caniuse-lite@npm:1.0.30001780" + checksum: 10/4232c1bc97559c3052769b1ddbfa9e5095c4b48cbbf994db0515d4fd2d11bade9755f6f15dfbdd9a3cb7816109464b702d7241f3b299b0874a5dce8a355452d3 languageName: node linkType: hard @@ -6964,9 +6968,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.263": - version: 1.5.313 - resolution: "electron-to-chromium@npm:1.5.313" - checksum: 10/cf23275f15a0bb53ed7811fe75715a4c4bea33cb552caf80f3caf28cb1b46b7299bbc014afc2d4d89d14254723625f1e94be7f4c5a584285f967124c44dcdea4 + version: 1.5.321 + resolution: "electron-to-chromium@npm:1.5.321" + checksum: 10/ccb635798bf9451f4b77ef10288c43f39f30904300c176aac38dbb082ab080828588c3f48952f27b4157dcf068405dc44a7df439715907dadf9d65d9785215e7 languageName: node linkType: hard @@ -8019,9 +8023,9 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.4.1 - resolution: "flatted@npm:3.4.1" - checksum: 10/39a308e2ef82d2d8c80ebc74b67d4ff3f668be168137b649440b6735eb9c115d1e0c13ab0d9958b3d2ea9c85087ab7e14c14aa6f625a22b2916d89bbd91bc4a0 + version: 3.4.2 + resolution: "flatted@npm:3.4.2" + checksum: 10/a9e78fe5c2c1fcd98209a015ccee3a6caa953e01729778e83c1fe92e68601a63e1e69cd4e573010ca99eaf585a581b80ccf1018b99283e6cbc2117bcba1e030f languageName: node linkType: hard @@ -10875,11 +10879,12 @@ __metadata: linkType: hard "make-fetch-happen@npm:^15.0.0": - version: 15.0.4 - resolution: "make-fetch-happen@npm:15.0.4" + version: 15.0.5 + resolution: "make-fetch-happen@npm:15.0.5" dependencies: "@gar/promise-retry": "npm:^1.0.0" "@npmcli/agent": "npm:^4.0.0" + "@npmcli/redact": "npm:^4.0.0" cacache: "npm:^20.0.1" http-cache-semantics: "npm:^4.1.1" minipass: "npm:^7.0.2" @@ -10889,7 +10894,7 @@ __metadata: negotiator: "npm:^1.0.0" proc-log: "npm:^6.0.0" ssri: "npm:^13.0.0" - checksum: 10/4aa75baab500eff4259f2e1a3e76cf01ab3a3cd750037e4bd7b5e22bc5a60f12cc766b3c45e6288accb5ab609e88de5019a8014e0f96f6594b7b03cb504f4b81 + checksum: 10/d2649effb06c00cb2b266057cb1c8c1e99cfc8d1378e7d9c26cc8f00be41bc63d59b77a5576ed28f8105acc57fb16220b64217f8d3a6a066a594c004aa163afa languageName: node linkType: hard @@ -11413,23 +11418,23 @@ __metadata: linkType: hard "next@npm:^16.1.1": - version: 16.1.6 - resolution: "next@npm:16.1.6" - dependencies: - "@next/env": "npm:16.1.6" - "@next/swc-darwin-arm64": "npm:16.1.6" - "@next/swc-darwin-x64": "npm:16.1.6" - "@next/swc-linux-arm64-gnu": "npm:16.1.6" - "@next/swc-linux-arm64-musl": "npm:16.1.6" - "@next/swc-linux-x64-gnu": "npm:16.1.6" - "@next/swc-linux-x64-musl": "npm:16.1.6" - "@next/swc-win32-arm64-msvc": "npm:16.1.6" - "@next/swc-win32-x64-msvc": "npm:16.1.6" + version: 16.2.0 + resolution: "next@npm:16.2.0" + dependencies: + "@next/env": "npm:16.2.0" + "@next/swc-darwin-arm64": "npm:16.2.0" + "@next/swc-darwin-x64": "npm:16.2.0" + "@next/swc-linux-arm64-gnu": "npm:16.2.0" + "@next/swc-linux-arm64-musl": "npm:16.2.0" + "@next/swc-linux-x64-gnu": "npm:16.2.0" + "@next/swc-linux-x64-musl": "npm:16.2.0" + "@next/swc-win32-arm64-msvc": "npm:16.2.0" + "@next/swc-win32-x64-msvc": "npm:16.2.0" "@swc/helpers": "npm:0.5.15" - baseline-browser-mapping: "npm:^2.8.3" + baseline-browser-mapping: "npm:^2.9.19" caniuse-lite: "npm:^1.0.30001579" postcss: "npm:8.4.31" - sharp: "npm:^0.34.4" + sharp: "npm:^0.34.5" styled-jsx: "npm:5.1.6" peerDependencies: "@opentelemetry/api": ^1.1.0 @@ -11468,7 +11473,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 10/00322378df865d4f00d232f8b1f42f4f0ad114e1d14f660d483b01ccd533459af6202e2f80067bf41a97aa0c99c3ae1d21a0aef2c081b0a9f456b978de4bf757 + checksum: 10/9bd1a87f34cae488efdd07cf22b8926f3c45d9ef76a3d1978557c4c147ed82f273925430d3e599c29d59e63b0128c5b5376b7ddb4d7b250994d24f58e4dc1c10 languageName: node linkType: hard @@ -13193,13 +13198,6 @@ __metadata: languageName: node linkType: hard -"retry@npm:^0.13.1": - version: 0.13.1 - resolution: "retry@npm:0.13.1" - checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d - languageName: node - linkType: hard - "reusify@npm:^1.0.4": version: 1.1.0 resolution: "reusify@npm:1.1.0" @@ -13460,7 +13458,7 @@ __metadata: languageName: node linkType: hard -"sharp@npm:^0.34.4": +"sharp@npm:^0.34.5": version: 0.34.5 resolution: "sharp@npm:0.34.5" dependencies: @@ -14194,15 +14192,15 @@ __metadata: linkType: hard "tar@npm:^7.5.4": - version: 7.5.11 - resolution: "tar@npm:7.5.11" + version: 7.5.12 + resolution: "tar@npm:7.5.12" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/fb2e77ee858a73936c68e066f4a602d428d6f812e6da0cc1e14a41f99498e4f7fd3535e355fa15157240a5538aa416026cfa6306bb0d1d1c1abf314b1f878e9a + checksum: 10/a72114d28ab9b4878eeebaae8987692a577c390683c13f150d8330e139237038cc46fbb0be6983b02acf5a31b01d74776436ba03790f320a59efb44b8ac39e39 languageName: node linkType: hard @@ -14386,11 +14384,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^2.1.0, ts-api-utils@npm:^2.4.0": - version: 2.4.0 - resolution: "ts-api-utils@npm:2.4.0" + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" peerDependencies: typescript: ">=4.8.4" - checksum: 10/d6b2b3b6caad8d2f4ddc0c3785d22bb1a6041773335a1c71d73a5d67d11d993763fe8e4faefc4a4d03bb42b26c6126bbcf2e34826baed1def5369d0ebad358fa + checksum: 10/d5f1936f5618c6ab6942a97b78802217540ced00e7501862ae1f578d9a3aa189fc06050e64cb8951d21f7088e5fd35f53d2bf0d0370a883861c7b05e993ebc44 languageName: node linkType: hard @@ -14653,11 +14651,11 @@ __metadata: linkType: hard "typedoc-plugin-markdown@npm:^4.6.3": - version: 4.10.0 - resolution: "typedoc-plugin-markdown@npm:4.10.0" + version: 4.11.0 + resolution: "typedoc-plugin-markdown@npm:4.11.0" peerDependencies: typedoc: 0.28.x - checksum: 10/d16bb8efdc5fc7553f8f90802866a94040a8f19e37ddc0d430c19dcff6dd2cca9af7e55dd2144103ab44efcc36022a55f8161ffefb0bb0a4a3c9f48a93545828 + checksum: 10/dbde98e76536f6bd713041cfc22b07adacdec8a314da9d1848b5daa9e0de46ef471718b916a2938cac627bc45b0609a3d1765b795aa96aeea2771093207d4faf languageName: node linkType: hard @@ -14796,15 +14794,6 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^5.0.0": - version: 5.0.0 - resolution: "unique-filename@npm:5.0.0" - dependencies: - unique-slug: "npm:^6.0.0" - checksum: 10/a5f67085caef74bdd2a6869a200ed5d68d171f5cc38435a836b5fd12cce4e4eb55e6a190298035c325053a5687ed7a3c96f0a91e82215fd14729769d9ac57d9b - languageName: node - linkType: hard - "unique-slug@npm:^3.0.0": version: 3.0.0 resolution: "unique-slug@npm:3.0.0" @@ -14814,15 +14803,6 @@ __metadata: languageName: node linkType: hard -"unique-slug@npm:^6.0.0": - version: 6.0.0 - resolution: "unique-slug@npm:6.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10/b78ed9d5b01ff465f80975f17387750ed3639909ac487fa82c4ae4326759f6de87c2131c0c39eca4c68cf06c537a8d104fba1dfc8a30308f99bc505345e1eba3 - languageName: node - linkType: hard - "universal-user-agent@npm:^6.0.0": version: 6.0.1 resolution: "universal-user-agent@npm:6.0.1" @@ -15595,7 +15575,7 @@ __metadata: languageName: node linkType: hard -"zod@npm:^4.0.0": +"zod@npm:^4.3.6": version: 4.3.6 resolution: "zod@npm:4.3.6" checksum: 10/25fc0f62e01b557b4644bf0b393bbaf47542ab30877c37837ea8caf314a8713d220c7d7fe51f68ffa72f0e1018ddfa34d96f1973d23033f5a2a5a9b6b9d9da01 From d4b9464ad3a7ca3ae1fd772ceda47fc47064ab7c Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:25:26 +0300 Subject: [PATCH 03/32] [Atom schema] Introduce utilities in CSDK to define a the callback registry (#422) --- CHANGELOG.md | 38 +++- .../content/api/content-sdk-content.api.md | 32 +++ .../editing/atoms-builder/atoms-builder.ts | 25 +- .../src/editing/atoms-builder/index.ts | 1 + packages/content/src/editing/index.ts | 1 + packages/react/api/content-sdk-react.api.md | 40 +++- .../src/atoms/callback-registry-utils.test.ts | 213 ++++++++++++++++++ .../src/atoms/callback-registry-utils.ts | 33 +++ .../react/src/atoms/createCallback.test.ts | 76 +++++++ packages/react/src/atoms/createCallback.ts | 33 +++ packages/react/src/atoms/index.ts | 5 + packages/react/src/atoms/types.ts | 50 ++++ .../components/AtomRenderer/AtomRenderer.tsx | 14 +- .../DesignLibrary/DesignLibraryAtoms.tsx | 24 +- .../react/src/components/SitecoreProvider.tsx | 11 +- 15 files changed, 566 insertions(+), 30 deletions(-) create mode 100644 packages/react/src/atoms/callback-registry-utils.test.ts create mode 100644 packages/react/src/atoms/callback-registry-utils.ts create mode 100644 packages/react/src/atoms/createCallback.test.ts create mode 100644 packages/react/src/atoms/createCallback.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 87bfa49e1b..de603c2ccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,30 @@ Our versioning strategy is as follows: - Minor: non-breaking feature additions – no breaking changes (e.g. new features, improvements) - Major: new features + breaking changes (e.g. framework upgrades, major architectural changes, major features) -## Unreleased +## unreleased + +### 🎉 New Features & Improvements + +`[events]` `[nextjs]` Lightweight Tracking ([#414](https://github.com/Sitecore/content-sdk/pull/414)) + - Introduced `BotTrackingProxy` Next.js proxy to capture bot tracking events. +* `[content]` `[react]` `[nextjs]` Atomic Design Foundation + - Introduced the ability to create a callback registry ([#422](https://github.com/Sitecore/content-sdk/pull/422)) + + +### 🐛 Bug Fixes + +* Preview mode shows unpublishable content ([#410](https://github.com/Sitecore/content-sdk/pull/410))([416](https://github.com/Sitecore/content-sdk/pull/416)) +* `[core] [content]` Fix GraphQL client factory ignoring custom `fetch` and related options ([#418](https://github.com/Sitecore/content-sdk/pull/418)) + +## 2.0.1 + +### ✨ Bug fixes + +- Fix wrong versions and samples appearing when running `create-content-sdk-app` command. + - `angular` sample no longer appears + - `nextjs` samples use correct content-sdk dependencies for analytics when scaffolded + +## 2.0.0 ### 🎉 New Features & Improvements @@ -22,9 +45,6 @@ Our versioning strategy is as follows: * `[nextjs]` `[create-content-sdk-app]` Enable Next.js 16 Cache Components and Turbopack File System Caching ([#334](https://github.com/Sitecore/content-sdk/pull/334)) -* `[core]` `[content]` `[nextjs]` Support custom Edge hostnames via `SITECORE_EDGE_PLATFORM_HOSTNAME` (Next.js: `NEXT_PUBLIC_SITECORE_EDGE_PLATFORM_HOSTNAME`) ([#359](https://github.com/Sitecore/content-sdk/pull/359)) - - New `rewriteMediaUrls` option: when `true`, rewrites layout media URLs to the custom Edge hostname; when a function, applies a custom string transformer. - * Search integration ([#295](https://github.com/Sitecore/content-sdk/pull/295)) * `[search]` New `@sitecore-content-sdk/search` package providing search functionality - `SearchService` class for performing search queries with support for pagination, sorting, request cancellation. @@ -45,12 +65,18 @@ Our versioning strategy is as follows: ### 🛠 Breaking Changes +* `[core]` `[content]` `[nextjs]` Support custom Edge hostnames via `SITECORE_EDGE_PLATFORM_HOSTNAME` (Next.js: `NEXT_PUBLIC_SITECORE_EDGE_PLATFORM_HOSTNAME`) ([#359](https://github.com/Sitecore/content-sdk/pull/359)) + - New `rewriteMediaUrls` option: when `true`, rewrites layout media URLs to the custom Edge hostname; when a function, applies a custom string transformer. + - The old `SITECORE_EDGE_URL` environment variable is no longer used + * Decouple `@sitecore-content-sdk/content` from `@sitecore-content-sdk/core` ([#351](https://github.com/Sitecore/content-sdk/pull/351)([#383](https://github.com/Sitecore/content-sdk/pull/383))): - See a detailed upgrade guide for migration instructions + * `[nextjs]` `[create-content-sdk-app]` Upgrade to Next.js 16 ([#334](https://github.com/Sitecore/content-sdk/pull/334))([#343](https://github.com/Sitecore/content-sdk/pull/343))([#353](https://github.com/Sitecore/content-sdk/pull/353)) - Next.js 16 is now required (minimum version `^16.0.0`) - `middleware.ts` renamed to `proxy.ts` with updated function signature - Removed deprecated `images.domains` usage (use `remotePatterns` instead) + * `[nextjs]` Expand SXA redirects logic with support for isLanguagePreserved flag. This provides an option to preserve current locale when target redirect URL does not have a locale prefix ([#305](https://github.com/Sitecore/content-sdk/pull/305)) - This changes the default redirects behavior out of the box. - Previously, `/da/source -> /target` rule would redirect to `/da/target` path when default locale is not `da` @@ -64,7 +90,7 @@ Our versioning strategy is as follows: - `DesignLibrary` component now does not accept any props - `SitecoreProvider`'s `loadImportMap` is now required * [react] [nextjs] Major revamp of components in the react package ([#371](https://github.com/Sitecore/content-sdk/pull/371)): - - `Placeholder` and `AppPlaceholder` components' prop `modifyComponentProps` has been removed. `passThroughComponentProps` prop has been added to fill the role of passing props to child components + - `Placeholder` and `AppPlaceholder`, `passThroughComponentProps` prop has been added to fill make passing props to child components easier - `componentMap` and `loadImportMap` have been added to the context shared via `SitecoreProvider` - `useLoadImportMap`, `useComponentMap` HOCs have been removed. The Sitecore context data can be accessed via `useSitecore` hook. - `withSitecore`'s `updatePage` prop has been renamed to `setPage`. This HOC has also been marked deprecated, and will eventually be removed in favor of `useSitecore` hook. @@ -501,4 +527,4 @@ Our versioning strategy is as follows: ### 🧹 Chores * `[template/nextjs]` Clean package.json scripts ([#75](https://github.com/Sitecore/content-sdk/pull/75)) -* Upgrade 3rd party dependencies ([#88](https://github.com/Sitecore/content-sdk/pull/88)) ([#92](https://github.com/Sitecore/content-sdk/pull/92)) +* Upgrade 3rd party dependencies ([#88](https://github.com/Sitecore/content-sdk/pull/88)) ([#92](https://github.com/Sitecore/content-sdk/pull/92)) \ No newline at end of file diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 7b2baa1528..9481f32ced 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -33,6 +33,31 @@ export function addStyleElement(stylesContent: string): void; // @internal export function applyMediaUrlRewrite(value: T, transform: (s: string) => string): T; +// @internal +export type AtomInfo = { + name: string; + version?: number; + type: (typeof AtomType)[keyof typeof AtomType]; + description: string; + props: Record; + allowedChildren: string[]; + defaultChildren?: SerializedDefaultChild[]; + htmlEvents?: string[]; + customEvents?: Record; +}; + +// @internal +export const AtomType: { + readonly ATOM: "atom"; + readonly ATOM_CHILD: "atom-child"; +}; + +// @internal +export type CallbackInfo = { + description: string; + params?: Record; +}; + // @public export class CdpHelper { static getComponentFriendlyId(pageId: string, componentId: string, language: string, scope?: string): string; @@ -554,6 +579,11 @@ export const getContentStylesheetLink: (layoutData: LayoutServiceData, sitecoreE // @internal export function getDefaultMediaUrlTransformer(edgeUrl: string): (value: string) => string; +// Warning: (ae-forgotten-export) The symbol "DesignLibraryAtomsRegistryEvent" needs to be exported by the entry point api-surface.d.ts +// +// @internal +export function getDesignLibraryAtomsRegistryEvent(atomsRegistry: AtomInfo[], callbackRegistry: Record): DesignLibraryAtomsRegistryEvent; + // Warning: (ae-forgotten-export) The symbol "DesignLibraryComponentPropsEvent" needs to be exported by the entry point api-surface.d.ts // // @internal @@ -834,6 +864,7 @@ export type PageMode = { name: PageModeName; designLibrary: { isVariantGeneration: boolean; + isAtomsMode?: boolean; }; isNormal: boolean; isPreview: boolean; @@ -1381,6 +1412,7 @@ export type WriteImportMapArgsInternal = WriteImportMapArgs & { // Warnings were encountered during analysis: // // src/client/sitecore-client.ts:68:3 - (ae-forgotten-export) The symbol "PageModeName" needs to be exported by the entry point api-surface.d.ts +// src/editing/atoms-builder/atoms-builder.ts:63:3 - (ae-forgotten-export) The symbol "SerializedDefaultChild" needs to be exported by the entry point api-surface.d.ts // src/editing/codegen/preview.ts:115:3 - (ae-forgotten-export) The symbol "ComponentImport_2" needs to be exported by the entry point api-surface.d.ts // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "ComponentMapTemplate" which is marked as @internal // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "EnhancedComponentMapTemplate" which is marked as @internal diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.ts b/packages/content/src/editing/atoms-builder/atoms-builder.ts index f324268995..a557c801bb 100644 --- a/packages/content/src/editing/atoms-builder/atoms-builder.ts +++ b/packages/content/src/editing/atoms-builder/atoms-builder.ts @@ -13,6 +13,21 @@ export const AtomType = { ATOM: 'atom', ATOM_CHILD: 'atom-child' } as const; export type SerializedDefaultChild = string | { atom: string; props?: Record }; +/** + * Represents the serialized callback metadata information to be sent to design library + * @internal + */ +export type CallbackInfo = { + /** + * A description of the callback. + */ + description: string; + /** + * The parameters of the callback. + */ + params?: Record; +}; + /** * Represents the serialized atom metadata information to be sent to design library * @internal @@ -64,22 +79,26 @@ export interface DesignLibraryAtomsRegistryEvent extends DesignLibraryEvent { name: typeof DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME; message: { atomsRegistry: AtomInfo[]; + callbackRegistry: Record; }; } /** - * Creates a DesignLibraryAtomsRegistryEvent with the given atoms registry. + * Creates a DesignLibraryAtomsRegistryEvent with the given atoms registry and callback registry. * @param {AtomInfo[]} atomsRegistry - the atoms registry to be sent in the event - * @returns {DesignLibraryAtomsRegistryEvent} the created event with the atoms registry + * @param {Record} callbackRegistry - the callback registry to be sent in the event + * @returns {DesignLibraryAtomsRegistryEvent} the created event with the atoms registry and callback registry * @internal */ export function getDesignLibraryAtomsRegistryEvent( - atomsRegistry: AtomInfo[] + atomsRegistry: AtomInfo[], + callbackRegistry: Record ): DesignLibraryAtomsRegistryEvent { return { name: DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME, message: { atomsRegistry, + callbackRegistry, }, }; } diff --git a/packages/content/src/editing/atoms-builder/index.ts b/packages/content/src/editing/atoms-builder/index.ts index 0498960ac4..595215fe14 100644 --- a/packages/content/src/editing/atoms-builder/index.ts +++ b/packages/content/src/editing/atoms-builder/index.ts @@ -4,4 +4,5 @@ export { SerializedDefaultChild, DesignLibraryAtomsRegistryEvent, getDesignLibraryAtomsRegistryEvent, + CallbackInfo, } from './atoms-builder'; diff --git a/packages/content/src/editing/index.ts b/packages/content/src/editing/index.ts index d86547fb72..837bdeb7be 100644 --- a/packages/content/src/editing/index.ts +++ b/packages/content/src/editing/index.ts @@ -43,6 +43,7 @@ export { } from './design-library'; export { AtomInfo, + CallbackInfo, AtomType, getDesignLibraryAtomsRegistryEvent, } from './atoms-builder/atoms-builder'; diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 284aca0121..29a04aa177 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -4,12 +4,12 @@ ```ts +import { AtomType } from '@sitecore-content-sdk/content/editing'; import { CacheClient } from '@sitecore-content-sdk/core'; import { CacheOptions } from '@sitecore-content-sdk/core'; import { ClientError } from '@sitecore-content-sdk/core'; import { ComponentFields } from '@sitecore-content-sdk/content/layout'; import { ComponentParams } from '@sitecore-content-sdk/content/layout'; -import type { ComponentPropsWithoutRef } from 'react'; import { ComponentRendering } from '@sitecore-content-sdk/content/layout'; import { ComponentType } from 'react'; import { constants } from '@sitecore-content-sdk/core'; @@ -79,7 +79,7 @@ export type AtomChild = AtomMetadata | 'text' | 'atom'; export type AtomMetadata = { name: string; version?: number; - type: 'atom' | 'atom-child'; + type: (typeof AtomType)[keyof typeof AtomType]; description: string; props: z.ZodObject; component: (props: unknown) => React.ReactNode; @@ -89,23 +89,30 @@ export type AtomMetadata = { defaultChildren?: DefaultChild[]; }; +// @public (undocumented) +export const AtomRenderer: (input: { + atoms?: AtomMetadata[]; +}) => React_2.JSX.Element; + // @public -export type AtomSchemaInput> = { +export type AtomSchemaInput = { name: string; description: string; - type?: 'atom' | 'atom-child'; + type?: (typeof AtomType)[keyof typeof AtomType]; version?: number; props: { [K in keyof EditableComponentProps]?: z.ZodType[K]>; }; htmlEvents?: CallbackPropKeys>[]; customEvents?: { - [K in CallbackPropKeys>]?: z.ZodType[]; + [K in CallbackPropKeys>]?: CallbackArgZodTuple[K]>>; }; allowedChildren?: AtomChild[]; defaultChildren?: DefaultChild[]; }; +export { AtomType } + // @public export class BYOCComponent extends React_2.Component { constructor(props: BYOCComponentProps); @@ -152,9 +159,14 @@ export { CacheClient } export { CacheOptions } +// @public +export type CallbackArgZodTuple = F extends (...args: infer A) => unknown ? { + [I in keyof A]: z.ZodType; +} : never; + // @public export type CallbackPropKeys = { - [K in keyof T & string]: NonNullable extends (...args: unknown[]) => unknown ? K : never; + [K in keyof T & string]: NonNullable extends (...args: any[]) => unknown ? K : never; }[keyof T & string]; // @public @@ -174,7 +186,7 @@ export { ComponentRendering } export { constants } // @public -export function createAtom>(component: C, schema: AtomSchemaInput): AtomMetadata; +export function createAtom(component: C, schema: AtomSchemaInput): AtomMetadata; // @public export const DateField: React_2.FC; @@ -218,6 +230,9 @@ export { DefaultRetryStrategy } // @public export const DesignLibrary: () => React_2.JSX.Element | null; +// @internal +export const DesignLibraryAtoms: () => React_2.JSX.Element; + // Warning: (ae-forgotten-export) The symbol "DesignLibraryErrorBoundaryProps" needs to be exported by the entry point api-surface.d.ts // // @internal @@ -249,8 +264,10 @@ export type DynamicComponent = React.ComponentType<{ params?: ComponentParams; }>; +// Warning: (ae-forgotten-export) The symbol "PropsOfComponent" needs to be exported by the entry point api-surface.d.ts +// // @public -export type EditableComponentProps> = Omit, 'children'>; +export type EditableComponentProps = Omit, 'children' | 'ref'>; // @public export const EditingScripts: () => React_2.JSX.Element; @@ -338,7 +355,7 @@ export { getContentStylesheetLink } export { getDesignLibraryStylesheetLinks } -// @public +// @internal export function getFieldMeta(schemaOrJsonSchema: z.ZodType | Record): Record | undefined; export { getFieldValue } @@ -563,6 +580,9 @@ export const SitecoreProviderReactContext: React_2.Context Promise; @@ -692,7 +712,7 @@ export function withSitecore(options?: UseSitecoreOptions): { + it('should serialize a callback without params', () => { + const callback = createCallback('onSave', { + description: 'onSave callback', + params: {}, + callbackFn: () => {}, + }); + + const result = serializeCallbacks([callback]); + + expect(result).to.deep.equal({ + onSave: { + description: 'onSave callback', + }, + }); + }); + + it('should serialize a callback with params to JSON schema tuple', () => { + const callback = createCallback('onSubmit', { + description: 'onSubmit callback', + params: { + label: { type: z.string(), description: 'The label' }, + count: { type: z.number(), description: 'The count' }, + }, + callbackFn: ({ label, count }) => { + console.log(label, count); + }, + }); + + const result = serializeCallbacks([callback]); + + expect(result).to.deep.equal({ + onSubmit: { + description: 'onSubmit callback', + params: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + label: { type: 'string', description: 'The label' }, + count: { type: 'number', description: 'The count' }, + }, + required: ['label', 'count'], + additionalProperties: false, + }, + }, + }); + }); + + it('should serialize multiple callbacks', () => { + const callbackA = createCallback('onSave', { + description: 'Save handler', + params: {}, + callbackFn: () => {}, + }); + const callbackB = createCallback('onCancel', { + description: 'Cancel handler', + params: {}, + callbackFn: () => {}, + }); + + const result = serializeCallbacks([callbackA, callbackB]); + + expect(result).to.deep.equal({ + onSave: { + description: 'Save handler', + }, + onCancel: { + description: 'Cancel handler', + }, + }); + }); + + it('should return an empty object for an empty array', () => { + const result = serializeCallbacks([]); + + expect(result).to.deep.equal({}); + }); + + it('should serialize params with argument names and descriptions', () => { + const callback = createCallback('onUpdate', { + description: 'onUpdate callback', + params: { + name: { type: z.string(), description: 'Name to update' }, + isActive: { type: z.boolean(), description: 'Whether active' }, + }, + callbackFn: ({ name, isActive }) => { + console.log(name, isActive); + }, + }); + + const result = serializeCallbacks([callback]); + + expect(result).to.deep.equal({ + onUpdate: { + description: 'onUpdate callback', + params: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + name: { type: 'string', description: 'Name to update' }, + isActive: { type: 'boolean', description: 'Whether active' }, + }, + required: ['name', 'isActive'], + additionalProperties: false, + }, + }, + }); + }); + + it('should serialize callbacks with mixed params and no-params', () => { + const callbackA = createCallback('onSave', { + description: 'onSave callback', + params: { + title: { type: z.string(), description: 'The title' }, + }, + callbackFn: ({ title }) => { + console.log(title); + }, + }); + const callbackB = createCallback('onCancel', { + description: 'onCancel callback', + params: {}, + callbackFn: () => {}, + }); + + const result = serializeCallbacks([callbackA, callbackB]); + + expect(result).to.deep.equal({ + onSave: { + description: 'onSave callback', + params: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + title: { type: 'string', description: 'The title' }, + }, + required: ['title'], + additionalProperties: false, + }, + }, + onCancel: { + description: 'onCancel callback', + }, + }); + }); + + it('should serialize an array of callbacks with varying params', () => { + const callbackA = createCallback('onSubmit', { + description: 'Submit handler', + params: { + label: { type: z.string(), description: 'The label' }, + count: { type: z.number(), description: 'The count' }, + note: { type: z.string().optional(), description: 'Optional note' }, + }, + callbackFn: ({ label, count, note }) => { + console.log(label, count, note); + }, + }); + const callbackB = createCallback('onReset', { + description: 'Reset handler', + params: { + force: { type: z.boolean(), description: 'Force reset' }, + }, + callbackFn: ({ force }) => { + console.log(force); + }, + }); + const callbackC = createCallback('onCancel', { + description: 'Cancel handler', + params: {}, + callbackFn: () => {}, + }); + + const result = serializeCallbacks([callbackA, callbackB, callbackC]); + + expect(result).to.deep.equal({ + onSubmit: { + description: 'Submit handler', + params: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + label: { type: 'string', description: 'The label' }, + count: { type: 'number', description: 'The count' }, + note: { description: 'Optional note', type: 'string' }, + }, + required: ['label', 'count'], + additionalProperties: false, + }, + }, + onReset: { + description: 'Reset handler', + params: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + force: { type: 'boolean', description: 'Force reset' }, + }, + required: ['force'], + additionalProperties: false, + }, + }, + onCancel: { + description: 'Cancel handler', + }, + }); + }); +}); diff --git a/packages/react/src/atoms/callback-registry-utils.ts b/packages/react/src/atoms/callback-registry-utils.ts new file mode 100644 index 0000000000..a8105e8524 --- /dev/null +++ b/packages/react/src/atoms/callback-registry-utils.ts @@ -0,0 +1,33 @@ +import { z } from 'zod'; +import { CallbackMetadata } from './types'; +import { CallbackInfo } from '@sitecore-content-sdk/content/editing'; + +/** + * Serializes the provided callbacks metadata array into a format suitable for broadcasting via postMessage to the host application + * @param {CallbackMetadata[]} callbacks - the callbacks to be serialized + * @returns {Record} the serialized callbacks + * @internal + */ +export const serializeCallbacks = (callbacks: CallbackMetadata[]): Record => { + const callbacksInfo: Record = {}; + + callbacks.forEach((callback) => { + const callbackInfo: CallbackInfo = { + description: callback.description, + }; + + if (callback.params && Object.keys(callback.params).length > 0) { + const shape: Record = {}; + for (const [argName, p] of Object.entries(callback.params)) { + shape[argName] = p.type.meta({ description: p.description }); + } + callbackInfo.params = z.toJSONSchema(z.object(shape), { + target: 'draft-7', + }); + } + + callbacksInfo[callback.name] = callbackInfo; + }); + + return callbacksInfo; +}; diff --git a/packages/react/src/atoms/createCallback.test.ts b/packages/react/src/atoms/createCallback.test.ts new file mode 100644 index 0000000000..6671ccb30a --- /dev/null +++ b/packages/react/src/atoms/createCallback.test.ts @@ -0,0 +1,76 @@ +import { expect } from 'chai'; +import { z } from 'zod'; +import { createCallback } from './createCallback'; + +describe('createCallback', () => { + it('should create a callback with name and description', () => { + const result = createCallback('onSave', { + description: 'Save handler', + params: {}, + callbackFn: () => {}, + }); + + expect(result.name).to.equal('onSave'); + expect(result.description).to.equal('Save handler'); + }); + + it('should set params to empty object when no params provided', () => { + const result = createCallback('onSave', { + description: 'Save handler', + params: {}, + callbackFn: () => {}, + }); + + expect(result.params).to.deep.equal({}); + }); + + it('should pass through params as provided', () => { + const labelType = z.string(); + const countType = z.number(); + + const result = createCallback('onSubmit', { + description: 'Submit handler', + params: { + label: { type: labelType, description: 'The label' }, + count: { type: countType, description: 'The count' }, + }, + callbackFn: ({ label, count }) => { + console.log(label, count); + }, + }); + + expect(result.params).to.have.property('label'); + expect(result.params).to.have.property('count'); + expect(result.params!.label.description).to.equal('The label'); + expect(result.params!.count.description).to.equal('The count'); + }); + + it('should preserve the callbackFn reference', () => { + const fn = () => {}; + + const result = createCallback('onCancel', { + description: 'Cancel handler', + params: {}, + callbackFn: fn, + }); + + expect(result.callbackFn).to.equal(fn); + }); + + it('should handle optional params', () => { + const result = createCallback('onUpdate', { + description: 'Update handler', + params: { + name: { type: z.string(), description: 'The name' }, + note: { type: z.string().optional(), description: 'Optional note' }, + }, + callbackFn: ({ name, note }) => { + console.log(name, note); + }, + }); + + expect(result.params).to.have.property('name'); + expect(result.params).to.have.property('note'); + }); +}); + diff --git a/packages/react/src/atoms/createCallback.ts b/packages/react/src/atoms/createCallback.ts new file mode 100644 index 0000000000..95083dea54 --- /dev/null +++ b/packages/react/src/atoms/createCallback.ts @@ -0,0 +1,33 @@ +import type { CallbackMetadata, CallbackParamsInput, InferCallbackArgs } from './types'; + +/** + * Schema input for createCallback. Param names are the keys of the params record, + * and impl receives a single object argument typed from those keys. + * @public + */ +export type CallbackSchemaInput

= { + /** Human-readable summary for the callback */ + description: string; + /** Record of param names to Zod types. Keys define the param names. */ + params: P; + /** Implementation of the callback. Receives a single object with keys matching params. */ + callbackFn: (args: InferCallbackArgs

) => void; +}; + +/** + * Create a callback descriptor. The params record keys define the parameter names, + * and TypeScript enforces that impl's argument object matches those keys and types. + * @param {string} name - The unique identifier for this callback + * @param {CallbackSchemaInput

} schema - The schema that defines the callback's description and params + * @returns {CallbackMetadata} CallbackMetadata with params as a CallbackParam array + * @public + */ +export function createCallback

( + name: string, + schema: CallbackSchemaInput

+): CallbackMetadata { + return { + name, + ...schema, + }; +} diff --git a/packages/react/src/atoms/index.ts b/packages/react/src/atoms/index.ts index 2f2e4bb7e2..6f59f6b20b 100644 --- a/packages/react/src/atoms/index.ts +++ b/packages/react/src/atoms/index.ts @@ -8,6 +8,11 @@ export type { CallbackArgZodTuple, PropMeta, ArgMeta, + CallbackMetadata, + CallbackParamInput, + CallbackParamsInput, + InferCallbackArgs, } from './types'; export { withPropMeta, withArgMeta, getFieldMeta } from './schema-utils'; export { createAtom, type AtomSchemaInput } from './createAtom'; +export { createCallback, type CallbackSchemaInput } from './createCallback'; diff --git a/packages/react/src/atoms/types.ts b/packages/react/src/atoms/types.ts index 8b13141c05..77923aad85 100644 --- a/packages/react/src/atoms/types.ts +++ b/packages/react/src/atoms/types.ts @@ -3,6 +3,17 @@ import type { AtomType } from '@sitecore-content-sdk/content/editing'; import type { z } from 'zod'; import type { ComponentType } from 'react'; +/** + * Metadata for callback + * @public + */ +export type CallbackMetadata = { + name: string; + description: string; + params?: CallbackParamsInput; + callbackFn: (...args: any[]) => void; +}; + /** Metadata for an atom or atom-child; type differentiates. @public */ export type AtomMetadata = { name: string; @@ -58,3 +69,42 @@ export type PropMeta = { control?: string }; /** Event argument metadata (e.g. argName). @public */ export type ArgMeta = { argName: string }; + +/** + * Input shape for a single param entry in createCallback. + * @public + */ +export type CallbackParamInput = { + /** Zod schema for the parameter type */ + type: z.ZodType; + /** Human-readable description of the parameter */ + description: string; +}; + +/** + * Record of param names to their schema and description. Keys become the param names. + * @public + */ +export type CallbackParamsInput = Record; + +/** + * Keys from P whose Zod type includes undefined (i.e. optional). + * @public + */ +type OptionalParamKeys

= { + [K in keyof P]: undefined extends z.infer ? K : never; +}[keyof P]; + +/** Keys from P whose Zod type does NOT include undefined (i.e. required). @internal */ +type RequiredParamKeys

= Exclude>; + +/** + * Infers the impl function's argument object type from a CallbackParamsInput record. + * Required params become required properties, optional Zod types become optional properties. + * @public + */ +export type InferCallbackArgs

= { + [K in RequiredParamKeys

]: z.infer; +} & { + [K in OptionalParamKeys

]?: z.infer; +}; diff --git a/packages/react/src/components/AtomRenderer/AtomRenderer.tsx b/packages/react/src/components/AtomRenderer/AtomRenderer.tsx index 752be0652f..ed115e8229 100644 --- a/packages/react/src/components/AtomRenderer/AtomRenderer.tsx +++ b/packages/react/src/components/AtomRenderer/AtomRenderer.tsx @@ -1,11 +1,17 @@ 'use client'; import React, { useEffect } from 'react'; -import { AtomMetadata } from '../../atoms'; +import { AtomMetadata, CallbackMetadata } from '../../atoms'; -export const AtomRenderer = ({ atoms }: { atoms?: AtomMetadata[] }) => { +export const AtomRenderer = ({ + atoms, + callbacks, +}: { + atoms?: AtomMetadata[]; + callbacks?: CallbackMetadata[]; +}) => { useEffect(() => { - console.log('AtomRenderer, available atoms:', atoms); - }, [atoms]); + console.log(`AtomRenderer, available atoms: ${atoms}, available callbacks: ${callbacks}`); + }, [atoms, callbacks]); return

Atoms Renderer
; }; diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx index b86b0b5864..8d0631a7b3 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx @@ -2,9 +2,12 @@ import React, { useEffect } from 'react'; import { useSitecore } from '../SitecoreProvider'; import { serializeAtoms } from '../../atoms/atom-registry-utils'; +import { serializeCallbacks } from '../../atoms/callback-registry-utils'; import { postToDesignLibrary, getDesignLibraryAtomsRegistryEvent, + AtomInfo, + CallbackInfo, } from '@sitecore-content-sdk/content/editing'; import { AtomRenderer } from '../AtomRenderer/AtomRenderer'; @@ -12,24 +15,33 @@ import { AtomRenderer } from '../AtomRenderer/AtomRenderer'; * Design Library Atoms component. * * Facilitates the communication between the Design Studio and the Rendering Host when in atom rendering mode. - * - On mount, it unfolds and serializes the atoms registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. + * - On mount, it unfolds and serializes the atoms registry and callback registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. * - Fetches Component model data, and passes it to the `AtomRenderer` which is responsible for rendering the low code component * based on component model data and the available atoms. * @internal */ export const DesignLibraryAtoms = () => { - const { atoms } = useSitecore(); + const { atoms, callbacks } = useSitecore(); useEffect(() => { + let serializedAtoms: AtomInfo[] = []; + let serializedCallbacks: Record = {}; + if (atoms) { - const serializedAtoms = serializeAtoms(atoms); + serializedAtoms = serializeAtoms(atoms); console.log('Serialized Atoms:', serializedAtoms); + } - postToDesignLibrary(getDesignLibraryAtomsRegistryEvent(serializedAtoms)); + if (callbacks) { + serializedCallbacks = serializeCallbacks(callbacks); + console.log('Serialized Callbacks:', serializedCallbacks); } + postToDesignLibrary(getDesignLibraryAtomsRegistryEvent(serializedAtoms, serializedCallbacks)); + console.log('Design Library Atoms mounted'); - }, [atoms]); + console.log('Design Library Callbacks mounted'); + }, [atoms, callbacks]); - return ; + return ; }; diff --git a/packages/react/src/components/SitecoreProvider.tsx b/packages/react/src/components/SitecoreProvider.tsx index bbefe8380d..3d9250b107 100644 --- a/packages/react/src/components/SitecoreProvider.tsx +++ b/packages/react/src/components/SitecoreProvider.tsx @@ -5,7 +5,7 @@ import { Page } from '@sitecore-content-sdk/content/client'; import { SitecoreConfig } from '@sitecore-content-sdk/content/config'; import { ComponentMap } from './sharedTypes'; import { ImportMapImport } from './DesignLibrary/models'; -import { AtomMetadata } from '../atoms/types'; +import { AtomMetadata, CallbackMetadata } from '../atoms/types'; export interface SitecoreProviderProps { /** @@ -30,6 +30,11 @@ export interface SitecoreProviderProps { */ atoms?: AtomMetadata[]; + /** + * The callbacks metadata to be used in the Design Library. + */ + callbacks?: CallbackMetadata[]; + children: React.ReactNode; } @@ -56,6 +61,10 @@ export interface SitecoreProviderState { * The atoms metadata to be used in the Design Library. */ atoms?: AtomMetadata[]; + /** + * The callbacks metadata to be used in the Design Library. + */ + callbacks?: CallbackMetadata[]; /** * The component map to use for rendering components. */ From 5bcf37487175a7934e2e3aa4744ffcceb09eb82f Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:59:31 +0300 Subject: [PATCH 04/32] [content] [react] [atoms] Render atom components and Design Studio integration (#423) * introduce atom registry utils, for unfolding and serializing atom metadata; introduce AtomRenderer and send atom registry event on mount * post merge fixes, regenerate yarn lock * update unit tests * fix describe block only * add DesignLibraryAtoms component and atoms mode for design library * NCC component layout renderer * add test data, atom renderer, wip * update yarn lock * remove index.tsx * move component-layout out of /editing * move getAtomRegistry * move getAtomsRegistry tests. add callback registry to SitecoreProvider * minor fix * adjust AtomType type * set up dl atoms events and handlers for comunication with dl * initial refactor * refactor createView * remove LayoutOptions interface * updates to document definitions * more refactoring * more refactoring * add DesignLibraryMode - atoms * more refactor * minor update * create view unit tests * update tests * unit tests * error boundary * remove useRef * refactoring * refactoring * small fix * api extractor updates * updaete atomRegistry property of sitecore provider * rename getAtomsRegistry funct * move createView * restructure atoms submodule in content package * DesignLibraryAtoms doesn't rely on other component to render view * remove AtomRenderer * remove .only from uni test * update api surface * update changelog * Resolve PR comments * Adjusts tests and JSDocs --------- Co-authored-by: MenKNas --- CHANGELOG.md | 1 + packages/content/api/api-surface.d.ts | 1 + .../content/api/content-sdk-content.api.md | 174 +- packages/content/atoms.d.ts | 1 + packages/content/package.json | 7 +- .../atoms/component-layout/document.test.ts | 129 + .../src/atoms/component-layout/document.ts | 294 +++ .../atoms/component-layout/resolver.test.ts | 279 +++ .../src/atoms/component-layout/resolver.ts | 303 +++ packages/content/src/atoms/index.ts | 44 + .../atoms-builder/atoms-builder.test.ts | 149 +- .../editing/atoms-builder/atoms-builder.ts | 101 +- .../src/editing/atoms-builder/index.ts | 4 +- packages/content/src/editing/index.ts | 3 + packages/content/src/editing/models.ts | 2 + packages/content/tsconfig.json | 1 + .../nextjs-app-router/src/Providers.tsx | 2 +- packages/nextjs/src/index.ts | 1 - packages/react/api/content-sdk-react.api.md | 19 +- .../src/atoms/atom-registry-utils.test.ts | 45 +- .../react/src/atoms/atom-registry-utils.ts | 29 + .../component-layout/createView.test.tsx | 2209 +++++++++++++++++ .../src/atoms/component-layout/createView.tsx | 312 +++ .../react/src/atoms/component-layout/index.ts | 5 + packages/react/src/atoms/createAtom.test.ts | 21 +- packages/react/src/atoms/createAtom.ts | 8 +- packages/react/src/atoms/schema-utils.ts | 10 +- packages/react/src/atoms/types.ts | 2 +- .../components/AtomRenderer/AtomRenderer.tsx | 17 - .../src/components/AtomRenderer/index.ts | 1 - .../DesignLibrary/DesignLibraryAtoms.test.tsx | 637 +++++ .../DesignLibrary/DesignLibraryAtoms.tsx | 93 +- .../src/components/SitecoreProvider.test.tsx | 52 +- .../react/src/components/SitecoreProvider.tsx | 44 +- packages/react/src/index.ts | 1 - .../src/test-data/atom-component-layouts.ts | 420 ++++ yarn.lock | 308 +-- 37 files changed, 5456 insertions(+), 273 deletions(-) create mode 100644 packages/content/atoms.d.ts create mode 100644 packages/content/src/atoms/component-layout/document.test.ts create mode 100644 packages/content/src/atoms/component-layout/document.ts create mode 100644 packages/content/src/atoms/component-layout/resolver.test.ts create mode 100644 packages/content/src/atoms/component-layout/resolver.ts create mode 100644 packages/content/src/atoms/index.ts create mode 100644 packages/react/src/atoms/component-layout/createView.test.tsx create mode 100644 packages/react/src/atoms/component-layout/createView.tsx create mode 100644 packages/react/src/atoms/component-layout/index.ts delete mode 100644 packages/react/src/components/AtomRenderer/AtomRenderer.tsx delete mode 100644 packages/react/src/components/AtomRenderer/index.ts create mode 100644 packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx create mode 100644 packages/react/src/test-data/atom-component-layouts.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index de603c2ccd..0a2c68d029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Our versioning strategy is as follows: - Introduced `BotTrackingProxy` Next.js proxy to capture bot tracking events. * `[content]` `[react]` `[nextjs]` Atomic Design Foundation - Introduced the ability to create a callback registry ([#422](https://github.com/Sitecore/content-sdk/pull/422)) + - Introduce the component layout Document contract and ability to render it; set up Design Studio integration ([#423](https://github.com/Sitecore/content-sdk/pull/423)) ### 🐛 Bug Fixes diff --git a/packages/content/api/api-surface.d.ts b/packages/content/api/api-surface.d.ts index 745f5479f9..b9765b9b93 100644 --- a/packages/content/api/api-surface.d.ts +++ b/packages/content/api/api-surface.d.ts @@ -18,5 +18,6 @@ export * from '../layout'; export * from '../media'; export * from '../personalize'; export * from '../site'; +export * from '../atoms'; export * from '../tools'; export * from '../node-tools'; diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 9481f32ced..0648dbdf66 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -18,12 +18,18 @@ import { GraphQLRequestClientFactory } from '@sitecore-content-sdk/core'; import { GraphQLRequestClientFactoryConfig } from '@sitecore-content-sdk/core'; import { RetryStrategy } from '@sitecore-content-sdk/core'; +// @internal +export type Action = SetStateAction | CallAction; + // @internal export const addComponentPreviewHandler: (importMap: ImportEntry[], callback: (error: unknown | null, Component: unknown) => void) => (() => void) | undefined; // @internal export const addComponentUpdateHandler: (rootComponent: ComponentRendering, successCallback?: (updatedRootComponent: ComponentRendering) => void) => (() => void) | undefined; +// @internal +export const addDocumentUpdateHandler: (callback: (updatedRootComponent: Document_2) => void) => () => void; + // @internal export const addServerComponentPreviewHandler: (rootComponent: ComponentRendering, callback: (componentToUpdate: ComponentRendering | null, eventArgs: ServerComponentPreviewEventArgs) => void) => () => void; @@ -37,7 +43,7 @@ export function applyMediaUrlRewrite(value: T, transform: (s: string) => stri export type AtomInfo = { name: string; version?: number; - type: (typeof AtomType)[keyof typeof AtomType]; + type: AtomType; description: string; props: Record; allowedChildren: string[]; @@ -47,10 +53,16 @@ export type AtomInfo = { }; // @internal -export const AtomType: { - readonly ATOM: "atom"; - readonly ATOM_CHILD: "atom-child"; -}; +export type AtomType = 'atom' | 'atom-child'; + +// @internal +export type Binding = ExpressionBinding | EventBinding; + +// @internal +export interface CallAction { + args?: Primitive[]; + call: string; +} // @internal export type CallbackInfo = { @@ -248,8 +260,12 @@ export const defineCliConfig: (cliConfig: SitecoreCliConfigInput) => SitecoreCli // @public export const defineConfig: (config: SitecoreConfigInput) => SitecoreConfig; +// @internal +export type DesignLibraryAtomsError = 'render' | 'atoms-missing'; + // @public export enum DesignLibraryMode { + Atoms = "library-atoms", Metadata = "library-metadata", Normal = "library" } @@ -342,6 +358,15 @@ export interface DictionaryServiceConfig extends CacheOptions, GraphQLServiceCon pageSize?: number; } +// @internal +interface Document_2 { + name: string; + props?: unknown; + root: Node_2; + state?: Record; +} +export { Document_2 as Document } + // @internal export const EDITING_ALLOWED_ORIGINS: string[]; @@ -417,6 +442,19 @@ export enum EditMode { Metadata = "metadata" } +// @internal +interface Element_2 { + bindings?: Record; + children?: Node_2[]; + for?: ForLoop; + id?: string; + show?: ShowNode; + staticProps?: Record; + type: string; + version?: number; +} +export { Element_2 as Element } + // @internal export const EMPTY_DATE_FIELD_VALUE = "0001-01-01T00:00:00Z"; @@ -464,9 +502,30 @@ export interface ErrorPagesServiceConfig extends GraphQLServiceConfig { language: string; } +// @internal +export function evaluateShowNode(node: ShowNode, ctx: ResolveContext): boolean; + +// @internal +export interface EventBinding { + // (undocumented) + actions: Action[]; + // (undocumented) + arguments: string[]; + // (undocumented) + bindType: 'event'; +} + // @internal const executeScriptElements: (rootElement: HTMLElement) => void; +// @internal +export interface ExpressionBinding { + // (undocumented) + bindType: 'expression'; + // (undocumented) + value: string; +} + // Warning: (ae-forgotten-export) The symbol "_extractFiles" needs to be exported by the entry point api-surface.d.ts // // @public @@ -494,6 +553,16 @@ export interface FieldMetadata { // @internal export function filterComponentsByType(components: ComponentFileWithType[], allowedTypes: ComponentType[]): ComponentFileWithType[]; +// @internal +export interface ForLoop { + // (undocumented) + as: string; + // (undocumented) + each: string; + // (undocumented) + key?: string; +} + declare namespace form { export { executeScriptElements, @@ -681,6 +750,16 @@ export { GraphQLRequestClientFactory } export { GraphQLRequestClientFactoryConfig } +// @internal +export function hasFor(node: Element_2): node is Element_2 & { + for: ForLoop; +}; + +// @internal +export function hasShow(node: Element_2): node is Element_2 & { + show: ShowNode; +}; + // @internal export const HIDDEN_RENDERING_NAME = "Hidden Rendering"; @@ -711,6 +790,9 @@ export interface ImportEntryInfo { // @internal export const INVALID_SECRET_HTML_MESSAGE = "Missing or invalid secret"; +// @internal +export function isCallAction(action: Action): action is CallAction; + // @internal export function isDesignLibraryMode(mode: unknown): mode is DesignLibraryMode; @@ -720,9 +802,33 @@ export const isDynamicPlaceholder: (placeholder: string) => boolean; // @public export const isEditorActive: () => boolean; +// @internal +export function isElement(node: Node_2): node is Element_2; + +// @internal +export function isEventBinding(binding: Binding): binding is EventBinding; + +// @internal +export function isExpressionBinding(binding: Binding): binding is ExpressionBinding; + // @public export function isFieldValueEmpty(field: GenericFieldValue | Partial | null | undefined): field is null | undefined; +// @internal +export function isPrimitive(value: unknown): value is Primitive; + +// @internal +export function isSetStateAction(action: Action): action is SetStateAction; + +// @internal +export function isShowAnd(node: ShowNode): node is ShowAnd; + +// @internal +export function isShowComparison(node: ShowNode): node is ShowComparison; + +// @internal +export function isShowOr(node: ShowNode): node is ShowOr; + // @public export interface Item { // (undocumented) @@ -839,6 +945,10 @@ export type ModuleExports = { namespaceExport: string | null; }; +// @internal +type Node_2 = Element_2 | Primitive; +export { Node_2 as Node } + // @public export function normalizePersonalizedRewrite(pathname: string): string; @@ -946,6 +1056,9 @@ export const postToDesignLibrary: (evt: DesignLibraryEvent) => void; // @internal export const PREVIEW_KEY = "sc_preview"; +// @internal +export type Primitive = string | number | boolean | null; + // @internal export const QUERY_PARAM_EDITING_SECRET = "secret"; @@ -1029,6 +1142,21 @@ const replaceMediaUrlPrefix: (url: string, mediaUrlPrefix?: RegExp) => string; // @public export const resetEditorChromes: () => void; +// @internal +export interface ResolveContext { + event?: unknown; + item?: unknown; + props: Record; + scope?: Record; + state: Record; +} + +// @internal +export const resolveIfTemplate: (value: unknown, ctx: ResolveContext) => unknown; + +// @internal +export function resolveTemplateString(template: string, ctx: ResolveContext): unknown; + export { RetryStrategy } // @public @@ -1108,6 +1236,9 @@ export type ScaffoldTemplate = { getNextSteps?: (componentOutputPath: string) => string[]; }; +// @internal +export const sendAtomsErrorEvent: (error: unknown, type: DesignLibraryAtomsError) => void; + // @internal export const sendErrorEvent: (uid: string, error: unknown, type: DesignLibraryPreviewError) => void; @@ -1124,6 +1255,37 @@ export interface ServerComponentPreviewEventArgs extends DesignLibraryEvent { name: typeof DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME; } +// @internal +export interface SetStateAction { + // (undocumented) + setState: Record; +} + +// @internal +export interface ShowAnd { + // (undocumented) + and: ShowNode[]; +} + +// @internal +export interface ShowComparison { + // (undocumented) + left: string; + // (undocumented) + op: 'eq' | 'ne'; + // (undocumented) + right: string; +} + +// @internal +export type ShowNode = ShowComparison | ShowAnd | ShowOr; + +// @internal +export interface ShowOr { + // (undocumented) + or: ShowNode[]; +} + // @public export const SITE_KEY = "sc_site"; @@ -1412,7 +1574,7 @@ export type WriteImportMapArgsInternal = WriteImportMapArgs & { // Warnings were encountered during analysis: // // src/client/sitecore-client.ts:68:3 - (ae-forgotten-export) The symbol "PageModeName" needs to be exported by the entry point api-surface.d.ts -// src/editing/atoms-builder/atoms-builder.ts:63:3 - (ae-forgotten-export) The symbol "SerializedDefaultChild" needs to be exported by the entry point api-surface.d.ts +// src/editing/atoms-builder/atoms-builder.ts:95:3 - (ae-forgotten-export) The symbol "SerializedDefaultChild" needs to be exported by the entry point api-surface.d.ts // src/editing/codegen/preview.ts:115:3 - (ae-forgotten-export) The symbol "ComponentImport_2" needs to be exported by the entry point api-surface.d.ts // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "ComponentMapTemplate" which is marked as @internal // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "EnhancedComponentMapTemplate" which is marked as @internal diff --git a/packages/content/atoms.d.ts b/packages/content/atoms.d.ts new file mode 100644 index 0000000000..997de60048 --- /dev/null +++ b/packages/content/atoms.d.ts @@ -0,0 +1 @@ +export * from './types/atoms/index'; diff --git a/packages/content/package.json b/packages/content/package.json index 4e163f7570..8dc61ea504 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -15,7 +15,7 @@ "test": "mocha \"./src/**/*.test.ts\"", "prepublishOnly": "npm run build", "coverage": "nyc npm test", - "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --outputFileStrategy Members --parametersFormat table --readme none --out ../../ref-docs/content --entryPoints src/index.ts --entryPoints src/config/index.ts --entryPoints src/config-cli/index.ts --entryPoints src/client/index.ts --entryPoints src/i18n/index.ts --entryPoints src/layout/index.ts --entryPoints src/media/index.ts --entryPoints src/personalize/index.ts --entryPoints src/site/index.ts --entryPoints src/editing/index.ts --entryPoints src/tools/index.ts --entryPoints src/tools/index-node.ts --entryPoints src/codegen/index.ts --githubPages false", + "generate-docs": "npx typedoc --plugin typedoc-plugin-markdown --outputFileStrategy Members --parametersFormat table --readme none --out ../../ref-docs/content --entryPoints src/index.ts --entryPoints src/config/index.ts --entryPoints src/config-cli/index.ts --entryPoints src/client/index.ts --entryPoints src/i18n/index.ts --entryPoints src/layout/index.ts --entryPoints src/media/index.ts --entryPoints src/personalize/index.ts --entryPoints src/site/index.ts --entryPoints src/editing/index.ts --entryPoints src/atoms/index.ts --entryPoints src/tools/index.ts --entryPoints src/tools/index-node.ts --entryPoints src/codegen/index.ts --githubPages false", "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, @@ -118,6 +118,11 @@ "require": "./dist/cjs/editing/codegen/index.js", "types": "./types/editing/codegen/index.d.ts" }, + "./atoms": { + "import": "./dist/esm/atoms/index.js", + "require": "./dist/cjs/atoms/index.js", + "types": "./types/atoms/index.d.ts" + }, "./editing": { "import": "./dist/esm/editing/index.js", "require": "./dist/cjs/editing/index.js", diff --git a/packages/content/src/atoms/component-layout/document.test.ts b/packages/content/src/atoms/component-layout/document.test.ts new file mode 100644 index 0000000000..f9b54e83f0 --- /dev/null +++ b/packages/content/src/atoms/component-layout/document.test.ts @@ -0,0 +1,129 @@ +import { expect } from 'chai'; +import type { + Action, + Binding, + Element, + Node, + ShowNode, +} from './document'; +import { + hasFor, + hasShow, + isCallAction, + isElement, + isEventBinding, + isExpressionBinding, + isPrimitive, + isSetStateAction, + isShowAnd, + isShowComparison, + isShowOr, +} from './document'; + +describe('component-layout document guards', () => { + describe('isElement', () => { + it('returns true for object nodes with a type field', () => { + const el: Node = { type: 'Card', id: '1' }; + expect(isElement(el)).to.equal(true); + }); + + it('returns false for primitives', () => { + expect(isElement('text')).to.equal(false); + expect(isElement(null)).to.equal(false); + expect(isElement(42)).to.equal(false); + }); + + it('returns false for plain objects without a type field', () => { + expect(isElement({} as unknown as Node)).to.equal(false); + }); + }); + + describe('hasFor', () => { + it('returns true when for.each is a string', () => { + const el: Element = { + type: 'Row', + for: { each: '{{props.items}}', as: 'item' }, + }; + expect(hasFor(el)).to.equal(true); + }); + + it('returns false when for is missing or invalid', () => { + expect(hasFor({ type: 'Row' })).to.equal(false); + expect(hasFor({ type: 'Row', for: {} as never })).to.equal(false); + }); + + it('returns false when for.each is not a string', () => { + const el = { + type: 'Row', + for: { each: 123, as: 'item' }, + } as unknown as Element; + expect(hasFor(el)).to.equal(false); + }); + }); + + describe('hasShow', () => { + it('returns true when show is set', () => { + const show: ShowNode = { left: 'a', op: 'eq', right: 'b' }; + const el: Element = { type: 'Row', show }; + expect(hasShow(el)).to.equal(true); + }); + + it('returns false when show is null or undefined', () => { + expect(hasShow({ type: 'Row' })).to.equal(false); + expect(hasShow({ type: 'Row', show: null as never })).to.equal(false); + }); + }); + + describe('binding guards', () => { + it('isExpressionBinding / isEventBinding', () => { + const expr: Binding = { bindType: 'expression', value: '{{props.x}}' }; + const evt: Binding = { + bindType: 'event', + arguments: [], + actions: [], + }; + expect(isExpressionBinding(expr)).to.equal(true); + expect(isEventBinding(expr)).to.equal(false); + expect(isExpressionBinding(evt)).to.equal(false); + expect(isEventBinding(evt)).to.equal(true); + }); + }); + + describe('action guards', () => { + it('isSetStateAction / isCallAction', () => { + const set: Action = { setState: { x: '1' } }; + const call: Action = { call: 'cb', args: [] }; + expect(isSetStateAction(set)).to.equal(true); + expect(isCallAction(set)).to.equal(false); + expect(isSetStateAction(call)).to.equal(false); + expect(isCallAction(call)).to.equal(true); + }); + }); + + describe('isPrimitive', () => { + it('accepts null, string, number, boolean only', () => { + expect(isPrimitive(null)).to.equal(true); + expect(isPrimitive('a')).to.equal(true); + expect(isPrimitive(0)).to.equal(true); + expect(isPrimitive(false)).to.equal(true); + expect(isPrimitive({})).to.equal(false); + expect(isPrimitive(undefined)).to.equal(false); + }); + }); + + describe('show node guards', () => { + it('isShowComparison', () => { + const n: ShowNode = { left: 'x', op: 'eq', right: 'y' }; + expect(isShowComparison(n)).to.equal(true); + expect(isShowAnd(n)).to.equal(false); + expect(isShowOr(n)).to.equal(false); + }); + + it('isShowAnd / isShowOr', () => { + const and: ShowNode = { and: [{ left: 'a', op: 'eq', right: 'a' }] }; + const or: ShowNode = { or: [{ left: 'a', op: 'eq', right: 'b' }] }; + expect(isShowAnd(and)).to.equal(true); + expect(isShowOr(or)).to.equal(true); + }); + }); +}); diff --git a/packages/content/src/atoms/component-layout/document.ts b/packages/content/src/atoms/component-layout/document.ts new file mode 100644 index 0000000000..77341e5750 --- /dev/null +++ b/packages/content/src/atoms/component-layout/document.ts @@ -0,0 +1,294 @@ +/** + * Component Layout document types — contract between Design Studio / XM and the rendering host. + * Aligned with the Component Layout technical specification (Document, Node, Element, bindings, for, show). + */ + +/* Primitive values */ + +/** + * Primitive value in the layout tree. + * @internal + */ +export type Primitive = string | number | boolean | null; + +/* Bindings */ + +/** + * Expression binding: `{{ }}` template string resolved at runtime (props, state, item, scope, etc.). + * @internal + */ +export interface ExpressionBinding { + /** Discriminator for expression bindings. */ + bindType: 'expression'; + /** Template text inside `{{ }}` (may reference props, state, item, scope, etc.). */ + value: string; +} + +/** + * Action executed when an event fires: merges into document/component state (see `state` on {@link Document}). + * @internal + */ +export interface SetStateAction { + /** State patch: keys are state field names; values are primitives applied via setState in the runtime. */ + setState: Record; +} + +/** + * Action to call a named callback with arguments. + * @internal + */ +export interface CallAction { + /** Registered callback name (must match an entry in the callback registry). */ + call: string; + /** Arguments passed to the callback; each value may be a literal or resolved from templates. */ + args?: Primitive[]; +} + +/** + * Union of actions allowed inside an {@link EventBinding}: update state or invoke a registered callback. + * @internal + */ +export type Action = SetStateAction | CallAction; + +/** + * Event binding: DOM-style handler that runs a list of actions (setState / call). + * @internal + */ +export interface EventBinding { + /** Discriminator for event bindings. */ + bindType: 'event'; + /** Names of handler parameters (e.g. DOM event argument names) used when resolving action templates. */ + arguments: string[]; + /** Actions to run when the event fires, in order. */ + actions: Action[]; +} + +/** + * Per-prop binding on an {@link Element}: either a one-way expression or a typed event handler. + * @internal + */ +export type Binding = ExpressionBinding | EventBinding; + +/* For loop */ + +/** + * For-loop: iterate over an array resolved from a binding expression; `as` names the loop variable in child scope. + * @internal + */ +export interface ForLoop { + /** Expression (typically `state.items` or `props.rows`) that resolves to an array to iterate. */ + each: string; + /** Variable name for the current item in nested expressions (e.g. `item.id`). */ + as: string; + /** Optional expression for a stable React key per row. */ + key?: string; +} + +/* Conditional visibility (show) — tree-based */ + +/** + * Show condition: comparison (left op right). Operands are template strings resolved before compare. + * @internal + */ +export interface ShowComparison { + /** Left-hand expression (template string). */ + left: string; + /** Equality or inequality after resolving both sides. */ + op: 'eq' | 'ne'; + /** Right-hand expression (template string). */ + right: string; +} + +/** + * Show condition: logical AND — all child nodes must evaluate to true. + * @internal + */ +export interface ShowAnd { + /** Nested show conditions (comparisons or nested and/or). */ + and: ShowNode[]; +} + +/** + * Show condition: logical OR — at least one child node must evaluate to true. + * @internal + */ +export interface ShowOr { + /** Nested show conditions (comparisons or nested and/or). */ + or: ShowNode[]; +} + +/** + * Recursive show AST: leaf comparison or nested AND / OR groups. + * @internal + */ +export type ShowNode = ShowComparison | ShowAnd | ShowOr; + +/* Nodes */ + +/** + * Element node: atom type, optional id, props, bindings, children, for, show, layout. + * @internal + */ +export interface Element { + /** Atom name matching a key in the Atom Registry */ + type: string; + /** Unique identifier. Hydrated by the editor on load. Used for design-time selection and highlight metadata */ + id?: string; + /** Version number of the element. */ + version?: number; + /** Map of prop name to static Primitive value. Passed directly to the component unchanged. */ + staticProps?: Record; + /** Map of prop name to {@link Binding}. */ + bindings?: Record; + /** Child nodes of the element. */ + children?: Node[]; + /** For-loop configuration for the element. */ + for?: ForLoop; + /** Show condition for the element. */ + show?: ShowNode; +} + +/** + * Node in the layout tree: either an Element or a primitive. + * @internal + */ +export type Node = Element | Primitive; + +/* Document root */ + +/** + * Component Layout document: name, optional initial state, root node, optional runtime props. + * @internal + */ +export interface Document { + /** Human-readable identifier of the document */ + name: string; + /** Initial state for the document. Keys become targets for state.* bindings and setState actions. */ + state?: Record; + /** Root node of the component tree. */ + root: Node; + /** Static props payload spread into the runtime props object passed to the generated component. Fields are accessible via props.* bindings. */ + props?: unknown; +} + +/* Type guards */ + +const isObj = (x: unknown): x is Record => typeof x === 'object' && x !== null; + +const has = (o: T, k: PropertyKey): boolean => + Object.prototype.hasOwnProperty.call(o, k); + +/** + * Type guard: node is an Element. + * @param {Node} node - The node to check + * @returns {boolean} True if the node is an Element, false otherwise + * @internal + */ +export function isElement(node: Node): node is Element { + return isObj(node) && has(node, 'type'); +} + +/** + * Type guard: element has a for-loop. + * @param {Element} node - The element to check + * @returns {boolean} True if the element has a for-loop, false otherwise + * @internal + */ +export function hasFor(node: Element): node is Element & { for: ForLoop } { + return node.for !== null && node.for !== undefined && typeof node.for.each === 'string'; +} + +/** + * Type guard: element has a show condition. + * @param {Element} node - The element to check + * @returns {boolean} True if the element has a show condition, false otherwise + * @internal + */ +export function hasShow(node: Element): node is Element & { show: ShowNode } { + return node.show !== null && node.show !== undefined; +} + +/** + * Type guard: binding is an expression binding. + * @param {Binding} binding - The binding to check + * @returns {boolean} True if the binding is an expression binding, false otherwise + * @internal + */ +export function isExpressionBinding(binding: Binding): binding is ExpressionBinding { + return isObj(binding) && (binding as ExpressionBinding).bindType === 'expression'; +} + +/** + * Type guard: binding is an event binding. + * @param {Binding} binding - The binding to check + * @returns {boolean} True if the binding is an event binding, false otherwise + * @internal + */ +export function isEventBinding(binding: Binding): binding is EventBinding { + return isObj(binding) && (binding as EventBinding).bindType === 'event'; +} + +/** + * Type guard: action is setState. + * @param {Action} action - The action to check + * @returns {boolean} True if the action is setState, false otherwise + * @internal + */ +export function isSetStateAction(action: Action): action is SetStateAction { + return isObj(action) && has(action, 'setState'); +} + +/** + * Type guard: action is call. + * @param {Action} action - The action to check + * @returns {boolean} True if the action is call, false otherwise + * @internal + */ +export function isCallAction(action: Action): action is CallAction { + return isObj(action) && has(action, 'call'); +} + +/** + * Type guard: value is a primitive. + * @param {unknown} value - The value to check + * @returns {boolean} True if the value is a primitive, false otherwise + * @internal + */ +export function isPrimitive(value: unknown): value is Primitive { + return ( + value === null || + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ); +} + +/** + * Type guard: show node is a comparison. + * @param {ShowNode} node - The node to check + * @returns {boolean} True if the node is a comparison, false otherwise + * @internal + */ +export function isShowComparison(node: ShowNode): node is ShowComparison { + return isObj(node) && has(node, 'left') && has(node, 'op') && has(node, 'right'); +} + +/** + * Type guard: show node is and. + * @param {ShowNode} node - The node to check + * @returns {boolean} True if the node is an and, false otherwise + * @internal + */ +export function isShowAnd(node: ShowNode): node is ShowAnd { + return isObj(node) && has(node, 'and') && Array.isArray((node as ShowAnd).and); +} + +/** + * Type guard: show node is or. + * @param {ShowNode} node - The node to check + * @returns {boolean} True if the node is an or, false otherwise + * @internal + */ +export function isShowOr(node: ShowNode): node is ShowOr { + return isObj(node) && has(node, 'or') && Array.isArray((node as ShowOr).or); +} diff --git a/packages/content/src/atoms/component-layout/resolver.test.ts b/packages/content/src/atoms/component-layout/resolver.test.ts new file mode 100644 index 0000000000..66d4b94949 --- /dev/null +++ b/packages/content/src/atoms/component-layout/resolver.test.ts @@ -0,0 +1,279 @@ +/* eslint-disable no-unused-expressions */ +import { expect } from 'chai'; +import { + parseBindExpression, + resolveBindExpression, + isTemplateString, + resolveTemplateString, + evaluateShowNode, + resolveIfTemplate, + type ResolveContext, +} from './resolver'; + +const ctx: ResolveContext = { + props: { user: { name: 'Alice' }, count: 0 }, + item: { id: 'item-1', label: 'First' }, + state: { visible: true, category: 'cat' }, + event: { value: 'clicked' }, +}; + +describe('component-layout resolver', () => { + describe('parseBindExpression', () => { + it('should parse simple source-only expressions', () => { + expect(parseBindExpression('props')).to.deep.equal({ source: 'props', segments: [] }); + expect(parseBindExpression('item')).to.deep.equal({ source: 'item', segments: [] }); + expect(parseBindExpression('state')).to.deep.equal({ source: 'state', segments: [] }); + expect(parseBindExpression('event')).to.deep.equal({ source: 'event', segments: [] }); + }); + + it('should accept optional leading $', () => { + expect(parseBindExpression('$props')).to.deep.equal({ source: 'props', segments: [] }); + expect(parseBindExpression('$state.count')).to.deep.equal({ + source: 'state', + segments: [{ type: 'dot', key: 'count' }], + }); + }); + + it('should parse dot path segments', () => { + expect(parseBindExpression('props.user')).to.deep.equal({ + source: 'props', + segments: [{ type: 'dot', key: 'user' }], + }); + expect(parseBindExpression('props.user.name')).to.deep.equal({ + source: 'props', + segments: [ + { type: 'dot', key: 'user' }, + { type: 'dot', key: 'name' }, + ], + }); + expect(parseBindExpression('item.id')).to.deep.equal({ + source: 'item', + segments: [{ type: 'dot', key: 'id' }], + }); + }); + + it('should parse bracket segment with nested expression', () => { + const parsed = parseBindExpression('props.user[state.category]'); + expect(parsed.source).to.equal('props'); + expect(parsed.segments).to.have.lengthOf(2); + expect(parsed.segments[0]).to.deep.equal({ type: 'dot', key: 'user' }); + expect(parsed.segments[1]).to.have.property('type', 'bracket'); + expect( + (parsed.segments[1] as { expr: { source: string; segments: unknown[] } }).expr + ).to.deep.equal({ source: 'state', segments: [{ type: 'dot', key: 'category' }] }); + }); + + it('should parse scope-key sources (e.g. for.as)', () => { + expect(parseBindExpression('product.name')).to.deep.equal({ + source: 'product', + segments: [{ type: 'dot', key: 'name' }], + }); + expect(parseBindExpression('row.id')).to.deep.equal({ + source: 'row', + segments: [{ type: 'dot', key: 'id' }], + }); + }); + + it('should throw on empty source', () => { + expect(() => parseBindExpression('$')).to.throw(/Expected identifier/); + }); + + it('should throw on invalid syntax', () => { + expect(() => parseBindExpression('props.')).to.throw(/Expected identifier/); + expect(() => parseBindExpression('props.user.')).to.throw(/Expected identifier/); + expect(() => parseBindExpression('props[state.x')).to.throw(/Expected "\]"/); + expect(() => parseBindExpression('props user')).to.throw(/Unexpected character/); + }); + }); + + describe('resolveBindExpression', () => { + it('should resolve source-only', () => { + expect(resolveBindExpression(parseBindExpression('props'), ctx)).to.deep.equal(ctx.props); + expect(resolveBindExpression(parseBindExpression('item'), ctx)).to.equal(ctx.item); + expect(resolveBindExpression(parseBindExpression('state'), ctx)).to.deep.equal(ctx.state); + expect(resolveBindExpression(parseBindExpression('event'), ctx)).to.deep.equal(ctx.event); + }); + + it('should resolve dot paths', () => { + expect(resolveBindExpression(parseBindExpression('props.user.name'), ctx)).to.equal('Alice'); + expect(resolveBindExpression(parseBindExpression('item.label'), ctx)).to.equal('First'); + expect(resolveBindExpression(parseBindExpression('state.visible'), ctx)).to.equal(true); + }); + + it('should return undefined when path is null/undefined', () => { + expect(resolveBindExpression(parseBindExpression('props.missing'), ctx)).to.equal(undefined); + expect(resolveBindExpression(parseBindExpression('props.user.missing'), ctx)).to.equal( + undefined + ); + }); + + it('should resolve bracket segment', () => { + const ctxWithMap = { + ...ctx, + props: { ...ctx.props, cat: 'value-for-cat', other: 'other' } as Record, + }; + const parsed = parseBindExpression('props[state.category]'); + expect(resolveBindExpression(parsed, ctxWithMap)).to.equal('value-for-cat'); + }); + + it('should resolve scope-key source (for.as)', () => { + const ctxWithScope: ResolveContext = { + props: {}, + state: {}, + scope: { product: { name: 'Widget', id: 'p1' } }, + }; + expect(resolveBindExpression(parseBindExpression('product.name'), ctxWithScope)).to.equal( + 'Widget' + ); + expect(resolveBindExpression(parseBindExpression('product.id'), ctxWithScope)).to.equal('p1'); + }); + + it('should resolve item from ctx.item when provided', () => { + expect(resolveBindExpression(parseBindExpression('item.label'), ctx)).to.equal('First'); + }); + + it('should resolve item from scope.item when ctx.item is missing', () => { + const ctxItemInScope: ResolveContext = { + props: {}, + state: {}, + scope: { item: { label: 'FromScope' } }, + }; + expect(resolveBindExpression(parseBindExpression('item.label'), ctxItemInScope)).to.equal( + 'FromScope' + ); + }); + + it('should return undefined for unknown scope key when scope is missing', () => { + const ctxNoScope: ResolveContext = { props: {}, state: {} }; + expect(resolveBindExpression(parseBindExpression('product.name'), ctxNoScope)).to.equal( + undefined + ); + }); + }); + + describe('isTemplateString', () => { + it('should return true when string contains {{ }}', () => { + expect(isTemplateString('{{props.user}}')).to.be.true; + expect(isTemplateString('Hello {{state.name}}!')).to.be.true; + }); + + it('should return false when no template', () => { + expect(isTemplateString('plain')).to.be.false; + expect(isTemplateString('')).to.be.false; + }); + }); + + describe('resolveTemplateString', () => { + it('should return string as-is when no {{ }}', () => { + expect(resolveTemplateString('plain text', ctx)).to.equal('plain text'); + }); + + it('should return raw resolved value when entire string is single {{ }}', () => { + expect(resolveTemplateString('{{props.user.name}}', ctx)).to.equal('Alice'); + expect(resolveTemplateString('{{state.visible}}', ctx)).to.equal(true); + }); + + it('should interpolate mixed literal and placeholders', () => { + expect(resolveTemplateString('Hello {{props.user.name}}!', ctx)).to.equal('Hello Alice!'); + expect(resolveTemplateString('{{item.label}} - {{item.id}}', ctx)).to.equal('First - item-1'); + }); + + it('should replace undefined with empty string in mixed mode', () => { + expect(resolveTemplateString('x{{props.missing}}y', ctx)).to.equal('xy'); + }); + }); + + describe('evaluateShowNode', () => { + it('should evaluate comparison eq/ne with literal left/right', () => { + expect(evaluateShowNode({ left: 'a', op: 'eq', right: 'a' }, ctx)).to.be.true; + expect(evaluateShowNode({ left: 'a', op: 'eq', right: 'b' }, ctx)).to.be.false; + expect(evaluateShowNode({ left: 'a', op: 'ne', right: 'b' }, ctx)).to.be.true; + expect(evaluateShowNode({ left: 'a', op: 'ne', right: 'a' }, ctx)).to.be.false; + }); + + it('should resolve template left/right before comparing', () => { + expect( + evaluateShowNode( + { left: '{{state.visible}}', op: 'eq', right: 'true' }, + { ...ctx, state: { visible: true } } + ) + ).to.be.false; // resolved left is boolean true, right is string "true" + expect( + evaluateShowNode( + { left: '{{state.visible}}', op: 'eq', right: '{{state.visible}}' }, + { ...ctx, state: { visible: true } } + ) + ).to.be.true; + }); + + it('should evaluate and (all must be true)', () => { + expect( + evaluateShowNode( + { + and: [ + { left: 'a', op: 'eq', right: 'a' }, + { left: 'b', op: 'eq', right: 'b' }, + ], + }, + ctx + ) + ).to.be.true; + expect( + evaluateShowNode( + { + and: [ + { left: 'a', op: 'eq', right: 'a' }, + { left: 'a', op: 'eq', right: 'b' }, + ], + }, + ctx + ) + ).to.be.false; + }); + + it('should evaluate or (at least one true)', () => { + expect( + evaluateShowNode( + { + or: [ + { left: 'a', op: 'eq', right: 'b' }, + { left: 'a', op: 'eq', right: 'a' }, + ], + }, + ctx + ) + ).to.be.true; + expect( + evaluateShowNode( + { + or: [ + { left: 'a', op: 'eq', right: 'b' }, + { left: 'x', op: 'eq', right: 'y' }, + ], + }, + ctx + ) + ).to.be.false; + }); + }); + + describe('resolveIfTemplate', () => { + it('should resolve strings that look like template expressions', () => { + expect(resolveIfTemplate('{{props.user.name}}', ctx)).to.equal('Alice'); + }); + + it('should return non-string values unchanged', () => { + expect(resolveIfTemplate(42, ctx)).to.equal(42); + expect(resolveIfTemplate(true, ctx)).to.equal(true); + expect(resolveIfTemplate(null, ctx)).to.equal(null); + }); + + it('should return plain strings without {{ }} unchanged', () => { + expect(resolveIfTemplate('no-template-here', ctx)).to.equal('no-template-here'); + }); + + it('should delegate mixed literal + {{ }} strings to resolveTemplateString', () => { + expect(resolveIfTemplate('Hello {{props.user.name}}!', ctx)).to.equal('Hello Alice!'); + }); + }); +}); diff --git a/packages/content/src/atoms/component-layout/resolver.ts b/packages/content/src/atoms/component-layout/resolver.ts new file mode 100644 index 0000000000..9263a93663 --- /dev/null +++ b/packages/content/src/atoms/component-layout/resolver.ts @@ -0,0 +1,303 @@ +/** + * Expression and binding resolution for Component Layout documents. + * Parses bind expressions (props/item/state/event), resolves {{ }} template strings, + * and evaluates show conditions. + */ + +import type { ShowNode } from './document'; +import { isShowComparison, isShowAnd, isShowOr } from './document'; + +/* Expression parser */ + +/** + * Source name for bind expressions. Built-in: "props", "item", "state", "event". + * Any other identifier is resolved from context.scope (e.g. for.as loop variable name). + * @internal + */ +export type BindSource = 'props' | 'item' | 'state' | 'event' | (string & Record); + +/** + * Segment in a parsed path: dot key or bracket sub-expression. + * @internal + */ +export type BindSegment = { type: 'dot'; key: string } | { type: 'bracket'; expr: ParsedBind }; + +/** + * Parsed bind expression: source plus path segments. + * @internal + */ +export interface ParsedBind { + source: BindSource; + segments: BindSegment[]; +} + +/** + * Parses an expression string into a structured representation. + * Accepts optional leading "$" and dot/bracket path segments. + * Source may be any identifier: built-ins (props, item, state, event) or a scope key (e.g. for.as). + * @param {string} expr - Expression string (e.g. "props.user.name", "state.count", "item.id", "product.name") + * @returns {ParsedBind} Parsed expression + * @throws {Error} on invalid syntax or empty source + * @internal + */ +export function parseBindExpression(expr: string): ParsedBind { + let pos = 0; + + const skipOptionalDollar = (): void => { + if (expr[pos] === '$') { + pos++; + } + }; + + const parseIdentifier = (): string => { + const start = pos; + while (pos < expr.length && /[a-zA-Z0-9_]/.test(expr[pos])) { + pos++; + } + const identifier = expr.slice(start, pos); + + if (identifier.length === 0) { + throw new Error(`Expected identifier in "${expr}" at pos ${pos}`); + } + if (!/^[a-zA-Z_]/.test(identifier)) { + throw new Error( + `Invalid identifier "${identifier}" in "${expr}". Must start with a letter or underscore.` + ); + } + + return identifier; + }; + + const parseSegments = (): BindSegment[] => { + const segments: BindSegment[] = []; + + while (pos < expr.length) { + if (expr[pos] === '.') { + pos++; + const key = parseIdentifier(); + segments.push({ type: 'dot', key }); + } else if (expr[pos] === '[') { + pos++; // skip [ + const bracketStart = pos; + + // Find the matching ] + const closeBracket = expr.indexOf(']', pos); + if (closeBracket === -1) { + throw new Error(`Expected "]" in "${expr}"`); + } + + // Extract the inner expression between [ and ] + const innerExpr = expr.slice(bracketStart, closeBracket); + const inner = parseBindExpression(innerExpr); + + pos = closeBracket + 1; // move past ] + segments.push({ type: 'bracket', expr: inner }); + } else { + break; + } + } + + return segments; + }; + + skipOptionalDollar(); + const source = parseIdentifier(); + const segments = parseSegments(); + + if (pos !== expr.length) { + throw new Error(`Unexpected character "${expr[pos]}" in "${expr}" at pos ${pos}`); + } + + return { source, segments }; +} + +/* Resolve context and expression resolver */ + +/** + * Runtime context for resolving expressions. + * - props, state, event: built-in sources. + * - item: loop variable (backward compat); also resolvable as scope.item when scope is set. + * - scope: optional map for named loop variables (e.g. scope[for.as] = currentItem). + * @internal + */ +export interface ResolveContext { + /** Runtime props. */ + props: Record; + /** Current state. */ + state: Record; + /** Loop variable. When inside a for-loop, set to current element; also use scope.item when scope is used. */ + item?: unknown; + /** Current event. */ + event?: unknown; + /** Optional scope for named sources (e.g. for.as). Resolved after built-in props/state/event/item. */ + scope?: Record; +} + +/** + * Resolves the source identifier to its runtime value by matching on the source name. + * Built-in names (`props`, `state`, `event`, `item`) use the corresponding context fields; + * any other identifier is read from `ctx.scope[source]`. + * @param {BindSource} source - Source identifier + * @param {ResolveContext} ctx - Runtime context + * @returns {unknown} Resolved source value or undefined + */ +function resolveSource(source: BindSource, ctx: ResolveContext): unknown { + switch (source) { + case 'props': + return ctx.props; + case 'state': + return ctx.state; + case 'event': + return ctx.event; + case 'item': + return ctx.item ?? ctx.scope?.item; + default: + return ctx.scope?.[source]; + } +} + +/** + * Safely accesses a property on an object-like value. + * Returns undefined if current is null/undefined or not an object. + * @param {unknown} current - Current value + * @param {string | number} key - Property key + * @returns {unknown} Property value or undefined + */ +function safePropertyAccess(current: unknown, key: string | number): unknown { + if (current === null || current === undefined) { + return undefined; + } + + // Check if current is an object (including arrays) + if (typeof current !== 'object') { + return undefined; + } + + return (current as Record)[key]; +} + +/** + * Resolves a parsed bind expression against the given context: starts from the resolved + * source root, then walks dot/bracket segments with safe property access. + * @param {ParsedBind} parsed - Parsed expression from parseBindExpression + * @param {ResolveContext} ctx - Runtime context (props, state, event, item, scope) + * @returns {unknown} Resolved value or undefined if any segment is null/undefined + * @internal + */ +export function resolveBindExpression(parsed: ParsedBind, ctx: ResolveContext): unknown { + let current = resolveSource(parsed.source, ctx); + + for (const segment of parsed.segments) { + if (current === null || current === undefined) { + return undefined; + } + + if (segment.type === 'dot') { + current = safePropertyAccess(current, segment.key); + } else { + // Bracket accessor - resolve the expression to get the key + const key = resolveBindExpression(segment.expr, ctx); + if (key === null || key === undefined) { + return undefined; + } + current = safePropertyAccess(current, key as string | number); + } + } + + return current; +} + +/* Template string resolution ({{...}} syntax) */ + +// Match single {{expr}} so inner content cannot contain "}}" +const SINGLE_TEMPLATE_RE = /^\{\{((?:(?!\}\}).)*)\}\}$/; +const TEMPLATE_PATTERN = /\{\{((?:(?!\}\}).)*)\}\}/; +const TEMPLATE_GLOBAL = /\{\{((?:(?!\}\}).)*)\}\}/g; + +/** + * Returns true if the string contains {{...}} template expressions. + * @param {string} s - String to check + * @returns {boolean} True if the string contains {{...}} templates, false otherwise + * @internal + */ +export function isTemplateString(s: string): boolean { + return TEMPLATE_PATTERN.test(s); +} + +/** + * Resolves a template string containing {{expr}} placeholders. + * - If the entire string is a single {{expr}}, returns the raw resolved value (preserving type). + * - If mixed with literal text, interpolates and returns a string. + * - If no {{}} patterns, returns the string as-is. + * @param {string} template - String that may contain {{...}} placeholders + * @param {ResolveContext} ctx - Runtime context for resolving expressions + * @returns {unknown} Resolved value (any type for single {{}}, string otherwise) + * @internal + */ +export function resolveTemplateString(template: string, ctx: ResolveContext): unknown { + const singleMatch = template.match(SINGLE_TEMPLATE_RE); + if (singleMatch) { + const parsed = parseBindExpression(singleMatch[1].trim()); + return resolveBindExpression(parsed, ctx); + } + + if (!TEMPLATE_PATTERN.test(template)) { + return template; + } + + return template.replace(TEMPLATE_GLOBAL, (_, expr) => { + const parsed = parseBindExpression(expr.trim()); + const resolved = resolveBindExpression(parsed, ctx); + return resolved !== null && resolved !== undefined ? String(resolved) : ''; + }); +} + +/* Show condition evaluation */ + +/** + * Evaluates a ShowNode tree against the runtime context. + * Left/right in comparisons may be template strings and are resolved before comparing. + * @param {ShowNode} node - Show condition node (comparison or and/or tree) + * @param {ResolveContext} ctx - Runtime context + * @returns {boolean} True if the condition passes (element should be visible) + * @internal + */ +export function evaluateShowNode(node: ShowNode, ctx: ResolveContext): boolean { + if (isShowAnd(node)) { + return node.and.every((child) => evaluateShowNode(child, ctx)); + } + if (isShowOr(node)) { + return node.or.some((child) => evaluateShowNode(child, ctx)); + } + if (isShowComparison(node)) { + const left = isTemplateString(node.left) ? resolveTemplateString(node.left, ctx) : node.left; + const right = isTemplateString(node.right) + ? resolveTemplateString(node.right, ctx) + : node.right; + switch (node.op) { + case 'eq': + return left === right; + case 'ne': + return left !== right; + default: + return true; + } + } + return true; +} + +/** + * Resolves a template string value against the provided context. + * Returns the original value when it is not a template string. + * @param {unknown} value - Value that may be a template string + * @param {ResolveContext} ctx - Resolve context + * @returns {unknown} Resolved value or original value + * @internal + */ +export const resolveIfTemplate = (value: unknown, ctx: ResolveContext): unknown => { + if (typeof value === 'string' && isTemplateString(value)) { + return resolveTemplateString(value, ctx); + } + + return value; +}; diff --git a/packages/content/src/atoms/index.ts b/packages/content/src/atoms/index.ts new file mode 100644 index 0000000000..f8e5ff93c7 --- /dev/null +++ b/packages/content/src/atoms/index.ts @@ -0,0 +1,44 @@ +/** + * Component Layout document types, guards, and runtime resolution helpers exposed on the + * `@sitecore-content-sdk/content/atoms` entry point. Parser-level APIs (`parseBindExpression`, + * etc.) remain available from `./component-layout/resolver` within this package. + */ + +export { + type Primitive, + type ExpressionBinding, + type EventBinding, + type Binding, + type SetStateAction, + type CallAction, + type Action, + type ForLoop, + type ShowComparison, + type ShowAnd, + type ShowOr, + type ShowNode, + type Element, + type Node, + type Document, +} from './component-layout/document'; + +export { + isElement, + hasFor, + hasShow, + isExpressionBinding, + isEventBinding, + isSetStateAction, + isCallAction, + isPrimitive, + isShowComparison, + isShowAnd, + isShowOr, +} from './component-layout/document'; + +export { + type ResolveContext, + resolveTemplateString, + evaluateShowNode, + resolveIfTemplate, +} from './component-layout/resolver'; diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.test.ts b/packages/content/src/editing/atoms-builder/atoms-builder.test.ts index c635ab13cd..51861643da 100644 --- a/packages/content/src/editing/atoms-builder/atoms-builder.test.ts +++ b/packages/content/src/editing/atoms-builder/atoms-builder.test.ts @@ -1,16 +1,18 @@ import { expect } from 'chai'; -import { AtomInfo, AtomType, getDesignLibraryAtomsRegistryEvent } from './atoms-builder'; +import sinon from 'sinon'; +import type { Document } from '../../atoms/component-layout/document'; +import { AtomInfo, CallbackInfo, addDocumentUpdateHandler, getDesignLibraryAtomsRegistryEvent } from './atoms-builder'; describe('atoms-builder', () => { describe('getDesignLibraryAtomsRegistryEvent', () => { it('should return an event with name "atom:registry"', () => { - const event = getDesignLibraryAtomsRegistryEvent([]); + const event = getDesignLibraryAtomsRegistryEvent([], {}); expect(event.name).to.equal('atom:registry'); }); it('should return an event with an empty atoms registry when given an empty array', () => { - const event = getDesignLibraryAtomsRegistryEvent([]); + const event = getDesignLibraryAtomsRegistryEvent([], {}); expect(event.message.atomsRegistry).to.deep.equal([]); }); @@ -19,14 +21,14 @@ describe('atoms-builder', () => { const atomsRegistry: AtomInfo[] = [ { name: 'Button', - type: AtomType.ATOM, + type: 'atom', description: 'A button atom', props: { label: 'string' }, allowedChildren: [], }, ]; - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); }); @@ -35,21 +37,21 @@ describe('atoms-builder', () => { const atomsRegistry: AtomInfo[] = [ { name: 'Button', - type: AtomType.ATOM, + type: 'atom', description: 'A button atom', props: { label: 'string' }, allowedChildren: ['ButtonIcon'], }, { name: 'ButtonIcon', - type: AtomType.ATOM_CHILD, + type: 'atom-child', description: 'An icon child of Button', props: { src: 'string' }, allowedChildren: [], }, ]; - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); expect(event.message.atomsRegistry).to.have.length(2); expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); @@ -60,7 +62,7 @@ describe('atoms-builder', () => { { name: 'Card', version: 2, - type: AtomType.ATOM, + type: 'atom', description: 'A card atom with optional fields', props: { title: 'string' }, allowedChildren: ['CardBody'], @@ -70,7 +72,7 @@ describe('atoms-builder', () => { }, ]; - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry); + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); const [atom] = event.message.atomsRegistry; expect(atom.version).to.equal(2); @@ -81,5 +83,132 @@ describe('atoms-builder', () => { expect(atom.htmlEvents).to.deep.equal(['click', 'focus']); expect(atom.customEvents).to.deep.equal({ onExpand: 'CustomExpandEvent' }); }); + + it('should return an event with an empty callback registry when given an empty object', () => { + const event = getDesignLibraryAtomsRegistryEvent([], {}); + + expect(event.message.callbackRegistry).to.deep.equal({}); + }); + + it('should return an event containing the provided callback registry', () => { + const callbackRegistry: Record = { + handleClick: { + description: 'Handles click events', + params: { event: 'MouseEvent' }, + }, + }; + + const event = getDesignLibraryAtomsRegistryEvent([], callbackRegistry); + + expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); + }); + + it('should return an event containing multiple callbacks', () => { + const callbackRegistry: Record = { + handleClick: { + description: 'Handles click events', + params: { event: 'MouseEvent' }, + }, + handleSubmit: { + description: 'Handles form submission', + }, + handleChange: { + description: 'Handles input changes', + params: { value: 'string', name: 'string' }, + }, + }; + + const event = getDesignLibraryAtomsRegistryEvent([], callbackRegistry); + + expect(Object.keys(event.message.callbackRegistry)).to.have.length(3); + expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); + }); + + it('should return an event with both atoms registry and callback registry', () => { + const atomsRegistry: AtomInfo[] = [ + { + name: 'Button', + type: 'atom', + description: 'A button atom', + props: { label: 'string' }, + allowedChildren: [], + }, + ]; + + const callbackRegistry: Record = { + onClick: { + description: 'Button click handler', + params: { event: 'MouseEvent' }, + }, + }; + + const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, callbackRegistry); + + expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); + expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); + }); + }); + + describe('addDocumentUpdateHandler', () => { + let addListener: sinon.SinonStub; + let removeListener: sinon.SinonStub; + let messageHandler: ((e: MessageEvent) => void) | undefined; + + beforeEach(() => { + messageHandler = undefined; + addListener = sinon.stub().callsFake((event: string, handler: (e: MessageEvent) => void) => { + if (event === 'message') { + messageHandler = handler; + } + }); + removeListener = sinon.stub(); + (globalThis as unknown as { window: Window & typeof globalThis }).window = { + addEventListener: addListener, + removeEventListener: removeListener, + } as unknown as Window & typeof globalThis; + }); + + afterEach(() => { + delete (globalThis as unknown as { window?: Window }).window; + }); + + it('should register a message listener and invoke callback with document when event is valid', () => { + const callback = sinon.spy(); + const doc: Document = { + name: 'preview', + root: { type: 'Stack', id: 'r1', children: [] }, + }; + + const unsub = addDocumentUpdateHandler(callback); + + sinon.assert.calledWith(addListener, 'message', sinon.match.func); + + messageHandler!( + new MessageEvent('message', { + origin: 'http://localhost', + data: { name: 'component:atoms:preview', document: doc }, + }) + ); + + sinon.assert.calledOnceWithExactly(callback, doc); + expect(typeof unsub).to.equal('function'); + + unsub(); + sinon.assert.calledWith(removeListener, 'message', messageHandler); + }); + + it('should ignore messages that do not match the preview event name', () => { + const callback = sinon.spy(); + addDocumentUpdateHandler(callback); + + messageHandler!( + new MessageEvent('message', { + origin: 'http://localhost', + data: { name: 'other:event', document: {} }, + }) + ); + + sinon.assert.notCalled(callback); + }); }); }); diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.ts b/packages/content/src/editing/atoms-builder/atoms-builder.ts index a557c801bb..10385fa230 100644 --- a/packages/content/src/editing/atoms-builder/atoms-builder.ts +++ b/packages/content/src/editing/atoms-builder/atoms-builder.ts @@ -1,15 +1,47 @@ -import { DesignLibraryEvent } from '../design-library'; +import { constants } from '@sitecore-content-sdk/core'; +import { DesignLibraryEvent, validateEvent } from '../design-library'; +import type { Document } from '../../atoms/component-layout/document'; + +const { ERROR_MESSAGES } = constants; + +/** + * Event name for component preview updates from design library + */ +const DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME = 'component:atoms:preview'; /** * Event to send import map to design library */ const DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME = 'atom:registry'; +/** + * Event to send to design library when rendering atoms error occurs + */ +const DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME = 'atoms:error'; + +/** + * Enumeration of error types for the design library atoms. + * @internal + */ +export type DesignLibraryAtomsError = 'render' | 'atoms-missing'; + +/** + * Represents a atom rendering error event to be sent to design library + * @internal + */ +export interface DesignLibraryAtomsErrorEvent extends DesignLibraryEvent { + name: typeof DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME; + message: { + error: unknown; + type: DesignLibraryAtomsError; + }; +} + /** * Represents the type of atom, which can be either a top-level atom or an atom child * @internal */ -export const AtomType = { ATOM: 'atom', ATOM_CHILD: 'atom-child' } as const; +export type AtomType = 'atom' | 'atom-child'; export type SerializedDefaultChild = string | { atom: string; props?: Record }; @@ -42,9 +74,9 @@ export type AtomInfo = { */ version?: number; /** - * The type of the atom, which can be either a top-level 'atom' or an 'atom-child'. + * The type of the atom. */ - type: (typeof AtomType)[keyof typeof AtomType]; + type: AtomType; /** * A description of the atom. */ @@ -102,3 +134,64 @@ export function getDesignLibraryAtomsRegistryEvent( }, }; } + +/** + * Generates a DesignLibraryAtomsErrorEvent depending on the type of error with the given error. + * @param {unknown} error - The error to be sent. + * @param {DesignLibraryAtomsError} type - The type of error. + * @returns An object representing the DesignLibraryAtomsErrorEvent. + * @internal + */ +export function getDesignLibraryAtomsErrorEvent( + error: unknown, + type: DesignLibraryAtomsError +): DesignLibraryAtomsErrorEvent { + return { + name: DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME, + message: { error, type }, + }; +} + +/** + * Sends a design library atoms error event to the design library + * @param {unknown} error - The error object or message to be sent. + * @param {DesignLibraryAtomsError} type - The type of error, as defined in DesignLibraryAtomsError. + * @internal + */ +export const sendAtomsErrorEvent = (error: unknown, type: DesignLibraryAtomsError) => { + const errorEvent = getDesignLibraryAtomsErrorEvent(error, type); + console.error( + `Component Library: sending error event. ${ERROR_MESSAGES.CONTACT_SUPPORT}`, + errorEvent + ); + if (typeof window !== 'undefined') { + const target = window.parent && window.parent !== window ? window.parent : window; + target.postMessage(errorEvent, '*'); + } +}; + +/** + * Adds a handler for atom document update events from the design library. + * @param {(updatedRootComponent: Document) => void} callback - The callback to be invoked when a document update event is received. + * @returns A function to unsubscribe from the atom document update events. + * @internal + */ +export const addDocumentUpdateHandler = (callback: (updatedRootComponent: Document) => void) => { + const handler = (e: MessageEvent) => { + if (!validateEvent(e, DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME)) { + return; + } + + console.debug('Component Library atoms: message received', e.data); + + callback(e.data.document as Document); + }; + + window.addEventListener('message', handler); + + const unsubscribe = () => { + window.removeEventListener('message', handler); + }; + + return unsubscribe; +}; diff --git a/packages/content/src/editing/atoms-builder/index.ts b/packages/content/src/editing/atoms-builder/index.ts index 595215fe14..b5cbfb342b 100644 --- a/packages/content/src/editing/atoms-builder/index.ts +++ b/packages/content/src/editing/atoms-builder/index.ts @@ -2,7 +2,9 @@ export { AtomInfo, AtomType, SerializedDefaultChild, - DesignLibraryAtomsRegistryEvent, getDesignLibraryAtomsRegistryEvent, + sendAtomsErrorEvent, + DesignLibraryAtomsError, + addDocumentUpdateHandler, CallbackInfo, } from './atoms-builder'; diff --git a/packages/content/src/editing/index.ts b/packages/content/src/editing/index.ts index 837bdeb7be..6248cde241 100644 --- a/packages/content/src/editing/index.ts +++ b/packages/content/src/editing/index.ts @@ -46,4 +46,7 @@ export { CallbackInfo, AtomType, getDesignLibraryAtomsRegistryEvent, + sendAtomsErrorEvent, + DesignLibraryAtomsError, + addDocumentUpdateHandler, } from './atoms-builder/atoms-builder'; diff --git a/packages/content/src/editing/models.ts b/packages/content/src/editing/models.ts index 442a396818..1e2744f939 100644 --- a/packages/content/src/editing/models.ts +++ b/packages/content/src/editing/models.ts @@ -82,6 +82,8 @@ export enum DesignLibraryMode { Normal = 'library', /** Metadata mode */ Metadata = 'library-metadata', + /** Atoms mode */ + Atoms = 'library-atoms', } /** diff --git a/packages/content/tsconfig.json b/packages/content/tsconfig.json index 89997e6ec7..7c8692c69d 100644 --- a/packages/content/tsconfig.json +++ b/packages/content/tsconfig.json @@ -25,6 +25,7 @@ "personalize.d.ts", "config-cli.d.ts", "codegen.d.ts", + "atoms.d.ts", "src/**/*.test.ts", "src/test-data/**/*", "src/tools/codegen/test-data/**/*", diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx index 1c5682d358..7405190a35 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx @@ -12,7 +12,7 @@ export default function Providers({ children, page }: { children: React.ReactNod componentMap={components} page={page} loadImportMap={() => import('.sitecore/import-map.client')} - atoms={atoms} + atomRegistry={{ atoms }} > {children} diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 043a8a2b35..1aca4436dd 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -163,7 +163,6 @@ export { AppPlaceholder, AppPlaceholderProps, renderEmptyPlaceholder, - AtomRenderer, AtomType, createAtom, withPropMeta, diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 29a04aa177..646a0f3277 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -79,7 +79,7 @@ export type AtomChild = AtomMetadata | 'text' | 'atom'; export type AtomMetadata = { name: string; version?: number; - type: (typeof AtomType)[keyof typeof AtomType]; + type: AtomType; description: string; props: z.ZodObject; component: (props: unknown) => React.ReactNode; @@ -89,16 +89,11 @@ export type AtomMetadata = { defaultChildren?: DefaultChild[]; }; -// @public (undocumented) -export const AtomRenderer: (input: { - atoms?: AtomMetadata[]; -}) => React_2.JSX.Element; - // @public export type AtomSchemaInput = { name: string; description: string; - type?: (typeof AtomType)[keyof typeof AtomType]; + type?: AtomType; version?: number; props: { [K in keyof EditableComponentProps]?: z.ZodType[K]>; @@ -580,9 +575,10 @@ export const SitecoreProviderReactContext: React_2.Context Promise; @@ -712,7 +708,8 @@ export function withSitecore(options?: UseSitecoreOptions): ({ name, - type: AtomType.ATOM, + type: 'atom', description: `${name} atom`, props: z.object({}), component: () => null, @@ -233,3 +232,43 @@ describe('serializeAtoms', () => { expect(serializedB.allowedChildren).to.deep.equal(['text', 'atom', 'C']); }); }); + +describe('getAtomMap', () => { + it('should return a map of name to component from metadata', () => { + const Button = () => null; + const meta: AtomMetadata = { + name: 'Button', + type: 'atom', + description: 'Button', + props: {} as AtomMetadata['props'], + component: Button, + }; + const registry = getAtomMap([meta]); + expect(registry.Button).to.equal(Button); + expect(Object.keys(registry)).to.deep.equal(['Button']); + }); + + it('should include allowedChildren in registry', () => { + const Card = () => null; + const CardBody = () => null; + const cardMeta: AtomMetadata = { + name: 'Card', + type: 'atom', + description: 'Card', + props: {} as AtomMetadata['props'], + component: Card, + allowedChildren: [ + { + name: 'CardBody', + type: 'atom-child', + description: 'Body', + props: {} as AtomMetadata['props'], + component: CardBody, + }, + ], + }; + const registry = getAtomMap([cardMeta]); + expect(registry.Card).to.equal(Card); + expect(registry.CardBody).to.equal(CardBody); + }); +}); diff --git a/packages/react/src/atoms/atom-registry-utils.ts b/packages/react/src/atoms/atom-registry-utils.ts index 0e3008b544..6a126a880b 100644 --- a/packages/react/src/atoms/atom-registry-utils.ts +++ b/packages/react/src/atoms/atom-registry-utils.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { AtomMetadata } from './types'; import { AtomInfo } from '@sitecore-content-sdk/content/editing'; +import { ComponentType } from 'react'; const isAtomMetadata = (value: unknown): value is AtomMetadata => { return typeof value !== 'string'; @@ -67,3 +68,31 @@ export const serializeAtoms = (atoms: AtomMetadata[]): AtomInfo[] => { return atomInfos; }; + +/** + * Returns a map of atom type name to React component for use with createView. + * @param {AtomMetadata[]} metadata - Array of atom metadata (e.g. from createAtom) + * @returns {Record>} Record of atom name to component + * @internal + */ +export function getAtomMap(metadata: AtomMetadata[]): Record> { + const registry: Record> = {}; + + const addAtom = (atom: AtomMetadata): void => { + if (registry[atom.name]) { + return; + } + registry[atom.name] = atom.component as ComponentType; + for (const child of atom.allowedChildren ?? []) { + if (typeof child === 'object' && child !== null) { + addAtom(child); + } + } + }; + + for (const atom of metadata) { + addAtom(atom); + } + + return registry; +} diff --git a/packages/react/src/atoms/component-layout/createView.test.tsx b/packages/react/src/atoms/component-layout/createView.test.tsx new file mode 100644 index 0000000000..1bc0da85c8 --- /dev/null +++ b/packages/react/src/atoms/component-layout/createView.test.tsx @@ -0,0 +1,2209 @@ +/* eslint-disable jsdoc/require-jsdoc */ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { fireEvent, render } from '@testing-library/react'; +import type { Document, Element, ResolveContext } from '@sitecore-content-sdk/content/atoms'; +import { + buildEventCallback, + createView, + renderElementNode, + renderFor, + renderPrimitiveNode, +} from './createView'; + +describe('component-layout/createView', () => { + describe('renderFor()', () => { + it('renders each item and resolves keys', () => { + const node: Element = { + id: 'item-template', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [], + }; + + const ctx: ResolveContext = { + props: { + items: [ + { id: 'a', title: 'Alpha' }, + { id: 'b', title: 'Beta' }, + ], + }, + state: {}, + item: undefined, + scope: undefined, + }; + + const renderNodeSpy = sinon.spy( + (_tmpl: any, key: React.Key, item: unknown, itemScope: Record) => ( +
+ {(item as { title: string }).title}:{(itemScope.item as { id: string }).id} +
+ ) + ); + + const result = renderFor(node, ctx, renderNodeSpy); + + expect(Array.isArray(result)).to.equal(true); + expect(renderNodeSpy.callCount).to.equal(2); + expect(renderNodeSpy.firstCall.args[0].for).to.equal(undefined); + expect(renderNodeSpy.firstCall.args[1]).to.equal('a'); + expect(renderNodeSpy.secondCall.args[1]).to.equal('b'); + }); + + it('returns null when for.each does not resolve to array', () => { + const node: Element = { + id: 'item-template', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + }, + children: [], + }; + + const ctx: ResolveContext = { + props: { items: 'not-an-array' }, + state: {}, + item: undefined, + scope: undefined, + }; + + const result = renderFor(node, ctx, () => null); + expect(result).to.equal(null); + }); + + it('uses array index as key when for.key is not provided', () => { + const node: Element = { + id: 'item-template-no-key', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + }, + children: [], + }; + + const ctx: ResolveContext = { + props: { + items: ['one', 'two', 'three'], + }, + state: {}, + item: undefined, + scope: undefined, + }; + + const renderNodeSpy = sinon.spy( + (...args: [any, React.Key, unknown, Record]) => { + void args; + return null; + } + ); + + renderFor(node, ctx, renderNodeSpy); + + expect(renderNodeSpy.callCount).to.equal(3); + expect(renderNodeSpy.firstCall.args[1]).to.equal(0); + expect(renderNodeSpy.secondCall.args[1]).to.equal(1); + expect(renderNodeSpy.thirdCall.args[1]).to.equal(2); + }); + + it('returns an empty list when for.each resolves to an empty array', () => { + const node: Element = { + id: 'item-template-empty', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + }, + children: [], + }; + + const ctx: ResolveContext = { + props: { + items: [], + }, + state: {}, + item: undefined, + scope: undefined, + }; + + const renderNodeSpy = sinon.spy( + (...args: [any, React.Key, unknown, Record]) => { + void args; + return null; + } + ); + + const result = renderFor(node, ctx, renderNodeSpy); + + expect(result).to.deep.equal([]); + expect(renderNodeSpy.called).to.equal(false); + }); + + it('passes per-item values and scope to renderNode for each iteration', () => { + const node: Element = { + id: 'item-template-scope', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [], + }; + + const items = [ + { id: 'x1', value: 'A' }, + { id: 'x2', value: 'B' }, + ]; + + const ctx: ResolveContext = { + props: { + items, + }, + state: {}, + item: { ignored: true }, + scope: { parent: 'scope-value' }, + }; + + const renderNodeSpy = sinon.spy( + (...args: [any, React.Key, unknown, Record]) => { + void args; + return null; + } + ); + + renderFor(node, ctx, renderNodeSpy); + + expect(renderNodeSpy.callCount).to.equal(2); + expect(renderNodeSpy.firstCall.args[2]).to.equal(items[0]); + expect(renderNodeSpy.secondCall.args[2]).to.equal(items[1]); + expect(renderNodeSpy.firstCall.args[3]).to.deep.equal({ item: items[0] }); + expect(renderNodeSpy.secondCall.args[3]).to.deep.equal({ item: items[1] }); + }); + + it('does not mutate the original loop node', () => { + const node: Element = { + id: 'item-template-immutable', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [], + }; + + const originalFor = node.for; + + const ctx: ResolveContext = { + props: { + items: [{ id: '1' }], + }, + state: {}, + item: undefined, + scope: undefined, + }; + + const renderNodeSpy = sinon.spy( + (...args: [any, React.Key, unknown, Record]) => { + void args; + return null; + } + ); + + renderFor(node, ctx, renderNodeSpy); + + expect(node.for).to.equal(originalFor); + expect(node.for?.as).to.equal('item'); + expect(renderNodeSpy.firstCall.args[0].for).to.equal(undefined); + }); + + it('supports non-primitive resolved keys from key template', () => { + const node: Element = { + id: 'item-template-object-key', + type: 'Label', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.keyObj}}', + }, + children: [], + }; + + const keyObj = { nested: 'value' }; + const ctx: ResolveContext = { + props: { + items: [{ keyObj }], + }, + state: {}, + item: undefined, + scope: undefined, + }; + + const renderNodeSpy = sinon.spy( + (...args: [any, React.Key, unknown, Record]) => { + void args; + return null; + } + ); + + renderFor(node, ctx, renderNodeSpy); + + expect(renderNodeSpy.calledOnce).to.equal(true); + expect(renderNodeSpy.firstCall.args[1]).to.equal(keyObj as any); + }); + }); + + describe('renderPrimitiveNode()', () => { + const ctx: ResolveContext = { + props: { name: 'Alice' }, + state: {}, + item: undefined, + scope: undefined, + }; + + it('resolves template strings', () => { + const result = renderPrimitiveNode('{{props.name}}', ctx); + expect(result).to.equal('Alice'); + }); + + it('passes through primitive values', () => { + expect(renderPrimitiveNode(0 as any, ctx)).to.equal(0); + expect(renderPrimitiveNode(true as any, ctx)).to.equal(true); + expect(renderPrimitiveNode(null as any, ctx)).to.equal(null); + }); + + it('returns null for non-primitive objects', () => { + const result = renderPrimitiveNode({ bad: 'node' } as any, ctx); + expect(result).to.equal(null); + }); + }); + + describe('renderElementNode()', () => { + const atoms: Record> = { + Card: ({ children, ...props }) => ( +
+ {children} +
+ ), + Child: ({ children }) => {children}, + }; + + const baseContext: ResolveContext = { + props: { + title: 'Hello world', + childText: 'from props', + }, + state: { + message: 'initial', + }, + item: undefined, + scope: undefined, + }; + + it('throws for unknown atom type', () => { + const node: Element = { + id: 'unknown-id', + type: 'Unknown', + children: [], + }; + + expect(() => + renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + () => null + ) + ).to.throw('unknown atom "Unknown"'); + }); + + it('renders atom with resolved props, binding props and children', () => { + const setStateSpy = sinon.spy(); + const callbacks = [ + { + name: 'onChange', + description: 'onChange callback', + callbackFn: sinon.spy(), + }, + ]; + + const childElement: Element = { + id: 'child-node', + type: 'Child', + children: ['nested-child'], + }; + + const node: Element = { + id: 'card-id', + type: 'Card', + staticProps: { + className: 'wrapper', + }, + bindings: { + title: { + bindType: 'expression', + value: '{{props.title}}', + }, + onClick: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + message: '{{event}}', + }, + }, + ], + }, + }, + children: ['{{props.childText}}', childElement], + }; + + const renderNodeSpy = sinon.spy((child: any) => { + if (child?.type === 'Child') { + return ( + + Nested + + ); + } + return null; + }); + + const element = renderElementNode( + node, + 'k1', + atoms, + callbacks, + setStateSpy as any, + () => ({ message: 'initial' }), + baseContext, + renderNodeSpy + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(card.getAttribute('class')).to.equal('wrapper'); + expect(card.getAttribute('data-atom-id')).to.equal('card-id'); + expect(card.getAttribute('data-atom-label')).to.equal('Card'); + expect(card.getAttribute('title')).to.equal('Hello world'); + expect(card.textContent).to.contain('from props'); + expect(card.textContent).to.contain('Nested'); + + expect(renderNodeSpy.calledOnce).to.equal(true); + expect(renderNodeSpy.firstCall.args[0]).to.equal(childElement); + + const onClick = (card as any).onclick ?? (card as any).props?.onClick; + expect(typeof onClick === 'function' || card.getAttribute('onClick') === null).to.equal(true); + }); + + it('renders with only static props and no bindings', () => { + const node: Element = { + id: 'static-id', + type: 'Card', + staticProps: { + className: 'static-class', + role: 'main', + }, + children: ['Static content'], + }; + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + () => null + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(card.getAttribute('class')).to.equal('static-class'); + expect(card.getAttribute('role')).to.equal('main'); + expect(card.textContent).to.equal('Static content'); + }); + + it('renders with only expression bindings', () => { + const node: Element = { + id: 'expr-id', + type: 'Card', + bindings: { + title: { + bindType: 'expression', + value: '{{props.title}}', + }, + subtitle: { + bindType: 'expression', + value: '{{state.message}}', + }, + 'aria-label': { + bindType: 'expression', + value: '{{props.title}} - {{state.message}}', + }, + }, + children: [], + }; + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({ message: 'initial' }), + baseContext, + () => null + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(card.getAttribute('title')).to.equal('Hello world'); + expect(card.getAttribute('subtitle')).to.equal('initial'); + expect(card.getAttribute('aria-label')).to.equal('Hello world - initial'); + }); + + it('renders with primitive-only children (text and template strings)', () => { + const node: Element = { + id: 'primitive-id', + type: 'Card', + children: ['Plain text: ', '{{props.childText}}', ' - end'], + }; + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + () => null + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(card.textContent).to.equal('Plain text: from props - end'); + }); + + it('renders with element-only children (no text content)', () => { + const childElement1: Element = { + id: 'child1', + type: 'Child', + children: ['First child'], + }; + + const childElement2: Element = { + id: 'child2', + type: 'Child', + children: ['Second child'], + }; + + const node: Element = { + id: 'element-children-id', + type: 'Card', + children: [childElement1, childElement2], + }; + + const renderNodeSpy = sinon.spy((child: any) => { + if (child?.id === 'child1') { + return ( + + Child 1 + + ); + } + if (child?.id === 'child2') { + return ( + + Child 2 + + ); + } + return null; + }); + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + renderNodeSpy + ); + + const rendered = render(<>{element}); + expect(rendered.getByTestId('card')).to.exist; + + expect(rendered.getByTestId('child-1')).to.exist; + expect(rendered.getByTestId('child-2')).to.exist; + expect(renderNodeSpy.callCount).to.equal(2); + }); + + it('handles mixed children when renderNode returns null for some elements', () => { + const childElement1: Element = { + id: 'child1', + type: 'Child', + children: ['Keep this'], + }; + + const childElement2: Element = { + id: 'child2', + type: 'Child', + children: ['Skip this'], + }; + + const node: Element = { + id: 'mixed-id', + type: 'Card', + children: [ + 'Text before: ', + childElement1, + ' - text between - ', + childElement2, + ' :text after', + ], + }; + + const renderNodeSpy = sinon.spy((child: any) => { + if (child?.id === 'child1') { + return ( + + Kept + + ); + } + return null; // Skip child2 + }); + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + renderNodeSpy + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(rendered.getByTestId('kept')).to.exist; + expect(card.textContent).to.contain('Text before:'); + expect(card.textContent).to.contain('Kept'); + expect(card.textContent).to.contain('text between'); + expect(card.textContent).to.contain('text after'); + }); + + it('verifies data attributes (data-atom-id and data-atom-label) are set', () => { + const node: Element = { + id: 'special-atom-id', + type: 'Card', + children: ['Content'], + }; + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({}), + baseContext, + () => null + ); + + const rendered = render(<>{element}); + const card = rendered.getByTestId('card'); + + expect(card.getAttribute('data-atom-id')).to.equal('special-atom-id'); + expect(card.getAttribute('data-atom-label')).to.equal('Card'); + }); + + it('handles bindings to undefined props gracefully', () => { + const node: Element = { + id: 'undefined-props-id', + type: 'Card', + bindings: { + missing: { + bindType: 'expression', + value: '{{props.nonexistent}}', + }, + alsoMissing: { + bindType: 'expression', + value: '{{state.notThere}}', + }, + }, + children: ['Content with undefined bindings'], + }; + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({ message: 'exists' }), + baseContext, + () => null + ); + + const rendered = render(<>{element}); + + // Should not throw, attributes may be undefined or omitted + expect(rendered.getByTestId('card').textContent).to.contain( + 'Content with undefined bindings' + ); + }); + + it('renders complex nested structure with mixed binding types', () => { + const innerChild: Element = { + id: 'inner-child', + type: 'Child', + children: ['Inner {{props.title}}'], + }; + + const middleElement: Element = { + id: 'middle', + type: 'Card', + staticProps: { role: 'group' }, + bindings: { + dataLevel: { + bindType: 'expression', + value: '{{state.message}}', + }, + }, + children: ['Middle: ', innerChild], + }; + + const node: Element = { + id: 'outer', + type: 'Card', + staticProps: { className: 'outer-wrapper' }, + bindings: { + title: { + bindType: 'expression', + value: '{{props.title}}', + }, + }, + children: ['Outer text ', middleElement], + }; + + const renderNodeSpy = sinon.spy((child: any) => { + if (child?.id === 'middle') { + return ( +
+ {/* Rendered recursively */} + {child.type} +
+ ); + } + if (child?.id === 'inner-child') { + return ( + + Inner content + + ); + } + return null; + }); + + const element = renderElementNode( + node, + 0, + atoms, + [], + () => undefined, + () => ({ message: 'nested-state' }), + baseContext, + renderNodeSpy + ); + + const rendered = render(<>{element}); + const outer = rendered.getByTestId('card'); + + expect(outer.getAttribute('class')).to.equal('outer-wrapper'); + expect(outer.getAttribute('title')).to.equal('Hello world'); + expect(outer.textContent).to.contain('Outer text'); + }); + + it('event callback has access to current props and state at call time', () => { + const setStateSpy = sinon.spy(); + const onEventSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + clicked: '{{event}}', + timestamp: '{{state.message}}', + }, + }, + { + call: 'onEvent', + args: ['{{event}}', '{{props.title}}', '{{state.message}}'], + }, + ], + } as any, + [{ name: 'onEvent', description: 'onEvent callback', callbackFn: onEventSpy }], + () => ({ message: 'state-value' }), + setStateSpy as any, + { ...baseContext, props: { title: 'Button Title' } } + ); + + callback(true); + + expect(setStateSpy.called).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ + clicked: true, + timestamp: 'state-value', + }); + + expect(onEventSpy.called).to.equal(true); + expect(onEventSpy.firstCall.args).to.deep.equal([true, 'Button Title', 'state-value']); + }); + + it('atom component receives exact props shape with static and bound props', () => { + const atomSpy = sinon.spy(({ children, ...props }: any) => ( +
+ {children} +
+ )); + + const customAtoms = { + SpyCard: atomSpy, + }; + + const node: Element = { + id: 'spy-id', + type: 'SpyCard', + staticProps: { + className: 'static-class', + role: 'main', + }, + bindings: { + title: { + bindType: 'expression', + value: '{{props.title}}', + }, + 'aria-label': { + bindType: 'expression', + value: 'Label', + }, + }, + children: [], + }; + + const element = renderElementNode( + node, + 0, + customAtoms, + [], + () => undefined, + () => ({}), + { ...baseContext, props: { title: 'Test Title' } }, + () => null + ); + + render(<>{element}); + + expect(atomSpy.called).to.equal(true); + const propsArg = atomSpy.firstCall.args[0]; + + expect(propsArg.className).to.equal('static-class'); + expect(propsArg.role).to.equal('main'); + expect(propsArg.title).to.equal('Test Title'); + expect(propsArg['aria-label']).to.equal('Label'); + expect(propsArg['data-atom-id']).to.equal('spy-id'); + expect(propsArg['data-atom-label']).to.equal('SpyCard'); + }); + + it('multiple setState actions from single event update all state properties', () => { + const setStateSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['x', 'y'], + actions: [ + { + setState: { + first: '{{event.x}}', + second: '{{event.y}}', + combined: '{{event.x}}-{{event.y}}', + fromState: '{{state.message}}', + }, + }, + ], + } as any, + [], + () => ({ message: 'original' }), + setStateSpy as any, + baseContext + ); + + callback('first-val', 'second-val'); + + expect(setStateSpy.called).to.equal(true); + const stateArg = setStateSpy.firstCall.args[0]; + expect(stateArg.fromState).to.equal('original'); + expect(stateArg.first).to.equal('first-val'); + expect(stateArg.second).to.equal('second-val'); + }); + + it('renders with key prop passed through correctly', () => { + const atomWithKeySpy = sinon.spy(({ children, ...props }: any) => ( +
+ {children} +
+ )); + + const customAtoms = { + KeyCard: atomWithKeySpy, + }; + + const node: Element = { + id: 'key-test-id', + type: 'KeyCard', + children: ['Content'], + }; + + const element = renderElementNode( + node, + 'unique-key-123', + customAtoms, + [], + () => undefined, + () => ({}), + baseContext, + () => null + ); + + render(<>{element}); + + // Key is used by React internally but not passed as prop + // The component should still render correctly + expect(atomWithKeySpy.called).to.equal(true); + }); + }); + + describe('buildEventCallback()', () => { + it('resolves setState and call actions with multi-argument event payload', () => { + const setStateSpy = sinon.spy(); + const trackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value', 'label'], + actions: [ + { + setState: { + selected: '{{event.value}}', + selectedLabel: '{{event.label}}', + previous: '{{state.current}}', + }, + }, + { + call: 'trackSelection', + args: ['{{event.value}}', '{{props.kind}}', '{{state.current}}'], + }, + ], + } as any, + [ + { + name: 'trackSelection', + description: 'trackSelection callback', + callbackFn: trackSpy, + }, + ], + () => ({ current: 'hats' }), + setStateSpy as any, + { + props: { kind: 'category' }, + state: { current: 'hats' }, + item: undefined, + scope: undefined, + } + ); + + callback('bags', 'Bags'); + + expect(setStateSpy.calledOnce).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ + selected: 'bags', + selectedLabel: 'Bags', + previous: 'hats', + }); + + expect(trackSpy.calledOnce).to.equal(true); + expect(trackSpy.firstCall.args).to.deep.equal(['bags', 'category', 'hats']); + }); + + it('does not set state when there are no setState actions', () => { + const setStateSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'missingCallback', + args: ['{{event}}'], + }, + ], + } as any, + [], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('anything'); + expect(setStateSpy.called).to.equal(false); + }); + + it('executes only call actions without setState actions', () => { + const setStateSpy = sinon.spy(); + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'onlyCall', + args: ['{{event}}', '{{props.label}}'], + }, + ], + } as any, + [{ name: 'onlyCall', description: 'onlyCall callback', callbackFn: callbackSpy }], + () => ({}), + setStateSpy as any, + { + props: { label: 'Test Label' }, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('test-value'); + + expect(setStateSpy.called).to.equal(false); + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal(['test-value', 'Test Label']); + }); + + it('handles missing callback function gracefully (does not throw)', () => { + const setStateSpy = sinon.spy(); + const warnSpy = sinon.stub(console, 'warn'); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'nonexistentCallback', + args: ['{{event}}'], + }, + ], + } as any, + [], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + // Should not throw even though callback doesn't exist + expect(() => callback('value')).to.not.throw(); + expect(warnSpy.calledWithMatch(/nonexistentCallback/)).to.equal(true); + warnSpy.restore(); + }); + + it('handles event with no arguments', () => { + const setStateSpy = sinon.spy(); + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: [], + actions: [ + { + setState: { + triggered: true, + }, + }, + { + call: 'onTriggered', + args: [], + }, + ], + } as any, + [{ name: 'onTriggered', description: 'onTriggered callback', callbackFn: callbackSpy }], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback(); + + expect(setStateSpy.calledOnce).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ triggered: true }); + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal([]); + }); + + it('resolves template args with props, state, item, and scope', () => { + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'complexCall', + args: ['{{event}}', '{{props.name}}', '{{state.count}}', '{{item.id}}'], + }, + ], + } as any, + [{ name: 'complexCall', description: 'complexCall callback', callbackFn: callbackSpy }], + () => ({ count: 42 }), + () => undefined, + { + props: { name: 'John' }, + state: { count: 42 }, + item: { id: 'item-123' }, + scope: { context: 'scope-value' }, + } + ); + + callback('event-data'); + + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal(['event-data', 'John', 42, 'item-123']); + }); + + it('executes setState before call actions in order', () => { + const setStateSpy = sinon.spy(); + + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + updated: true, + }, + }, + { + call: 'onUpdated', + args: [], + }, + ], + } as any, + [{ name: 'onUpdated', description: 'onUpdated callback', callbackFn: callbackSpy }], + () => ({ updated: false }), + setStateSpy as any, + { + props: {}, + state: { updated: false }, + item: undefined, + scope: undefined, + } + ); + + callback(); + + // Verify both were called + expect(setStateSpy.calledOnce).to.equal(true); + expect(callbackSpy.calledOnce).to.equal(true); + }); + + it('handles multiple call actions in sequence', () => { + const setStateSpy = sinon.spy(); + const call1Spy = sinon.spy(); + const call2Spy = sinon.spy(); + const call3Spy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'first', + args: ['{{event}}-1'], + }, + { + call: 'second', + args: ['{{event}}-2'], + }, + { + call: 'third', + args: ['{{event}}-3'], + }, + ], + } as any, + [ + { name: 'first', description: 'first callback', callbackFn: call1Spy }, + { name: 'second', description: 'second callback', callbackFn: call2Spy }, + { name: 'third', description: 'third callback', callbackFn: call3Spy }, + ], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('test'); + + expect(call1Spy.calledOnce).to.equal(true); + expect(call1Spy.firstCall.args).to.deep.equal(['test-1']); + expect(call2Spy.calledOnce).to.equal(true); + expect(call2Spy.firstCall.args).to.deep.equal(['test-2']); + expect(call3Spy.calledOnce).to.equal(true); + expect(call3Spy.firstCall.args).to.deep.equal(['test-3']); + }); + + it('handles call action without any arguments', () => { + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + call: 'noArgs', + args: [], + }, + ], + } as any, + [{ name: 'noArgs', description: 'noArgs callback', callbackFn: callbackSpy }], + () => ({}), + () => undefined, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('ignored'); + + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal([]); + }); + + it('handles empty actions array', () => { + const setStateSpy = sinon.spy(); + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [], + } as any, + [{ name: 'anyCallback', description: 'anyCallback', callbackFn: callbackSpy }], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('value'); + + expect(setStateSpy.called).to.equal(false); + expect(callbackSpy.called).to.equal(false); + }); + + it('handles single argument event binding', () => { + const setStateSpy = sinon.spy(); + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['selectedId'], + actions: [ + { + setState: { + id: '{{event}}', + }, + }, + { + call: 'onSelect', + args: ['{{event}}'], + }, + ], + } as any, + [{ name: 'onSelect', description: 'onSelect callback', callbackFn: callbackSpy }], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback('id-456'); + + expect(setStateSpy.calledOnce).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ id: 'id-456' }); + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal(['id-456']); + }); + + it('call action can access state values changed by setState', () => { + const setStateSpy = sinon.spy(); + const callbackSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['newValue'], + actions: [ + { + setState: { + value: '{{event}}', + }, + }, + { + call: 'onValueSet', + args: ['{{state.value}}'], + }, + ], + } as any, + [{ name: 'onValueSet', description: 'onValueSet callback', callbackFn: callbackSpy }], + () => ({ value: 'old' }), + setStateSpy as any, + { + props: {}, + state: { value: 'old' }, + item: undefined, + scope: undefined, + } + ); + + callback('new'); + + // When call action accesses state, it should use the original state (before setState) + // This validates that templates are resolved with the current context + expect(callbackSpy.calledOnce).to.equal(true); + expect(callbackSpy.firstCall.args).to.deep.equal(['old']); + }); + + it('handles undefined and null event values in setState', () => { + const setStateSpy = sinon.spy(); + + const callback = buildEventCallback( + { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + result: '{{event}}', + }, + }, + ], + } as any, + [], + () => ({}), + setStateSpy as any, + { + props: {}, + state: {}, + item: undefined, + scope: undefined, + } + ); + + callback(null); + + expect(setStateSpy.calledOnce).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ result: null }); + + setStateSpy.resetHistory(); + + callback(undefined); + + expect(setStateSpy.calledOnce).to.equal(true); + expect(setStateSpy.firstCall.args[0]).to.deep.equal({ result: undefined }); + }); + }); + + describe('createView()', () => { + const atoms: Record> = { + Stack: ({ children }) =>
{children}
, + Text: ({ children }) =>

{children}

, + Emitter: ({ onValueChange }) => ( + + ), + }; + + it('creates a component with displayName and updates state through bound events', () => { + const onChangedSpy = sinon.spy(); + const doc: Document = { + name: 'SimpleGeneratedView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['{{state.message}}'], + }, + { + id: 'emitter', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + message: '{{event}}', + }, + }, + { + call: 'onChanged', + args: ['{{event}}'], + }, + ], + }, + }, + children: [], + }, + ], + }, + state: { + message: 'initial', + }, + }; + + const Generated = createView(doc, atoms, [ + { name: 'onChanged', description: 'onChanged callback', callbackFn: onChangedSpy }, + ]); + + expect(Generated.displayName).to.equal('SimpleGeneratedView'); + + const rendered = render(); + + expect(rendered.getByTestId('text').textContent).to.equal('initial'); + + fireEvent.click(rendered.getByTestId('emit')); + + expect(rendered.getByTestId('text').textContent).to.equal('updated'); + expect(onChangedSpy.calledOnce).to.equal(true); + expect(onChangedSpy.firstCall.args[0]).to.equal('updated'); + }); + + it('supports for/show expressions with runtime props', () => { + const doc: Document = { + name: 'LoopAndShowDoc', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'line', + type: 'Stack', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [ + { + id: 'line-text', + type: 'Text', + show: { + left: '{{item.visible}}', + op: 'eq', + right: true as any, + }, + children: ['{{item.label}}'], + }, + ], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + const rendered = render( + + ); + + expect(rendered.container.textContent).to.contain('First'); + expect(rendered.container.textContent).to.contain('Third'); + expect(rendered.container.textContent).to.not.contain('Second'); + }); + + it('renders component with no initial state', () => { + const doc: Document = { + name: 'NoStateView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['Static content'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + expect(rendered.getByTestId('text').textContent).to.equal('Static content'); + }); + + it('handles multiple events updating different state properties independently', () => { + const onFirstSpy = sinon.spy(); + const onSecondSpy = sinon.spy(); + + const doc: Document = { + name: 'MultiEventView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text1', + type: 'Text', + children: ['First: {{state.first}}'], + }, + { + id: 'text2', + type: 'Text', + children: ['Second: {{state.second}}'], + }, + { + id: 'emitter1', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + first: '{{event}}', + }, + }, + { + call: 'onFirst', + args: ['{{event}}'], + }, + ], + }, + }, + children: [], + }, + { + id: 'emitter2', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + second: '{{event}}', + }, + }, + { + call: 'onSecond', + args: ['{{event}}'], + }, + ], + }, + }, + children: [], + }, + ], + }, + state: { + first: 'A', + second: 'B', + }, + }; + + const Generated = createView(doc, atoms, [ + { name: 'onFirst', description: 'onFirst callback', callbackFn: onFirstSpy }, + { name: 'onSecond', description: 'onSecond callback', callbackFn: onSecondSpy }, + ]); + + const rendered = render(); + + expect(rendered.container.textContent).to.contain('First: A'); + expect(rendered.container.textContent).to.contain('Second: B'); + + fireEvent.click(rendered.getAllByTestId('emit')[0]); + + expect(rendered.container.textContent).to.contain('First: updated'); + expect(rendered.container.textContent).to.contain('Second: B'); + expect(onFirstSpy.calledOnce).to.equal(true); + + fireEvent.click(rendered.getAllByTestId('emit')[1]); + + expect(rendered.container.textContent).to.contain('First: updated'); + expect(rendered.container.textContent).to.contain('Second: updated'); + expect(onSecondSpy.calledOnce).to.equal(true); + }); + + it('renders nested for loops (for within for)', () => { + const doc: Document = { + name: 'NestedLoopView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'group', + type: 'Stack', + for: { + each: '{{props.groups}}', + as: 'group', + key: '{{group.id}}', + }, + children: [ + { + id: 'item', + type: 'Text', + for: { + each: '{{group.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: ['{{item.name}}'], + }, + ], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + const rendered = render( + + ); + + expect(rendered.container.textContent).to.contain('ItemA'); + expect(rendered.container.textContent).to.contain('ItemB'); + expect(rendered.container.textContent).to.contain('ItemC'); + }); + + it('supports show condition with different operators (eq, ne)', () => { + const doc: Document = { + name: 'ShowOperatorsView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'eq-text', + type: 'Text', + show: { + left: '{{props.status}}', + op: 'eq', + right: 'active', + }, + children: ['Status is active'], + }, + { + id: 'ne-text', + type: 'Text', + show: { + left: '{{props.status}}', + op: 'ne', + right: 'inactive', + }, + children: ['Status is not inactive'], + }, + { + id: 'level-text', + type: 'Text', + show: { + left: '{{props.level}}', + op: 'eq', + right: 'admin', + }, + children: ['User is admin'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + const rendered = render(); + + expect(rendered.container.textContent).to.contain('Status is active'); + expect(rendered.container.textContent).to.contain('Status is not inactive'); + expect(rendered.container.textContent).to.contain('User is admin'); + }); + + it('combines for and per-item show via a nested element for conditional list rendering', () => { + const doc: Document = { + name: 'ForShowCombinedView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'row', + type: 'Stack', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [ + { + id: 'item', + type: 'Text', + show: { + left: '{{item.status}}', + op: 'eq', + right: 'active', + }, + children: ['{{item.label}}'], + }, + ], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + const rendered = render( + + ); + + expect(rendered.container.textContent).to.contain('Active One'); + expect(rendered.container.textContent).to.contain('Active Two'); + expect(rendered.container.textContent).to.not.contain('Inactive'); + }); + + it('evaluates show on the same node as for once in the parent context to skip the entire loop', () => { + const doc: Document = { + name: 'LoopLevelShowDoc', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'line', + type: 'Text', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + show: { + left: '{{props.renderItems}}', + op: 'eq', + right: 'yes', + }, + children: ['{{item.label}}'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + const hidden = render( + + ); + expect(hidden.container.textContent).to.not.contain('A'); + + const visible = render( + + ); + expect(visible.container.textContent).to.contain('A'); + expect(visible.container.textContent).to.contain('B'); + }); + + it('updates state through multiple sequential events', () => { + const doc: Document = { + name: 'SequentialEventsView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'count-text', + type: 'Text', + children: ['Count: {{state.count}}'], + }, + { + id: 'emitter', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + count: '{{event}}', + }, + }, + ], + }, + }, + children: [], + }, + ], + }, + state: { + count: 0, + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + expect(rendered.getByTestId('text').textContent).to.equal('Count: 0'); + + fireEvent.click(rendered.getByTestId('emit')); + expect(rendered.getByTestId('text').textContent).to.equal('Count: updated'); + + fireEvent.click(rendered.getByTestId('emit')); + expect(rendered.getByTestId('text').textContent).to.equal('Count: updated'); + }); + + it('handles event binding with no actions', () => { + const doc: Document = { + name: 'NoActionsView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['Click me'], + }, + { + id: 'button-with-no-actions', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [], + }, + }, + children: [], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + expect(() => { + const rendered = render(); + fireEvent.click(rendered.getByTestId('emit')); + }).to.not.throw(); + }); + + it('accepts runtime props not referenced in templates', () => { + const doc: Document = { + name: 'UnusedPropsView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['Content'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + + expect(() => { + const rendered = render( + + ); + expect(rendered.getByTestId('text').textContent).to.equal('Content'); + }).to.not.throw(); + }); + + it('initializes state with various value types (numbers, booleans, objects, arrays)', () => { + const doc: Document = { + name: 'VariousTypesView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'number-text', + type: 'Text', + children: ['Number: {{state.count}}'], + }, + { + id: 'bool-text', + type: 'Text', + children: ['Active: {{state.active}}'], + }, + { + id: 'object-text', + type: 'Text', + children: ['Name: {{state.user.name}}'], + }, + { + id: 'array-text', + type: 'Text', + children: ['Items: {{state.items.length}}'], + }, + ], + }, + state: { + count: 42, + active: true, + user: { name: 'John', age: 30 }, + items: [1, 2, 3], + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + const textElements = rendered.getAllByTestId('text'); + expect(textElements[0].textContent).to.contain('Number: 42'); + expect(textElements[1].textContent).to.contain('Active: true'); + expect(textElements[2].textContent).to.contain('Name: John'); + expect(textElements[3].textContent).to.contain('Items: 3'); + }); + + it('supports dynamic prop expression binding to state', () => { + const onUpdateSpy = sinon.spy(); + + const doc: Document = { + name: 'DynamicPropsView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'emitter', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + label: '{{event}}', + }, + }, + { + call: 'onUpdate', + args: ['{{state.label}}'], + }, + ], + }, + }, + children: [], + }, + ], + }, + state: { + label: 'initial', + }, + }; + + const Generated = createView(doc, atoms, [ + { name: 'onUpdate', description: 'onUpdate callback', callbackFn: onUpdateSpy }, + ]); + const rendered = render(); + + fireEvent.click(rendered.getByTestId('emit')); + + expect(onUpdateSpy.calledOnce).to.equal(true); + }); + + it('show condition based on state value updates reactively', () => { + const doc: Document = { + name: 'ShowStateReactiveView', + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'message', + type: 'Text', + show: { + left: '{{state.messageVisible}}', + op: 'eq', + right: 'shown', + }, + children: ['Message is visible'], + }, + { + id: 'emitter', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + messageVisible: 'shown', + }, + }, + ], + }, + }, + children: [], + }, + ], + }, + state: { + messageVisible: 'hidden', + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + expect(rendered.container.textContent).to.not.contain('Message is visible'); + + fireEvent.click(rendered.getByTestId('emit')); + + expect(rendered.container.textContent).to.contain('Message is visible'); + }); + + it('renders complex nested element structure with mixed binding types', () => { + const onNestedEventSpy = sinon.spy(); + + const doc: Document = { + name: 'ComplexNestedView', + root: { + id: 'root', + type: 'Stack', + staticProps: { role: 'main' }, + children: [ + { + id: 'outer-text', + type: 'Text', + bindings: { + title: { + bindType: 'expression', + value: '{{props.title}}', + }, + }, + children: ['Outer: {{state.outerValue}}'], + }, + { + id: 'group', + type: 'Stack', + for: { + each: '{{props.items}}', + as: 'item', + key: '{{item.id}}', + }, + children: [ + { + id: 'group-row', + type: 'Stack', + show: { + left: '{{item.visibility}}', + op: 'eq', + right: 'visible', + }, + children: [ + { + id: 'item-text', + type: 'Text', + children: ['{{item.label}} - {{state.outerValue}} - '], + }, + { + id: 'nested-emitter', + type: 'Emitter', + bindings: { + onValueChange: { + bindType: 'event', + arguments: ['value'], + actions: [ + { + setState: { + outerValue: '{{event}}', + }, + }, + { + call: 'onNestedEvent', + args: ['{{item.id}}', '{{event}}'], + }, + ], + }, + }, + children: [], + }, + ], + }, + ], + }, + ], + }, + state: { + outerValue: 'initial', + }, + }; + + const Generated = createView(doc, atoms, [ + { + name: 'onNestedEvent', + description: 'onNestedEvent callback', + callbackFn: onNestedEventSpy, + }, + ]); + + const rendered = render( + + ); + + expect(rendered.container.textContent).to.contain('Item 1'); + expect(rendered.container.textContent).to.contain('Item 3'); + expect(rendered.container.textContent).to.not.contain('Item 2'); + + const buttons = rendered.getAllByTestId('emit'); + fireEvent.click(buttons[0]); + + expect(onNestedEventSpy.calledOnce).to.equal(true); + expect(onNestedEventSpy.firstCall.args).to.deep.equal(['i1', 'updated']); + }); + }); +}); diff --git a/packages/react/src/atoms/component-layout/createView.tsx b/packages/react/src/atoms/component-layout/createView.tsx new file mode 100644 index 0000000000..d5223607c1 --- /dev/null +++ b/packages/react/src/atoms/component-layout/createView.tsx @@ -0,0 +1,312 @@ +'use client'; +/** + * createView: renders a Component Layout document as a React tree. + * Uses document types and resolver from @sitecore-content-sdk/content/editing. + */ + +import React, { type FC, type Key, useReducer, useRef } from 'react'; +import { + type Document, + type Node, + type Element, + type EventBinding, + type ResolveContext, + isElement, + hasFor, + hasShow, + isExpressionBinding, + isEventBinding, + isSetStateAction, + isCallAction, + resolveTemplateString, + evaluateShowNode, + isPrimitive, + resolveIfTemplate, +} from '@sitecore-content-sdk/content/atoms'; +import { CallbackMetadata } from '..'; + +/** Props passed to an Atom Component */ +type AtomProps = React.PropsWithChildren; + +/** The Atom Component */ +type AtomComponent = React.ComponentType; + +/** Internal state shape used by generated view components. */ +type ViewState = Record; + +/** Patch shape for reducer updates. */ +type StatePatch = Partial; + +/** Scope values available to template resolution (e.g. for-loop alias). */ +type ScopeMap = Record; + +/** Props passed to rendered atoms after bindings/template resolution. */ +type ResolvedProps = Record & { + 'data-atom-id'?: string; + 'data-atom-label'?: string; +}; + +/** + * Renders nodes in a for-loop, iterating over an array with optional key resolution. + * @param {Element} node - The element node with a `for` binding + * @param {ResolveContext} forCtx - Resolve context for the loop + * @param {(tmpl: Element, key: Key, item: unknown, itemScope: ScopeMap) => React.ReactNode} renderNode - Render function + * @returns {React.ReactNode} Array of rendered nodes or null if array resolution fails + * @internal + */ +export const renderFor = ( + node: Element, + forCtx: ResolveContext, + renderNode: (tmpl: Node, key: Key, item: unknown, itemScope: ScopeMap) => React.ReactNode +): React.ReactNode => { + const forArr = resolveTemplateString(node.for?.each ?? '', forCtx); + if (!Array.isArray(forArr)) { + return null; + } + + return forArr.map((item: unknown, index: number) => { + const tmpl: Element = { ...node, for: undefined }; + const itemScope: ScopeMap = { [node.for!.as]: item }; + const itemKey = node.for?.key + ? resolveTemplateString(node.for.key, { + ...forCtx, + item: item, + scope: itemScope, + }) + : index; + + return renderNode(tmpl, itemKey as Key, item, itemScope); + }); +}; + +/** + * Renders a primitive or template-string node. + * Returns null for unrecognised node types. + * @param {Node} node - A non-element node (string, number, boolean, null) + * @param {ResolveContext} ctx - Resolve context for template string resolution + * @returns {React.ReactNode} Resolved node or null + */ +export const renderPrimitiveNode = (node: Node, ctx: ResolveContext): React.ReactNode => { + // resolve template strings + if (typeof node === 'string') { + return (resolveIfTemplate(node, ctx) ?? null) as React.ReactNode; + } + + // pass through primitives React can render natively + if (isPrimitive(node)) { + return node; + } + + return null; +}; + +/** + * Renders an element node (atom) with resolved props, bindings, and children. + * @param {Element} node - The element node to render + * @param {Key | undefined} key - React key for the element + * @param {Record>} atoms - Atom component registry + * @param {CallbackMetadata[]} callbacks - Callback metadata array + * @param {React.Dispatch} setState - State dispatcher + * @param {() => ViewState} getState - Function to get the current state at callback time + * @param {ResolveContext} ctx - Resolve context + * @param {(node: Node, key: Key | undefined, itemCtx: unknown, scope: ScopeMap | undefined) => React.ReactNode} renderNode - Recursive render function + * @returns {React.ReactNode} Rendered atom element + * @internal + */ +export const renderElementNode = ( + node: Element, + key: Key | undefined, + atoms: Record>, + callbacks: CallbackMetadata[], + setState: React.Dispatch, + getState: () => ViewState, + ctx: ResolveContext, + renderNode: ( + node: Node, + key: Key | undefined, + itemCtx: unknown, + scope: ScopeMap | undefined + ) => React.ReactNode +): React.ReactNode => { + const { id, type, staticProps = {}, bindings = {}, children = [] } = node; + + const Atom = atoms[type] as AtomComponent | undefined; + if (!Atom) { + throw new Error(`Component Layout: unknown atom "${type}" with id "${id}".`); + } + + const resolvedProps: ResolvedProps = { ...staticProps }; + + for (const [propName, binding] of Object.entries(bindings)) { + if (isExpressionBinding(binding)) { + resolvedProps[propName] = resolveTemplateString(binding.value, ctx); + } else if (isEventBinding(binding)) { + resolvedProps[propName] = buildEventCallback(binding, callbacks, getState, setState, ctx); + } + } + + resolvedProps['data-atom-id'] = id; + resolvedProps['data-atom-label'] = type; + + const childNodes: React.ReactNode[] = children.map((c, i) => { + if (typeof c === 'string') { + return (resolveIfTemplate(c, ctx) ?? null) as React.ReactNode; + } + return renderNode(c, i, ctx.item, ctx.scope); + }); + + return childNodes.length > 0 ? ( + + {childNodes} + + ) : ( + + ); +}; + +/** + * Builds a callable function from an event binding. + * Resolves setState values and call args with template strings; invokes callbacks. + * @param {EventBinding} binding the event binding to build the callback from + * @param {CallbackMetadata[]} callbacks the array of callback metadata to use for call actions + * @param {() => ViewState} getState function to get the latest state at the time of event handling + * @param {React.Dispatch} setState the React state dispatcher to apply setState actions + * @param {ResolveContext} resolveContext - Resolve context + * @returns {(...args: unknown[]) => void} a function that can be used as an event handler + * @internal + */ +export const buildEventCallback = ( + binding: EventBinding, + callbacks: CallbackMetadata[], + getState: () => ViewState, + setState: React.Dispatch, + resolveContext: ResolveContext +): ((...args: unknown[]) => void) => { + return (...args: unknown[]) => { + let eventValue: unknown; + if (binding.arguments.length <= 1) { + eventValue = args[0]; + } else { + const obj: Record = {}; + binding.arguments.forEach((name, i) => { + obj[name] = args[i]; + }); + eventValue = obj; + } + + const patch: StatePatch = {}; + const { props, item, scope } = resolveContext; + const ctx: ResolveContext = { + props, + item, + scope, + state: getState(), + event: eventValue, + }; + + for (const action of binding.actions) { + if (isSetStateAction(action)) { + for (const [key, value] of Object.entries(action.setState)) { + patch[key] = resolveIfTemplate(value, ctx); + } + continue; + } + + if (isCallAction(action)) { + const resolvedArgs = (action.args ?? []).map((a) => resolveIfTemplate(a, ctx)); + const callable = callbacks.find((c) => c.name === action.call)?.callbackFn; + if (typeof callable === 'function') { + callable(...resolvedArgs); + } else { + console.warn( + `[createView] Callback "${action.call}" is not registered or is not a function.` + ); + } + continue; + } + } + + if (Object.keys(patch).length > 0) { + setState(patch); + } + }; +}; + +/** + * Creates a React functional component that renders the given Component Layout document. + * @param {Document} doc - Component Layout document + * @param {Record>} atoms - Map of atom type name to its React implementation + * @param {CallbackMetadata[]} [callbacks] - Optional array of callback metadata for event actions + * @returns {FC} FC that accepts runtime props (spread as props in expressions) + * @internal + */ +export function createView = Record>( + doc: Document, + atoms: Record>, + callbacks: CallbackMetadata[] = [] +): FC { + const { root, state: initialState = {} } = doc; + + const Generated: FC = (runtimeProps) => { + const [state, setState] = useReducer( + (state: ViewState, patch: StatePatch) => ({ + ...state, + ...patch, + }), + initialState as ViewState + ); + + const stateRef = useRef(state); + stateRef.current = state; + + const getState = () => stateRef.current; + + const renderNode = ( + node: Node, + key: Key | undefined, + itemCtx: unknown, + scope: ScopeMap | undefined + ): React.ReactNode => { + const ctx: ResolveContext = { + props: runtimeProps as Record, + state: stateRef.current, + item: itemCtx, + scope, + }; + + if (isElement(node)) { + // When both `for` and `show` are set, evaluate `show` once in the parent context + // before iterating so the loop can be skipped entirely (then strip `show` per iteration). + if (hasFor(node)) { + const loopNode = + hasShow(node) && !evaluateShowNode(node.show, ctx) + ? null + : hasShow(node) + ? ({ ...node, show: undefined } as Element) + : node; + if (loopNode === null) { + return null; + } + return renderFor(loopNode, ctx, renderNode); + } + + if (hasShow(node)) { + if (!evaluateShowNode(node.show, ctx)) { + return null; + } + } + + return renderElementNode(node, key, atoms, callbacks, setState, getState, ctx, renderNode); + } + + return renderPrimitiveNode(node, ctx); + }; + + const rootNode = renderNode(root, 0, undefined, undefined); + + return <>{rootNode}; + }; + + Generated.displayName = doc.name; + return Generated; +} diff --git a/packages/react/src/atoms/component-layout/index.ts b/packages/react/src/atoms/component-layout/index.ts new file mode 100644 index 0000000000..038922623d --- /dev/null +++ b/packages/react/src/atoms/component-layout/index.ts @@ -0,0 +1,5 @@ +/** + * Component Layout: createView for rendering no-code layout documents. + */ + +export { createView } from './createView'; diff --git a/packages/react/src/atoms/createAtom.test.ts b/packages/react/src/atoms/createAtom.test.ts index f59501b8df..4032273bf2 100644 --- a/packages/react/src/atoms/createAtom.test.ts +++ b/packages/react/src/atoms/createAtom.test.ts @@ -85,7 +85,10 @@ describe('createAtom', () => { describe('component scenarios (props only, no callbacks)', () => { it('accepts component with only required props', () => { - const OnlyProps = (props: { title: string; count: number }) => null; + const OnlyProps = (props: { title: string; count: number }) => { + void props; + return null; + }; const meta = createAtom(OnlyProps, { name: 'OnlyProps', description: 'Props only', @@ -100,7 +103,10 @@ describe('createAtom', () => { }); it('accepts component with optional props only', () => { - const OptionalOnly = (props: { tag?: string }) => null; + const OptionalOnly = (props: { tag?: string }) => { + void props; + return null; + }; const meta = createAtom(OptionalOnly, { name: 'OptionalOnly', description: 'Optional', @@ -113,15 +119,14 @@ describe('createAtom', () => { describe('customEvents (typed to callback parameters)', () => { it('accepts component with explicit props (no ComponentType cast)', () => { - const Test = ({ - customEvent, - prop1, - prop2, - }: { + const Test = (props: { customEvent: (x: string, y: number) => void; prop1: string; prop2: number; - }) => null; + }) => { + void props; + return null; + }; const meta = createAtom(Test, { name: 'Test', description: 'Test', diff --git a/packages/react/src/atoms/createAtom.ts b/packages/react/src/atoms/createAtom.ts index b9dd8266ed..faf983aae7 100644 --- a/packages/react/src/atoms/createAtom.ts +++ b/packages/react/src/atoms/createAtom.ts @@ -21,7 +21,7 @@ export type AtomSchemaInput = { /** Human-readable summary for the component palette */ description: string; /** 'atom' (default) for top-level, 'atom-child' for scoped children */ - type?: (typeof AtomType)[keyof typeof AtomType]; + type?: AtomType; /** Optional version for schema evolution */ version?: number; /** Zod schemas for editable props (keys must be component props excluding children/ref) */ @@ -45,13 +45,13 @@ export type AtomSchemaInput = { /** * Create an atom or atom-child descriptor. The component is the first argument, the schema the * second; schema.type selects 'atom' (default) or 'atom-child'. - * @param component - The React component that renders this atom - * @param schema - Name, description, type, props, events, and children rules + * @param {C} component - The React component that renders this atom + * @param {AtomSchemaInput} schema - Name, description, type, props, events, and children rules * @returns AtomMetadata with type taken from schema.type (default 'atom') * @public */ export function createAtom(component: C, schema: AtomSchemaInput): AtomMetadata { - const atomType = schema.type ?? AtomType.ATOM; + const atomType: AtomType = schema.type ?? 'atom'; const propsShape = schema.props as Record; const propsSchema = z.object(propsShape); diff --git a/packages/react/src/atoms/schema-utils.ts b/packages/react/src/atoms/schema-utils.ts index f70336bf76..6b4f770779 100644 --- a/packages/react/src/atoms/schema-utils.ts +++ b/packages/react/src/atoms/schema-utils.ts @@ -7,8 +7,8 @@ const META_KEY = 'meta'; /** * Attach editor hint (e.g. control type) to a prop schema. Metadata is stored under a key that * survives JSON Schema conversion for Design Studio. - * @param schema - Zod type for the prop - * @param meta - Editor metadata (e.g. control) + * @param {import('zod').ZodType} schema - Zod type for the prop + * @param {PropMeta} meta - Editor metadata (e.g. control) * @returns The same Zod type with meta attached (or schema unchanged if .meta is not callable) * @public */ @@ -23,8 +23,8 @@ export function withPropMeta(schema: T, meta: PropMeta): T /** * Attach display metadata to a custom event argument (e.g. argName for DS). Stored under a key * that survives JSON Schema conversion. - * @param schema - Zod type for the argument - * @param meta - Argument metadata + * @param {import('zod').ZodType} schema - Zod type for the argument + * @param {ArgMeta} meta - Argument metadata * @returns The same Zod type with meta attached (or schema unchanged if .meta is not callable) * @public */ @@ -39,7 +39,7 @@ export function withArgMeta(schema: T, meta: ArgMeta): T { /** * Get field metadata from a Zod type or a plain JSON Schema object. Uses _zod to detect Zod * schemas; otherwise reads the meta key from the object. For internal use by the renderer / DS. - * @param schemaOrJsonSchema - Live Zod type or plain JSON Schema object + * @param {import('zod').ZodType | Record} schemaOrJsonSchema - Live Zod type or plain JSON Schema object * @returns The meta object or undefined * @internal */ diff --git a/packages/react/src/atoms/types.ts b/packages/react/src/atoms/types.ts index 77923aad85..02bdfde6cd 100644 --- a/packages/react/src/atoms/types.ts +++ b/packages/react/src/atoms/types.ts @@ -18,7 +18,7 @@ export type CallbackMetadata = { export type AtomMetadata = { name: string; version?: number; - type: (typeof AtomType)[keyof typeof AtomType]; + type: AtomType; description: string; props: z.ZodObject; component: (props: unknown) => React.ReactNode; diff --git a/packages/react/src/components/AtomRenderer/AtomRenderer.tsx b/packages/react/src/components/AtomRenderer/AtomRenderer.tsx deleted file mode 100644 index ed115e8229..0000000000 --- a/packages/react/src/components/AtomRenderer/AtomRenderer.tsx +++ /dev/null @@ -1,17 +0,0 @@ -'use client'; -import React, { useEffect } from 'react'; -import { AtomMetadata, CallbackMetadata } from '../../atoms'; - -export const AtomRenderer = ({ - atoms, - callbacks, -}: { - atoms?: AtomMetadata[]; - callbacks?: CallbackMetadata[]; -}) => { - useEffect(() => { - console.log(`AtomRenderer, available atoms: ${atoms}, available callbacks: ${callbacks}`); - }, [atoms, callbacks]); - - return
Atoms Renderer
; -}; diff --git a/packages/react/src/components/AtomRenderer/index.ts b/packages/react/src/components/AtomRenderer/index.ts deleted file mode 100644 index 0957e9ae25..0000000000 --- a/packages/react/src/components/AtomRenderer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AtomRenderer } from './AtomRenderer'; diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx new file mode 100644 index 0000000000..0b5b78eef1 --- /dev/null +++ b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx @@ -0,0 +1,637 @@ +/* eslint-disable jsdoc/require-jsdoc */ +/* eslint-disable no-unused-expressions */ +/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ +import React from 'react'; +import sinon from 'sinon'; +import { expect, use as chaiUse } from 'chai'; +import sinonChai from 'sinon-chai'; + +chaiUse(sinonChai); +import { render, waitFor } from '@testing-library/react'; +import { DesignLibraryAtoms, __mockDependencies } from './DesignLibraryAtoms'; +import { SitecoreProvider } from '../SitecoreProvider'; +import { + DesignLibraryStatus, + getDesignLibraryStatusEvent, + getDesignLibraryAtomsRegistryEvent, + AtomInfo, +} from '@sitecore-content-sdk/content/editing'; +import * as atomRegistryUtils from '../../atoms/atom-registry-utils'; +import { serializeCallbacks } from '../../atoms/callback-registry-utils'; +import { Document } from '@sitecore-content-sdk/content/atoms'; +import { AtomMetadata } from '../../atoms/types'; +import { z } from 'zod'; +import type { ImportMapImport } from './models'; + +describe('', () => { + const sandbox = sinon.createSandbox(); + + /** Minimal stubs so `SitecoreProvider` matches its public contract in tests */ + const apiStub = {} as any; + const emptyComponentMap = new Map(); + const loadImportMapStub = async (): Promise => ({}) as ImportMapImport; + + let postToDesignLibrarySpy: sinon.SinonStub; + let sendAtomsErrorEventSpy: sinon.SinonStub; + let addDocumentUpdateHandlerSpy: sinon.SinonStub; + let serializeAtomsStub: sinon.SinonStub; + let getAtomMapStub: sinon.SinonStub; + + const mockAtoms: AtomMetadata[] = [ + { + name: 'Button', + type: 'atom', + description: 'A button component', + component: () => React.createElement('button', null, 'Button'), + props: z.object({ + label: z.string(), + }), + }, + { + name: 'Text', + type: 'atom', + description: 'A text component', + component: () => React.createElement('div', null, 'Text'), + props: z.object({ + content: z.string(), + }), + }, + ]; + + const mockAtomMap: Record> = { + Stack: () => React.createElement('div', { 'data-test': 'stack' }), + Card: () => React.createElement('div', { 'data-test': 'card' }), + CardHeader: () => React.createElement('div', { 'data-test': 'card-header' }), + CardTitle: () => React.createElement('div', { 'data-test': 'card-title' }), + CardDescription: () => React.createElement('div', { 'data-test': 'card-description' }), + CardContent: () => React.createElement('div', { 'data-test': 'card-content' }), + Image: () => React.createElement('img', { 'data-test': 'image' }), + Button: () => React.createElement('button', { 'data-test': 'button' }), + Text: () => React.createElement('div', { 'data-test': 'text' }), + }; + + const mockSerializedAtoms: AtomInfo[] = [ + { + name: 'Button', + type: 'atom', + description: 'A button component', + props: { label: { type: 'string' } }, + allowedChildren: [], + }, + { + name: 'Text', + type: 'atom', + description: 'A text component', + props: { content: { type: 'string' } }, + allowedChildren: [], + }, + ]; + + const mockCallbacks = [ + { + name: 'trackSelection', + description: 'Track selection callback', + callbackFn: () => {}, + }, + ]; + + const getPage = () => ({ + locale: 'en', + layout: { sitecore: { context: {}, route: null } }, + mode: { + name: 'normal', + isDesignLibrary: false, + designLibrary: { + isVariantGeneration: false, + }, + isNormal: true, + isPreview: false, + isEditing: false, + }, + }); + + beforeEach(() => { + postToDesignLibrarySpy = sandbox.stub(); + sendAtomsErrorEventSpy = sandbox.stub(); + addDocumentUpdateHandlerSpy = sandbox.stub().returns(() => {}); + serializeAtomsStub = sandbox.stub(atomRegistryUtils, 'serializeAtoms'); + getAtomMapStub = sandbox.stub(atomRegistryUtils, 'getAtomMap'); + + __mockDependencies({ + postToDesignLibrary: postToDesignLibrarySpy, + sendAtomsErrorEvent: sendAtomsErrorEventSpy, + addDocumentUpdateHandler: addDocumentUpdateHandlerSpy, + }); + + serializeAtomsStub.returns(mockSerializedAtoms); + getAtomMapStub.returns(mockAtomMap); + + // Stub console methods to suppress console output during tests + sandbox.stub(console, 'log'); + sandbox.stub(console, 'warn'); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('should send READY status event on mount', () => { + const page = getPage(); + + render( + + + + ); + + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'low-code-component') + ); + }); + + it('should wrap rendered view in DesignLibraryErrorBoundary', () => { + const page = getPage(); + + const { container } = render( + + + + ); + + expect(container).to.exist; + expect(getAtomMapStub).to.have.been.called; + }); + + it('should serialize atoms and send atoms registry event', () => { + const page = getPage(); + + render( + + + + ); + + expect(serializeAtomsStub).to.have.been.called; + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryAtomsRegistryEvent(mockSerializedAtoms, serializeCallbacks(mockCallbacks)) + ); + }); + + it('should send error event when serialized atoms list is empty', () => { + const page = getPage(); + serializeAtomsStub.returns([]); + + render( + + + + ); + + expect(serializeAtomsStub).to.have.been.calledWith([]); + expect(sendAtomsErrorEventSpy).to.have.been.calledWith('No atoms provided', 'atoms-missing'); + expect(postToDesignLibrarySpy).not.to.have.been.calledWith( + sinon.match((arg) => arg.name === 'atom:registry') + ); + }); + + it('should subscribe to document updates on mount', () => { + const page = getPage(); + + render( + + + + ); + + expect(addDocumentUpdateHandlerSpy).to.have.been.called; + }); + + it('should update document state when document update is received', async () => { + const page = getPage(); + let capturedHandler: ((doc: Document) => void) | null = null; + + addDocumentUpdateHandlerSpy.callsFake((handler) => { + capturedHandler = handler; + return () => {}; + }); + + const { container } = render( + + + + ); + + expect(capturedHandler).to.not.be.null; + + const updatedDocument: Document = { + name: 'UpdatedDocument', + root: { + id: 'updated-root', + type: 'Stack', + children: [], + }, + props: {}, + state: {}, + }; + + capturedHandler!(updatedDocument); + + await waitFor(() => { + expect(container).to.exist; + }); + }); + + it('should increment renderKey when document update is received', async () => { + const page = getPage(); + let capturedHandler: ((doc: Document) => void) | null = null; + + addDocumentUpdateHandlerSpy.callsFake((handler) => { + capturedHandler = handler; + return () => {}; + }); + + render( + + + + ); + + const initialCallCount = postToDesignLibrarySpy.callCount; + + const updatedDocument: Document = { + name: 'UpdatedDocument', + root: { + id: 'updated-root', + type: 'Stack', + children: [], + }, + props: {}, + state: {}, + }; + + capturedHandler!(updatedDocument); + + // renderKey increment should trigger RENDERED event + await waitFor(() => { + expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(initialCallCount); + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + ); + }); + }); + + it('should send RENDERED status event after document update', async () => { + const page = getPage(); + let capturedHandler: ((doc: Document) => void) | null = null; + + addDocumentUpdateHandlerSpy.callsFake((handler) => { + capturedHandler = handler; + return () => {}; + }); + + render( + + + + ); + + const initialCallCount = postToDesignLibrarySpy.callCount; + + const updatedDocument: Document = { + name: 'UpdatedDocument', + root: { + id: 'updated-root', + type: 'Stack', + children: [], + }, + props: {}, + state: {}, + }; + + capturedHandler!(updatedDocument); + + await waitFor(() => { + expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(initialCallCount); + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + ); + }); + }); + + it('should not send RENDERED status before document update', () => { + const page = getPage(); + + render( + + + + ); + + const renderedCalls = postToDesignLibrarySpy + .getCalls() + .filter((call) => call.args[0]?.message?.status === DesignLibraryStatus.RENDERED); + + expect(renderedCalls).to.have.length(0); + }); + + it('should unsubscribe from document updates on unmount', () => { + const page = getPage(); + const unsubscribeSpy = sandbox.spy(); + + addDocumentUpdateHandlerSpy.returns(unsubscribeSpy); + + const { unmount } = render( + + + + ); + + expect(unsubscribeSpy).not.to.have.been.called; + + unmount(); + + expect(unsubscribeSpy).to.have.been.called; + }); + + it('should render view with atomMap from atoms registry', () => { + const page = getPage(); + + const { container } = render( + + + + ); + + expect(container).to.exist; + expect(getAtomMapStub).to.have.been.called; + }); + + it('should send error when atom registry has no atoms', () => { + const page = getPage(); + serializeAtomsStub.returns([]); + + render( + + + + ); + + expect(serializeAtomsStub).to.have.been.calledWith([]); + expect(sendAtomsErrorEventSpy).to.have.been.calledWith('No atoms provided', 'atoms-missing'); + }); + + it('should create atomMap from atoms registry', () => { + const page = getPage(); + + const { container } = render( + + + + ); + + expect(container).to.exist; + expect(getAtomMapStub).to.have.been.called; + }); + + it('should subscribe to document updates via atomRegistry effect', () => { + const page = getPage(); + const unsubscribeSpy = sandbox.spy(); + + addDocumentUpdateHandlerSpy.returns(unsubscribeSpy); + + const { unmount } = render( + + + + ); + + expect(addDocumentUpdateHandlerSpy).to.have.been.called; + + unmount(); + + expect(unsubscribeSpy).to.have.been.called; + }); + + it('should handle multiple document updates', async () => { + const page = getPage(); + let capturedHandler: ((doc: Document) => void) | null = null; + + addDocumentUpdateHandlerSpy.callsFake((handler) => { + capturedHandler = handler; + return () => {}; + }); + + render( + + + + ); + + const doc1: Document = { + name: 'Doc1', + root: { id: '1', type: 'Stack', children: [] }, + props: {}, + state: {}, + }; + + const doc2: Document = { + name: 'Doc2', + root: { id: '2', type: 'Stack', children: [] }, + props: {}, + state: {}, + }; + + capturedHandler!(doc1); + + await waitFor(() => { + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + ); + }); + + const callCountAfterFirst = postToDesignLibrarySpy.callCount; + + capturedHandler!(doc2); + + await waitFor(() => { + expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(callCountAfterFirst); + }); + }); + + it('should use callback registry when creating view', () => { + const page = getPage(); + const customCallbacks = [ + { name: 'onButtonClick', description: 'Button click callback', callbackFn: () => {} }, + { name: 'onSelectChange', description: 'Select change callback', callbackFn: () => {} }, + ]; + + const { container } = render( + + + + ); + + expect(container).to.exist; + }); + + it('should handle empty callback registry', () => { + const page = getPage(); + + const { container } = render( + + + + ); + + expect(container).to.exist; + }); + + it('should send events in correct order', async () => { + const page = getPage(); + let capturedHandler: ((doc: Document) => void) | null = null; + + addDocumentUpdateHandlerSpy.callsFake((handler) => { + capturedHandler = handler; + return () => {}; + }); + + render( + + + + ); + + const calls = postToDesignLibrarySpy.getCalls(); + const readyCall = calls.find( + (call) => call.args[0]?.message?.status === DesignLibraryStatus.READY + ); + const registryCall = calls.find((call) => call.args[0]?.name === 'atom:registry'); + + expect(readyCall).to.exist; + expect(registryCall).to.exist; + + const updatedDocument: Document = { + name: 'Updated', + root: { id: 'root', type: 'Stack', children: [] }, + props: {}, + state: {}, + }; + + capturedHandler!(updatedDocument); + + await waitFor(() => { + const renderedCall = postToDesignLibrarySpy + .getCalls() + .find((call) => call.args[0]?.message?.status === DesignLibraryStatus.RENDERED); + expect(renderedCall).to.exist; + }); + }); +}); diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx index 8d0631a7b3..099a9de4e7 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx @@ -1,47 +1,94 @@ 'use client'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useSitecore } from '../SitecoreProvider'; -import { serializeAtoms } from '../../atoms/atom-registry-utils'; +import { serializeAtoms, getAtomMap } from '../../atoms/atom-registry-utils'; import { serializeCallbacks } from '../../atoms/callback-registry-utils'; -import { +import { createView } from '../../atoms/component-layout'; +import type { Document } from '@sitecore-content-sdk/content/atoms'; +import * as editing from '@sitecore-content-sdk/content/editing'; +import { DesignLibraryErrorBoundary } from '../..'; + +let { postToDesignLibrary, getDesignLibraryAtomsRegistryEvent, - AtomInfo, - CallbackInfo, -} from '@sitecore-content-sdk/content/editing'; -import { AtomRenderer } from '../AtomRenderer/AtomRenderer'; + getDesignLibraryStatusEvent, + DesignLibraryStatus, + sendAtomsErrorEvent, + addDocumentUpdateHandler, +} = editing; + +export const __mockDependencies = (mocks: any) => { + if (mocks.postToDesignLibrary) { + postToDesignLibrary = mocks.postToDesignLibrary; + } + if (mocks.sendAtomsErrorEvent) { + sendAtomsErrorEvent = mocks.sendAtomsErrorEvent; + } + if (mocks.addDocumentUpdateHandler) { + addDocumentUpdateHandler = mocks.addDocumentUpdateHandler; + } +}; /** * Design Library Atoms component. * * Facilitates the communication between the Design Studio and the Rendering Host when in atom rendering mode. * - On mount, it unfolds and serializes the atoms registry and callback registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. - * - Fetches Component model data, and passes it to the `AtomRenderer` which is responsible for rendering the low code component - * based on component model data and the available atoms. + * - Receives Component model data updates via document update handler and renders the low code component + * based on component model data and the available atoms using createView. * @internal */ export const DesignLibraryAtoms = () => { - const { atoms, callbacks } = useSitecore(); + const { atomRegistry } = useSitecore(); + const [currentDocument, setCurrentDocument] = useState(null); + const [renderKey, setRenderKey] = useState(0); + + const atomMap = useMemo(() => getAtomMap(atomRegistry?.atoms || []), [atomRegistry?.atoms]); + + const ViewComponent = useMemo(() => { + if (!currentDocument) return null; + return createView(currentDocument, atomMap, atomRegistry?.callbacks); + }, [currentDocument, atomMap, atomRegistry?.callbacks]); useEffect(() => { - let serializedAtoms: AtomInfo[] = []; - let serializedCallbacks: Record = {}; + postToDesignLibrary( + getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'low-code-component') + ); + }, []); - if (atoms) { - serializedAtoms = serializeAtoms(atoms); - console.log('Serialized Atoms:', serializedAtoms); + useEffect(() => { + const serializedAtoms = serializeAtoms(atomRegistry?.atoms ?? []); + if (serializedAtoms.length === 0) { + sendAtomsErrorEvent('No atoms provided', 'atoms-missing'); + return; } - if (callbacks) { - serializedCallbacks = serializeCallbacks(callbacks); - console.log('Serialized Callbacks:', serializedCallbacks); - } + const serializedCallbacks = serializeCallbacks(atomRegistry?.callbacks ?? []); postToDesignLibrary(getDesignLibraryAtomsRegistryEvent(serializedAtoms, serializedCallbacks)); - console.log('Design Library Atoms mounted'); - console.log('Design Library Callbacks mounted'); - }, [atoms, callbacks]); + const unsubDocumentUpdate = addDocumentUpdateHandler((updatedDocument) => { + setCurrentDocument(updatedDocument); + setRenderKey((k) => k + 1); + }); + + return () => unsubDocumentUpdate(); + }, [atomRegistry]); + + useEffect(() => { + if (renderKey === 0) return; + + postToDesignLibrary( + getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + ); + }, [renderKey]); - return ; + return ( + + {ViewComponent ? : null} + + ); }; diff --git a/packages/react/src/components/SitecoreProvider.test.tsx b/packages/react/src/components/SitecoreProvider.test.tsx index 1527ccaea6..128537fd83 100644 --- a/packages/react/src/components/SitecoreProvider.test.tsx +++ b/packages/react/src/components/SitecoreProvider.test.tsx @@ -6,6 +6,7 @@ import { SitecoreProvider, useSitecore } from './SitecoreProvider'; import { WithSitecoreProps, withSitecore } from '../enhancers/withSitecore'; import { LayoutServiceData, LayoutServicePageState } from '../index'; import { render } from '@testing-library/react'; +import type { ImportMapImport } from './DesignLibrary/models'; describe('SitecoreProvider', () => { let nestedContext = {}; @@ -14,12 +15,20 @@ describe('SitecoreProvider', () => { anotherProperty?: string; } + const loadImportMapStub = async (): Promise => ({}) as ImportMapImport; + const NestedComponent: FC = () => { const { page } = useSitecore(); nestedContext = page; return Page mode is {page.mode.name}; }; + const AtomRegistryProbe: FC = () => { + const { atomRegistry } = useSitecore(); + nestedContext = atomRegistry as unknown; + return probe; + }; + const NestedComponentWithContext = withSitecore()(NestedComponent); const components = new Map(); @@ -58,7 +67,12 @@ describe('SitecoreProvider', () => { it('renders the component with the context', () => { const rendered = render( - + ); @@ -69,7 +83,12 @@ describe('SitecoreProvider', () => { it('updates state when new page is received via props', () => { const rendered = render( - + ); @@ -82,7 +101,12 @@ describe('SitecoreProvider', () => { }; rendered.rerender( - + ); @@ -92,4 +116,26 @@ describe('SitecoreProvider', () => { locale: 'gr', }); }); + + it('exposes atomRegistry on context when provided', () => { + nestedContext = undefined; + const atomRegistry = { + atoms: [], + callbacks: [], + }; + + render( + + + + ); + + expect(nestedContext).to.deep.equal(atomRegistry); + }); }); diff --git a/packages/react/src/components/SitecoreProvider.tsx b/packages/react/src/components/SitecoreProvider.tsx index 3d9250b107..904851d44c 100644 --- a/packages/react/src/components/SitecoreProvider.tsx +++ b/packages/react/src/components/SitecoreProvider.tsx @@ -24,16 +24,19 @@ export interface SitecoreProviderProps { * The dynamic import for import map to be used in variant generation mode. */ loadImportMap: () => Promise; - - /** - * The atoms metadata to be used in the Design Library. - */ - atoms?: AtomMetadata[]; - /** - * The callbacks metadata to be used in the Design Library. + * Atom and callback metadata for low-code components (mirrors the singular `componentMap` pattern). */ - callbacks?: CallbackMetadata[]; + atomRegistry?: { + /** + * The atom metadata to be used for rendering atom components. + */ + atoms?: AtomMetadata[]; + /** + * The callback metadata to be used for rendering atom components. + */ + callbacks?: CallbackMetadata[]; + }; children: React.ReactNode; } @@ -58,13 +61,18 @@ export interface SitecoreProviderState { */ loadImportMap: () => Promise; /** - * The atoms metadata to be used in the Design Library. - */ - atoms?: AtomMetadata[]; - /** - * The callbacks metadata to be used in the Design Library. + * Atom and callback metadata for low-code components (mirrors the singular `componentMap` pattern). */ - callbacks?: CallbackMetadata[]; + atomRegistry?: { + /** + * The atom metadata to be used for rendering atom components. + */ + atoms?: AtomMetadata[]; + /** + * The callback metadata to be used for rendering atom components. + */ + callbacks?: CallbackMetadata[]; + }; /** * The component map to use for rendering components. */ @@ -107,13 +115,13 @@ export const ImportMapReactContext = React.createContext< * @param {SitecoreProviderProps['page']} props.page - The page data. * @param {SitecoreProviderProps['componentMap']} props.componentMap - The component map. * @param {SitecoreProviderProps['loadImportMap']} props.loadImportMap - The function to load the import map. - * @param {SitecoreProviderProps['atoms']} props.atoms - The atoms metadata. + * @param {SitecoreProviderProps['atomRegistry']} props.atomRegistry - Atom and callback metadata for low-code components. * @param {React.ReactNode} props.children - The children to render. * @returns {React.ReactNode} The SitecoreProvider component. * @public */ export const SitecoreProvider = (props: SitecoreProviderProps) => { - const { api, page: propsPage, componentMap, loadImportMap, atoms, children } = props; + const { api, page: propsPage, componentMap, loadImportMap, atomRegistry, children } = props; const [page, setPageInternal] = useState(propsPage); @@ -137,9 +145,9 @@ export const SitecoreProvider = (props: SitecoreProviderProps) => { api, componentMap, loadImportMap, - atoms, + atomRegistry, }), - [page, setPage, api, componentMap, loadImportMap, atoms] + [page, setPage, api, componentMap, loadImportMap, atomRegistry] ); return ( diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index d14fadde95..349475a5e1 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -90,7 +90,6 @@ export { DynamicComponent, ImportMapImport, } from './components/DesignLibrary'; -export { AtomRenderer } from './components/AtomRenderer'; export {} from './components/FEaaS/BYOCComponent'; export { Link, LinkField, LinkFieldValue, LinkProps } from './components/Link'; export { File, FileField } from './components/File'; diff --git a/packages/react/src/test-data/atom-component-layouts.ts b/packages/react/src/test-data/atom-component-layouts.ts new file mode 100644 index 0000000000..885c13a6e8 --- /dev/null +++ b/packages/react/src/test-data/atom-component-layouts.ts @@ -0,0 +1,420 @@ +import { Document } from '@sitecore-content-sdk/content/types/atoms'; + +export const productPuicker: Document = { + name: 'ProductPickerPreset', + root: { + id: 'ae262910-c132-431f-a488-d18af9c39e43', + type: 'Stack', + version: 2, + children: [ + { + id: 'eead1514-d55c-4113-8d73-f6e51cce1c01', + type: 'Select', + bindings: { + value: { + bindType: 'expression', + value: '{{state.category}}', + }, + onValueChange: { + bindType: 'event', + arguments: ['value', 'label'], + actions: [ + { + setState: { + category: '{{value}}', + }, + }, + { + call: 'trackSelection', + args: ['{{value}}', '{{label}}'], + }, + ], + }, + }, + children: [ + { + id: 'e0abec67-6c9e-442a-81e5-acc86b4eaffb', + type: 'SelectTrigger', + children: [ + { + id: '80f907ac-ff71-409d-b39a-327e7833814c', + type: 'SelectValue', + staticProps: { + placeholder: 'Select a category', + }, + }, + ], + }, + { + id: '8b11ed97-2af1-4bdf-aaa9-cd3dadc4cd4a', + type: 'SelectContent', + children: [ + { + id: '168db3c5-ce62-4bcc-8044-3c199fd761a3', + type: 'SelectItem', + show: { + and: [ + { + left: '{{props.categories.length}}', + op: 'eq', + right: '{{state.selectedCategories.count}}', + }, + { + left: '{{item.value}}', + op: 'eq', + right: '{{state.selectedCategory}}', + }, + ], + }, + for: { + each: '{{props.categories}}', + as: 'item', + key: '{{item.value}}', + }, + staticProps: { + classname: 'color-balck', + }, + bindings: { + value: { + bindType: 'expression', + value: '{{item.value}}', + }, + }, + children: ['{{item.label}}'], + }, + ], + }, + ], + }, + { + id: '966bf556-04ff-4112-a8ae-00a071606cd4', + type: 'Image', + staticProps: { + alt: 'alt', + width: 300, + }, + bindings: { + src: { + bindType: 'expression', + value: '{{props.imagesByCategory[state.category]}}', + }, + onValueChange: { + bindType: 'event', + arguments: ['event', 'foo'], + actions: [ + { + setState: { + category: '{{event.target.value}}', + }, + }, + { + call: 'analyticsTrack', + args: [ + '{{event.target.value}}', + 'category-selected', + '{{props.imagesByCategory[state.category]}}', + ], + }, + ], + }, + }, + }, + ], + }, + props: { + categories: [ + { + value: 'hats', + label: 'Hats', + }, + { + value: 'shoes', + label: 'Shoes', + }, + { + value: 'bags', + label: 'Bags', + }, + ], + imagesByCategory: { + hats: '/images/hats.png', + shoes: '/images/shoes.png', + bags: '/images/bags.png', + }, + }, + state: {}, +}; + +export const cardsWithDataBinding: Document = { + name: 'CardsPresetWithDataBinding', + root: { + id: '5112db36-b362-4be5-8b78-5f2f21ad6c77', + type: 'Stack', + staticProps: { + gap: 3, + }, + children: [ + { + id: 'd10f1255-412e-4b40-9e8e-0b0643b439d4', + type: 'Card', + for: { + each: '{{props.Teaser}}', + as: 'item', + }, + children: [ + { + id: '7237936e-9cf0-43b7-9445-b10f3b32b94c', + type: 'CardHeader', + children: [ + { + id: '15ff0525-c3d6-4d24-994b-78c11ef19a15', + type: 'CardTitle', + children: ['{{item.title}}'], + }, + { + id: '442df2cc-3aa9-4a0d-9221-f70dbf045ac6', + type: 'CardDescription', + children: ['{{item.description}}'], + }, + ], + }, + { + id: 'bfb8aa23-b397-48ea-930f-190488963b77', + type: 'CardContent', + children: [ + { + id: 'aad40af8-14f6-4752-8310-6eb4bc52b619', + type: 'Image', + staticProps: { + width: 300, + height: 200, + }, + bindings: { + src: { + bindType: 'expression', + value: '{{item.image}}', + }, + alt: { + bindType: 'expression', + value: '{{item.title}}', + }, + }, + }, + ], + }, + ], + }, + ], + }, + props: { + Link_list_intro: { + Title: 'Learn more about SaaS content management', + }, + Teaser: [ + { + badge: 'Article', + button_label: 'Read the article', + button_URL: + 'https://www.sitecore.com/blog/cloud/what-is-cloud-native-saas?utm_websource=products.xm-cloud', + description: + 'Grasping the benefits and differences between SaaS, cloud-computing, cloud-native, and cloud-hosted is important in determining which technology has the scalability you need — and should expect — to support your long-term growth.', + image: + 'https://sitecorecontenthub.stylelabs.cloud/api/public/content/b4ee038a89874af1838812bfd47c3c7c?v=67c7884d', + title: 'SaaS, cloud computing, and cloud-native development — unravel the difference', + }, + { + badge: 'Article', + button_label: 'Read the article', + button_URL: + 'https://www.sitecore.com/knowledge-center/digital-marketing-resources/why-saas?utm_websource=products.xm-cloud', + description: + 'Investing in a SaaS platform can provide benefits for your business, your internal teams, and your customers. Whatever your unique needs, we can empower you to create the experiences that drive competitive advantage and deliver value.', + image: + 'https://sitecorecontenthub.stylelabs.cloud/api/public/content/e253b03b15dd4cd49f84acefcdeef95a?v=3d1436cd', + title: "What's the big deal with a SaaS CMS?", + }, + { + badge: 'Article', + button_label: 'Read the article', + button_URL: + 'https://www.sitecore.com/knowledge-center/digital-marketing-resources/what-is-cloud-content-management?utm_websource=products.xm-cloud', + description: + "Explore cloud-based content management — including its definition, history, benefits, and how to determine when it's right for your organization.", + image: + 'https://sitecorecontenthub.stylelabs.cloud/api/public/content/3a4e4216497c4edb833954241d10bf01?v=93a3d391', + title: 'Why is everyone moving their content to the cloud?', + }, + ], + }, +}; + +export const accordionWithCards: Document = { + name: 'AccordionPreset', + root: { + id: '87a7cbaa-76fb-4086-bbe7-b8f43ea35eb8', + type: 'Accordion', + staticProps: { + type: 'single', + collapsible: true, + }, + children: [ + { + id: 'b1df2e76-c2b5-4420-8ee3-d60127ba3553', + type: 'AccordionItem', + staticProps: { + value: 'item-1', + }, + children: [ + { + id: 'a13e9646-f8f2-4519-a92a-9bf15cbd933c', + type: 'AccordionTrigger', + children: ['Section One'], + }, + { + id: '0122d596-9b63-4ac7-9a3b-be0401962b9e', + type: 'AccordionContent', + children: [ + { + id: '053ca280-5df8-4a77-823b-c10774b56b77', + type: 'Card', + children: [ + { + id: 'f2396b45-1620-4136-9bed-11e9566c8407', + type: 'CardHeader', + children: [ + { + id: '33e3041d-7d75-4e04-a2f0-571020be9791', + type: 'CardTitle', + children: ['First'], + }, + ], + }, + { + id: 'bb33fdb9-c9ad-444f-bd25-0314c1b0202c', + type: 'CardContent', + children: [ + { + id: 'b5e32073-e254-4904-82fd-07718c5761b0', + type: 'Button', + children: ['Click'], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + id: '0444d9e6-fee6-4b5f-862e-2b9488a2fdee', + type: 'AccordionItem', + staticProps: { + value: 'item-2', + }, + children: [ + { + id: '63479846-0e0e-430a-94d3-fb88c07cc13c', + type: 'AccordionTrigger', + children: ['Section Two'], + }, + { + id: 'a3a1a342-a913-45af-866f-3958df717ec8', + type: 'AccordionContent', + children: [ + { + id: 'ce73c89a-e9df-4295-b7d5-ee58f3f8b938', + type: 'Card', + children: [ + { + id: 'a43af69f-57bd-4916-bcf4-49b1092a315c', + type: 'CardHeader', + children: [ + { + id: 'c3283b16-9c95-4820-aec8-ed481cff6a58', + type: 'CardTitle', + children: ['Second'], + }, + ], + }, + { + id: 'b6b20825-464b-423f-8062-45379c687ba8', + type: 'CardContent', + children: [ + { + id: '5f70bed9-91e9-44d6-bba3-63974f95bb8a', + type: 'Button', + children: ['Open'], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, +}; + +export const cardPreset: Document = { + name: 'CardPreset', + root: { + id: '60ed1306-fda6-4e6b-974d-55746f516e37', + type: 'Card', + children: [ + { + id: 'f3b22759-8dd0-4d2c-a295-5ce5069ee771', + type: 'CardHeader', + children: [ + { + id: '5563c8c8-c88f-4357-a0b3-0520a3dcce37', + type: 'CardTitle', + children: ['Profile'], + }, + { + id: 'e51bcfe3-04d1-4ccd-98e7-cd56d97de021', + type: 'CardDescription', + children: ['Manage your profile settings'], + }, + ], + }, + { + id: 'a2437056-2155-4a38-8479-d6aaeb05e33f', + type: 'CardContent', + children: [ + { + id: 'e4d5dba0-2b67-4093-914b-ce83ec7e2c64', + type: 'Input', + staticProps: { + placeholder: 'Your name', + }, + }, + { + id: '3111c1c6-c6eb-422a-8665-077247204a11', + type: 'Button', + children: ['Save'], + }, + ], + }, + { + id: '63accf88-805f-42df-a17a-83f0b4a052aa', + type: 'CardFooter', + children: [ + { + id: 'b3928606-1e64-44b8-ab66-b69f28354787', + type: 'Button', + staticProps: { + variant: 'outline', + }, + children: ['Cancel'], + }, + ], + }, + ], + }, + state: { + name: '', + }, +}; diff --git a/yarn.lock b/yarn.lock index 062c7224e8..5890c23b41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -801,7 +801,7 @@ __metadata: languageName: node linkType: hard -"@gerrit0/mini-shiki@npm:^3.17.0": +"@gerrit0/mini-shiki@npm:^3.23.0": version: 3.23.0 resolution: "@gerrit0/mini-shiki@npm:3.23.0" dependencies: @@ -2698,10 +2698,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:16.2.0": - version: 16.2.0 - resolution: "@next/env@npm:16.2.0" - checksum: 10/3b0e6c178f225a489767b04e2cfe66e9f278e0b9f900ec27882e8dcc00ba418f0c2af06bf224c92d81f82bcf3a1656946d3f39d75c863439869b0594958b194d +"@next/env@npm:16.2.1": + version: 16.2.1 + resolution: "@next/env@npm:16.2.1" + checksum: 10/c4f19f1767d7a1e8e9ff93cdee7e3b6a923d26d9d71f44124a797f03703ab9a18508b5ede997cc99d0307f2e0d0d1c426e9673a6c11ea10e170b87462a572236 languageName: node linkType: hard @@ -2714,58 +2714,58 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-darwin-arm64@npm:16.2.0" +"@next/swc-darwin-arm64@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-darwin-arm64@npm:16.2.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-darwin-x64@npm:16.2.0" +"@next/swc-darwin-x64@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-darwin-x64@npm:16.2.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-linux-arm64-gnu@npm:16.2.0" +"@next/swc-linux-arm64-gnu@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-linux-arm64-gnu@npm:16.2.1" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-linux-arm64-musl@npm:16.2.0" +"@next/swc-linux-arm64-musl@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-linux-arm64-musl@npm:16.2.1" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-linux-x64-gnu@npm:16.2.0" +"@next/swc-linux-x64-gnu@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-linux-x64-gnu@npm:16.2.1" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-linux-x64-musl@npm:16.2.0" +"@next/swc-linux-x64-musl@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-linux-x64-musl@npm:16.2.1" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-win32-arm64-msvc@npm:16.2.0" +"@next/swc-win32-arm64-msvc@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-win32-arm64-msvc@npm:16.2.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:16.2.0": - version: 16.2.0 - resolution: "@next/swc-win32-x64-msvc@npm:16.2.0" +"@next/swc-win32-x64-msvc@npm:16.2.1": + version: 16.2.1 + resolution: "@next/swc-win32-x64-msvc@npm:16.2.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -4482,22 +4482,22 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": - version: 8.57.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.57.1" + version: 8.57.2 + resolution: "@typescript-eslint/eslint-plugin@npm:8.57.2" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/type-utils": "npm:8.57.1" - "@typescript-eslint/utils": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" + "@typescript-eslint/scope-manager": "npm:8.57.2" + "@typescript-eslint/type-utils": "npm:8.57.2" + "@typescript-eslint/utils": "npm:8.57.2" + "@typescript-eslint/visitor-keys": "npm:8.57.2" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.4.0" peerDependencies: - "@typescript-eslint/parser": ^8.57.1 + "@typescript-eslint/parser": ^8.57.2 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/339a1c3204d45b41cdfab210de624cdd621c6c3928beea018e9deca95aa62fd6bd38b380f858e4f548498bf5fc8b26375286e6f8e7be12e92db7e42161bf31a3 + checksum: 10/0735281c26b1e9b3b9ccce3b872ef9eedcfbbffa6b766470de55735cd7a796b064376189378a65c19c62fa59f24187cecb563fb56d194129673be5cd8d3949e9 languageName: node linkType: hard @@ -4518,18 +4518,18 @@ __metadata: linkType: hard "@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/parser@npm:^8.39.0": - version: 8.57.1 - resolution: "@typescript-eslint/parser@npm:8.57.1" + version: 8.57.2 + resolution: "@typescript-eslint/parser@npm:8.57.2" dependencies: - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" + "@typescript-eslint/scope-manager": "npm:8.57.2" + "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/typescript-estree": "npm:8.57.2" + "@typescript-eslint/visitor-keys": "npm:8.57.2" debug: "npm:^4.4.3" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/b6b84c15f99221d23f2e1c1dc9db5eae0a7a6ff6eaeb4daa8ed8f1ee464c79dfd70041c2239f9387f778ee171c9c832cc3be7e9ba1d993cbe1b421cdecd2a51f + checksum: 10/c9a8a03767ac1b1e8d0bee938fb636b30b7043a776d20353b37be32011ed2b174aa906c27b574fe90bbc48d0235320a4339fb5868f02f5a262a58531e244e1f8 languageName: node linkType: hard @@ -4546,16 +4546,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/project-service@npm:8.57.1" +"@typescript-eslint/project-service@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/project-service@npm:8.57.2" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.57.1" - "@typescript-eslint/types": "npm:^8.57.1" + "@typescript-eslint/tsconfig-utils": "npm:^8.57.2" + "@typescript-eslint/types": "npm:^8.57.2" debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/f3186b1bd68ea9dfaa73482575cad89c0c0c995354b4729911a1a11fd3a11882a8b8c7c2f331a6f7d45061150bb9babb86271c7d2cb463b6d25c8444fad18b84 + checksum: 10/e7f47d5394c6ca3c92fa373bd3a149ad13549944d0d7a12f0ddef6dc921b017cf283876bb272b1f6c4f90e6c84bac7b140ac08acee3a3f26dfd8b43556e0949e languageName: node linkType: hard @@ -4569,13 +4569,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/scope-manager@npm:8.57.1" +"@typescript-eslint/scope-manager@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/scope-manager@npm:8.57.2" dependencies: - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" - checksum: 10/633c13461f31c3d15ef017d73f9f6e6fcf84573205d2863635928f69a75fce48547697c8aac3cbe4c727f754c5c8fbfcc1838fbfeeec1179f3789e7a0798ca0d + "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/visitor-keys": "npm:8.57.2" + checksum: 10/2c1498e25481ee6b1de5c6cbf8fbbdfa9e5e940a91ddd12687dd17d68de5df81c286861f0378282fbe49172dd44094bfeb5b63f80faf37e54dce8fd2ddc7f733 languageName: node linkType: hard @@ -4588,12 +4588,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.57.1, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.1" +"@typescript-eslint/tsconfig-utils@npm:8.57.2, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.2" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/432966db5b031a2b1a43a96d64b78e2a7965952c5902f04bf8b6b2b8e91abcd137e0f1a6d9787653c69b1ae2fb6b0a41525ebcf39c401104225224536373a41d + checksum: 10/2a6e27d541fd0071e5ca14116ba210eab77a8f74cedda32dfce4acce951632971b066261e4b151d34dd0cfad357cb4fd2f7d0b38c9e82a155c226e5c12bd41b3 languageName: node linkType: hard @@ -4613,19 +4613,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/type-utils@npm:8.57.1" +"@typescript-eslint/type-utils@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/type-utils@npm:8.57.2" dependencies: - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" - "@typescript-eslint/utils": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/typescript-estree": "npm:8.57.2" + "@typescript-eslint/utils": "npm:8.57.2" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.4.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/3796ae162a00a74984085bef47c5b664fd19769e701a1d46aa2f2efc0e8d46759d781dfd258d763a044dc754485f9af5a6d79b04853ad7cd25134a8073cab58f + checksum: 10/9df6b587143b2dd3f9fc17dab74e877013cc3ce21a7266af6d85bef05b4602103aa2c0d612a69f479225b67f089dad2a92f8ebbd474a9df0a256caeaa5aa165b languageName: node linkType: hard @@ -4636,10 +4636,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.57.1, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/types@npm:8.57.1" - checksum: 10/3480785a7e833061579cf39f1f0e742925897ffb6510f5acbf4784f11eb60a5e737fc26e63b229c7cd1db5c39a1bf2f17d4c663be6c33b1a7db0ff0874f4fe35 +"@typescript-eslint/types@npm:8.57.2, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/types@npm:8.57.2" + checksum: 10/6d30ff13c8ebe01ba8bcce5f1a5ba52f2c6546056e056f50e010394461d2fffda46dad2a4ebdff59c179873abd274242d8f6a8246c60d7ce244d02ad87bb07f8 languageName: node linkType: hard @@ -4663,14 +4663,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.57.1" +"@typescript-eslint/typescript-estree@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/typescript-estree@npm:8.57.2" dependencies: - "@typescript-eslint/project-service": "npm:8.57.1" - "@typescript-eslint/tsconfig-utils": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/visitor-keys": "npm:8.57.1" + "@typescript-eslint/project-service": "npm:8.57.2" + "@typescript-eslint/tsconfig-utils": "npm:8.57.2" + "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/visitor-keys": "npm:8.57.2" debug: "npm:^4.4.3" minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" @@ -4678,7 +4678,7 @@ __metadata: ts-api-utils: "npm:^2.4.0" peerDependencies: typescript: ">=4.8.4 <6.0.0" - checksum: 10/9c2f7f7f42f1abcb9000e70bb8df08afbae162959a57e12d67f32d351a542b68e85a7ead5898de3ffac525a7a01b3ffb0d29006d0d19c97f79a5c31915bb62fb + checksum: 10/85d3f2799ee3915b4a06014d90db4aab28c0c0377b40aced5b49acb5f038862e5447f6c355d85368a8f587820f7840e945fa8a9d5347234f1fb070a97c2a9c1d languageName: node linkType: hard @@ -4697,18 +4697,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/utils@npm:8.57.1" +"@typescript-eslint/utils@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/utils@npm:8.57.2" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.57.1" - "@typescript-eslint/types": "npm:8.57.1" - "@typescript-eslint/typescript-estree": "npm:8.57.1" + "@typescript-eslint/scope-manager": "npm:8.57.2" + "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/typescript-estree": "npm:8.57.2" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.0.0" - checksum: 10/456481b16627552f20c8c2cfe1d406d06abc64fa32b550bc6337d13975e548ce56922895751103190a62c56cf3ae11ca975383282f8dc11de1876c09e84f8606 + checksum: 10/48851aa53b403e67dce7504859f3e5a9e008d43fa8c98dc2b0fc900980c1f77dda60648f957fd69a78cea44e0a94d1d7e807fcbc30ffca3fb688d04be2acd898 languageName: node linkType: hard @@ -4722,13 +4722,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.57.1": - version: 8.57.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.57.1" +"@typescript-eslint/visitor-keys@npm:8.57.2": + version: 8.57.2 + resolution: "@typescript-eslint/visitor-keys@npm:8.57.2" dependencies: - "@typescript-eslint/types": "npm:8.57.1" + "@typescript-eslint/types": "npm:8.57.2" eslint-visitor-keys: "npm:^5.0.0" - checksum: 10/692b95d204a35a00b3dd8d6a3ba86cce2f9ee1bf64691ca3be0fee2f393d6466560c8742fe3246fee63292165c5a682d98952c7ac4515062b5ac6eaf000b795a + checksum: 10/8059dcb05694b31b3372f022e25493c4a3fd1a9952720bcb3eac1911828f0b81b3f8aeb11a30dc646880e16396317512747a9b2afee059c0472ea5c4d4546d29 languageName: node linkType: hard @@ -5577,11 +5577,11 @@ __metadata: linkType: hard "baseline-browser-mapping@npm:^2.9.0, baseline-browser-mapping@npm:^2.9.19": - version: 2.10.9 - resolution: "baseline-browser-mapping@npm:2.10.9" + version: 2.10.10 + resolution: "baseline-browser-mapping@npm:2.10.10" bin: baseline-browser-mapping: dist/cli.cjs - checksum: 10/40ab7b9294b9b865d51e9c62c630c1416b15326bc7cb98040a3f4619676e1c024b6fc7c20c4169b7f4593769918461fab7bb3a3ce862c7833be72a9dc4161ade + checksum: 10/c956e25cd566bf36dcd594a3f3050d33ed24309bd84572469ac2f4332942efd30b8b014241484a3b6101bd975d32d58def2df9f7881d66ff11e0ab8069ae99ae languageName: node linkType: hard @@ -5637,11 +5637,11 @@ __metadata: linkType: hard "brace-expansion@npm:^5.0.2": - version: 5.0.4 - resolution: "brace-expansion@npm:5.0.4" + version: 5.0.5 + resolution: "brace-expansion@npm:5.0.5" dependencies: balanced-match: "npm:^4.0.2" - checksum: 10/cfd57e20d8ded9578149e47ae4d3fff2b2f78d06b54a32a73057bddff65c8e9b930613f0cbcfefedf12dd117151e19d4da16367d5127c54f3bff02d8a4479bb2 + checksum: 10/f259b2ddf04489da9512ad637ba6b4ef2d77abd4445d20f7f1714585f153435200a53fa6a2e4a5ee974df14ddad4cd16421f6f803e96e8b452bd48598878d0ee languageName: node linkType: hard @@ -5855,9 +5855,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001780 - resolution: "caniuse-lite@npm:1.0.30001780" - checksum: 10/4232c1bc97559c3052769b1ddbfa9e5095c4b48cbbf994db0515d4fd2d11bade9755f6f15dfbdd9a3cb7816109464b702d7241f3b299b0874a5dce8a355452d3 + version: 1.0.30001781 + resolution: "caniuse-lite@npm:1.0.30001781" + checksum: 10/13000e0bbb340d5b7d2543293c84439acf3ac6b643ae9a5b69e92138c95471b5e97067a8eb2b28e79b67d1390cc2a51ba2d5c474f745f7f1f7a955392fd0ec4f languageName: node linkType: hard @@ -6850,9 +6850,9 @@ __metadata: linkType: hard "diff@npm:~8.0.2": - version: 8.0.3 - resolution: "diff@npm:8.0.3" - checksum: 10/52f957e1fa53db4616ff5f4811b92b22b97a160c12a2f86f22debd4181227b0f6751aa8fd711d6a8fcf4618acb13b86bc702e6d9d6d6ed82acfd00c9cb26ace2 + version: 8.0.4 + resolution: "diff@npm:8.0.4" + checksum: 10/b4036ceda0d1e10683a2313079ed52c5e6b09553ae29da87bce81d98714d9725dbf3c0f6f7c3b1f16eec049fe17087e38ee329e732580fa62f6ec1c2487b2435 languageName: node linkType: hard @@ -6968,9 +6968,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.263": - version: 1.5.321 - resolution: "electron-to-chromium@npm:1.5.321" - checksum: 10/ccb635798bf9451f4b77ef10288c43f39f30904300c176aac38dbb082ab080828588c3f48952f27b4157dcf068405dc44a7df439715907dadf9d65d9785215e7 + version: 1.5.325 + resolution: "electron-to-chromium@npm:1.5.325" + checksum: 10/5cf705b7abe78b49c1e0a7b3c424636712c671a89c2172b646bc5ca41d377c094ef5efe16e5988f4d5256b3027d48ffd3dc5e6e1e96977b2c24f33c63bd8065f languageName: node linkType: hard @@ -8312,11 +8312,11 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.10.0, get-tsconfig@npm:^4.7.5": - version: 4.13.6 - resolution: "get-tsconfig@npm:4.13.6" + version: 4.13.7 + resolution: "get-tsconfig@npm:4.13.7" dependencies: resolve-pkg-maps: "npm:^1.0.0" - checksum: 10/5cd1c1f273e9f1cd9f1ebeaaea281a3b7b71562fc9614ee0cf0575463b0435de68831354434a5a1a564e1049062d597d0dae8ef33f489a6d12afccee032f6784 + checksum: 10/e23622bd3c5766a2fe43a28cb7a490ebb175eb7f429e4ec53688e3b7a40882fb0ec6f3b753f237757d63861ccd8033232d1d76f8960a683af8e09099e7c897fe languageName: node linkType: hard @@ -8566,9 +8566,9 @@ __metadata: linkType: hard "graphql@npm:^16.11.0": - version: 16.13.1 - resolution: "graphql@npm:16.13.1" - checksum: 10/a42f857f60351e1f4665aa5bc5524796d3f45bf81793e6db932f902aff769b0488dafaa1d9c07bddda7122a1f6d0b0105fab12d38992f6b6fb81fc3d7ced1afc + version: 16.13.2 + resolution: "graphql@npm:16.13.2" + checksum: 10/9ede86f0a7227d47a41e2076e0132839f66fbd14899abfd818d7de2ab81076ee249c8788ad42243367d68a027a8763cc4c7be2e441ccfb03a8549490c7b66c2f languageName: node linkType: hard @@ -10921,7 +10921,7 @@ __metadata: languageName: node linkType: hard -"markdown-it@npm:^14.1.0": +"markdown-it@npm:^14.1.1": version: 14.1.1 resolution: "markdown-it@npm:14.1.1" dependencies: @@ -11070,7 +11070,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.1.1, minimatch@npm:^10.2.2": +"minimatch@npm:^10.1.1, minimatch@npm:^10.2.2, minimatch@npm:^10.2.4": version: 10.2.4 resolution: "minimatch@npm:10.2.4" dependencies: @@ -11418,18 +11418,18 @@ __metadata: linkType: hard "next@npm:^16.1.1": - version: 16.2.0 - resolution: "next@npm:16.2.0" - dependencies: - "@next/env": "npm:16.2.0" - "@next/swc-darwin-arm64": "npm:16.2.0" - "@next/swc-darwin-x64": "npm:16.2.0" - "@next/swc-linux-arm64-gnu": "npm:16.2.0" - "@next/swc-linux-arm64-musl": "npm:16.2.0" - "@next/swc-linux-x64-gnu": "npm:16.2.0" - "@next/swc-linux-x64-musl": "npm:16.2.0" - "@next/swc-win32-arm64-msvc": "npm:16.2.0" - "@next/swc-win32-x64-msvc": "npm:16.2.0" + version: 16.2.1 + resolution: "next@npm:16.2.1" + dependencies: + "@next/env": "npm:16.2.1" + "@next/swc-darwin-arm64": "npm:16.2.1" + "@next/swc-darwin-x64": "npm:16.2.1" + "@next/swc-linux-arm64-gnu": "npm:16.2.1" + "@next/swc-linux-arm64-musl": "npm:16.2.1" + "@next/swc-linux-x64-gnu": "npm:16.2.1" + "@next/swc-linux-x64-musl": "npm:16.2.1" + "@next/swc-win32-arm64-msvc": "npm:16.2.1" + "@next/swc-win32-x64-msvc": "npm:16.2.1" "@swc/helpers": "npm:0.5.15" baseline-browser-mapping: "npm:^2.9.19" caniuse-lite: "npm:^1.0.30001579" @@ -11473,7 +11473,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 10/9bd1a87f34cae488efdd07cf22b8926f3c45d9ef76a3d1978557c4c147ed82f273925430d3e599c29d59e63b0128c5b5376b7ddb4d7b250994d24f58e4dc1c10 + checksum: 10/319c0b18173a90e53b5e5ffafa8a8fecb7cc340b77728796743edd996c7ee7652201892bff60c32f6a3b75abdff1449b77f13f6fab8fd56d4f9da47cf0fb9299 languageName: node linkType: hard @@ -12479,16 +12479,16 @@ __metadata: linkType: hard "picomatch@npm:^2.0.4, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10/60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc + version: 2.3.2 + resolution: "picomatch@npm:2.3.2" + checksum: 10/b788ef8148a2415b9dec12f0bb350ae6a5830f8f1950e472abc2f5225494debf7d1b75eb031df0ceaea9e8ec3e7bad599e8dbf3c60d61b42be429ba41bff4426 languageName: node linkType: hard "picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 10/57b99055f40b16798f2802916d9c17e9744e620a0db136554af01d19598b96e45e2f00014c91d1b8b13874b80caa8c295b3d589a3f72373ec4aaf54baa5962d5 + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10/f6ef80a3590827ce20378ae110ac78209cc4f74d39236370f1780f957b7ee41c12acde0e4651b90f39983506fd2f5e449994716f516db2e9752924aff8de93ce languageName: node linkType: hard @@ -14192,15 +14192,15 @@ __metadata: linkType: hard "tar@npm:^7.5.4": - version: 7.5.12 - resolution: "tar@npm:7.5.12" + version: 7.5.13 + resolution: "tar@npm:7.5.13" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/a72114d28ab9b4878eeebaae8987692a577c390683c13f150d8330e139237038cc46fbb0be6983b02acf5a31b01d74776436ba03790f320a59efb44b8ac39e39 + checksum: 10/2bc2b6f0349038a6621dbba1c4522d45752d5071b2994692257113c2050cd23fafc30308f820e5f8ad6fda3f7d7f92adc9a432aa733daa04c42af2061c021c3f languageName: node linkType: hard @@ -14660,19 +14660,19 @@ __metadata: linkType: hard "typedoc@npm:^0.28.4": - version: 0.28.17 - resolution: "typedoc@npm:0.28.17" + version: 0.28.18 + resolution: "typedoc@npm:0.28.18" dependencies: - "@gerrit0/mini-shiki": "npm:^3.17.0" + "@gerrit0/mini-shiki": "npm:^3.23.0" lunr: "npm:^2.3.9" - markdown-it: "npm:^14.1.0" - minimatch: "npm:^9.0.5" - yaml: "npm:^2.8.1" + markdown-it: "npm:^14.1.1" + minimatch: "npm:^10.2.4" + yaml: "npm:^2.8.2" peerDependencies: - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x bin: typedoc: bin/typedoc - checksum: 10/09bf15a30c51c7e16533865fb7988cece90c9eb10f7fa239b7db0ec8000c69b63c6a9ee999f40e4783311a6b0d641fb384b76c273a12fcd09f3ef5e9315902f6 + checksum: 10/d7342aeeda34a69e853463e5300a7a06ec42757e2942f3de4a2c882f08f1d4e4d03662a4e8e8259e14c1a0b948b57a6e833d6e391ba41987c4cd400d4d76aeab languageName: node linkType: hard @@ -15369,8 +15369,8 @@ __metadata: linkType: hard "ws@npm:^8.11.0, ws@npm:^8.18.0": - version: 8.19.0 - resolution: "ws@npm:8.19.0" + version: 8.20.0 + resolution: "ws@npm:8.20.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -15379,7 +15379,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b + checksum: 10/b7ab934b21ffdea9f25a5af5097e8c1ec7625db553bca026c5a23e35b7c236f3fb89782f2b57fab9da553864512f9aa7d245827ef998d26ffa1b2187a19a6d10 languageName: node linkType: hard @@ -15447,18 +15447,18 @@ __metadata: linkType: hard "yaml@npm:^1.10.0": - version: 1.10.2 - resolution: "yaml@npm:1.10.2" - checksum: 10/e088b37b4d4885b70b50c9fa1b7e54bd2e27f5c87205f9deaffd1fb293ab263d9c964feadb9817a7b129a5bf30a06582cb08750f810568ecc14f3cdbabb79cb3 + version: 1.10.3 + resolution: "yaml@npm:1.10.3" + checksum: 10/e2ef2feb92c708138f016c69777a0f1e45f6d3c5e7cbcda30807a98a37eda2e008bd4fa57352b043c65245a4c799d0c99d1f9b3425de40e70929e26d2ea38215 languageName: node linkType: hard -"yaml@npm:^2.8.1": - version: 2.8.2 - resolution: "yaml@npm:2.8.2" +"yaml@npm:^2.8.2": + version: 2.8.3 + resolution: "yaml@npm:2.8.3" bin: yaml: bin.mjs - checksum: 10/4eab0074da6bc5a5bffd25b9b359cf7061b771b95d1b3b571852098380db3b1b8f96e0f1f354b56cc7216aa97cea25163377ccbc33a2e9ce00316fe8d02f4539 + checksum: 10/ecad41d39d34fae5cc17ea2d4b7f7f55faacd45cbce8983ba22d48d1ed1a92ed242ea49ea813a79ac39a69f75f9c5a03e7b5395fd954d55476f25e21a47c141d languageName: node linkType: hard From 9bbe3653f6a9c619fd4f06d92acb4b6c3efce747 Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Wed, 29 Apr 2026 10:57:33 +0300 Subject: [PATCH 05/32] Update to beta versions for beta release (#453) --- packages/analytics-core/package.json | 4 +- packages/cli/package.json | 6 +- packages/content/package.json | 8 +-- packages/core/package.json | 2 +- packages/create-content-sdk-app/package.json | 2 +- packages/events/package.json | 6 +- packages/nextjs/package.json | 18 +++--- packages/personalize/package.json | 8 +-- packages/react/package.json | 12 ++-- packages/search/package.json | 4 +- yarn.lock | 64 ++++++++++---------- 11 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/analytics-core/package.json b/packages/analytics-core/package.json index 981828dc66..521f1738fb 100644 --- a/packages/analytics-core/package.json +++ b/packages/analytics-core/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-canary.20", + "@sitecore-content-sdk/core": "2.0.0-beta.1", "debug": "^4.4.3" }, "description": "Provides shared logic and runtime initialization. Required for the Content SDK 'events' and 'personalize' packages to function.", @@ -73,5 +73,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-canary.20" + "version": "2.0.0-beta.1" } diff --git a/packages/cli/package.json b/packages/cli/package.json index fe949ee0f8..c6b0d94c26 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/cli", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "description": "Sitecore Content SDK CLI", "main": "dist/cjs/cli.js", "module": "dist/esm/cli.js", @@ -34,8 +34,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-canary.20", - "@sitecore-content-sdk/core": "2.0.0-canary.20", + "@sitecore-content-sdk/content": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.1", "chokidar": "^4.0.3", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", diff --git a/packages/content/package.json b/packages/content/package.json index 8dc61ea504..1fb55e4234 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/content", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/events": "2.0.0-canary.20", + "@sitecore-content-sdk/events": "2.0.0-beta.1", "@stylistic/eslint-plugin": "^5.2.2", "@types/chai": "^5.2.2", "@types/chai-spies": "^1.0.6", @@ -74,10 +74,10 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/events": "^2.0.0-canary" + "@sitecore-content-sdk/events": "^2.0.0-beta.1" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-canary.20", + "@sitecore-content-sdk/core": "2.0.0-beta.1", "chalk": "^4.1.2", "debug": "^4.4.0", "glob": "^11.0.2", diff --git a/packages/core/package.json b/packages/core/package.json index 8fbecd95f9..a9d148f210 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/core", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/create-content-sdk-app/package.json b/packages/create-content-sdk-app/package.json index 2290cf1b64..b1b75cb002 100644 --- a/packages/create-content-sdk-app/package.json +++ b/packages/create-content-sdk-app/package.json @@ -1,6 +1,6 @@ { "name": "create-content-sdk-app", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "description": "Sitecore Content SDK initializer", "bin": "./dist/index.js", "scripts": { diff --git a/packages/events/package.json b/packages/events/package.json index 0966709462..e1c7024ea1 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/Sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-canary.20", - "@sitecore-content-sdk/core": "2.0.0-canary.20", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.1", "debug": "^4.4.3" }, "description": "Enables real-time, unified tracking to send events to Sitecore.", @@ -75,5 +75,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-canary.20" + "version": "2.0.0-beta.1" } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index dab84981dd..2cf757570f 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/nextjs", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -32,8 +32,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-canary.20", - "@sitecore-content-sdk/personalize": "2.0.0-canary.20", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", + "@sitecore-content-sdk/personalize": "2.0.0-beta.1", "@stylistic/eslint-plugin": "^5.2.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.3.0", @@ -76,9 +76,9 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-canary", - "@sitecore-content-sdk/events": "^2.0.0-canary", - "@sitecore-content-sdk/personalize": "^2.0.0-canary", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.1", + "@sitecore-content-sdk/events": "^2.0.0-beta.1", + "@sitecore-content-sdk/personalize": "^2.0.0-beta.1", "next": "^16.1.1", "react": "^19.2.1", "react-dom": "^19.2.1", @@ -91,9 +91,9 @@ }, "dependencies": { "@babel/parser": "^7.27.2", - "@sitecore-content-sdk/content": "2.0.0-canary.20", - "@sitecore-content-sdk/core": "2.0.0-canary.20", - "@sitecore-content-sdk/react": "2.0.0-canary.20", + "@sitecore-content-sdk/content": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/react": "2.0.0-beta.1", "recast": "^0.23.11", "regex-parser": "^2.3.1", "sync-disk-cache": "^2.1.0" diff --git a/packages/personalize/package.json b/packages/personalize/package.json index bf689b6a51..8ae5fd6bfd 100644 --- a/packages/personalize/package.json +++ b/packages/personalize/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-canary.20", - "@sitecore-content-sdk/core": "2.0.0-canary.20", - "@sitecore-content-sdk/events": "2.0.0-canary.20", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/events": "2.0.0-beta.1", "debug": "^4.4.3" }, "description": "Provides personalization capabilities to build tailored experiences for site visitors.", @@ -70,5 +70,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-canary.20" + "version": "2.0.0-beta.1" } diff --git a/packages/react/package.json b/packages/react/package.json index 7671a225c9..874af821c4 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/react", - "version": "2.0.0-canary.20", + "version": "2.0.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -70,16 +70,16 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-canary", - "@sitecore-content-sdk/events": "^2.0.0-canary", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.1", + "@sitecore-content-sdk/events": "^2.0.0-beta.1", "@sitecore-feaas/clientside": "^0.6.0", "react": "^19.2.1", "react-dom": "^19.2.1" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-canary.20", - "@sitecore-content-sdk/core": "2.0.0-canary.20", - "@sitecore-content-sdk/search": "0.2.0-canary.30", + "@sitecore-content-sdk/content": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/search": "0.2.0-beta.1", "fast-deep-equal": "^3.1.3", "zod": "^4.3.6" }, diff --git a/packages/search/package.json b/packages/search/package.json index 8de630ad75..7165347b75 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/search", - "version": "0.2.0-canary.30", + "version": "0.2.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-canary.20" + "@sitecore-content-sdk/core": "2.0.0-beta.1" }, "devDependencies": { "@types/chai": "^5.2.3", diff --git a/yarn.lock b/yarn.lock index 5890c23b41..d5bf5ec3a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3514,12 +3514,12 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.0.0-canary.20, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3544,8 +3544,8 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/inquirer": "npm:^9.0.9" @@ -3584,12 +3584,12 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/content@npm:2.0.0-canary.20, @sitecore-content-sdk/content@workspace:packages/content": +"@sitecore-content-sdk/content@npm:2.0.0-beta.1, @sitecore-content-sdk/content@workspace:packages/content": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/content@workspace:packages/content" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/events": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/chai-spies": "npm:^1.0.6" @@ -3631,11 +3631,11 @@ __metadata: typescript: "npm:~5.8.3" url-parse: "npm:^1.5.10" peerDependencies: - "@sitecore-content-sdk/events": ^2.0.0-canary + "@sitecore-content-sdk/events": ^2.0.0-beta.1 languageName: unknown linkType: soft -"@sitecore-content-sdk/core@npm:2.0.0-canary.20, @sitecore-content-sdk/core@workspace:packages/core": +"@sitecore-content-sdk/core@npm:2.0.0-beta.1, @sitecore-content-sdk/core@workspace:packages/core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/core@workspace:packages/core" dependencies: @@ -3675,14 +3675,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/events@npm:2.0.0-canary.20, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/events@npm:2.0.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: "@jest/globals": "npm:^30.2.0" "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3708,11 +3708,11 @@ __metadata: resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: "@babel/parser": "npm:^7.27.2" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/content": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/personalize": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/react": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/react": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" "@testing-library/react": "npm:^16.3.0" @@ -3757,9 +3757,9 @@ __metadata: ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-canary - "@sitecore-content-sdk/events": ^2.0.0-canary - "@sitecore-content-sdk/personalize": ^2.0.0-canary + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.1 + "@sitecore-content-sdk/events": ^2.0.0-beta.1 + "@sitecore-content-sdk/personalize": ^2.0.0-beta.1 next: ^16.1.1 react: ^19.2.1 react-dom: ^19.2.1 @@ -3770,14 +3770,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.0.0-canary.20, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.0.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/events": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3798,13 +3798,13 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/react@npm:2.0.0-canary.20, @sitecore-content-sdk/react@workspace:packages/react": +"@sitecore-content-sdk/react@npm:2.0.0-beta.1, @sitecore-content-sdk/react@workspace:packages/react": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/react@workspace:packages/react" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" - "@sitecore-content-sdk/search": "npm:0.2.0-canary.30" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/search": "npm:0.2.0-beta.1" "@sitecore-feaas/clientside": "npm:^0.6.0" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" @@ -3844,19 +3844,19 @@ __metadata: typescript: "npm:~5.8.3" zod: "npm:^4.3.6" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-canary - "@sitecore-content-sdk/events": ^2.0.0-canary + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.1 + "@sitecore-content-sdk/events": ^2.0.0-beta.1 "@sitecore-feaas/clientside": ^0.6.0 react: ^19.2.1 react-dom: ^19.2.1 languageName: unknown linkType: soft -"@sitecore-content-sdk/search@npm:0.2.0-canary.30, @sitecore-content-sdk/search@workspace:packages/search": +"@sitecore-content-sdk/search@npm:0.2.0-beta.1, @sitecore-content-sdk/search@workspace:packages/search": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/search@workspace:packages/search" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-canary.20" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" chai: "npm:^6.2.1" From 60c6ce38f5c078191c392537d734d4f2081e0312 Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:04:37 +0300 Subject: [PATCH 06/32] [content] Introduce library-atoms mode for design library (#452) * update to beta versions * add library mode atoms * changelog * update changelog * api extractor --- CHANGELOG.md | 1 + .../content/api/content-sdk-content.api.md | 14 ---------- .../src/client/sitecore-client.test.ts | 28 +++++++++++++++---- .../content/src/client/sitecore-client.ts | 6 +++- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a2c68d029..305cb1cbb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Our versioning strategy is as follows: * `[content]` `[react]` `[nextjs]` Atomic Design Foundation - Introduced the ability to create a callback registry ([#422](https://github.com/Sitecore/content-sdk/pull/422)) - Introduce the component layout Document contract and ability to render it; set up Design Studio integration ([#423](https://github.com/Sitecore/content-sdk/pull/423)) + - Introduce `library-atoms` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) ### 🐛 Bug Fixes diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 0648dbdf66..5fb45c2043 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -507,11 +507,8 @@ export function evaluateShowNode(node: ShowNode, ctx: ResolveContext): boolean; // @internal export interface EventBinding { - // (undocumented) actions: Action[]; - // (undocumented) arguments: string[]; - // (undocumented) bindType: 'event'; } @@ -520,9 +517,7 @@ const executeScriptElements: (rootElement: HTMLElement) => void; // @internal export interface ExpressionBinding { - // (undocumented) bindType: 'expression'; - // (undocumented) value: string; } @@ -555,11 +550,8 @@ export function filterComponentsByType(components: ComponentFileWithType[], allo // @internal export interface ForLoop { - // (undocumented) as: string; - // (undocumented) each: string; - // (undocumented) key?: string; } @@ -1257,23 +1249,18 @@ export interface ServerComponentPreviewEventArgs extends DesignLibraryEvent { // @internal export interface SetStateAction { - // (undocumented) setState: Record; } // @internal export interface ShowAnd { - // (undocumented) and: ShowNode[]; } // @internal export interface ShowComparison { - // (undocumented) left: string; - // (undocumented) op: 'eq' | 'ne'; - // (undocumented) right: string; } @@ -1282,7 +1269,6 @@ export type ShowNode = ShowComparison | ShowAnd | ShowOr; // @internal export interface ShowOr { - // (undocumented) or: ShowNode[]; } diff --git a/packages/content/src/client/sitecore-client.test.ts b/packages/content/src/client/sitecore-client.test.ts index 48aef40c48..c3f4aaada2 100644 --- a/packages/content/src/client/sitecore-client.test.ts +++ b/packages/content/src/client/sitecore-client.test.ts @@ -498,8 +498,7 @@ describe('SitecoreClient', () => { }, }; layoutServiceStub.fetchLayoutData.returns(rawLayout); - const stringTransformer = (value: string) => - value === 'home' ? 'rewritten' : value; + const stringTransformer = (value: string) => (value === 'home' ? 'rewritten' : value); const clientWithRewrite = new SitecoreClient({ ...defaultInitOptions, rewriteMediaUrls: stringTransformer, @@ -537,9 +536,9 @@ describe('SitecoreClient', () => { const result = await clientWithRewrite.getPage(path, { locale }); - expect( - (result?.layout.sitecore.route?.fields?.image?.value as { src: string }).src - ).to.equal('https://custom.example.com/-/media/hero.jpg'); + expect((result?.layout.sitecore.route?.fields?.image?.value as { src: string }).src).to.equal( + 'https://custom.example.com/-/media/hero.jpg' + ); delete process.env[SITECORE_EXPERIENCE_EDGE_HOSTNAME_ENV]; }); @@ -1262,6 +1261,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, }); @@ -1273,6 +1273,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, }); @@ -1284,6 +1285,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, }); @@ -1295,6 +1297,7 @@ describe('SitecoreClient', () => { isDesignLibrary: true, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, }); @@ -1302,6 +1305,7 @@ describe('SitecoreClient', () => { name: DesignLibraryMode.Metadata, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, isNormal: false, isPreview: false, @@ -1313,6 +1317,7 @@ describe('SitecoreClient', () => { name: DesignLibraryMode.Normal, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, isNormal: false, isPreview: false, @@ -1320,6 +1325,18 @@ describe('SitecoreClient', () => { isDesignLibrary: true, }); + expect(sitecoreClient['getPageMode'](DesignLibraryMode.Atoms)).to.deep.equal({ + name: DesignLibraryMode.Atoms, + isNormal: false, + isPreview: false, + isEditing: false, + isDesignLibrary: true, + designLibrary: { + isVariantGeneration: false, + isAtomsMode: true, + }, + }); + expect(sitecoreClient['getPageMode']('invalid-mode' as any)).to.deep.equal({ name: 'invalid-mode', isNormal: false, @@ -1328,6 +1345,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isAtomsMode: false, }, }); }); diff --git a/packages/content/src/client/sitecore-client.ts b/packages/content/src/client/sitecore-client.ts index 1f2d5bd93e..88be78b22f 100644 --- a/packages/content/src/client/sitecore-client.ts +++ b/packages/content/src/client/sitecore-client.ts @@ -751,7 +751,7 @@ export class SitecoreClient implements BaseSitecoreClient { isPreview: false, isEditing: false, isDesignLibrary: false, - designLibrary: { isVariantGeneration: false }, + designLibrary: { isVariantGeneration: false, isAtomsMode: false }, }; switch (mode) { @@ -775,6 +775,10 @@ export class SitecoreClient implements BaseSitecoreClient { pageMode.isDesignLibrary = true; pageMode.isEditing = true; break; + case DesignLibraryMode.Atoms: + pageMode.isDesignLibrary = true; + pageMode.designLibrary.isAtomsMode = true; + break; default: break; From ba8c6fb539bc1854b44da4edb02ec25f1ee98cbf Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:06:14 +0300 Subject: [PATCH 07/32] Introduce Zod Schemas for Content SDK Fields (#450) --- CHANGELOG.md | 1 + packages/nextjs/api/content-sdk-nextjs.api.md | 78 ++++++ packages/nextjs/src/index.ts | 12 + packages/react/api/content-sdk-react.api.md | 68 ++++++ .../react/src/atoms/field-schemas.test.ts | 231 ++++++++++++++++++ packages/react/src/atoms/field-schemas.ts | 177 ++++++++++++++ packages/react/src/atoms/index.ts | 14 ++ packages/react/src/index.ts | 12 + 8 files changed, 593 insertions(+) create mode 100644 packages/react/src/atoms/field-schemas.test.ts create mode 100644 packages/react/src/atoms/field-schemas.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 305cb1cbb0..6990d8e25e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Our versioning strategy is as follows: * `[content]` `[react]` `[nextjs]` Atomic Design Foundation - Introduced the ability to create a callback registry ([#422](https://github.com/Sitecore/content-sdk/pull/422)) - Introduce the component layout Document contract and ability to render it; set up Design Studio integration ([#423](https://github.com/Sitecore/content-sdk/pull/423)) + - Introduce Zod Schemas for Content SDK Fields ([#450](https://github.com/Sitecore/content-sdk/pull/450)) - Introduce `library-atoms` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) diff --git a/packages/nextjs/api/content-sdk-nextjs.api.md b/packages/nextjs/api/content-sdk-nextjs.api.md index f9fb17f33b..163b3093d6 100644 --- a/packages/nextjs/api/content-sdk-nextjs.api.md +++ b/packages/nextjs/api/content-sdk-nextjs.api.md @@ -7,6 +7,11 @@ import { AnalyticsAdapter } from '@sitecore-content-sdk/analytics-core/internal'; import { AppPlaceholder } from '@sitecore-content-sdk/react'; import { AppPlaceholderProps } from '@sitecore-content-sdk/react'; +import { ArgMeta } from '@sitecore-content-sdk/react'; +import { AtomChild } from '@sitecore-content-sdk/react'; +import { AtomMetadata } from '@sitecore-content-sdk/react'; +import { AtomSchemaInput } from '@sitecore-content-sdk/react'; +import { AtomType } from '@sitecore-content-sdk/react'; import { BYOCClientWrapper } from '@sitecore-content-sdk/react'; import { BYOCComponent } from '@sitecore-content-sdk/react'; import { BYOCComponentParams } from '@sitecore-content-sdk/react'; @@ -15,6 +20,8 @@ import { BYOCServerWrapper } from '@sitecore-content-sdk/react'; import { BYOCWrapper as BYOCWrapper_2 } from '@sitecore-content-sdk/react'; import { CacheClient } from '@sitecore-content-sdk/core'; import { CacheOptions } from '@sitecore-content-sdk/core'; +import { CallbackArgZodTuple } from '@sitecore-content-sdk/react'; +import { CallbackPropKeys } from '@sitecore-content-sdk/react'; import { CdpHelper } from '@sitecore-content-sdk/content/personalize'; import { ClientEditingChromesUpdate } from '@sitecore-content-sdk/react'; import { ComponentFields } from '@sitecore-content-sdk/content/layout'; @@ -25,9 +32,13 @@ import { ComponentMap } from '@sitecore-content-sdk/react'; import { ComponentParams } from '@sitecore-content-sdk/content/layout'; import { ComponentRendering } from '@sitecore-content-sdk/content/layout'; import { constants } from '@sitecore-content-sdk/core'; +import { createAtom } from '@sitecore-content-sdk/react'; import { createGraphQLClientFactory } from '@sitecore-content-sdk/content/client'; import { DateField } from '@sitecore-content-sdk/react'; +import { DateFieldSchema } from '@sitecore-content-sdk/react'; +import { dateFieldSchema } from '@sitecore-content-sdk/react'; import { DeepRequired } from '@sitecore-content-sdk/content/config'; +import { DefaultChild } from '@sitecore-content-sdk/react'; import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-content-sdk/react'; import { DefaultEmptyFieldEditingComponentText } from '@sitecore-content-sdk/react'; import { DefaultRetryStrategy } from '@sitecore-content-sdk/content/client'; @@ -36,6 +47,7 @@ import { DesignLibraryRenderPreviewData } from '@sitecore-content-sdk/content/ed import { DictionaryPhrases } from '@sitecore-content-sdk/content/i18n'; import { DictionaryService } from '@sitecore-content-sdk/content/i18n'; import { DictionaryServiceConfig } from '@sitecore-content-sdk/content/i18n'; +import { EditableComponentProps } from '@sitecore-content-sdk/react'; import { EDITING_COMPONENT_ID } from '@sitecore-content-sdk/content/layout'; import { EDITING_COMPONENT_PLACEHOLDER } from '@sitecore-content-sdk/content/layout'; import { EditingRenderQueryParams } from '@sitecore-content-sdk/content/editing'; @@ -60,6 +72,8 @@ import { FetchOptions } from '@sitecore-content-sdk/content/client'; import { Field } from '@sitecore-content-sdk/content/layout'; import { File as File_2 } from '@sitecore-content-sdk/react'; import { FileField } from '@sitecore-content-sdk/react'; +import { FileFieldSchema } from '@sitecore-content-sdk/react'; +import { fileFieldSchema } from '@sitecore-content-sdk/react'; import { Form } from '@sitecore-content-sdk/react'; import { GenerateMapArgs } from '@sitecore-content-sdk/content/tools'; import { GenerateMapFunction } from '@sitecore-content-sdk/content/tools'; @@ -71,6 +85,7 @@ import { getComponentList } from '@sitecore-content-sdk/content/node-tools'; import { getContentStylesheetLink } from '@sitecore-content-sdk/content/layout'; import { getDesignLibraryStylesheetLinks } from '@sitecore-content-sdk/react'; import { getEdgeProxyContentUrl } from '@sitecore-content-sdk/content/client'; +import { getFieldMeta } from '@sitecore-content-sdk/react'; import { getFieldValue } from '@sitecore-content-sdk/content/layout'; import { getGroomedVariantIds } from '@sitecore-content-sdk/content/personalize'; import { getPersonalizedRewrite } from '@sitecore-content-sdk/content/personalize'; @@ -88,6 +103,8 @@ import { GraphQLRequestClientFactoryConfig } from '@sitecore-content-sdk/content import { HTMLLink } from '@sitecore-content-sdk/content'; import { Image as Image_2 } from '@sitecore-content-sdk/react'; import { ImageField } from '@sitecore-content-sdk/react'; +import { ImageFieldSchema } from '@sitecore-content-sdk/react'; +import { imageFieldSchema } from '@sitecore-content-sdk/react'; import { ImageFieldValue } from '@sitecore-content-sdk/react'; import { ImageProps } from '@sitecore-content-sdk/react'; import { ImageProps as ImageProps_2 } from 'next/image'; @@ -106,6 +123,8 @@ import { LayoutServiceContextData } from '@sitecore-content-sdk/content/layout'; import { LayoutServiceData } from '@sitecore-content-sdk/content/layout'; import { LayoutServicePageState } from '@sitecore-content-sdk/content/layout'; import { LinkField } from '@sitecore-content-sdk/react'; +import { LinkFieldSchema } from '@sitecore-content-sdk/react'; +import { linkFieldSchema } from '@sitecore-content-sdk/react'; import { LinkFieldValue } from '@sitecore-content-sdk/react'; import { LinkProps as LinkProps_2 } from '@sitecore-content-sdk/react'; import { LinkProps as LinkProps_3 } from 'next/link'; @@ -135,6 +154,7 @@ import { PlaceholderComponentProps } from '@sitecore-content-sdk/react'; import { PlaceholderData } from '@sitecore-content-sdk/content/layout'; import { PlaceholdersData } from '@sitecore-content-sdk/content/layout'; import { PreviewData } from 'next'; +import { PropMeta } from '@sitecore-content-sdk/react'; import { default as React_2 } from 'react'; import { ReactContentSdkComponent } from '@sitecore-content-sdk/react'; import { ReactNode } from 'react'; @@ -150,6 +170,8 @@ import { resetEditorChromes } from '@sitecore-content-sdk/content/editing'; import { resolveUrl } from '@sitecore-content-sdk/core/tools'; import { RetryStrategy } from '@sitecore-content-sdk/content/client'; import { RichTextField } from '@sitecore-content-sdk/react'; +import { RichTextFieldSchema } from '@sitecore-content-sdk/react'; +import { richTextFieldSchema } from '@sitecore-content-sdk/react'; import { RichTextProps as RichTextProps_2 } from '@sitecore-content-sdk/react'; import { RobotsQueryResult } from '@sitecore-content-sdk/content/site'; import { RobotsService } from '@sitecore-content-sdk/content/site'; @@ -175,13 +197,17 @@ import { SiteResolver } from '@sitecore-content-sdk/content/site'; import { StaticPath } from '@sitecore-content-sdk/content'; import { Text as Text_2 } from '@sitecore-content-sdk/react'; import { TextField } from '@sitecore-content-sdk/react'; +import { TextFieldSchema } from '@sitecore-content-sdk/react'; +import { textFieldSchema } from '@sitecore-content-sdk/react'; import { useSitecore } from '@sitecore-content-sdk/react'; import { withAppPlaceholder } from '@sitecore-content-sdk/react'; +import { withArgMeta } from '@sitecore-content-sdk/react'; import { withDatasourceCheck } from '@sitecore-content-sdk/react'; import { withEditorChromes } from '@sitecore-content-sdk/react'; import { withEmptyFieldEditingComponent } from '@sitecore-content-sdk/react'; import { withFieldMetadata } from '@sitecore-content-sdk/react'; import { withPlaceholder } from '@sitecore-content-sdk/react'; +import { withPropMeta } from '@sitecore-content-sdk/react'; import { withSitecore } from '@sitecore-content-sdk/react'; import { WriteImportMapArgs } from '@sitecore-content-sdk/content/node-tools'; @@ -216,6 +242,16 @@ export class AppRouterMultisiteProxy extends MultisiteProxy { protected shouldWarnWhenDisabled(_res: NextResponse): void; } +export { ArgMeta } + +export { AtomChild } + +export { AtomMetadata } + +export { AtomSchemaInput } + +export { AtomType } + export { BYOCClientWrapper } export { BYOCComponent } @@ -244,6 +280,10 @@ export type CachedPageParams = { export { CacheOptions } +export { CallbackArgZodTuple } + +export { CallbackPropKeys } + export { CdpHelper } export { ClientEditingChromesUpdate } @@ -308,6 +348,8 @@ export { ComponentRendering } export { constants } +export { createAtom } + // Warning: (ae-forgotten-export) The symbol "EditingConfigRouteHandlerOptions" needs to be exported by the entry point api-surface.d.ts // // @public @@ -343,10 +385,16 @@ export function createSitemapRouteHandler(options: RouteHandlerOptions): { export { DateField } +export { DateFieldSchema } + +export { dateFieldSchema } + // @public const debug_2: Record; export { debug_2 as debug } +export { DefaultChild } + export { DefaultEmptyFieldEditingComponentImage } export { DefaultEmptyFieldEditingComponentText } @@ -383,6 +431,8 @@ export { DictionaryService } export { DictionaryServiceConfig } +export { EditableComponentProps } + export { EDITING_COMPONENT_ID } export { EDITING_COMPONENT_PLACEHOLDER } @@ -479,6 +529,10 @@ export { File_2 as File } export { FileField } +export { FileFieldSchema } + +export { fileFieldSchema } + export { Form } // @public @@ -519,6 +573,8 @@ export { getDesignLibraryStylesheetLinks } export { getEdgeProxyContentUrl } +export { getFieldMeta } + export { getFieldValue } export { getGroomedVariantIds } @@ -565,6 +621,10 @@ export { Image_2 as Image } export { ImageField } +export { ImageFieldSchema } + +export { imageFieldSchema } + export { ImageFieldValue } export { ImageProps } @@ -602,6 +662,10 @@ export const Link: React_2.ForwardRefExoticComponent & Re export { LinkField } +export { LinkFieldSchema } + +export { linkFieldSchema } + export { LinkFieldValue } // Warning: (ae-forgotten-export) The symbol "supportedNextLinkProps" needs to be exported by the entry point api-surface.d.ts @@ -759,6 +823,8 @@ export { PlaceholderData } export { PlaceholdersData } +export { PropMeta } + // @public export abstract class ProxyBase extends ProxyHandler_2 { constructor(config: ProxyBaseConfig); @@ -852,6 +918,10 @@ export const RichText: { export { RichTextField } +export { RichTextFieldSchema } + +export { richTextFieldSchema } + // @public export type RichTextProps = RichTextProps_2 & { internalLinksSelector?: string; @@ -953,6 +1023,10 @@ export { Text_2 as Text } export { TextField } +export { TextFieldSchema } + +export { textFieldSchema } + // @public export function useComponentProps(componentUid: string | undefined): ComponentData | undefined; @@ -960,6 +1034,8 @@ export { useSitecore } export { withAppPlaceholder } +export { withArgMeta } + export { withDatasourceCheck } export { withEditorChromes } @@ -970,6 +1046,8 @@ export { withFieldMetadata } export { withPlaceholder } +export { withPropMeta } + export { withSitecore } // @public diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 1aca4436dd..bd399a9f83 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -177,6 +177,18 @@ export { type PropMeta, type ArgMeta, type AtomSchemaInput, + textFieldSchema, + richTextFieldSchema, + dateFieldSchema, + linkFieldSchema, + imageFieldSchema, + fileFieldSchema, + type TextFieldSchema, + type RichTextFieldSchema, + type DateFieldSchema, + type LinkFieldSchema, + type ImageFieldSchema, + type FileFieldSchema, } from '@sitecore-content-sdk/react'; export { initContentSdk } from '@sitecore-content-sdk/core'; diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 646a0f3277..6fe6080835 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -200,6 +200,14 @@ export interface DateFieldProps extends EditableFieldProps { tag?: string; } +// @public +export type DateFieldSchema = z.infer>; + +// @public +export const dateFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodOptional; +}, z.core.$strip>; + export { debug_2 as debug } // @public @@ -339,6 +347,18 @@ export interface FileField { value: FileFieldValue; } +// @public +export type FileFieldSchema = z.infer>; + +// @public +export const fileFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodObject<{ + src: z.ZodOptional; + title: z.ZodOptional; + displayName: z.ZodOptional; + }, z.core.$loose>; +}, z.core.$strip>; + // Warning: (ae-forgotten-export) The symbol "FormProps" needs to be exported by the entry point api-surface.d.ts // // @public @@ -371,6 +391,20 @@ export interface ImageField { value?: ImageFieldValue; } +// @public +export type ImageFieldSchema = z.infer>; + +// @public +export const imageFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodOptional; + alt: z.ZodOptional; + width: z.ZodOptional>; + height: z.ZodOptional>; + class: z.ZodOptional; + }, z.core.$loose>>; +}, z.core.$strip>; + // @public export interface ImageFieldValue { // (undocumented) @@ -433,6 +467,24 @@ export interface LinkField { value: LinkFieldValue; } +// @public +export type LinkFieldSchema = z.infer>; + +// @public +export const linkFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodObject<{ + href: z.ZodOptional; + className: z.ZodOptional; + class: z.ZodOptional; + title: z.ZodOptional; + target: z.ZodOptional; + text: z.ZodOptional; + anchor: z.ZodOptional; + querystring: z.ZodOptional; + linktype: z.ZodOptional; + }, z.core.$loose>; +}, z.core.$strip>; + // @public export interface LinkFieldValue { // (undocumented) @@ -549,6 +601,14 @@ export interface RichTextField extends FieldMetadata { value?: string; } +// @public +export type RichTextFieldSchema = z.infer>; + +// @public +export const richTextFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodOptional; +}, z.core.$strip>; + // @public export interface RichTextProps extends EditableFieldProps { // (undocumented) @@ -602,6 +662,14 @@ export interface TextField extends FieldMetadata { value?: string | number; } +// @public +export type TextFieldSchema = z.infer>; + +// @public +export const textFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ + value: z.ZodOptional>; +}, z.core.$strip>; + // @public export const useInfiniteSearch: (options: UseInfiniteSearchOptions) => UseInfiniteSearchState; diff --git a/packages/react/src/atoms/field-schemas.test.ts b/packages/react/src/atoms/field-schemas.test.ts new file mode 100644 index 0000000000..1cef6bd015 --- /dev/null +++ b/packages/react/src/atoms/field-schemas.test.ts @@ -0,0 +1,231 @@ +import { expect } from 'chai'; +import { z } from 'zod'; +import { getFieldMeta } from './schema-utils'; +import { createAtom } from './createAtom'; +import { + textFieldSchema, + richTextFieldSchema, + dateFieldSchema, + linkFieldSchema, + imageFieldSchema, + fileFieldSchema, + type TextFieldSchema, + type RichTextFieldSchema, + type DateFieldSchema, + type LinkFieldSchema, + type ImageFieldSchema, + type FileFieldSchema, +} from './field-schemas'; + +describe('field-schemas', () => { + const factories = [ + { name: 'textFieldSchema', factory: textFieldSchema, control: 'Single-Line Text' }, + { name: 'richTextFieldSchema', factory: richTextFieldSchema, control: 'Rich Text' }, + { name: 'dateFieldSchema', factory: dateFieldSchema, control: 'Date' }, + { name: 'linkFieldSchema', factory: linkFieldSchema, control: 'Link' }, + { name: 'imageFieldSchema', factory: imageFieldSchema, control: 'Image' }, + { name: 'fileFieldSchema', factory: fileFieldSchema, control: 'File' }, + ]; + + factories.forEach(({ name, factory, control }) => { + describe(name, () => { + it('returns a ZodObject', () => { + expect(factory()).to.be.instanceOf(z.ZodObject); + }); + + it(`has control hint "${control}"`, () => { + const meta = getFieldMeta(factory()); + expect(meta).to.deep.equal({ control }); + }); + + it('preserves control hint when extra shape is provided', () => { + const meta = getFieldMeta(factory({ extra: z.string() })); + expect(meta).to.deep.equal({ control }); + }); + + it('includes extra shape in the schema', () => { + const schema = factory({ extra: z.string() }); + expect(schema.shape).to.have.property('extra'); + }); + }); + }); + + describe('textFieldSchema', () => { + it('parses a valid text field', () => { + expect(() => textFieldSchema().parse({ value: 'hello' })).not.to.throw(); + }); + + it('parses a numeric value', () => { + expect(() => textFieldSchema().parse({ value: 42 })).not.to.throw(); + }); + + it('parses with no value', () => { + expect(() => textFieldSchema().parse({})).not.to.throw(); + }); + }); + + describe('richTextFieldSchema', () => { + it('parses a valid rich text field', () => { + expect(() => richTextFieldSchema().parse({ value: '

hello

' })).not.to.throw(); + }); + + it('parses with no value', () => { + expect(() => richTextFieldSchema().parse({})).not.to.throw(); + }); + }); + + describe('dateFieldSchema', () => { + it('parses a valid date field', () => { + expect(() => dateFieldSchema().parse({ value: '20231025T120000Z' })).not.to.throw(); + }); + + it('parses with no value', () => { + expect(() => dateFieldSchema().parse({})).not.to.throw(); + }); + }); + + describe('linkFieldSchema', () => { + it('parses a valid link field', () => { + expect(() => + linkFieldSchema().parse({ value: { href: '/about', text: 'About', target: '_blank' } }) + ).not.to.throw(); + }); + + it('parses an empty value object', () => { + expect(() => linkFieldSchema().parse({ value: {} })).not.to.throw(); + }); + + it('passes through unknown attributes on the value', () => { + const result = linkFieldSchema().parse({ + value: { href: '/', 'data-custom': 'foo' }, + }) as LinkFieldSchema & { value: Record }; + expect(result.value['data-custom']).to.equal('foo'); + }); + }); + + describe('imageFieldSchema', () => { + it('parses a valid image field', () => { + expect(() => + imageFieldSchema().parse({ + value: { src: '/img.png', alt: 'An image', width: 100, height: 200 }, + }) + ).not.to.throw(); + }); + + it('parses with no value', () => { + expect(() => imageFieldSchema().parse({})).not.to.throw(); + }); + + it('passes through unknown attributes on the value', () => { + const result = imageFieldSchema().parse({ + value: { src: '/img.png', 'data-id': '1' }, + }) as ImageFieldSchema & { value: Record }; + expect(result.value['data-id']).to.equal('1'); + }); + }); + + describe('fileFieldSchema', () => { + it('parses a valid file field', () => { + expect(() => + fileFieldSchema().parse({ value: { src: '/doc.pdf', title: 'My Doc', displayName: 'doc' } }) + ).not.to.throw(); + }); + + it('parses an empty value object', () => { + expect(() => fileFieldSchema().parse({ value: {} })).not.to.throw(); + }); + + it('passes through unknown properties on the value', () => { + const result = fileFieldSchema().parse({ + value: { src: '/doc.pdf', customProp: 'bar' }, + }) as FileFieldSchema & { value: Record }; + expect(result.value.customProp).to.equal('bar'); + }); + }); + + describe('createAtom integration', () => { + const CardComponent = (props: { + title?: TextFieldSchema; + body?: RichTextFieldSchema; + cta?: LinkFieldSchema; + image?: ImageFieldSchema; + doc?: FileFieldSchema; + publishedAt?: DateFieldSchema; + }) => { + void props; + return null; + }; + + it('accepts textFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { title: textFieldSchema() }, + }); + expect(meta.props).to.be.instanceOf(z.ZodObject); + const parsed = meta.props.safeParse({ title: { value: 'Hello' } }); + expect(parsed.success).to.equal(true); + }); + + it('accepts richTextFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { body: richTextFieldSchema() }, + }); + const parsed = meta.props.safeParse({ body: { value: '

content

' } }); + expect(parsed.success).to.equal(true); + }); + + it('accepts linkFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { cta: linkFieldSchema() }, + }); + const parsed = meta.props.safeParse({ cta: { value: { href: '/about', text: 'About' } } }); + expect(parsed.success).to.equal(true); + }); + + it('accepts imageFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { image: imageFieldSchema() }, + }); + const parsed = meta.props.safeParse({ image: { value: { src: '/img.png', alt: 'Alt' } } }); + expect(parsed.success).to.equal(true); + }); + + it('accepts fileFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { doc: fileFieldSchema() }, + }); + const parsed = meta.props.safeParse({ doc: { value: { src: '/file.pdf', title: 'Doc' } } }); + expect(parsed.success).to.equal(true); + }); + + it('accepts dateFieldSchema as a prop schema in createAtom', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { publishedAt: dateFieldSchema() }, + }); + const parsed = meta.props.safeParse({ publishedAt: { value: '20240101T000000Z' } }); + expect(parsed.success).to.equal(true); + }); + + it('preserves control hint on field schemas inside createAtom props', () => { + const meta = createAtom(CardComponent, { + name: 'Card', + description: 'A card atom', + props: { cta: linkFieldSchema() }, + }); + const ctaShape = meta.props.shape.cta as z.ZodType; + expect(getFieldMeta(ctaShape)).to.deep.equal({ control: 'Link' }); + }); + }); +}); + diff --git a/packages/react/src/atoms/field-schemas.ts b/packages/react/src/atoms/field-schemas.ts new file mode 100644 index 0000000000..ed9a7a227d --- /dev/null +++ b/packages/react/src/atoms/field-schemas.ts @@ -0,0 +1,177 @@ +/** Zod schemas for Sitecore field types, for use in createAtom prop definitions. */ +import { z } from 'zod'; +import { withPropMeta } from './schema-utils'; + +/** + * Zod schema for a Sitecore Single-Line Text or Multi-Line Text field. + * Mirrors {@link [Text Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Text.tsx)}. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. + * @returns A ZodObject with `value?: string | number` and the DS control hint attached. + * @public + */ +export const textFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z.union([z.string(), z.number()]).optional(), + ...extra, + }), + { control: 'Single-Line Text' } + ); + +/** + * Zod schema for a Sitecore Rich Text field. + * Mirrors {@link [Rich Text Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/RichText.tsx)}. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. + * @returns A ZodObject with `value?: string` and the DS control hint attached. + * @public + */ +export const richTextFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z.string().optional(), + ...extra, + }), + { control: 'Rich Text' } + ); + +/** + * Zod schema for a Sitecore Date field. + * Mirrors the field shape used in {@link [Date Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Date.tsx)}. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. + * @returns A ZodObject with `value?: string` and the DS control hint attached. + * @public + */ +export const dateFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z.string().optional(), + ...extra, + }), + { control: 'Date' } + ); + +/** + * Zod schema for a Sitecore Link field. + * Mirrors {@link [Link Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Link.tsx)}. + * The inner value object uses `z.looseObject` to allow arbitrary Sitecore-added attributes, + * matching the `[attributeName: string]: unknown` index signature on `LinkFieldValue`. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. + * @returns A ZodObject with `value: LinkFieldValue` and the DS control hint attached. + * @public + */ +export const linkFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z.looseObject({ + href: z.string().optional(), + className: z.string().optional(), + class: z.string().optional(), + title: z.string().optional(), + target: z.string().optional(), + text: z.string().optional(), + anchor: z.string().optional(), + querystring: z.string().optional(), + linktype: z.string().optional(), + }), + ...extra, + }), + { control: 'Link' } + ); + +/** + * Zod schema for a Sitecore Image field. + * Mirrors {@link [Image Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Image.tsx)}. + * The inner value object uses `z.looseObject` to allow arbitrary HTML attributes, + * matching the `[attributeName: string]: unknown` index signature on `ImageFieldValue`. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. + * @returns A ZodObject with `value?: ImageFieldValue` and the DS control hint attached. + * @public + */ +export const imageFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z + .looseObject({ + src: z.string().optional(), + alt: z.string().optional(), + width: z.union([z.string(), z.number()]).optional(), + height: z.union([z.string(), z.number()]).optional(), + class: z.string().optional(), + }) + .optional(), + ...extra, + }), + { control: 'Image' } + ); + +/** + * Zod schema for a Sitecore File field. + * Mirrors {@link [File Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/File.tsx)}. + * The inner value object uses `z.looseObject` to allow arbitrary extra properties, + * matching the `[propName: string]: unknown` index signature on `FileFieldValue`. + * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. + * @returns A ZodObject with `value: FileFieldValue` and the DS control hint attached. + * @public + */ +export const fileFieldSchema = (extra?: z.ZodRawShape) => + withPropMeta( + z.object({ + value: z.looseObject({ + src: z.string().optional(), + title: z.string().optional(), + displayName: z.string().optional(), + }), + ...extra, + }), + { control: 'File' } + ); + +/** + * Inferred type for a Sitecore Single-Line Text / Multi-Line Text field prop. + * Use this to type component props that accept a text field. + * @example + * const MyComponent = (props: { title: TextFieldSchema }) => ... + * @public + */ +export type TextFieldSchema = z.infer>; + +/** + * Inferred type for a Sitecore Rich Text field prop. + * @example + * const MyComponent = (props: { body: RichTextFieldSchema }) => ... + * @public + */ +export type RichTextFieldSchema = z.infer>; + +/** + * Inferred type for a Sitecore Date field prop. + * @example + * const MyComponent = (props: { publishedAt: DateFieldSchema }) => ... + * @public + */ +export type DateFieldSchema = z.infer>; + +/** + * Inferred type for a Sitecore General Link field prop. + * @example + * const MyComponent = (props: { cta: LinkFieldSchema }) => ... + * @public + */ +export type LinkFieldSchema = z.infer>; + +/** + * Inferred type for a Sitecore Image field prop. + * @example + * const MyComponent = (props: { image: ImageFieldSchema }) => ... + * @public + */ +export type ImageFieldSchema = z.infer>; + +/** + * Inferred type for a Sitecore File field prop. + * @example + * const MyComponent = (props: { doc: FileFieldSchema }) => ... + * @public + */ +export type FileFieldSchema = z.infer>; + diff --git a/packages/react/src/atoms/index.ts b/packages/react/src/atoms/index.ts index 6f59f6b20b..60d5714326 100644 --- a/packages/react/src/atoms/index.ts +++ b/packages/react/src/atoms/index.ts @@ -16,3 +16,17 @@ export type { export { withPropMeta, withArgMeta, getFieldMeta } from './schema-utils'; export { createAtom, type AtomSchemaInput } from './createAtom'; export { createCallback, type CallbackSchemaInput } from './createCallback'; +export { + textFieldSchema, + richTextFieldSchema, + dateFieldSchema, + linkFieldSchema, + imageFieldSchema, + fileFieldSchema, + type TextFieldSchema, + type RichTextFieldSchema, + type DateFieldSchema, + type LinkFieldSchema, + type ImageFieldSchema, + type FileFieldSchema, +} from './field-schemas'; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 349475a5e1..893b22d742 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -128,4 +128,16 @@ export { type PropMeta, type ArgMeta, type AtomSchemaInput, + textFieldSchema, + richTextFieldSchema, + dateFieldSchema, + linkFieldSchema, + imageFieldSchema, + fileFieldSchema, + type TextFieldSchema, + type RichTextFieldSchema, + type DateFieldSchema, + type LinkFieldSchema, + type ImageFieldSchema, + type FileFieldSchema, } from './atoms'; From 1aa7c1fdd4c55f48b1c89da731527c930766797e Mon Sep 17 00:00:00 2001 From: Illia Kovalenko <23364749+illiakovalenko@users.noreply.github.com> Date: Tue, 5 May 2026 14:44:13 +0300 Subject: [PATCH 08/32] [Design Studio] Support Atom-based code generation (#465) * [Design Studio] Support Atom-based code generation * Updated Changelog --- CHANGELOG.md | 4 +- .../content/api/content-sdk-content.api.md | 4 +- .../src/client/sitecore-client.test.ts | 29 ++- .../content/src/client/sitecore-client.ts | 10 +- packages/content/src/editing/models.ts | 4 +- .../DesignLibrary/DesignLibraryApp.test.tsx | 22 +- .../DesignLibrary/DesignLibraryApp.tsx | 10 +- packages/react/api/content-sdk-react.api.md | 6 +- .../DesignLibrary/DesignLibrary.test.tsx | 236 +++++++++++++++++- .../DesignLibrary/DesignLibrary.tsx | 13 +- ...=> DesignLibraryLowCodeComponent.test.tsx} | 40 +-- ....tsx => DesignLibraryLowCodeComponent.tsx} | 8 +- .../src/components/DesignLibrary/index.ts | 2 +- packages/react/src/index.ts | 2 +- 14 files changed, 314 insertions(+), 76 deletions(-) rename packages/react/src/components/DesignLibrary/{DesignLibraryAtoms.test.tsx => DesignLibraryLowCodeComponent.test.tsx} (95%) rename packages/react/src/components/DesignLibrary/{DesignLibraryAtoms.tsx => DesignLibraryLowCodeComponent.tsx} (92%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6990d8e25e..d1849d6b94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,9 @@ Our versioning strategy is as follows: - Introduced the ability to create a callback registry ([#422](https://github.com/Sitecore/content-sdk/pull/422)) - Introduce the component layout Document contract and ability to render it; set up Design Studio integration ([#423](https://github.com/Sitecore/content-sdk/pull/423)) - Introduce Zod Schemas for Content SDK Fields ([#450](https://github.com/Sitecore/content-sdk/pull/450)) - - Introduce `library-atoms` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) + - Introduce `low-code` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) + - Support Atom-based code generation ([#465](https://github.com/Sitecore/content-sdk/pull/465)) + ### 🐛 Bug Fixes diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 5fb45c2043..287caf1008 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -265,7 +265,7 @@ export type DesignLibraryAtomsError = 'render' | 'atoms-missing'; // @public export enum DesignLibraryMode { - Atoms = "library-atoms", + LowCode = "library-low-code", Metadata = "library-metadata", Normal = "library" } @@ -966,7 +966,7 @@ export type PageMode = { name: PageModeName; designLibrary: { isVariantGeneration: boolean; - isAtomsMode?: boolean; + isLowCode: boolean; }; isNormal: boolean; isPreview: boolean; diff --git a/packages/content/src/client/sitecore-client.test.ts b/packages/content/src/client/sitecore-client.test.ts index c3f4aaada2..dc8d960f49 100644 --- a/packages/content/src/client/sitecore-client.test.ts +++ b/packages/content/src/client/sitecore-client.test.ts @@ -365,6 +365,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -409,6 +410,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -607,6 +609,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -652,6 +655,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -699,6 +703,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -886,6 +891,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -937,6 +943,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -1111,6 +1118,7 @@ describe('SitecoreClient', () => { isEditing: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, }, }); @@ -1172,6 +1180,7 @@ describe('SitecoreClient', () => { isEditing: true, designLibrary: { isVariantGeneration: true, + isLowCode: false, }, }, }); @@ -1261,7 +1270,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, }); @@ -1273,7 +1282,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, }); @@ -1285,7 +1294,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, }); @@ -1297,7 +1306,7 @@ describe('SitecoreClient', () => { isDesignLibrary: true, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, }); @@ -1305,7 +1314,7 @@ describe('SitecoreClient', () => { name: DesignLibraryMode.Metadata, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, isNormal: false, isPreview: false, @@ -1317,7 +1326,7 @@ describe('SitecoreClient', () => { name: DesignLibraryMode.Normal, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, isNormal: false, isPreview: false, @@ -1325,15 +1334,15 @@ describe('SitecoreClient', () => { isDesignLibrary: true, }); - expect(sitecoreClient['getPageMode'](DesignLibraryMode.Atoms)).to.deep.equal({ - name: DesignLibraryMode.Atoms, + expect(sitecoreClient['getPageMode'](DesignLibraryMode.LowCode)).to.deep.equal({ + name: DesignLibraryMode.LowCode, isNormal: false, isPreview: false, isEditing: false, isDesignLibrary: true, designLibrary: { isVariantGeneration: false, - isAtomsMode: true, + isLowCode: true, }, }); @@ -1345,7 +1354,7 @@ describe('SitecoreClient', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, - isAtomsMode: false, + isLowCode: false, }, }); }); diff --git a/packages/content/src/client/sitecore-client.ts b/packages/content/src/client/sitecore-client.ts index 88be78b22f..e5cd37d5b9 100644 --- a/packages/content/src/client/sitecore-client.ts +++ b/packages/content/src/client/sitecore-client.ts @@ -75,9 +75,9 @@ export type PageMode = { */ isVariantGeneration: boolean; /** - * Whether the page is in atoms editing mode + * Whether the page is in low code component editing mode */ - isAtomsMode?: boolean; + isLowCode: boolean; }; /** * Whether the page is in normal mode @@ -751,7 +751,7 @@ export class SitecoreClient implements BaseSitecoreClient { isPreview: false, isEditing: false, isDesignLibrary: false, - designLibrary: { isVariantGeneration: false, isAtomsMode: false }, + designLibrary: { isVariantGeneration: false, isLowCode: false }, }; switch (mode) { @@ -775,9 +775,9 @@ export class SitecoreClient implements BaseSitecoreClient { pageMode.isDesignLibrary = true; pageMode.isEditing = true; break; - case DesignLibraryMode.Atoms: + case DesignLibraryMode.LowCode: pageMode.isDesignLibrary = true; - pageMode.designLibrary.isAtomsMode = true; + pageMode.designLibrary.isLowCode = true; break; default: diff --git a/packages/content/src/editing/models.ts b/packages/content/src/editing/models.ts index 1e2744f939..7d56d2af16 100644 --- a/packages/content/src/editing/models.ts +++ b/packages/content/src/editing/models.ts @@ -82,8 +82,8 @@ export enum DesignLibraryMode { Normal = 'library', /** Metadata mode */ Metadata = 'library-metadata', - /** Atoms mode */ - Atoms = 'library-atoms', + /** Low code mode */ + LowCode = 'library-low-code', } /** diff --git a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx index 32cf3a4f5c..55f514b86b 100644 --- a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx +++ b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.test.tsx @@ -18,7 +18,7 @@ use(sinonChai); describe('', () => { let DesignLibraryStub: sinon.SinonStub; - let DesignLibraryAtomsStub: sinon.SinonStub; + let DesignLibraryLowCodeComponentStub: sinon.SinonStub; let DesignLibraryServerStub: sinon.SinonStub; let DesignLibraryApp: any; const sandbox = sinon.createSandbox(); @@ -28,9 +28,9 @@ describe('', () => { .stub() .returns(
Client Component Rendered
); - DesignLibraryAtomsStub = sandbox + DesignLibraryLowCodeComponentStub = sandbox .stub() - .returns(
Atoms Component Rendered
); + .returns(
Low Code Component Rendered
); DesignLibraryServerStub = sandbox .stub() @@ -40,7 +40,7 @@ describe('', () => { const module = proxyquire('./DesignLibraryApp', { '@sitecore-content-sdk/react': { DesignLibrary: DesignLibraryStub, - DesignLibraryAtoms: DesignLibraryAtomsStub, + DesignLibraryLowCodeComponent: DesignLibraryLowCodeComponentStub, }, './DesignLibraryServer': { DesignLibraryServer: DesignLibraryServerStub, @@ -57,15 +57,15 @@ describe('', () => { const modeLibrary: PageMode = { name: DesignLibraryMode.Normal, isDesignLibrary: true, - designLibrary: { isVariantGeneration: false, isAtomsMode: false }, + designLibrary: { isVariantGeneration: false, isLowCode: false }, isNormal: false, isPreview: false, isEditing: true, }; - const modeLibraryAtoms: PageMode = { + const modeLibraryLowCode: PageMode = { ...modeLibrary, - designLibrary: { ...modeLibrary.designLibrary, isAtomsMode: true }, + designLibrary: { ...modeLibrary.designLibrary, isLowCode: true }, }; const ClientComponent: React.FC<{ [prop: string]: unknown }> = () =>
Client Component
; @@ -126,13 +126,13 @@ describe('', () => { render(awaitedDesignLibraryServer); expect(DesignLibraryStub).to.have.been.calledOnce; - expect(DesignLibraryAtomsStub).to.not.have.been.called; + expect(DesignLibraryLowCodeComponentStub).to.not.have.been.called; expect(DesignLibraryServerStub).to.not.have.been.called; }); - it('should render DesignLibraryAtoms when isAtomsMode is true', () => { + it('should render DesignLibraryLowCodeComponent when isLowCode is true', () => { const layoutData: LayoutServiceData = getTestLayoutData().layoutData; - const page = getPage(layoutData, modeLibraryAtoms); + const page = getPage(layoutData, modeLibraryLowCode); const componentMap = createComponentMap('ContentBlock', 'client'); const awaitedDesignLibraryServer = DesignLibraryApp({ @@ -144,7 +144,7 @@ describe('', () => { render(awaitedDesignLibraryServer); - expect(DesignLibraryAtomsStub).to.have.been.calledOnce; + expect(DesignLibraryLowCodeComponentStub).to.have.been.calledOnce; expect(DesignLibraryStub).to.not.have.been.called; expect(DesignLibraryServerStub).to.not.have.been.called; }); diff --git a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx index 0172a17b89..91d3b0c2fb 100644 --- a/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx +++ b/packages/nextjs/src/components/DesignLibrary/DesignLibraryApp.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { EDITING_COMPONENT_PLACEHOLDER } from '@sitecore-content-sdk/content/layout'; import { DesingLibraryAppProps } from './models'; import { DesignLibraryServer } from './DesignLibraryServer'; -import { DesignLibrary, DesignLibraryAtoms } from '@sitecore-content-sdk/react'; +import { DesignLibrary, DesignLibraryLowCodeComponent } from '@sitecore-content-sdk/react'; /** * Design Library component intended to be used by the NextJs app router application @@ -11,7 +11,7 @@ import { DesignLibrary, DesignLibraryAtoms } from '@sitecore-content-sdk/react'; * delegates to the appropriate rendering implementation: * - Client components are rendered using the `DesignLibrary` component * - Server components are rendered using the `DesignLibraryServer` component - * - Atoms mode components are rendered using the `DesignLibraryAtoms` component + * - Low code components are rendered using the `DesignLibraryLowCodeComponent` component * @param {DesingLibraryAppProps} props - The properties for the Design Library App. * @public */ @@ -23,15 +23,15 @@ export const DesignLibraryApp = ({ const { route } = page.layout.sitecore; if (!route) return null; - const isAtomsMode = page.mode.designLibrary.isAtomsMode; + const isLowCode = page.mode.designLibrary.isLowCode; const rendering = route?.placeholders[EDITING_COMPONENT_PLACEHOLDER]?.[0]; const component = componentMap.get(rendering?.componentName || ''); const isClient = component && component.componentType === 'client'; return ( <> - {isAtomsMode ? ( - + {isLowCode ? ( + ) : isClient ? ( ) : ( diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 6fe6080835..1a8ba9495f 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -233,9 +233,6 @@ export { DefaultRetryStrategy } // @public export const DesignLibrary: () => React_2.JSX.Element | null; -// @internal -export const DesignLibraryAtoms: () => React_2.JSX.Element; - // Warning: (ae-forgotten-export) The symbol "DesignLibraryErrorBoundaryProps" needs to be exported by the entry point api-surface.d.ts // // @internal @@ -256,6 +253,9 @@ export class DesignLibraryErrorBoundary extends React_2.Component React_2.JSX.Element; + export { DictionaryPhrases } export { DictionaryService } diff --git a/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx b/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx index e5261aa983..e685c2d5d0 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx @@ -21,13 +21,18 @@ import { DesignLibraryStatus, getDesignLibraryStatusEvent, DesignLibraryMode, + getDesignLibraryAtomsRegistryEvent, + AtomInfo, } from '@sitecore-content-sdk/content/editing'; import { __mockDependencies } from './DesignLibrary'; import { DesignLibraryPreviewError, getDesignLibraryErrorEvent, } from '@sitecore-content-sdk/content/codegen'; -import { after } from 'node:test'; +import { z } from 'zod'; +import * as atomRegistryUtils from '../../atoms/atom-registry-utils'; +import * as callbackRegistryUtils from '../../atoms/callback-registry-utils'; +import { AtomMetadata, CallbackMetadata } from '../../atoms/types'; import * as rscUtils from '#rsc-env'; before(() => { @@ -37,9 +42,6 @@ before(() => { }); describe('', () => { - after(() => { - sandbox.restore(); - }); const sandbox = sinon.createSandbox(); before(() => { sandbox.replace(rscUtils, 'rsc', false as any); @@ -57,10 +59,19 @@ describe('', () => { }; // Modes + const defaultDesignLibraryPageMode: PageMode = { + name: DesignLibraryMode.Normal, + isDesignLibrary: true, + designLibrary: { isVariantGeneration: false, isLowCode: false }, + isNormal: false, + isPreview: false, + isEditing: false, + }; + const modeLibraryMetadata: PageMode = { name: DesignLibraryMode.Metadata, isDesignLibrary: true, - designLibrary: { isVariantGeneration: false }, + designLibrary: { isVariantGeneration: false, isLowCode: false }, isNormal: false, isPreview: false, isEditing: true, @@ -69,7 +80,7 @@ describe('', () => { const modeLibrary_Gen: PageMode = { name: DesignLibraryMode.Normal, isDesignLibrary: true, - designLibrary: { isVariantGeneration: true }, + designLibrary: { isVariantGeneration: true, isLowCode: false }, isNormal: false, isPreview: false, isEditing: false, @@ -78,13 +89,16 @@ describe('', () => { const modeLibraryMetadata_Gen: PageMode = { name: DesignLibraryMode.Metadata, isDesignLibrary: true, - designLibrary: { isVariantGeneration: true }, + designLibrary: { isVariantGeneration: true, isLowCode: false }, isNormal: false, isPreview: false, isEditing: true, }; - const getPage = (layout?: LayoutServiceData, pageMode: PageMode = modeLibrary): Page => ({ + const getPage = ( + layout?: LayoutServiceData, + pageMode: PageMode = defaultDesignLibraryPageMode + ): Page => ({ locale: 'en', layout: layout || { sitecore: { context: {}, route: null } }, mode: pageMode, @@ -124,11 +138,63 @@ describe('', () => { default: [{ module: 'react', exports: [{ name: 'default', value: React }] }], }); + const mockedAtoms: AtomMetadata[] = [ + { + name: 'Button', + type: 'atom', + description: 'A button component', + component: () => React.createElement('button', null, 'Button'), + props: z.object({ + label: z.string(), + }), + }, + { + name: 'Text', + type: 'atom', + description: 'A text component', + component: () => React.createElement('div', null, 'Text'), + props: z.object({ + content: z.string(), + }), + }, + ]; + + const mockedCallbacks: CallbackMetadata[] = [ + { + name: 'trackSelection', + description: 'Track selection callback', + callbackFn: () => {}, + }, + ]; + + const mockedSerializedAtoms: AtomInfo[] = [ + { + name: 'Button', + type: 'atom', + description: 'A button component', + props: { label: { type: 'string' } }, + allowedChildren: [], + }, + { + name: 'Text', + type: 'atom', + description: 'A text component', + props: { content: { type: 'string' } }, + allowedChildren: [], + }, + ]; + + const mockedSerializedCallbacks: Record = { + trackSelection: { description: 'Track selection callback' }, + }; + const unsubscribeSpy = sandbox.spy(); let addComponentPreviewHandlerSpy: sinon.SinonStub; let postToDesignLibrarySpy: sinon.SinonStub; let sendErrorEventSpy: sinon.SinonStub; let callbackEvent: any = null; + let serializeAtomsStub: sinon.SinonStub; + let serializeCallbacksStub: sinon.SinonStub; const RENDER_ID = 'test-content'; const PLACEHOLDER_GUID = '00000000-0000-0000-0000-000000000000'; @@ -204,6 +270,7 @@ describe('', () => { isDesignLibrary: false, designLibrary: { isVariantGeneration: false, + isLowCode: false, }, isNormal: false, isPreview: false, @@ -225,7 +292,7 @@ describe('', () => { const modeLibrary: PageMode = { name: DesignLibraryMode.Normal, isDesignLibrary: true, - designLibrary: { isVariantGeneration: false }, + designLibrary: { isVariantGeneration: false, isLowCode: false }, isNormal: false, isPreview: false, isEditing: false, @@ -423,6 +490,18 @@ describe('', () => { }); postMessageSpy.resetHistory(); + + serializeAtomsStub = sandbox + .stub(atomRegistryUtils, 'serializeAtoms') + .callsFake((atoms) => (!atoms?.length ? [] : mockedSerializedAtoms)); + serializeCallbacksStub = sandbox + .stub(callbackRegistryUtils, 'serializeCallbacks') + .callsFake((callbacks) => (!callbacks?.length ? {} : mockedSerializedCallbacks)); + }); + + afterEach(() => { + serializeAtomsStub.restore(); + serializeCallbacksStub.restore(); }); it('fires component:ready on mount', () => { @@ -634,6 +713,68 @@ describe('', () => { ).to.be.true; }); }); + + it('posts empty atoms registry event when atomRegistry is not provided on SitecoreProvider', async () => { + const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); + const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent([], {}); + + render( + + + + ); + + await waitFor(() => { + expect( + postMessageSpy + .getCalls() + .some((c) => JSON.stringify(c.args[0]) === JSON.stringify(expectedRegistryEvent)) + ).to.be.true; + }); + + sinon.assert.calledWith(serializeAtomsStub, []); + sinon.assert.calledWith(serializeCallbacksStub, []); + }); + + it('posts atoms registry event with serialized atoms and callbacks when atomRegistry is provided', async () => { + const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); + const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent( + mockedSerializedAtoms, + mockedSerializedCallbacks + ); + + render( + + + + ); + + await waitFor(() => { + const postedRegistryEvent = postMessageSpy + .getCalls() + .map((call) => call.args[0]) + .find((event) => event?.name === 'atom:registry'); + + expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); + }); + + sinon.assert.calledWith(serializeAtomsStub, mockedAtoms); + sinon.assert.calledWith(serializeCallbacksStub, mockedCallbacks); + }); }); describe('?mode=library-metadata&generation=variant and isVariantGeneration=true', () => { @@ -647,6 +788,20 @@ describe('', () => { postToDesignLibrary: postToDesignLibrarySpy, sendErrorEvent: sendErrorEventSpy, }); + + postMessageSpy.resetHistory(); + + serializeAtomsStub = sandbox + .stub(atomRegistryUtils, 'serializeAtoms') + .callsFake((atoms) => (!atoms?.length ? [] : mockedSerializedAtoms)); + serializeCallbacksStub = sandbox + .stub(callbackRegistryUtils, 'serializeCallbacks') + .callsFake((callbacks) => (!callbacks?.length ? {} : mockedSerializedCallbacks)); + }); + + afterEach(() => { + serializeAtomsStub.restore(); + serializeCallbacksStub.restore(); }); const expectedGeneratedParts = [ @@ -691,6 +846,69 @@ describe('', () => { await waitFor(() => expectStatus(postMessageSpy, DesignLibraryStatus.RENDERED, RENDER_ID)); }); + + it('posts empty atoms registry event when atomRegistry is not provided on SitecoreProvider', async () => { + const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata_Gen); + const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent([], {}); + + render( + + + + ); + + await waitFor(() => { + const postedRegistryEvent = postMessageSpy + .getCalls() + .map((call) => call.args[0]) + .find((event) => event?.name === 'atom:registry'); + + expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); + }); + + sinon.assert.calledWith(serializeAtomsStub, []); + sinon.assert.calledWith(serializeCallbacksStub, []); + }); + + it('posts atoms registry event with serialized atoms and callbacks when atomRegistry is provided', async () => { + const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata_Gen); + const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent( + mockedSerializedAtoms, + mockedSerializedCallbacks + ); + + render( + + + + ); + + await waitFor(() => { + const postedRegistryEvent = postMessageSpy + .getCalls() + .map((call) => call.args[0]) + .find((event) => event?.name === 'atom:registry'); + + expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); + }); + + sinon.assert.calledWith(serializeAtomsStub, mockedAtoms); + sinon.assert.calledWith(serializeCallbacksStub, mockedCallbacks); + }); }); describe('error handling', () => { diff --git a/packages/react/src/components/DesignLibrary/DesignLibrary.tsx b/packages/react/src/components/DesignLibrary/DesignLibrary.tsx index d496f927e2..9f488ab2c3 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibrary.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibrary.tsx @@ -10,6 +10,7 @@ import { DesignLibraryStatus, getDesignLibraryStatusEvent, addComponentUpdateHandler, + getDesignLibraryAtomsRegistryEvent, } from '@sitecore-content-sdk/content/editing'; import * as codegen from '@sitecore-content-sdk/content/codegen'; import * as editing from '@sitecore-content-sdk/content/editing'; @@ -18,6 +19,8 @@ import { Placeholder, PlaceholderMetadata } from '../Placeholder'; import { DesignLibraryErrorBoundary } from './DesignLibraryErrorBoundary'; import { DynamicComponent } from './models'; import { ErrorComponent } from '../ErrorBoundary'; +import { serializeAtoms } from '../../atoms/atom-registry-utils'; +import { serializeCallbacks } from '../../atoms/callback-registry-utils'; let { getDesignLibraryImportMapEvent, @@ -48,7 +51,7 @@ export const __mockDependencies = (mocks: any) => { * @public */ export const DesignLibrary = () => { - const { page, loadImportMap } = useSitecore(); + const { page, loadImportMap, atomRegistry } = useSitecore(); const route = page.layout.sitecore.route; const rendering = route?.placeholders[EDITING_COMPONENT_PLACEHOLDER]?.[0]; const uid = rendering?.uid; @@ -139,6 +142,12 @@ export const DesignLibrary = () => { const importMapEvent = getDesignLibraryImportMapEvent(uid, importMap); postToDesignLibrary(importMapEvent); + const serializedAtoms = serializeAtoms(atomRegistry?.atoms ?? []); + const serializedCallbacks = serializeCallbacks(atomRegistry?.callbacks ?? []); + + const atomRegistryEvent = getDesignLibraryAtomsRegistryEvent(serializedAtoms, serializedCallbacks); + postToDesignLibrary(atomRegistryEvent); + const propsEvent = getDesignLibraryComponentPropsEvent( uid, propsState.fields, @@ -152,7 +161,7 @@ export const DesignLibrary = () => { cancelled = true; unsubscribe && unsubscribe(); }; - }, [isDesignLibrary, isVariantGeneration, uid, loadImportMap, propsState]); + }, [isDesignLibrary, isVariantGeneration, uid, loadImportMap, propsState, atomRegistry]); return (
diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx similarity index 95% rename from packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx rename to packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx index 0b5b78eef1..0f8b9e275e 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.test.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx @@ -8,7 +8,7 @@ import sinonChai from 'sinon-chai'; chaiUse(sinonChai); import { render, waitFor } from '@testing-library/react'; -import { DesignLibraryAtoms, __mockDependencies } from './DesignLibraryAtoms'; +import { DesignLibraryLowCodeComponent, __mockDependencies } from './DesignLibraryLowCodeComponent'; import { SitecoreProvider } from '../SitecoreProvider'; import { DesignLibraryStatus, @@ -23,7 +23,7 @@ import { AtomMetadata } from '../../atoms/types'; import { z } from 'zod'; import type { ImportMapImport } from './models'; -describe('', () => { +describe('', () => { const sandbox = sinon.createSandbox(); /** Minimal stubs so `SitecoreProvider` matches its public contract in tests */ @@ -146,7 +146,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -166,7 +166,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -185,7 +185,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -207,7 +207,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: [], callbacks: mockCallbacks }} > - + ); @@ -229,7 +229,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -253,7 +253,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -294,7 +294,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -339,7 +339,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -377,7 +377,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -402,7 +402,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -424,7 +424,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -444,7 +444,7 @@ describe('', () => { page={page} atomRegistry={{ callbacks: mockCallbacks }} > - + ); @@ -463,7 +463,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -485,7 +485,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -513,7 +513,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); @@ -563,7 +563,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: customCallbacks }} > - + ); @@ -581,7 +581,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: [] }} > - + ); @@ -605,7 +605,7 @@ describe('', () => { page={page} atomRegistry={{ atoms: mockAtoms, callbacks: mockCallbacks }} > - + ); diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx similarity index 92% rename from packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx rename to packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx index 099a9de4e7..59c0c2d67b 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryAtoms.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx @@ -30,15 +30,15 @@ export const __mockDependencies = (mocks: any) => { }; /** - * Design Library Atoms component. + * Design Library Low Code component. * - * Facilitates the communication between the Design Studio and the Rendering Host when in atom rendering mode. + * Facilitates the communication between the Design Studio and the Rendering Host when previewing a low code component built with the Atoms. * - On mount, it unfolds and serializes the atoms registry and callback registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. * - Receives Component model data updates via document update handler and renders the low code component * based on component model data and the available atoms using createView. * @internal */ -export const DesignLibraryAtoms = () => { +export const DesignLibraryLowCodeComponent = () => { const { atomRegistry } = useSitecore(); const [currentDocument, setCurrentDocument] = useState(null); const [renderKey, setRenderKey] = useState(0); @@ -85,7 +85,7 @@ export const DesignLibraryAtoms = () => { return ( {ViewComponent ? : null} diff --git a/packages/react/src/components/DesignLibrary/index.ts b/packages/react/src/components/DesignLibrary/index.ts index bf0a4177d8..84f5bda114 100644 --- a/packages/react/src/components/DesignLibrary/index.ts +++ b/packages/react/src/components/DesignLibrary/index.ts @@ -1,4 +1,4 @@ export { DesignLibrary } from './DesignLibrary'; -export { DesignLibraryAtoms } from './DesignLibraryAtoms'; +export { DesignLibraryLowCodeComponent } from './DesignLibraryLowCodeComponent'; export { DesignLibraryErrorBoundary } from './DesignLibraryErrorBoundary'; export { DynamicComponent, ImportMapImport } from './models'; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 893b22d742..460d6be48b 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -85,7 +85,7 @@ export { } from './components/FEaaS'; export { DesignLibrary, - DesignLibraryAtoms, + DesignLibraryLowCodeComponent, DesignLibraryErrorBoundary, DynamicComponent, ImportMapImport, From 299b1b87072cb387ba57323a65db9317538e3bd1 Mon Sep 17 00:00:00 2001 From: Yavor Krastev <4502045+yavorsk@users.noreply.github.com> Date: Tue, 5 May 2026 15:32:51 +0300 Subject: [PATCH 09/32] [chore] Atoms release prep for beta.2 (#454) --- packages/analytics-core/package.json | 4 +- packages/cli/package.json | 6 +- packages/content/package.json | 8 +-- packages/core/package.json | 2 +- packages/create-content-sdk-app/package.json | 2 +- packages/events/package.json | 6 +- packages/nextjs/package.json | 18 +++--- packages/personalize/package.json | 8 +-- packages/react/package.json | 12 ++-- packages/search/package.json | 4 +- yarn.lock | 64 ++++++++++---------- 11 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/analytics-core/package.json b/packages/analytics-core/package.json index 521f1738fb..5a9c6653d7 100644 --- a/packages/analytics-core/package.json +++ b/packages/analytics-core/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.2", "debug": "^4.4.3" }, "description": "Provides shared logic and runtime initialization. Required for the Content SDK 'events' and 'personalize' packages to function.", @@ -73,5 +73,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.1" + "version": "2.0.0-beta.2" } diff --git a/packages/cli/package.json b/packages/cli/package.json index c6b0d94c26..479d3c52f9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/cli", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "description": "Sitecore Content SDK CLI", "main": "dist/cjs/cli.js", "module": "dist/esm/cli.js", @@ -34,8 +34,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.1", - "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/content": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.2", "chokidar": "^4.0.3", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", diff --git a/packages/content/package.json b/packages/content/package.json index 1fb55e4234..b32df6340d 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/content", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/events": "2.0.0-beta.1", + "@sitecore-content-sdk/events": "2.0.0-beta.2", "@stylistic/eslint-plugin": "^5.2.2", "@types/chai": "^5.2.2", "@types/chai-spies": "^1.0.6", @@ -74,10 +74,10 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/events": "^2.0.0-beta.1" + "@sitecore-content-sdk/events": "^2.0.0-beta.2" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/core": "2.0.0-beta.2", "chalk": "^4.1.2", "debug": "^4.4.0", "glob": "^11.0.2", diff --git a/packages/core/package.json b/packages/core/package.json index a9d148f210..4e17b2c665 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/core", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/create-content-sdk-app/package.json b/packages/create-content-sdk-app/package.json index b1b75cb002..9c0f69a533 100644 --- a/packages/create-content-sdk-app/package.json +++ b/packages/create-content-sdk-app/package.json @@ -1,6 +1,6 @@ { "name": "create-content-sdk-app", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "description": "Sitecore Content SDK initializer", "bin": "./dist/index.js", "scripts": { diff --git a/packages/events/package.json b/packages/events/package.json index e1c7024ea1..4c6bc66bec 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/Sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", - "@sitecore-content-sdk/core": "2.0.0-beta.1", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.2", "debug": "^4.4.3" }, "description": "Enables real-time, unified tracking to send events to Sitecore.", @@ -75,5 +75,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.1" + "version": "2.0.0-beta.2" } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 2cf757570f..afb9e4fdfc 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/nextjs", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -32,8 +32,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", - "@sitecore-content-sdk/personalize": "2.0.0-beta.1", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", + "@sitecore-content-sdk/personalize": "2.0.0-beta.2", "@stylistic/eslint-plugin": "^5.2.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.3.0", @@ -76,9 +76,9 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.1", - "@sitecore-content-sdk/events": "^2.0.0-beta.1", - "@sitecore-content-sdk/personalize": "^2.0.0-beta.1", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.2", + "@sitecore-content-sdk/events": "^2.0.0-beta.2", + "@sitecore-content-sdk/personalize": "^2.0.0-beta.2", "next": "^16.1.1", "react": "^19.2.1", "react-dom": "^19.2.1", @@ -91,9 +91,9 @@ }, "dependencies": { "@babel/parser": "^7.27.2", - "@sitecore-content-sdk/content": "2.0.0-beta.1", - "@sitecore-content-sdk/core": "2.0.0-beta.1", - "@sitecore-content-sdk/react": "2.0.0-beta.1", + "@sitecore-content-sdk/content": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/react": "2.0.0-beta.2", "recast": "^0.23.11", "regex-parser": "^2.3.1", "sync-disk-cache": "^2.1.0" diff --git a/packages/personalize/package.json b/packages/personalize/package.json index 8ae5fd6bfd..a3f146d010 100644 --- a/packages/personalize/package.json +++ b/packages/personalize/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.1", - "@sitecore-content-sdk/core": "2.0.0-beta.1", - "@sitecore-content-sdk/events": "2.0.0-beta.1", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/events": "2.0.0-beta.2", "debug": "^4.4.3" }, "description": "Provides personalization capabilities to build tailored experiences for site visitors.", @@ -70,5 +70,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.1" + "version": "2.0.0-beta.2" } diff --git a/packages/react/package.json b/packages/react/package.json index 874af821c4..87bd74fdef 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/react", - "version": "2.0.0-beta.1", + "version": "2.0.0-beta.2", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -70,16 +70,16 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.1", - "@sitecore-content-sdk/events": "^2.0.0-beta.1", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.2", + "@sitecore-content-sdk/events": "^2.0.0-beta.2", "@sitecore-feaas/clientside": "^0.6.0", "react": "^19.2.1", "react-dom": "^19.2.1" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.1", - "@sitecore-content-sdk/core": "2.0.0-beta.1", - "@sitecore-content-sdk/search": "0.2.0-beta.1", + "@sitecore-content-sdk/content": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/search": "0.2.0-beta.2", "fast-deep-equal": "^3.1.3", "zod": "^4.3.6" }, diff --git a/packages/search/package.json b/packages/search/package.json index 7165347b75..a8d720532e 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/search", - "version": "0.2.0-beta.1", + "version": "0.2.0-beta.2", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.1" + "@sitecore-content-sdk/core": "2.0.0-beta.2" }, "devDependencies": { "@types/chai": "^5.2.3", diff --git a/yarn.lock b/yarn.lock index d5bf5ec3a7..9f08220ad8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3514,12 +3514,12 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.2, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3544,8 +3544,8 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/inquirer": "npm:^9.0.9" @@ -3584,12 +3584,12 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/content@npm:2.0.0-beta.1, @sitecore-content-sdk/content@workspace:packages/content": +"@sitecore-content-sdk/content@npm:2.0.0-beta.2, @sitecore-content-sdk/content@workspace:packages/content": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/content@workspace:packages/content" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/chai-spies": "npm:^1.0.6" @@ -3631,11 +3631,11 @@ __metadata: typescript: "npm:~5.8.3" url-parse: "npm:^1.5.10" peerDependencies: - "@sitecore-content-sdk/events": ^2.0.0-beta.1 + "@sitecore-content-sdk/events": ^2.0.0-beta.2 languageName: unknown linkType: soft -"@sitecore-content-sdk/core@npm:2.0.0-beta.1, @sitecore-content-sdk/core@workspace:packages/core": +"@sitecore-content-sdk/core@npm:2.0.0-beta.2, @sitecore-content-sdk/core@workspace:packages/core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/core@workspace:packages/core" dependencies: @@ -3675,14 +3675,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/events@npm:2.0.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/events@npm:2.0.0-beta.2, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: "@jest/globals": "npm:^30.2.0" "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3708,11 +3708,11 @@ __metadata: resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: "@babel/parser": "npm:^7.27.2" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/react": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/react": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" "@testing-library/react": "npm:^16.3.0" @@ -3757,9 +3757,9 @@ __metadata: ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.1 - "@sitecore-content-sdk/events": ^2.0.0-beta.1 - "@sitecore-content-sdk/personalize": ^2.0.0-beta.1 + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.2 + "@sitecore-content-sdk/events": ^2.0.0-beta.2 + "@sitecore-content-sdk/personalize": ^2.0.0-beta.2 next: ^16.1.1 react: ^19.2.1 react-dom: ^19.2.1 @@ -3770,14 +3770,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.0.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.0.0-beta.2, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.2" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3798,13 +3798,13 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/react@npm:2.0.0-beta.1, @sitecore-content-sdk/react@workspace:packages/react": +"@sitecore-content-sdk/react@npm:2.0.0-beta.2, @sitecore-content-sdk/react@workspace:packages/react": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/react@workspace:packages/react" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" - "@sitecore-content-sdk/search": "npm:0.2.0-beta.1" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/search": "npm:0.2.0-beta.2" "@sitecore-feaas/clientside": "npm:^0.6.0" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" @@ -3844,19 +3844,19 @@ __metadata: typescript: "npm:~5.8.3" zod: "npm:^4.3.6" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.1 - "@sitecore-content-sdk/events": ^2.0.0-beta.1 + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.2 + "@sitecore-content-sdk/events": ^2.0.0-beta.2 "@sitecore-feaas/clientside": ^0.6.0 react: ^19.2.1 react-dom: ^19.2.1 languageName: unknown linkType: soft -"@sitecore-content-sdk/search@npm:0.2.0-beta.1, @sitecore-content-sdk/search@workspace:packages/search": +"@sitecore-content-sdk/search@npm:0.2.0-beta.2, @sitecore-content-sdk/search@workspace:packages/search": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/search@workspace:packages/search" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" chai: "npm:^6.2.1" From 49b114239cd6be9989d61634854c714b0994abac Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 6 May 2026 10:22:14 +0300 Subject: [PATCH 10/32] Bump to beta.3 - @sitecore-content-sdk/analytics-core@2.0.0-beta.3 - @sitecore-content-sdk/cli@2.0.0-beta.3 - @sitecore-content-sdk/content@2.0.0-beta.3 - @sitecore-content-sdk/core@2.0.0-beta.3 - create-content-sdk-app@2.0.0-beta.3 - @sitecore-content-sdk/events@2.0.0-beta.3 - @sitecore-content-sdk/nextjs@2.0.0-beta.3 - @sitecore-content-sdk/personalize@2.0.0-beta.3 - @sitecore-content-sdk/react@2.0.0-beta.3 - @sitecore-content-sdk/search@0.2.0-beta.3 --- packages/analytics-core/package.json | 4 +- packages/cli/package.json | 6 +-- packages/content/package.json | 6 +-- packages/core/package.json | 2 +- packages/create-content-sdk-app/package.json | 2 +- packages/events/package.json | 6 +-- packages/nextjs/package.json | 12 ++--- packages/personalize/package.json | 8 +-- packages/react/package.json | 8 +-- packages/search/package.json | 4 +- yarn.lock | 52 ++++++++++---------- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/packages/analytics-core/package.json b/packages/analytics-core/package.json index 5a9c6653d7..f809890b2d 100644 --- a/packages/analytics-core/package.json +++ b/packages/analytics-core/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.3", "debug": "^4.4.3" }, "description": "Provides shared logic and runtime initialization. Required for the Content SDK 'events' and 'personalize' packages to function.", @@ -73,5 +73,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.2" + "version": "2.0.0-beta.3" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 479d3c52f9..14d7bead30 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/cli", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Sitecore Content SDK CLI", "main": "dist/cjs/cli.js", "module": "dist/esm/cli.js", @@ -34,8 +34,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.2", - "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/content": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.0.0-beta.3", "chokidar": "^4.0.3", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", diff --git a/packages/content/package.json b/packages/content/package.json index b32df6340d..6a81b461b9 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/content", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/events": "2.0.0-beta.2", + "@sitecore-content-sdk/events": "2.0.0-beta.3", "@stylistic/eslint-plugin": "^5.2.2", "@types/chai": "^5.2.2", "@types/chai-spies": "^1.0.6", @@ -77,7 +77,7 @@ "@sitecore-content-sdk/events": "^2.0.0-beta.2" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/core": "2.0.0-beta.3", "chalk": "^4.1.2", "debug": "^4.4.0", "glob": "^11.0.2", diff --git a/packages/core/package.json b/packages/core/package.json index 4e17b2c665..57e708607d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/core", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/create-content-sdk-app/package.json b/packages/create-content-sdk-app/package.json index 9c0f69a533..82e47e29cd 100644 --- a/packages/create-content-sdk-app/package.json +++ b/packages/create-content-sdk-app/package.json @@ -1,6 +1,6 @@ { "name": "create-content-sdk-app", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Sitecore Content SDK initializer", "bin": "./dist/index.js", "scripts": { diff --git a/packages/events/package.json b/packages/events/package.json index 4c6bc66bec..a6e87f2906 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/Sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", - "@sitecore-content-sdk/core": "2.0.0-beta.2", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.0.0-beta.3", "debug": "^4.4.3" }, "description": "Enables real-time, unified tracking to send events to Sitecore.", @@ -75,5 +75,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.2" + "version": "2.0.0-beta.3" } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index afb9e4fdfc..156392b15a 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/nextjs", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -32,8 +32,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", - "@sitecore-content-sdk/personalize": "2.0.0-beta.2", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", + "@sitecore-content-sdk/personalize": "2.0.0-beta.3", "@stylistic/eslint-plugin": "^5.2.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.3.0", @@ -91,9 +91,9 @@ }, "dependencies": { "@babel/parser": "^7.27.2", - "@sitecore-content-sdk/content": "2.0.0-beta.2", - "@sitecore-content-sdk/core": "2.0.0-beta.2", - "@sitecore-content-sdk/react": "2.0.0-beta.2", + "@sitecore-content-sdk/content": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/react": "2.0.0-beta.3", "recast": "^0.23.11", "regex-parser": "^2.3.1", "sync-disk-cache": "^2.1.0" diff --git a/packages/personalize/package.json b/packages/personalize/package.json index a3f146d010..d885ee67ed 100644 --- a/packages/personalize/package.json +++ b/packages/personalize/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.2", - "@sitecore-content-sdk/core": "2.0.0-beta.2", - "@sitecore-content-sdk/events": "2.0.0-beta.2", + "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/events": "2.0.0-beta.3", "debug": "^4.4.3" }, "description": "Provides personalization capabilities to build tailored experiences for site visitors.", @@ -70,5 +70,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.2" + "version": "2.0.0-beta.3" } diff --git a/packages/react/package.json b/packages/react/package.json index 87bd74fdef..e51fd4bcb8 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/react", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -77,9 +77,9 @@ "react-dom": "^19.2.1" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.2", - "@sitecore-content-sdk/core": "2.0.0-beta.2", - "@sitecore-content-sdk/search": "0.2.0-beta.2", + "@sitecore-content-sdk/content": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/search": "0.2.0-beta.3", "fast-deep-equal": "^3.1.3", "zod": "^4.3.6" }, diff --git a/packages/search/package.json b/packages/search/package.json index a8d720532e..c21ec6f154 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/search", - "version": "0.2.0-beta.2", + "version": "0.2.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.2" + "@sitecore-content-sdk/core": "2.0.0-beta.3" }, "devDependencies": { "@types/chai": "^5.2.3", diff --git a/yarn.lock b/yarn.lock index 9f08220ad8..fb1e7b434c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3514,12 +3514,12 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.2, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.3, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3544,8 +3544,8 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/inquirer": "npm:^9.0.9" @@ -3584,12 +3584,12 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/content@npm:2.0.0-beta.2, @sitecore-content-sdk/content@workspace:packages/content": +"@sitecore-content-sdk/content@npm:2.0.0-beta.3, @sitecore-content-sdk/content@workspace:packages/content": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/content@workspace:packages/content" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/chai-spies": "npm:^1.0.6" @@ -3635,7 +3635,7 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/core@npm:2.0.0-beta.2, @sitecore-content-sdk/core@workspace:packages/core": +"@sitecore-content-sdk/core@npm:2.0.0-beta.3, @sitecore-content-sdk/core@workspace:packages/core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/core@workspace:packages/core" dependencies: @@ -3675,14 +3675,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/events@npm:2.0.0-beta.2, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/events@npm:2.0.0-beta.3, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: "@jest/globals": "npm:^30.2.0" "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3708,11 +3708,11 @@ __metadata: resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: "@babel/parser": "npm:^7.27.2" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/react": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/react": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" "@testing-library/react": "npm:^16.3.0" @@ -3770,14 +3770,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.0.0-beta.2, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.0.0-beta.3, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/events": "npm:2.0.0-beta.3" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3798,13 +3798,13 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/react@npm:2.0.0-beta.2, @sitecore-content-sdk/react@workspace:packages/react": +"@sitecore-content-sdk/react@npm:2.0.0-beta.3, @sitecore-content-sdk/react@workspace:packages/react": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/react@workspace:packages/react" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" - "@sitecore-content-sdk/search": "npm:0.2.0-beta.2" + "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/search": "npm:0.2.0-beta.3" "@sitecore-feaas/clientside": "npm:^0.6.0" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" @@ -3852,11 +3852,11 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/search@npm:0.2.0-beta.2, @sitecore-content-sdk/search@workspace:packages/search": +"@sitecore-content-sdk/search@npm:0.2.0-beta.3, @sitecore-content-sdk/search@workspace:packages/search": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/search@workspace:packages/search" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.2" + "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" chai: "npm:^6.2.1" From 89c0dcd1258eb835dcadc8efb8e0fb65f1f93635 Mon Sep 17 00:00:00 2001 From: illiakovalenko Date: Wed, 6 May 2026 10:26:25 +0300 Subject: [PATCH 11/32] Bump peer deps to beta.3 --- packages/content/package.json | 2 +- packages/nextjs/package.json | 6 +++--- packages/react/package.json | 4 ++-- yarn.lock | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/content/package.json b/packages/content/package.json index 6a81b461b9..34fc9fd3b1 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -74,7 +74,7 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/events": "^2.0.0-beta.2" + "@sitecore-content-sdk/events": "^2.0.0-beta.3" }, "dependencies": { "@sitecore-content-sdk/core": "2.0.0-beta.3", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 156392b15a..ac79f6c742 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -76,9 +76,9 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.2", - "@sitecore-content-sdk/events": "^2.0.0-beta.2", - "@sitecore-content-sdk/personalize": "^2.0.0-beta.2", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.3", + "@sitecore-content-sdk/events": "^2.0.0-beta.3", + "@sitecore-content-sdk/personalize": "^2.0.0-beta.3", "next": "^16.1.1", "react": "^19.2.1", "react-dom": "^19.2.1", diff --git a/packages/react/package.json b/packages/react/package.json index e51fd4bcb8..b79f57e03d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -70,8 +70,8 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.2", - "@sitecore-content-sdk/events": "^2.0.0-beta.2", + "@sitecore-content-sdk/analytics-core": "^2.0.0-beta.3", + "@sitecore-content-sdk/events": "^2.0.0-beta.3", "@sitecore-feaas/clientside": "^0.6.0", "react": "^19.2.1", "react-dom": "^19.2.1" diff --git a/yarn.lock b/yarn.lock index fb1e7b434c..ebbe74345c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3631,7 +3631,7 @@ __metadata: typescript: "npm:~5.8.3" url-parse: "npm:^1.5.10" peerDependencies: - "@sitecore-content-sdk/events": ^2.0.0-beta.2 + "@sitecore-content-sdk/events": ^2.0.0-beta.3 languageName: unknown linkType: soft @@ -3757,9 +3757,9 @@ __metadata: ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.2 - "@sitecore-content-sdk/events": ^2.0.0-beta.2 - "@sitecore-content-sdk/personalize": ^2.0.0-beta.2 + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.3 + "@sitecore-content-sdk/events": ^2.0.0-beta.3 + "@sitecore-content-sdk/personalize": ^2.0.0-beta.3 next: ^16.1.1 react: ^19.2.1 react-dom: ^19.2.1 @@ -3844,8 +3844,8 @@ __metadata: typescript: "npm:~5.8.3" zod: "npm:^4.3.6" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.2 - "@sitecore-content-sdk/events": ^2.0.0-beta.2 + "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.3 + "@sitecore-content-sdk/events": ^2.0.0-beta.3 "@sitecore-feaas/clientside": ^0.6.0 react: ^19.2.1 react-dom: ^19.2.1 From d46238dd8649602274005e91ed2bb35f59e3cd43 Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Mon, 18 May 2026 18:45:51 +0300 Subject: [PATCH 12/32] [Render NCC wrapper] Create an SXA component in RH to fetch NCC layout from external host and pass it to render (#478) * feat: create the wrapper component Co-authored-by: Copilot * docs: changelog * chore: export types Co-authored-by: Copilot * chore: use resolveEdgeUrl to get the edge url Co-authored-by: Copilot * chore: change the message prefix Co-authored-by: Copilot --------- Co-authored-by: Copilot --- CHANGELOG.md | 2 +- .../StudioComponentServerWrapper.test.tsx | 223 ++++++++++++++++++ .../Wrapper/StudioComponentServerWrapper.tsx | 116 +++++++++ .../Wrapper/StudioComponentWrapper.test.tsx | 156 ++++++++++++ .../atoms/Wrapper/StudioComponentWrapper.tsx | 30 +++ packages/react/src/atoms/Wrapper/index.ts | 8 + packages/react/src/atoms/Wrapper/models.ts | 34 +++ packages/react/src/index.ts | 8 + 8 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx create mode 100644 packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx create mode 100644 packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx create mode 100644 packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx create mode 100644 packages/react/src/atoms/Wrapper/index.ts create mode 100644 packages/react/src/atoms/Wrapper/models.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d1849d6b94..4f31aa1a0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Our versioning strategy is as follows: - Introduce Zod Schemas for Content SDK Fields ([#450](https://github.com/Sitecore/content-sdk/pull/450)) - Introduce `low-code` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) - Support Atom-based code generation ([#465](https://github.com/Sitecore/content-sdk/pull/465)) - + - Create an SXA component in RH to fetch NCC layout from external host and pass it to render ([#478](https://github.com/Sitecore/content-sdk/pull/478)) ### 🐛 Bug Fixes diff --git a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx new file mode 100644 index 0000000000..3dfe5d70b6 --- /dev/null +++ b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx @@ -0,0 +1,223 @@ +/* eslint-disable no-unused-expressions */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React from 'react'; +import { expect } from 'chai'; +import { createSandbox, SinonSandbox, SinonStub } from 'sinon'; +import proxyquire from 'proxyquire'; +import type { Document } from '@sitecore-content-sdk/content/atoms'; + +describe('StudioComponentServerWrapper', () => { + let sandbox: SinonSandbox; + + let module: any; + let StudioComponentServerWrapper: any; + + let fetcherGetStub: SinonStub; + let StudioComponentWrapperStub: SinonStub; + let consoleWarnStub: SinonStub; + let consoleErrorStub: SinonStub; + let resolveEdgeUrlStub: SinonStub; + + const sampleDocument: Document = { + name: 'hero', + root: { id: 'r', type: 'Box' }, + }; + + beforeEach(() => { + sandbox = createSandbox(); + + fetcherGetStub = sandbox.stub().resolves({ data: JSON.stringify(sampleDocument) }); + StudioComponentWrapperStub = sandbox + .stub() + .returns(React.createElement('div', { 'data-test': 'wrapper' })); + consoleWarnStub = sandbox.stub(console, 'warn'); + consoleErrorStub = sandbox.stub(console, 'error'); + resolveEdgeUrlStub = sandbox.stub().returns('https://edge.example.com'); + + module = proxyquire('./StudioComponentServerWrapper', { + '@sitecore-content-sdk/core': { + NativeDataFetcher: class { + get: SinonStub; + constructor() { + this.get = fetcherGetStub; + } + }, + }, + '@sitecore-content-sdk/core/tools': { + resolveEdgeUrl: resolveEdgeUrlStub, + }, + '@sitecore-content-sdk/content': { + debug: { layout: undefined }, + }, + './StudioComponentWrapper': { + StudioComponentWrapper: StudioComponentWrapperStub, + }, + }); + + StudioComponentServerWrapper = module.StudioComponentServerWrapper; + + process.env.SITECORE_EDGE_PLATFORM_HOSTNAME = 'https://edge.example.com'; + }); + + afterEach(() => { + sandbox.restore(); + delete process.env.SITECORE_EDGE_PLATFORM_HOSTNAME; + }); + + describe('early returns', () => { + it('returns null when componentRef is empty string', async () => { + const result = await StudioComponentServerWrapper({ componentRef: '' }); + expect(result).to.be.null; + }); + + it('returns null when componentRef is undefined', async () => { + const result = await StudioComponentServerWrapper({ componentRef: undefined as any }); + expect(result).to.be.null; + }); + + it('returns null when no path can be extracted from componentRef', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'some/path/variant1', + fieldNames: 'nonexistent', + }); + // 'nonexistent' does not match 'variant1' and there is no 'default' segment + expect(result).to.be.null; + expect(consoleWarnStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: failed to extract path' + ); + }); + }); + + describe('path extraction (extractVariantPathFromComponentRef)', () => { + it('uses the path whose last segment matches fieldNames', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'org/components/hero/mobile | org/components/hero/desktop', + fieldNames: 'desktop', + }); + + expect(result).to.not.be.null; + // Ensure fetcher was called with the desktop path + const calledUrl: string = fetcherGetStub.firstCall.args[0]; + expect(calledUrl).to.include('desktop'); + }); + + it('falls back to the "default" variant when fieldNames does not match', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'org/components/hero/default | org/components/hero/mobile', + fieldNames: 'desktop', + }); + + expect(result).to.not.be.null; + const calledUrl: string = fetcherGetStub.firstCall.args[0]; + expect(calledUrl).to.include('default'); + }); + + it('uses "default" fieldNames when fieldNames prop is omitted', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'org/components/hero/default', + }); + + expect(result).to.not.be.null; + const calledUrl: string = fetcherGetStub.firstCall.args[0]; + expect(calledUrl).to.include('default'); + }); + + it('returns null and warns when no path matches and no default exists', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'org/components/hero/mobile', + fieldNames: 'desktop', + }); + + expect(result).to.be.null; + expect(consoleWarnStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: failed to extract path' + ); + }); + }); + + describe('fetchDocument — URL construction', () => { + it('prepends /mms/ to a relative path that starts with /', async () => { + await StudioComponentServerWrapper({ componentRef: '/components/hero/default' }); + + const calledUrl: string = fetcherGetStub.firstCall.args[0]; + expect(calledUrl).to.include('/mms/components/hero/default'); + }); + + it('prepends /mms/ to a relative path without a leading /', async () => { + await StudioComponentServerWrapper({ componentRef: 'components/hero/default' }); + + const calledUrl: string = fetcherGetStub.firstCall.args[0]; + expect(calledUrl).to.include('/mms/components/hero/default'); + }); + }); + + describe('fetchDocument — fetch errors', () => { + it('returns null and errors when fetcher.get throws', async () => { + fetcherGetStub.rejects(new Error('network error')); + + const result = await StudioComponentServerWrapper({ + componentRef: 'components/hero/default', + }); + + expect(result).to.be.null; + expect(consoleErrorStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: failed to fetch component layout' + ); + }); + + it('returns null and errors when response body is not valid JSON', async () => { + fetcherGetStub.resolves({ data: 'not-json{{' }); + + const result = await StudioComponentServerWrapper({ + componentRef: 'components/hero/default', + }); + + expect(result).to.be.null; + expect(consoleErrorStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: failed to parse component layout response' + ); + }); + }); + + describe('fetchDocument — path validation', () => { + it('returns null and warns when path is empty', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'org/components/hero/nonexistent', + fieldNames: 'nonexistent', + }); + + expect(result).to.be.null; + expect(consoleWarnStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: missing component reference path' + ); + }); + + it('returns null and errors when URL resolution fails', async () => { + resolveEdgeUrlStub.throws(new Error('invalid hostname')); + + const result = await StudioComponentServerWrapper({ + componentRef: 'components/hero/default', + }); + + expect(result).to.be.null; + expect(consoleErrorStub).to.have.been.calledWithMatch( + 'StudioComponentServerWrapper: failed to resolve component from' + ); + }); + }); + + describe('successful render', () => { + it('renders StudioComponentWrapper with the fetched document', async () => { + const result = await StudioComponentServerWrapper({ + componentRef: 'components/hero/default', + }); + + // The server wrapper returns a React element (JSX), not a rendered output. + // Assert the element type and props directly. + expect(result).to.not.be.null; + expect(result.type).to.equal(StudioComponentWrapperStub); + expect(result.props.document).to.deep.equal(sampleDocument); + }); + }); +}); + diff --git a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx new file mode 100644 index 0000000000..b2d7296d61 --- /dev/null +++ b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx @@ -0,0 +1,116 @@ +import React from 'react'; +import { StudioComponentWrapper } from './StudioComponentWrapper'; +import { StudioComponentServerWrapperProps } from './models'; +import { NativeDataFetcher, NativeDataFetcherResponse } from '@sitecore-content-sdk/core'; +import { debug } from '@sitecore-content-sdk/content'; +import { Document } from '@sitecore-content-sdk/content/atoms'; +import { resolveEdgeUrl } from '@sitecore-content-sdk/core/tools'; + +/** + * Server component for Studio (NCC) components. Fetches the component layout + * `Document` from MMS server-side and renders the client `StudioComponentWrapper`. + * @param {StudioComponentServerWrapperProps} props incoming props + * @returns rendered `StudioComponentWrapper` + * @internal + */ +export const StudioComponentServerWrapper = async (props: StudioComponentServerWrapperProps) => { + const componentRef = props.componentRef || ''; + if (!componentRef) return null; + + const path = extractVariantPathFromComponentRef(componentRef, props.fieldNames); + if (!path) return null; + + const document = await fetchDocument(path); + if (!document) return null; + + return ; +}; + +/** + * Extracts the variant path from a component reference. + * @param {string} componentRef The component reference string. + * @param {string} fieldNames The field names to extract the variant path for. + * @returns {string} The variant path. + * @internal + */ +function extractVariantPathFromComponentRef( + componentRef: string, + fieldNames: string = 'default' +): string | null { + const paths = componentRef.split('|').reduce((acc, part) => { + const path = part.trim(); + const segments = path.split('/'); + const variant = segments[segments.length - 1]; + + acc.set(variant, path); + + return acc; + }, new Map()); + + const path = paths.get(fieldNames) || paths.get('default') || null; + + if (!path) { + console.warn( + `StudioComponentServerWrapper: failed to extract path from ComponentRef "${componentRef}" with fieldNames "${fieldNames}". ` + + 'Ensure the ComponentRef is in the expected format and that the correct fieldNames are provided.' + ); + } + + return path; +} + +/** + * Prefix for MMS component paths in componentRef. The final URL will be resolved as `${host}/${MMS_COMPONENT_PATH_PREFIX}/${path}`. + */ +const MMS_COMPONENT_PATH_PREFIX = 'mms'; + +/** + * Fetch a Studio component layout `Document` by reference. + * @param {string} path extracted component reference (path) from `params.ComponentRef` + * @returns {Promise} the resolved component layout, or `null` on missing path, fetch failure, or un-parseable body. + */ +async function fetchDocument(path: string): Promise { + if (!path) { + console.warn('StudioComponentServerWrapper: missing component reference path'); + return null; + } + + let url: string; + try { + const pathWithMmsPrefix = path.startsWith('/') + ? `/${MMS_COMPONENT_PATH_PREFIX}${path}` + : `/${MMS_COMPONENT_PATH_PREFIX}/${path}`; + + const hostURL = resolveEdgeUrl(); + + url = new URL(pathWithMmsPrefix, hostURL).toString(); + } catch (err) { + console.error(`StudioComponentServerWrapper: failed to resolve component from "${path}"`, err); + return null; + } + + let response: NativeDataFetcherResponse; + try { + const fetcher = new NativeDataFetcher({ debugger: debug.layout }); + response = await fetcher.get(url); + } catch (error) { + console.error( + `StudioComponentServerWrapper: failed to fetch component layout from ${url}`, + error + ); + return null; + } + + try { + const document: Document = JSON.parse(response.data); + + return document; + } catch (err) { + console.error( + `StudioComponentServerWrapper: failed to parse component layout response from ${url}`, + err + ); + return null; + } +} + diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx new file mode 100644 index 0000000000..8167d49e90 --- /dev/null +++ b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx @@ -0,0 +1,156 @@ +/* eslint-disable jsdoc/require-jsdoc */ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import sinon from 'sinon'; +import { expect, use as chaiUse } from 'chai'; +import sinonChai from 'sinon-chai'; + +chaiUse(sinonChai); +import { render } from '@testing-library/react'; +import { z } from 'zod'; +import { StudioComponentWrapper } from './StudioComponentWrapper'; +import { SitecoreProvider } from '../../components/SitecoreProvider'; +import type { Document } from '@sitecore-content-sdk/content/atoms'; +import type { AtomMetadata } from '../types'; +import type { ImportMapImport } from '../../components/DesignLibrary/models'; + +/** + * Minimal error boundary to catch React render errors without crashing the test. + */ +class ErrorBoundary extends React.Component< + React.PropsWithChildren<{ onError?: (err: Error) => void }>, + { hasError: boolean } +> { + state = { hasError: false }; + + componentDidCatch(err: Error) { + this.props.onError?.(err); + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + render() { + return this.state.hasError ? null : this.props.children; + } +} + +describe('', () => { + const sandbox = sinon.createSandbox(); + const apiStub = {} as any; + const emptyComponentMap = new Map(); + const loadImportMapStub = async (): Promise => ({} as ImportMapImport); + + const Box: React.FC>> = (props) => + React.createElement('div', { 'data-test': 'box', ...props }, props.children); + + const atoms: AtomMetadata[] = [ + { + name: 'Box', + type: 'atom', + description: 'Container', + component: Box as React.ComponentType, + props: z.object({}), + }, + ]; + + const sampleDoc: Document = { + name: 'hero', + root: { id: 'r', type: 'Box' }, + }; + + const getPage = () => ({ + locale: 'en', + layout: { sitecore: { context: {}, route: null } }, + mode: { + name: 'normal', + isDesignLibrary: false, + designLibrary: { isVariantGeneration: false }, + isNormal: true, + isPreview: false, + isEditing: false, + }, + }); + + const renderInProvider = (ui: React.ReactNode) => + render( + + {ui} + + ); + + const renderWithoutAtomRegistry = (ui: React.ReactNode) => + render( + + {ui} + + ); + + afterEach(() => { + sandbox.restore(); + }); + + it('renders nothing when document is null', () => { + const { container } = renderInProvider(); + expect(container.innerHTML).to.equal(''); + }); + + it('renders nothing when document is undefined', () => { + const { container } = renderInProvider(); + expect(container.innerHTML).to.equal(''); + }); + + it('renders the component-layout view when document is provided', () => { + const { container } = renderInProvider(); + expect(container.querySelector('[data-test="box"]')).to.exist; + }); + + it('renders nothing safely when atomRegistry is absent from the provider and document is null', () => { + const { container } = renderWithoutAtomRegistry(); + expect(container.innerHTML).to.equal(''); + }); + + it('throws when document references an atom type not present in the registry', () => { + const docWithUnknownAtom: Document = { name: 'test', root: { id: 'x', type: 'UnknownAtom' } }; + let caughtError: Error | undefined; + + // Suppress the expected React error boundary console output + const consoleErrorStub = sandbox.stub(console, 'error'); + + render( + + { + caughtError = err; + }} + > + + + + ); + + consoleErrorStub.restore(); + + expect(caughtError).to.be.instanceOf(Error); + expect(caughtError!.message).to.include('UnknownAtom'); + }); +}); + diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx new file mode 100644 index 0000000000..86a50dca9c --- /dev/null +++ b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx @@ -0,0 +1,30 @@ +'use client'; +import React, { JSX, useMemo } from 'react'; +import { createView } from '../component-layout'; +import { getAtomMap } from '../atom-registry-utils'; +import { useSitecore } from '../../components/SitecoreProvider'; +import { StudioComponentWrapperProps } from './models'; + +/** + * Client component that renders a pre-fetched Studio (NCC) component layout. + * + * Expects `document` to be provided (fetched server-side by + * `StudioComponentServerWrapper` or from DesignLibraryLowCodeComponent). Renders `null` when no layout is available. + * @param {StudioComponentWrapperProps} props component props + * @internal + */ +export const StudioComponentWrapper = (props: StudioComponentWrapperProps): JSX.Element | null => { + const { atomRegistry } = useSitecore(); + const atomMap = useMemo(() => getAtomMap(atomRegistry?.atoms ?? []), [atomRegistry?.atoms]); + + const ViewComponent = useMemo(() => { + if (!props.document) return null; + + return createView(props.document, atomMap, atomRegistry?.callbacks); + }, [props.document, atomMap, atomRegistry?.callbacks]); + + if (!ViewComponent) return null; + + return ; +}; + diff --git a/packages/react/src/atoms/Wrapper/index.ts b/packages/react/src/atoms/Wrapper/index.ts new file mode 100644 index 0000000000..a728ccb362 --- /dev/null +++ b/packages/react/src/atoms/Wrapper/index.ts @@ -0,0 +1,8 @@ +export { StudioComponentWrapper } from './StudioComponentWrapper'; +export { StudioComponentServerWrapper } from './StudioComponentServerWrapper'; +export { + StudioComponentParams, + StudioComponentServerWrapperProps, + StudioComponentWrapperProps, +} from './models'; + diff --git a/packages/react/src/atoms/Wrapper/models.ts b/packages/react/src/atoms/Wrapper/models.ts new file mode 100644 index 0000000000..3fbec4f272 --- /dev/null +++ b/packages/react/src/atoms/Wrapper/models.ts @@ -0,0 +1,34 @@ +import type { Document } from '@sitecore-content-sdk/content/atoms'; + +/** + * Rendering parameters injected by the layout service for Studio components. + * The `ComponentRef$` field on the rendering is copied into params with the `$` stripped. + * @internal + */ +export type StudioComponentParams = { + /** + * Identifier or relative/absolute path to the Studio component layout JSON in MMS. + */ + componentRef?: string; +}; + +/** + * Props accepted by the RSC `StudioComponentServerWrapper`. + * @internal + */ +export type StudioComponentServerWrapperProps = { + /** + * Pipe separated relative paths to the Studio component layout JSON in MMS with the last segment as the variant name. The path matching `FieldNames` will be used, or `default` if no match. + */ + componentRef: string; + fieldNames?: string; +}; + +/** + * Props accepted by the `StudioComponentWrapper` used to render a Studio component layout on the client. Expects a pre-fetched `document` containing the component layout data. + * @internal + */ +export type StudioComponentWrapperProps = { + document?: Document | null; +}; + diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 460d6be48b..62ffb6a99d 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -83,6 +83,14 @@ export { FEaaSServerWrapper, BYOCServerWrapper, } from './components/FEaaS'; +export { + StudioComponentWrapper, + StudioComponentWrapper as StudioComponentClientWrapper, + StudioComponentServerWrapper, + type StudioComponentParams, + type StudioComponentServerWrapperProps, + type StudioComponentWrapperProps, +} from './atoms/Wrapper'; export { DesignLibrary, DesignLibraryLowCodeComponent, From adbec9b69453208da29b978785f4061e4b5c7769 Mon Sep 17 00:00:00 2001 From: Menelaos Nasies <38861573+MenKNas@users.noreply.github.com> Date: Thu, 21 May 2026 14:48:01 +0300 Subject: [PATCH 13/32] [atoms-epic] Integrate SXA component (#485) * [atoms-epic] Add SXA client component for preview * Add StudioNccWrapper for App Router NCC page rendering * Address PR comments * Adjust CHANGELOG entries * Remove unwanted files --- CHANGELOG.md | 1 + packages/react/api/content-sdk-react.api.md | 25 ++++++++++ .../StudioComponentServerWrapper.test.tsx | 10 ++-- .../Wrapper/StudioComponentWrapper.test.tsx | 8 ++-- .../atoms/Wrapper/StudioComponentWrapper.tsx | 6 ++- .../component-layout/createView.test.tsx | 46 +++++++++++++++++++ .../src/atoms/component-layout/createView.tsx | 9 +++- packages/react/src/atoms/field-schemas.ts | 12 ++--- .../DesignLibraryLowCodeComponent.tsx | 17 ++----- 9 files changed, 104 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f31aa1a0b..141d98925a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Our versioning strategy is as follows: - Introduce `low-code` mode for design library ([#452](https://github.com/Sitecore/content-sdk/pull/452)) - Support Atom-based code generation ([#465](https://github.com/Sitecore/content-sdk/pull/465)) - Create an SXA component in RH to fetch NCC layout from external host and pass it to render ([#478](https://github.com/Sitecore/content-sdk/pull/478)) + - Integrate Studio/NCC Design Library low-code preview through `StudioComponentWrapper` and apply `document.props` as runtime props in `createView` ([#485](https://github.com/Sitecore/content-sdk/pull/485)) ### 🐛 Bug Fixes diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index 1a8ba9495f..e101e41862 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -17,6 +17,7 @@ import { debug as debug_2 } from '@sitecore-content-sdk/search'; import { DefaultRetryStrategy } from '@sitecore-content-sdk/content/client'; import { DictionaryPhrases } from '@sitecore-content-sdk/content/i18n'; import { DictionaryService } from '@sitecore-content-sdk/content/i18n'; +import type { Document as Document_2 } from '@sitecore-content-sdk/content/atoms'; import { EditMode } from '@sitecore-content-sdk/content/layout'; import { enableDebug } from '@sitecore-content-sdk/core'; import { EnhancedOmit } from '@sitecore-content-sdk/core/tools'; @@ -650,6 +651,30 @@ export { SitePathService } export { SitePathServiceConfig } +// @internal +export type StudioComponentParams = { + componentRef?: string; +}; + +// @internal +export const StudioComponentServerWrapper: (props: StudioComponentServerWrapperProps) => Promise; + +// @internal +export type StudioComponentServerWrapperProps = { + componentRef: string; + fieldNames?: string; +}; + +// @internal +const StudioComponentWrapper: (props: StudioComponentWrapperProps) => JSX_2.Element | null; +export { StudioComponentWrapper as StudioComponentClientWrapper } +export { StudioComponentWrapper } + +// @internal +export type StudioComponentWrapperProps = { + document?: Document_2 | null; +}; + // Warning: (ae-forgotten-export) The symbol "TextProps" needs to be exported by the entry point api-surface.d.ts // // @public diff --git a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx index 3dfe5d70b6..564bc95f04 100644 --- a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx +++ b/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx @@ -180,16 +180,16 @@ describe('StudioComponentServerWrapper', () => { }); describe('fetchDocument — path validation', () => { - it('returns null and warns when path is empty', async () => { + it('renders when fieldNames matches the variant segment in componentRef', async () => { const result = await StudioComponentServerWrapper({ componentRef: 'org/components/hero/nonexistent', fieldNames: 'nonexistent', }); - expect(result).to.be.null; - expect(consoleWarnStub).to.have.been.calledWithMatch( - 'StudioComponentServerWrapper: missing component reference path' - ); + expect(result).to.not.be.null; + expect(fetcherGetStub).to.have.been.calledOnce; + expect(result.type).to.equal(StudioComponentWrapperStub); + expect(result.props.document).to.deep.equal(sampleDocument); }); it('returns null and errors when URL resolution fails', async () => { diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx index 8167d49e90..c7110c71a9 100644 --- a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx +++ b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx @@ -23,14 +23,14 @@ class ErrorBoundary extends React.Component< > { state = { hasError: false }; - componentDidCatch(err: Error) { - this.props.onError?.(err); - } - static getDerivedStateFromError() { return { hasError: true }; } + componentDidCatch(err: Error) { + this.props.onError?.(err); + } + render() { return this.state.hasError ? null : this.props.children; } diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx index 86a50dca9c..7dca4920ae 100644 --- a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx +++ b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx @@ -9,7 +9,10 @@ import { StudioComponentWrapperProps } from './models'; * Client component that renders a pre-fetched Studio (NCC) component layout. * * Expects `document` to be provided (fetched server-side by - * `StudioComponentServerWrapper` or from DesignLibraryLowCodeComponent). Renders `null` when no layout is available. + * `StudioComponentServerWrapper`, from Design Library document updates, or any other + * preview path that supplies a layout `Document`). Runtime props from plain-object + * `document.props` are applied as runtime props when the layout view is created. Renders `null` when no layout + * is available. * @param {StudioComponentWrapperProps} props component props * @internal */ @@ -27,4 +30,3 @@ export const StudioComponentWrapper = (props: StudioComponentWrapperProps): JSX. return ; }; - diff --git a/packages/react/src/atoms/component-layout/createView.test.tsx b/packages/react/src/atoms/component-layout/createView.test.tsx index 1bc0da85c8..36e1c2bb56 100644 --- a/packages/react/src/atoms/component-layout/createView.test.tsx +++ b/packages/react/src/atoms/component-layout/createView.test.tsx @@ -1464,6 +1464,52 @@ describe('component-layout/createView', () => { expect(onChangedSpy.firstCall.args[0]).to.equal('updated'); }); + it('merges plain-object document.props into resolve context for bindings', () => { + const doc: Document = { + name: 'PropsDoc', + props: { label: 'from-document' }, + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['{{props.label}}'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + expect(rendered.getByTestId('text').textContent).to.equal('from-document'); + }); + + it('ignores non-object document.props when resolving bindings', () => { + const doc: Document = { + name: 'BadPropsDoc', + props: 'not-an-object' as unknown as Document['props'], + root: { + id: 'root', + type: 'Stack', + children: [ + { + id: 'text', + type: 'Text', + children: ['{{props.label}}'], + }, + ], + }, + }; + + const Generated = createView(doc, atoms); + const rendered = render(); + + expect(rendered.getByTestId('text').textContent).to.equal('runtime'); + }); + it('supports for/show expressions with runtime props', () => { const doc: Document = { name: 'LoopAndShowDoc', diff --git a/packages/react/src/atoms/component-layout/createView.tsx b/packages/react/src/atoms/component-layout/createView.tsx index d5223607c1..ea4a2eebd0 100644 --- a/packages/react/src/atoms/component-layout/createView.tsx +++ b/packages/react/src/atoms/component-layout/createView.tsx @@ -246,6 +246,13 @@ export function createView = Record callbacks: CallbackMetadata[] = [] ): FC { const { root, state: initialState = {} } = doc; + const documentProps = + doc.props !== null && + doc.props !== undefined && + typeof doc.props === 'object' && + !Array.isArray(doc.props) + ? (doc.props as Record) + : {}; const Generated: FC = (runtimeProps) => { const [state, setState] = useReducer( @@ -268,7 +275,7 @@ export function createView = Record scope: ScopeMap | undefined ): React.ReactNode => { const ctx: ResolveContext = { - props: runtimeProps as Record, + props: { ...documentProps, ...(runtimeProps as Record) }, state: stateRef.current, item: itemCtx, scope, diff --git a/packages/react/src/atoms/field-schemas.ts b/packages/react/src/atoms/field-schemas.ts index ed9a7a227d..8328536727 100644 --- a/packages/react/src/atoms/field-schemas.ts +++ b/packages/react/src/atoms/field-schemas.ts @@ -4,7 +4,7 @@ import { withPropMeta } from './schema-utils'; /** * Zod schema for a Sitecore Single-Line Text or Multi-Line Text field. - * Mirrors {@link [Text Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Text.tsx)}. + * Mirrors the Sitecore Text component (`Text.tsx` in `@sitecore-content-sdk/react`). * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. * @returns A ZodObject with `value?: string | number` and the DS control hint attached. * @public @@ -20,7 +20,7 @@ export const textFieldSchema = (extra?: z.ZodRawShape) => /** * Zod schema for a Sitecore Rich Text field. - * Mirrors {@link [Rich Text Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/RichText.tsx)}. + * Mirrors the Sitecore Rich Text component (`RichText.tsx` in `@sitecore-content-sdk/react`). * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. * @returns A ZodObject with `value?: string` and the DS control hint attached. * @public @@ -36,7 +36,7 @@ export const richTextFieldSchema = (extra?: z.ZodRawShape) => /** * Zod schema for a Sitecore Date field. - * Mirrors the field shape used in {@link [Date Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Date.tsx)}. + * Mirrors the field shape used in the Date component (`Date.tsx` in `@sitecore-content-sdk/react`). * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. * @returns A ZodObject with `value?: string` and the DS control hint attached. * @public @@ -52,7 +52,7 @@ export const dateFieldSchema = (extra?: z.ZodRawShape) => /** * Zod schema for a Sitecore Link field. - * Mirrors {@link [Link Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Link.tsx)}. + * Mirrors the Sitecore Link component (`Link.tsx` in `@sitecore-content-sdk/react`). * The inner value object uses `z.looseObject` to allow arbitrary Sitecore-added attributes, * matching the `[attributeName: string]: unknown` index signature on `LinkFieldValue`. * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. @@ -80,7 +80,7 @@ export const linkFieldSchema = (extra?: z.ZodRawShape) => /** * Zod schema for a Sitecore Image field. - * Mirrors {@link [Image Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/Image.tsx)}. + * Mirrors the Sitecore Image component (`Image.tsx` in `@sitecore-content-sdk/react`). * The inner value object uses `z.looseObject` to allow arbitrary HTML attributes, * matching the `[attributeName: string]: unknown` index signature on `ImageFieldValue`. * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. @@ -106,7 +106,7 @@ export const imageFieldSchema = (extra?: z.ZodRawShape) => /** * Zod schema for a Sitecore File field. - * Mirrors {@link [File Component](https://github.com/Sitecore/content-sdk/blob/dev/packages/react/src/components/File.tsx)}. + * Mirrors the Sitecore File component (`File.tsx` in `@sitecore-content-sdk/react`). * The inner value object uses `z.looseObject` to allow arbitrary extra properties, * matching the `[propName: string]: unknown` index signature on `FileFieldValue`. * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the outer schema. diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx index 59c0c2d67b..e67ecdf87b 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx @@ -1,9 +1,9 @@ 'use client'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useSitecore } from '../SitecoreProvider'; -import { serializeAtoms, getAtomMap } from '../../atoms/atom-registry-utils'; +import { serializeAtoms } from '../../atoms/atom-registry-utils'; import { serializeCallbacks } from '../../atoms/callback-registry-utils'; -import { createView } from '../../atoms/component-layout'; +import { StudioComponentWrapper } from '../../atoms/Wrapper/StudioComponentWrapper'; import type { Document } from '@sitecore-content-sdk/content/atoms'; import * as editing from '@sitecore-content-sdk/content/editing'; import { DesignLibraryErrorBoundary } from '../..'; @@ -35,7 +35,7 @@ export const __mockDependencies = (mocks: any) => { * Facilitates the communication between the Design Studio and the Rendering Host when previewing a low code component built with the Atoms. * - On mount, it unfolds and serializes the atoms registry and callback registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. * - Receives Component model data updates via document update handler and renders the low code component - * based on component model data and the available atoms using createView. + * via {@link StudioComponentWrapper} (same client path as Studio / NCC preview elsewhere). * @internal */ export const DesignLibraryLowCodeComponent = () => { @@ -43,13 +43,6 @@ export const DesignLibraryLowCodeComponent = () => { const [currentDocument, setCurrentDocument] = useState(null); const [renderKey, setRenderKey] = useState(0); - const atomMap = useMemo(() => getAtomMap(atomRegistry?.atoms || []), [atomRegistry?.atoms]); - - const ViewComponent = useMemo(() => { - if (!currentDocument) return null; - return createView(currentDocument, atomMap, atomRegistry?.callbacks); - }, [currentDocument, atomMap, atomRegistry?.callbacks]); - useEffect(() => { postToDesignLibrary( getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'low-code-component') @@ -88,7 +81,7 @@ export const DesignLibraryLowCodeComponent = () => { uid={currentDocument?.name ?? 'design-library-low-code-component'} renderKey={renderKey} > - {ViewComponent ? : null} + ); }; From e79962a5a5cd1a446c4bb0ee8b4197648f8257c8 Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Mon, 15 Jun 2026 17:54:14 +0300 Subject: [PATCH 14/32] Atoms json renderer migration (#514) --- .../src/scripts/project/atoms/constants.ts | 4 + .../cli/src/scripts/project/atoms/index.ts | 17 + .../cli/src/scripts/project/atoms/types.ts | 55 + .../src/scripts/project/atoms/update.test.ts | 140 + .../cli/src/scripts/project/atoms/update.ts | 48 + .../src/scripts/project/atoms/utils.test.ts | 349 +++ .../cli/src/scripts/project/atoms/utils.ts | 122 + .../scripts/project/atoms/validate.test.ts | 321 +++ .../cli/src/scripts/project/atoms/validate.ts | 114 + packages/cli/src/scripts/project/index.ts | 3 +- .../utils/ensure-sitecore-directory.test.ts | 36 + .../src/utils/ensure-sitecore-directory.ts | 14 + packages/cli/tsconfig.json | 9 +- packages/content/package.json | 1 + .../atoms/component-layout/document.test.ts | 129 - .../src/atoms/component-layout/document.ts | 294 --- .../atoms/component-layout/resolver.test.ts | 279 -- .../src/atoms/component-layout/resolver.ts | 303 --- .../atoms/desing-library-bridge/constants.ts | 18 + .../desing-library-bridge/events.test.ts | 81 + .../src/atoms/desing-library-bridge/events.ts | 92 + .../src/atoms/desing-library-bridge/index.ts | 3 + .../src/atoms/desing-library-bridge/types.ts | 72 + packages/content/src/atoms/index.ts | 57 +- packages/content/src/atoms/types.ts | 69 + packages/content/src/config/models.ts | 16 + .../atoms-builder/atoms-builder.test.ts | 214 -- .../editing/atoms-builder/atoms-builder.ts | 197 -- .../src/editing/atoms-builder/index.ts | 10 - packages/content/src/editing/index.ts | 9 - packages/content/tsconfig.json | 1 + .../templates/nextjs-app-router/package.json | 8 +- .../nextjs-app-router/sitecore.cli.config.ts | 5 + .../nextjs-app-router/src/Providers.tsx | 7 +- .../nextjs-app-router/src/atoms/index.tsx | 10 + .../src/components/atoms/index.ts | 1 - .../src/templates/nextjs/package.json | 8 +- .../templates/nextjs/sitecore.cli.config.ts | 5 + .../src/templates/nextjs/src/Providers.tsx | 5 + .../src/templates/nextjs/src/atoms/index.tsx | 10 + packages/nextjs/atoms.d.ts | 1 + packages/nextjs/package.json | 5 + packages/nextjs/src/atoms/index.ts | 27 + packages/nextjs/src/atoms/re-exports.ts | 75 + .../src/config-cli/define-cli-config.ts | 7 + packages/nextjs/src/index.ts | 28 +- packages/nextjs/tsconfig.json | 2 + packages/react/package.json | 4 +- .../Wrapper/StudioComponentWrapper.test.tsx | 156 -- .../atoms/Wrapper/StudioComponentWrapper.tsx | 32 - packages/react/src/atoms/Wrapper/index.ts | 8 - packages/react/src/atoms/Wrapper/models.ts | 34 - .../src/atoms/atom-registry-utils.test.ts | 274 -- .../react/src/atoms/atom-registry-utils.ts | 98 - .../src/atoms/callback-registry-utils.test.ts | 213 -- .../src/atoms/callback-registry-utils.ts | 33 - .../src/atoms/catalog-serializer.test.ts | 166 ++ .../react/src/atoms/catalog-serializer.ts | 57 + .../component-layout/createView.test.tsx | 2255 ----------------- .../src/atoms/component-layout/createView.tsx | 319 --- .../react/src/atoms/component-layout/index.ts | 5 - packages/react/src/atoms/create-ncc.test.tsx | 36 + packages/react/src/atoms/create-ncc.tsx | 57 + packages/react/src/atoms/createAtom.test.ts | 216 -- packages/react/src/atoms/createAtom.ts | 77 - .../react/src/atoms/createCallback.test.ts | 76 - packages/react/src/atoms/createCallback.ts | 33 - .../react/src/atoms/define-atoms-catalog.ts | 45 + .../react/src/atoms/define-atoms-registry.ts | 34 + .../react/src/atoms/field-schemas.test.ts | 109 +- packages/react/src/atoms/field-schemas.ts | 15 +- packages/react/src/atoms/index.ts | 30 +- packages/react/src/atoms/re-exports.ts | 23 + packages/react/src/atoms/schema-utils.ts | 48 +- packages/react/src/atoms/types.ts | 132 +- .../DesignLibrary/DesignLibrary.test.tsx | 1002 +------- .../DesignLibrary/DesignLibrary.tsx | 18 +- .../DesignLibraryLowCodeComponent.test.tsx | 601 +---- .../DesignLibraryLowCodeComponent.tsx | 32 +- .../StudioComponentServerWrapper.test.tsx | 6 +- .../StudioComponentServerWrapper.tsx | 19 +- .../StudioComponentWrapper.test.tsx | 899 +++++++ .../DesignLibrary/StudioComponentWrapper.tsx | 37 + .../src/components/SitecoreProvider.test.tsx | 18 +- .../react/src/components/SitecoreProvider.tsx | 37 +- packages/react/src/index.ts | 35 +- packages/react/tsconfig.json | 1 + 87 files changed, 3434 insertions(+), 7137 deletions(-) create mode 100644 packages/cli/src/scripts/project/atoms/constants.ts create mode 100644 packages/cli/src/scripts/project/atoms/index.ts create mode 100644 packages/cli/src/scripts/project/atoms/types.ts create mode 100644 packages/cli/src/scripts/project/atoms/update.test.ts create mode 100644 packages/cli/src/scripts/project/atoms/update.ts create mode 100644 packages/cli/src/scripts/project/atoms/utils.test.ts create mode 100644 packages/cli/src/scripts/project/atoms/utils.ts create mode 100644 packages/cli/src/scripts/project/atoms/validate.test.ts create mode 100644 packages/cli/src/scripts/project/atoms/validate.ts create mode 100644 packages/cli/src/utils/ensure-sitecore-directory.test.ts create mode 100644 packages/cli/src/utils/ensure-sitecore-directory.ts delete mode 100644 packages/content/src/atoms/component-layout/document.test.ts delete mode 100644 packages/content/src/atoms/component-layout/document.ts delete mode 100644 packages/content/src/atoms/component-layout/resolver.test.ts delete mode 100644 packages/content/src/atoms/component-layout/resolver.ts create mode 100644 packages/content/src/atoms/desing-library-bridge/constants.ts create mode 100644 packages/content/src/atoms/desing-library-bridge/events.test.ts create mode 100644 packages/content/src/atoms/desing-library-bridge/events.ts create mode 100644 packages/content/src/atoms/desing-library-bridge/index.ts create mode 100644 packages/content/src/atoms/desing-library-bridge/types.ts create mode 100644 packages/content/src/atoms/types.ts delete mode 100644 packages/content/src/editing/atoms-builder/atoms-builder.test.ts delete mode 100644 packages/content/src/editing/atoms-builder/atoms-builder.ts delete mode 100644 packages/content/src/editing/atoms-builder/index.ts create mode 100644 packages/create-content-sdk-app/src/templates/nextjs-app-router/src/atoms/index.tsx delete mode 100644 packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts create mode 100644 packages/create-content-sdk-app/src/templates/nextjs/src/atoms/index.tsx create mode 100644 packages/nextjs/atoms.d.ts create mode 100644 packages/nextjs/src/atoms/index.ts create mode 100644 packages/nextjs/src/atoms/re-exports.ts delete mode 100644 packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx delete mode 100644 packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx delete mode 100644 packages/react/src/atoms/Wrapper/index.ts delete mode 100644 packages/react/src/atoms/Wrapper/models.ts delete mode 100644 packages/react/src/atoms/atom-registry-utils.test.ts delete mode 100644 packages/react/src/atoms/atom-registry-utils.ts delete mode 100644 packages/react/src/atoms/callback-registry-utils.test.ts delete mode 100644 packages/react/src/atoms/callback-registry-utils.ts create mode 100644 packages/react/src/atoms/catalog-serializer.test.ts create mode 100644 packages/react/src/atoms/catalog-serializer.ts delete mode 100644 packages/react/src/atoms/component-layout/createView.test.tsx delete mode 100644 packages/react/src/atoms/component-layout/createView.tsx delete mode 100644 packages/react/src/atoms/component-layout/index.ts create mode 100644 packages/react/src/atoms/create-ncc.test.tsx create mode 100644 packages/react/src/atoms/create-ncc.tsx delete mode 100644 packages/react/src/atoms/createAtom.test.ts delete mode 100644 packages/react/src/atoms/createAtom.ts delete mode 100644 packages/react/src/atoms/createCallback.test.ts delete mode 100644 packages/react/src/atoms/createCallback.ts create mode 100644 packages/react/src/atoms/define-atoms-catalog.ts create mode 100644 packages/react/src/atoms/define-atoms-registry.ts create mode 100644 packages/react/src/atoms/re-exports.ts rename packages/react/src/{atoms/Wrapper => components/DesignLibrary}/StudioComponentServerWrapper.test.tsx (98%) rename packages/react/src/{atoms/Wrapper => components/DesignLibrary}/StudioComponentServerWrapper.tsx (90%) create mode 100644 packages/react/src/components/DesignLibrary/StudioComponentWrapper.test.tsx create mode 100644 packages/react/src/components/DesignLibrary/StudioComponentWrapper.tsx diff --git a/packages/cli/src/scripts/project/atoms/constants.ts b/packages/cli/src/scripts/project/atoms/constants.ts new file mode 100644 index 0000000000..cd18a35df5 --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/constants.ts @@ -0,0 +1,4 @@ +export const ATOMS_MODULE_PATH = 'src/atoms/index'; +export const LOCK_FILE_DIR = '.sitecore'; +export const LOCK_FILE_NAME = 'atoms.lock.json'; + diff --git a/packages/cli/src/scripts/project/atoms/index.ts b/packages/cli/src/scripts/project/atoms/index.ts new file mode 100644 index 0000000000..dc3a08f9ae --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/index.ts @@ -0,0 +1,17 @@ +import { Argv } from 'yargs'; +import * as update from './update'; +import * as validate from './validate'; + +export const command = ['atoms', 'a']; + +export const describe = 'Manage atom version locks and validate atom contracts'; + +/** + * @param {Argv} yargs + */ +export function builder(yargs: Argv) { + return yargs + .command([update, validate] as any) + .strict() + .demandCommand(1, 'You need to specify an atoms subcommand (update, validate)'); +} diff --git a/packages/cli/src/scripts/project/atoms/types.ts b/packages/cli/src/scripts/project/atoms/types.ts new file mode 100644 index 0000000000..872a1b547e --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/types.ts @@ -0,0 +1,55 @@ +/** + * Shape of a single atom entry in the lock file. + * @internal + */ +export interface AtomLockEntry { + /** Semver version of this component. Absent when not declared on the component. */ + version?: string; + hash: string; +} + +/** + * Shape of the lock file. + * @internal + */ +export interface AtomVersionsLock { + /** Catalog root version from `defineAtomsCatalog`. Absent when not declared. */ + version?: string; + generated: string; + atoms: Record; +} + +/** + * Atoms info extracted from the catalog, used for lock file generation and validation. + * @internal + */ +export interface AtomInfo { + version: string; + schemaHash: string; +} + +/** + * Map of atom name to its info (version and schema hash) extracted from the catalog. + * @internal + */ +export type AtomsInfoMap = Record; + +/** + * Load the raw catalog object from the project's atoms module. + * @returns The raw catalog export from the atoms module, which should include component definitions and optionally a version. + * @internal + */ +export interface CatalogLoadResult { + data?: { components?: Record>; version?: string }; + componentNames?: string[]; +} + +/** + * Result of a lock file validation. + * @internal + */ +export interface ValidateResult { + valid: boolean; + issues: string[]; +} + diff --git a/packages/cli/src/scripts/project/atoms/update.test.ts b/packages/cli/src/scripts/project/atoms/update.test.ts new file mode 100644 index 0000000000..da449d2b35 --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/update.test.ts @@ -0,0 +1,140 @@ +/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ +import { expect } from 'chai'; +import sinon from 'sinon'; +import * as utilsModule from './utils'; +import { handler } from './update'; + +describe('atoms/update handler', () => { + let loadCurrentAtomsStub: sinon.SinonStub; + let loadCatalogStub: sinon.SinonStub; + let writeLockFileStub: sinon.SinonStub; + let consoleLogStub: sinon.SinonStub; + + beforeEach(() => { + loadCurrentAtomsStub = sinon.stub(utilsModule, 'loadCurrentAtoms'); + loadCatalogStub = sinon.stub(utilsModule, 'loadCatalog'); + writeLockFileStub = sinon.stub(utilsModule, 'writeLockFile'); + consoleLogStub = sinon.stub(console, 'log'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should call loadCurrentAtoms and loadCatalog', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + expect(loadCurrentAtomsStub.calledOnce).to.be.true; + expect(loadCatalogStub.calledOnce).to.be.true; + }); + + it('should write lock file with correct atom entries', async () => { + loadCurrentAtomsStub.resolves({ + Button: { version: '1.0.0', schemaHash: 'abc123' }, + }); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + expect(writeLockFileStub.calledOnce).to.be.true; + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.atoms.Button).to.deep.equal({ version: '1.0.0', hash: 'abc123' }); + }); + + it('should include catalog version in lock when catalog declares one', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: { version: '2.0.0' } }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.version).to.equal('2.0.0'); + }); + + it('should not include version in lock when catalog has no version', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.version).to.be.undefined; + }); + + it('should not include version in lock when catalog.data is undefined', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({}); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.version).to.be.undefined; + }); + + it('should omit atom version entry when atom has no version', async () => { + loadCurrentAtomsStub.resolves({ + Card: { version: undefined, schemaHash: 'def456' }, + }); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.atoms.Card).to.deep.equal({ hash: 'def456' }); + expect(lock.atoms.Card.version).to.be.undefined; + }); + + it('should omit atom version entry when atom version is empty string', async () => { + loadCurrentAtomsStub.resolves({ + Card: { version: '', schemaHash: 'def456' }, + }); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.atoms.Card.version).to.be.undefined; + }); + + it('should set generated to a valid ISO timestamp', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(lock.generated).to.be.a('string'); + expect(new Date(lock.generated).toISOString()).to.equal(lock.generated); + }); + + it('should write a lock with multiple atoms', async () => { + loadCurrentAtomsStub.resolves({ + Button: { version: '1.0.0', schemaHash: 'hash1' }, + Card: { version: undefined, schemaHash: 'hash2' }, + Banner: { version: '0.5.0', schemaHash: 'hash3' }, + }); + loadCatalogStub.returns({ data: { version: '3.0.0' } }); + + await handler(); + + const lock = writeLockFileStub.firstCall.args[0]; + expect(Object.keys(lock.atoms)).to.have.length(3); + expect(lock.atoms.Button).to.deep.equal({ version: '1.0.0', hash: 'hash1' }); + expect(lock.atoms.Card).to.deep.equal({ hash: 'hash2' }); + expect(lock.atoms.Banner).to.deep.equal({ version: '0.5.0', hash: 'hash3' }); + expect(lock.version).to.equal('3.0.0'); + }); + + it('should log success message after writing', async () => { + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler(); + + expect(consoleLogStub.calledWith('[atoms update] Lock file updated successfully.')).to.be.true; + }); +}); + diff --git a/packages/cli/src/scripts/project/atoms/update.ts b/packages/cli/src/scripts/project/atoms/update.ts new file mode 100644 index 0000000000..fab749464a --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/update.ts @@ -0,0 +1,48 @@ +import { AtomLockEntry, AtomVersionsLock } from './types'; +import { loadCatalog, loadCurrentAtoms, writeLockFile } from './utils'; + +export const command = 'update'; + +export const describe = + 'Regenerate the atom versions lock file from the current atom definitions. Run after intentional schema changes.'; + +export const builder = { + config: { + requiresArg: false, + type: 'string', + describe: 'Path to the `sitecore.cli.config` file.', + }, +}; + +export type UpdateArgs = { + config?: string; +}; + +/** + * Handler for `sitecore-tools project atoms update`. + * Regenerates `.sitecore/atom-versions.lock.json` from current atom definitions. + */ +export async function handler() { + const currentAtoms = await loadCurrentAtoms(); + const catalog = loadCatalog(); + const catalogData = catalog.data as Record; + const catalogVersion = typeof catalogData?.version === 'string' ? catalogData.version : undefined; + + const atoms: Record = {}; + for (const [name, def] of Object.entries(currentAtoms)) { + atoms[name] = { + ...(def.version && { version: def.version }), + hash: def.schemaHash, + }; + } + + const lock: AtomVersionsLock = { + ...(catalogVersion !== undefined && { version: catalogVersion }), + generated: new Date().toISOString(), + atoms, + }; + + writeLockFile(lock); + + console.log('[atoms update] Lock file updated successfully.'); +} diff --git a/packages/cli/src/scripts/project/atoms/utils.test.ts b/packages/cli/src/scripts/project/atoms/utils.test.ts new file mode 100644 index 0000000000..262acf33f0 --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/utils.test.ts @@ -0,0 +1,349 @@ +/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ +import { expect } from 'chai'; +import sinon from 'sinon'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; +import proxyquire from 'proxyquire'; +import * as ensureDirModule from '../../../utils/ensure-sitecore-directory'; +import { + getLockFilePath, + hashSchema, + readLockFile, + writeLockFile, + resolveAtomsModulePath, +} from './utils'; + +describe('atoms/utils', () => { + afterEach(() => { + sinon.restore(); + }); + + describe('getLockFilePath', () => { + it('should return a path combining cwd, lock dir and lock file name', () => { + sinon.stub(process, 'cwd').returns('/project'); + const result = getLockFilePath(); + expect(result).to.equal(path.resolve('/project', '.sitecore', 'atoms.lock.json')); + }); + }); + + describe('hashSchema', () => { + it('should return a sha256 hex string for a given object', () => { + const result = hashSchema({ foo: 'bar' }); + const expected = crypto.createHash('sha256').update('{"foo":"bar"}').digest('hex'); + expect(result).to.equal(expected); + }); + + it('should produce the same hash for identical inputs', () => { + expect(hashSchema({ x: 1, y: 2 })).to.equal(hashSchema({ x: 1, y: 2 })); + }); + + it('should produce different hashes for different inputs', () => { + expect(hashSchema({ x: 1 })).to.not.equal(hashSchema({ x: 2 })); + }); + + it('should handle nested objects', () => { + const result = hashSchema({ props: { title: 'string' } }); + expect(result).to.be.a('string').with.length(64); + }); + }); + + describe('readLockFile', () => { + let ensureDirStub: sinon.SinonStub; + + beforeEach(() => { + ensureDirStub = sinon.stub(ensureDirModule, 'ensureSitecoreDirectory'); + }); + + it('should call ensureSitecoreDirectory', () => { + sinon.stub(fs, 'existsSync').returns(false); + readLockFile(); + expect(ensureDirStub.calledOnce).to.be.true; + }); + + it('should return null when the lock file does not exist', () => { + sinon.stub(fs, 'existsSync').returns(false); + const result = readLockFile(); + expect(result).to.be.null; + }); + + it('should return parsed lock file content when it exists', () => { + const lockData = { version: '1.0.0', generated: '2024-01-01T00:00:00Z', atoms: {} }; + sinon.stub(fs, 'existsSync').returns(true); + sinon.stub(fs, 'readFileSync').returns(JSON.stringify(lockData)); + const result = readLockFile(); + expect(result).to.deep.equal(lockData); + }); + }); + + describe('writeLockFile', () => { + it('should create directory when it does not exist and write the file', () => { + const lockData = { generated: '2024-01-01T00:00:00Z', atoms: {} }; + sinon.stub(fs, 'existsSync').returns(false); + const mkdirStub = sinon.stub(fs, 'mkdirSync'); + const writeStub = sinon.stub(fs, 'writeFileSync'); + + writeLockFile(lockData as any); + + expect(mkdirStub.calledOnce).to.be.true; + expect(writeStub.calledOnce).to.be.true; + const [, content] = writeStub.firstCall.args; + expect(content).to.include('"atoms"'); + expect(content).to.include('"generated"'); + }); + + it('should not create directory when it already exists', () => { + const lockData = { generated: '2024-01-01T00:00:00Z', atoms: {} }; + sinon.stub(fs, 'existsSync').returns(true); + const mkdirStub = sinon.stub(fs, 'mkdirSync'); + sinon.stub(fs, 'writeFileSync'); + + writeLockFile(lockData as any); + + expect(mkdirStub.notCalled).to.be.true; + }); + + it('should write JSON followed by a newline', () => { + const lockData = { generated: '2024-01-01T00:00:00Z', atoms: {} }; + sinon.stub(fs, 'existsSync').returns(true); + const writeStub = sinon.stub(fs, 'writeFileSync'); + + writeLockFile(lockData as any); + + const [, content] = writeStub.firstCall.args; + expect((content as string).endsWith('\n')).to.be.true; + }); + }); + + describe('resolveAtomsModulePath', () => { + it('should return the .ts path when it exists', () => { + sinon.stub(fs, 'existsSync').callsFake((p: any) => String(p).endsWith('.ts')); + const result = resolveAtomsModulePath(); + expect(result).to.not.be.null; + expect(result).to.match(/\.ts$/); + }); + + it('should return the .tsx path when .ts does not exist but .tsx does', () => { + sinon.stub(fs, 'existsSync').callsFake((p: any) => String(p).endsWith('.tsx')); + const result = resolveAtomsModulePath(); + expect(result).to.not.be.null; + expect(result).to.match(/\.tsx$/); + }); + + it('should return null when neither .ts nor .tsx exists', () => { + sinon.stub(fs, 'existsSync').returns(false); + const result = resolveAtomsModulePath(); + expect(result).to.be.null; + }); + + it('should prefer .ts over .tsx', () => { + sinon.stub(fs, 'existsSync').returns(true); + const result = resolveAtomsModulePath(); + expect(result).to.match(/\.ts$/); + expect(result).to.not.match(/\.tsx$/); + }); + }); + + describe('loadCatalog', () => { + let fsExistsSyncStub: sinon.SinonStub; + let tsxRequireStub: sinon.SinonStub; + let utilsProxied: any; + + beforeEach(() => { + fsExistsSyncStub = sinon.stub(fs, 'existsSync'); + tsxRequireStub = sinon.stub(); + utilsProxied = proxyquire('./utils', { + 'tsx/cjs/api': { require: tsxRequireStub }, + }); + }); + + it('should throw when atoms module is not found', () => { + fsExistsSyncStub.returns(false); + expect(() => utilsProxied.loadCatalog()).to.throw('Atoms module not found'); + }); + + it('should throw when module does not export catalog', () => { + fsExistsSyncStub.returns(true); + tsxRequireStub.returns({}); + expect(() => utilsProxied.loadCatalog()).to.throw('does not export "catalog"'); + }); + + it('should return catalog from module.catalog', () => { + const mockCatalog = { data: { components: {} }, componentNames: [] }; + fsExistsSyncStub.returns(true); + tsxRequireStub.returns({ catalog: mockCatalog }); + const result = utilsProxied.loadCatalog(); + expect(result).to.deep.equal(mockCatalog); + }); + + it('should return catalog from module.default.catalog', () => { + const mockCatalog = { data: { components: {} }, componentNames: [] }; + fsExistsSyncStub.returns(true); + tsxRequireStub.returns({ default: { catalog: mockCatalog } }); + const result = utilsProxied.loadCatalog(); + expect(result).to.deep.equal(mockCatalog); + }); + }); + + describe('loadCurrentAtoms', () => { + let fsExistsSyncStub: sinon.SinonStub; + let tsxRequireStub: sinon.SinonStub; + let utilsProxied: any; + + beforeEach(() => { + fsExistsSyncStub = sinon.stub(fs, 'existsSync'); + tsxRequireStub = sinon.stub(); + utilsProxied = proxyquire('./utils', { + 'tsx/cjs/api': { require: tsxRequireStub }, + }); + }); + + it('should throw when atoms module is not found', async () => { + fsExistsSyncStub.returns(false); + try { + await utilsProxied.loadCurrentAtoms(); + expect.fail('expected to throw'); + } catch (err: any) { + expect(err.message).to.include('Atoms module not found'); + } + }); + + it('should return a map of atom name to version and schemaHash', async () => { + fsExistsSyncStub.returns(true); + const catalog = { + componentNames: ['Button', 'Card'], + data: { + components: { + Button: { version: '1.2.0', label: 'Primary Button' }, + Card: { props: { title: 'string' } }, + }, + }, + }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result.Button).to.exist; + expect(result.Button.version).to.equal('1.2.0'); + expect(result.Button.schemaHash).to.be.a('string').with.length(64); + expect(result.Card).to.exist; + expect(result.Card.version).to.be.undefined; + expect(result.Card.schemaHash).to.be.a('string').with.length(64); + }); + + it('should exclude version from schema hash computation', async () => { + fsExistsSyncStub.returns(true); + const catalog = { + componentNames: ['Button'], + data: { + components: { + Button: { version: '1.0.0', label: 'My Button' }, + }, + }, + }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify({ label: 'My Button' }, null, 0)) + .digest('hex'); + expect(result.Button.schemaHash).to.equal(expectedHash); + }); + + it('should handle atoms with no properties besides version', async () => { + fsExistsSyncStub.returns(true); + const catalog = { + componentNames: ['Empty'], + data: { + components: { + Empty: { version: '1.0.0' }, + }, + }, + }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result.Empty.version).to.equal('1.0.0'); + // Hash is computed over {} (empty after removing version) + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify({}, null, 0)) + .digest('hex'); + expect(result.Empty.schemaHash).to.equal(expectedHash); + }); + + it('should return empty map when componentNames is empty', async () => { + fsExistsSyncStub.returns(true); + const catalog = { componentNames: [], data: { components: {} } }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result).to.deep.equal({}); + }); + + it('should default componentNames to [] when catalog does not provide it', async () => { + fsExistsSyncStub.returns(true); + // catalog has no componentNames property + const catalog = { data: { components: { Button: { version: '1.0.0' } } } }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result).to.deep.equal({}); + }); + + it('should default components to {} when catalog.data.components is absent', async () => { + fsExistsSyncStub.returns(true); + // componentNames lists an atom but data.components is missing + const catalog = { componentNames: ['Button'], data: {} }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result.Button).to.exist; + expect(result.Button.version).to.be.undefined; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify({}, null, 0)) + .digest('hex'); + expect(result.Button.schemaHash).to.equal(expectedHash); + }); + + it('should default components to {} when catalog.data is absent', async () => { + fsExistsSyncStub.returns(true); + // componentNames lists an atom but data is missing entirely + const catalog = { componentNames: ['Card'] }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result.Card).to.exist; + expect(result.Card.version).to.be.undefined; + }); + + it('should use empty object for component data when name is not in components map', async () => { + fsExistsSyncStub.returns(true); + // componentNames has 'Ghost' but components map does not + const catalog = { + componentNames: ['Ghost'], + data: { components: {} }, + }; + tsxRequireStub.returns({ catalog }); + + const result = await utilsProxied.loadCurrentAtoms(); + + expect(result.Ghost).to.exist; + expect(result.Ghost.version).to.be.undefined; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify({}, null, 0)) + .digest('hex'); + expect(result.Ghost.schemaHash).to.equal(expectedHash); + }); + }); +}); + diff --git a/packages/cli/src/scripts/project/atoms/utils.ts b/packages/cli/src/scripts/project/atoms/utils.ts new file mode 100644 index 0000000000..aba647d13d --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/utils.ts @@ -0,0 +1,122 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as crypto from 'crypto'; +import { ensureSitecoreDirectory } from '../../../utils/ensure-sitecore-directory'; +import { AtomsInfoMap, AtomVersionsLock, CatalogLoadResult } from './types'; +import { ATOMS_MODULE_PATH, LOCK_FILE_DIR, LOCK_FILE_NAME } from './constants'; + +/** + * Get the absolute path to the lock file. + * @returns {string} Absolute path to the lock file. + * @internal + */ +export function getLockFilePath(): string { + return path.resolve(process.cwd(), LOCK_FILE_DIR, LOCK_FILE_NAME); +} + +/** + * Compute a SHA-256 hash of a value serialized as JSON. + * @param {unknown} schema - The component definition to hash. + * @internal + */ +export function hashSchema(schema: unknown): string { + const json = JSON.stringify(schema, null, 0); + return crypto.createHash('sha256').update(json).digest('hex'); +} + +/** + * Read the existing lock file. Returns null if it doesn't exist. + * @internal + */ +export function readLockFile(): AtomVersionsLock | null { + ensureSitecoreDirectory(); + const lockPath = getLockFilePath(); + if (!fs.existsSync(lockPath)) return null; + + const content = fs.readFileSync(lockPath, 'utf-8'); + return JSON.parse(content) as AtomVersionsLock; +} + +/** + * Write the lock file to disk. + * @param {AtomVersionsLock} lock - The lock data to write. Will be stringified as JSON. + * @internal + */ +export function writeLockFile(lock: AtomVersionsLock): void { + const lockPath = getLockFilePath(); + const dir = path.dirname(lockPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n', 'utf-8'); +} + +/** + * Resolve the absolute path to the atoms module, trying common extensions. + * @internal + */ +export function resolveAtomsModulePath(): string | null { + const base = path.resolve(process.cwd(), ATOMS_MODULE_PATH); + const extensions = ['.ts', '.tsx']; + + for (const ext of extensions) { + if (fs.existsSync(base + ext)) return base + ext; + } + + return null; +} + +/** + * Load the raw catalog object from the project's atoms module. + * @internal + */ +export function loadCatalog(): CatalogLoadResult { + const modulePath = resolveAtomsModulePath(); + if (!modulePath) { + throw new Error( + `Atoms module not found at ${ATOMS_MODULE_PATH}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx}` + ); + } + + const tsx = require('tsx/cjs/api'); + + const atomsModule = tsx.require(modulePath, __filename); + const catalog = atomsModule.catalog ?? atomsModule.default?.catalog; + if (!catalog) + throw new Error( + `Atoms module at ${modulePath} does not export "catalog". Export the result of defineAtomsCatalog as "catalog".` + ); + + return catalog; +} + +/** + * Load the current atom definitions from the project's atoms module. + * Uses tsx to import TypeScript at runtime. + * Returns a map of atom name to { version, schemaHash }. + * @internal + */ +export async function loadCurrentAtoms(): Promise { + const modulePath = resolveAtomsModulePath(); + if (!modulePath) + throw new Error( + `Atoms module not found at ${ATOMS_MODULE_PATH}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx} and export a catalog.` + ); + + const catalog = loadCatalog(); + const result: AtomsInfoMap = {}; + const componentNames = catalog.componentNames ?? []; + const components = catalog.data?.components ?? {}; + + for (const name of componentNames) { + // Use the component's full data (props schema, slots, etc.) for schema hash except the version, which is pulled out separately. + const { version: atomVersion, ...componentData } = components[name] ?? {}; + + result[name] = { + version: (atomVersion as string) ?? undefined, + schemaHash: hashSchema(componentData), + }; + } + + return result; +} diff --git a/packages/cli/src/scripts/project/atoms/validate.test.ts b/packages/cli/src/scripts/project/atoms/validate.test.ts new file mode 100644 index 0000000000..2af578da47 --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/validate.test.ts @@ -0,0 +1,321 @@ +/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ +import { expect } from 'chai'; +import sinon from 'sinon'; +import * as utilsModule from './utils'; +import * as loadConfigModule from '../../../utils/load-config'; +import { handler } from './validate'; + +describe('atoms/validate handler', () => { + let readLockFileStub: sinon.SinonStub; + let loadCurrentAtomsStub: sinon.SinonStub; + let loadCatalogStub: sinon.SinonStub; + let loadCliConfigStub: sinon.SinonStub; + let consoleLogStub: sinon.SinonStub; + let consoleErrorStub: sinon.SinonStub; + + // Default fixtures representing a perfectly valid state + const defaultLock = { + generated: '2024-01-01T00:00:00Z', + atoms: { + Button: { hash: 'hash-button', version: '1.0.0' }, + }, + }; + + const defaultCurrentAtoms = { + Button: { version: '1.0.0', schemaHash: 'hash-button' }, + }; + + const defaultCatalog = { data: {} }; + + const noBreakConfig = { atoms: { validation: { breakOnError: false } } }; + + beforeEach(() => { + readLockFileStub = sinon.stub(utilsModule, 'readLockFile'); + loadCurrentAtomsStub = sinon.stub(utilsModule, 'loadCurrentAtoms'); + loadCatalogStub = sinon.stub(utilsModule, 'loadCatalog'); + loadCliConfigStub = sinon.stub(loadConfigModule, 'default').returns(noBreakConfig as any); + consoleLogStub = sinon.stub(console, 'log'); + consoleErrorStub = sinon.stub(console, 'error'); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should pass config arg to loadCliConfig', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves(defaultCurrentAtoms); + loadCatalogStub.returns(defaultCatalog); + + await handler({ config: './custom-config.ts' }); + + expect(loadCliConfigStub.calledOnceWith('./custom-config.ts')).to.be.true; + }); + + it('should log success when lock is valid', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves(defaultCurrentAtoms); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + expect(consoleLogStub.calledWith('[atoms validate] atoms.lock.json is up to date.')).to.be.true; + expect(consoleErrorStub.notCalled).to.be.true; + }); + + describe('lock file missing', () => { + it('should report issue when lock file is not found', async () => { + readLockFileStub.returns(null); + + await handler({}); + + expect(consoleErrorStub.calledWith('[atoms validate] Lock file validation failed:')).to.be + .true; + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('Lock file not found'))).to.be.true; + }); + }); + + describe('catalog version validation', () => { + it('should report version mismatch when lock and catalog versions differ', async () => { + readLockFileStub.returns({ ...defaultLock, version: '1.0.0', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: { version: '2.0.0' } }); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('Catalog version mismatch'))).to.be.true; + }); + + it('should report mismatch when lock has version but catalog does not', async () => { + readLockFileStub.returns({ ...defaultLock, version: '1.0.0', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('Catalog version mismatch'))).to.be.true; + }); + + it('should report mismatch when catalog has version but lock does not', async () => { + readLockFileStub.returns({ generated: '2024-01-01T00:00:00Z', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: { version: '1.0.0' } }); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('Catalog version mismatch'))).to.be.true; + }); + + it('should not report version issue when neither lock nor catalog declare a version', async () => { + readLockFileStub.returns({ generated: '2024-01-01T00:00:00Z', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: {} }); + + await handler({}); + + expect(consoleLogStub.calledWith('[atoms validate] atoms.lock.json is up to date.')).to.be + .true; + }); + + it('should not report version issue when catalog.data is undefined', async () => { + readLockFileStub.returns({ generated: '2024-01-01T00:00:00Z', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({}); + + await handler({}); + + expect(consoleLogStub.calledWith('[atoms validate] atoms.lock.json is up to date.')).to.be + .true; + }); + + it('should not report issue when catalog versions match', async () => { + readLockFileStub.returns({ ...defaultLock, version: '1.0.0', atoms: {} }); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns({ data: { version: '1.0.0' } }); + + await handler({}); + + expect(consoleLogStub.calledWith('[atoms validate] atoms.lock.json is up to date.')).to.be + .true; + }); + }); + + describe('atom-level validation', () => { + it('should report issue when atom in lock is missing from current definitions', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves({}); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" is in the lock file but not found'))).to + .be.true; + }); + + it('should report issue when atom version in lock differs from current', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves({ + Button: { version: '2.0.0', schemaHash: 'hash-button' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" version mismatch'))).to.be.true; + }); + + it('should report issue when lock has atom version but current atom does not', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves({ + Button: { version: undefined, schemaHash: 'hash-button' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" version mismatch'))).to.be.true; + }); + + it('should report issue when current atom has version but lock does not', async () => { + readLockFileStub.returns({ + generated: '2024-01-01T00:00:00Z', + atoms: { Button: { hash: 'hash-button' } }, + }); + loadCurrentAtomsStub.resolves({ + Button: { version: '1.0.0', schemaHash: 'hash-button' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" version mismatch'))).to.be.true; + }); + + it('should not report version issue when neither lock nor current atom declare a version', async () => { + readLockFileStub.returns({ + generated: '2024-01-01T00:00:00Z', + atoms: { Button: { hash: 'hash-button' } }, + }); + loadCurrentAtomsStub.resolves({ + Button: { version: undefined, schemaHash: 'hash-button' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + expect(consoleLogStub.calledWith('[atoms validate] atoms.lock.json is up to date.')).to.be + .true; + }); + + it('should report issue when atom schema hash has changed', async () => { + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves({ + Button: { version: '1.0.0', schemaHash: 'new-hash-button' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" schema has changed'))).to.be.true; + }); + + it('should report issue when current has a new atom not present in lock', async () => { + readLockFileStub.returns({ generated: '2024-01-01T00:00:00Z', atoms: {} }); + loadCurrentAtomsStub.resolves({ + NewAtom: { version: '1.0.0', schemaHash: 'hash-new' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"NewAtom" is new and not in the lock file'))).to + .be.true; + }); + + it('should accumulate multiple issues across atoms', async () => { + readLockFileStub.returns({ + generated: '2024-01-01T00:00:00Z', + atoms: { + Button: { hash: 'hash-button', version: '1.0.0' }, + Card: { hash: 'hash-card' }, + }, + }); + loadCurrentAtomsStub.resolves({ + Button: { version: '2.0.0', schemaHash: 'hash-button' }, + NewAtom: { version: undefined, schemaHash: 'hash-new' }, + }); + loadCatalogStub.returns(defaultCatalog); + + await handler({}); + + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.some((msg) => msg.includes('"Button" version mismatch'))).to.be.true; + expect(errorArgs.some((msg) => msg.includes('"Card" is in the lock file but not found'))).to + .be.true; + expect(errorArgs.some((msg) => msg.includes('"NewAtom" is new and not in the lock file'))).to + .be.true; + }); + }); + + describe('breakOnError behavior', () => { + it('should throw when breakOnError is true and validation fails', async () => { + loadCliConfigStub.returns({ atoms: { validation: { breakOnError: true } } } as any); + readLockFileStub.returns(null); + + try { + await handler({}); + expect.fail('expected to throw'); + } catch (err: any) { + expect(err.message).to.include('Atom validation failed'); + } + }); + + it('should not throw when breakOnError is false and validation fails', async () => { + loadCliConfigStub.returns({ atoms: { validation: { breakOnError: false } } } as any); + readLockFileStub.returns(null); + + await handler({}); + expect(consoleErrorStub.called).to.be.true; + }); + + it('should default breakOnError to false when atoms config is absent', async () => { + loadCliConfigStub.returns({} as any); + readLockFileStub.returns(null); + + await handler({}); + expect(consoleErrorStub.called).to.be.true; + }); + + it('should log all individual issues before throwing', async () => { + loadCliConfigStub.returns({ atoms: { validation: { breakOnError: true } } } as any); + readLockFileStub.returns(defaultLock); + loadCurrentAtomsStub.resolves({ + Button: { version: '2.0.0', schemaHash: 'different-hash' }, + }); + loadCatalogStub.returns(defaultCatalog); + + try { + await handler({}); + } catch { + // expected + } + + // Both the header and individual issues should be logged before the throw + expect(consoleErrorStub.calledWith('[atoms validate] Lock file validation failed:')).to.be + .true; + const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); + expect(errorArgs.length).to.be.greaterThan(1); + }); + }); +}); + diff --git a/packages/cli/src/scripts/project/atoms/validate.ts b/packages/cli/src/scripts/project/atoms/validate.ts new file mode 100644 index 0000000000..f64303066b --- /dev/null +++ b/packages/cli/src/scripts/project/atoms/validate.ts @@ -0,0 +1,114 @@ +import loadCliConfig from '../../../utils/load-config'; +import { ValidateResult } from './types'; +import { loadCatalog, loadCurrentAtoms, readLockFile } from './utils'; + +export const command = 'validate'; + +export const describe = + 'Validate that the current atom implementations match the lock file. Fails if any atom has changed without a version bump.'; + +export const builder = { + config: { + requiresArg: false, + type: 'string', + describe: 'Path to the `sitecore.cli.config` file.', + }, +}; + +export type ValidateArgs = { + config?: string; +}; + +/** + * Handler for `sitecore-tools project atoms validate`. + * Reads the lock file and compares hashes against current atom definitions. + * @param {ValidateArgs} argv - The arguments passed to the command. + */ +export async function handler(argv: ValidateArgs) { + const cliConfig = loadCliConfig(argv.config); + const breakOnError = cliConfig.atoms?.validation?.breakOnError ?? false; + + const result = await validateLockFile(); + + if (!result.valid) { + console.error('[atoms validate] Lock file validation failed:'); + for (const issue of result.issues) { + console.error(` - ${issue}`); + } + + if (breakOnError) + throw new Error( + 'Atom validation failed. See issues above. You see this error because `breakOnError` is enabled in your CLI config.' + ); + } else console.log('[atoms validate] atoms.lock.json is up to date.'); +} + +/** + * Validate the lock file against current atom definitions. + * @returns A result with issues if any atom hash or version does not match. + * @internal + */ +async function validateLockFile(): Promise { + const lock = readLockFile(); + if (!lock) + return { + valid: false, + issues: ['Lock file not found. Run `sitecore-tools project atoms update` to generate it.'], + }; + + const currentAtoms = await loadCurrentAtoms(); + const catalogData = loadCatalog().data; + const catalogVersion = typeof catalogData?.version === 'string' ? catalogData.version : undefined; + const issues: string[] = []; + + // Check catalog root version drift (skip only when neither side declares a version) + if (lock.version !== undefined || catalogVersion !== undefined) { + if (catalogVersion !== lock.version) { + const lockSide = lock.version !== undefined ? `"${lock.version}"` : 'not set'; + const currentSide = catalogVersion !== undefined ? `"${catalogVersion}"` : 'not set'; + issues.push( + `Catalog version mismatch: lock file has ${lockSide}, current is ${currentSide}.` + ); + } + } + + // Check for atoms in lock but missing from current definitions + for (const [name, entry] of Object.entries(lock.atoms)) { + if (!currentAtoms[name]) { + issues.push(`Atom "${name}" is in the lock file but not found in current definitions.`); + continue; + } + + const current = currentAtoms[name]; + const currentVersion = current.version || undefined; + + // Check per-atom version drift (skip only when neither side declares a version) + if (entry.version !== undefined || currentVersion !== undefined) { + if (entry.version !== currentVersion) { + const lockSide = entry.version !== undefined ? `"${entry.version}"` : 'not set'; + const currentSide = currentVersion !== undefined ? `"${currentVersion}"` : 'not set'; + issues.push( + `Atom "${name}" version mismatch: lock file has ${lockSide}, current is ${currentSide}.` + ); + } + } + + if (current.schemaHash !== entry.hash) { + issues.push(`Atom "${name}" schema has changed.`); + } + } + + // Check for new atoms not in lock file + for (const name of Object.keys(currentAtoms)) { + if (!lock.atoms[name]) + issues.push( + `Atom "${name}" is new and not in the lock file. ` + + `Run \`sitecore-tools project atoms update\` to add it.` + ); + } + + return { + valid: issues.length === 0, + issues, + }; +} diff --git a/packages/cli/src/scripts/project/index.ts b/packages/cli/src/scripts/project/index.ts index c5a422b442..81bab48688 100644 --- a/packages/cli/src/scripts/project/index.ts +++ b/packages/cli/src/scripts/project/index.ts @@ -2,6 +2,7 @@ import { Argv } from 'yargs'; import * as build from './build'; import * as component from './component'; +import * as atoms from './atoms'; /** * @param {Argv} yargs @@ -12,7 +13,7 @@ export function builder(yargs: Argv) { describe: 'Performs project level operations', builder: (_yargs: Argv) => { _yargs = _yargs - .command([build, component] as any) + .command([build, component, atoms] as any) .strict() .demandCommand(1, 'You need to specify a command to run'); diff --git a/packages/cli/src/utils/ensure-sitecore-directory.test.ts b/packages/cli/src/utils/ensure-sitecore-directory.test.ts new file mode 100644 index 0000000000..a7ddbc0af6 --- /dev/null +++ b/packages/cli/src/utils/ensure-sitecore-directory.test.ts @@ -0,0 +1,36 @@ +/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ +import { expect } from 'chai'; +import fs from 'fs'; +import path from 'path'; +import sinon from 'sinon'; +import { ensureSitecoreDirectory } from './ensure-sitecore-directory'; + +describe('ensureSitecoreDirectory', () => { + afterEach(() => { + sinon.restore(); + }); + + it('should create the output directory when it does not exist', () => { + sinon.stub(fs, 'existsSync').returns(false); + sinon.stub(fs, 'mkdirSync'); + + ensureSitecoreDirectory('.sitecore'); + + const outputPath = path.resolve(process.cwd(), '.sitecore'); + expect((fs.existsSync as sinon.SinonStub).calledWith(outputPath)).to.be.true; + expect( + (fs.mkdirSync as sinon.SinonStub).calledOnceWithExactly(outputPath, { + recursive: true, + }) + ).to.be.true; + }); + + it('should not create the output directory when it already exists', () => { + sinon.stub(fs, 'existsSync').returns(true); + sinon.stub(fs, 'mkdirSync'); + + ensureSitecoreDirectory('custom/path'); + + expect((fs.mkdirSync as sinon.SinonStub).notCalled).to.be.true; + }); +}); diff --git a/packages/cli/src/utils/ensure-sitecore-directory.ts b/packages/cli/src/utils/ensure-sitecore-directory.ts new file mode 100644 index 0000000000..5bbd4d3bb7 --- /dev/null +++ b/packages/cli/src/utils/ensure-sitecore-directory.ts @@ -0,0 +1,14 @@ +import fs from 'fs'; +import path from 'path'; + +/** + * Ensures the Sitecore output directory exists, creating it if necessary. + * @param {string} destination Relative path to the output directory. + */ +export function ensureSitecoreDirectory(destination = '.sitecore') { + const outputPath = path.resolve(process.cwd(), destination); + if (!fs.existsSync(outputPath)) { + console.log('[Codegen] Creating Sitecore directory:', destination); + fs.mkdirSync(outputPath, { recursive: true }); + } +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index aa47cc297a..34752f9fa1 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "rootDir": "./src", "target": "es6", "module": "commonjs", "forceConsistentCasingInFileNames": true, @@ -8,11 +9,5 @@ "typeRoots": ["node_modules/@types"], "declarationDir": "./types" }, - "exclude": [ - "node_modules", - "types", - "dist", - "src/test", - "src/**/*.test.ts", - ] + "exclude": ["node_modules", "types", "dist", "src/test", "src/**/*.test.ts"] } diff --git a/packages/content/package.json b/packages/content/package.json index 34fc9fd3b1..097ab03a0b 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -77,6 +77,7 @@ "@sitecore-content-sdk/events": "^2.0.0-beta.3" }, "dependencies": { + "@json-render/core": "0.19.0", "@sitecore-content-sdk/core": "2.0.0-beta.3", "chalk": "^4.1.2", "debug": "^4.4.0", diff --git a/packages/content/src/atoms/component-layout/document.test.ts b/packages/content/src/atoms/component-layout/document.test.ts deleted file mode 100644 index f9b54e83f0..0000000000 --- a/packages/content/src/atoms/component-layout/document.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { expect } from 'chai'; -import type { - Action, - Binding, - Element, - Node, - ShowNode, -} from './document'; -import { - hasFor, - hasShow, - isCallAction, - isElement, - isEventBinding, - isExpressionBinding, - isPrimitive, - isSetStateAction, - isShowAnd, - isShowComparison, - isShowOr, -} from './document'; - -describe('component-layout document guards', () => { - describe('isElement', () => { - it('returns true for object nodes with a type field', () => { - const el: Node = { type: 'Card', id: '1' }; - expect(isElement(el)).to.equal(true); - }); - - it('returns false for primitives', () => { - expect(isElement('text')).to.equal(false); - expect(isElement(null)).to.equal(false); - expect(isElement(42)).to.equal(false); - }); - - it('returns false for plain objects without a type field', () => { - expect(isElement({} as unknown as Node)).to.equal(false); - }); - }); - - describe('hasFor', () => { - it('returns true when for.each is a string', () => { - const el: Element = { - type: 'Row', - for: { each: '{{props.items}}', as: 'item' }, - }; - expect(hasFor(el)).to.equal(true); - }); - - it('returns false when for is missing or invalid', () => { - expect(hasFor({ type: 'Row' })).to.equal(false); - expect(hasFor({ type: 'Row', for: {} as never })).to.equal(false); - }); - - it('returns false when for.each is not a string', () => { - const el = { - type: 'Row', - for: { each: 123, as: 'item' }, - } as unknown as Element; - expect(hasFor(el)).to.equal(false); - }); - }); - - describe('hasShow', () => { - it('returns true when show is set', () => { - const show: ShowNode = { left: 'a', op: 'eq', right: 'b' }; - const el: Element = { type: 'Row', show }; - expect(hasShow(el)).to.equal(true); - }); - - it('returns false when show is null or undefined', () => { - expect(hasShow({ type: 'Row' })).to.equal(false); - expect(hasShow({ type: 'Row', show: null as never })).to.equal(false); - }); - }); - - describe('binding guards', () => { - it('isExpressionBinding / isEventBinding', () => { - const expr: Binding = { bindType: 'expression', value: '{{props.x}}' }; - const evt: Binding = { - bindType: 'event', - arguments: [], - actions: [], - }; - expect(isExpressionBinding(expr)).to.equal(true); - expect(isEventBinding(expr)).to.equal(false); - expect(isExpressionBinding(evt)).to.equal(false); - expect(isEventBinding(evt)).to.equal(true); - }); - }); - - describe('action guards', () => { - it('isSetStateAction / isCallAction', () => { - const set: Action = { setState: { x: '1' } }; - const call: Action = { call: 'cb', args: [] }; - expect(isSetStateAction(set)).to.equal(true); - expect(isCallAction(set)).to.equal(false); - expect(isSetStateAction(call)).to.equal(false); - expect(isCallAction(call)).to.equal(true); - }); - }); - - describe('isPrimitive', () => { - it('accepts null, string, number, boolean only', () => { - expect(isPrimitive(null)).to.equal(true); - expect(isPrimitive('a')).to.equal(true); - expect(isPrimitive(0)).to.equal(true); - expect(isPrimitive(false)).to.equal(true); - expect(isPrimitive({})).to.equal(false); - expect(isPrimitive(undefined)).to.equal(false); - }); - }); - - describe('show node guards', () => { - it('isShowComparison', () => { - const n: ShowNode = { left: 'x', op: 'eq', right: 'y' }; - expect(isShowComparison(n)).to.equal(true); - expect(isShowAnd(n)).to.equal(false); - expect(isShowOr(n)).to.equal(false); - }); - - it('isShowAnd / isShowOr', () => { - const and: ShowNode = { and: [{ left: 'a', op: 'eq', right: 'a' }] }; - const or: ShowNode = { or: [{ left: 'a', op: 'eq', right: 'b' }] }; - expect(isShowAnd(and)).to.equal(true); - expect(isShowOr(or)).to.equal(true); - }); - }); -}); diff --git a/packages/content/src/atoms/component-layout/document.ts b/packages/content/src/atoms/component-layout/document.ts deleted file mode 100644 index 77341e5750..0000000000 --- a/packages/content/src/atoms/component-layout/document.ts +++ /dev/null @@ -1,294 +0,0 @@ -/** - * Component Layout document types — contract between Design Studio / XM and the rendering host. - * Aligned with the Component Layout technical specification (Document, Node, Element, bindings, for, show). - */ - -/* Primitive values */ - -/** - * Primitive value in the layout tree. - * @internal - */ -export type Primitive = string | number | boolean | null; - -/* Bindings */ - -/** - * Expression binding: `{{ }}` template string resolved at runtime (props, state, item, scope, etc.). - * @internal - */ -export interface ExpressionBinding { - /** Discriminator for expression bindings. */ - bindType: 'expression'; - /** Template text inside `{{ }}` (may reference props, state, item, scope, etc.). */ - value: string; -} - -/** - * Action executed when an event fires: merges into document/component state (see `state` on {@link Document}). - * @internal - */ -export interface SetStateAction { - /** State patch: keys are state field names; values are primitives applied via setState in the runtime. */ - setState: Record; -} - -/** - * Action to call a named callback with arguments. - * @internal - */ -export interface CallAction { - /** Registered callback name (must match an entry in the callback registry). */ - call: string; - /** Arguments passed to the callback; each value may be a literal or resolved from templates. */ - args?: Primitive[]; -} - -/** - * Union of actions allowed inside an {@link EventBinding}: update state or invoke a registered callback. - * @internal - */ -export type Action = SetStateAction | CallAction; - -/** - * Event binding: DOM-style handler that runs a list of actions (setState / call). - * @internal - */ -export interface EventBinding { - /** Discriminator for event bindings. */ - bindType: 'event'; - /** Names of handler parameters (e.g. DOM event argument names) used when resolving action templates. */ - arguments: string[]; - /** Actions to run when the event fires, in order. */ - actions: Action[]; -} - -/** - * Per-prop binding on an {@link Element}: either a one-way expression or a typed event handler. - * @internal - */ -export type Binding = ExpressionBinding | EventBinding; - -/* For loop */ - -/** - * For-loop: iterate over an array resolved from a binding expression; `as` names the loop variable in child scope. - * @internal - */ -export interface ForLoop { - /** Expression (typically `state.items` or `props.rows`) that resolves to an array to iterate. */ - each: string; - /** Variable name for the current item in nested expressions (e.g. `item.id`). */ - as: string; - /** Optional expression for a stable React key per row. */ - key?: string; -} - -/* Conditional visibility (show) — tree-based */ - -/** - * Show condition: comparison (left op right). Operands are template strings resolved before compare. - * @internal - */ -export interface ShowComparison { - /** Left-hand expression (template string). */ - left: string; - /** Equality or inequality after resolving both sides. */ - op: 'eq' | 'ne'; - /** Right-hand expression (template string). */ - right: string; -} - -/** - * Show condition: logical AND — all child nodes must evaluate to true. - * @internal - */ -export interface ShowAnd { - /** Nested show conditions (comparisons or nested and/or). */ - and: ShowNode[]; -} - -/** - * Show condition: logical OR — at least one child node must evaluate to true. - * @internal - */ -export interface ShowOr { - /** Nested show conditions (comparisons or nested and/or). */ - or: ShowNode[]; -} - -/** - * Recursive show AST: leaf comparison or nested AND / OR groups. - * @internal - */ -export type ShowNode = ShowComparison | ShowAnd | ShowOr; - -/* Nodes */ - -/** - * Element node: atom type, optional id, props, bindings, children, for, show, layout. - * @internal - */ -export interface Element { - /** Atom name matching a key in the Atom Registry */ - type: string; - /** Unique identifier. Hydrated by the editor on load. Used for design-time selection and highlight metadata */ - id?: string; - /** Version number of the element. */ - version?: number; - /** Map of prop name to static Primitive value. Passed directly to the component unchanged. */ - staticProps?: Record; - /** Map of prop name to {@link Binding}. */ - bindings?: Record; - /** Child nodes of the element. */ - children?: Node[]; - /** For-loop configuration for the element. */ - for?: ForLoop; - /** Show condition for the element. */ - show?: ShowNode; -} - -/** - * Node in the layout tree: either an Element or a primitive. - * @internal - */ -export type Node = Element | Primitive; - -/* Document root */ - -/** - * Component Layout document: name, optional initial state, root node, optional runtime props. - * @internal - */ -export interface Document { - /** Human-readable identifier of the document */ - name: string; - /** Initial state for the document. Keys become targets for state.* bindings and setState actions. */ - state?: Record; - /** Root node of the component tree. */ - root: Node; - /** Static props payload spread into the runtime props object passed to the generated component. Fields are accessible via props.* bindings. */ - props?: unknown; -} - -/* Type guards */ - -const isObj = (x: unknown): x is Record => typeof x === 'object' && x !== null; - -const has = (o: T, k: PropertyKey): boolean => - Object.prototype.hasOwnProperty.call(o, k); - -/** - * Type guard: node is an Element. - * @param {Node} node - The node to check - * @returns {boolean} True if the node is an Element, false otherwise - * @internal - */ -export function isElement(node: Node): node is Element { - return isObj(node) && has(node, 'type'); -} - -/** - * Type guard: element has a for-loop. - * @param {Element} node - The element to check - * @returns {boolean} True if the element has a for-loop, false otherwise - * @internal - */ -export function hasFor(node: Element): node is Element & { for: ForLoop } { - return node.for !== null && node.for !== undefined && typeof node.for.each === 'string'; -} - -/** - * Type guard: element has a show condition. - * @param {Element} node - The element to check - * @returns {boolean} True if the element has a show condition, false otherwise - * @internal - */ -export function hasShow(node: Element): node is Element & { show: ShowNode } { - return node.show !== null && node.show !== undefined; -} - -/** - * Type guard: binding is an expression binding. - * @param {Binding} binding - The binding to check - * @returns {boolean} True if the binding is an expression binding, false otherwise - * @internal - */ -export function isExpressionBinding(binding: Binding): binding is ExpressionBinding { - return isObj(binding) && (binding as ExpressionBinding).bindType === 'expression'; -} - -/** - * Type guard: binding is an event binding. - * @param {Binding} binding - The binding to check - * @returns {boolean} True if the binding is an event binding, false otherwise - * @internal - */ -export function isEventBinding(binding: Binding): binding is EventBinding { - return isObj(binding) && (binding as EventBinding).bindType === 'event'; -} - -/** - * Type guard: action is setState. - * @param {Action} action - The action to check - * @returns {boolean} True if the action is setState, false otherwise - * @internal - */ -export function isSetStateAction(action: Action): action is SetStateAction { - return isObj(action) && has(action, 'setState'); -} - -/** - * Type guard: action is call. - * @param {Action} action - The action to check - * @returns {boolean} True if the action is call, false otherwise - * @internal - */ -export function isCallAction(action: Action): action is CallAction { - return isObj(action) && has(action, 'call'); -} - -/** - * Type guard: value is a primitive. - * @param {unknown} value - The value to check - * @returns {boolean} True if the value is a primitive, false otherwise - * @internal - */ -export function isPrimitive(value: unknown): value is Primitive { - return ( - value === null || - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' - ); -} - -/** - * Type guard: show node is a comparison. - * @param {ShowNode} node - The node to check - * @returns {boolean} True if the node is a comparison, false otherwise - * @internal - */ -export function isShowComparison(node: ShowNode): node is ShowComparison { - return isObj(node) && has(node, 'left') && has(node, 'op') && has(node, 'right'); -} - -/** - * Type guard: show node is and. - * @param {ShowNode} node - The node to check - * @returns {boolean} True if the node is an and, false otherwise - * @internal - */ -export function isShowAnd(node: ShowNode): node is ShowAnd { - return isObj(node) && has(node, 'and') && Array.isArray((node as ShowAnd).and); -} - -/** - * Type guard: show node is or. - * @param {ShowNode} node - The node to check - * @returns {boolean} True if the node is an or, false otherwise - * @internal - */ -export function isShowOr(node: ShowNode): node is ShowOr { - return isObj(node) && has(node, 'or') && Array.isArray((node as ShowOr).or); -} diff --git a/packages/content/src/atoms/component-layout/resolver.test.ts b/packages/content/src/atoms/component-layout/resolver.test.ts deleted file mode 100644 index 66d4b94949..0000000000 --- a/packages/content/src/atoms/component-layout/resolver.test.ts +++ /dev/null @@ -1,279 +0,0 @@ -/* eslint-disable no-unused-expressions */ -import { expect } from 'chai'; -import { - parseBindExpression, - resolveBindExpression, - isTemplateString, - resolveTemplateString, - evaluateShowNode, - resolveIfTemplate, - type ResolveContext, -} from './resolver'; - -const ctx: ResolveContext = { - props: { user: { name: 'Alice' }, count: 0 }, - item: { id: 'item-1', label: 'First' }, - state: { visible: true, category: 'cat' }, - event: { value: 'clicked' }, -}; - -describe('component-layout resolver', () => { - describe('parseBindExpression', () => { - it('should parse simple source-only expressions', () => { - expect(parseBindExpression('props')).to.deep.equal({ source: 'props', segments: [] }); - expect(parseBindExpression('item')).to.deep.equal({ source: 'item', segments: [] }); - expect(parseBindExpression('state')).to.deep.equal({ source: 'state', segments: [] }); - expect(parseBindExpression('event')).to.deep.equal({ source: 'event', segments: [] }); - }); - - it('should accept optional leading $', () => { - expect(parseBindExpression('$props')).to.deep.equal({ source: 'props', segments: [] }); - expect(parseBindExpression('$state.count')).to.deep.equal({ - source: 'state', - segments: [{ type: 'dot', key: 'count' }], - }); - }); - - it('should parse dot path segments', () => { - expect(parseBindExpression('props.user')).to.deep.equal({ - source: 'props', - segments: [{ type: 'dot', key: 'user' }], - }); - expect(parseBindExpression('props.user.name')).to.deep.equal({ - source: 'props', - segments: [ - { type: 'dot', key: 'user' }, - { type: 'dot', key: 'name' }, - ], - }); - expect(parseBindExpression('item.id')).to.deep.equal({ - source: 'item', - segments: [{ type: 'dot', key: 'id' }], - }); - }); - - it('should parse bracket segment with nested expression', () => { - const parsed = parseBindExpression('props.user[state.category]'); - expect(parsed.source).to.equal('props'); - expect(parsed.segments).to.have.lengthOf(2); - expect(parsed.segments[0]).to.deep.equal({ type: 'dot', key: 'user' }); - expect(parsed.segments[1]).to.have.property('type', 'bracket'); - expect( - (parsed.segments[1] as { expr: { source: string; segments: unknown[] } }).expr - ).to.deep.equal({ source: 'state', segments: [{ type: 'dot', key: 'category' }] }); - }); - - it('should parse scope-key sources (e.g. for.as)', () => { - expect(parseBindExpression('product.name')).to.deep.equal({ - source: 'product', - segments: [{ type: 'dot', key: 'name' }], - }); - expect(parseBindExpression('row.id')).to.deep.equal({ - source: 'row', - segments: [{ type: 'dot', key: 'id' }], - }); - }); - - it('should throw on empty source', () => { - expect(() => parseBindExpression('$')).to.throw(/Expected identifier/); - }); - - it('should throw on invalid syntax', () => { - expect(() => parseBindExpression('props.')).to.throw(/Expected identifier/); - expect(() => parseBindExpression('props.user.')).to.throw(/Expected identifier/); - expect(() => parseBindExpression('props[state.x')).to.throw(/Expected "\]"/); - expect(() => parseBindExpression('props user')).to.throw(/Unexpected character/); - }); - }); - - describe('resolveBindExpression', () => { - it('should resolve source-only', () => { - expect(resolveBindExpression(parseBindExpression('props'), ctx)).to.deep.equal(ctx.props); - expect(resolveBindExpression(parseBindExpression('item'), ctx)).to.equal(ctx.item); - expect(resolveBindExpression(parseBindExpression('state'), ctx)).to.deep.equal(ctx.state); - expect(resolveBindExpression(parseBindExpression('event'), ctx)).to.deep.equal(ctx.event); - }); - - it('should resolve dot paths', () => { - expect(resolveBindExpression(parseBindExpression('props.user.name'), ctx)).to.equal('Alice'); - expect(resolveBindExpression(parseBindExpression('item.label'), ctx)).to.equal('First'); - expect(resolveBindExpression(parseBindExpression('state.visible'), ctx)).to.equal(true); - }); - - it('should return undefined when path is null/undefined', () => { - expect(resolveBindExpression(parseBindExpression('props.missing'), ctx)).to.equal(undefined); - expect(resolveBindExpression(parseBindExpression('props.user.missing'), ctx)).to.equal( - undefined - ); - }); - - it('should resolve bracket segment', () => { - const ctxWithMap = { - ...ctx, - props: { ...ctx.props, cat: 'value-for-cat', other: 'other' } as Record, - }; - const parsed = parseBindExpression('props[state.category]'); - expect(resolveBindExpression(parsed, ctxWithMap)).to.equal('value-for-cat'); - }); - - it('should resolve scope-key source (for.as)', () => { - const ctxWithScope: ResolveContext = { - props: {}, - state: {}, - scope: { product: { name: 'Widget', id: 'p1' } }, - }; - expect(resolveBindExpression(parseBindExpression('product.name'), ctxWithScope)).to.equal( - 'Widget' - ); - expect(resolveBindExpression(parseBindExpression('product.id'), ctxWithScope)).to.equal('p1'); - }); - - it('should resolve item from ctx.item when provided', () => { - expect(resolveBindExpression(parseBindExpression('item.label'), ctx)).to.equal('First'); - }); - - it('should resolve item from scope.item when ctx.item is missing', () => { - const ctxItemInScope: ResolveContext = { - props: {}, - state: {}, - scope: { item: { label: 'FromScope' } }, - }; - expect(resolveBindExpression(parseBindExpression('item.label'), ctxItemInScope)).to.equal( - 'FromScope' - ); - }); - - it('should return undefined for unknown scope key when scope is missing', () => { - const ctxNoScope: ResolveContext = { props: {}, state: {} }; - expect(resolveBindExpression(parseBindExpression('product.name'), ctxNoScope)).to.equal( - undefined - ); - }); - }); - - describe('isTemplateString', () => { - it('should return true when string contains {{ }}', () => { - expect(isTemplateString('{{props.user}}')).to.be.true; - expect(isTemplateString('Hello {{state.name}}!')).to.be.true; - }); - - it('should return false when no template', () => { - expect(isTemplateString('plain')).to.be.false; - expect(isTemplateString('')).to.be.false; - }); - }); - - describe('resolveTemplateString', () => { - it('should return string as-is when no {{ }}', () => { - expect(resolveTemplateString('plain text', ctx)).to.equal('plain text'); - }); - - it('should return raw resolved value when entire string is single {{ }}', () => { - expect(resolveTemplateString('{{props.user.name}}', ctx)).to.equal('Alice'); - expect(resolveTemplateString('{{state.visible}}', ctx)).to.equal(true); - }); - - it('should interpolate mixed literal and placeholders', () => { - expect(resolveTemplateString('Hello {{props.user.name}}!', ctx)).to.equal('Hello Alice!'); - expect(resolveTemplateString('{{item.label}} - {{item.id}}', ctx)).to.equal('First - item-1'); - }); - - it('should replace undefined with empty string in mixed mode', () => { - expect(resolveTemplateString('x{{props.missing}}y', ctx)).to.equal('xy'); - }); - }); - - describe('evaluateShowNode', () => { - it('should evaluate comparison eq/ne with literal left/right', () => { - expect(evaluateShowNode({ left: 'a', op: 'eq', right: 'a' }, ctx)).to.be.true; - expect(evaluateShowNode({ left: 'a', op: 'eq', right: 'b' }, ctx)).to.be.false; - expect(evaluateShowNode({ left: 'a', op: 'ne', right: 'b' }, ctx)).to.be.true; - expect(evaluateShowNode({ left: 'a', op: 'ne', right: 'a' }, ctx)).to.be.false; - }); - - it('should resolve template left/right before comparing', () => { - expect( - evaluateShowNode( - { left: '{{state.visible}}', op: 'eq', right: 'true' }, - { ...ctx, state: { visible: true } } - ) - ).to.be.false; // resolved left is boolean true, right is string "true" - expect( - evaluateShowNode( - { left: '{{state.visible}}', op: 'eq', right: '{{state.visible}}' }, - { ...ctx, state: { visible: true } } - ) - ).to.be.true; - }); - - it('should evaluate and (all must be true)', () => { - expect( - evaluateShowNode( - { - and: [ - { left: 'a', op: 'eq', right: 'a' }, - { left: 'b', op: 'eq', right: 'b' }, - ], - }, - ctx - ) - ).to.be.true; - expect( - evaluateShowNode( - { - and: [ - { left: 'a', op: 'eq', right: 'a' }, - { left: 'a', op: 'eq', right: 'b' }, - ], - }, - ctx - ) - ).to.be.false; - }); - - it('should evaluate or (at least one true)', () => { - expect( - evaluateShowNode( - { - or: [ - { left: 'a', op: 'eq', right: 'b' }, - { left: 'a', op: 'eq', right: 'a' }, - ], - }, - ctx - ) - ).to.be.true; - expect( - evaluateShowNode( - { - or: [ - { left: 'a', op: 'eq', right: 'b' }, - { left: 'x', op: 'eq', right: 'y' }, - ], - }, - ctx - ) - ).to.be.false; - }); - }); - - describe('resolveIfTemplate', () => { - it('should resolve strings that look like template expressions', () => { - expect(resolveIfTemplate('{{props.user.name}}', ctx)).to.equal('Alice'); - }); - - it('should return non-string values unchanged', () => { - expect(resolveIfTemplate(42, ctx)).to.equal(42); - expect(resolveIfTemplate(true, ctx)).to.equal(true); - expect(resolveIfTemplate(null, ctx)).to.equal(null); - }); - - it('should return plain strings without {{ }} unchanged', () => { - expect(resolveIfTemplate('no-template-here', ctx)).to.equal('no-template-here'); - }); - - it('should delegate mixed literal + {{ }} strings to resolveTemplateString', () => { - expect(resolveIfTemplate('Hello {{props.user.name}}!', ctx)).to.equal('Hello Alice!'); - }); - }); -}); diff --git a/packages/content/src/atoms/component-layout/resolver.ts b/packages/content/src/atoms/component-layout/resolver.ts deleted file mode 100644 index 9263a93663..0000000000 --- a/packages/content/src/atoms/component-layout/resolver.ts +++ /dev/null @@ -1,303 +0,0 @@ -/** - * Expression and binding resolution for Component Layout documents. - * Parses bind expressions (props/item/state/event), resolves {{ }} template strings, - * and evaluates show conditions. - */ - -import type { ShowNode } from './document'; -import { isShowComparison, isShowAnd, isShowOr } from './document'; - -/* Expression parser */ - -/** - * Source name for bind expressions. Built-in: "props", "item", "state", "event". - * Any other identifier is resolved from context.scope (e.g. for.as loop variable name). - * @internal - */ -export type BindSource = 'props' | 'item' | 'state' | 'event' | (string & Record); - -/** - * Segment in a parsed path: dot key or bracket sub-expression. - * @internal - */ -export type BindSegment = { type: 'dot'; key: string } | { type: 'bracket'; expr: ParsedBind }; - -/** - * Parsed bind expression: source plus path segments. - * @internal - */ -export interface ParsedBind { - source: BindSource; - segments: BindSegment[]; -} - -/** - * Parses an expression string into a structured representation. - * Accepts optional leading "$" and dot/bracket path segments. - * Source may be any identifier: built-ins (props, item, state, event) or a scope key (e.g. for.as). - * @param {string} expr - Expression string (e.g. "props.user.name", "state.count", "item.id", "product.name") - * @returns {ParsedBind} Parsed expression - * @throws {Error} on invalid syntax or empty source - * @internal - */ -export function parseBindExpression(expr: string): ParsedBind { - let pos = 0; - - const skipOptionalDollar = (): void => { - if (expr[pos] === '$') { - pos++; - } - }; - - const parseIdentifier = (): string => { - const start = pos; - while (pos < expr.length && /[a-zA-Z0-9_]/.test(expr[pos])) { - pos++; - } - const identifier = expr.slice(start, pos); - - if (identifier.length === 0) { - throw new Error(`Expected identifier in "${expr}" at pos ${pos}`); - } - if (!/^[a-zA-Z_]/.test(identifier)) { - throw new Error( - `Invalid identifier "${identifier}" in "${expr}". Must start with a letter or underscore.` - ); - } - - return identifier; - }; - - const parseSegments = (): BindSegment[] => { - const segments: BindSegment[] = []; - - while (pos < expr.length) { - if (expr[pos] === '.') { - pos++; - const key = parseIdentifier(); - segments.push({ type: 'dot', key }); - } else if (expr[pos] === '[') { - pos++; // skip [ - const bracketStart = pos; - - // Find the matching ] - const closeBracket = expr.indexOf(']', pos); - if (closeBracket === -1) { - throw new Error(`Expected "]" in "${expr}"`); - } - - // Extract the inner expression between [ and ] - const innerExpr = expr.slice(bracketStart, closeBracket); - const inner = parseBindExpression(innerExpr); - - pos = closeBracket + 1; // move past ] - segments.push({ type: 'bracket', expr: inner }); - } else { - break; - } - } - - return segments; - }; - - skipOptionalDollar(); - const source = parseIdentifier(); - const segments = parseSegments(); - - if (pos !== expr.length) { - throw new Error(`Unexpected character "${expr[pos]}" in "${expr}" at pos ${pos}`); - } - - return { source, segments }; -} - -/* Resolve context and expression resolver */ - -/** - * Runtime context for resolving expressions. - * - props, state, event: built-in sources. - * - item: loop variable (backward compat); also resolvable as scope.item when scope is set. - * - scope: optional map for named loop variables (e.g. scope[for.as] = currentItem). - * @internal - */ -export interface ResolveContext { - /** Runtime props. */ - props: Record; - /** Current state. */ - state: Record; - /** Loop variable. When inside a for-loop, set to current element; also use scope.item when scope is used. */ - item?: unknown; - /** Current event. */ - event?: unknown; - /** Optional scope for named sources (e.g. for.as). Resolved after built-in props/state/event/item. */ - scope?: Record; -} - -/** - * Resolves the source identifier to its runtime value by matching on the source name. - * Built-in names (`props`, `state`, `event`, `item`) use the corresponding context fields; - * any other identifier is read from `ctx.scope[source]`. - * @param {BindSource} source - Source identifier - * @param {ResolveContext} ctx - Runtime context - * @returns {unknown} Resolved source value or undefined - */ -function resolveSource(source: BindSource, ctx: ResolveContext): unknown { - switch (source) { - case 'props': - return ctx.props; - case 'state': - return ctx.state; - case 'event': - return ctx.event; - case 'item': - return ctx.item ?? ctx.scope?.item; - default: - return ctx.scope?.[source]; - } -} - -/** - * Safely accesses a property on an object-like value. - * Returns undefined if current is null/undefined or not an object. - * @param {unknown} current - Current value - * @param {string | number} key - Property key - * @returns {unknown} Property value or undefined - */ -function safePropertyAccess(current: unknown, key: string | number): unknown { - if (current === null || current === undefined) { - return undefined; - } - - // Check if current is an object (including arrays) - if (typeof current !== 'object') { - return undefined; - } - - return (current as Record)[key]; -} - -/** - * Resolves a parsed bind expression against the given context: starts from the resolved - * source root, then walks dot/bracket segments with safe property access. - * @param {ParsedBind} parsed - Parsed expression from parseBindExpression - * @param {ResolveContext} ctx - Runtime context (props, state, event, item, scope) - * @returns {unknown} Resolved value or undefined if any segment is null/undefined - * @internal - */ -export function resolveBindExpression(parsed: ParsedBind, ctx: ResolveContext): unknown { - let current = resolveSource(parsed.source, ctx); - - for (const segment of parsed.segments) { - if (current === null || current === undefined) { - return undefined; - } - - if (segment.type === 'dot') { - current = safePropertyAccess(current, segment.key); - } else { - // Bracket accessor - resolve the expression to get the key - const key = resolveBindExpression(segment.expr, ctx); - if (key === null || key === undefined) { - return undefined; - } - current = safePropertyAccess(current, key as string | number); - } - } - - return current; -} - -/* Template string resolution ({{...}} syntax) */ - -// Match single {{expr}} so inner content cannot contain "}}" -const SINGLE_TEMPLATE_RE = /^\{\{((?:(?!\}\}).)*)\}\}$/; -const TEMPLATE_PATTERN = /\{\{((?:(?!\}\}).)*)\}\}/; -const TEMPLATE_GLOBAL = /\{\{((?:(?!\}\}).)*)\}\}/g; - -/** - * Returns true if the string contains {{...}} template expressions. - * @param {string} s - String to check - * @returns {boolean} True if the string contains {{...}} templates, false otherwise - * @internal - */ -export function isTemplateString(s: string): boolean { - return TEMPLATE_PATTERN.test(s); -} - -/** - * Resolves a template string containing {{expr}} placeholders. - * - If the entire string is a single {{expr}}, returns the raw resolved value (preserving type). - * - If mixed with literal text, interpolates and returns a string. - * - If no {{}} patterns, returns the string as-is. - * @param {string} template - String that may contain {{...}} placeholders - * @param {ResolveContext} ctx - Runtime context for resolving expressions - * @returns {unknown} Resolved value (any type for single {{}}, string otherwise) - * @internal - */ -export function resolveTemplateString(template: string, ctx: ResolveContext): unknown { - const singleMatch = template.match(SINGLE_TEMPLATE_RE); - if (singleMatch) { - const parsed = parseBindExpression(singleMatch[1].trim()); - return resolveBindExpression(parsed, ctx); - } - - if (!TEMPLATE_PATTERN.test(template)) { - return template; - } - - return template.replace(TEMPLATE_GLOBAL, (_, expr) => { - const parsed = parseBindExpression(expr.trim()); - const resolved = resolveBindExpression(parsed, ctx); - return resolved !== null && resolved !== undefined ? String(resolved) : ''; - }); -} - -/* Show condition evaluation */ - -/** - * Evaluates a ShowNode tree against the runtime context. - * Left/right in comparisons may be template strings and are resolved before comparing. - * @param {ShowNode} node - Show condition node (comparison or and/or tree) - * @param {ResolveContext} ctx - Runtime context - * @returns {boolean} True if the condition passes (element should be visible) - * @internal - */ -export function evaluateShowNode(node: ShowNode, ctx: ResolveContext): boolean { - if (isShowAnd(node)) { - return node.and.every((child) => evaluateShowNode(child, ctx)); - } - if (isShowOr(node)) { - return node.or.some((child) => evaluateShowNode(child, ctx)); - } - if (isShowComparison(node)) { - const left = isTemplateString(node.left) ? resolveTemplateString(node.left, ctx) : node.left; - const right = isTemplateString(node.right) - ? resolveTemplateString(node.right, ctx) - : node.right; - switch (node.op) { - case 'eq': - return left === right; - case 'ne': - return left !== right; - default: - return true; - } - } - return true; -} - -/** - * Resolves a template string value against the provided context. - * Returns the original value when it is not a template string. - * @param {unknown} value - Value that may be a template string - * @param {ResolveContext} ctx - Resolve context - * @returns {unknown} Resolved value or original value - * @internal - */ -export const resolveIfTemplate = (value: unknown, ctx: ResolveContext): unknown => { - if (typeof value === 'string' && isTemplateString(value)) { - return resolveTemplateString(value, ctx); - } - - return value; -}; diff --git a/packages/content/src/atoms/desing-library-bridge/constants.ts b/packages/content/src/atoms/desing-library-bridge/constants.ts new file mode 100644 index 0000000000..fbefd48bb2 --- /dev/null +++ b/packages/content/src/atoms/desing-library-bridge/constants.ts @@ -0,0 +1,18 @@ +/** + * Event name for component preview updates from design library + * @internal + */ +export const DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME = 'component:atoms:preview'; + +/** + * Event name for the atoms catalog registration (replaces legacy `atom:registry`). + * @internal + */ +export const DESIGN_LIBRARY_ATOMS_CATALOG_EVENT_NAME = 'atoms:catalog'; + +/** + * Event to send to design library when rendering atoms error occurs + * @internal + */ +export const DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME = 'atoms:error'; + diff --git a/packages/content/src/atoms/desing-library-bridge/events.test.ts b/packages/content/src/atoms/desing-library-bridge/events.test.ts new file mode 100644 index 0000000000..8513bfc1b4 --- /dev/null +++ b/packages/content/src/atoms/desing-library-bridge/events.test.ts @@ -0,0 +1,81 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { AtomsCatalogPayload } from './types'; +import { + addDocumentUpdateHandler, + getDesignLibraryAtomsCatalogEvent, + getDesignLibraryAtomsErrorEvent, +} from './events'; + +describe('design-library-bridge events', () => { + describe('getDesignLibraryAtomsCatalogEvent', () => { + it('returns an event with name "atoms:catalog"', () => { + const payload: AtomsCatalogPayload = { components: [], actions: [] }; + const event = getDesignLibraryAtomsCatalogEvent(payload); + + expect(event.name).to.equal('atoms:catalog'); + }); + + it('returns an event containing the provided catalog payload', () => { + const payload: AtomsCatalogPayload = { + components: [ + { + name: 'Button', + propsSchema: { type: 'object' }, + description: 'A button', + slots: ['default'], + }, + ], + actions: [{ name: 'submit', paramsSchema: { type: 'object' }, description: 'Submit form' }], + }; + + const event = getDesignLibraryAtomsCatalogEvent(payload); + + expect(event.message.components).to.have.length(1); + expect(event.message.components[0].name).to.equal('Button'); + expect(event.message.actions).to.have.length(1); + expect(event.message.actions[0].name).to.equal('submit'); + }); + }); + + describe('getDesignLibraryAtomsErrorEvent', () => { + it('returns an event with name "atoms:error"', () => { + const event = getDesignLibraryAtomsErrorEvent('some error', 'render'); + + expect(event.name).to.equal('atoms:error'); + expect(event.message.error).to.equal('some error'); + expect(event.message.type).to.equal('render'); + }); + }); + + describe('addDocumentUpdateHandler', () => { + let addEventListener: sinon.SinonStub; + let removeEventListener: sinon.SinonStub; + + beforeEach(() => { + addEventListener = sinon.stub(); + removeEventListener = sinon.stub(); + (global as any).window = { addEventListener, removeEventListener }; + }); + + afterEach(() => { + (global as any).window = undefined; + }); + + it('registers a message event listener', () => { + const callback = sinon.stub(); + addDocumentUpdateHandler(callback); + + expect(addEventListener).to.have.been.calledWith('message', sinon.match.func); + }); + + it('returns an unsubscribe function that removes the listener', () => { + const callback = sinon.stub(); + const unsubscribe = addDocumentUpdateHandler(callback); + + unsubscribe(); + + expect(removeEventListener).to.have.been.calledWith('message', sinon.match.func); + }); + }); +}); diff --git a/packages/content/src/atoms/desing-library-bridge/events.ts b/packages/content/src/atoms/desing-library-bridge/events.ts new file mode 100644 index 0000000000..56fcbacf57 --- /dev/null +++ b/packages/content/src/atoms/desing-library-bridge/events.ts @@ -0,0 +1,92 @@ +import { constants } from '@sitecore-content-sdk/core'; +import { validateEvent } from '../../editing/design-library'; +import { Document, SerializedCatalog } from '../types'; +import { + DESIGN_LIBRARY_ATOMS_CATALOG_EVENT_NAME, + DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME, + DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME, +} from './constants'; +import { + DesignLibraryAtomsCatalogEvent, + DesignLibraryAtomsError, + DesignLibraryAtomsErrorEvent, +} from './types'; + +const { ERROR_MESSAGES } = constants; + +/** + * Creates a DesignLibraryAtomsCatalogEvent with the given catalog payload. + * @param {AtomsCatalogPayload} payload - serialized catalog data + * @returns {DesignLibraryAtomsCatalogEvent} the event ready to be posted to Design Studio + * @internal + */ +export function getDesignLibraryAtomsCatalogEvent( + payload: SerializedCatalog +): DesignLibraryAtomsCatalogEvent { + return { + name: DESIGN_LIBRARY_ATOMS_CATALOG_EVENT_NAME, + message: payload as unknown as Record, + }; +} + +/** + * Generates a DesignLibraryAtomsErrorEvent depending on the type of error with the given error. + * @param {unknown} error - The error to be sent. + * @param {DesignLibraryAtomsError} type - The type of error. + * @returns An object representing the DesignLibraryAtomsErrorEvent. + * @internal + */ +export function getDesignLibraryAtomsErrorEvent( + error: unknown, + type: DesignLibraryAtomsError +): DesignLibraryAtomsErrorEvent { + return { + name: DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME, + message: { error, type }, + }; +} + +/** + * Sends a design library atoms error event to the design library + * @param {unknown} error - The error object or message to be sent. + * @param {DesignLibraryAtomsError} type - The type of error, as defined in DesignLibraryAtomsError. + * @internal + */ +export const sendAtomsErrorEvent = (error: unknown, type: DesignLibraryAtomsError) => { + const errorEvent = getDesignLibraryAtomsErrorEvent(error, type); + console.error( + `Component Library: sending error event. ${ERROR_MESSAGES.CONTACT_SUPPORT}`, + errorEvent + ); + if (typeof window !== 'undefined') { + const target = window.parent && window.parent !== window ? window.parent : window; + target.postMessage(errorEvent, '*'); + } +}; + +/** + * Adds a handler for atom document update events from the design library. + * @param {(updatedRootComponent: Document) => void} callback - The callback to be invoked when a document update event is received. + * @returns A function to unsubscribe from the atom document update events. + * @internal + */ +export const addDocumentUpdateHandler = (callback: (updatedRootComponent: Document) => void) => { + const handler = (e: MessageEvent) => { + if (!validateEvent(e, DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME)) { + return; + } + + console.debug('Component Library atoms: message received', e.data); + + callback(e.data.document as Document); + }; + + window.addEventListener('message', handler); + + const unsubscribe = () => { + window.removeEventListener('message', handler); + }; + + return unsubscribe; +}; + diff --git a/packages/content/src/atoms/desing-library-bridge/index.ts b/packages/content/src/atoms/desing-library-bridge/index.ts new file mode 100644 index 0000000000..20dc99096a --- /dev/null +++ b/packages/content/src/atoms/desing-library-bridge/index.ts @@ -0,0 +1,3 @@ +export * from './events'; +export * from './types'; + diff --git a/packages/content/src/atoms/desing-library-bridge/types.ts b/packages/content/src/atoms/desing-library-bridge/types.ts new file mode 100644 index 0000000000..0f1ffbf87c --- /dev/null +++ b/packages/content/src/atoms/desing-library-bridge/types.ts @@ -0,0 +1,72 @@ +import { DesignLibraryEvent } from '../../editing/design-library'; +import { + DESIGN_LIBRARY_ATOMS_CATALOG_EVENT_NAME, + DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME, +} from './constants'; + +/** + * Enumeration of error types for the design library atoms. + * @internal + */ +export type DesignLibraryAtomsError = 'render' | 'atoms-missing'; + +/** + * Represents a atom rendering error event to be sent to design library + * @internal + */ +export interface DesignLibraryAtomsErrorEvent extends DesignLibraryEvent { + name: typeof DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME; + message: { + error: unknown; + type: DesignLibraryAtomsError; + }; +} + +/** + * Serialized component entry in the catalog payload sent to Design Studio. + * @internal + */ +export interface AtomCatalogEntry { + /** Component name (key in the catalog). */ + name: string; + /** JSON Schema representation of the component props. */ + propsSchema: object; + /** Human-readable description. */ + description: string; + /** Named slots (children). */ + slots: string[]; +} + +/** + * Serialized action entry in the catalog payload sent to Design Studio. + * @internal + */ +export interface ActionCatalogEntry { + /** Action name (key in the catalog). */ + name: string; + /** JSON Schema representation of the action params. */ + paramsSchema: object; + /** Human-readable description. */ + description: string; +} + +/** + * Payload of the atoms:catalog event sent to Design Studio. + * @internal + */ +export interface AtomsCatalogPayload { + /** Serialized component entries. */ + components: AtomCatalogEntry[]; + /** Serialized action entries. */ + actions: ActionCatalogEntry[]; +} + +/** + * Represents the atoms:catalog event sent to Design Studio. + * @internal + */ +export interface DesignLibraryAtomsCatalogEvent extends DesignLibraryEvent { + name: typeof DESIGN_LIBRARY_ATOMS_CATALOG_EVENT_NAME; + message: Record; +} + diff --git a/packages/content/src/atoms/index.ts b/packages/content/src/atoms/index.ts index f8e5ff93c7..38d9dc123f 100644 --- a/packages/content/src/atoms/index.ts +++ b/packages/content/src/atoms/index.ts @@ -1,44 +1,17 @@ -/** - * Component Layout document types, guards, and runtime resolution helpers exposed on the - * `@sitecore-content-sdk/content/atoms` entry point. Parser-level APIs (`parseBindExpression`, - * etc.) remain available from `./component-layout/resolver` within this package. - */ +export type { + SerializedCatalog, + SitecoreComponentMeta, + AtomCatalogComponentEntry, + AtomCatalogActionEntry, + Document, +} from './types'; export { - type Primitive, - type ExpressionBinding, - type EventBinding, - type Binding, - type SetStateAction, - type CallAction, - type Action, - type ForLoop, - type ShowComparison, - type ShowAnd, - type ShowOr, - type ShowNode, - type Element, - type Node, - type Document, -} from './component-layout/document'; - -export { - isElement, - hasFor, - hasShow, - isExpressionBinding, - isEventBinding, - isSetStateAction, - isCallAction, - isPrimitive, - isShowComparison, - isShowAnd, - isShowOr, -} from './component-layout/document'; - -export { - type ResolveContext, - resolveTemplateString, - evaluateShowNode, - resolveIfTemplate, -} from './component-layout/resolver'; + AtomCatalogEntry, + ActionCatalogEntry, + AtomsCatalogPayload, + getDesignLibraryAtomsCatalogEvent, + sendAtomsErrorEvent, + DesignLibraryAtomsError, + addDocumentUpdateHandler, +} from './desing-library-bridge'; diff --git a/packages/content/src/atoms/types.ts b/packages/content/src/atoms/types.ts new file mode 100644 index 0000000000..8cee8a2bad --- /dev/null +++ b/packages/content/src/atoms/types.ts @@ -0,0 +1,69 @@ +import { Spec } from '@json-render/core'; + +/** + * Serialized atom info for a single component, sent to Design Studio. + * @internal + */ +export interface AtomCatalogComponentEntry { + /** Component name (key in the catalog). */ + name: string; + /** JSON Schema representation of the component props. */ + propsSchema: object; + /** Human-readable description. */ + description?: string; + /** Named slots (children). */ + slots: string[]; + /** Semver version of this component definition. */ + version?: string; + /** Component names that are allowed as children in this component's slots. */ + allowedChildren?: string[]; + /** Component names that this component is allowed to be placed inside. */ + allowedParents?: string[]; + /** Example prop values for AI prompt generation. Auto-generated from Zod schema if omitted. */ + example?: unknown; +} + +/** + * Serialized action info, sent to Design Studio. + * @internal + */ +export interface AtomCatalogActionEntry { + /** Action name (key in the catalog). */ + name: string; + /** JSON Schema representation of the action params. */ + paramsSchema?: object; + /** Human-readable description. */ + description: string | undefined; +} + +/** + * Full catalog payload sent to Design Studio. + * @internal + */ +export interface SerializedCatalog { + /** Catalog root version from `defineAtomsCatalog`. Absent when not declared. */ + version?: string; + /** Serialized component entries. */ + components: AtomCatalogComponentEntry[]; + /** Serialized action entries. */ + actions: AtomCatalogActionEntry[]; +} + +/** + * Sitecore-specific placement metadata added to a component definition. + * @public + */ +export interface SitecoreComponentMeta { + /** Semver version of this component definition. */ + version?: string; + /** Component names that are allowed as children in this component's slots. */ + allowedChildren?: string[]; + /** Component names that this component is allowed to be placed inside. */ + allowedParents?: string[]; +} + +export interface Document extends Spec { + /** Human-readable identifier of the document. */ + name: string; +} + diff --git a/packages/content/src/config/models.ts b/packages/content/src/config/models.ts index 17acda07c3..a9c5a431be 100644 --- a/packages/content/src/config/models.ts +++ b/packages/content/src/config/models.ts @@ -260,6 +260,22 @@ export type SitecoreCliConfigInput = { */ generator?: GenerateMapFunction; }; + /** + * Configuration for the `sitecore-tools project atoms` CLI commands. + */ + atoms?: { + /** + * Validation configuration for atoms. + */ + validation?: { + /** + * When true, the CLI will exit with a non-zero code on validation errors. + * Useful for CI pipelines that should fail on broken atom contracts. + * @default false + */ + breakOnError?: boolean; + }; + }; }; /** diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.test.ts b/packages/content/src/editing/atoms-builder/atoms-builder.test.ts deleted file mode 100644 index 51861643da..0000000000 --- a/packages/content/src/editing/atoms-builder/atoms-builder.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { expect } from 'chai'; -import sinon from 'sinon'; -import type { Document } from '../../atoms/component-layout/document'; -import { AtomInfo, CallbackInfo, addDocumentUpdateHandler, getDesignLibraryAtomsRegistryEvent } from './atoms-builder'; - -describe('atoms-builder', () => { - describe('getDesignLibraryAtomsRegistryEvent', () => { - it('should return an event with name "atom:registry"', () => { - const event = getDesignLibraryAtomsRegistryEvent([], {}); - - expect(event.name).to.equal('atom:registry'); - }); - - it('should return an event with an empty atoms registry when given an empty array', () => { - const event = getDesignLibraryAtomsRegistryEvent([], {}); - - expect(event.message.atomsRegistry).to.deep.equal([]); - }); - - it('should return an event containing the provided atoms registry', () => { - const atomsRegistry: AtomInfo[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button atom', - props: { label: 'string' }, - allowedChildren: [], - }, - ]; - - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); - - expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); - }); - - it('should return an event containing multiple atoms', () => { - const atomsRegistry: AtomInfo[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button atom', - props: { label: 'string' }, - allowedChildren: ['ButtonIcon'], - }, - { - name: 'ButtonIcon', - type: 'atom-child', - description: 'An icon child of Button', - props: { src: 'string' }, - allowedChildren: [], - }, - ]; - - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); - - expect(event.message.atomsRegistry).to.have.length(2); - expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); - }); - - it('should include optional fields when provided', () => { - const atomsRegistry: AtomInfo[] = [ - { - name: 'Card', - version: 2, - type: 'atom', - description: 'A card atom with optional fields', - props: { title: 'string' }, - allowedChildren: ['CardBody'], - defaultChildren: ['CardBody', { atom: 'CardFooter', props: { cta: 'Learn more' } }], - htmlEvents: ['click', 'focus'], - customEvents: { onExpand: 'CustomExpandEvent' }, - }, - ]; - - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, {}); - - const [atom] = event.message.atomsRegistry; - expect(atom.version).to.equal(2); - expect(atom.defaultChildren).to.deep.equal([ - 'CardBody', - { atom: 'CardFooter', props: { cta: 'Learn more' } }, - ]); - expect(atom.htmlEvents).to.deep.equal(['click', 'focus']); - expect(atom.customEvents).to.deep.equal({ onExpand: 'CustomExpandEvent' }); - }); - - it('should return an event with an empty callback registry when given an empty object', () => { - const event = getDesignLibraryAtomsRegistryEvent([], {}); - - expect(event.message.callbackRegistry).to.deep.equal({}); - }); - - it('should return an event containing the provided callback registry', () => { - const callbackRegistry: Record = { - handleClick: { - description: 'Handles click events', - params: { event: 'MouseEvent' }, - }, - }; - - const event = getDesignLibraryAtomsRegistryEvent([], callbackRegistry); - - expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); - }); - - it('should return an event containing multiple callbacks', () => { - const callbackRegistry: Record = { - handleClick: { - description: 'Handles click events', - params: { event: 'MouseEvent' }, - }, - handleSubmit: { - description: 'Handles form submission', - }, - handleChange: { - description: 'Handles input changes', - params: { value: 'string', name: 'string' }, - }, - }; - - const event = getDesignLibraryAtomsRegistryEvent([], callbackRegistry); - - expect(Object.keys(event.message.callbackRegistry)).to.have.length(3); - expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); - }); - - it('should return an event with both atoms registry and callback registry', () => { - const atomsRegistry: AtomInfo[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button atom', - props: { label: 'string' }, - allowedChildren: [], - }, - ]; - - const callbackRegistry: Record = { - onClick: { - description: 'Button click handler', - params: { event: 'MouseEvent' }, - }, - }; - - const event = getDesignLibraryAtomsRegistryEvent(atomsRegistry, callbackRegistry); - - expect(event.message.atomsRegistry).to.deep.equal(atomsRegistry); - expect(event.message.callbackRegistry).to.deep.equal(callbackRegistry); - }); - }); - - describe('addDocumentUpdateHandler', () => { - let addListener: sinon.SinonStub; - let removeListener: sinon.SinonStub; - let messageHandler: ((e: MessageEvent) => void) | undefined; - - beforeEach(() => { - messageHandler = undefined; - addListener = sinon.stub().callsFake((event: string, handler: (e: MessageEvent) => void) => { - if (event === 'message') { - messageHandler = handler; - } - }); - removeListener = sinon.stub(); - (globalThis as unknown as { window: Window & typeof globalThis }).window = { - addEventListener: addListener, - removeEventListener: removeListener, - } as unknown as Window & typeof globalThis; - }); - - afterEach(() => { - delete (globalThis as unknown as { window?: Window }).window; - }); - - it('should register a message listener and invoke callback with document when event is valid', () => { - const callback = sinon.spy(); - const doc: Document = { - name: 'preview', - root: { type: 'Stack', id: 'r1', children: [] }, - }; - - const unsub = addDocumentUpdateHandler(callback); - - sinon.assert.calledWith(addListener, 'message', sinon.match.func); - - messageHandler!( - new MessageEvent('message', { - origin: 'http://localhost', - data: { name: 'component:atoms:preview', document: doc }, - }) - ); - - sinon.assert.calledOnceWithExactly(callback, doc); - expect(typeof unsub).to.equal('function'); - - unsub(); - sinon.assert.calledWith(removeListener, 'message', messageHandler); - }); - - it('should ignore messages that do not match the preview event name', () => { - const callback = sinon.spy(); - addDocumentUpdateHandler(callback); - - messageHandler!( - new MessageEvent('message', { - origin: 'http://localhost', - data: { name: 'other:event', document: {} }, - }) - ); - - sinon.assert.notCalled(callback); - }); - }); -}); diff --git a/packages/content/src/editing/atoms-builder/atoms-builder.ts b/packages/content/src/editing/atoms-builder/atoms-builder.ts deleted file mode 100644 index 10385fa230..0000000000 --- a/packages/content/src/editing/atoms-builder/atoms-builder.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { constants } from '@sitecore-content-sdk/core'; -import { DesignLibraryEvent, validateEvent } from '../design-library'; -import type { Document } from '../../atoms/component-layout/document'; - -const { ERROR_MESSAGES } = constants; - -/** - * Event name for component preview updates from design library - */ -const DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME = 'component:atoms:preview'; - -/** - * Event to send import map to design library - */ -const DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME = 'atom:registry'; - -/** - * Event to send to design library when rendering atoms error occurs - */ -const DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME = 'atoms:error'; - -/** - * Enumeration of error types for the design library atoms. - * @internal - */ -export type DesignLibraryAtomsError = 'render' | 'atoms-missing'; - -/** - * Represents a atom rendering error event to be sent to design library - * @internal - */ -export interface DesignLibraryAtomsErrorEvent extends DesignLibraryEvent { - name: typeof DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME; - message: { - error: unknown; - type: DesignLibraryAtomsError; - }; -} - -/** - * Represents the type of atom, which can be either a top-level atom or an atom child - * @internal - */ -export type AtomType = 'atom' | 'atom-child'; - -export type SerializedDefaultChild = string | { atom: string; props?: Record }; - -/** - * Represents the serialized callback metadata information to be sent to design library - * @internal - */ -export type CallbackInfo = { - /** - * A description of the callback. - */ - description: string; - /** - * The parameters of the callback. - */ - params?: Record; -}; - -/** - * Represents the serialized atom metadata information to be sent to design library - * @internal - */ -export type AtomInfo = { - /** - * The name of the atom, which should be unique across the registry and is used to identify the atom in the atom registry. - */ - name: string; - /** - * The optional version of the atom. - */ - version?: number; - /** - * The type of the atom. - */ - type: AtomType; - /** - * A description of the atom. - */ - description: string; - /** - * The properties of the atom. - */ - props: Record; - /** - * The allowed children of the atom, which define the nested structure of the atom. - */ - allowedChildren: string[]; - /** - * The default children of the atom. - */ - defaultChildren?: SerializedDefaultChild[]; - /** - * The HTML events of the atom, which define the standard DOM events the atom can handle. - */ - htmlEvents?: string[]; - /** - * The custom events of the atom, which define the non-standard events the atom can handle. - */ - customEvents?: Record; -}; - -/** - * Represents an event indicating the atom registry to be sent to design library - * @internal - */ -export interface DesignLibraryAtomsRegistryEvent extends DesignLibraryEvent { - name: typeof DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME; - message: { - atomsRegistry: AtomInfo[]; - callbackRegistry: Record; - }; -} - -/** - * Creates a DesignLibraryAtomsRegistryEvent with the given atoms registry and callback registry. - * @param {AtomInfo[]} atomsRegistry - the atoms registry to be sent in the event - * @param {Record} callbackRegistry - the callback registry to be sent in the event - * @returns {DesignLibraryAtomsRegistryEvent} the created event with the atoms registry and callback registry - * @internal - */ -export function getDesignLibraryAtomsRegistryEvent( - atomsRegistry: AtomInfo[], - callbackRegistry: Record -): DesignLibraryAtomsRegistryEvent { - return { - name: DESIGN_LIBRARY_ATOM_REGISTRY_EVENT_NAME, - message: { - atomsRegistry, - callbackRegistry, - }, - }; -} - -/** - * Generates a DesignLibraryAtomsErrorEvent depending on the type of error with the given error. - * @param {unknown} error - The error to be sent. - * @param {DesignLibraryAtomsError} type - The type of error. - * @returns An object representing the DesignLibraryAtomsErrorEvent. - * @internal - */ -export function getDesignLibraryAtomsErrorEvent( - error: unknown, - type: DesignLibraryAtomsError -): DesignLibraryAtomsErrorEvent { - return { - name: DESIGN_LIBRARY_ATOMS_ERROR_EVENT_NAME, - message: { error, type }, - }; -} - -/** - * Sends a design library atoms error event to the design library - * @param {unknown} error - The error object or message to be sent. - * @param {DesignLibraryAtomsError} type - The type of error, as defined in DesignLibraryAtomsError. - * @internal - */ -export const sendAtomsErrorEvent = (error: unknown, type: DesignLibraryAtomsError) => { - const errorEvent = getDesignLibraryAtomsErrorEvent(error, type); - console.error( - `Component Library: sending error event. ${ERROR_MESSAGES.CONTACT_SUPPORT}`, - errorEvent - ); - if (typeof window !== 'undefined') { - const target = window.parent && window.parent !== window ? window.parent : window; - target.postMessage(errorEvent, '*'); - } -}; - -/** - * Adds a handler for atom document update events from the design library. - * @param {(updatedRootComponent: Document) => void} callback - The callback to be invoked when a document update event is received. - * @returns A function to unsubscribe from the atom document update events. - * @internal - */ -export const addDocumentUpdateHandler = (callback: (updatedRootComponent: Document) => void) => { - const handler = (e: MessageEvent) => { - if (!validateEvent(e, DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME)) { - return; - } - - console.debug('Component Library atoms: message received', e.data); - - callback(e.data.document as Document); - }; - - window.addEventListener('message', handler); - - const unsubscribe = () => { - window.removeEventListener('message', handler); - }; - - return unsubscribe; -}; diff --git a/packages/content/src/editing/atoms-builder/index.ts b/packages/content/src/editing/atoms-builder/index.ts deleted file mode 100644 index b5cbfb342b..0000000000 --- a/packages/content/src/editing/atoms-builder/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { - AtomInfo, - AtomType, - SerializedDefaultChild, - getDesignLibraryAtomsRegistryEvent, - sendAtomsErrorEvent, - DesignLibraryAtomsError, - addDocumentUpdateHandler, - CallbackInfo, -} from './atoms-builder'; diff --git a/packages/content/src/editing/index.ts b/packages/content/src/editing/index.ts index 6248cde241..c27b43ce44 100644 --- a/packages/content/src/editing/index.ts +++ b/packages/content/src/editing/index.ts @@ -41,12 +41,3 @@ export { COMPONENT_PREVIEW_CACHE_KEY_PREFIX, updateComponent, } from './design-library'; -export { - AtomInfo, - CallbackInfo, - AtomType, - getDesignLibraryAtomsRegistryEvent, - sendAtomsErrorEvent, - DesignLibraryAtomsError, - addDocumentUpdateHandler, -} from './atoms-builder/atoms-builder'; diff --git a/packages/content/tsconfig.json b/packages/content/tsconfig.json index 7c8692c69d..51cf31ee1c 100644 --- a/packages/content/tsconfig.json +++ b/packages/content/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "rootDir": "./src", "target": "es2017", "skipLibCheck": true, "useUnknownInCatchVariables": false, diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json b/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json index 40a123106e..0c881d5b17 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/package.json @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "scripts": { - "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build next:build", + "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build next:build", "lint": "eslint ./src/**/*.tsx ./src/**/*.ts", "next:build": "next build", "next:dev": "cross-env NODE_OPTIONS='--inspect' next dev", @@ -24,8 +24,10 @@ "sitecore-tools:generate-map": "sitecore-tools project component generate-map", "sitecore-tools:generate-map:watch": "sitecore-tools project component generate-map --watch", "sitecore-tools:build": "sitecore-tools project build", - "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", - "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start" + "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", + "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start", + "sitecore-tools:atoms:validate": "sitecore-tools project atoms validate", + "sitecore-tools:atoms:update": "sitecore-tools project atoms update" }, "dependencies": { "@sitecore-content-sdk/nextjs": "<%- version %>", diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/sitecore.cli.config.ts b/packages/create-content-sdk-app/src/templates/nextjs-app-router/sitecore.cli.config.ts index 4f756541f4..c558cc9b57 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/sitecore.cli.config.ts +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/sitecore.cli.config.ts @@ -19,6 +19,11 @@ export default defineCliConfig({ }), ], }, + atoms: { + validation: { + breakOnError: false, + }, + }, componentMap: { paths: ['src/components'], exclude: ['src/components/content-sdk/*'], diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx index 7405190a35..bee8fe4610 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/Providers.tsx @@ -3,16 +3,19 @@ import React from 'react'; import { Page, SitecoreProvider } from '@sitecore-content-sdk/nextjs'; import scConfig from 'sitecore.config'; import components from '.sitecore/component-map.client'; -import { atoms } from 'src/components/atoms'; +import { catalog, registry } from 'src/atoms'; +import { useRouter } from 'next/navigation'; export default function Providers({ children, page }: { children: React.ReactNode; page: Page }) { + const router = useRouter(); + return ( import('.sitecore/import-map.client')} - atomRegistry={{ atoms }} + atomsConfig={{ catalog, registry, navigate: router.push }} > {children} diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/atoms/index.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/atoms/index.tsx new file mode 100644 index 0000000000..94edbdb118 --- /dev/null +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/atoms/index.tsx @@ -0,0 +1,10 @@ +import { defineAtomsCatalog, defineAtomsRegistry } from '@sitecore-content-sdk/nextjs'; + +export const catalog = defineAtomsCatalog({ + components: {}, + actions: {}, +}); + +export const registry = defineAtomsRegistry(catalog, { + components: {}, +}); diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts b/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts deleted file mode 100644 index a826d81029..0000000000 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/src/components/atoms/index.ts +++ /dev/null @@ -1 +0,0 @@ -export const atoms = []; diff --git a/packages/create-content-sdk-app/src/templates/nextjs/package.json b/packages/create-content-sdk-app/src/templates/nextjs/package.json index e63590874a..f03e8181d1 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/package.json +++ b/packages/create-content-sdk-app/src/templates/nextjs/package.json @@ -54,7 +54,7 @@ "typescript": "~5.8.3" }, "scripts": { - "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build next:build", + "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build next:build", "lint": "eslint ./src/**/*.tsx ./src/**/*.ts", "next:build": "next build", "next:dev": "cross-env NODE_OPTIONS='--inspect' next dev", @@ -62,7 +62,9 @@ "sitecore-tools:generate-map": "sitecore-tools project component generate-map", "sitecore-tools:generate-map:watch": "sitecore-tools project component generate-map --watch", "sitecore-tools:build": "sitecore-tools project build", - "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", - "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start" + "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", + "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start", + "sitecore-tools:atoms:validate": "sitecore-tools project atoms validate", + "sitecore-tools:atoms:update": "sitecore-tools project atoms update" } } diff --git a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts index 3a0056c3b9..ba5f6692c5 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts +++ b/packages/create-content-sdk-app/src/templates/nextjs/sitecore.cli.config.ts @@ -19,6 +19,11 @@ export default defineCliConfig({ }), ], }, + atoms: { + validation: { + breakOnError: false, + }, + }, componentMap: { paths: ['src/components'], // Exclude content-sdk auxillary components diff --git a/packages/create-content-sdk-app/src/templates/nextjs/src/Providers.tsx b/packages/create-content-sdk-app/src/templates/nextjs/src/Providers.tsx index 1a0136e949..e879fa0ee2 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/src/Providers.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs/src/Providers.tsx @@ -6,6 +6,8 @@ import { } from '@sitecore-content-sdk/nextjs'; import components from '.sitecore/component-map'; import scConfig from 'sitecore.config'; +import { catalog, registry } from 'src/atoms'; +import { useRouter } from 'next/navigation'; const Providers = ({ children, @@ -16,6 +18,8 @@ const Providers = ({ componentProps?: ComponentPropsCollection; page: Page; }) => { + const router = useRouter(); + return ( import('.sitecore/import-map')} + atomsConfig={{ catalog, registry, navigate: router.push }} > {children} diff --git a/packages/create-content-sdk-app/src/templates/nextjs/src/atoms/index.tsx b/packages/create-content-sdk-app/src/templates/nextjs/src/atoms/index.tsx new file mode 100644 index 0000000000..94edbdb118 --- /dev/null +++ b/packages/create-content-sdk-app/src/templates/nextjs/src/atoms/index.tsx @@ -0,0 +1,10 @@ +import { defineAtomsCatalog, defineAtomsRegistry } from '@sitecore-content-sdk/nextjs'; + +export const catalog = defineAtomsCatalog({ + components: {}, + actions: {}, +}); + +export const registry = defineAtomsRegistry(catalog, { + components: {}, +}); diff --git a/packages/nextjs/atoms.d.ts b/packages/nextjs/atoms.d.ts new file mode 100644 index 0000000000..08ea70d18b --- /dev/null +++ b/packages/nextjs/atoms.d.ts @@ -0,0 +1 @@ +export * from './types/atoms/index'; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index ac79f6c742..05e61c485a 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -104,6 +104,11 @@ "require": "./dist/cjs/index.js", "types": "./types/index.d.ts" }, + "./atoms": { + "import": "./dist/esm/atoms/index.js", + "require": "./dist/cjs/atoms/index.js", + "types": "./types/atoms/index.d.ts" + }, "./client": { "import": "./dist/esm/client/index.js", "require": "./dist/cjs/client/index.js", diff --git a/packages/nextjs/src/atoms/index.ts b/packages/nextjs/src/atoms/index.ts new file mode 100644 index 0000000000..dff78c75e5 --- /dev/null +++ b/packages/nextjs/src/atoms/index.ts @@ -0,0 +1,27 @@ +export { + useBoundProp, + withPropMeta, + type AtomComponentDefinition, + type AtomActionDefinition, + type AtomsCatalogInput, + type AtomsComponentsMap, + type AtomActionHandler, + type AtomsActionsMap, + type AtomsConfig, + type PropMeta, + textFieldSchema, + richTextFieldSchema, + dateFieldSchema, + linkFieldSchema, + imageFieldSchema, + fileFieldSchema, + type TextFieldSchema, + type RichTextFieldSchema, + type DateFieldSchema, + type LinkFieldSchema, + type ImageFieldSchema, + type FileFieldSchema, +} from '@sitecore-content-sdk/react'; + +export { defineAtomsCatalog, defineAtomsRegistry } from './re-exports'; + diff --git a/packages/nextjs/src/atoms/re-exports.ts b/packages/nextjs/src/atoms/re-exports.ts new file mode 100644 index 0000000000..fb0dfc1057 --- /dev/null +++ b/packages/nextjs/src/atoms/re-exports.ts @@ -0,0 +1,75 @@ +import { + defineAtomsCatalog as defineAtomsCatalogReact, + defineAtomsRegistry as defineAtomsRegistryReact, +} from '@sitecore-content-sdk/react'; + +/** + * Define an atoms catalog from component and action definitions. + * + * Pass component/action definitions exactly as json-render expects them. + * The returned catalog carries full type information so `defineAtomsRegistry` + * can infer props per component. + * @param {T} input - Catalog input with `components` and optionally `actions` + * @returns A typed json-render Catalog + * @example + * ```ts + * import { z } from 'zod'; + * import { defineAtomsCatalog } from '@sitecore-content-sdk/nextjs'; + * + * const catalog = defineAtomsCatalog({ + * components: { + * Button: { + * props: z.object({ label: z.string(), variant: z.enum(['primary', 'secondary']) }), + * description: 'A clickable button', + * slots: ['default'], + * }, + * Card: { + * props: z.object({ title: z.string() }), + * description: 'A content card', + * slots: ['default'], + * }, + * }, + * actions: { + * submit: { + * params: z.object({ formId: z.string() }), + * description: 'Submit a form', + * }, + * }, + * }); + * ``` + * @public + */ +export const defineAtomsCatalog = defineAtomsCatalogReact; + +/** + * Define an atoms registry that maps catalog definitions to Nextjs implementations. + * + * Each component receives `{ props, children, emit, on, bindings, loading }` + * @param catalog - The catalog created by defineAtomsCatalog + * @param options - Component and action implementations + * @returns Registry result with component registry and action handlers + * @example + * + * ```tsx + * import { defineAtomsRegistry } from '@sitecore-content-sdk/nextjs'; + * + * const { registry, handlers, executeAction } = defineAtomsRegistry(catalog, { + * components: { + * Button: ({ props, children, emit }) => ( + * + * ), + * Card: ({ props, children }) => ( + *

{props.title}

{children}
+ * ), + * }, + * actions: { + * submit: async (params) => { + * await fetch('/api/submit', { method: 'POST', body: JSON.stringify(params) }); + * }, + * }, + * }); + * ``` + * @public + */ +export const defineAtomsRegistry: typeof defineAtomsRegistryReact = defineAtomsRegistryReact; + diff --git a/packages/nextjs/src/config-cli/define-cli-config.ts b/packages/nextjs/src/config-cli/define-cli-config.ts index 4b029b676d..6f8e05db82 100644 --- a/packages/nextjs/src/config-cli/define-cli-config.ts +++ b/packages/nextjs/src/config-cli/define-cli-config.ts @@ -33,6 +33,12 @@ function addDefaultScaffoldTemplates(cliConfig: SitecoreCliConfigInput) { cliConfig.scaffold.templates.unshift(defaultTemplate, byocTemplate); } +/** + * Default exclude patterns for the component map generator. + * Atoms components are rendered via the json-render registry, not the Sitecore component map. + */ +const DEFAULT_COMPONENT_MAP_EXCLUDE = ['**/atoms/**']; + /** * Add the framework-specific implementaion of the component map generator to the CLI configuration. * @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object @@ -42,5 +48,6 @@ function addDefaultComponentMapGenerator(cliConfig: SitecoreCliConfigInput) { generator: generateMap, paths: ['src/components'], ...cliConfig.componentMap, + exclude: [...DEFAULT_COMPONENT_MAP_EXCLUDE, ...(cliConfig.componentMap?.exclude || [])], }; } diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index bd399a9f83..0673574022 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -119,6 +119,8 @@ export { export { FEaaSWrapper }; export { BYOCWrapper }; +export * from './atoms'; + export { ComponentMap, Image, @@ -163,32 +165,6 @@ export { AppPlaceholder, AppPlaceholderProps, renderEmptyPlaceholder, - AtomType, - createAtom, - withPropMeta, - withArgMeta, - getFieldMeta, - type AtomMetadata, - type AtomChild, - type DefaultChild, - type EditableComponentProps, - type CallbackPropKeys, - type CallbackArgZodTuple, - type PropMeta, - type ArgMeta, - type AtomSchemaInput, - textFieldSchema, - richTextFieldSchema, - dateFieldSchema, - linkFieldSchema, - imageFieldSchema, - fileFieldSchema, - type TextFieldSchema, - type RichTextFieldSchema, - type DateFieldSchema, - type LinkFieldSchema, - type ImageFieldSchema, - type FileFieldSchema, } from '@sitecore-content-sdk/react'; export { initContentSdk } from '@sitecore-content-sdk/core'; diff --git a/packages/nextjs/tsconfig.json b/packages/nextjs/tsconfig.json index c0f3b51746..d8a54eae81 100644 --- a/packages/nextjs/tsconfig.json +++ b/packages/nextjs/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "rootDir": "./src", "target": "ES2017", "module": "CommonJS", "jsx": "react", @@ -27,6 +28,7 @@ "site.d.ts", "search.d.ts", "client.d.ts", + "atoms.d.ts", "utils.d.ts", "tools.d.ts", "config-cli.d.ts", diff --git a/packages/react/package.json b/packages/react/package.json index b79f57e03d..d58ded160a 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -77,11 +77,13 @@ "react-dom": "^19.2.1" }, "dependencies": { + "@json-render/core": "0.19.0", + "@json-render/react": "0.19.0", "@sitecore-content-sdk/content": "2.0.0-beta.3", "@sitecore-content-sdk/core": "2.0.0-beta.3", "@sitecore-content-sdk/search": "0.2.0-beta.3", "fast-deep-equal": "^3.1.3", - "zod": "^4.3.6" + "zod": "^4.4.3" }, "description": "", "types": "types/index.d.ts", diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx deleted file mode 100644 index c7110c71a9..0000000000 --- a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.test.tsx +++ /dev/null @@ -1,156 +0,0 @@ -/* eslint-disable jsdoc/require-jsdoc */ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import sinon from 'sinon'; -import { expect, use as chaiUse } from 'chai'; -import sinonChai from 'sinon-chai'; - -chaiUse(sinonChai); -import { render } from '@testing-library/react'; -import { z } from 'zod'; -import { StudioComponentWrapper } from './StudioComponentWrapper'; -import { SitecoreProvider } from '../../components/SitecoreProvider'; -import type { Document } from '@sitecore-content-sdk/content/atoms'; -import type { AtomMetadata } from '../types'; -import type { ImportMapImport } from '../../components/DesignLibrary/models'; - -/** - * Minimal error boundary to catch React render errors without crashing the test. - */ -class ErrorBoundary extends React.Component< - React.PropsWithChildren<{ onError?: (err: Error) => void }>, - { hasError: boolean } -> { - state = { hasError: false }; - - static getDerivedStateFromError() { - return { hasError: true }; - } - - componentDidCatch(err: Error) { - this.props.onError?.(err); - } - - render() { - return this.state.hasError ? null : this.props.children; - } -} - -describe('', () => { - const sandbox = sinon.createSandbox(); - const apiStub = {} as any; - const emptyComponentMap = new Map(); - const loadImportMapStub = async (): Promise => ({} as ImportMapImport); - - const Box: React.FC>> = (props) => - React.createElement('div', { 'data-test': 'box', ...props }, props.children); - - const atoms: AtomMetadata[] = [ - { - name: 'Box', - type: 'atom', - description: 'Container', - component: Box as React.ComponentType, - props: z.object({}), - }, - ]; - - const sampleDoc: Document = { - name: 'hero', - root: { id: 'r', type: 'Box' }, - }; - - const getPage = () => ({ - locale: 'en', - layout: { sitecore: { context: {}, route: null } }, - mode: { - name: 'normal', - isDesignLibrary: false, - designLibrary: { isVariantGeneration: false }, - isNormal: true, - isPreview: false, - isEditing: false, - }, - }); - - const renderInProvider = (ui: React.ReactNode) => - render( - - {ui} - - ); - - const renderWithoutAtomRegistry = (ui: React.ReactNode) => - render( - - {ui} - - ); - - afterEach(() => { - sandbox.restore(); - }); - - it('renders nothing when document is null', () => { - const { container } = renderInProvider(); - expect(container.innerHTML).to.equal(''); - }); - - it('renders nothing when document is undefined', () => { - const { container } = renderInProvider(); - expect(container.innerHTML).to.equal(''); - }); - - it('renders the component-layout view when document is provided', () => { - const { container } = renderInProvider(); - expect(container.querySelector('[data-test="box"]')).to.exist; - }); - - it('renders nothing safely when atomRegistry is absent from the provider and document is null', () => { - const { container } = renderWithoutAtomRegistry(); - expect(container.innerHTML).to.equal(''); - }); - - it('throws when document references an atom type not present in the registry', () => { - const docWithUnknownAtom: Document = { name: 'test', root: { id: 'x', type: 'UnknownAtom' } }; - let caughtError: Error | undefined; - - // Suppress the expected React error boundary console output - const consoleErrorStub = sandbox.stub(console, 'error'); - - render( - - { - caughtError = err; - }} - > - - - - ); - - consoleErrorStub.restore(); - - expect(caughtError).to.be.instanceOf(Error); - expect(caughtError!.message).to.include('UnknownAtom'); - }); -}); - diff --git a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx b/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx deleted file mode 100644 index 7dca4920ae..0000000000 --- a/packages/react/src/atoms/Wrapper/StudioComponentWrapper.tsx +++ /dev/null @@ -1,32 +0,0 @@ -'use client'; -import React, { JSX, useMemo } from 'react'; -import { createView } from '../component-layout'; -import { getAtomMap } from '../atom-registry-utils'; -import { useSitecore } from '../../components/SitecoreProvider'; -import { StudioComponentWrapperProps } from './models'; - -/** - * Client component that renders a pre-fetched Studio (NCC) component layout. - * - * Expects `document` to be provided (fetched server-side by - * `StudioComponentServerWrapper`, from Design Library document updates, or any other - * preview path that supplies a layout `Document`). Runtime props from plain-object - * `document.props` are applied as runtime props when the layout view is created. Renders `null` when no layout - * is available. - * @param {StudioComponentWrapperProps} props component props - * @internal - */ -export const StudioComponentWrapper = (props: StudioComponentWrapperProps): JSX.Element | null => { - const { atomRegistry } = useSitecore(); - const atomMap = useMemo(() => getAtomMap(atomRegistry?.atoms ?? []), [atomRegistry?.atoms]); - - const ViewComponent = useMemo(() => { - if (!props.document) return null; - - return createView(props.document, atomMap, atomRegistry?.callbacks); - }, [props.document, atomMap, atomRegistry?.callbacks]); - - if (!ViewComponent) return null; - - return ; -}; diff --git a/packages/react/src/atoms/Wrapper/index.ts b/packages/react/src/atoms/Wrapper/index.ts deleted file mode 100644 index a728ccb362..0000000000 --- a/packages/react/src/atoms/Wrapper/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { StudioComponentWrapper } from './StudioComponentWrapper'; -export { StudioComponentServerWrapper } from './StudioComponentServerWrapper'; -export { - StudioComponentParams, - StudioComponentServerWrapperProps, - StudioComponentWrapperProps, -} from './models'; - diff --git a/packages/react/src/atoms/Wrapper/models.ts b/packages/react/src/atoms/Wrapper/models.ts deleted file mode 100644 index 3fbec4f272..0000000000 --- a/packages/react/src/atoms/Wrapper/models.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Document } from '@sitecore-content-sdk/content/atoms'; - -/** - * Rendering parameters injected by the layout service for Studio components. - * The `ComponentRef$` field on the rendering is copied into params with the `$` stripped. - * @internal - */ -export type StudioComponentParams = { - /** - * Identifier or relative/absolute path to the Studio component layout JSON in MMS. - */ - componentRef?: string; -}; - -/** - * Props accepted by the RSC `StudioComponentServerWrapper`. - * @internal - */ -export type StudioComponentServerWrapperProps = { - /** - * Pipe separated relative paths to the Studio component layout JSON in MMS with the last segment as the variant name. The path matching `FieldNames` will be used, or `default` if no match. - */ - componentRef: string; - fieldNames?: string; -}; - -/** - * Props accepted by the `StudioComponentWrapper` used to render a Studio component layout on the client. Expects a pre-fetched `document` containing the component layout data. - * @internal - */ -export type StudioComponentWrapperProps = { - document?: Document | null; -}; - diff --git a/packages/react/src/atoms/atom-registry-utils.test.ts b/packages/react/src/atoms/atom-registry-utils.test.ts deleted file mode 100644 index 62fc4c0a1b..0000000000 --- a/packages/react/src/atoms/atom-registry-utils.test.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { expect } from 'chai'; -import { z } from 'zod'; -import { getAtomMap, serializeAtoms } from './atom-registry-utils'; -import { AtomChild, AtomMetadata } from './types'; - -const createAtom = (name: string, allowedChildren?: AtomChild[]): AtomMetadata => ({ - name, - type: 'atom', - description: `${name} atom`, - props: z.object({}), - component: () => null, - allowedChildren, -}); - -describe('serializeAtoms', () => { - it('should serialize atom props to JSON schema', () => { - const propsSchema = z.object({ - label: z.string(), - count: z.number(), - }); - const atomA = createAtom('A'); - atomA.props = propsSchema; - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(1); - expect(result[0]).to.have.property('props'); - expect(result[0].props).to.be.an('object'); - expect(result[0].props.type).to.equal('object'); - }); - - it('should serialize allowedChildren as string array', () => { - const atomB = createAtom('B'); - const atomA = createAtom('A', [atomB, 'text', 'atom']); - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(2); - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.allowedChildren).to.deep.equal(['B', 'text', 'atom']); - }); - - it('should serialize defaultChildren with atom names', () => { - const defaultAtom = createAtom('DefaultChild'); - const atomA = createAtom('A'); - atomA.defaultChildren = [defaultAtom, { atom: defaultAtom, props: { key: 'value' } }]; - - const result = serializeAtoms([atomA]); - - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.defaultChildren).to.be.an('array'); - expect(serializedA.defaultChildren![0]).to.equal('DefaultChild'); - expect(serializedA.defaultChildren![1]).to.deep.equal({ - atom: 'DefaultChild', - props: { key: 'value' }, - }); - }); - - it('should serialize customEvents to JSON schema', () => { - const atomA = createAtom('A'); - atomA.customEvents = { - onClick: [z.string(), z.number()], - onHover: [z.boolean()], - }; - - const result = serializeAtoms([atomA]); - - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.customEvents).to.be.an('object'); - expect(serializedA.customEvents?.type).to.equal('object'); - expect(serializedA.customEvents?.properties).to.have.property('onClick'); - expect(serializedA.customEvents?.properties).to.have.property('onHover'); - }); - - it('should serialize atom without customEvents when not provided', () => { - const atomA = createAtom('A'); - - const result = serializeAtoms([atomA]); - - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.customEvents).to.equal(undefined); - }); - - it('should serialize multiple atoms with all properties', () => { - const propsSchema = z.object({ title: z.string() }); - const atomB = createAtom('B'); - const atomA = createAtom('A', [atomB]); - atomA.props = propsSchema; - atomA.customEvents = { onEvent: [z.string()] }; - atomA.htmlEvents = ['onClick', 'onHover']; - atomA.defaultChildren = [atomB]; - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(2); - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.props).to.be.an('object'); - expect(serializedA.allowedChildren).to.include('B'); - expect(serializedA.customEvents).to.be.an('object'); - expect(serializedA.htmlEvents).to.deep.equal(['onClick', 'onHover']); - expect(serializedA.defaultChildren).to.deep.equal(['B']); - }); - - it('should serialize atom props with provided metadata', () => { - const propsSchema = z.object({ - name: z.string().meta({ description: 'The name of the atom', default: 'Unnamed' }), - isActive: z.boolean().meta({ description: 'Whether the atom is active', default: false }), - }); - const atomA = createAtom('A'); - atomA.props = propsSchema; - - const result = serializeAtoms([atomA]); - - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.props).to.be.an('object'); - expect(serializedA.props?.properties).to.have.property('name'); - expect(serializedA.props?.properties).to.have.property('isActive'); - expect(serializedA.props?.properties?.name?.description).to.equal('The name of the atom'); - expect(serializedA.props?.properties?.name?.default).to.equal('Unnamed'); - expect(serializedA.props?.properties?.isActive?.description).to.equal( - 'Whether the atom is active' - ); - expect(serializedA.props?.properties?.isActive?.default).to.equal(false); - }); - - it('should serialize atom custom events with provided arguiment metadata', () => { - const atomA = createAtom('A'); - atomA.customEvents = { - onSubmit: [ - z.string().meta({ firstArgName: 'First argument' }), - z.number().meta({ secondArgName: 'Second argument' }), - ], - }; - - const result = serializeAtoms([atomA]); - - const serializedA = result.find((info) => info.name === 'A'); - if (!serializedA) throw new Error('serializedA should be defined'); - expect(serializedA.customEvents).to.be.an('object'); - expect(serializedA.customEvents?.properties).to.have.property('onSubmit'); - expect(serializedA.customEvents?.properties?.onSubmit?.items).to.have.length(2); - expect(serializedA.customEvents?.properties?.onSubmit?.items?.[0]?.firstArgName).to.equal( - 'First argument' - ); - expect(serializedA.customEvents?.properties?.onSubmit?.items?.[1]?.secondArgName).to.equal( - 'Second argument' - ); - }); - - it('should flatten three levels of nested allowedChildren', () => { - // A → B → C - const atomC = createAtom('C'); - const atomB = createAtom('B', [atomC]); - const atomA = createAtom('A', [atomB]); - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(3); - const names = result.map((r) => r.name); - expect(names).to.include.members(['A', 'B', 'C']); - - const serializedA = result.find((r) => r.name === 'A')!; - const serializedB = result.find((r) => r.name === 'B')!; - const serializedC = result.find((r) => r.name === 'C')!; - - // Each level's allowedChildren should be string names, not objects - expect(serializedA.allowedChildren).to.deep.equal(['B']); - expect(serializedB.allowedChildren).to.deep.equal(['C']); - expect(serializedC.allowedChildren).to.deep.equal([]); - }); - - it('should not duplicate atoms shared across multiple levels (diamond pattern)', () => { - // A allows B and C; B also allows C → C should appear only once - const atomC = createAtom('C'); - const atomB = createAtom('B', [atomC]); - const atomA = createAtom('A', [atomB, atomC]); - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(3); - const names = result.map((r) => r.name); - expect(names).to.include.members(['A', 'B', 'C']); - // Ensure no duplicates - expect(new Set(names).size).to.equal(3); - - const serializedA = result.find((r) => r.name === 'A')!; - expect(serializedA.allowedChildren).to.deep.equal(['B', 'C']); - }); - - it('should serialize defaultChildren at multiple levels of nesting', () => { - // A has defaultChildren referencing B; B has defaultChildren referencing C - const atomC = createAtom('C'); - const atomB = createAtom('B', [atomC]); - atomB.defaultChildren = [atomC, { atom: atomC, props: { key: 'val' } }]; - const atomA = createAtom('A', [atomB]); - atomA.defaultChildren = [atomB]; - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(3); - - const serializedA = result.find((r) => r.name === 'A')!; - const serializedB = result.find((r) => r.name === 'B')!; - - // A's defaultChildren → B serialized as name string - expect(serializedA.defaultChildren).to.deep.equal(['B']); - - // B's defaultChildren → C serialized as name string and { atom: 'C', props } - expect(serializedB.defaultChildren).to.deep.equal(['C', { atom: 'C', props: { key: 'val' } }]); - }); - - it('should handle mixed string and object allowedChildren at multiple levels', () => { - // B allows 'text' and 'atom' (strings) and a concrete atom C - const atomC = createAtom('C'); - const atomB = createAtom('B', ['text', 'atom', atomC]); - const atomA = createAtom('A', [atomB, 'text']); - - const result = serializeAtoms([atomA]); - - expect(result).to.have.length(3); - - const serializedA = result.find((r) => r.name === 'A')!; - const serializedB = result.find((r) => r.name === 'B')!; - - expect(serializedA.allowedChildren).to.deep.equal(['B', 'text']); - expect(serializedB.allowedChildren).to.deep.equal(['text', 'atom', 'C']); - }); -}); - -describe('getAtomMap', () => { - it('should return a map of name to component from metadata', () => { - const Button = () => null; - const meta: AtomMetadata = { - name: 'Button', - type: 'atom', - description: 'Button', - props: {} as AtomMetadata['props'], - component: Button, - }; - const registry = getAtomMap([meta]); - expect(registry.Button).to.equal(Button); - expect(Object.keys(registry)).to.deep.equal(['Button']); - }); - - it('should include allowedChildren in registry', () => { - const Card = () => null; - const CardBody = () => null; - const cardMeta: AtomMetadata = { - name: 'Card', - type: 'atom', - description: 'Card', - props: {} as AtomMetadata['props'], - component: Card, - allowedChildren: [ - { - name: 'CardBody', - type: 'atom-child', - description: 'Body', - props: {} as AtomMetadata['props'], - component: CardBody, - }, - ], - }; - const registry = getAtomMap([cardMeta]); - expect(registry.Card).to.equal(Card); - expect(registry.CardBody).to.equal(CardBody); - }); -}); diff --git a/packages/react/src/atoms/atom-registry-utils.ts b/packages/react/src/atoms/atom-registry-utils.ts deleted file mode 100644 index 6a126a880b..0000000000 --- a/packages/react/src/atoms/atom-registry-utils.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { z } from 'zod'; -import { AtomMetadata } from './types'; -import { AtomInfo } from '@sitecore-content-sdk/content/editing'; -import { ComponentType } from 'react'; - -const isAtomMetadata = (value: unknown): value is AtomMetadata => { - return typeof value !== 'string'; -}; - -/** - * Serializes the provided atoms metadata array into a format suitable for broadcasting via postMessage to the host application - * @param {AtomMetadata[]} atoms - the atoms to be serialized - * @returns {AtomInfo[]} the serialized atoms - * @internal - */ -export const serializeAtoms = (atoms: AtomMetadata[]): AtomInfo[] => { - const atomInfos: AtomInfo[] = []; - const visited = new Set(); - - const visit = (atom: AtomMetadata): void => { - if (visited.has(atom.name)) return; - - visited.add(atom.name); - - // process allowed children - const allowedChildren: string[] = []; - for (const child of atom.allowedChildren ?? []) { - if (isAtomMetadata(child)) { - allowedChildren.push(child.name); - } else { - allowedChildren.push(child); - } - } - - // process default children - const defaultChildren = atom.defaultChildren?.map((child) => - 'name' in child ? child.name : { atom: child.atom.name, props: child.props } - ); - - // process custom events - let customEvents: Record | undefined = undefined; - if (atom.customEvents && Object.keys(atom.customEvents).length > 0) { - const eventsShape: Record = {}; - for (const [name, args] of Object.entries(atom.customEvents)) { - eventsShape[name] = z.tuple(args as [z.ZodType, ...z.ZodType[]]); - } - customEvents = z.toJSONSchema(z.object(eventsShape), { target: 'draft-7' }); - } - - atomInfos.push({ - name: atom.name, - version: atom.version, - type: atom.type, - description: atom.description, - props: z.toJSONSchema(atom.props, { target: 'draft-7' }), - allowedChildren, - defaultChildren, - htmlEvents: atom.htmlEvents, - customEvents, - }); - - for (const ch of atom.allowedChildren ?? []) { - if (typeof ch === 'object') visit(ch); - } - }; - - atoms.forEach(visit); - - return atomInfos; -}; - -/** - * Returns a map of atom type name to React component for use with createView. - * @param {AtomMetadata[]} metadata - Array of atom metadata (e.g. from createAtom) - * @returns {Record>} Record of atom name to component - * @internal - */ -export function getAtomMap(metadata: AtomMetadata[]): Record> { - const registry: Record> = {}; - - const addAtom = (atom: AtomMetadata): void => { - if (registry[atom.name]) { - return; - } - registry[atom.name] = atom.component as ComponentType; - for (const child of atom.allowedChildren ?? []) { - if (typeof child === 'object' && child !== null) { - addAtom(child); - } - } - }; - - for (const atom of metadata) { - addAtom(atom); - } - - return registry; -} diff --git a/packages/react/src/atoms/callback-registry-utils.test.ts b/packages/react/src/atoms/callback-registry-utils.test.ts deleted file mode 100644 index 4116ffae46..0000000000 --- a/packages/react/src/atoms/callback-registry-utils.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { expect } from 'chai'; -import { z } from 'zod'; -import { serializeCallbacks } from './callback-registry-utils'; -import { createCallback } from './createCallback'; - -describe('serializeCallbacks', () => { - it('should serialize a callback without params', () => { - const callback = createCallback('onSave', { - description: 'onSave callback', - params: {}, - callbackFn: () => {}, - }); - - const result = serializeCallbacks([callback]); - - expect(result).to.deep.equal({ - onSave: { - description: 'onSave callback', - }, - }); - }); - - it('should serialize a callback with params to JSON schema tuple', () => { - const callback = createCallback('onSubmit', { - description: 'onSubmit callback', - params: { - label: { type: z.string(), description: 'The label' }, - count: { type: z.number(), description: 'The count' }, - }, - callbackFn: ({ label, count }) => { - console.log(label, count); - }, - }); - - const result = serializeCallbacks([callback]); - - expect(result).to.deep.equal({ - onSubmit: { - description: 'onSubmit callback', - params: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - label: { type: 'string', description: 'The label' }, - count: { type: 'number', description: 'The count' }, - }, - required: ['label', 'count'], - additionalProperties: false, - }, - }, - }); - }); - - it('should serialize multiple callbacks', () => { - const callbackA = createCallback('onSave', { - description: 'Save handler', - params: {}, - callbackFn: () => {}, - }); - const callbackB = createCallback('onCancel', { - description: 'Cancel handler', - params: {}, - callbackFn: () => {}, - }); - - const result = serializeCallbacks([callbackA, callbackB]); - - expect(result).to.deep.equal({ - onSave: { - description: 'Save handler', - }, - onCancel: { - description: 'Cancel handler', - }, - }); - }); - - it('should return an empty object for an empty array', () => { - const result = serializeCallbacks([]); - - expect(result).to.deep.equal({}); - }); - - it('should serialize params with argument names and descriptions', () => { - const callback = createCallback('onUpdate', { - description: 'onUpdate callback', - params: { - name: { type: z.string(), description: 'Name to update' }, - isActive: { type: z.boolean(), description: 'Whether active' }, - }, - callbackFn: ({ name, isActive }) => { - console.log(name, isActive); - }, - }); - - const result = serializeCallbacks([callback]); - - expect(result).to.deep.equal({ - onUpdate: { - description: 'onUpdate callback', - params: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - name: { type: 'string', description: 'Name to update' }, - isActive: { type: 'boolean', description: 'Whether active' }, - }, - required: ['name', 'isActive'], - additionalProperties: false, - }, - }, - }); - }); - - it('should serialize callbacks with mixed params and no-params', () => { - const callbackA = createCallback('onSave', { - description: 'onSave callback', - params: { - title: { type: z.string(), description: 'The title' }, - }, - callbackFn: ({ title }) => { - console.log(title); - }, - }); - const callbackB = createCallback('onCancel', { - description: 'onCancel callback', - params: {}, - callbackFn: () => {}, - }); - - const result = serializeCallbacks([callbackA, callbackB]); - - expect(result).to.deep.equal({ - onSave: { - description: 'onSave callback', - params: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - title: { type: 'string', description: 'The title' }, - }, - required: ['title'], - additionalProperties: false, - }, - }, - onCancel: { - description: 'onCancel callback', - }, - }); - }); - - it('should serialize an array of callbacks with varying params', () => { - const callbackA = createCallback('onSubmit', { - description: 'Submit handler', - params: { - label: { type: z.string(), description: 'The label' }, - count: { type: z.number(), description: 'The count' }, - note: { type: z.string().optional(), description: 'Optional note' }, - }, - callbackFn: ({ label, count, note }) => { - console.log(label, count, note); - }, - }); - const callbackB = createCallback('onReset', { - description: 'Reset handler', - params: { - force: { type: z.boolean(), description: 'Force reset' }, - }, - callbackFn: ({ force }) => { - console.log(force); - }, - }); - const callbackC = createCallback('onCancel', { - description: 'Cancel handler', - params: {}, - callbackFn: () => {}, - }); - - const result = serializeCallbacks([callbackA, callbackB, callbackC]); - - expect(result).to.deep.equal({ - onSubmit: { - description: 'Submit handler', - params: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - label: { type: 'string', description: 'The label' }, - count: { type: 'number', description: 'The count' }, - note: { description: 'Optional note', type: 'string' }, - }, - required: ['label', 'count'], - additionalProperties: false, - }, - }, - onReset: { - description: 'Reset handler', - params: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - force: { type: 'boolean', description: 'Force reset' }, - }, - required: ['force'], - additionalProperties: false, - }, - }, - onCancel: { - description: 'Cancel handler', - }, - }); - }); -}); diff --git a/packages/react/src/atoms/callback-registry-utils.ts b/packages/react/src/atoms/callback-registry-utils.ts deleted file mode 100644 index a8105e8524..0000000000 --- a/packages/react/src/atoms/callback-registry-utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { z } from 'zod'; -import { CallbackMetadata } from './types'; -import { CallbackInfo } from '@sitecore-content-sdk/content/editing'; - -/** - * Serializes the provided callbacks metadata array into a format suitable for broadcasting via postMessage to the host application - * @param {CallbackMetadata[]} callbacks - the callbacks to be serialized - * @returns {Record} the serialized callbacks - * @internal - */ -export const serializeCallbacks = (callbacks: CallbackMetadata[]): Record => { - const callbacksInfo: Record = {}; - - callbacks.forEach((callback) => { - const callbackInfo: CallbackInfo = { - description: callback.description, - }; - - if (callback.params && Object.keys(callback.params).length > 0) { - const shape: Record = {}; - for (const [argName, p] of Object.entries(callback.params)) { - shape[argName] = p.type.meta({ description: p.description }); - } - callbackInfo.params = z.toJSONSchema(z.object(shape), { - target: 'draft-7', - }); - } - - callbacksInfo[callback.name] = callbackInfo; - }); - - return callbacksInfo; -}; diff --git a/packages/react/src/atoms/catalog-serializer.test.ts b/packages/react/src/atoms/catalog-serializer.test.ts new file mode 100644 index 0000000000..26ff835b1e --- /dev/null +++ b/packages/react/src/atoms/catalog-serializer.test.ts @@ -0,0 +1,166 @@ +/* eslint-disable jsdoc/require-jsdoc */ +import { expect } from 'chai'; +import { z } from 'zod'; +import { defineAtomsCatalog } from './define-atoms-catalog'; +import { serializeCatalog } from './catalog-serializer'; + +describe('serializeCatalog()', () => { + it('returns components and actions arrays', () => { + const catalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string() }), description: 'A text node' }, + }, + actions: {}, + }); + + const result = serializeCatalog(catalog); + + expect(result).to.have.property('components').that.is.an('array'); + expect(result).to.have.property('actions').that.is.an('array'); + }); + + it('omits version when not set on the catalog', () => { + const catalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string() }), description: 'A text node' }, + }, + actions: {}, + }); + + const result = serializeCatalog(catalog); + + expect(result).to.not.have.property('version'); + }); + + it('includes catalog-level version when set', () => { + const catalog = defineAtomsCatalog({ + version: '2.0.0', + components: { + Text: { props: z.object({ content: z.string() }), description: 'A text node' }, + }, + actions: {}, + }); + const result = serializeCatalog(catalog); + expect(result).to.have.property('version', '2.0.0'); + }); + + it('serializes component with full schema', () => { + const catalog = defineAtomsCatalog({ + components: { + Button: { + version: '0.3.0', + props: z.object({ label: z.string() }), + description: 'A button', + example: { label: 'Hello world' }, + allowedChildren: ['Button', 'Text'], + allowedParents: ['Column', 'Row'], + slots: ['header', 'body', 'footer'], + }, + }, + actions: {}, + }); + + const [comp] = serializeCatalog(catalog).components; + + expect(comp.name).to.equal('Button'); + expect(comp.description).to.equal('A button'); + expect(comp.propsSchema).to.deep.equal({ + $schema: 'https://json-schema.org/draft/2020-12/schema', + type: 'object', + properties: { label: { type: 'string' } }, + required: ['label'], + additionalProperties: false, + }); + expect(comp.slots).to.deep.equal(['header', 'body', 'footer']); + expect(comp.allowedChildren).to.deep.equal(['Button', 'Text']); + expect(comp.allowedParents).to.deep.equal(['Column', 'Row']); + expect(comp.example).to.deep.equal({ label: 'Hello world' }); + expect(comp.version).to.equal('0.3.0'); + }); + + it('serializes multiple components in catalog key order', () => { + const catalog = defineAtomsCatalog({ + components: { + Alpha: { props: z.object({}), description: 'Alpha' }, + Beta: { props: z.object({}), description: 'Beta' }, + Gamma: { props: z.object({}), description: 'Gamma' }, + }, + actions: {}, + }); + + const names = serializeCatalog(catalog).components.map((c) => c.name); + + expect(names).to.deep.equal(['Alpha', 'Beta', 'Gamma']); + }); + + it('returns an empty actions array when catalog has no actions', () => { + const catalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string() }), description: 'Text' }, + }, + actions: {}, + }); + expect(serializeCatalog(catalog).actions).to.deep.equal([]); + }); + + it('serializes action name and description', () => { + const catalog = defineAtomsCatalog({ + components: { + Button: { props: z.object({ label: z.string() }), description: 'A button' }, + }, + actions: { + submit: { params: z.object({ formId: z.string() }), description: 'Submit the form' }, + }, + }); + const [action] = serializeCatalog(catalog).actions; + expect(action.name).to.equal('submit'); + expect(action.description).to.equal('Submit the form'); + }); + + it('converts action params to JSON Schema', () => { + const catalog = defineAtomsCatalog({ + components: { + Button: { props: z.object({ label: z.string() }), description: 'A button' }, + }, + actions: { + navigate: { params: z.object({ path: z.string() }), description: 'Navigate' }, + }, + }); + const [action] = serializeCatalog(catalog).actions; + expect(action.paramsSchema).to.deep.equal({ + $schema: 'https://json-schema.org/draft/2020-12/schema', + type: 'object', + properties: { path: { type: 'string' } }, + required: ['path'], + additionalProperties: false, + }); + }); + + it('converts action without params to JSON Schema', () => { + const catalog = defineAtomsCatalog({ + components: {}, + actions: { + navigate: { description: 'Navigate' }, + }, + }); + + const [action] = serializeCatalog(catalog).actions; + + expect(action).to.not.have.property('paramsSchema'); + }); + + it('serializes multiple actions in catalog key order', () => { + const catalog = defineAtomsCatalog({ + components: { + Button: { props: z.object({ label: z.string() }), description: 'Button' }, + }, + actions: { + open: { params: z.object({ id: z.string() }), description: 'Open' }, + close: { params: z.object({ id: z.string() }), description: 'Close' }, + }, + }); + const names = serializeCatalog(catalog).actions.map((a) => a.name); + expect(names).to.deep.equal(['open', 'close']); + }); +}); + diff --git a/packages/react/src/atoms/catalog-serializer.ts b/packages/react/src/atoms/catalog-serializer.ts new file mode 100644 index 0000000000..2eb6f77252 --- /dev/null +++ b/packages/react/src/atoms/catalog-serializer.ts @@ -0,0 +1,57 @@ +import type { Catalog } from '@json-render/core'; +import { AtomsCatalogInput } from './types'; +import { + AtomCatalogActionEntry, + AtomCatalogComponentEntry, + SerializedCatalog, +} from '@sitecore-content-sdk/content/atoms'; + +/** + * Serialize a json-render Catalog into the payload shape expected by Design Studio. + * @param { Catalog } catalog - The json-render Catalog to serialize + * @returns Serialized catalog for the Design Library event + * @internal + */ +export function serializeCatalog(catalog: Catalog): SerializedCatalog { + const { version, components, actions } = catalog.data; + + const serializedComponents: AtomCatalogComponentEntry[] = Object.entries(components).map( + ([name, component]) => { + const serializedComponent: AtomCatalogComponentEntry = { + name, + propsSchema: component.props.toJSONSchema(), + description: component.description, + slots: component.slots ?? ['default'], + allowedChildren: component.allowedChildren, + allowedParents: component.allowedParents, + example: component.example, + }; + + if (component.version) serializedComponent.version = component.version; + + return serializedComponent; + } + ); + + const serializedActions: AtomCatalogActionEntry[] = Object.entries(actions).map( + ([name, action]) => { + const serializedAction: AtomCatalogActionEntry = { + name, + description: action.description, + }; + + if (action.params) serializedAction.paramsSchema = action.params.toJSONSchema(); + + return serializedAction; + } + ); + + const serializedCatalog: SerializedCatalog = { + components: serializedComponents, + actions: serializedActions, + }; + + if (version) serializedCatalog.version = version; + + return serializedCatalog; +} diff --git a/packages/react/src/atoms/component-layout/createView.test.tsx b/packages/react/src/atoms/component-layout/createView.test.tsx deleted file mode 100644 index 36e1c2bb56..0000000000 --- a/packages/react/src/atoms/component-layout/createView.test.tsx +++ /dev/null @@ -1,2255 +0,0 @@ -/* eslint-disable jsdoc/require-jsdoc */ -/* eslint-disable no-unused-expressions */ -import React from 'react'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import { fireEvent, render } from '@testing-library/react'; -import type { Document, Element, ResolveContext } from '@sitecore-content-sdk/content/atoms'; -import { - buildEventCallback, - createView, - renderElementNode, - renderFor, - renderPrimitiveNode, -} from './createView'; - -describe('component-layout/createView', () => { - describe('renderFor()', () => { - it('renders each item and resolves keys', () => { - const node: Element = { - id: 'item-template', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [], - }; - - const ctx: ResolveContext = { - props: { - items: [ - { id: 'a', title: 'Alpha' }, - { id: 'b', title: 'Beta' }, - ], - }, - state: {}, - item: undefined, - scope: undefined, - }; - - const renderNodeSpy = sinon.spy( - (_tmpl: any, key: React.Key, item: unknown, itemScope: Record) => ( -
- {(item as { title: string }).title}:{(itemScope.item as { id: string }).id} -
- ) - ); - - const result = renderFor(node, ctx, renderNodeSpy); - - expect(Array.isArray(result)).to.equal(true); - expect(renderNodeSpy.callCount).to.equal(2); - expect(renderNodeSpy.firstCall.args[0].for).to.equal(undefined); - expect(renderNodeSpy.firstCall.args[1]).to.equal('a'); - expect(renderNodeSpy.secondCall.args[1]).to.equal('b'); - }); - - it('returns null when for.each does not resolve to array', () => { - const node: Element = { - id: 'item-template', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - }, - children: [], - }; - - const ctx: ResolveContext = { - props: { items: 'not-an-array' }, - state: {}, - item: undefined, - scope: undefined, - }; - - const result = renderFor(node, ctx, () => null); - expect(result).to.equal(null); - }); - - it('uses array index as key when for.key is not provided', () => { - const node: Element = { - id: 'item-template-no-key', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - }, - children: [], - }; - - const ctx: ResolveContext = { - props: { - items: ['one', 'two', 'three'], - }, - state: {}, - item: undefined, - scope: undefined, - }; - - const renderNodeSpy = sinon.spy( - (...args: [any, React.Key, unknown, Record]) => { - void args; - return null; - } - ); - - renderFor(node, ctx, renderNodeSpy); - - expect(renderNodeSpy.callCount).to.equal(3); - expect(renderNodeSpy.firstCall.args[1]).to.equal(0); - expect(renderNodeSpy.secondCall.args[1]).to.equal(1); - expect(renderNodeSpy.thirdCall.args[1]).to.equal(2); - }); - - it('returns an empty list when for.each resolves to an empty array', () => { - const node: Element = { - id: 'item-template-empty', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - }, - children: [], - }; - - const ctx: ResolveContext = { - props: { - items: [], - }, - state: {}, - item: undefined, - scope: undefined, - }; - - const renderNodeSpy = sinon.spy( - (...args: [any, React.Key, unknown, Record]) => { - void args; - return null; - } - ); - - const result = renderFor(node, ctx, renderNodeSpy); - - expect(result).to.deep.equal([]); - expect(renderNodeSpy.called).to.equal(false); - }); - - it('passes per-item values and scope to renderNode for each iteration', () => { - const node: Element = { - id: 'item-template-scope', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [], - }; - - const items = [ - { id: 'x1', value: 'A' }, - { id: 'x2', value: 'B' }, - ]; - - const ctx: ResolveContext = { - props: { - items, - }, - state: {}, - item: { ignored: true }, - scope: { parent: 'scope-value' }, - }; - - const renderNodeSpy = sinon.spy( - (...args: [any, React.Key, unknown, Record]) => { - void args; - return null; - } - ); - - renderFor(node, ctx, renderNodeSpy); - - expect(renderNodeSpy.callCount).to.equal(2); - expect(renderNodeSpy.firstCall.args[2]).to.equal(items[0]); - expect(renderNodeSpy.secondCall.args[2]).to.equal(items[1]); - expect(renderNodeSpy.firstCall.args[3]).to.deep.equal({ item: items[0] }); - expect(renderNodeSpy.secondCall.args[3]).to.deep.equal({ item: items[1] }); - }); - - it('does not mutate the original loop node', () => { - const node: Element = { - id: 'item-template-immutable', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [], - }; - - const originalFor = node.for; - - const ctx: ResolveContext = { - props: { - items: [{ id: '1' }], - }, - state: {}, - item: undefined, - scope: undefined, - }; - - const renderNodeSpy = sinon.spy( - (...args: [any, React.Key, unknown, Record]) => { - void args; - return null; - } - ); - - renderFor(node, ctx, renderNodeSpy); - - expect(node.for).to.equal(originalFor); - expect(node.for?.as).to.equal('item'); - expect(renderNodeSpy.firstCall.args[0].for).to.equal(undefined); - }); - - it('supports non-primitive resolved keys from key template', () => { - const node: Element = { - id: 'item-template-object-key', - type: 'Label', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.keyObj}}', - }, - children: [], - }; - - const keyObj = { nested: 'value' }; - const ctx: ResolveContext = { - props: { - items: [{ keyObj }], - }, - state: {}, - item: undefined, - scope: undefined, - }; - - const renderNodeSpy = sinon.spy( - (...args: [any, React.Key, unknown, Record]) => { - void args; - return null; - } - ); - - renderFor(node, ctx, renderNodeSpy); - - expect(renderNodeSpy.calledOnce).to.equal(true); - expect(renderNodeSpy.firstCall.args[1]).to.equal(keyObj as any); - }); - }); - - describe('renderPrimitiveNode()', () => { - const ctx: ResolveContext = { - props: { name: 'Alice' }, - state: {}, - item: undefined, - scope: undefined, - }; - - it('resolves template strings', () => { - const result = renderPrimitiveNode('{{props.name}}', ctx); - expect(result).to.equal('Alice'); - }); - - it('passes through primitive values', () => { - expect(renderPrimitiveNode(0 as any, ctx)).to.equal(0); - expect(renderPrimitiveNode(true as any, ctx)).to.equal(true); - expect(renderPrimitiveNode(null as any, ctx)).to.equal(null); - }); - - it('returns null for non-primitive objects', () => { - const result = renderPrimitiveNode({ bad: 'node' } as any, ctx); - expect(result).to.equal(null); - }); - }); - - describe('renderElementNode()', () => { - const atoms: Record> = { - Card: ({ children, ...props }) => ( -
- {children} -
- ), - Child: ({ children }) => {children}, - }; - - const baseContext: ResolveContext = { - props: { - title: 'Hello world', - childText: 'from props', - }, - state: { - message: 'initial', - }, - item: undefined, - scope: undefined, - }; - - it('throws for unknown atom type', () => { - const node: Element = { - id: 'unknown-id', - type: 'Unknown', - children: [], - }; - - expect(() => - renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - () => null - ) - ).to.throw('unknown atom "Unknown"'); - }); - - it('renders atom with resolved props, binding props and children', () => { - const setStateSpy = sinon.spy(); - const callbacks = [ - { - name: 'onChange', - description: 'onChange callback', - callbackFn: sinon.spy(), - }, - ]; - - const childElement: Element = { - id: 'child-node', - type: 'Child', - children: ['nested-child'], - }; - - const node: Element = { - id: 'card-id', - type: 'Card', - staticProps: { - className: 'wrapper', - }, - bindings: { - title: { - bindType: 'expression', - value: '{{props.title}}', - }, - onClick: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - message: '{{event}}', - }, - }, - ], - }, - }, - children: ['{{props.childText}}', childElement], - }; - - const renderNodeSpy = sinon.spy((child: any) => { - if (child?.type === 'Child') { - return ( - - Nested - - ); - } - return null; - }); - - const element = renderElementNode( - node, - 'k1', - atoms, - callbacks, - setStateSpy as any, - () => ({ message: 'initial' }), - baseContext, - renderNodeSpy - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(card.getAttribute('class')).to.equal('wrapper'); - expect(card.getAttribute('data-atom-id')).to.equal('card-id'); - expect(card.getAttribute('data-atom-label')).to.equal('Card'); - expect(card.getAttribute('title')).to.equal('Hello world'); - expect(card.textContent).to.contain('from props'); - expect(card.textContent).to.contain('Nested'); - - expect(renderNodeSpy.calledOnce).to.equal(true); - expect(renderNodeSpy.firstCall.args[0]).to.equal(childElement); - - const onClick = (card as any).onclick ?? (card as any).props?.onClick; - expect(typeof onClick === 'function' || card.getAttribute('onClick') === null).to.equal(true); - }); - - it('renders with only static props and no bindings', () => { - const node: Element = { - id: 'static-id', - type: 'Card', - staticProps: { - className: 'static-class', - role: 'main', - }, - children: ['Static content'], - }; - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - () => null - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(card.getAttribute('class')).to.equal('static-class'); - expect(card.getAttribute('role')).to.equal('main'); - expect(card.textContent).to.equal('Static content'); - }); - - it('renders with only expression bindings', () => { - const node: Element = { - id: 'expr-id', - type: 'Card', - bindings: { - title: { - bindType: 'expression', - value: '{{props.title}}', - }, - subtitle: { - bindType: 'expression', - value: '{{state.message}}', - }, - 'aria-label': { - bindType: 'expression', - value: '{{props.title}} - {{state.message}}', - }, - }, - children: [], - }; - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({ message: 'initial' }), - baseContext, - () => null - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(card.getAttribute('title')).to.equal('Hello world'); - expect(card.getAttribute('subtitle')).to.equal('initial'); - expect(card.getAttribute('aria-label')).to.equal('Hello world - initial'); - }); - - it('renders with primitive-only children (text and template strings)', () => { - const node: Element = { - id: 'primitive-id', - type: 'Card', - children: ['Plain text: ', '{{props.childText}}', ' - end'], - }; - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - () => null - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(card.textContent).to.equal('Plain text: from props - end'); - }); - - it('renders with element-only children (no text content)', () => { - const childElement1: Element = { - id: 'child1', - type: 'Child', - children: ['First child'], - }; - - const childElement2: Element = { - id: 'child2', - type: 'Child', - children: ['Second child'], - }; - - const node: Element = { - id: 'element-children-id', - type: 'Card', - children: [childElement1, childElement2], - }; - - const renderNodeSpy = sinon.spy((child: any) => { - if (child?.id === 'child1') { - return ( - - Child 1 - - ); - } - if (child?.id === 'child2') { - return ( - - Child 2 - - ); - } - return null; - }); - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - renderNodeSpy - ); - - const rendered = render(<>{element}); - expect(rendered.getByTestId('card')).to.exist; - - expect(rendered.getByTestId('child-1')).to.exist; - expect(rendered.getByTestId('child-2')).to.exist; - expect(renderNodeSpy.callCount).to.equal(2); - }); - - it('handles mixed children when renderNode returns null for some elements', () => { - const childElement1: Element = { - id: 'child1', - type: 'Child', - children: ['Keep this'], - }; - - const childElement2: Element = { - id: 'child2', - type: 'Child', - children: ['Skip this'], - }; - - const node: Element = { - id: 'mixed-id', - type: 'Card', - children: [ - 'Text before: ', - childElement1, - ' - text between - ', - childElement2, - ' :text after', - ], - }; - - const renderNodeSpy = sinon.spy((child: any) => { - if (child?.id === 'child1') { - return ( - - Kept - - ); - } - return null; // Skip child2 - }); - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - renderNodeSpy - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(rendered.getByTestId('kept')).to.exist; - expect(card.textContent).to.contain('Text before:'); - expect(card.textContent).to.contain('Kept'); - expect(card.textContent).to.contain('text between'); - expect(card.textContent).to.contain('text after'); - }); - - it('verifies data attributes (data-atom-id and data-atom-label) are set', () => { - const node: Element = { - id: 'special-atom-id', - type: 'Card', - children: ['Content'], - }; - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({}), - baseContext, - () => null - ); - - const rendered = render(<>{element}); - const card = rendered.getByTestId('card'); - - expect(card.getAttribute('data-atom-id')).to.equal('special-atom-id'); - expect(card.getAttribute('data-atom-label')).to.equal('Card'); - }); - - it('handles bindings to undefined props gracefully', () => { - const node: Element = { - id: 'undefined-props-id', - type: 'Card', - bindings: { - missing: { - bindType: 'expression', - value: '{{props.nonexistent}}', - }, - alsoMissing: { - bindType: 'expression', - value: '{{state.notThere}}', - }, - }, - children: ['Content with undefined bindings'], - }; - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({ message: 'exists' }), - baseContext, - () => null - ); - - const rendered = render(<>{element}); - - // Should not throw, attributes may be undefined or omitted - expect(rendered.getByTestId('card').textContent).to.contain( - 'Content with undefined bindings' - ); - }); - - it('renders complex nested structure with mixed binding types', () => { - const innerChild: Element = { - id: 'inner-child', - type: 'Child', - children: ['Inner {{props.title}}'], - }; - - const middleElement: Element = { - id: 'middle', - type: 'Card', - staticProps: { role: 'group' }, - bindings: { - dataLevel: { - bindType: 'expression', - value: '{{state.message}}', - }, - }, - children: ['Middle: ', innerChild], - }; - - const node: Element = { - id: 'outer', - type: 'Card', - staticProps: { className: 'outer-wrapper' }, - bindings: { - title: { - bindType: 'expression', - value: '{{props.title}}', - }, - }, - children: ['Outer text ', middleElement], - }; - - const renderNodeSpy = sinon.spy((child: any) => { - if (child?.id === 'middle') { - return ( -
- {/* Rendered recursively */} - {child.type} -
- ); - } - if (child?.id === 'inner-child') { - return ( - - Inner content - - ); - } - return null; - }); - - const element = renderElementNode( - node, - 0, - atoms, - [], - () => undefined, - () => ({ message: 'nested-state' }), - baseContext, - renderNodeSpy - ); - - const rendered = render(<>{element}); - const outer = rendered.getByTestId('card'); - - expect(outer.getAttribute('class')).to.equal('outer-wrapper'); - expect(outer.getAttribute('title')).to.equal('Hello world'); - expect(outer.textContent).to.contain('Outer text'); - }); - - it('event callback has access to current props and state at call time', () => { - const setStateSpy = sinon.spy(); - const onEventSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - clicked: '{{event}}', - timestamp: '{{state.message}}', - }, - }, - { - call: 'onEvent', - args: ['{{event}}', '{{props.title}}', '{{state.message}}'], - }, - ], - } as any, - [{ name: 'onEvent', description: 'onEvent callback', callbackFn: onEventSpy }], - () => ({ message: 'state-value' }), - setStateSpy as any, - { ...baseContext, props: { title: 'Button Title' } } - ); - - callback(true); - - expect(setStateSpy.called).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ - clicked: true, - timestamp: 'state-value', - }); - - expect(onEventSpy.called).to.equal(true); - expect(onEventSpy.firstCall.args).to.deep.equal([true, 'Button Title', 'state-value']); - }); - - it('atom component receives exact props shape with static and bound props', () => { - const atomSpy = sinon.spy(({ children, ...props }: any) => ( -
- {children} -
- )); - - const customAtoms = { - SpyCard: atomSpy, - }; - - const node: Element = { - id: 'spy-id', - type: 'SpyCard', - staticProps: { - className: 'static-class', - role: 'main', - }, - bindings: { - title: { - bindType: 'expression', - value: '{{props.title}}', - }, - 'aria-label': { - bindType: 'expression', - value: 'Label', - }, - }, - children: [], - }; - - const element = renderElementNode( - node, - 0, - customAtoms, - [], - () => undefined, - () => ({}), - { ...baseContext, props: { title: 'Test Title' } }, - () => null - ); - - render(<>{element}); - - expect(atomSpy.called).to.equal(true); - const propsArg = atomSpy.firstCall.args[0]; - - expect(propsArg.className).to.equal('static-class'); - expect(propsArg.role).to.equal('main'); - expect(propsArg.title).to.equal('Test Title'); - expect(propsArg['aria-label']).to.equal('Label'); - expect(propsArg['data-atom-id']).to.equal('spy-id'); - expect(propsArg['data-atom-label']).to.equal('SpyCard'); - }); - - it('multiple setState actions from single event update all state properties', () => { - const setStateSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['x', 'y'], - actions: [ - { - setState: { - first: '{{event.x}}', - second: '{{event.y}}', - combined: '{{event.x}}-{{event.y}}', - fromState: '{{state.message}}', - }, - }, - ], - } as any, - [], - () => ({ message: 'original' }), - setStateSpy as any, - baseContext - ); - - callback('first-val', 'second-val'); - - expect(setStateSpy.called).to.equal(true); - const stateArg = setStateSpy.firstCall.args[0]; - expect(stateArg.fromState).to.equal('original'); - expect(stateArg.first).to.equal('first-val'); - expect(stateArg.second).to.equal('second-val'); - }); - - it('renders with key prop passed through correctly', () => { - const atomWithKeySpy = sinon.spy(({ children, ...props }: any) => ( -
- {children} -
- )); - - const customAtoms = { - KeyCard: atomWithKeySpy, - }; - - const node: Element = { - id: 'key-test-id', - type: 'KeyCard', - children: ['Content'], - }; - - const element = renderElementNode( - node, - 'unique-key-123', - customAtoms, - [], - () => undefined, - () => ({}), - baseContext, - () => null - ); - - render(<>{element}); - - // Key is used by React internally but not passed as prop - // The component should still render correctly - expect(atomWithKeySpy.called).to.equal(true); - }); - }); - - describe('buildEventCallback()', () => { - it('resolves setState and call actions with multi-argument event payload', () => { - const setStateSpy = sinon.spy(); - const trackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value', 'label'], - actions: [ - { - setState: { - selected: '{{event.value}}', - selectedLabel: '{{event.label}}', - previous: '{{state.current}}', - }, - }, - { - call: 'trackSelection', - args: ['{{event.value}}', '{{props.kind}}', '{{state.current}}'], - }, - ], - } as any, - [ - { - name: 'trackSelection', - description: 'trackSelection callback', - callbackFn: trackSpy, - }, - ], - () => ({ current: 'hats' }), - setStateSpy as any, - { - props: { kind: 'category' }, - state: { current: 'hats' }, - item: undefined, - scope: undefined, - } - ); - - callback('bags', 'Bags'); - - expect(setStateSpy.calledOnce).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ - selected: 'bags', - selectedLabel: 'Bags', - previous: 'hats', - }); - - expect(trackSpy.calledOnce).to.equal(true); - expect(trackSpy.firstCall.args).to.deep.equal(['bags', 'category', 'hats']); - }); - - it('does not set state when there are no setState actions', () => { - const setStateSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'missingCallback', - args: ['{{event}}'], - }, - ], - } as any, - [], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('anything'); - expect(setStateSpy.called).to.equal(false); - }); - - it('executes only call actions without setState actions', () => { - const setStateSpy = sinon.spy(); - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'onlyCall', - args: ['{{event}}', '{{props.label}}'], - }, - ], - } as any, - [{ name: 'onlyCall', description: 'onlyCall callback', callbackFn: callbackSpy }], - () => ({}), - setStateSpy as any, - { - props: { label: 'Test Label' }, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('test-value'); - - expect(setStateSpy.called).to.equal(false); - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal(['test-value', 'Test Label']); - }); - - it('handles missing callback function gracefully (does not throw)', () => { - const setStateSpy = sinon.spy(); - const warnSpy = sinon.stub(console, 'warn'); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'nonexistentCallback', - args: ['{{event}}'], - }, - ], - } as any, - [], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - // Should not throw even though callback doesn't exist - expect(() => callback('value')).to.not.throw(); - expect(warnSpy.calledWithMatch(/nonexistentCallback/)).to.equal(true); - warnSpy.restore(); - }); - - it('handles event with no arguments', () => { - const setStateSpy = sinon.spy(); - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: [], - actions: [ - { - setState: { - triggered: true, - }, - }, - { - call: 'onTriggered', - args: [], - }, - ], - } as any, - [{ name: 'onTriggered', description: 'onTriggered callback', callbackFn: callbackSpy }], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback(); - - expect(setStateSpy.calledOnce).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ triggered: true }); - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal([]); - }); - - it('resolves template args with props, state, item, and scope', () => { - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'complexCall', - args: ['{{event}}', '{{props.name}}', '{{state.count}}', '{{item.id}}'], - }, - ], - } as any, - [{ name: 'complexCall', description: 'complexCall callback', callbackFn: callbackSpy }], - () => ({ count: 42 }), - () => undefined, - { - props: { name: 'John' }, - state: { count: 42 }, - item: { id: 'item-123' }, - scope: { context: 'scope-value' }, - } - ); - - callback('event-data'); - - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal(['event-data', 'John', 42, 'item-123']); - }); - - it('executes setState before call actions in order', () => { - const setStateSpy = sinon.spy(); - - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - updated: true, - }, - }, - { - call: 'onUpdated', - args: [], - }, - ], - } as any, - [{ name: 'onUpdated', description: 'onUpdated callback', callbackFn: callbackSpy }], - () => ({ updated: false }), - setStateSpy as any, - { - props: {}, - state: { updated: false }, - item: undefined, - scope: undefined, - } - ); - - callback(); - - // Verify both were called - expect(setStateSpy.calledOnce).to.equal(true); - expect(callbackSpy.calledOnce).to.equal(true); - }); - - it('handles multiple call actions in sequence', () => { - const setStateSpy = sinon.spy(); - const call1Spy = sinon.spy(); - const call2Spy = sinon.spy(); - const call3Spy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'first', - args: ['{{event}}-1'], - }, - { - call: 'second', - args: ['{{event}}-2'], - }, - { - call: 'third', - args: ['{{event}}-3'], - }, - ], - } as any, - [ - { name: 'first', description: 'first callback', callbackFn: call1Spy }, - { name: 'second', description: 'second callback', callbackFn: call2Spy }, - { name: 'third', description: 'third callback', callbackFn: call3Spy }, - ], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('test'); - - expect(call1Spy.calledOnce).to.equal(true); - expect(call1Spy.firstCall.args).to.deep.equal(['test-1']); - expect(call2Spy.calledOnce).to.equal(true); - expect(call2Spy.firstCall.args).to.deep.equal(['test-2']); - expect(call3Spy.calledOnce).to.equal(true); - expect(call3Spy.firstCall.args).to.deep.equal(['test-3']); - }); - - it('handles call action without any arguments', () => { - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - call: 'noArgs', - args: [], - }, - ], - } as any, - [{ name: 'noArgs', description: 'noArgs callback', callbackFn: callbackSpy }], - () => ({}), - () => undefined, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('ignored'); - - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal([]); - }); - - it('handles empty actions array', () => { - const setStateSpy = sinon.spy(); - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [], - } as any, - [{ name: 'anyCallback', description: 'anyCallback', callbackFn: callbackSpy }], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('value'); - - expect(setStateSpy.called).to.equal(false); - expect(callbackSpy.called).to.equal(false); - }); - - it('handles single argument event binding', () => { - const setStateSpy = sinon.spy(); - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['selectedId'], - actions: [ - { - setState: { - id: '{{event}}', - }, - }, - { - call: 'onSelect', - args: ['{{event}}'], - }, - ], - } as any, - [{ name: 'onSelect', description: 'onSelect callback', callbackFn: callbackSpy }], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback('id-456'); - - expect(setStateSpy.calledOnce).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ id: 'id-456' }); - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal(['id-456']); - }); - - it('call action can access state values changed by setState', () => { - const setStateSpy = sinon.spy(); - const callbackSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['newValue'], - actions: [ - { - setState: { - value: '{{event}}', - }, - }, - { - call: 'onValueSet', - args: ['{{state.value}}'], - }, - ], - } as any, - [{ name: 'onValueSet', description: 'onValueSet callback', callbackFn: callbackSpy }], - () => ({ value: 'old' }), - setStateSpy as any, - { - props: {}, - state: { value: 'old' }, - item: undefined, - scope: undefined, - } - ); - - callback('new'); - - // When call action accesses state, it should use the original state (before setState) - // This validates that templates are resolved with the current context - expect(callbackSpy.calledOnce).to.equal(true); - expect(callbackSpy.firstCall.args).to.deep.equal(['old']); - }); - - it('handles undefined and null event values in setState', () => { - const setStateSpy = sinon.spy(); - - const callback = buildEventCallback( - { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - result: '{{event}}', - }, - }, - ], - } as any, - [], - () => ({}), - setStateSpy as any, - { - props: {}, - state: {}, - item: undefined, - scope: undefined, - } - ); - - callback(null); - - expect(setStateSpy.calledOnce).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ result: null }); - - setStateSpy.resetHistory(); - - callback(undefined); - - expect(setStateSpy.calledOnce).to.equal(true); - expect(setStateSpy.firstCall.args[0]).to.deep.equal({ result: undefined }); - }); - }); - - describe('createView()', () => { - const atoms: Record> = { - Stack: ({ children }) =>
{children}
, - Text: ({ children }) =>

{children}

, - Emitter: ({ onValueChange }) => ( - - ), - }; - - it('creates a component with displayName and updates state through bound events', () => { - const onChangedSpy = sinon.spy(); - const doc: Document = { - name: 'SimpleGeneratedView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['{{state.message}}'], - }, - { - id: 'emitter', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - message: '{{event}}', - }, - }, - { - call: 'onChanged', - args: ['{{event}}'], - }, - ], - }, - }, - children: [], - }, - ], - }, - state: { - message: 'initial', - }, - }; - - const Generated = createView(doc, atoms, [ - { name: 'onChanged', description: 'onChanged callback', callbackFn: onChangedSpy }, - ]); - - expect(Generated.displayName).to.equal('SimpleGeneratedView'); - - const rendered = render(); - - expect(rendered.getByTestId('text').textContent).to.equal('initial'); - - fireEvent.click(rendered.getByTestId('emit')); - - expect(rendered.getByTestId('text').textContent).to.equal('updated'); - expect(onChangedSpy.calledOnce).to.equal(true); - expect(onChangedSpy.firstCall.args[0]).to.equal('updated'); - }); - - it('merges plain-object document.props into resolve context for bindings', () => { - const doc: Document = { - name: 'PropsDoc', - props: { label: 'from-document' }, - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['{{props.label}}'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - expect(rendered.getByTestId('text').textContent).to.equal('from-document'); - }); - - it('ignores non-object document.props when resolving bindings', () => { - const doc: Document = { - name: 'BadPropsDoc', - props: 'not-an-object' as unknown as Document['props'], - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['{{props.label}}'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - expect(rendered.getByTestId('text').textContent).to.equal('runtime'); - }); - - it('supports for/show expressions with runtime props', () => { - const doc: Document = { - name: 'LoopAndShowDoc', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'line', - type: 'Stack', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [ - { - id: 'line-text', - type: 'Text', - show: { - left: '{{item.visible}}', - op: 'eq', - right: true as any, - }, - children: ['{{item.label}}'], - }, - ], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - const rendered = render( - - ); - - expect(rendered.container.textContent).to.contain('First'); - expect(rendered.container.textContent).to.contain('Third'); - expect(rendered.container.textContent).to.not.contain('Second'); - }); - - it('renders component with no initial state', () => { - const doc: Document = { - name: 'NoStateView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['Static content'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - expect(rendered.getByTestId('text').textContent).to.equal('Static content'); - }); - - it('handles multiple events updating different state properties independently', () => { - const onFirstSpy = sinon.spy(); - const onSecondSpy = sinon.spy(); - - const doc: Document = { - name: 'MultiEventView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text1', - type: 'Text', - children: ['First: {{state.first}}'], - }, - { - id: 'text2', - type: 'Text', - children: ['Second: {{state.second}}'], - }, - { - id: 'emitter1', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - first: '{{event}}', - }, - }, - { - call: 'onFirst', - args: ['{{event}}'], - }, - ], - }, - }, - children: [], - }, - { - id: 'emitter2', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - second: '{{event}}', - }, - }, - { - call: 'onSecond', - args: ['{{event}}'], - }, - ], - }, - }, - children: [], - }, - ], - }, - state: { - first: 'A', - second: 'B', - }, - }; - - const Generated = createView(doc, atoms, [ - { name: 'onFirst', description: 'onFirst callback', callbackFn: onFirstSpy }, - { name: 'onSecond', description: 'onSecond callback', callbackFn: onSecondSpy }, - ]); - - const rendered = render(); - - expect(rendered.container.textContent).to.contain('First: A'); - expect(rendered.container.textContent).to.contain('Second: B'); - - fireEvent.click(rendered.getAllByTestId('emit')[0]); - - expect(rendered.container.textContent).to.contain('First: updated'); - expect(rendered.container.textContent).to.contain('Second: B'); - expect(onFirstSpy.calledOnce).to.equal(true); - - fireEvent.click(rendered.getAllByTestId('emit')[1]); - - expect(rendered.container.textContent).to.contain('First: updated'); - expect(rendered.container.textContent).to.contain('Second: updated'); - expect(onSecondSpy.calledOnce).to.equal(true); - }); - - it('renders nested for loops (for within for)', () => { - const doc: Document = { - name: 'NestedLoopView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'group', - type: 'Stack', - for: { - each: '{{props.groups}}', - as: 'group', - key: '{{group.id}}', - }, - children: [ - { - id: 'item', - type: 'Text', - for: { - each: '{{group.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: ['{{item.name}}'], - }, - ], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - const rendered = render( - - ); - - expect(rendered.container.textContent).to.contain('ItemA'); - expect(rendered.container.textContent).to.contain('ItemB'); - expect(rendered.container.textContent).to.contain('ItemC'); - }); - - it('supports show condition with different operators (eq, ne)', () => { - const doc: Document = { - name: 'ShowOperatorsView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'eq-text', - type: 'Text', - show: { - left: '{{props.status}}', - op: 'eq', - right: 'active', - }, - children: ['Status is active'], - }, - { - id: 'ne-text', - type: 'Text', - show: { - left: '{{props.status}}', - op: 'ne', - right: 'inactive', - }, - children: ['Status is not inactive'], - }, - { - id: 'level-text', - type: 'Text', - show: { - left: '{{props.level}}', - op: 'eq', - right: 'admin', - }, - children: ['User is admin'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - const rendered = render(); - - expect(rendered.container.textContent).to.contain('Status is active'); - expect(rendered.container.textContent).to.contain('Status is not inactive'); - expect(rendered.container.textContent).to.contain('User is admin'); - }); - - it('combines for and per-item show via a nested element for conditional list rendering', () => { - const doc: Document = { - name: 'ForShowCombinedView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'row', - type: 'Stack', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [ - { - id: 'item', - type: 'Text', - show: { - left: '{{item.status}}', - op: 'eq', - right: 'active', - }, - children: ['{{item.label}}'], - }, - ], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - const rendered = render( - - ); - - expect(rendered.container.textContent).to.contain('Active One'); - expect(rendered.container.textContent).to.contain('Active Two'); - expect(rendered.container.textContent).to.not.contain('Inactive'); - }); - - it('evaluates show on the same node as for once in the parent context to skip the entire loop', () => { - const doc: Document = { - name: 'LoopLevelShowDoc', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'line', - type: 'Text', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - show: { - left: '{{props.renderItems}}', - op: 'eq', - right: 'yes', - }, - children: ['{{item.label}}'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - const hidden = render( - - ); - expect(hidden.container.textContent).to.not.contain('A'); - - const visible = render( - - ); - expect(visible.container.textContent).to.contain('A'); - expect(visible.container.textContent).to.contain('B'); - }); - - it('updates state through multiple sequential events', () => { - const doc: Document = { - name: 'SequentialEventsView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'count-text', - type: 'Text', - children: ['Count: {{state.count}}'], - }, - { - id: 'emitter', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - count: '{{event}}', - }, - }, - ], - }, - }, - children: [], - }, - ], - }, - state: { - count: 0, - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - expect(rendered.getByTestId('text').textContent).to.equal('Count: 0'); - - fireEvent.click(rendered.getByTestId('emit')); - expect(rendered.getByTestId('text').textContent).to.equal('Count: updated'); - - fireEvent.click(rendered.getByTestId('emit')); - expect(rendered.getByTestId('text').textContent).to.equal('Count: updated'); - }); - - it('handles event binding with no actions', () => { - const doc: Document = { - name: 'NoActionsView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['Click me'], - }, - { - id: 'button-with-no-actions', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [], - }, - }, - children: [], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - expect(() => { - const rendered = render(); - fireEvent.click(rendered.getByTestId('emit')); - }).to.not.throw(); - }); - - it('accepts runtime props not referenced in templates', () => { - const doc: Document = { - name: 'UnusedPropsView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'text', - type: 'Text', - children: ['Content'], - }, - ], - }, - }; - - const Generated = createView(doc, atoms); - - expect(() => { - const rendered = render( - - ); - expect(rendered.getByTestId('text').textContent).to.equal('Content'); - }).to.not.throw(); - }); - - it('initializes state with various value types (numbers, booleans, objects, arrays)', () => { - const doc: Document = { - name: 'VariousTypesView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'number-text', - type: 'Text', - children: ['Number: {{state.count}}'], - }, - { - id: 'bool-text', - type: 'Text', - children: ['Active: {{state.active}}'], - }, - { - id: 'object-text', - type: 'Text', - children: ['Name: {{state.user.name}}'], - }, - { - id: 'array-text', - type: 'Text', - children: ['Items: {{state.items.length}}'], - }, - ], - }, - state: { - count: 42, - active: true, - user: { name: 'John', age: 30 }, - items: [1, 2, 3], - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - const textElements = rendered.getAllByTestId('text'); - expect(textElements[0].textContent).to.contain('Number: 42'); - expect(textElements[1].textContent).to.contain('Active: true'); - expect(textElements[2].textContent).to.contain('Name: John'); - expect(textElements[3].textContent).to.contain('Items: 3'); - }); - - it('supports dynamic prop expression binding to state', () => { - const onUpdateSpy = sinon.spy(); - - const doc: Document = { - name: 'DynamicPropsView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'emitter', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - label: '{{event}}', - }, - }, - { - call: 'onUpdate', - args: ['{{state.label}}'], - }, - ], - }, - }, - children: [], - }, - ], - }, - state: { - label: 'initial', - }, - }; - - const Generated = createView(doc, atoms, [ - { name: 'onUpdate', description: 'onUpdate callback', callbackFn: onUpdateSpy }, - ]); - const rendered = render(); - - fireEvent.click(rendered.getByTestId('emit')); - - expect(onUpdateSpy.calledOnce).to.equal(true); - }); - - it('show condition based on state value updates reactively', () => { - const doc: Document = { - name: 'ShowStateReactiveView', - root: { - id: 'root', - type: 'Stack', - children: [ - { - id: 'message', - type: 'Text', - show: { - left: '{{state.messageVisible}}', - op: 'eq', - right: 'shown', - }, - children: ['Message is visible'], - }, - { - id: 'emitter', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - messageVisible: 'shown', - }, - }, - ], - }, - }, - children: [], - }, - ], - }, - state: { - messageVisible: 'hidden', - }, - }; - - const Generated = createView(doc, atoms); - const rendered = render(); - - expect(rendered.container.textContent).to.not.contain('Message is visible'); - - fireEvent.click(rendered.getByTestId('emit')); - - expect(rendered.container.textContent).to.contain('Message is visible'); - }); - - it('renders complex nested element structure with mixed binding types', () => { - const onNestedEventSpy = sinon.spy(); - - const doc: Document = { - name: 'ComplexNestedView', - root: { - id: 'root', - type: 'Stack', - staticProps: { role: 'main' }, - children: [ - { - id: 'outer-text', - type: 'Text', - bindings: { - title: { - bindType: 'expression', - value: '{{props.title}}', - }, - }, - children: ['Outer: {{state.outerValue}}'], - }, - { - id: 'group', - type: 'Stack', - for: { - each: '{{props.items}}', - as: 'item', - key: '{{item.id}}', - }, - children: [ - { - id: 'group-row', - type: 'Stack', - show: { - left: '{{item.visibility}}', - op: 'eq', - right: 'visible', - }, - children: [ - { - id: 'item-text', - type: 'Text', - children: ['{{item.label}} - {{state.outerValue}} - '], - }, - { - id: 'nested-emitter', - type: 'Emitter', - bindings: { - onValueChange: { - bindType: 'event', - arguments: ['value'], - actions: [ - { - setState: { - outerValue: '{{event}}', - }, - }, - { - call: 'onNestedEvent', - args: ['{{item.id}}', '{{event}}'], - }, - ], - }, - }, - children: [], - }, - ], - }, - ], - }, - ], - }, - state: { - outerValue: 'initial', - }, - }; - - const Generated = createView(doc, atoms, [ - { - name: 'onNestedEvent', - description: 'onNestedEvent callback', - callbackFn: onNestedEventSpy, - }, - ]); - - const rendered = render( - - ); - - expect(rendered.container.textContent).to.contain('Item 1'); - expect(rendered.container.textContent).to.contain('Item 3'); - expect(rendered.container.textContent).to.not.contain('Item 2'); - - const buttons = rendered.getAllByTestId('emit'); - fireEvent.click(buttons[0]); - - expect(onNestedEventSpy.calledOnce).to.equal(true); - expect(onNestedEventSpy.firstCall.args).to.deep.equal(['i1', 'updated']); - }); - }); -}); diff --git a/packages/react/src/atoms/component-layout/createView.tsx b/packages/react/src/atoms/component-layout/createView.tsx deleted file mode 100644 index ea4a2eebd0..0000000000 --- a/packages/react/src/atoms/component-layout/createView.tsx +++ /dev/null @@ -1,319 +0,0 @@ -'use client'; -/** - * createView: renders a Component Layout document as a React tree. - * Uses document types and resolver from @sitecore-content-sdk/content/editing. - */ - -import React, { type FC, type Key, useReducer, useRef } from 'react'; -import { - type Document, - type Node, - type Element, - type EventBinding, - type ResolveContext, - isElement, - hasFor, - hasShow, - isExpressionBinding, - isEventBinding, - isSetStateAction, - isCallAction, - resolveTemplateString, - evaluateShowNode, - isPrimitive, - resolveIfTemplate, -} from '@sitecore-content-sdk/content/atoms'; -import { CallbackMetadata } from '..'; - -/** Props passed to an Atom Component */ -type AtomProps = React.PropsWithChildren; - -/** The Atom Component */ -type AtomComponent = React.ComponentType; - -/** Internal state shape used by generated view components. */ -type ViewState = Record; - -/** Patch shape for reducer updates. */ -type StatePatch = Partial; - -/** Scope values available to template resolution (e.g. for-loop alias). */ -type ScopeMap = Record; - -/** Props passed to rendered atoms after bindings/template resolution. */ -type ResolvedProps = Record & { - 'data-atom-id'?: string; - 'data-atom-label'?: string; -}; - -/** - * Renders nodes in a for-loop, iterating over an array with optional key resolution. - * @param {Element} node - The element node with a `for` binding - * @param {ResolveContext} forCtx - Resolve context for the loop - * @param {(tmpl: Element, key: Key, item: unknown, itemScope: ScopeMap) => React.ReactNode} renderNode - Render function - * @returns {React.ReactNode} Array of rendered nodes or null if array resolution fails - * @internal - */ -export const renderFor = ( - node: Element, - forCtx: ResolveContext, - renderNode: (tmpl: Node, key: Key, item: unknown, itemScope: ScopeMap) => React.ReactNode -): React.ReactNode => { - const forArr = resolveTemplateString(node.for?.each ?? '', forCtx); - if (!Array.isArray(forArr)) { - return null; - } - - return forArr.map((item: unknown, index: number) => { - const tmpl: Element = { ...node, for: undefined }; - const itemScope: ScopeMap = { [node.for!.as]: item }; - const itemKey = node.for?.key - ? resolveTemplateString(node.for.key, { - ...forCtx, - item: item, - scope: itemScope, - }) - : index; - - return renderNode(tmpl, itemKey as Key, item, itemScope); - }); -}; - -/** - * Renders a primitive or template-string node. - * Returns null for unrecognised node types. - * @param {Node} node - A non-element node (string, number, boolean, null) - * @param {ResolveContext} ctx - Resolve context for template string resolution - * @returns {React.ReactNode} Resolved node or null - */ -export const renderPrimitiveNode = (node: Node, ctx: ResolveContext): React.ReactNode => { - // resolve template strings - if (typeof node === 'string') { - return (resolveIfTemplate(node, ctx) ?? null) as React.ReactNode; - } - - // pass through primitives React can render natively - if (isPrimitive(node)) { - return node; - } - - return null; -}; - -/** - * Renders an element node (atom) with resolved props, bindings, and children. - * @param {Element} node - The element node to render - * @param {Key | undefined} key - React key for the element - * @param {Record>} atoms - Atom component registry - * @param {CallbackMetadata[]} callbacks - Callback metadata array - * @param {React.Dispatch} setState - State dispatcher - * @param {() => ViewState} getState - Function to get the current state at callback time - * @param {ResolveContext} ctx - Resolve context - * @param {(node: Node, key: Key | undefined, itemCtx: unknown, scope: ScopeMap | undefined) => React.ReactNode} renderNode - Recursive render function - * @returns {React.ReactNode} Rendered atom element - * @internal - */ -export const renderElementNode = ( - node: Element, - key: Key | undefined, - atoms: Record>, - callbacks: CallbackMetadata[], - setState: React.Dispatch, - getState: () => ViewState, - ctx: ResolveContext, - renderNode: ( - node: Node, - key: Key | undefined, - itemCtx: unknown, - scope: ScopeMap | undefined - ) => React.ReactNode -): React.ReactNode => { - const { id, type, staticProps = {}, bindings = {}, children = [] } = node; - - const Atom = atoms[type] as AtomComponent | undefined; - if (!Atom) { - throw new Error(`Component Layout: unknown atom "${type}" with id "${id}".`); - } - - const resolvedProps: ResolvedProps = { ...staticProps }; - - for (const [propName, binding] of Object.entries(bindings)) { - if (isExpressionBinding(binding)) { - resolvedProps[propName] = resolveTemplateString(binding.value, ctx); - } else if (isEventBinding(binding)) { - resolvedProps[propName] = buildEventCallback(binding, callbacks, getState, setState, ctx); - } - } - - resolvedProps['data-atom-id'] = id; - resolvedProps['data-atom-label'] = type; - - const childNodes: React.ReactNode[] = children.map((c, i) => { - if (typeof c === 'string') { - return (resolveIfTemplate(c, ctx) ?? null) as React.ReactNode; - } - return renderNode(c, i, ctx.item, ctx.scope); - }); - - return childNodes.length > 0 ? ( - - {childNodes} - - ) : ( - - ); -}; - -/** - * Builds a callable function from an event binding. - * Resolves setState values and call args with template strings; invokes callbacks. - * @param {EventBinding} binding the event binding to build the callback from - * @param {CallbackMetadata[]} callbacks the array of callback metadata to use for call actions - * @param {() => ViewState} getState function to get the latest state at the time of event handling - * @param {React.Dispatch} setState the React state dispatcher to apply setState actions - * @param {ResolveContext} resolveContext - Resolve context - * @returns {(...args: unknown[]) => void} a function that can be used as an event handler - * @internal - */ -export const buildEventCallback = ( - binding: EventBinding, - callbacks: CallbackMetadata[], - getState: () => ViewState, - setState: React.Dispatch, - resolveContext: ResolveContext -): ((...args: unknown[]) => void) => { - return (...args: unknown[]) => { - let eventValue: unknown; - if (binding.arguments.length <= 1) { - eventValue = args[0]; - } else { - const obj: Record = {}; - binding.arguments.forEach((name, i) => { - obj[name] = args[i]; - }); - eventValue = obj; - } - - const patch: StatePatch = {}; - const { props, item, scope } = resolveContext; - const ctx: ResolveContext = { - props, - item, - scope, - state: getState(), - event: eventValue, - }; - - for (const action of binding.actions) { - if (isSetStateAction(action)) { - for (const [key, value] of Object.entries(action.setState)) { - patch[key] = resolveIfTemplate(value, ctx); - } - continue; - } - - if (isCallAction(action)) { - const resolvedArgs = (action.args ?? []).map((a) => resolveIfTemplate(a, ctx)); - const callable = callbacks.find((c) => c.name === action.call)?.callbackFn; - if (typeof callable === 'function') { - callable(...resolvedArgs); - } else { - console.warn( - `[createView] Callback "${action.call}" is not registered or is not a function.` - ); - } - continue; - } - } - - if (Object.keys(patch).length > 0) { - setState(patch); - } - }; -}; - -/** - * Creates a React functional component that renders the given Component Layout document. - * @param {Document} doc - Component Layout document - * @param {Record>} atoms - Map of atom type name to its React implementation - * @param {CallbackMetadata[]} [callbacks] - Optional array of callback metadata for event actions - * @returns {FC} FC that accepts runtime props (spread as props in expressions) - * @internal - */ -export function createView = Record>( - doc: Document, - atoms: Record>, - callbacks: CallbackMetadata[] = [] -): FC { - const { root, state: initialState = {} } = doc; - const documentProps = - doc.props !== null && - doc.props !== undefined && - typeof doc.props === 'object' && - !Array.isArray(doc.props) - ? (doc.props as Record) - : {}; - - const Generated: FC = (runtimeProps) => { - const [state, setState] = useReducer( - (state: ViewState, patch: StatePatch) => ({ - ...state, - ...patch, - }), - initialState as ViewState - ); - - const stateRef = useRef(state); - stateRef.current = state; - - const getState = () => stateRef.current; - - const renderNode = ( - node: Node, - key: Key | undefined, - itemCtx: unknown, - scope: ScopeMap | undefined - ): React.ReactNode => { - const ctx: ResolveContext = { - props: { ...documentProps, ...(runtimeProps as Record) }, - state: stateRef.current, - item: itemCtx, - scope, - }; - - if (isElement(node)) { - // When both `for` and `show` are set, evaluate `show` once in the parent context - // before iterating so the loop can be skipped entirely (then strip `show` per iteration). - if (hasFor(node)) { - const loopNode = - hasShow(node) && !evaluateShowNode(node.show, ctx) - ? null - : hasShow(node) - ? ({ ...node, show: undefined } as Element) - : node; - if (loopNode === null) { - return null; - } - return renderFor(loopNode, ctx, renderNode); - } - - if (hasShow(node)) { - if (!evaluateShowNode(node.show, ctx)) { - return null; - } - } - - return renderElementNode(node, key, atoms, callbacks, setState, getState, ctx, renderNode); - } - - return renderPrimitiveNode(node, ctx); - }; - - const rootNode = renderNode(root, 0, undefined, undefined); - - return <>{rootNode}; - }; - - Generated.displayName = doc.name; - return Generated; -} diff --git a/packages/react/src/atoms/component-layout/index.ts b/packages/react/src/atoms/component-layout/index.ts deleted file mode 100644 index 038922623d..0000000000 --- a/packages/react/src/atoms/component-layout/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Component Layout: createView for rendering no-code layout documents. - */ - -export { createView } from './createView'; diff --git a/packages/react/src/atoms/create-ncc.test.tsx b/packages/react/src/atoms/create-ncc.test.tsx new file mode 100644 index 0000000000..6b4d3283aa --- /dev/null +++ b/packages/react/src/atoms/create-ncc.test.tsx @@ -0,0 +1,36 @@ +/* eslint-disable jsdoc/require-jsdoc */ +import { expect } from 'chai'; +import type { Document } from '@sitecore-content-sdk/content/atoms'; +import type { DefineRegistryResult } from '@json-render/react'; +import { createNCC } from '.'; + +describe('create-ncc', () => { + const mockDoc: Document = { + name: 'TestComponent', + root: 'root-el', + elements: { + 'root-el': { + type: 'Card', + props: { title: 'Hello' }, + children: [], + }, + }, + state: { count: 0 }, + }; + + const mockRegistry: DefineRegistryResult = { + registry: {} as any, + handlers: () => ({}), + executeAction: async () => {}, + }; + + it('returns an FC with the document name as displayName', () => { + const View = createNCC(mockDoc, mockRegistry); + expect(View.displayName).to.equal('TestComponent'); + }); + + it('returns a functional component', () => { + const View = createNCC(mockDoc, mockRegistry); + expect(typeof View).to.equal('function'); + }); +}); diff --git a/packages/react/src/atoms/create-ncc.tsx b/packages/react/src/atoms/create-ncc.tsx new file mode 100644 index 0000000000..b2c93abb23 --- /dev/null +++ b/packages/react/src/atoms/create-ncc.tsx @@ -0,0 +1,57 @@ +'use client'; +import React, { type FC, useState } from 'react'; +import { Renderer, StateProvider, ActionProvider, VisibilityProvider } from '@json-render/react'; +import type { DefineRegistryResult } from '@json-render/react'; +import { createStateStore } from '@json-render/react'; +import type { StateModel } from '@json-render/core'; +import { Document } from '@sitecore-content-sdk/content/atoms'; +import { useSitecore } from '../components/SitecoreProvider'; + +/** + * Creates a React functional component that renders the given Component Layout document + * using json-render's Renderer. The document's flat element map is passed as a spec. + * @param {Document} doc - Component Layout document (flat spec format) + * @param {DefineRegistryResult} registryResult - The registry from defineAtomsRegistry + * @returns {FC>} FC that accepts runtime props merged into spec state + * @internal + */ +export function createNCC( + doc: Document, + registryResult: DefineRegistryResult +): FC> { + const { registry, handlers } = registryResult; + + const initialState: StateModel = { + ...(doc.state ?? {}), + }; + + const Generated: FC> = (runtimeProps) => { + const { atomsConfig } = useSitecore(); + const [store] = useState(() => + createStateStore({ + ...initialState, + ...runtimeProps, + }) + ); + const [resolvedHandlers] = useState(() => + handlers( + () => (updater) => store.update(updater(store.getSnapshot())), + () => store.getSnapshot() + ) + ); + + return ( + + + + + + + + ); + }; + + Generated.displayName = doc.name; + + return Generated; +} diff --git a/packages/react/src/atoms/createAtom.test.ts b/packages/react/src/atoms/createAtom.test.ts deleted file mode 100644 index 4032273bf2..0000000000 --- a/packages/react/src/atoms/createAtom.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { expect } from 'chai'; -import { z } from 'zod'; -import { createAtom, withPropMeta, getFieldMeta } from './index'; - -describe('createAtom', () => { - const DummyComponent = (props: { variant?: string; size?: string }) => { - void props; - return null; - }; - - it('returns AtomMetadata with type "atom" when schema.type is omitted', () => { - const meta = createAtom(DummyComponent, { - name: 'Dummy', - description: 'A dummy atom', - props: { - variant: z.enum(['a', 'b']).optional().default('a'), - size: z.string().optional(), - }, - }); - expect(meta.type).to.equal('atom'); - expect(meta.name).to.equal('Dummy'); - expect(meta.description).to.equal('A dummy atom'); - expect(meta.props).to.be.instanceOf(z.ZodObject); - expect(meta.component).to.equal(DummyComponent); - }); - - it('returns AtomMetadata with type "atom-child" when schema.type is "atom-child"', () => { - const meta = createAtom(DummyComponent, { - name: 'DummyChild', - description: 'A dummy child', - type: 'atom-child', - props: {}, - }); - expect(meta.type).to.equal('atom-child'); - expect(meta.name).to.equal('DummyChild'); - }); - - it('includes htmlEvents and allowedChildren when provided', () => { - const Clickable = (props: { onClick?: () => void }) => { - void props; - return null; - }; - const meta = createAtom(Clickable, { - name: 'Clickable', - description: 'Clickable', - props: {}, - htmlEvents: ['onClick'], - allowedChildren: ['text'], - }); - expect(meta.htmlEvents).to.deep.equal(['onClick']); - expect(meta.allowedChildren).to.deep.equal(['text']); - }); - - it('builds props schema from schema.props', () => { - const meta = createAtom(DummyComponent, { - name: 'WithProps', - description: 'With props', - props: { - variant: z.enum(['x', 'y']).default('x'), - }, - }); - const parsed = meta.props.safeParse({ variant: 'y' }); - expect(parsed.success).to.equal(true); - if (parsed.success) { - expect(parsed.data.variant).to.equal('y'); - } - }); - - it('accepts withPropMeta in props', () => { - const meta = createAtom(DummyComponent, { - name: 'WithMeta', - description: 'With meta', - props: { - variant: withPropMeta(z.string().optional(), { control: 'text' }), - }, - }); - expect(meta.props).to.be.instanceOf(z.ZodObject); - }); - - it('getFieldMeta returns meta from Zod schema', () => { - const schema = withPropMeta(z.string(), { control: 'color' }); - const meta = getFieldMeta(schema); - expect(meta).to.deep.equal({ control: 'color' }); - }); - - describe('component scenarios (props only, no callbacks)', () => { - it('accepts component with only required props', () => { - const OnlyProps = (props: { title: string; count: number }) => { - void props; - return null; - }; - const meta = createAtom(OnlyProps, { - name: 'OnlyProps', - description: 'Props only', - props: { - title: z.string(), - count: z.number(), - }, - }); - expect(meta.name).to.equal('OnlyProps'); - const parsed = meta.props.safeParse({ title: 'Hi', count: 1 }); - expect(parsed.success).to.equal(true); - }); - - it('accepts component with optional props only', () => { - const OptionalOnly = (props: { tag?: string }) => { - void props; - return null; - }; - const meta = createAtom(OptionalOnly, { - name: 'OptionalOnly', - description: 'Optional', - props: { tag: z.string().optional() }, - }); - expect(meta.props.safeParse({})).to.have.property('success', true); - expect(meta.props.safeParse({ tag: 'x' }).success).to.equal(true); - }); - }); - - describe('customEvents (typed to callback parameters)', () => { - it('accepts component with explicit props (no ComponentType cast)', () => { - const Test = (props: { - customEvent: (x: string, y: number) => void; - prop1: string; - prop2: number; - }) => { - void props; - return null; - }; - const meta = createAtom(Test, { - name: 'Test', - description: 'Test', - props: { - prop1: z.string(), - prop2: z.number(), - }, - customEvents: { - customEvent: [z.string(), z.number()], - }, - }); - expect(meta.name).to.equal('Test'); - expect(meta.customEvents?.customEvent).to.have.lengthOf(2); - }); - - it('accepts customEvents with tuple matching callback params (two args)', () => { - const WithSubmit = (props: { - onSubmit?: (name: string, count: number) => void; - }) => { - void props; - return null; - }; - const onSubmitSchemas = [z.string(), z.number()]; - const meta = createAtom(WithSubmit, { - name: 'WithSubmit', - description: 'With submit', - props: {}, - customEvents: { onSubmit: onSubmitSchemas }, - }); - expect(meta.customEvents?.onSubmit).to.equal(onSubmitSchemas); - }); - - it('accepts customEvents with no-arg callback (empty tuple)', () => { - const WithClick = (props: { onClick?: () => void }) => { - void props; - return null; - }; - const meta = createAtom(WithClick, { - name: 'WithClick', - description: 'With click', - props: {}, - customEvents: { - onClick: [], - }, - }); - expect(meta.customEvents).to.deep.equal({ onClick: [] }); - }); - - it('accepts customEvents with optional param (union with undefined)', () => { - const WithChange = (props: { - onChange?: (value: string, extra?: number) => void; - }) => { - void props; - return null; - }; - const meta = createAtom(WithChange, { - name: 'WithChange', - description: 'With change', - props: {}, - customEvents: { - onChange: [z.string(), z.number().optional()], - }, - }); - expect(meta.customEvents?.onChange).to.have.lengthOf(2); - }); - - it('passes through multiple customEvents', () => { - const Multi = (props: { - onA?: (x: string) => void; - onB?: (y: number) => void; - }) => { - void props; - return null; - }; - const onASchemas = [z.string()]; - const onBSchemas = [z.number()]; - const meta = createAtom(Multi, { - name: 'Multi', - description: 'Multi', - props: {}, - customEvents: { onA: onASchemas, onB: onBSchemas }, - }); - expect(meta.customEvents?.onA).to.equal(onASchemas); - expect(meta.customEvents?.onB).to.equal(onBSchemas); - }); - }); -}); diff --git a/packages/react/src/atoms/createAtom.ts b/packages/react/src/atoms/createAtom.ts deleted file mode 100644 index faf983aae7..0000000000 --- a/packages/react/src/atoms/createAtom.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** Component-first atom/atom-child definition; schema.type differentiates. */ -import { AtomType } from '@sitecore-content-sdk/content/editing'; -import { z } from 'zod'; -import type { - AtomMetadata, - AtomChild, - DefaultChild, - EditableComponentProps, - CallbackPropKeys, - CallbackArgZodTuple, -} from './types'; - -/** - * Schema input for createAtom. Prop keys are restricted to the component's props excluding - * children and ref; event keys are restricted to callback props of the component. - * @public - */ -export type AtomSchemaInput = { - /** Unique identifier used as the element type in the DSL */ - name: string; - /** Human-readable summary for the component palette */ - description: string; - /** 'atom' (default) for top-level, 'atom-child' for scoped children */ - type?: AtomType; - /** Optional version for schema evolution */ - version?: number; - /** Zod schemas for editable props (keys must be component props excluding children/ref) */ - props: { - [K in keyof EditableComponentProps]?: z.ZodType[K]>; - }; - /** DOM event handler prop names (e.g. onClick). Must be callback props. */ - htmlEvents?: CallbackPropKeys>[]; - /** Custom callback prop names to tuple of Zod types matching that callback's parameters. */ - customEvents?: { - [K in CallbackPropKeys>]?: CallbackArgZodTuple< - NonNullable[K]> - >; - }; - /** Allowed child types (atom-child metadata, 'text', or 'atom') */ - allowedChildren?: AtomChild[]; - /** Default children to insert when the atom is added */ - defaultChildren?: DefaultChild[]; -}; - -/** - * Create an atom or atom-child descriptor. The component is the first argument, the schema the - * second; schema.type selects 'atom' (default) or 'atom-child'. - * @param {C} component - The React component that renders this atom - * @param {AtomSchemaInput} schema - Name, description, type, props, events, and children rules - * @returns AtomMetadata with type taken from schema.type (default 'atom') - * @public - */ -export function createAtom(component: C, schema: AtomSchemaInput): AtomMetadata { - const atomType: AtomType = schema.type ?? 'atom'; - const propsShape = schema.props as Record; - const propsSchema = z.object(propsShape); - - const customEvents = - schema.customEvents && Object.keys(schema.customEvents).length > 0 - ? (Object.fromEntries( - Object.entries(schema.customEvents).filter(([, v]) => v !== undefined) - ) as Record) - : undefined; - - return { - name: schema.name, - version: schema.version, - type: atomType, - description: schema.description, - props: propsSchema, - component: component as (props: unknown) => React.ReactNode, - htmlEvents: schema.htmlEvents, - customEvents, - allowedChildren: schema.allowedChildren, - defaultChildren: schema.defaultChildren, - }; -} diff --git a/packages/react/src/atoms/createCallback.test.ts b/packages/react/src/atoms/createCallback.test.ts deleted file mode 100644 index 6671ccb30a..0000000000 --- a/packages/react/src/atoms/createCallback.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { expect } from 'chai'; -import { z } from 'zod'; -import { createCallback } from './createCallback'; - -describe('createCallback', () => { - it('should create a callback with name and description', () => { - const result = createCallback('onSave', { - description: 'Save handler', - params: {}, - callbackFn: () => {}, - }); - - expect(result.name).to.equal('onSave'); - expect(result.description).to.equal('Save handler'); - }); - - it('should set params to empty object when no params provided', () => { - const result = createCallback('onSave', { - description: 'Save handler', - params: {}, - callbackFn: () => {}, - }); - - expect(result.params).to.deep.equal({}); - }); - - it('should pass through params as provided', () => { - const labelType = z.string(); - const countType = z.number(); - - const result = createCallback('onSubmit', { - description: 'Submit handler', - params: { - label: { type: labelType, description: 'The label' }, - count: { type: countType, description: 'The count' }, - }, - callbackFn: ({ label, count }) => { - console.log(label, count); - }, - }); - - expect(result.params).to.have.property('label'); - expect(result.params).to.have.property('count'); - expect(result.params!.label.description).to.equal('The label'); - expect(result.params!.count.description).to.equal('The count'); - }); - - it('should preserve the callbackFn reference', () => { - const fn = () => {}; - - const result = createCallback('onCancel', { - description: 'Cancel handler', - params: {}, - callbackFn: fn, - }); - - expect(result.callbackFn).to.equal(fn); - }); - - it('should handle optional params', () => { - const result = createCallback('onUpdate', { - description: 'Update handler', - params: { - name: { type: z.string(), description: 'The name' }, - note: { type: z.string().optional(), description: 'Optional note' }, - }, - callbackFn: ({ name, note }) => { - console.log(name, note); - }, - }); - - expect(result.params).to.have.property('name'); - expect(result.params).to.have.property('note'); - }); -}); - diff --git a/packages/react/src/atoms/createCallback.ts b/packages/react/src/atoms/createCallback.ts deleted file mode 100644 index 95083dea54..0000000000 --- a/packages/react/src/atoms/createCallback.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { CallbackMetadata, CallbackParamsInput, InferCallbackArgs } from './types'; - -/** - * Schema input for createCallback. Param names are the keys of the params record, - * and impl receives a single object argument typed from those keys. - * @public - */ -export type CallbackSchemaInput

= { - /** Human-readable summary for the callback */ - description: string; - /** Record of param names to Zod types. Keys define the param names. */ - params: P; - /** Implementation of the callback. Receives a single object with keys matching params. */ - callbackFn: (args: InferCallbackArgs

) => void; -}; - -/** - * Create a callback descriptor. The params record keys define the parameter names, - * and TypeScript enforces that impl's argument object matches those keys and types. - * @param {string} name - The unique identifier for this callback - * @param {CallbackSchemaInput

} schema - The schema that defines the callback's description and params - * @returns {CallbackMetadata} CallbackMetadata with params as a CallbackParam array - * @public - */ -export function createCallback

( - name: string, - schema: CallbackSchemaInput

-): CallbackMetadata { - return { - name, - ...schema, - }; -} diff --git a/packages/react/src/atoms/define-atoms-catalog.ts b/packages/react/src/atoms/define-atoms-catalog.ts new file mode 100644 index 0000000000..92d7e71695 --- /dev/null +++ b/packages/react/src/atoms/define-atoms-catalog.ts @@ -0,0 +1,45 @@ +import { defineCatalog } from '@json-render/core'; +import { schema } from '@json-render/react'; +import type { AtomsCatalogInput, Exact } from './types'; + +/** + * Define an atoms catalog from component and action definitions. + * + * Pass component/action definitions exactly as json-render expects them. + * The returned catalog carries full type information so `defineAtomsRegistry` + * can infer props per component. + * @param {T} input - Catalog input with `components` and optionally `actions` + * @returns A typed json-render Catalog + * @example + * ```ts + * import { z } from 'zod'; + * import { defineAtomsCatalog } from '@sitecore-content-sdk/react'; + * + * const catalog = defineAtomsCatalog({ + * components: { + * Button: { + * props: z.object({ label: z.string(), variant: z.enum(['primary', 'secondary']) }), + * description: 'A clickable button', + * slots: ['default'], + * }, + * Card: { + * props: z.object({ title: z.string() }), + * description: 'A content card', + * slots: ['default'], + * }, + * }, + * actions: { + * submit: { + * params: z.object({ formId: z.string() }), + * description: 'Submit a form', + * }, + * }, + * }); + * ``` + * @public + */ +export function defineAtomsCatalog( + input: Exact +) { + return defineCatalog(schema, input); +} diff --git a/packages/react/src/atoms/define-atoms-registry.ts b/packages/react/src/atoms/define-atoms-registry.ts new file mode 100644 index 0000000000..317a38631a --- /dev/null +++ b/packages/react/src/atoms/define-atoms-registry.ts @@ -0,0 +1,34 @@ +'use client'; +import { defineRegistry } from '@json-render/react'; + +/** + * Define an atoms registry that maps catalog definitions to React implementations. + * + * Each component receives `{ props, children, emit, on, bindings, loading }` + * @param catalog - The catalog created by defineAtomsCatalog + * @param options - Component and action implementations + * @returns Registry result with component registry and action handlers + * @example + * + * ```tsx + * import { defineAtomsRegistry } from '@sitecore-content-sdk/react'; + * + * const { registry, handlers, executeAction } = defineAtomsRegistry(catalog, { + * components: { + * Button: ({ props, children, emit }) => ( + * + * ), + * Card: ({ props, children }) => ( + *

{props.title}

{children}
+ * ), + * }, + * actions: { + * submit: async (params) => { + * await fetch('/api/submit', { method: 'POST', body: JSON.stringify(params) }); + * }, + * }, + * }); + * ``` + * @public + */ +export const defineAtomsRegistry = defineRegistry; diff --git a/packages/react/src/atoms/field-schemas.test.ts b/packages/react/src/atoms/field-schemas.test.ts index 1cef6bd015..2609e4fe94 100644 --- a/packages/react/src/atoms/field-schemas.test.ts +++ b/packages/react/src/atoms/field-schemas.test.ts @@ -1,7 +1,5 @@ import { expect } from 'chai'; import { z } from 'zod'; -import { getFieldMeta } from './schema-utils'; -import { createAtom } from './createAtom'; import { textFieldSchema, richTextFieldSchema, @@ -9,15 +7,30 @@ import { linkFieldSchema, imageFieldSchema, fileFieldSchema, - type TextFieldSchema, - type RichTextFieldSchema, - type DateFieldSchema, type LinkFieldSchema, type ImageFieldSchema, type FileFieldSchema, } from './field-schemas'; describe('field-schemas', () => { + const getFieldMeta = ( + schemaOrJsonSchema: z.ZodType | Record + ): Record | undefined => { + if (typeof schemaOrJsonSchema !== 'object' || schemaOrJsonSchema === null) { + return undefined; + } + if ('_zod' in schemaOrJsonSchema) { + const obj = schemaOrJsonSchema as Record & { + meta?: () => Record; + }; + const m = typeof obj.meta === 'function' ? obj.meta() : undefined; + return m?.meta as Record | undefined; + } + return (schemaOrJsonSchema as Record).meta as + | Record + | undefined; + }; + const factories = [ { name: 'textFieldSchema', factory: textFieldSchema, control: 'Single-Line Text' }, { name: 'richTextFieldSchema', factory: richTextFieldSchema, control: 'Rich Text' }, @@ -143,89 +156,47 @@ describe('field-schemas', () => { }); }); - describe('createAtom integration', () => { - const CardComponent = (props: { - title?: TextFieldSchema; - body?: RichTextFieldSchema; - cta?: LinkFieldSchema; - image?: ImageFieldSchema; - doc?: FileFieldSchema; - publishedAt?: DateFieldSchema; - }) => { - void props; - return null; - }; - - it('accepts textFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { title: textFieldSchema() }, - }); - expect(meta.props).to.be.instanceOf(z.ZodObject); - const parsed = meta.props.safeParse({ title: { value: 'Hello' } }); + describe('defineAtomsCatalog integration', () => { + it('accepts textFieldSchema as a prop in a component definition', () => { + const props = z.object({ title: textFieldSchema() }); + const parsed = props.safeParse({ title: { value: 'Hello' } }); expect(parsed.success).to.equal(true); }); - it('accepts richTextFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { body: richTextFieldSchema() }, - }); - const parsed = meta.props.safeParse({ body: { value: '

content

' } }); + it('accepts richTextFieldSchema as a prop', () => { + const props = z.object({ body: richTextFieldSchema() }); + const parsed = props.safeParse({ body: { value: '

content

' } }); expect(parsed.success).to.equal(true); }); - it('accepts linkFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { cta: linkFieldSchema() }, - }); - const parsed = meta.props.safeParse({ cta: { value: { href: '/about', text: 'About' } } }); + it('accepts linkFieldSchema as a prop', () => { + const props = z.object({ cta: linkFieldSchema() }); + const parsed = props.safeParse({ cta: { value: { href: '/about', text: 'About' } } }); expect(parsed.success).to.equal(true); }); - it('accepts imageFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { image: imageFieldSchema() }, - }); - const parsed = meta.props.safeParse({ image: { value: { src: '/img.png', alt: 'Alt' } } }); + it('accepts imageFieldSchema as a prop', () => { + const props = z.object({ image: imageFieldSchema() }); + const parsed = props.safeParse({ image: { value: { src: '/img.png', alt: 'Alt' } } }); expect(parsed.success).to.equal(true); }); - it('accepts fileFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { doc: fileFieldSchema() }, - }); - const parsed = meta.props.safeParse({ doc: { value: { src: '/file.pdf', title: 'Doc' } } }); + it('accepts fileFieldSchema as a prop', () => { + const props = z.object({ doc: fileFieldSchema() }); + const parsed = props.safeParse({ doc: { value: { src: '/file.pdf', title: 'Doc' } } }); expect(parsed.success).to.equal(true); }); - it('accepts dateFieldSchema as a prop schema in createAtom', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { publishedAt: dateFieldSchema() }, - }); - const parsed = meta.props.safeParse({ publishedAt: { value: '20240101T000000Z' } }); + it('accepts dateFieldSchema as a prop', () => { + const props = z.object({ publishedAt: dateFieldSchema() }); + const parsed = props.safeParse({ publishedAt: { value: '20240101T000000Z' } }); expect(parsed.success).to.equal(true); }); - it('preserves control hint on field schemas inside createAtom props', () => { - const meta = createAtom(CardComponent, { - name: 'Card', - description: 'A card atom', - props: { cta: linkFieldSchema() }, - }); - const ctaShape = meta.props.shape.cta as z.ZodType; + it('preserves control hint on field schemas', () => { + const props = z.object({ cta: linkFieldSchema() }); + const ctaShape = props.shape.cta as z.ZodType; expect(getFieldMeta(ctaShape)).to.deep.equal({ control: 'Link' }); }); }); }); - diff --git a/packages/react/src/atoms/field-schemas.ts b/packages/react/src/atoms/field-schemas.ts index 8328536727..d96256ed6c 100644 --- a/packages/react/src/atoms/field-schemas.ts +++ b/packages/react/src/atoms/field-schemas.ts @@ -1,4 +1,4 @@ -/** Zod schemas for Sitecore field types, for use in createAtom prop definitions. */ +/** Zod schemas for Sitecore field types, for use in defineAtomsCatalog prop definitions. */ import { z } from 'zod'; import { withPropMeta } from './schema-utils'; @@ -129,49 +129,36 @@ export const fileFieldSchema = (extra?: z.ZodRawShape) => /** * Inferred type for a Sitecore Single-Line Text / Multi-Line Text field prop. * Use this to type component props that accept a text field. - * @example - * const MyComponent = (props: { title: TextFieldSchema }) => ... * @public */ export type TextFieldSchema = z.infer>; /** * Inferred type for a Sitecore Rich Text field prop. - * @example - * const MyComponent = (props: { body: RichTextFieldSchema }) => ... * @public */ export type RichTextFieldSchema = z.infer>; /** * Inferred type for a Sitecore Date field prop. - * @example - * const MyComponent = (props: { publishedAt: DateFieldSchema }) => ... * @public */ export type DateFieldSchema = z.infer>; /** * Inferred type for a Sitecore General Link field prop. - * @example - * const MyComponent = (props: { cta: LinkFieldSchema }) => ... * @public */ export type LinkFieldSchema = z.infer>; /** * Inferred type for a Sitecore Image field prop. - * @example - * const MyComponent = (props: { image: ImageFieldSchema }) => ... * @public */ export type ImageFieldSchema = z.infer>; /** * Inferred type for a Sitecore File field prop. - * @example - * const MyComponent = (props: { doc: FileFieldSchema }) => ... * @public */ export type FileFieldSchema = z.infer>; - diff --git a/packages/react/src/atoms/index.ts b/packages/react/src/atoms/index.ts index 60d5714326..ea7c94e69a 100644 --- a/packages/react/src/atoms/index.ts +++ b/packages/react/src/atoms/index.ts @@ -1,21 +1,17 @@ -/** Atom schema utilities. */ export type { - AtomMetadata, - AtomChild, - DefaultChild, - EditableComponentProps, - CallbackPropKeys, - CallbackArgZodTuple, - PropMeta, - ArgMeta, - CallbackMetadata, - CallbackParamInput, - CallbackParamsInput, - InferCallbackArgs, + AtomComponentDefinition, + AtomActionDefinition, + AtomsCatalogInput, + AtomsComponentsMap, + AtomActionHandler, + AtomsActionsMap, + AtomsConfig, + Exact, } from './types'; -export { withPropMeta, withArgMeta, getFieldMeta } from './schema-utils'; -export { createAtom, type AtomSchemaInput } from './createAtom'; -export { createCallback, type CallbackSchemaInput } from './createCallback'; +export { defineAtomsCatalog } from './define-atoms-catalog'; +export { defineAtomsRegistry } from './define-atoms-registry'; +export { serializeCatalog } from './catalog-serializer'; +export { withPropMeta, type PropMeta } from './schema-utils'; export { textFieldSchema, richTextFieldSchema, @@ -30,3 +26,5 @@ export { type ImageFieldSchema, type FileFieldSchema, } from './field-schemas'; +export { createNCC } from './create-ncc'; +export * from './re-exports'; diff --git a/packages/react/src/atoms/re-exports.ts b/packages/react/src/atoms/re-exports.ts new file mode 100644 index 0000000000..245f63ee90 --- /dev/null +++ b/packages/react/src/atoms/re-exports.ts @@ -0,0 +1,23 @@ +import { useBoundProp as useBoundPropInternal } from '@json-render/react'; + +/** + * Hook for two-way bound props. Returns `[value, setValue]` where: + * + * - `value` is the already-resolved prop value (passed through from render props) + * - `setValue` writes back to the bound state path (no-op if not bound) + * + * Designed to work with the `bindings` map that the renderer provides when + * a prop uses `{ $bindState: "/path" }` or `{ $bindItem: "field" }`. + * @example + * ```tsx + * import { useBoundProp } from '@sitecore-content-sdk/react'; + * + * const Input: ComponentRenderer = ({ props, bindings }) => { + * const [value, setValue] = useBoundProp(props.value, bindings?.value); + * return setValue(e.target.value)} />; + * }; + * ``` + * @public + */ +export const useBoundProp = useBoundPropInternal; + diff --git a/packages/react/src/atoms/schema-utils.ts b/packages/react/src/atoms/schema-utils.ts index 6b4f770779..aab73dc4c2 100644 --- a/packages/react/src/atoms/schema-utils.ts +++ b/packages/react/src/atoms/schema-utils.ts @@ -1,6 +1,11 @@ /** Schema metadata for atom props/events. */ import { z } from 'zod'; -import type { PropMeta, ArgMeta } from './types'; + +/** + * Prop metadata (e.g. control hint for Design Studio). + * @public + */ +export type PropMeta = { control?: string }; const META_KEY = 'meta'; @@ -19,44 +24,3 @@ export function withPropMeta(schema: T, meta: PropMeta): T } return schema; } - -/** - * Attach display metadata to a custom event argument (e.g. argName for DS). Stored under a key - * that survives JSON Schema conversion. - * @param {import('zod').ZodType} schema - Zod type for the argument - * @param {ArgMeta} meta - Argument metadata - * @returns The same Zod type with meta attached (or schema unchanged if .meta is not callable) - * @public - */ -export function withArgMeta(schema: T, meta: ArgMeta): T { - const s = schema as unknown as { meta?: (m: Record) => T }; - if (typeof s.meta === 'function') { - return s.meta({ [META_KEY]: meta }); - } - return schema; -} - -/** - * Get field metadata from a Zod type or a plain JSON Schema object. Uses _zod to detect Zod - * schemas; otherwise reads the meta key from the object. For internal use by the renderer / DS. - * @param {import('zod').ZodType | Record} schemaOrJsonSchema - Live Zod type or plain JSON Schema object - * @returns The meta object or undefined - * @internal - */ -export function getFieldMeta( - schemaOrJsonSchema: z.ZodType | Record -): Record | undefined { - if (typeof schemaOrJsonSchema !== 'object' || schemaOrJsonSchema === null) { - return undefined; - } - if ('_zod' in schemaOrJsonSchema) { - const obj = schemaOrJsonSchema as Record & { - meta?: () => Record; - }; - const m = typeof obj.meta === 'function' ? obj.meta() : undefined; - return m?.[META_KEY] as Record | undefined; - } - return (schemaOrJsonSchema as Record)[META_KEY] as - | Record - | undefined; -} diff --git a/packages/react/src/atoms/types.ts b/packages/react/src/atoms/types.ts index 02bdfde6cd..c8e4dfa46c 100644 --- a/packages/react/src/atoms/types.ts +++ b/packages/react/src/atoms/types.ts @@ -1,110 +1,76 @@ -/** Atom schema types. @public */ -import type { AtomType } from '@sitecore-content-sdk/content/editing'; -import type { z } from 'zod'; -import type { ComponentType } from 'react'; +import type { Catalog, InferCatalogInput } from '@json-render/core'; +import type { ComponentRenderer, DefineRegistryResult, ReactSchema } from '@json-render/react'; +import { SitecoreComponentMeta } from '@sitecore-content-sdk/content/atoms'; + +type BaseCatalog = InferCatalogInput; +type BaseComponent = BaseCatalog['components'][string]; +type BaseAction = BaseCatalog['actions'][string]; /** - * Metadata for callback - * @public + * Utility type that prevents extra keys beyond those defined in `Base`. + * @internal */ -export type CallbackMetadata = { - name: string; - description: string; - params?: CallbackParamsInput; - callbackFn: (...args: any[]) => void; -}; - -/** Metadata for an atom or atom-child; type differentiates. @public */ -export type AtomMetadata = { - name: string; - version?: number; - type: AtomType; - description: string; - props: z.ZodObject; - component: (props: unknown) => React.ReactNode; - htmlEvents?: string[]; - customEvents?: Record; - allowedChildren?: AtomChild[]; - defaultChildren?: DefaultChild[]; -}; - -/** Allowed child: atom-child metadata, 'text', or 'atom'. @public */ -export type AtomChild = AtomMetadata | 'text' | 'atom'; - -/** Default child: metadata or { atom, props? }. @public */ -export type DefaultChild = AtomMetadata | { atom: AtomMetadata; props?: Record }; +export type Exact = T & Record, never>; /** - * Extracts props from a component type without requiring ComponentType, so function - * components with specific props (e.g. (props: { x: string }) => JSX.Element) are accepted. + * Component definition in the atoms catalog input. + * @public */ -type PropsOfComponent = C extends (props: infer P) => unknown - ? P - : C extends ComponentType - ? C extends ComponentType - ? P - : never - : never; - -/** Component props excluding children and ref. @public */ -export type EditableComponentProps = Omit, 'children' | 'ref'>; - -/** Keys of T that are callback (function) props. @public */ -export type CallbackPropKeys = { - [K in keyof T & string]: NonNullable extends (...args: any[]) => unknown ? K : never; -}[keyof T & string]; +export type AtomComponentDefinition = BaseComponent & SitecoreComponentMeta; /** - * Tuple of Zod types matching a function's parameter list. Used to type customEvents so each - * callback's schemas match its parameters. Keys are strict; tuple length/element strictness - * is partial (optional params y?: number infer as undefined and need z.number().optional()). + * Action definition in the atoms catalog input. * @public */ -export type CallbackArgZodTuple = F extends (...args: infer A) => unknown - ? { [I in keyof A]: z.ZodType } - : never; - -/** Prop metadata (e.g. control hint for DS). @public */ -export type PropMeta = { control?: string }; - -/** Event argument metadata (e.g. argName). @public */ -export type ArgMeta = { argName: string }; +export type AtomActionDefinition = BaseAction; /** - * Input shape for a single param entry in createCallback. - * @public + * Input shape for defineAtomsCatalog. + * Extends json-render's base catalog input with Sitecore-specific fields. + * @public */ -export type CallbackParamInput = { - /** Zod schema for the parameter type */ - type: z.ZodType; - /** Human-readable description of the parameter */ - description: string; +export type AtomsCatalogInput = BaseCatalog & { + /** Semver version of the catalog as a whole. Used by the lock file and Design Studio. */ + version?: string; + /** Component definitions keyed by name. */ + components: Record; + /** Action definitions keyed by name (required). */ + actions: Record; }; /** - * Record of param names to their schema and description. Keys become the param names. + * Type alias for the component renderer. + * @public + */ +export type AtomsComponentRenderer = ComponentRenderer; + +/** + * Component implementations map for defineAtomsRegistry. * @public */ -export type CallbackParamsInput = Record; +export type AtomsComponentsMap = Record; /** - * Keys from P whose Zod type includes undefined (i.e. optional). + * Action handler function. * @public */ -type OptionalParamKeys

= { - [K in keyof P]: undefined extends z.infer ? K : never; -}[keyof P]; +export type AtomActionHandler = (params: Record) => Promise | void; -/** Keys from P whose Zod type does NOT include undefined (i.e. required). @internal */ -type RequiredParamKeys

= Exclude>; +/** + * Action implementations map for defineAtomsRegistry. + * @public + */ +export type AtomsActionsMap = Record; /** - * Infers the impl function's argument object type from a CallbackParamsInput record. - * Required params become required properties, optional Zod types become optional properties. + * Props the developer passes to the provider for atoms support. * @public */ -export type InferCallbackArgs

= { - [K in RequiredParamKeys

]: z.infer; -} & { - [K in OptionalParamKeys

]?: z.infer; -}; +export interface AtomsConfig { + /** The json-render catalog (schema + component/action definitions). */ + catalog: Catalog; + /** The registry result returned by defineAtomsRegistry. */ + registry: DefineRegistryResult; + /** Optional navigate function to be passed to action handlers for navigation purposes. */ + navigate?: (path: string) => void; +} diff --git a/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx b/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx index e685c2d5d0..df8ecc990c 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibrary.test.tsx @@ -1,951 +1,129 @@ /* eslint-disable jsdoc/require-jsdoc */ /* eslint-disable no-unused-expressions */ -/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ import React from 'react'; import sinon from 'sinon'; -import { expect } from 'chai'; -import { Page, PageMode } from '@sitecore-content-sdk/content/client'; -import { - LayoutServiceData, - EDITING_COMPONENT_PLACEHOLDER, -} from '@sitecore-content-sdk/content/layout'; -import { act, fireEvent, render, waitFor } from '@testing-library/react'; -import { DesignLibrary } from './DesignLibrary'; -import { getTestLayoutData } from '../../test-data/component-editing-data'; -import { SitecoreProvider } from '../SitecoreProvider'; -import { RichText } from '../RichText'; -import { Text } from '../Text'; -import { Placeholder } from '../Placeholder'; +import { expect, use as chaiUse } from 'chai'; +import sinonChai from 'sinon-chai'; +chaiUse(sinonChai); +import { render, waitFor } from '@testing-library/react'; +import { DesignLibrary, __mockDependencies } from './DesignLibrary'; +import { SitecoreProvider } from '../SitecoreProvider'; import { DesignLibraryStatus, getDesignLibraryStatusEvent, - DesignLibraryMode, - getDesignLibraryAtomsRegistryEvent, - AtomInfo, } from '@sitecore-content-sdk/content/editing'; -import { __mockDependencies } from './DesignLibrary'; -import { - DesignLibraryPreviewError, - getDesignLibraryErrorEvent, -} from '@sitecore-content-sdk/content/codegen'; -import { z } from 'zod'; -import * as atomRegistryUtils from '../../atoms/atom-registry-utils'; -import * as callbackRegistryUtils from '../../atoms/callback-registry-utils'; -import { AtomMetadata, CallbackMetadata } from '../../atoms/types'; -import * as rscUtils from '#rsc-env'; - -before(() => { - if (typeof window !== 'undefined' && !window.requestAnimationFrame) { - (window as any).requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(cb, 0); - } -}); +import type { AtomsConfig } from '../../atoms/types'; +import type { ImportMapImport } from './models'; +import type { DefineRegistryResult } from '@json-render/react'; describe('', () => { const sandbox = sinon.createSandbox(); - before(() => { - sandbox.replace(rscUtils, 'rsc', false as any); - }); - const postMessageSpy = sandbox.spy(window, 'postMessage'); - const components = new Map(); - const api = { - edge: { - contextId: 'test-context-id', - clientContextId: 'test-client-context-id', - edgeUrl: 'https://test-edge-url.com', - }, - local: { apiKey: 'test-api-key', apiHost: 'https://test-api-host.com', path: '/test-path' }, - }; + const apiStub = {} as any; + const emptyComponentMap = new Map(); + const loadImportMapStub = async (): Promise => + ({ + default: [], + } as unknown as ImportMapImport); - // Modes - const defaultDesignLibraryPageMode: PageMode = { - name: DesignLibraryMode.Normal, - isDesignLibrary: true, - designLibrary: { isVariantGeneration: false, isLowCode: false }, - isNormal: false, - isPreview: false, - isEditing: false, - }; + let postToDesignLibrarySpy: sinon.SinonStub; - const modeLibraryMetadata: PageMode = { - name: DesignLibraryMode.Metadata, - isDesignLibrary: true, - designLibrary: { isVariantGeneration: false, isLowCode: false }, - isNormal: false, - isPreview: false, - isEditing: true, + const mockRegistry: DefineRegistryResult = { + registry: {} as any, + handlers: () => ({}), + executeAction: async () => {}, }; - const modeLibrary_Gen: PageMode = { - name: DesignLibraryMode.Normal, - isDesignLibrary: true, - designLibrary: { isVariantGeneration: true, isLowCode: false }, - isNormal: false, - isPreview: false, - isEditing: false, - }; + const mockCatalog = { + componentNames: ['Button'], + actionNames: [], + data: { components: { Button: { description: 'A button', slots: ['default'] } } }, + jsonSchema: () => ({}), + } as any; - const modeLibraryMetadata_Gen: PageMode = { - name: DesignLibraryMode.Metadata, - isDesignLibrary: true, - designLibrary: { isVariantGeneration: true, isLowCode: false }, - isNormal: false, - isPreview: false, - isEditing: true, + const atomsConfig: AtomsConfig = { + catalog: mockCatalog, + registry: mockRegistry, }; - const getPage = ( - layout?: LayoutServiceData, - pageMode: PageMode = defaultDesignLibraryPageMode - ): Page => ({ + const getPage = (overrides: Record = {}) => ({ locale: 'en', - layout: layout || { sitecore: { context: {}, route: null } }, - mode: pageMode, - }); - - const ContentBlock: React.FC<{ - [prop: string]: unknown; - fields?: { content: { value: string }; heading: { value: string } }; - }> = (props) => ( -

- - -
- ); - - const InnerBlock: React.FC<{ [prop: string]: unknown; fields?: { text: { value: string } } }> = ( - props - ) => ( -
- -
- ); - - components.set('ContentBlock', ContentBlock); - components.set('InnerBlock', InnerBlock); - - async function sendUpdate(details: { uid: string; fields?: any; params?: any }) { - const ev = document.createEvent('Event'); - ev.initEvent('message', false, true); - (ev as any).origin = window.location.origin; - (ev as any).data = { name: 'component:update', details }; - await fireEvent(window, ev); - } - - const defaultImportMap = () => - Promise.resolve({ - default: [{ module: 'react', exports: [{ name: 'default', value: React }] }], - }); - - const mockedAtoms: AtomMetadata[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button component', - component: () => React.createElement('button', null, 'Button'), - props: z.object({ - label: z.string(), - }), - }, - { - name: 'Text', - type: 'atom', - description: 'A text component', - component: () => React.createElement('div', null, 'Text'), - props: z.object({ - content: z.string(), - }), - }, - ]; - - const mockedCallbacks: CallbackMetadata[] = [ - { - name: 'trackSelection', - description: 'Track selection callback', - callbackFn: () => {}, - }, - ]; - - const mockedSerializedAtoms: AtomInfo[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button component', - props: { label: { type: 'string' } }, - allowedChildren: [], - }, - { - name: 'Text', - type: 'atom', - description: 'A text component', - props: { content: { type: 'string' } }, - allowedChildren: [], - }, - ]; - - const mockedSerializedCallbacks: Record = { - trackSelection: { description: 'Track selection callback' }, - }; - - const unsubscribeSpy = sandbox.spy(); - let addComponentPreviewHandlerSpy: sinon.SinonStub; - let postToDesignLibrarySpy: sinon.SinonStub; - let sendErrorEventSpy: sinon.SinonStub; - let callbackEvent: any = null; - let serializeAtomsStub: sinon.SinonStub; - let serializeCallbacksStub: sinon.SinonStub; - - const RENDER_ID = 'test-content'; - const PLACEHOLDER_GUID = '00000000-0000-0000-0000-000000000000'; - - const joinHtml = (parts: string[]) => parts.join(''); - const expectContains = (html: string, parts: string[]) => - expect(html).to.contain(joinHtml(parts)); - - const expectedInitialMarkup = (guid = PLACEHOLDER_GUID, id = RENDER_ID) => - joinHtml([ - '
', - ``, - ``, - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - '', - '', - '
', - '', - '', - '
', - ]); - - const postedEventsJson = (spy: sinon.SinonSpy) => - spy.getCalls().map((c) => JSON.stringify(c.args[0])); - - const expectStatus = ( - spy: sinon.SinonSpy, - status: DesignLibraryStatus, - id: string, - opts: { strict?: boolean } = {} - ) => { - const target = JSON.stringify(getDesignLibraryStatusEvent(status, id)); - const events = postedEventsJson(spy); - if (opts.strict) { - expect(events).to.include(target); - } else { - expect(events.some((e) => e.includes(target))).to.be.true; - } - }; - - beforeEach(() => { - postMessageSpy.resetHistory(); - unsubscribeSpy.resetHistory(); - postToDesignLibrarySpy = sandbox.stub().callsFake((evt) => { - // postToDesignLibrary calls window.postMessage internally - window.postMessage(evt, '*'); - }); - sendErrorEventSpy = sandbox.stub().callsFake((uid, error, type) => { - // sendErrorEvent calls window.postMessage internally - const errorEvent = getDesignLibraryErrorEvent(uid, error, type); - window.postMessage(errorEvent, '*'); - }); - __mockDependencies({ - postToDesignLibrary: postToDesignLibrarySpy, - sendErrorEvent: sendErrorEventSpy, - }); - - if (typeof (globalThis as any).requestAnimationFrame === 'undefined') { - (globalThis as any).requestAnimationFrame = (cb: Function) => setTimeout(cb, 0); - (globalThis as any).cancelAnimationFrame = (id: any) => clearTimeout(id); - } - if (typeof window !== 'undefined') { - (window as any).requestAnimationFrame = (globalThis as any).requestAnimationFrame; - (window as any).cancelAnimationFrame = (globalThis as any).cancelAnimationFrame; - } - }); - - it('should render null if not in design library mode', () => { - const page = getPage(getTestLayoutData().layoutData, { - name: DesignLibraryMode.Normal, - isDesignLibrary: false, - designLibrary: { - isVariantGeneration: false, - isLowCode: false, + layout: { + sitecore: { + context: {}, + route: { + uid: 'test-uid', + placeholders: { + 'editing-componentmode-placeholder': [ + { + uid: 'component-1', + componentName: 'TestComponent', + fields: {}, + params: {}, + }, + ], + }, + }, }, - isNormal: false, - isPreview: false, - isEditing: false, - }); - - const rendered = render( - - - , - { container: document.body } - ); - expect(rendered.baseElement.innerHTML).to.equal(''); - }); - - describe('mode=library and isVariantGeneration=false', () => { - let page: Page; - - const modeLibrary: PageMode = { - name: DesignLibraryMode.Normal, + }, + mode: { + name: 'normal', isDesignLibrary: true, - designLibrary: { isVariantGeneration: false, isLowCode: false }, + designLibrary: { isVariantGeneration: false }, isNormal: false, isPreview: false, isEditing: false, - }; - - async function sendUpdateEvent(details: { uid: string; fields?: any; params?: any }) { - const ev = document.createEvent('Event'); - ev.initEvent('message', false, true); - (ev as any).origin = window.location.origin; - (ev as any).data = { name: 'component:update', details }; - await fireEvent(window, ev); - } - - beforeEach(() => { - const basic = getTestLayoutData(); - page = { - locale: 'en', - layout: basic.layoutData, - mode: modeLibrary, - }; - postMessageSpy.resetHistory(); - }); - - it('renders real component and sends READY + initial RENDERED', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary); - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - ].join('') - ); - - expect( - postMessageSpy - .getCalls() - .some( - (c) => - JSON.stringify(c.args[0]) === - JSON.stringify(getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'test-content')) - ) - ).to.be.true; - - await waitFor(() => { - expect( - postMessageSpy - .getCalls() - .some((c) => - JSON.stringify(c.args[0]).includes( - JSON.stringify( - getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'test-content') - ) - ) - ) - ).to.be.true; - }); - }); - - it('should render component with placeholders', () => { - page.layout = getTestLayoutData(true).layoutData; - - const rendered = render( - - - , - { container: document.body } - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - '
', - 'Its an inner component', - '
', - '
', - ].join('') - ); - }); - - it('should update root component', async () => { - const rendered = render( - - - , - { container: document.body } - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - ].join('') - ); - - await sendUpdateEvent({ - uid: 'test-content', - fields: { content: { value: 'new content!' } }, - }); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - 'new content!', - '
', - ].join('') - ); - }); - - it('should update nested component', async () => { - const withPlaceholder = getTestLayoutData(true); - page.layout = withPlaceholder.layoutData; - - const rendered = render( - - - , - { container: document.body } - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - '
', - 'Its an inner component', - '
', - '
', - ].join('') - ); - - await sendUpdateEvent({ - uid: 'test-inner', - fields: { text: { value: 'new inner content!' } }, - }); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - '
', - 'new inner content!', - '
', - '
', - ].join('') - ); - }); + ...overrides, + }, }); - describe('mode=library-metadata and isVariantGeneration=false', () => { - it('renders real component and sends READY + initial RENDERED (Pages Router)', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata); - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain(expectedInitialMarkup()); - - expectStatus(postMessageSpy, DesignLibraryStatus.READY, RENDER_ID, { strict: true }); - - await waitFor(() => expectStatus(postMessageSpy, DesignLibraryStatus.RENDERED, RENDER_ID)); + beforeEach(() => { + postToDesignLibrarySpy = sandbox.stub(); + __mockDependencies({ + postToDesignLibrary: postToDesignLibrarySpy, + addComponentPreviewHandler: sandbox.stub(), + sendErrorEvent: sandbox.stub(), }); }); - describe('mode=library&generation=variant and isVariantGeneration=true', () => { - beforeEach(() => { - addComponentPreviewHandlerSpy = sandbox.stub().callsFake((_importMap, cb) => { - callbackEvent = cb; - return unsubscribeSpy; - }); - __mockDependencies({ - addComponentPreviewHandler: addComponentPreviewHandlerSpy, - postToDesignLibrary: postToDesignLibrarySpy, - sendErrorEvent: sendErrorEventSpy, - }); - - postMessageSpy.resetHistory(); - - serializeAtomsStub = sandbox - .stub(atomRegistryUtils, 'serializeAtoms') - .callsFake((atoms) => (!atoms?.length ? [] : mockedSerializedAtoms)); - serializeCallbacksStub = sandbox - .stub(callbackRegistryUtils, 'serializeCallbacks') - .callsFake((callbacks) => (!callbacks?.length ? {} : mockedSerializedCallbacks)); - }); - - afterEach(() => { - serializeAtomsStub.restore(); - serializeCallbacksStub.restore(); - }); - - it('fires component:ready on mount', () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - render( - - - - ); - - const expectedReady = getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'test-content'); - expect( - postMessageSpy - .getCalls() - .some((c) => JSON.stringify(c.args[0]) === JSON.stringify(expectedReady)) - ).to.be.true; - - const readyCount = postMessageSpy - .getCalls() - .filter( - (c) => - c.args[0]?.name === expectedReady.name && - c.args[0]?.message?.uid === expectedReady.message.uid - ).length; - expect(readyCount).to.equal(1); - }); - - it('fires component:rendered only after generated component is received', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - render( - - - - ); - - const expectedRendered = getDesignLibraryStatusEvent( - DesignLibraryStatus.RENDERED, - 'test-content' - ); - expect( - postMessageSpy - .getCalls() - .some((c) => JSON.stringify(c.args[0]) === JSON.stringify(expectedRendered)) - ).to.be.false; - - await waitFor(() => { - expect(addComponentPreviewHandlerSpy).to.have.been.called; - }); - - const TestComponent = () =>
Generated!
; - callbackEvent(null, TestComponent); - - await waitFor(() => { - expect( - postMessageSpy - .getCalls() - .some((c) => JSON.stringify(c.args[0]) === JSON.stringify(expectedRendered)) - ).to.be.true; - }); - }); - - it('renders real component first, wires generation, then switches to generated component', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - ].join('') - ); - - await waitFor(() => { - expect(addComponentPreviewHandlerSpy).to.have.been.called; - }); - - const TestComponent = () =>
Generated!
; - callbackEvent(null, TestComponent); - - await waitFor(() => { - expect(rendered.baseElement.innerHTML).to.contain('
Generated!
'); - }); - }); - - it('renders real component first, wires generation, then switches to generated component when loadImportMap provided via SitecoreProvider', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain( - [ - '
', - '
', - '

This is a live set of examples of how to use Content SDK

\n', - '
', - ].join('') - ); - - await waitFor(() => { - expect(addComponentPreviewHandlerSpy).to.have.been.called; - }); - - const TestComponent = () =>
Generated!
; - callbackEvent(null, TestComponent); - - await waitFor(() => { - expect(rendered.baseElement.innerHTML).to.contain('
Generated!
'); - }); - }); - - it('updates via component:update after switch', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - const Gen = (props: any) =>
{props.fields?.content?.value}
; - - render( - - - - ); - - await waitFor(() => { - expect(addComponentPreviewHandlerSpy).to.have.been.called; - callbackEvent(null, Gen); - }); - - await sendUpdate({ - uid: 'test-content', - fields: { content: { value: 'updated!' } }, - }); - - await waitFor(() => { - expect( - postMessageSpy - .getCalls() - .some((c) => - JSON.stringify(c.args[0]).includes( - JSON.stringify( - getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'test-content') - ) - ) - ) - ).to.be.true; - }); - }); - - it('sends error event when no import map is provided', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - - render( - - - - ); - - await waitFor(() => { - expect( - postMessageSpy - .getCalls() - .some((c) => - JSON.stringify(c.args[0]).includes( - JSON.stringify( - getDesignLibraryErrorEvent( - 'test-content', - 'No loadImportMap provided', - DesignLibraryPreviewError.ImportMapMissing - ) - ) - ) - ) - ).to.be.true; - }); - }); - - it('posts empty atoms registry event when atomRegistry is not provided on SitecoreProvider', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent([], {}); - - render( - - - - ); - - await waitFor(() => { - expect( - postMessageSpy - .getCalls() - .some((c) => JSON.stringify(c.args[0]) === JSON.stringify(expectedRegistryEvent)) - ).to.be.true; - }); - - sinon.assert.calledWith(serializeAtomsStub, []); - sinon.assert.calledWith(serializeCallbacksStub, []); - }); - - it('posts atoms registry event with serialized atoms and callbacks when atomRegistry is provided', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibrary_Gen); - const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent( - mockedSerializedAtoms, - mockedSerializedCallbacks - ); - - render( - - - - ); - - await waitFor(() => { - const postedRegistryEvent = postMessageSpy - .getCalls() - .map((call) => call.args[0]) - .find((event) => event?.name === 'atom:registry'); - - expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); - }); - - sinon.assert.calledWith(serializeAtomsStub, mockedAtoms); - sinon.assert.calledWith(serializeCallbacksStub, mockedCallbacks); - }); + afterEach(() => { + sandbox.restore(); }); - describe('?mode=library-metadata&generation=variant and isVariantGeneration=true', () => { - beforeEach(() => { - addComponentPreviewHandlerSpy = sandbox.stub().callsFake((_importMap, cb) => { - callbackEvent = cb; - return unsubscribeSpy; - }); - __mockDependencies({ - addComponentPreviewHandler: addComponentPreviewHandlerSpy, - postToDesignLibrary: postToDesignLibrarySpy, - sendErrorEvent: sendErrorEventSpy, - }); - - postMessageSpy.resetHistory(); - - serializeAtomsStub = sandbox - .stub(atomRegistryUtils, 'serializeAtoms') - .callsFake((atoms) => (!atoms?.length ? [] : mockedSerializedAtoms)); - serializeCallbacksStub = sandbox - .stub(callbackRegistryUtils, 'serializeCallbacks') - .callsFake((callbacks) => (!callbacks?.length ? {} : mockedSerializedCallbacks)); - }); - - afterEach(() => { - serializeAtomsStub.restore(); - serializeCallbacksStub.restore(); - }); - - const expectedGeneratedParts = [ - '', - '
Gen-Metadata
', - '
', - ]; - - const triggerGeneration = async () => { - await act(async () => { - callbackEvent(null, () => ( - -
Gen-Metadata
-
- )); - }); - }; - - it('renders real component first, wires generation, then switches to generated component (Pages Router)', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata_Gen); - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain(expectedInitialMarkup()); - - await waitFor(() => expect(addComponentPreviewHandlerSpy).to.have.been.called); - - expectStatus(postMessageSpy, DesignLibraryStatus.READY, RENDER_ID, { strict: true }); - - await triggerGeneration(); - - await waitFor(() => expectContains(rendered.baseElement.innerHTML, expectedGeneratedParts)); - - await waitFor(() => expectStatus(postMessageSpy, DesignLibraryStatus.RENDERED, RENDER_ID)); - }); - - it('posts empty atoms registry event when atomRegistry is not provided on SitecoreProvider', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata_Gen); - const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent([], {}); - - render( - - - - ); - - await waitFor(() => { - const postedRegistryEvent = postMessageSpy - .getCalls() - .map((call) => call.args[0]) - .find((event) => event?.name === 'atom:registry'); - - expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); - }); - - sinon.assert.calledWith(serializeAtomsStub, []); - sinon.assert.calledWith(serializeCallbacksStub, []); - }); - - it('posts atoms registry event with serialized atoms and callbacks when atomRegistry is provided', async () => { - const page = getPage(getTestLayoutData().layoutData, modeLibraryMetadata_Gen); - const expectedRegistryEvent = getDesignLibraryAtomsRegistryEvent( - mockedSerializedAtoms, - mockedSerializedCallbacks - ); - - render( - - - - ); - - await waitFor(() => { - const postedRegistryEvent = postMessageSpy - .getCalls() - .map((call) => call.args[0]) - .find((event) => event?.name === 'atom:registry'); - - expect(postedRegistryEvent).to.deep.equal(expectedRegistryEvent); - }); - - sinon.assert.calledWith(serializeAtomsStub, mockedAtoms); - sinon.assert.calledWith(serializeCallbacksStub, mockedCallbacks); - }); + it('renders null when not in design library mode', () => { + const page = getPage({ isDesignLibrary: false }); + const { container } = render( + + + + ); + expect(container.innerHTML).to.equal(''); }); - describe('error handling', () => { - it('should render ErrorComponent when rendering UID is missing', () => { - const layoutData = getTestLayoutData().layoutData; - - // Remove UID from the component - const renderingWithoutUid = { - ...layoutData.sitecore.route, - placeholders: { - [EDITING_COMPONENT_PLACEHOLDER]: [ - { - ...layoutData.sitecore.route.placeholders?.[EDITING_COMPONENT_PLACEHOLDER]?.[0], - uid: undefined, - }, - ], - }, - }; - - const page = { - locale: 'en', - layout: { ...layoutData, sitecore: { ...layoutData.sitecore, route: renderingWithoutUid } }, - mode: modeLibraryMetadata, - }; - - const rendered = render( - - - - ); - - expect(rendered.baseElement.innerHTML).to.contain( - 'Rendering UID is missing in the rendering data' + it('posts READY status on mount when in design library mode', async () => { + const page = getPage(); + render( + + + + ); + await waitFor(() => { + expect(postToDesignLibrarySpy).to.have.been.calledWith( + getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'component-1') ); }); }); - after(() => { - sandbox.restore(); - }); }); diff --git a/packages/react/src/components/DesignLibrary/DesignLibrary.tsx b/packages/react/src/components/DesignLibrary/DesignLibrary.tsx index 9f488ab2c3..122c48ae78 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibrary.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibrary.tsx @@ -10,7 +10,6 @@ import { DesignLibraryStatus, getDesignLibraryStatusEvent, addComponentUpdateHandler, - getDesignLibraryAtomsRegistryEvent, } from '@sitecore-content-sdk/content/editing'; import * as codegen from '@sitecore-content-sdk/content/codegen'; import * as editing from '@sitecore-content-sdk/content/editing'; @@ -19,8 +18,8 @@ import { Placeholder, PlaceholderMetadata } from '../Placeholder'; import { DesignLibraryErrorBoundary } from './DesignLibraryErrorBoundary'; import { DynamicComponent } from './models'; import { ErrorComponent } from '../ErrorBoundary'; -import { serializeAtoms } from '../../atoms/atom-registry-utils'; -import { serializeCallbacks } from '../../atoms/callback-registry-utils'; +import { serializeCatalog } from '../../atoms'; +import { getDesignLibraryAtomsCatalogEvent } from '@sitecore-content-sdk/content/atoms'; let { getDesignLibraryImportMapEvent, @@ -51,7 +50,7 @@ export const __mockDependencies = (mocks: any) => { * @public */ export const DesignLibrary = () => { - const { page, loadImportMap, atomRegistry } = useSitecore(); + const { page, loadImportMap, atomsConfig } = useSitecore(); const route = page.layout.sitecore.route; const rendering = route?.placeholders[EDITING_COMPONENT_PLACEHOLDER]?.[0]; const uid = rendering?.uid; @@ -142,11 +141,10 @@ export const DesignLibrary = () => { const importMapEvent = getDesignLibraryImportMapEvent(uid, importMap); postToDesignLibrary(importMapEvent); - const serializedAtoms = serializeAtoms(atomRegistry?.atoms ?? []); - const serializedCallbacks = serializeCallbacks(atomRegistry?.callbacks ?? []); - - const atomRegistryEvent = getDesignLibraryAtomsRegistryEvent(serializedAtoms, serializedCallbacks); - postToDesignLibrary(atomRegistryEvent); + if (atomsConfig?.catalog) { + const catalogPayload = serializeCatalog(atomsConfig.catalog); + postToDesignLibrary(getDesignLibraryAtomsCatalogEvent(catalogPayload)); + } const propsEvent = getDesignLibraryComponentPropsEvent( uid, @@ -161,7 +159,7 @@ export const DesignLibrary = () => { cancelled = true; unsubscribe && unsubscribe(); }; - }, [isDesignLibrary, isVariantGeneration, uid, loadImportMap, propsState, atomRegistry]); + }, [isDesignLibrary, isVariantGeneration, uid, loadImportMap, propsState, atomsConfig]); return (
diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx index 0f8b9e275e..142898a4b7 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.test.tsx @@ -1,6 +1,5 @@ /* eslint-disable jsdoc/require-jsdoc */ /* eslint-disable no-unused-expressions */ -/* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ import React from 'react'; import sinon from 'sinon'; import { expect, use as chaiUse } from 'chai'; @@ -13,87 +12,48 @@ import { SitecoreProvider } from '../SitecoreProvider'; import { DesignLibraryStatus, getDesignLibraryStatusEvent, - getDesignLibraryAtomsRegistryEvent, - AtomInfo, } from '@sitecore-content-sdk/content/editing'; -import * as atomRegistryUtils from '../../atoms/atom-registry-utils'; -import { serializeCallbacks } from '../../atoms/callback-registry-utils'; -import { Document } from '@sitecore-content-sdk/content/atoms'; -import { AtomMetadata } from '../../atoms/types'; -import { z } from 'zod'; +import type { AtomsConfig } from '../../atoms/types'; import type { ImportMapImport } from './models'; +import type { DefineRegistryResult } from '@json-render/react'; describe('', () => { const sandbox = sinon.createSandbox(); - /** Minimal stubs so `SitecoreProvider` matches its public contract in tests */ const apiStub = {} as any; const emptyComponentMap = new Map(); - const loadImportMapStub = async (): Promise => ({}) as ImportMapImport; + const loadImportMapStub = async (): Promise => ({} as ImportMapImport); let postToDesignLibrarySpy: sinon.SinonStub; let sendAtomsErrorEventSpy: sinon.SinonStub; - let addDocumentUpdateHandlerSpy: sinon.SinonStub; - let serializeAtomsStub: sinon.SinonStub; - let getAtomMapStub: sinon.SinonStub; + let addDocumentUpdateHandlerStub: sinon.SinonStub; - const mockAtoms: AtomMetadata[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button component', - component: () => React.createElement('button', null, 'Button'), - props: z.object({ - label: z.string(), - }), - }, - { - name: 'Text', - type: 'atom', - description: 'A text component', - component: () => React.createElement('div', null, 'Text'), - props: z.object({ - content: z.string(), - }), - }, - ]; - - const mockAtomMap: Record> = { - Stack: () => React.createElement('div', { 'data-test': 'stack' }), - Card: () => React.createElement('div', { 'data-test': 'card' }), - CardHeader: () => React.createElement('div', { 'data-test': 'card-header' }), - CardTitle: () => React.createElement('div', { 'data-test': 'card-title' }), - CardDescription: () => React.createElement('div', { 'data-test': 'card-description' }), - CardContent: () => React.createElement('div', { 'data-test': 'card-content' }), - Image: () => React.createElement('img', { 'data-test': 'image' }), - Button: () => React.createElement('button', { 'data-test': 'button' }), - Text: () => React.createElement('div', { 'data-test': 'text' }), + const mockRegistry: DefineRegistryResult = { + registry: {} as any, + handlers: () => ({}), + executeAction: async () => {}, }; - const mockSerializedAtoms: AtomInfo[] = [ - { - name: 'Button', - type: 'atom', - description: 'A button component', - props: { label: { type: 'string' } }, - allowedChildren: [], - }, - { - name: 'Text', - type: 'atom', - description: 'A text component', - props: { content: { type: 'string' } }, - allowedChildren: [], + const mockCatalog = { + componentNames: ['Button'], + actionNames: [], + data: { + components: { + Button: { + props: { toJSONSchema: () => ({}) }, + description: 'A button', + slots: ['default'], + }, + }, + actions: {}, }, - ]; + jsonSchema: () => ({}), + } as any; - const mockCallbacks = [ - { - name: 'trackSelection', - description: 'Track selection callback', - callbackFn: () => {}, - }, - ]; + const atomsConfig: AtomsConfig = { + catalog: mockCatalog, + registry: mockRegistry, + }; const getPage = () => ({ locale: 'en', @@ -101,9 +61,7 @@ describe('', () => { mode: { name: 'normal', isDesignLibrary: false, - designLibrary: { - isVariantGeneration: false, - }, + designLibrary: { isVariantGeneration: false }, isNormal: true, isPreview: false, isEditing: false, @@ -113,525 +71,64 @@ describe('', () => { beforeEach(() => { postToDesignLibrarySpy = sandbox.stub(); sendAtomsErrorEventSpy = sandbox.stub(); - addDocumentUpdateHandlerSpy = sandbox.stub().returns(() => {}); - serializeAtomsStub = sandbox.stub(atomRegistryUtils, 'serializeAtoms'); - getAtomMapStub = sandbox.stub(atomRegistryUtils, 'getAtomMap'); - + addDocumentUpdateHandlerStub = sandbox.stub().returns(() => {}); __mockDependencies({ postToDesignLibrary: postToDesignLibrarySpy, sendAtomsErrorEvent: sendAtomsErrorEventSpy, - addDocumentUpdateHandler: addDocumentUpdateHandlerSpy, + addDocumentUpdateHandler: addDocumentUpdateHandlerStub, }); - - serializeAtomsStub.returns(mockSerializedAtoms); - getAtomMapStub.returns(mockAtomMap); - - // Stub console methods to suppress console output during tests - sandbox.stub(console, 'log'); - sandbox.stub(console, 'warn'); }); afterEach(() => { sandbox.restore(); }); - it('should send READY status event on mount', () => { - const page = getPage(); - - render( - - - - ); - - expect(postToDesignLibrarySpy).to.have.been.calledWith( - getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'low-code-component') - ); - }); - - it('should wrap rendered view in DesignLibraryErrorBoundary', () => { - const page = getPage(); - - const { container } = render( - - - - ); - - expect(container).to.exist; - expect(getAtomMapStub).to.have.been.called; - }); - - it('should serialize atoms and send atoms registry event', () => { - const page = getPage(); - - render( - - - - ); - - expect(serializeAtomsStub).to.have.been.called; - expect(postToDesignLibrarySpy).to.have.been.calledWith( - getDesignLibraryAtomsRegistryEvent(mockSerializedAtoms, serializeCallbacks(mockCallbacks)) - ); - }); - - it('should send error event when serialized atoms list is empty', () => { - const page = getPage(); - serializeAtomsStub.returns([]); - - render( - - - - ); - - expect(serializeAtomsStub).to.have.been.calledWith([]); - expect(sendAtomsErrorEventSpy).to.have.been.calledWith('No atoms provided', 'atoms-missing'); - expect(postToDesignLibrarySpy).not.to.have.been.calledWith( - sinon.match((arg) => arg.name === 'atom:registry') - ); - }); - - it('should subscribe to document updates on mount', () => { - const page = getPage(); - - render( - - - - ); - - expect(addDocumentUpdateHandlerSpy).to.have.been.called; - }); - - it('should update document state when document update is received', async () => { - const page = getPage(); - let capturedHandler: ((doc: Document) => void) | null = null; - - addDocumentUpdateHandlerSpy.callsFake((handler) => { - capturedHandler = handler; - return () => {}; - }); - - const { container } = render( - - - - ); - - expect(capturedHandler).to.not.be.null; - - const updatedDocument: Document = { - name: 'UpdatedDocument', - root: { - id: 'updated-root', - type: 'Stack', - children: [], - }, - props: {}, - state: {}, - }; - - capturedHandler!(updatedDocument); - - await waitFor(() => { - expect(container).to.exist; - }); - }); - - it('should increment renderKey when document update is received', async () => { - const page = getPage(); - let capturedHandler: ((doc: Document) => void) | null = null; - - addDocumentUpdateHandlerSpy.callsFake((handler) => { - capturedHandler = handler; - return () => {}; - }); - + const renderComponent = (runtime?: AtomsConfig) => render( ); - const initialCallCount = postToDesignLibrarySpy.callCount; - - const updatedDocument: Document = { - name: 'UpdatedDocument', - root: { - id: 'updated-root', - type: 'Stack', - children: [], - }, - props: {}, - state: {}, - }; - - capturedHandler!(updatedDocument); - - // renderKey increment should trigger RENDERED event + it('posts READY status on mount', async () => { + renderComponent(atomsConfig); await waitFor(() => { - expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(initialCallCount); expect(postToDesignLibrarySpy).to.have.been.calledWith( - getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + getDesignLibraryStatusEvent(DesignLibraryStatus.READY, 'low-code-component') ); }); }); - it('should send RENDERED status event after document update', async () => { - const page = getPage(); - let capturedHandler: ((doc: Document) => void) | null = null; - - addDocumentUpdateHandlerSpy.callsFake((handler) => { - capturedHandler = handler; - return () => {}; - }); - - render( - - - - ); - - const initialCallCount = postToDesignLibrarySpy.callCount; - - const updatedDocument: Document = { - name: 'UpdatedDocument', - root: { - id: 'updated-root', - type: 'Stack', - children: [], - }, - props: {}, - state: {}, - }; - - capturedHandler!(updatedDocument); - + it('sends error when no catalog is provided', async () => { + renderComponent(undefined); await waitFor(() => { - expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(initialCallCount); - expect(postToDesignLibrarySpy).to.have.been.calledWith( - getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') + expect(sendAtomsErrorEventSpy).to.have.been.calledWith( + 'No atoms catalog provided', + 'atoms-missing' ); }); }); - it('should not send RENDERED status before document update', () => { - const page = getPage(); - - render( - - - - ); - - const renderedCalls = postToDesignLibrarySpy - .getCalls() - .filter((call) => call.args[0]?.message?.status === DesignLibraryStatus.RENDERED); - - expect(renderedCalls).to.have.length(0); - }); - - it('should unsubscribe from document updates on unmount', () => { - const page = getPage(); - const unsubscribeSpy = sandbox.spy(); - - addDocumentUpdateHandlerSpy.returns(unsubscribeSpy); - - const { unmount } = render( - - - - ); - - expect(unsubscribeSpy).not.to.have.been.called; - - unmount(); - - expect(unsubscribeSpy).to.have.been.called; - }); - - it('should render view with atomMap from atoms registry', () => { - const page = getPage(); - - const { container } = render( - - - - ); - - expect(container).to.exist; - expect(getAtomMapStub).to.have.been.called; - }); - - it('should send error when atom registry has no atoms', () => { - const page = getPage(); - serializeAtomsStub.returns([]); - - render( - - - - ); - - expect(serializeAtomsStub).to.have.been.calledWith([]); - expect(sendAtomsErrorEventSpy).to.have.been.calledWith('No atoms provided', 'atoms-missing'); - }); - - it('should create atomMap from atoms registry', () => { - const page = getPage(); - - const { container } = render( - - - - ); - - expect(container).to.exist; - expect(getAtomMapStub).to.have.been.called; - }); - - it('should subscribe to document updates via atomRegistry effect', () => { - const page = getPage(); - const unsubscribeSpy = sandbox.spy(); - - addDocumentUpdateHandlerSpy.returns(unsubscribeSpy); - - const { unmount } = render( - - - - ); - - expect(addDocumentUpdateHandlerSpy).to.have.been.called; - - unmount(); - - expect(unsubscribeSpy).to.have.been.called; - }); - - it('should handle multiple document updates', async () => { - const page = getPage(); - let capturedHandler: ((doc: Document) => void) | null = null; - - addDocumentUpdateHandlerSpy.callsFake((handler) => { - capturedHandler = handler; - return () => {}; - }); - - render( - - - - ); - - const doc1: Document = { - name: 'Doc1', - root: { id: '1', type: 'Stack', children: [] }, - props: {}, - state: {}, - }; - - const doc2: Document = { - name: 'Doc2', - root: { id: '2', type: 'Stack', children: [] }, - props: {}, - state: {}, - }; - - capturedHandler!(doc1); - - await waitFor(() => { - expect(postToDesignLibrarySpy).to.have.been.calledWith( - getDesignLibraryStatusEvent(DesignLibraryStatus.RENDERED, 'low-code-component') - ); - }); - - const callCountAfterFirst = postToDesignLibrarySpy.callCount; - - capturedHandler!(doc2); - + it('posts atoms:catalog event when catalog is available', async () => { + renderComponent(atomsConfig); await waitFor(() => { - expect(postToDesignLibrarySpy.callCount).to.be.greaterThan(callCountAfterFirst); + const catalogCall = postToDesignLibrarySpy + .getCalls() + .find((c: sinon.SinonSpyCall) => c.args[0]?.name === 'atoms:catalog'); + expect(catalogCall).to.not.be.undefined; }); }); - it('should use callback registry when creating view', () => { - const page = getPage(); - const customCallbacks = [ - { name: 'onButtonClick', description: 'Button click callback', callbackFn: () => {} }, - { name: 'onSelectChange', description: 'Select change callback', callbackFn: () => {} }, - ]; - - const { container } = render( - - - - ); - - expect(container).to.exist; - }); - - it('should handle empty callback registry', () => { - const page = getPage(); - - const { container } = render( - - - - ); - - expect(container).to.exist; - }); - - it('should send events in correct order', async () => { - const page = getPage(); - let capturedHandler: ((doc: Document) => void) | null = null; - - addDocumentUpdateHandlerSpy.callsFake((handler) => { - capturedHandler = handler; - return () => {}; - }); - - render( - - - - ); - - const calls = postToDesignLibrarySpy.getCalls(); - const readyCall = calls.find( - (call) => call.args[0]?.message?.status === DesignLibraryStatus.READY - ); - const registryCall = calls.find((call) => call.args[0]?.name === 'atom:registry'); - - expect(readyCall).to.exist; - expect(registryCall).to.exist; - - const updatedDocument: Document = { - name: 'Updated', - root: { id: 'root', type: 'Stack', children: [] }, - props: {}, - state: {}, - }; - - capturedHandler!(updatedDocument); - + it('subscribes to document update handler', async () => { + renderComponent(atomsConfig); await waitFor(() => { - const renderedCall = postToDesignLibrarySpy - .getCalls() - .find((call) => call.args[0]?.message?.status === DesignLibraryStatus.RENDERED); - expect(renderedCall).to.exist; + expect(addDocumentUpdateHandlerStub).to.have.been.called; }); }); }); diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx index e67ecdf87b..7dfe7bfa5e 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx @@ -1,21 +1,15 @@ 'use client'; import React, { useEffect, useState } from 'react'; import { useSitecore } from '../SitecoreProvider'; -import { serializeAtoms } from '../../atoms/atom-registry-utils'; -import { serializeCallbacks } from '../../atoms/callback-registry-utils'; -import { StudioComponentWrapper } from '../../atoms/Wrapper/StudioComponentWrapper'; +import { serializeCatalog } from '../../atoms'; +import { StudioComponentWrapper } from './StudioComponentWrapper'; import type { Document } from '@sitecore-content-sdk/content/atoms'; import * as editing from '@sitecore-content-sdk/content/editing'; +import * as atoms from '@sitecore-content-sdk/content/atoms'; import { DesignLibraryErrorBoundary } from '../..'; -let { - postToDesignLibrary, - getDesignLibraryAtomsRegistryEvent, - getDesignLibraryStatusEvent, - DesignLibraryStatus, - sendAtomsErrorEvent, - addDocumentUpdateHandler, -} = editing; +let { postToDesignLibrary, getDesignLibraryStatusEvent, DesignLibraryStatus } = editing; +let { addDocumentUpdateHandler, getDesignLibraryAtomsCatalogEvent, sendAtomsErrorEvent } = atoms; export const __mockDependencies = (mocks: any) => { if (mocks.postToDesignLibrary) { @@ -33,13 +27,13 @@ export const __mockDependencies = (mocks: any) => { * Design Library Low Code component. * * Facilitates the communication between the Design Studio and the Rendering Host when previewing a low code component built with the Atoms. - * - On mount, it unfolds and serializes the atoms registry and callback registry and sends it to the Design Studio via the `getDesignLibraryAtomsRegistryEvent`. + * - On mount, it serializes the atoms catalog and sends it to the Design Studio via the `atoms:catalog` event. * - Receives Component model data updates via document update handler and renders the low code component * via {@link StudioComponentWrapper} (same client path as Studio / NCC preview elsewhere). * @internal */ export const DesignLibraryLowCodeComponent = () => { - const { atomRegistry } = useSitecore(); + const { atomsConfig } = useSitecore(); const [currentDocument, setCurrentDocument] = useState(null); const [renderKey, setRenderKey] = useState(0); @@ -50,15 +44,13 @@ export const DesignLibraryLowCodeComponent = () => { }, []); useEffect(() => { - const serializedAtoms = serializeAtoms(atomRegistry?.atoms ?? []); - if (serializedAtoms.length === 0) { - sendAtomsErrorEvent('No atoms provided', 'atoms-missing'); + if (!atomsConfig?.catalog) { + sendAtomsErrorEvent('No atoms catalog provided', 'atoms-missing'); return; } - const serializedCallbacks = serializeCallbacks(atomRegistry?.callbacks ?? []); - - postToDesignLibrary(getDesignLibraryAtomsRegistryEvent(serializedAtoms, serializedCallbacks)); + const payload = serializeCatalog(atomsConfig.catalog); + postToDesignLibrary(getDesignLibraryAtomsCatalogEvent(payload)); const unsubDocumentUpdate = addDocumentUpdateHandler((updatedDocument) => { setCurrentDocument(updatedDocument); @@ -66,7 +58,7 @@ export const DesignLibraryLowCodeComponent = () => { }); return () => unsubDocumentUpdate(); - }, [atomRegistry]); + }, [atomsConfig]); useEffect(() => { if (renderKey === 0) return; diff --git a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.test.tsx similarity index 98% rename from packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx rename to packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.test.tsx index 564bc95f04..42dc8ecb60 100644 --- a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.test.tsx +++ b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.test.tsx @@ -20,7 +20,10 @@ describe('StudioComponentServerWrapper', () => { const sampleDocument: Document = { name: 'hero', - root: { id: 'r', type: 'Box' }, + root: 'test', + elements: { + test: { type: 'Text', props: { content: 'Hello' }, children: [] }, + }, }; beforeEach(() => { @@ -220,4 +223,3 @@ describe('StudioComponentServerWrapper', () => { }); }); }); - diff --git a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx similarity index 90% rename from packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx rename to packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx index b2d7296d61..1593235b7b 100644 --- a/packages/react/src/atoms/Wrapper/StudioComponentServerWrapper.tsx +++ b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx @@ -1,11 +1,22 @@ import React from 'react'; import { StudioComponentWrapper } from './StudioComponentWrapper'; -import { StudioComponentServerWrapperProps } from './models'; import { NativeDataFetcher, NativeDataFetcherResponse } from '@sitecore-content-sdk/core'; import { debug } from '@sitecore-content-sdk/content'; import { Document } from '@sitecore-content-sdk/content/atoms'; import { resolveEdgeUrl } from '@sitecore-content-sdk/core/tools'; +/** + * Props accepted by the RSC `StudioComponentServerWrapper`. + * @internal + */ +export type StudioComponentServerWrapperProps = { + /** + * Pipe separated relative paths to the Studio component layout JSON in MMS with the last segment as the variant name. The path matching `FieldNames` will be used, or `default` if no match. + */ + componentRef: string; + fieldNames?: string; +}; + /** * Server component for Studio (NCC) components. Fetches the component layout * `Document` from MMS server-side and renders the client `StudioComponentWrapper`. @@ -70,11 +81,6 @@ const MMS_COMPONENT_PATH_PREFIX = 'mms'; * @returns {Promise} the resolved component layout, or `null` on missing path, fetch failure, or un-parseable body. */ async function fetchDocument(path: string): Promise { - if (!path) { - console.warn('StudioComponentServerWrapper: missing component reference path'); - return null; - } - let url: string; try { const pathWithMmsPrefix = path.startsWith('/') @@ -113,4 +119,3 @@ async function fetchDocument(path: string): Promise { return null; } } - diff --git a/packages/react/src/components/DesignLibrary/StudioComponentWrapper.test.tsx b/packages/react/src/components/DesignLibrary/StudioComponentWrapper.test.tsx new file mode 100644 index 0000000000..f7a9e79071 --- /dev/null +++ b/packages/react/src/components/DesignLibrary/StudioComponentWrapper.test.tsx @@ -0,0 +1,899 @@ +/* eslint-disable jsdoc/require-jsdoc */ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import { expect } from 'chai'; +import { render, fireEvent, act } from '@testing-library/react'; +import sinon from 'sinon'; +import { z } from 'zod'; +import { defineAtomsCatalog } from '../../atoms/define-atoms-catalog'; +import { defineAtomsRegistry } from '../../atoms/define-atoms-registry'; +import { useBoundProp } from '../../atoms'; +import { StudioComponentWrapper } from './StudioComponentWrapper'; +import { SitecoreProvider } from '../SitecoreProvider'; +import type { Document } from '@sitecore-content-sdk/content/atoms'; +import type { AtomsConfig } from '../../atoms/types'; +import type { ImportMapImport } from './models'; + +// ============================================================================= +// Shared provider helpers +// ============================================================================= + +const apiStub = {} as any; +const emptyComponentMap = new Map(); +const loadImportMapStub = async (): Promise => ({} as ImportMapImport); + +const getPage = () => ({ + locale: 'en', + layout: { sitecore: { context: {}, route: null } }, + mode: { + name: 'normal', + isDesignLibrary: false, + designLibrary: { isVariantGeneration: false }, + isNormal: true, + isPreview: false, + isEditing: false, + }, +}); + +function wrapInProvider(ui: React.ReactNode, atoms?: AtomsConfig) { + return ( + + {ui} + + ); +} + +// ============================================================================= +// Shared component stubs +// ============================================================================= + +const TextStub = ({ props }: any) => {(props as any)?.content}; +const BoxStub = ({ children }: any) =>
{children}
; +const ButtonStub = ({ props, emit }: any) => ( + +); +const CheckboxStub = ({ props, bindings }: any) => { + const [checked, setChecked] = useBoundProp((props as any)?.checked, bindings?.checked); + return ( + setChecked(e.target.checked)} + /> + ); +}; + +// ============================================================================= +// Shared catalogs + registries +// ============================================================================= + +/** Minimal catalog for prop-resolution tests (no Button, no actions). */ +const textBoxCatalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string().optional() }), description: 'Text' }, + Box: { props: z.object({}), description: 'Box', slots: ['default'] }, + }, + actions: {}, +}); + +const textBoxRegistry = defineAtomsRegistry(textBoxCatalog, { + components: { + Text: ({ props, children }) => ( + + {props?.content ?? children} + + ), + Box: ({ children }) =>
{children}
, + }, + actions: {}, +}); + +const textBoxConfig: AtomsConfig = { catalog: textBoxCatalog, registry: textBoxRegistry }; + +/** Catalog with Text + Box + Button for interactive action tests. */ +const interactiveCatalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string().optional() }), description: 'Text' }, + Box: { props: z.object({}), description: 'Box', slots: ['default'] }, + Button: { props: z.object({ label: z.string().optional() }), description: 'Button' }, + }, + actions: {}, +}); + +const interactiveRegistry = defineAtomsRegistry(interactiveCatalog, { + components: { Text: TextStub, Box: BoxStub, Button: ButtonStub }, +}); + +const interactiveConfig: AtomsConfig = { + catalog: interactiveCatalog, + registry: interactiveRegistry, +}; + +/** Catalog with Box + Text + Checkbox for two-way binding tests. */ +const bindCatalog = defineAtomsCatalog({ + components: { + Box: { props: z.object({}), description: 'Box', slots: ['default'] }, + Text: { props: z.object({ content: z.string().optional() }), description: 'Text' }, + Checkbox: { props: z.object({ checked: z.boolean().optional() }), description: 'Checkbox' }, + }, + actions: {}, +}); + +const bindRegistry = defineAtomsRegistry(bindCatalog, { + components: { Box: BoxStub, Text: TextStub, Checkbox: CheckboxStub }, +}); + +const bindConfig: AtomsConfig = { catalog: bindCatalog, registry: bindRegistry }; + +/** Catalog that adds Button to the bind set (for the external-setState test). */ +const bindWithButtonCatalog = defineAtomsCatalog({ + components: { + Box: { props: z.object({}), description: 'Box', slots: ['default'] }, + Text: { props: z.object({ content: z.string().optional() }), description: 'Text' }, + Checkbox: { props: z.object({ checked: z.boolean().optional() }), description: 'Checkbox' }, + Button: { props: z.object({ label: z.string().optional() }), description: 'Button' }, + }, + actions: {}, +}); + +const bindWithButtonRegistry = defineAtomsRegistry(bindWithButtonCatalog, { + components: { Box: BoxStub, Text: TextStub, Checkbox: CheckboxStub, Button: ButtonStub }, +}); + +const bindWithButtonConfig: AtomsConfig = { + catalog: bindWithButtonCatalog, + registry: bindWithButtonRegistry, +}; + +// ============================================================================= +// Sample documents +// ============================================================================= + +const sampleDoc: Document = { + name: 'hero', + root: 'root-el', + elements: { 'root-el': { type: 'Box', props: {}, children: [] } }, +}; + +// ============================================================================= +// Tests +// ============================================================================= + +describe('', () => { + const renderInProvider = (ui: React.ReactNode, atoms = textBoxConfig) => + render(wrapInProvider(ui, atoms)); + + // --------------------------------------------------------------------------- + // Guard conditions + // --------------------------------------------------------------------------- + + describe('guard conditions', () => { + it('renders null when document is null', () => { + const { container } = renderInProvider(); + expect(container.innerHTML).to.equal(''); + }); + + it('renders null when document is undefined', () => { + const { container } = renderInProvider(); + expect(container.innerHTML).to.equal(''); + }); + + it('renders null when atomsConfig is not provided', () => { + const { container } = render( + wrapInProvider(, undefined) + ); + expect(container.innerHTML).to.equal(''); + }); + + it('renders a view when document and atomsConfig are both provided', () => { + const { container } = renderInProvider(); + expect(container.innerHTML).to.not.equal(''); + }); + }); + + // --------------------------------------------------------------------------- + // Prop resolution + // --------------------------------------------------------------------------- + + describe('prop resolution', () => { + describe('$state', () => { + it('resolves a top-level path from doc.state', () => { + const doc: Document = { + name: 'state-test', + root: 'r', + elements: { + r: { type: 'Text', props: { content: { $state: '/message' } }, children: [] }, + }, + state: { message: 'Hello from state' }, + }; + const { getByTestId } = renderInProvider(); + expect(getByTestId('text-el').textContent).to.equal('Hello from state'); + }); + + it('resolves a nested JSON Pointer path', () => { + const doc: Document = { + name: 'nested-state', + root: 'r', + elements: { + r: { type: 'Text', props: { content: { $state: '/user/name' } }, children: [] }, + }, + state: { user: { name: 'Alice' } }, + }; + const { getByTestId } = renderInProvider(); + expect(getByTestId('text-el').textContent).to.equal('Alice'); + }); + }); + + describe('$template', () => { + it('interpolates state values into the template string', () => { + const doc: Document = { + name: 'template-test', + root: 'r', + elements: { + r: { + type: 'Text', + // Single-quoted — ${/name} is a literal template token, not a JS template literal + props: { content: { $template: 'Hello, ${/name}!' } }, + children: [], + }, + }, + state: { name: 'Alice' }, + }; + const { getByTestId } = renderInProvider(); + expect(getByTestId('text-el').textContent).to.equal('Hello, Alice!'); + }); + }); + + describe('$cond / $then / $else', () => { + it('renders the $then branch when the condition is truthy', () => { + const doc: Document = { + name: 'cond-truthy', + root: 'r', + elements: { + r: { + type: 'Text', + props: { + content: { $cond: { $state: '/isAdmin' }, $then: 'Admin', $else: 'Member' }, + }, + children: [], + }, + }, + state: { isAdmin: true }, + }; + const { getByTestId } = renderInProvider(); + expect(getByTestId('text-el').textContent).to.equal('Admin'); + }); + + it('renders the $else branch when the condition is falsy', () => { + const doc: Document = { + name: 'cond-falsy', + root: 'r', + elements: { + r: { + type: 'Text', + props: { + content: { $cond: { $state: '/isAdmin' }, $then: 'Admin', $else: 'Member' }, + }, + children: [], + }, + }, + state: { isAdmin: false }, + }; + const { getByTestId } = renderInProvider(); + expect(getByTestId('text-el').textContent).to.equal('Member'); + }); + }); + }); + + // --------------------------------------------------------------------------- + // Actions + // --------------------------------------------------------------------------- + + describe('actions', () => { + const renderInteractive = (ui: React.ReactNode) => renderInProvider(ui, interactiveConfig); + + describe('built-in setState', () => { + it('updates a $state-bound prop and re-renders', async () => { + const doc: Document = { + name: 'setstate-test', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'display'] }, + btn: { + type: 'Button', + props: { label: 'Update' }, + on: { + press: { action: 'setState', params: { statePath: '/message', value: 'updated' } }, + }, + children: [], + }, + display: { type: 'Text', props: { content: { $state: '/message' } }, children: [] }, + }, + state: { message: 'initial' }, + }; + + const { getByTestId } = renderInteractive(); + expect(getByTestId('text-el').textContent).to.equal('initial'); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(getByTestId('text-el').textContent).to.equal('updated'); + }); + + it('updates a nested JSON Pointer path', async () => { + const doc: Document = { + name: 'nested-setstate', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'display'] }, + btn: { + type: 'Button', + props: { label: 'Go' }, + on: { + press: { action: 'setState', params: { statePath: '/user/name', value: 'Bob' } }, + }, + children: [], + }, + display: { type: 'Text', props: { content: { $state: '/user/name' } }, children: [] }, + }, + state: { user: { name: 'Alice' } }, + }; + + const { getByTestId } = renderInteractive(); + expect(getByTestId('text-el').textContent).to.equal('Alice'); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(getByTestId('text-el').textContent).to.equal('Bob'); + }); + + it('a chained setState reads the value mutated by the preceding action', async () => { + const doc: Document = { + name: 'chained-setstate', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'display'] }, + btn: { + type: 'Button', + props: { label: 'Go' }, + on: { + press: [ + { action: 'setState', params: { statePath: '/counter', value: 42 } }, + { + action: 'setState', + params: { statePath: '/copy', value: { $state: '/counter' } }, + }, + ], + }, + children: [], + }, + display: { type: 'Text', props: { content: { $state: '/copy' } }, children: [] }, + }, + state: { counter: 0, copy: 0 }, + }; + + const { getByTestId } = renderInteractive(); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(getByTestId('text-el').textContent).to.equal('42'); + }); + }); + + describe('built-in pushState', () => { + it('appends an item to a state array and re-renders the list', async () => { + const doc: Document = { + name: 'pushstate-test', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'list'] }, + btn: { + type: 'Button', + props: { label: 'Add' }, + on: { + press: { + action: 'pushState', + params: { statePath: '/items', value: { id: '3', name: 'Charlie' } }, + }, + }, + children: [], + }, + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/items', key: 'id' }, + children: ['item'], + }, + item: { type: 'Text', props: { content: { $item: 'name' } }, children: [] }, + }, + state: { + items: [ + { id: '1', name: 'Alice' }, + { id: '2', name: 'Bob' }, + ], + }, + }; + + const { getAllByTestId, getByTestId } = renderInteractive( + + ); + expect(getAllByTestId('text-el')).to.have.length(2); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(getAllByTestId('text-el')).to.have.length(3); + expect(getAllByTestId('text-el')[2].textContent).to.equal('Charlie'); + }); + }); + + describe('built-in removeState', () => { + it('removes the item at the given index from a state array', async () => { + const doc: Document = { + name: 'removestate-test', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'list'] }, + btn: { + type: 'Button', + props: { label: 'Remove Middle' }, + on: { + press: { action: 'removeState', params: { statePath: '/items', index: 1 } }, + }, + children: [], + }, + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/items', key: 'id' }, + children: ['item'], + }, + item: { type: 'Text', props: { content: { $item: 'name' } }, children: [] }, + }, + state: { + items: [ + { id: '1', name: 'Alice' }, + { id: '2', name: 'Bob' }, + { id: '3', name: 'Charlie' }, + ], + }, + }; + + const { getAllByTestId, getByTestId } = renderInteractive( + + ); + expect(getAllByTestId('text-el')).to.have.length(3); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + const remaining = getAllByTestId('text-el').map((n) => n.textContent); + expect(remaining).to.deep.equal(['Alice', 'Charlie']); + }); + + it('removes the first item when index is 0', async () => { + const doc: Document = { + name: 'removestate-first', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'list'] }, + btn: { + type: 'Button', + props: { label: 'Remove First' }, + on: { + press: { action: 'removeState', params: { statePath: '/items', index: 0 } }, + }, + children: [], + }, + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/items', key: 'id' }, + children: ['item'], + }, + item: { type: 'Text', props: { content: { $item: 'name' } }, children: [] }, + }, + state: { + items: [ + { id: '1', name: 'Alice' }, + { id: '2', name: 'Bob' }, + ], + }, + }; + + const { getAllByTestId, getByTestId } = renderInteractive( + + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + const remaining = getAllByTestId('text-el').map((n) => n.textContent); + expect(remaining).to.deep.equal(['Bob']); + }); + }); + + describe('built-in navigate', () => { + const navigateSpy = sinon.stub(); + + const navigateCatalog = defineAtomsCatalog({ + components: { + Button: { props: z.object({ label: z.string().optional() }), description: 'Button' }, + }, + actions: { + save: { params: z.object({}), description: 'Save and navigate' }, + }, + }); + + const navigateRegistry = defineAtomsRegistry(navigateCatalog, { + components: { Button: ButtonStub }, + actions: { save: async () => {} }, + }); + + const navigateConfig: AtomsConfig = { + catalog: navigateCatalog, + registry: navigateRegistry, + navigate: navigateSpy, + }; + + beforeEach(() => navigateSpy.resetHistory()); + + it('calls the navigate callback with the path from onSuccess after the action succeeds', async () => { + const doc: Document = { + name: 'navigate-test', + root: 'r', + elements: { + r: { + type: 'Button', + props: { label: 'Save' }, + on: { press: { action: 'save', onSuccess: { navigate: '/dashboard' } } }, + children: [], + }, + }, + }; + + const { getByTestId } = renderInProvider( + , + navigateConfig + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(navigateSpy.calledOnce).to.be.true; + expect(navigateSpy.firstCall.args[0]).to.equal('/dashboard'); + }); + + it('does not call navigate when onSuccess is not set', async () => { + const doc: Document = { + name: 'navigate-no-success', + root: 'r', + elements: { + r: { + type: 'Button', + props: { label: 'Save' }, + on: { press: { action: 'save' } }, + children: [], + }, + }, + }; + + const { getByTestId } = renderInProvider( + , + navigateConfig + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(navigateSpy.called).to.be.false; + }); + }); + + describe('custom action handlers', () => { + const greetSpy = sinon.stub(); + + const greetCatalog = defineAtomsCatalog({ + components: { + Text: { props: z.object({ content: z.string().optional() }), description: 'Text' }, + Box: { props: z.object({}), description: 'Box', slots: ['default'] }, + Button: { props: z.object({ label: z.string().optional() }), description: 'Button' }, + }, + actions: { + greet: { params: z.object({ name: z.string() }), description: 'Greet someone' }, + }, + }); + + const greetRegistry = defineAtomsRegistry(greetCatalog, { + components: { Text: TextStub, Box: BoxStub, Button: ButtonStub }, + actions: { greet: async (params) => greetSpy(params) }, + }); + + const greetConfig: AtomsConfig = { catalog: greetCatalog, registry: greetRegistry }; + + beforeEach(() => greetSpy.resetHistory()); + + it('calls the registered handler with the correct params', async () => { + const doc: Document = { + name: 'custom-action', + root: 'r', + elements: { + r: { + type: 'Button', + props: { label: 'Say Hello' }, + on: { press: { action: 'greet', params: { name: 'World' } } }, + children: [], + }, + }, + }; + + const { getByTestId } = renderInProvider( + , + greetConfig + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(greetSpy.calledOnce).to.be.true; + expect(greetSpy.firstCall.args[0]).to.deep.equal({ name: 'World' }); + }); + + it('resolves $state references in params before invoking the handler', async () => { + const doc: Document = { + name: 'action-state-params', + root: 'r', + elements: { + r: { + type: 'Button', + props: { label: 'Greet' }, + on: { press: { action: 'greet', params: { name: { $state: '/userName' } } } }, + children: [], + }, + }, + state: { userName: 'Alice' }, + }; + + const { getByTestId } = renderInProvider( + , + greetConfig + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(greetSpy.calledOnce).to.be.true; + expect(greetSpy.firstCall.args[0]).to.deep.equal({ name: 'Alice' }); + }); + + it('does not call the handler when no binding is set for the event', async () => { + const doc: Document = { + name: 'no-binding', + root: 'r', + elements: { + r: { type: 'Button', props: { label: 'Inert' }, children: [] }, + }, + }; + + const { getByTestId } = renderInProvider( + , + greetConfig + ); + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect(greetSpy.called).to.be.false; + }); + }); + }); + + // --------------------------------------------------------------------------- + // List rendering with repeat + // --------------------------------------------------------------------------- + + describe('list rendering with repeat', () => { + const renderList = (doc: Document) => + renderInProvider(, interactiveConfig); + + it('renders one child per item in the state array', () => { + const doc: Document = { + name: 'list-test', + root: 'list', + elements: { + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/items', key: 'id' }, + children: ['item'], + }, + item: { type: 'Text', props: { content: { $item: 'name' } }, children: [] }, + }, + state: { + items: [ + { id: '1', name: 'Alice' }, + { id: '2', name: 'Bob' }, + { id: '3', name: 'Charlie' }, + ], + }, + }; + + const texts = renderList(doc) + .getAllByTestId('text-el') + .map((n) => n.textContent); + expect(texts).to.deep.equal(['Alice', 'Bob', 'Charlie']); + }); + + it('renders nothing when the state array is empty', () => { + const doc: Document = { + name: 'empty-list', + root: 'list', + elements: { + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/items', key: 'id' }, + children: ['item'], + }, + item: { type: 'Text', props: { content: { $item: 'name' } }, children: [] }, + }, + state: { items: [] }, + }; + + const { container } = renderList(doc); + expect(container.querySelectorAll('[data-testid="text-el"]').length).to.equal(0); + }); + + it('filters items via a $item visibility condition on the child element', () => { + // Visibility on the CHILD (inside the repeat scope) filters individual items. + const doc: Document = { + name: 'filtered-list', + root: 'list', + elements: { + list: { + type: 'Box', + props: {}, + repeat: { statePath: '/tasks', key: 'id' }, + children: ['item'], + }, + item: { + type: 'Text', + props: { content: { $item: 'title' } }, + visible: { $item: 'active', eq: true }, + children: [], + }, + }, + state: { + tasks: [ + { id: '1', title: 'Buy groceries', active: true }, + { id: '2', title: 'Read book', active: false }, + { id: '3', title: 'Go running', active: true }, + ], + }, + }; + + const texts = renderList(doc) + .getAllByTestId('text-el') + .map((n) => n.textContent); + expect(texts).to.deep.equal(['Buy groceries', 'Go running']); + }); + }); + + // --------------------------------------------------------------------------- + // $bindState — two-way binding + // --------------------------------------------------------------------------- + + describe('$bindState — two-way binding', () => { + it('reflects the initial state value in the bound prop', () => { + const doc: Document = { + name: 'bindstate-initial', + root: 'r', + elements: { + r: { type: 'Checkbox', props: { checked: { $bindState: '/isChecked' } }, children: [] }, + }, + state: { isChecked: true }, + }; + + const { getByTestId } = renderInProvider( + , + bindConfig + ); + expect((getByTestId('checkbox-el') as HTMLInputElement).checked).to.be.true; + }); + + it('writes back to state when the component changes the bound value', async () => { + const doc: Document = { + name: 'bindstate-writeback', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['checkbox', 'display'] }, + checkbox: { + type: 'Checkbox', + props: { checked: { $bindState: '/isChecked' } }, + children: [], + }, + display: { + type: 'Text', + props: { + content: { $cond: { $state: '/isChecked' }, $then: 'checked', $else: 'unchecked' }, + }, + children: [], + }, + }, + state: { isChecked: false }, + }; + + const { getByTestId } = renderInProvider( + , + bindConfig + ); + expect(getByTestId('text-el').textContent).to.equal('unchecked'); + expect((getByTestId('checkbox-el') as HTMLInputElement).checked).to.be.false; + + await act(async () => { + fireEvent.click(getByTestId('checkbox-el')); + }); + + expect(getByTestId('text-el').textContent).to.equal('checked'); + expect((getByTestId('checkbox-el') as HTMLInputElement).checked).to.be.true; + }); + + it('reflects an external setState into the bound prop (read direction)', async () => { + const doc: Document = { + name: 'bindstate-external', + root: 'root', + elements: { + root: { type: 'Box', props: {}, children: ['btn', 'checkbox'] }, + btn: { + type: 'Button', + props: { label: 'Check' }, + on: { press: { action: 'setState', params: { statePath: '/isChecked', value: true } } }, + children: [], + }, + checkbox: { + type: 'Checkbox', + props: { checked: { $bindState: '/isChecked' } }, + children: [], + }, + }, + state: { isChecked: false }, + }; + + const { getByTestId } = renderInProvider( + , + bindWithButtonConfig + ); + expect((getByTestId('checkbox-el') as HTMLInputElement).checked).to.be.false; + + await act(async () => { + fireEvent.click(getByTestId('btn')); + }); + + expect((getByTestId('checkbox-el') as HTMLInputElement).checked).to.be.true; + }); + }); +}); + diff --git a/packages/react/src/components/DesignLibrary/StudioComponentWrapper.tsx b/packages/react/src/components/DesignLibrary/StudioComponentWrapper.tsx new file mode 100644 index 0000000000..505e385ef9 --- /dev/null +++ b/packages/react/src/components/DesignLibrary/StudioComponentWrapper.tsx @@ -0,0 +1,37 @@ +'use client'; +import React, { JSX, useMemo } from 'react'; +import { createNCC } from '../../atoms'; +import { useSitecore } from '../SitecoreProvider'; +import { Document } from '@sitecore-content-sdk/content/atoms'; + +/** + * Props accepted by the `StudioComponentWrapper` used to render a Studio component layout on the client. Expects a pre-fetched `document` containing the component layout data. + * @internal + */ +type StudioComponentWrapperProps = { + document: Document | null; +}; + +/** + * Client component that renders a pre-fetched Studio (NCC) component layout. + * + * Expects `document` to be provided (fetched server-side by + * `StudioComponentServerWrapper`, from Design Library document updates, or any other + * preview path that supplies a layout `Document`). Renders `null` when no layout + * is available. + * @param {StudioComponentWrapperProps} props component props + * @internal + */ +export const StudioComponentWrapper = (props: StudioComponentWrapperProps): JSX.Element | null => { + const { atomsConfig } = useSitecore(); + + const NCComponent = useMemo(() => { + if (!props.document || !atomsConfig) return null; + + return createNCC(props.document, atomsConfig.registry); + }, [props.document, atomsConfig]); + + if (!NCComponent) return null; + + return ; +}; diff --git a/packages/react/src/components/SitecoreProvider.test.tsx b/packages/react/src/components/SitecoreProvider.test.tsx index 128537fd83..5d750828a0 100644 --- a/packages/react/src/components/SitecoreProvider.test.tsx +++ b/packages/react/src/components/SitecoreProvider.test.tsx @@ -15,7 +15,7 @@ describe('SitecoreProvider', () => { anotherProperty?: string; } - const loadImportMapStub = async (): Promise => ({}) as ImportMapImport; + const loadImportMapStub = async (): Promise => ({} as ImportMapImport); const NestedComponent: FC = () => { const { page } = useSitecore(); @@ -24,8 +24,8 @@ describe('SitecoreProvider', () => { }; const AtomRegistryProbe: FC = () => { - const { atomRegistry } = useSitecore(); - nestedContext = atomRegistry as unknown; + const { atomsConfig } = useSitecore(); + nestedContext = atomsConfig as unknown; return probe; }; @@ -117,11 +117,11 @@ describe('SitecoreProvider', () => { }); }); - it('exposes atomRegistry on context when provided', () => { + it('exposes atomsConfig on context when provided', () => { nestedContext = undefined; - const atomRegistry = { - atoms: [], - callbacks: [], + const atomsConfig = { + catalog: {} as any, + registry: {} as any, }; render( @@ -130,12 +130,12 @@ describe('SitecoreProvider', () => { componentMap={components} page={mockPage} loadImportMap={loadImportMapStub} - atomRegistry={atomRegistry} + atomsConfig={atomsConfig} > ); - expect(nestedContext).to.deep.equal(atomRegistry); + expect(nestedContext).to.deep.equal(atomsConfig); }); }); diff --git a/packages/react/src/components/SitecoreProvider.tsx b/packages/react/src/components/SitecoreProvider.tsx index 904851d44c..60822c7541 100644 --- a/packages/react/src/components/SitecoreProvider.tsx +++ b/packages/react/src/components/SitecoreProvider.tsx @@ -5,7 +5,7 @@ import { Page } from '@sitecore-content-sdk/content/client'; import { SitecoreConfig } from '@sitecore-content-sdk/content/config'; import { ComponentMap } from './sharedTypes'; import { ImportMapImport } from './DesignLibrary/models'; -import { AtomMetadata, CallbackMetadata } from '../atoms/types'; +import type { AtomsConfig } from '../atoms/types'; export interface SitecoreProviderProps { /** @@ -25,18 +25,10 @@ export interface SitecoreProviderProps { */ loadImportMap: () => Promise; /** - * Atom and callback metadata for low-code components (mirrors the singular `componentMap` pattern). + * Atoms configuration: catalog and registry for rendering low-code components. + * Pass the catalog from defineAtomsCatalog and the registry result from defineAtomsRegistry. */ - atomRegistry?: { - /** - * The atom metadata to be used for rendering atom components. - */ - atoms?: AtomMetadata[]; - /** - * The callback metadata to be used for rendering atom components. - */ - callbacks?: CallbackMetadata[]; - }; + atomsConfig?: AtomsConfig; children: React.ReactNode; } @@ -61,18 +53,9 @@ export interface SitecoreProviderState { */ loadImportMap: () => Promise; /** - * Atom and callback metadata for low-code components (mirrors the singular `componentMap` pattern). + * Atoms runtime: catalog and registry for rendering low-code components. */ - atomRegistry?: { - /** - * The atom metadata to be used for rendering atom components. - */ - atoms?: AtomMetadata[]; - /** - * The callback metadata to be used for rendering atom components. - */ - callbacks?: CallbackMetadata[]; - }; + atomsConfig?: AtomsConfig; /** * The component map to use for rendering components. */ @@ -115,13 +98,13 @@ export const ImportMapReactContext = React.createContext< * @param {SitecoreProviderProps['page']} props.page - The page data. * @param {SitecoreProviderProps['componentMap']} props.componentMap - The component map. * @param {SitecoreProviderProps['loadImportMap']} props.loadImportMap - The function to load the import map. - * @param {SitecoreProviderProps['atomRegistry']} props.atomRegistry - Atom and callback metadata for low-code components. + * @param {SitecoreProviderProps['atomsConfig']} props.atomsConfig - Atoms config (catalog + registry) for rendering low-code components. * @param {React.ReactNode} props.children - The children to render. * @returns {React.ReactNode} The SitecoreProvider component. * @public */ export const SitecoreProvider = (props: SitecoreProviderProps) => { - const { api, page: propsPage, componentMap, loadImportMap, atomRegistry, children } = props; + const { api, page: propsPage, componentMap, loadImportMap, atomsConfig, children } = props; const [page, setPageInternal] = useState(propsPage); @@ -145,9 +128,9 @@ export const SitecoreProvider = (props: SitecoreProviderProps) => { api, componentMap, loadImportMap, - atomRegistry, + atomsConfig, }), - [page, setPage, api, componentMap, loadImportMap, atomRegistry] + [page, setPage, api, componentMap, loadImportMap, atomsConfig] ); return ( diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 62ffb6a99d..82781318a8 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -10,11 +10,7 @@ export { NativeDataFetcherConfig, } from '@sitecore-content-sdk/core'; export { EnhancedOmit } from '@sitecore-content-sdk/core/tools'; -export { - isEditorActive, - resetEditorChromes, - AtomType, -} from '@sitecore-content-sdk/content/editing'; +export { isEditorActive, resetEditorChromes } from '@sitecore-content-sdk/content/editing'; export { getContentStylesheetLink, getDesignLibraryStylesheetLinks, @@ -83,14 +79,6 @@ export { FEaaSServerWrapper, BYOCServerWrapper, } from './components/FEaaS'; -export { - StudioComponentWrapper, - StudioComponentWrapper as StudioComponentClientWrapper, - StudioComponentServerWrapper, - type StudioComponentParams, - type StudioComponentServerWrapperProps, - type StudioComponentWrapperProps, -} from './atoms/Wrapper'; export { DesignLibrary, DesignLibraryLowCodeComponent, @@ -123,19 +111,18 @@ export { export { ClientEditingChromesUpdate } from './components/ClientEditingChromesUpdate'; export { SitePathService, SitePathServiceConfig } from '@sitecore-content-sdk/content/site'; export { - createAtom, + useBoundProp, + defineAtomsCatalog, + defineAtomsRegistry, withPropMeta, - withArgMeta, - getFieldMeta, - type AtomMetadata, - type AtomChild, - type DefaultChild, - type EditableComponentProps, - type CallbackPropKeys, - type CallbackArgZodTuple, type PropMeta, - type ArgMeta, - type AtomSchemaInput, + type AtomComponentDefinition, + type AtomActionDefinition, + type AtomsCatalogInput, + type AtomsComponentsMap, + type AtomActionHandler, + type AtomsActionsMap, + type AtomsConfig, textFieldSchema, richTextFieldSchema, dateFieldSchema, diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index f82d428bfe..10d4242ac0 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "compilerOptions": { + "rootDir": "./src", "target": "ES2017", "module": "commonjs", "jsx": "react", From 47c08e5fc0ffd707a1749d6984a10e76f95e0c2d Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 18:05:59 +0300 Subject: [PATCH 15/32] chore: set versions to beta --- packages/analytics-core/package.json | 2 +- packages/cli/package.json | 2 +- packages/content/package.json | 2 +- packages/core/package.json | 2 +- packages/create-content-sdk-app/package.json | 2 +- packages/events/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/personalize/package.json | 2 +- packages/react/package.json | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/analytics-core/package.json b/packages/analytics-core/package.json index e22b9cf27d..d4a9191531 100644 --- a/packages/analytics-core/package.json +++ b/packages/analytics-core/package.json @@ -75,5 +75,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.1.0" + "version": "2.0.0-beta.3" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 0442b335fc..14d7bead30 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/cli", - "version": "2.1.0", + "version": "2.0.0-beta.3", "description": "Sitecore Content SDK CLI", "main": "dist/cjs/cli.js", "module": "dist/esm/cli.js", diff --git a/packages/content/package.json b/packages/content/package.json index 1845cd7cd2..954c9c8cc5 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/content", - "version": "2.1.0", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/core/package.json b/packages/core/package.json index 6bb3c7c784..6c19ca00c3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/core", - "version": "2.1.0", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/create-content-sdk-app/package.json b/packages/create-content-sdk-app/package.json index 6d89a083ab..8d2d0832bf 100644 --- a/packages/create-content-sdk-app/package.json +++ b/packages/create-content-sdk-app/package.json @@ -1,6 +1,6 @@ { "name": "create-content-sdk-app", - "version": "2.1.0", + "version": "2.0.0-beta.3", "description": "Sitecore Content SDK initializer", "bin": "./dist/index.js", "scripts": { diff --git a/packages/events/package.json b/packages/events/package.json index 0cd50f253e..5c6ff03570 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -76,5 +76,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.1.0" + "version": "2.0.0-beta.3" } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index f421801b40..941207c936 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/nextjs", - "version": "2.1.0", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/personalize/package.json b/packages/personalize/package.json index 610893a7d9..76ccc4a10b 100644 --- a/packages/personalize/package.json +++ b/packages/personalize/package.json @@ -71,5 +71,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.1.0" + "version": "2.0.0-beta.3" } diff --git a/packages/react/package.json b/packages/react/package.json index f2a8d6eb54..b0ac16eefe 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/react", - "version": "2.1.0", + "version": "2.0.0-beta.3", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, From 59f8703cd60caa400589e2b1f6ea24dd834bf451 Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 18:09:28 +0300 Subject: [PATCH 16/32] chore: update template --- .../nextjs-app-router-cache-components/package.json | 8 +++++--- .../sitecore.cli.config.ts | 5 +++++ .../src/Providers.tsx | 5 +++++ .../src/atoms/index.tsx | 10 ++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/atoms/index.tsx diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/package.json b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/package.json index c699a9cdf2..191b623928 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/package.json +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/package.json @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "scripts": { - "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build next:build", + "build": "cross-env NODE_ENV=production npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build next:build", "lint": "eslint ./src/**/*.tsx ./src/**/*.ts", "next:build": "next build", "next:dev": "cross-env NODE_OPTIONS='--inspect' next dev", @@ -24,8 +24,10 @@ "sitecore-tools:generate-map": "sitecore-tools project component generate-map", "sitecore-tools:generate-map:watch": "sitecore-tools project component generate-map --watch", "sitecore-tools:build": "sitecore-tools project build", - "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", - "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start" + "dev": "cross-env NODE_ENV=development npm-run-all --serial sitecore-tools:atoms:validate sitecore-tools:generate-map sitecore-tools:build --parallel next:dev sitecore-tools:generate-map:watch", + "start": "cross-env-shell NODE_ENV=production npm-run-all --serial build next:start", + "sitecore-tools:atoms:validate": "sitecore-tools project atoms validate", + "sitecore-tools:atoms:update": "sitecore-tools project atoms update" }, "dependencies": { "@sitecore-content-sdk/analytics-core": "<%- versions['@sitecore-content-sdk/analytics-core'] %>", diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/sitecore.cli.config.ts b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/sitecore.cli.config.ts index 4f756541f4..c558cc9b57 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/sitecore.cli.config.ts +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/sitecore.cli.config.ts @@ -19,6 +19,11 @@ export default defineCliConfig({ }), ], }, + atoms: { + validation: { + breakOnError: false, + }, + }, componentMap: { paths: ['src/components'], exclude: ['src/components/content-sdk/*'], diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/Providers.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/Providers.tsx index 2bc2eed055..bee8fe4610 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/Providers.tsx +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/Providers.tsx @@ -3,14 +3,19 @@ import React from 'react'; import { Page, SitecoreProvider } from '@sitecore-content-sdk/nextjs'; import scConfig from 'sitecore.config'; import components from '.sitecore/component-map.client'; +import { catalog, registry } from 'src/atoms'; +import { useRouter } from 'next/navigation'; export default function Providers({ children, page }: { children: React.ReactNode; page: Page }) { + const router = useRouter(); + return ( import('.sitecore/import-map.client')} + atomsConfig={{ catalog, registry, navigate: router.push }} > {children} diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/atoms/index.tsx b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/atoms/index.tsx new file mode 100644 index 0000000000..94edbdb118 --- /dev/null +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/src/atoms/index.tsx @@ -0,0 +1,10 @@ +import { defineAtomsCatalog, defineAtomsRegistry } from '@sitecore-content-sdk/nextjs'; + +export const catalog = defineAtomsCatalog({ + components: {}, + actions: {}, +}); + +export const registry = defineAtomsRegistry(catalog, { + components: {}, +}); From 6cdf840307f0f975af88f6cc80e494da530a2122 Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 18:20:15 +0300 Subject: [PATCH 17/32] chore: new versions --- packages/analytics-core/package.json | 4 ++-- packages/cli/package.json | 6 +++--- packages/content/package.json | 8 ++++---- packages/core/package.json | 2 +- packages/create-content-sdk-app/package.json | 2 +- packages/events/package.json | 6 +++--- packages/nextjs/package.json | 20 ++++++++++---------- packages/personalize/package.json | 8 ++++---- packages/react/package.json | 12 ++++++------ packages/search/package.json | 6 +++--- 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/analytics-core/package.json b/packages/analytics-core/package.json index d4a9191531..10ae3eada5 100644 --- a/packages/analytics-core/package.json +++ b/packages/analytics-core/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.1.0-beta.1", "debug": "^4.4.3", "isbot": "^5.1.39" }, @@ -75,5 +75,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.3" + "version": "2.1.0-beta.1" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 14d7bead30..bf7e20920c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/cli", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "description": "Sitecore Content SDK CLI", "main": "dist/cjs/cli.js", "module": "dist/esm/cli.js", @@ -34,8 +34,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/content": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1", "chokidar": "^4.0.3", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", diff --git a/packages/content/package.json b/packages/content/package.json index 954c9c8cc5..3d8ea49558 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/content", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,7 +36,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/events": "2.0.0-beta.3", + "@sitecore-content-sdk/events": "2.1.0-beta.1", "@stylistic/eslint-plugin": "^5.2.2", "@types/chai": "^5.2.2", "@types/chai-spies": "^1.0.6", @@ -73,10 +73,10 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/events": "2.0.0-beta.3" + "@sitecore-content-sdk/events": "2.1.0-beta.1" }, "dependencies": { - "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/core": "2.1.0-beta.1", "chalk": "^4.1.2", "debug": "^4.4.0", "glob": "^11.0.2", diff --git a/packages/core/package.json b/packages/core/package.json index 6c19ca00c3..63d940a9b4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/core", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, diff --git a/packages/create-content-sdk-app/package.json b/packages/create-content-sdk-app/package.json index 8d2d0832bf..fafe0d3d5c 100644 --- a/packages/create-content-sdk-app/package.json +++ b/packages/create-content-sdk-app/package.json @@ -1,6 +1,6 @@ { "name": "create-content-sdk-app", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "description": "Sitecore Content SDK initializer", "bin": "./dist/index.js", "scripts": { diff --git a/packages/events/package.json b/packages/events/package.json index 5c6ff03570..ff8d2de223 100644 --- a/packages/events/package.json +++ b/packages/events/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/Sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1", "debug": "^4.4.3" }, "description": "Enables real-time, unified tracking to send events to Sitecore.", @@ -76,5 +76,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.3" + "version": "2.1.0-beta.1" } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 941207c936..531f078f86 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/nextjs", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -32,8 +32,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/personalize": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/personalize": "2.1.0-beta.1", "@stylistic/eslint-plugin": "^5.2.2", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.3.0", @@ -76,9 +76,9 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/events": "2.0.0-beta.3", - "@sitecore-content-sdk/personalize": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/events": "2.1.0-beta.1", + "@sitecore-content-sdk/personalize": "2.1.0-beta.1", "next": "^16.2.0", "react": "^19.2.1", "react-dom": "^19.2.1", @@ -91,10 +91,10 @@ }, "dependencies": { "@babel/parser": "^7.27.2", - "@sitecore-content-sdk/content": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3", - "@sitecore-content-sdk/events": "2.0.0-beta.3", - "@sitecore-content-sdk/react": "2.0.0-beta.3", + "@sitecore-content-sdk/content": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1", + "@sitecore-content-sdk/events": "2.1.0-beta.1", + "@sitecore-content-sdk/react": "2.1.0-beta.1", "recast": "^0.23.11", "regex-parser": "^2.3.1" }, diff --git a/packages/personalize/package.json b/packages/personalize/package.json index 76ccc4a10b..0a14d6f76e 100644 --- a/packages/personalize/package.json +++ b/packages/personalize/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3", - "@sitecore-content-sdk/events": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1", + "@sitecore-content-sdk/events": "2.1.0-beta.1", "debug": "^4.4.3" }, "description": "Provides personalization capabilities to build tailored experiences for site visitors.", @@ -71,5 +71,5 @@ "api-extractor": "npm run build && api-extractor run --local --verbose", "api-extractor:verify": "api-extractor run" }, - "version": "2.0.0-beta.3" + "version": "2.1.0-beta.1" } diff --git a/packages/react/package.json b/packages/react/package.json index b0ac16eefe..85b08ee132 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/react", - "version": "2.0.0-beta.3", + "version": "2.1.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -32,7 +32,7 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "devDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", "@sitecore-feaas/clientside": "^0.6.0", "@stylistic/eslint-plugin": "^5.2.2", "@testing-library/dom": "^10.4.0", @@ -71,15 +71,15 @@ "typescript": "~5.8.3" }, "peerDependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/events": "2.0.0-beta.3", + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/events": "2.1.0-beta.1", "@sitecore-feaas/clientside": "^0.6.0", "react": "^19.2.1", "react-dom": "^19.2.1" }, "dependencies": { - "@sitecore-content-sdk/content": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3", + "@sitecore-content-sdk/content": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1", "@sitecore-content-sdk/search": "^0.3.0", "fast-deep-equal": "^3.1.3" }, diff --git a/packages/search/package.json b/packages/search/package.json index 5a57c54dcb..7d49a49aa0 100644 --- a/packages/search/package.json +++ b/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "@sitecore-content-sdk/search", - "version": "0.2.0-beta.3", + "version": "0.3.0-beta.1", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "sideEffects": false, @@ -36,8 +36,8 @@ "url": "https://github.com/sitecore/content-sdk/issues" }, "dependencies": { - "@sitecore-content-sdk/analytics-core": "2.0.0-beta.3", - "@sitecore-content-sdk/core": "2.0.0-beta.3" + "@sitecore-content-sdk/analytics-core": "2.1.0-beta.1", + "@sitecore-content-sdk/core": "2.1.0-beta.1" }, "devDependencies": { "@types/chai": "^5.2.3", From f6fefbe010a76ad557958bd0efd21a97dfd674de Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 19:21:51 +0300 Subject: [PATCH 18/32] chore: dependencies and test --- packages/content/package.json | 1 + packages/react/package.json | 2 + packages/react/src/tests/jsdom-setup.ts | 7 + yarn.lock | 3418 +++++++++++------------ 4 files changed, 1574 insertions(+), 1854 deletions(-) diff --git a/packages/content/package.json b/packages/content/package.json index 3d8ea49558..ad20c02c90 100644 --- a/packages/content/package.json +++ b/packages/content/package.json @@ -76,6 +76,7 @@ "@sitecore-content-sdk/events": "2.1.0-beta.1" }, "dependencies": { + "@json-render/core": "0.19.0", "@sitecore-content-sdk/core": "2.1.0-beta.1", "chalk": "^4.1.2", "debug": "^4.4.0", diff --git a/packages/react/package.json b/packages/react/package.json index 85b08ee132..2c42a55214 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -78,6 +78,8 @@ "react-dom": "^19.2.1" }, "dependencies": { + "@json-render/core": "0.19.0", + "@json-render/react": "0.19.0", "@sitecore-content-sdk/content": "2.1.0-beta.1", "@sitecore-content-sdk/core": "2.1.0-beta.1", "@sitecore-content-sdk/search": "^0.3.0", diff --git a/packages/react/src/tests/jsdom-setup.ts b/packages/react/src/tests/jsdom-setup.ts index ea23ec0c31..db25593d6a 100644 --- a/packages/react/src/tests/jsdom-setup.ts +++ b/packages/react/src/tests/jsdom-setup.ts @@ -36,3 +36,10 @@ global.jsdom = jsdom; global.HTMLElement = jsDomWindow.HTMLElement; // makes chai "happy" https://github.com/chaijs/chai/issues/1029 copyProps(jsDomWindow, global); + +// jsdom does not implement requestAnimationFrame; provide a minimal stub +if (typeof global.requestAnimationFrame === 'undefined') { + global.requestAnimationFrame = (cb: FrameRequestCallback) => + setTimeout(cb, 0) as unknown as number; + global.cancelAnimationFrame = (id: number) => clearTimeout(id); +} diff --git a/yarn.lock b/yarn.lock index 426b58fda5..2c81dfefb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,57 +18,57 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.28.6, @babel/code-frame@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/code-frame@npm:7.29.0" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" dependencies: "@babel/helper-validator-identifier": "npm:^7.29.7" js-tokens: "npm:^4.0.0" picocolors: "npm:^1.1.1" - checksum: 10/199e15ff89007dd30675655eec52481cb245c9fdf4f81e4dc1f866603b0217b57aff25f5ffa0a95bbc8e31eb861695330cd7869ad52cc211aa63016320ef72c5 + checksum: 10/84da552e51a55795a50b3589116edb2f9e368a647d266380683775f18effd9acd4521b0246bebd0b049a7f32af1f87b1e8475d3bcb665f876bd04ade8da99697 languageName: node linkType: hard -"@babel/compat-data@npm:^7.28.6": - version: 7.29.0 - resolution: "@babel/compat-data@npm:7.29.0" - checksum: 10/7f21beedb930ed8fbf7eabafc60e6e6521c1d905646bf1317a61b2163339157fe797efeb85962bf55136e166b01fd1a6b526a15974b92a8b877d564dcb6c9580 +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10/ad2272714087f68970977f6e2b53597a8503fc9c3028c4a91686474bd77a707dd00903cdde4b73788972016d1bad4dc3fa4e5ff38e1ed8f1c3bde1095352973a languageName: node linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9, @babel/core@npm:^7.27.4": - version: 7.29.0 - resolution: "@babel/core@npm:7.29.0" - dependencies: - "@babel/code-frame": "npm:^7.29.0" - "@babel/generator": "npm:^7.29.0" - "@babel/helper-compilation-targets": "npm:^7.28.6" - "@babel/helper-module-transforms": "npm:^7.28.6" - "@babel/helpers": "npm:^7.28.6" - "@babel/parser": "npm:^7.29.0" - "@babel/template": "npm:^7.28.6" - "@babel/traverse": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" "@jridgewell/remapping": "npm:^2.3.5" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/25f4e91688cdfbaf1365831f4f245b436cdaabe63d59389b75752013b8d61819ee4257101b52fc328b0546159fd7d0e74457ed7cf12c365fea54be4fb0a40229 + checksum: 10/38e71cf81db790b0bb2a3a0c8140c2b1c87576b61dc6be676de4fab8c3be871af590a739e8c489fe8e8f9a8e5899fa11e35e59e9e09d40b259c6a675f2f98928 languageName: node linkType: hard -"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.0, @babel/generator@npm:^7.7.2": - version: 7.29.1 - resolution: "@babel/generator@npm:7.29.1" +"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7, @babel/generator@npm:^7.7.2": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" dependencies: - "@babel/parser": "npm:^7.29.0" - "@babel/types": "npm:^7.29.0" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" "@jridgewell/gen-mapping": "npm:^0.3.12" "@jridgewell/trace-mapping": "npm:^0.3.28" jsesc: "npm:^3.0.2" - checksum: 10/61fe4ddd6e817aa312a14963ccdbb5c9a8c57e8b97b98d19a8a99ccab2215fda1a5f52bc8dd8d2e3c064497ddeb3ab8ceb55c76fa0f58f8169c34679d2256fe0 + checksum: 10/60fb0432ebeab791b2d68e5fc49da6f8e8b68bcc4751211ccf08ac0101e9dcaddefd0cbbbd488afb1c1517515c7c3e76f63d9b05d06deaeb008afd499488db9c languageName: node linkType: hard @@ -143,24 +143,24 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.28.6": - version: 7.29.2 - resolution: "@babel/helpers@npm:7.29.2" +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" dependencies: - "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.29.0" - checksum: 10/ad77706f3f917bd224e037fd0fbc67c45b240d2a45981321b093f70b7c535bee9bbddb0a19e34c362cb000ae21cdd8638f8a87a5f305a5bd7547e93fdcc524fe + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10/b4d1ef12c19e896585c009ba29677839097ff04f8b11a2430d335c3fb6bd667b4f9e96a3b185a083fdde6b1137eabbbf2600c32425cb69cefc81d81d5cfe425d languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": - version: 7.29.2 - resolution: "@babel/parser@npm:7.29.2" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" dependencies: - "@babel/types": "npm:^7.29.0" + "@babel/types": "npm:^7.29.7" bin: parser: ./bin/babel-parser.js - checksum: 10/45d050bf75aa5194b3255f156173e8553d615ff5a2434674cc4a10cdc7c261931befb8618c996a1c449b87f0ef32a3407879af2ac967d95dc7b4fdbae7037efa + checksum: 10/da40c5928c95997b01aabe84fc3440881b8f20b866714fefa142961d37e82ffc03fbb9afed706f15f8a688278f95286ca0cea0d87ad6c77600f8c6c45d9824ee languageName: node linkType: hard @@ -351,10 +351,10 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.12.5": - version: 7.29.2 - resolution: "@babel/runtime@npm:7.29.2" - checksum: 10/f55ba4052aa0255055b34371a145fbe69c29b37b49eaea14805b095bfb4153701486416e89392fd27ec8abafa53868be86e960b9f8f959fff91f2c8ac2a14b02 +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.5.5": + version: 7.29.7 + resolution: "@babel/runtime@npm:7.29.7" + checksum: 10/9883b4951787779fd382b121f22f92966d85f19434841f65fb00b2dfec232107e139683f47c6f252891826ad8ee18317b46c3a0e4819116a9885f47b46d7126a languageName: node linkType: hard @@ -369,28 +369,28 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.28.6, @babel/traverse@npm:^7.29.0": - version: 7.29.0 - resolution: "@babel/traverse@npm:7.29.0" +"@babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" dependencies: - "@babel/code-frame": "npm:^7.29.0" - "@babel/generator": "npm:^7.29.0" - "@babel/helper-globals": "npm:^7.28.0" - "@babel/parser": "npm:^7.29.0" - "@babel/template": "npm:^7.28.6" - "@babel/types": "npm:^7.29.0" + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" debug: "npm:^4.3.1" - checksum: 10/3a0d0438f1ba9fed4fbe1706ea598a865f9af655a16ca9517ab57bda526e224569ca1b980b473fb68feea5e08deafbbf2cf9febb941f92f2d2533310c3fc4abc + checksum: 10/ce24086a7dd8c408cbdb159437d3c8e02464a6d32b320d884fa742e2c5a3344b9342a923c7a371fc1789b4d82a59972a7008b5d8bbc1bc0c5ae42a39b28dc7f6 languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.6, @babel/types@npm:^7.29.0, @babel/types@npm:^7.3.3": - version: 7.29.0 - resolution: "@babel/types@npm:7.29.0" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.3.3": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" dependencies: - "@babel/helper-string-parser": "npm:^7.27.1" - "@babel/helper-validator-identifier": "npm:^7.28.5" - checksum: 10/bfc2b211210f3894dcd7e6a33b2d1c32c93495dc1e36b547376aa33441abe551ab4bc1640d4154ee2acd8e46d3bbc925c7224caae02fcaf0e6a771e97fccc661 + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10/bd4f5635db1057bd0abeebf93eb3ae38399e152271cea8dce8288350f0afa13ed3e2db2e16e22bd3303068890eec18965a83420539afbe0dde31432b4cf9636d languageName: node linkType: hard @@ -689,33 +689,87 @@ __metadata: languageName: node linkType: hard -"@emnapi/core@npm:^1.4.3": - version: 1.9.1 - resolution: "@emnapi/core@npm:1.9.1" +"@emnapi/core@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/core@npm:1.10.0" dependencies: - "@emnapi/wasi-threads": "npm:1.2.0" + "@emnapi/wasi-threads": "npm:1.2.1" tslib: "npm:^2.4.0" - checksum: 10/c44cfe471702b43306b84d0f4f2f1506dac0065dbd73dc5a41bd99a2c39802ca7e2d7ebfbfae8997468d1ff0420603596bf35b19eabd5951bad1eb630d2d4574 + checksum: 10/d32f386084e64deaf2609aabb8295d1ad5af6144d0f46d2060b76cc53f1f3b486df54bec9b0f33c37d85a3822e1193ebcd4e3deb4a5f0e4cd650aa2ffc631715 languageName: node linkType: hard -"@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.0": - version: 1.9.1 - resolution: "@emnapi/runtime@npm:1.9.1" +"@emnapi/core@npm:1.4.5": + version: 1.4.5 + resolution: "@emnapi/core@npm:1.4.5" dependencies: "@emnapi/wasi-threads": "npm:1.0.4" tslib: "npm:^2.4.0" - checksum: 10/337767fa44ec1f6277494342664be8773f16aad4086e9e49423a9f06c5eee7495e2e1b0b50dcd764c5a5cc4c15c9d80c13fba2da6763a97c06a48115cd7ccd14 + checksum: 10/412322102dc861e8aa78123ae20560ac980362a220c736fe59ddea3228d490757780ea4cdc3bd54903a5ca2a92085f119e42f2c07f60e2aec2c0b8a69ea094c0 languageName: node linkType: hard -"@emnapi/wasi-threads@npm:1.2.0": - version: 1.2.0 - resolution: "@emnapi/wasi-threads@npm:1.2.0" +"@emnapi/core@npm:^1.1.0": + version: 1.11.1 + resolution: "@emnapi/core@npm:1.11.1" dependencies: "@emnapi/wasi-threads": "npm:1.2.2" tslib: "npm:^2.4.0" - checksum: 10/c8e48c7200530744dc58170d2e25933b61433e4a0c50b4f192f5d8d4b065c7023dbfc48dac0afadbc29bd239013f2ae454c6e54e0ca6e8248402bf95c9e77e22 + checksum: 10/9aba37e0c11a75ef8372fd0a9c6e5396f4e8c1ebdd6fee737414787610a9dc1cd9bf188f525153561ca9363896e1135dd240f1ce28f3470dba3ad7e683e6db1a + languageName: node + linkType: hard + +"@emnapi/runtime@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/runtime@npm:1.10.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/d21083d07fa0c2da171c142e78ef986b66b07d45b06accc0bcaf49fcc61bb4dbc10e1c1760813070165b9f49b054376a931045347f21c0f42ff1eb2d2040faac + languageName: node + linkType: hard + +"@emnapi/runtime@npm:1.4.5": + version: 1.4.5 + resolution: "@emnapi/runtime@npm:1.4.5" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/1d6f406ff116d2363e60aef3ed49eb8d577387f4941abea508ba376900d8831609d5cce92a58076b1a9613f8e83c75c2e3fea71e4fbcdbe06019876144c2559b + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.7.0": + version: 1.11.1 + resolution: "@emnapi/runtime@npm:1.11.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/8f7c622a49314df4d07952110e108e83b0fe129a8ddb9ef1e0ae372d754616169d5b0dd47a0d354a0fea2612abe42cedb582d15916936d1320c6c468acc804cc + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.0.4": + version: 1.0.4 + resolution: "@emnapi/wasi-threads@npm:1.0.4" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/86688f416095b59d8d3e5ea2d8b5574a7c180257fe0c067c7a492f3de2cf5ebc2c8b00af17d6341c7555c614266d3987f332015d7ce6e88b234a9a314e66f396 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.2.1": + version: 1.2.1 + resolution: "@emnapi/wasi-threads@npm:1.2.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/57cd4292be81c05d26aa886d68a9e4c449ff666e8503fed6463dfc6b64a4e4213f03c152d53296b7cda32840271e38cd33347332070658f01befeb9bf4e59f36 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.2.2": + version: 1.2.2 + resolution: "@emnapi/wasi-threads@npm:1.2.2" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/297fb6b1d89744bd0b41d5fec32bade05dc8dcf1f70eba86527226609fb3f6ad3fa96b3b2377b7449709715b3bd1569654c9def1dbbc85fb6b9cb0cff5bc5ebf languageName: node linkType: hard @@ -752,184 +806,366 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/aix-ppc64@npm:0.27.4" +"@esbuild/aix-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/aix-ppc64@npm:0.27.7" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/aix-ppc64@npm:0.28.1" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/android-arm64@npm:0.27.4" +"@esbuild/android-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm64@npm:0.27.7" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/android-arm@npm:0.27.4" +"@esbuild/android-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-arm64@npm:0.28.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-arm@npm:0.27.7" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-arm@npm:0.28.1" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/android-x64@npm:0.27.4" +"@esbuild/android-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/android-x64@npm:0.27.7" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/darwin-arm64@npm:0.27.4" +"@esbuild/android-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/android-x64@npm:0.28.1" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-arm64@npm:0.27.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/darwin-arm64@npm:0.28.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/darwin-x64@npm:0.27.4" +"@esbuild/darwin-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/darwin-x64@npm:0.27.7" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/freebsd-arm64@npm:0.27.4" +"@esbuild/darwin-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/darwin-x64@npm:0.28.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-arm64@npm:0.27.7" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/freebsd-arm64@npm:0.28.1" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/freebsd-x64@npm:0.27.4" +"@esbuild/freebsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/freebsd-x64@npm:0.27.7" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-arm64@npm:0.27.4" +"@esbuild/freebsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/freebsd-x64@npm:0.28.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm64@npm:0.27.7" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-arm64@npm:0.28.1" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-arm@npm:0.27.4" +"@esbuild/linux-arm@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-arm@npm:0.27.7" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-ia32@npm:0.27.4" +"@esbuild/linux-arm@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-arm@npm:0.28.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ia32@npm:0.27.7" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-ia32@npm:0.28.1" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-loong64@npm:0.27.4" +"@esbuild/linux-loong64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-loong64@npm:0.27.7" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-mips64el@npm:0.27.4" +"@esbuild/linux-loong64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-loong64@npm:0.28.1" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-mips64el@npm:0.27.7" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-mips64el@npm:0.28.1" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-ppc64@npm:0.27.4" +"@esbuild/linux-ppc64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-ppc64@npm:0.27.7" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-riscv64@npm:0.27.4" +"@esbuild/linux-ppc64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-ppc64@npm:0.28.1" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-riscv64@npm:0.27.7" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-riscv64@npm:0.28.1" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-s390x@npm:0.27.4" +"@esbuild/linux-s390x@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-s390x@npm:0.27.7" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/linux-x64@npm:0.27.4" +"@esbuild/linux-s390x@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-s390x@npm:0.28.1" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/linux-x64@npm:0.27.7" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/linux-x64@npm:0.28.1" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/netbsd-arm64@npm:0.27.4" +"@esbuild/netbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-arm64@npm:0.27.7" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/netbsd-x64@npm:0.27.4" +"@esbuild/netbsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/netbsd-arm64@npm:0.28.1" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/netbsd-x64@npm:0.27.7" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/netbsd-x64@npm:0.28.1" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/openbsd-arm64@npm:0.27.4" +"@esbuild/openbsd-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-arm64@npm:0.27.7" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/openbsd-x64@npm:0.27.4" +"@esbuild/openbsd-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openbsd-arm64@npm:0.28.1" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openbsd-x64@npm:0.27.7" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openbsd-x64@npm:0.28.1" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openharmony-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/openharmony-arm64@npm:0.27.4" +"@esbuild/openharmony-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/openharmony-arm64@npm:0.27.7" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/sunos-x64@npm:0.27.4" +"@esbuild/openharmony-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/openharmony-arm64@npm:0.28.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/sunos-x64@npm:0.27.7" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/sunos-x64@npm:0.28.1" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/win32-arm64@npm:0.27.4" +"@esbuild/win32-arm64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-arm64@npm:0.27.7" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/win32-ia32@npm:0.27.4" +"@esbuild/win32-arm64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-arm64@npm:0.28.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-ia32@npm:0.27.7" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-ia32@npm:0.28.1" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.27.4": - version: 0.27.4 - resolution: "@esbuild/win32-x64@npm:0.27.4" +"@esbuild/win32-x64@npm:0.27.7": + version: 0.27.7 + resolution: "@esbuild/win32-x64@npm:0.27.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.28.1": + version: 0.28.1 + resolution: "@esbuild/win32-x64@npm:0.28.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -952,9 +1188,6 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.21.2": - version: 0.21.2 - resolution: "@eslint/config-array@npm:0.21.2" "@eslint/config-array@npm:^0.21.2": version: 0.21.2 resolution: "@eslint/config-array@npm:0.21.2" @@ -962,8 +1195,6 @@ __metadata: "@eslint/object-schema": "npm:^2.1.7" debug: "npm:^4.3.1" minimatch: "npm:^3.1.5" - checksum: 10/148477ba995cf57fc725601916d5a7914aa249112d8bec2c3ac9122e2b2f540e6ef013ff4f6785346a4b565f09b20db127fa6f7322f5ffbdb3f1f8d2078a531c - minimatch: "npm:^3.1.5" checksum: 10/148477ba995cf57fc725601916d5a7914aa249112d8bec2c3ac9122e2b2f540e6ef013ff4f6785346a4b565f09b20db127fa6f7322f5ffbdb3f1f8d2078a531c languageName: node linkType: hard @@ -986,14 +1217,10 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.5": - version: 3.3.5 - resolution: "@eslint/eslintrc@npm:3.3.5" "@eslint/eslintrc@npm:^3.3.5": version: 3.3.5 resolution: "@eslint/eslintrc@npm:3.3.5" dependencies: - ajv: "npm:^6.14.0" ajv: "npm:^6.14.0" debug: "npm:^4.3.2" espree: "npm:^10.0.1" @@ -1002,17 +1229,11 @@ __metadata: import-fresh: "npm:^3.2.1" js-yaml: "npm:^4.1.1" minimatch: "npm:^3.1.5" - minimatch: "npm:^3.1.5" strip-json-comments: "npm:^3.1.1" checksum: 10/edabb65693d82a88cac3b2cf932a0f825e986b5e0a21ef08782d07e3a61ad87d39db67cfd5aeb146fd5053e5e24e389dbe5649ab22936a71d633c7b32a7e6d86 - checksum: 10/edabb65693d82a88cac3b2cf932a0f825e986b5e0a21ef08782d07e3a61ad87d39db67cfd5aeb146fd5053e5e24e389dbe5649ab22936a71d633c7b32a7e6d86 languageName: node linkType: hard -"@eslint/js@npm:9.39.4": - version: 9.39.4 - resolution: "@eslint/js@npm:9.39.4" - checksum: 10/0a7ab4c4108cf2cadf66849ebd20f5957cc53052b88d8807d0b54e489dbf6ffcaf741e144e7f9b187c395499ce2e6ddc565dbfa4f60c6df455cf2b30bcbdc5a3 "@eslint/js@npm:9.39.4": version: 9.39.4 resolution: "@eslint/js@npm:9.39.4" @@ -1037,38 +1258,23 @@ __metadata: languageName: node linkType: hard -"@gar/promise-retry@npm:^1.0.0": +"@gar/promise-retry@npm:^1.0.0, @gar/promise-retry@npm:^1.0.2": version: 1.0.3 resolution: "@gar/promise-retry@npm:1.0.3" checksum: 10/0d13ea3bb1025755e055648f6e290d2a7e0c87affaf552218f09f66b3fcd9ea9d5c9cc5fe2aa6e285e1530437768e40f9448fe9a86f4f3417b216dcf488d3d1a languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 10/052dd232140fa60e81588000cbe729a40146579b361f1070bce63e2a761388a22a16d00beeffc504bd3601cb8e055c57b21a185448b3ed550cf50716f4fd442e - languageName: node - linkType: hard - -"@gerrit0/mini-shiki@npm:^3.23.0": - version: 3.23.0 - resolution: "@gerrit0/mini-shiki@npm:3.23.0" "@gerrit0/mini-shiki@npm:^3.23.0": version: 3.23.0 resolution: "@gerrit0/mini-shiki@npm:3.23.0" dependencies: - "@shikijs/engine-oniguruma": "npm:^3.23.0" - "@shikijs/langs": "npm:^3.23.0" - "@shikijs/themes": "npm:^3.23.0" - "@shikijs/types": "npm:^3.23.0" "@shikijs/engine-oniguruma": "npm:^3.23.0" "@shikijs/langs": "npm:^3.23.0" "@shikijs/themes": "npm:^3.23.0" "@shikijs/types": "npm:^3.23.0" "@shikijs/vscode-textmate": "npm:^10.0.2" checksum: 10/d44dac7c3f58ba136285b3c15c8a89eba120f7a493dddcf6178601ab8c0a5286da6a19515100297934288c41ae225f21013a4c395fe2f656b58f812dd13ccc1e - checksum: 10/d44dac7c3f58ba136285b3c15c8a89eba120f7a493dddcf6178601ab8c0a5286da6a19515100297934288c41ae225f21013a4c395fe2f656b58f812dd13ccc1e languageName: node linkType: hard @@ -1130,9 +1336,6 @@ __metadata: linkType: hard "@img/colour@npm:^1.0.0": - version: 1.1.0 - resolution: "@img/colour@npm:1.1.0" - checksum: 10/2a29be7b06b046bd33c80ffa0f3493b7535b0841a69f54af81bf5e2d8867f21704fab42e9cf24ec30c089de34f790bae4dad1fc617c3163fbf264998dc316f0a version: 1.1.0 resolution: "@img/colour@npm:1.1.0" checksum: 10/2a29be7b06b046bd33c80ffa0f3493b7535b0841a69f54af81bf5e2d8867f21704fab42e9cf24ec30c089de34f790bae4dad1fc617c3163fbf264998dc316f0a @@ -1627,13 +1830,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/cliui@npm:^9.0.0": - version: 9.0.0 - resolution: "@isaacs/cliui@npm:9.0.0" - checksum: 10/8ea3d1009fd29071419209bb91ede20cf27e6e2a1630c5e0702d8b3f47f9e1a3f1c5a587fa2cb96d22d18219790327df49db1bcced573346bbaf4577cf46b643 - languageName: node - linkType: hard - "@isaacs/fs-minipass@npm:^4.0.0": version: 4.0.1 resolution: "@isaacs/fs-minipass@npm:4.0.1" @@ -1725,22 +1921,29 @@ __metadata: languageName: node linkType: hard -"@jest/diff-sequences@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/diff-sequences@npm:30.3.0" - checksum: 10/0d5b6e1599c5e0bb702f0804e7f93bbe4911b5929c40fd6a77c06105711eae24d709c8964e8d623cc70c34b7dc7262d76a115a6eb05f1576336cdb6c46593e7c +"@jest/diff-sequences@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/diff-sequences@npm:30.0.1" + checksum: 10/0ddb7c7ba92d6057a2ee51a9cfc2155b77cca707fe959167466ea02dcb0687018cc3c22b9622f25f3a417d6ad370e2d4dcfedf9f1410dc9c02954a7484423cc7 + languageName: node + linkType: hard + +"@jest/diff-sequences@npm:30.4.0": + version: 30.4.0 + resolution: "@jest/diff-sequences@npm:30.4.0" + checksum: 10/65c27937c10a7157899dad5d176806104286f9d55464f318955a0cee98db8aed6b8f70ad4aee7133468087146422cdd391d49b1e101ec543db3283ee4eb59c06 languageName: node linkType: hard -"@jest/environment@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/environment@npm:30.3.0" +"@jest/environment@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/environment@npm:30.4.1" dependencies: - "@jest/fake-timers": "npm:30.3.0" - "@jest/types": "npm:30.3.0" + "@jest/fake-timers": "npm:30.4.1" + "@jest/types": "npm:30.4.1" "@types/node": "npm:*" - jest-mock: "npm:30.3.0" - checksum: 10/9b64add2e5430411ca997aed23cd34786d0e87562f5930ad0d4160df51435ae061809fcaa6bbc6c0ff9f0ba5f1241a5ce9a32ec772fa1d7c6b022f0169b622a4 + jest-mock: "npm:30.4.1" + checksum: 10/c25946fee29604f5aa24ea059bc3cc7bc4c8cdaf26db1ed6ffa4f28e37f5193cc4e868650c807d89caff4123e44d07b58200d4cb5960ebdb7d66531509d76359 languageName: node linkType: hard @@ -1756,12 +1959,12 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/expect-utils@npm:30.3.0" +"@jest/expect-utils@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/expect-utils@npm:30.4.1" dependencies: "@jest/get-type": "npm:30.1.0" - checksum: 10/766fd24f527a13004c542c2642b68b9142270801ab20bd448a559d9c2f40af079d0eb9ec9520a47f97b4d6c7d0837ba46e86284f53c939f11d9fcbda73a11e19 + checksum: 10/3f0337ec791d669cacd07594521f2da71b956712dfd0c0007253dd5e886ef640df510af1357878a80ac56f09d3db9fd68e3db66959f0fdb3add5f551dd7e0f35 languageName: node linkType: hard @@ -1774,13 +1977,13 @@ __metadata: languageName: node linkType: hard -"@jest/expect@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/expect@npm:30.3.0" +"@jest/expect@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/expect@npm:30.4.1" dependencies: - expect: "npm:30.3.0" - jest-snapshot: "npm:30.3.0" - checksum: 10/74832945a2b18c7b962b27e0ca4d25d19a29d1c3ca6fe4a9c23946025b4146799e62a81d50060ac7bcaf7036fb477aa350ddf300e215333b42d013a3d9f8ba2b + expect: "npm:30.4.1" + jest-snapshot: "npm:30.4.1" + checksum: 10/40ae0317a3590ced7a7fd21c49e6b1af6b122e6a83822e643af83f02034dfed6485248cae08d6bcf9380039ba3824ac56db18478712c64ddf5f709ee23cf30cd languageName: node linkType: hard @@ -1794,17 +1997,17 @@ __metadata: languageName: node linkType: hard -"@jest/fake-timers@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/fake-timers@npm:30.3.0" +"@jest/fake-timers@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/fake-timers@npm:30.4.1" dependencies: - "@jest/types": "npm:30.3.0" - "@sinonjs/fake-timers": "npm:^15.0.0" + "@jest/types": "npm:30.4.1" + "@sinonjs/fake-timers": "npm:^15.4.0" "@types/node": "npm:*" - jest-message-util: "npm:30.3.0" - jest-mock: "npm:30.3.0" - jest-util: "npm:30.3.0" - checksum: 10/e39d30b61ae85485bfa0b1d86d62d866d33964bf0b95b8b4f45d2f1f1baa94fd7e134c7729370a58cb67b58d2b860fb396290b5c271782ed4d3728341027549b + jest-message-util: "npm:30.4.1" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + checksum: 10/bc7aff23548395d6e7957bc24f699f921a9616f2357ab49616b0468c7b5e94e6ac4cbdd45d306f1a5d7f72e2a055294f52be3666e4c1da7c137874c5b226e1c6 languageName: node linkType: hard @@ -1842,14 +2045,14 @@ __metadata: linkType: hard "@jest/globals@npm:^30.2.0": - version: 30.3.0 - resolution: "@jest/globals@npm:30.3.0" + version: 30.4.1 + resolution: "@jest/globals@npm:30.4.1" dependencies: - "@jest/environment": "npm:30.3.0" - "@jest/expect": "npm:30.3.0" - "@jest/types": "npm:30.3.0" - jest-mock: "npm:30.3.0" - checksum: 10/485bdc0f35faf3e76cb451b75e16892d87f7ab5757e290b1a9e849a3af0ef81c47abddb188fbc0442a4689514cf0551e34d13970c9cf03610a269c39f800ff46 + "@jest/environment": "npm:30.4.1" + "@jest/expect": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + jest-mock: "npm:30.4.1" + checksum: 10/5fe04b9c3b97f0061e4464201ee0dd674dd958843eb80542791d1c576c51f12aaa3f3b369e136d5fd3f8c716f9c9bbfbb76491a3cbc3c4efb3cc71063f909132 languageName: node linkType: hard @@ -1918,15 +2121,15 @@ __metadata: languageName: node linkType: hard -"@jest/snapshot-utils@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/snapshot-utils@npm:30.3.0" +"@jest/snapshot-utils@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/snapshot-utils@npm:30.4.1" dependencies: - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" chalk: "npm:^4.1.2" graceful-fs: "npm:^4.2.11" natural-compare: "npm:^1.4.0" - checksum: 10/2214d4f0f33d2363a0785c0ba75066bf4ed4beefd5b2d2a5c3124d66ab92f91163f03696be625223bdb0527f1e6360c4b306ba9ae421aeb966d4a57d6d972099 + checksum: 10/8f17768702153267388b3043f358027385e591ac4668699bfce3547cb8e08ac146a074913bcddf68c0a4f7155e24a6d582d27f4592f5c3bd5f9fbc3f9182ef78 languageName: node linkType: hard @@ -1965,25 +2168,25 @@ __metadata: languageName: node linkType: hard -"@jest/transform@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/transform@npm:30.3.0" +"@jest/transform@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/transform@npm:30.4.1" dependencies: "@babel/core": "npm:^7.27.4" - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" "@jridgewell/trace-mapping": "npm:^0.3.25" babel-plugin-istanbul: "npm:^7.0.1" chalk: "npm:^4.1.2" convert-source-map: "npm:^2.0.0" fast-json-stable-stringify: "npm:^2.1.0" graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.3.0" - jest-regex-util: "npm:30.0.1" - jest-util: "npm:30.3.0" + jest-haste-map: "npm:30.4.1" + jest-regex-util: "npm:30.4.0" + jest-util: "npm:30.4.1" pirates: "npm:^4.0.7" slash: "npm:^3.0.0" write-file-atomic: "npm:^5.0.1" - checksum: 10/279b6b73f59c274d7011febcbc0a1fa8939e8f677801a0a9bd95b9cf49244957267f3769c8cd541ae8026d8176089cd5e55f0f8d5361ec7788970978f4f394b4 + checksum: 10/7b570451f6c26360f1b852c2281dcc4e36fe685dbc159cf5eabf83d49d6aae4569f444d38f3afb5b3b6e0b809eb41b65f3145c0cac5fee3eec9c9b178fb1f0ea languageName: node linkType: hard @@ -2010,9 +2213,9 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:30.3.0": - version: 30.3.0 - resolution: "@jest/types@npm:30.3.0" +"@jest/types@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/types@npm:30.4.1" dependencies: "@jest/pattern": "npm:30.4.0" "@jest/schemas": "npm:30.4.1" @@ -2021,7 +2224,7 @@ __metadata: "@types/node": "npm:*" "@types/yargs": "npm:^17.0.33" chalk: "npm:^4.1.2" - checksum: 10/d6943cc270f07c7bc1ee6f3bb9ad1263ce7897d1a282221bf1d27499d77f2a68cfa6625ca73c193d3f81fe22a8e00635cd7acb5e73a546965c172219c81ec12c + checksum: 10/cc0999508613487c6d0f55661cd342ebe7cfe579fa9917534b94310204358f03f94524f70f00b4fe3c6dd2ccd0fd44657615a1b9f420ab310d68b43964bff87c languageName: node linkType: hard @@ -2093,6 +2296,28 @@ __metadata: languageName: node linkType: hard +"@json-render/core@npm:0.19.0, @json-render/core@npm:^0.19.0": + version: 0.19.0 + resolution: "@json-render/core@npm:0.19.0" + dependencies: + zod: "npm:^4.3.6" + peerDependencies: + zod: ^4.0.0 + checksum: 10/fa6f9733b5275d5e0c4f6fad4c7271e49e962b1e9a18af60b549767404d555795b4d7159c19cb6781e4ea535df9a6dc1d83391d69eacf22e36b04fd44d5c6ae5 + languageName: node + linkType: hard + +"@json-render/react@npm:^0.19.0": + version: 0.19.0 + resolution: "@json-render/react@npm:0.19.0" + dependencies: + "@json-render/core": "npm:0.19.0" + peerDependencies: + react: ^19.2.3 + checksum: 10/52f53740204521f04749fb93b6aa4e7181387baab9588c213781017e2345e8ff1711074b2720c9603a43103864b3c128a1c96a089d6132c6d71d50c3817821a9 + languageName: node + linkType: hard + "@manypkg/find-root@npm:^1.1.0": version: 1.1.0 resolution: "@manypkg/find-root@npm:1.1.0" @@ -2119,824 +2344,62 @@ __metadata: languageName: node linkType: hard -"@lerna/changed@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/changed@npm:5.6.2" +"@microsoft/api-extractor-model@npm:7.33.8": + version: 7.33.8 + resolution: "@microsoft/api-extractor-model@npm:7.33.8" dependencies: - "@lerna/collect-updates": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/listable": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - checksum: 10/0867f1b8354a345d1d7c26394aea617c4d585b3fc181afab398586ec27dd4bfcc23cd9078caca64689dd0a545158b1798184e3dce419590302b9e9db0e946b47 + "@microsoft/tsdoc": "npm:~0.16.0" + "@microsoft/tsdoc-config": "npm:~0.18.1" + "@rushstack/node-core-library": "npm:5.23.1" + checksum: 10/0f054bfa82507fe055754c087eb090922a4fb02a6dd7d5bd7c7e2c212a4723fd942cae90e62a3a8db2e64ec4f217b88676b082e0635615d40da4ccbb2fac5e4a languageName: node linkType: hard -"@lerna/check-working-tree@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/check-working-tree@npm:5.6.2" +"@microsoft/api-extractor@npm:^7.55.0": + version: 7.58.9 + resolution: "@microsoft/api-extractor@npm:7.58.9" dependencies: - "@lerna/collect-uncommitted": "npm:5.6.2" - "@lerna/describe-ref": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - checksum: 10/46a30143ab3f73f8e70c76bdffa66d521b787251c986800f60335188a62375186a380c0d772439b0fa9cf057da2f028780674744d684636e84e6974b9a4193e5 + "@microsoft/api-extractor-model": "npm:7.33.8" + "@microsoft/tsdoc": "npm:~0.16.0" + "@microsoft/tsdoc-config": "npm:~0.18.1" + "@rushstack/node-core-library": "npm:5.23.1" + "@rushstack/rig-package": "npm:0.7.3" + "@rushstack/terminal": "npm:0.24.0" + "@rushstack/ts-command-line": "npm:5.3.10" + diff: "npm:~8.0.2" + minimatch: "npm:10.2.3" + resolve: "npm:~1.22.1" + semver: "npm:~7.7.4" + source-map: "npm:~0.6.1" + typescript: "npm:5.9.3" + bin: + api-extractor: bin/api-extractor + checksum: 10/30c3a10834074308ce82fc3a78694e4c173bfd8783ac32e2779ca2c785e83e4be81e95ee421948d730eb83b5ce86184c59367d9cc3367832dd3cd61be1209f24 languageName: node linkType: hard -"@lerna/child-process@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/child-process@npm:5.6.2" +"@microsoft/tsdoc-config@npm:~0.18.1": + version: 0.18.1 + resolution: "@microsoft/tsdoc-config@npm:0.18.1" dependencies: - chalk: "npm:^4.1.0" - execa: "npm:^5.0.0" - strong-log-transformer: "npm:^2.1.0" - checksum: 10/94e9c03119b3177cb41e210ac8a4bf04386857192e3a99c8bdd3d2ae913eda1538f813138de03693681ee360644cab9a0584658df9e2acbd04eb52a2e3a761cf + "@microsoft/tsdoc": "npm:0.16.0" + ajv: "npm:~8.18.0" + jju: "npm:~1.4.0" + resolve: "npm:~1.22.2" + checksum: 10/1912c4d80af10c548897dafd2b76127a53d5154001fb63029d4414d57022bf0f0aced325d8a3a0970454bf651d506236b4d26c1c473a933ea77382bce67b1236 languageName: node linkType: hard -"@lerna/clean@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/clean@npm:5.6.2" - dependencies: - "@lerna/command": "npm:5.6.2" - "@lerna/filter-options": "npm:5.6.2" - "@lerna/prompt": "npm:5.6.2" - "@lerna/pulse-till-done": "npm:5.6.2" - "@lerna/rimraf-dir": "npm:5.6.2" - p-map: "npm:^4.0.0" - p-map-series: "npm:^2.1.0" - p-waterfall: "npm:^2.1.1" - checksum: 10/b20aa2d5c0ace554dcb2ce37915b6a172627e1d26f54a6be33ae8b59d2b31ac1c4c70fa99ca5bffefc9a725ef798059b3b83f751728f6471e9edee1cb901d8b9 +"@microsoft/tsdoc@npm:0.16.0, @microsoft/tsdoc@npm:~0.16.0": + version: 0.16.0 + resolution: "@microsoft/tsdoc@npm:0.16.0" + checksum: 10/1eaad3605234dc7e44898c15d1ba3c97fb968af1117025400cba572ce268da05afc36634d1fb9e779457af3ff7f13330aee07a962510a4d9c6612c13f71ee41e languageName: node linkType: hard -"@lerna/cli@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/cli@npm:5.6.2" - dependencies: - "@lerna/global-options": "npm:5.6.2" - dedent: "npm:^0.7.0" - npmlog: "npm:^6.0.2" - yargs: "npm:^16.2.0" - checksum: 10/e0b853feafe6d572056ea61a18fed4acb0ad62bcd99c3b5d753a8b8e8b69e5275f5eb7e102e7d09340d8f8e0e73a038b203acb4c77437d7edcf835470917b296 - languageName: node - linkType: hard - -"@lerna/collect-uncommitted@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/collect-uncommitted@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - chalk: "npm:^4.1.0" - npmlog: "npm:^6.0.2" - checksum: 10/9c9298bc447629819634dc5fa697caa6a4b33c4e9fd61ae7ad4108a42d916ef9193ea4cb72d6cf766fc6863e350211ab9b1fcde6a8fb75b75f43aa5e4a1afeb4 - languageName: node - linkType: hard - -"@lerna/collect-updates@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/collect-updates@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/describe-ref": "npm:5.6.2" - minimatch: "npm:^3.0.4" - npmlog: "npm:^6.0.2" - slash: "npm:^3.0.0" - checksum: 10/44149466c60e63f495bb09a3a8fd6c1d91f55de9dfcaac3adcefaf27c690adb6ac2c2a9b6bf5c9f8e430cb41db7c6994c9506b28945f5bb46a47e78f2829425d - languageName: node - linkType: hard - -"@lerna/command@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/command@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/package-graph": "npm:5.6.2" - "@lerna/project": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - "@lerna/write-log-file": "npm:5.6.2" - clone-deep: "npm:^4.0.1" - dedent: "npm:^0.7.0" - execa: "npm:^5.0.0" - is-ci: "npm:^2.0.0" - npmlog: "npm:^6.0.2" - checksum: 10/cd8ef728efccfa24effa8064af6227729544d0834e6f2b3f50eb0499d1d8ce40f84bb4e2b72a8e2a82f7e7816689b461f2a1065e5f83764b8a045ac57b22ffa0 - languageName: node - linkType: hard - -"@lerna/conventional-commits@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/conventional-commits@npm:5.6.2" - dependencies: - "@lerna/validation-error": "npm:5.6.2" - conventional-changelog-angular: "npm:^5.0.12" - conventional-changelog-core: "npm:^4.2.4" - conventional-recommended-bump: "npm:^6.1.0" - fs-extra: "npm:^9.1.0" - get-stream: "npm:^6.0.0" - npm-package-arg: "npm:8.1.1" - npmlog: "npm:^6.0.2" - pify: "npm:^5.0.0" - semver: "npm:^7.3.4" - checksum: 10/a8dbcd4bbb697aebb6c1b045f8597f019b754cf42b5abaf6a77da7379e212107bb46e8c9747a7bc1b41de640109036f71bc97df0b1066ca6c719172dd5d8b563 - languageName: node - linkType: hard - -"@lerna/create-symlink@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/create-symlink@npm:5.6.2" - dependencies: - cmd-shim: "npm:^5.0.0" - fs-extra: "npm:^9.1.0" - npmlog: "npm:^6.0.2" - checksum: 10/1848bd60d5f3227cf66103571779d8c12c363c54ade93aaddcb10b7bba00adaf263faccee15fd05ac87ee5514feecd0e20e42b79b798a457609af1e77e734762 - languageName: node - linkType: hard - -"@lerna/create@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/create@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/npm-conf": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - dedent: "npm:^0.7.0" - fs-extra: "npm:^9.1.0" - init-package-json: "npm:^3.0.2" - npm-package-arg: "npm:8.1.1" - p-reduce: "npm:^2.1.0" - pacote: "npm:^13.6.1" - pify: "npm:^5.0.0" - semver: "npm:^7.3.4" - slash: "npm:^3.0.0" - validate-npm-package-license: "npm:^3.0.4" - validate-npm-package-name: "npm:^4.0.0" - yargs-parser: "npm:20.2.4" - checksum: 10/a86cb30deecba203556cfde4280cde91ab7d48405dae89dd6662b58333edc99810f290661a3e57ec10a21659b7e4e24933981cea8997e89bed6fa2e9048174f0 - languageName: node - linkType: hard - -"@lerna/describe-ref@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/describe-ref@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - npmlog: "npm:^6.0.2" - checksum: 10/510814bd0004859475cf62917a3145b010b33b519be3b80f30170b98500e176285d8f4b0aa9e5928b80798be90bc65f1591d6c72e26fee70d46e0f006996d69e - languageName: node - linkType: hard - -"@lerna/diff@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/diff@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - npmlog: "npm:^6.0.2" - checksum: 10/0731f5819da8c7bb2a210a9514541e7f7cdde8ddf1802e3ec5e40bd689f3c546d6fba12b9c72cd48aa97d179ff767c658bdfe26bf9590056307ee738b5b44052 - languageName: node - linkType: hard - -"@lerna/exec@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/exec@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/filter-options": "npm:5.6.2" - "@lerna/profiler": "npm:5.6.2" - "@lerna/run-topologically": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - p-map: "npm:^4.0.0" - checksum: 10/30255cffbb67bc6a89290c1755e0e832fe9be1de0a98a2a6553a0baf0e1f509e0268318eeb3da4441bad2aa5517268b522f57dc3aefc49d122b301dd06ff6086 - languageName: node - linkType: hard - -"@lerna/filter-options@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/filter-options@npm:5.6.2" - dependencies: - "@lerna/collect-updates": "npm:5.6.2" - "@lerna/filter-packages": "npm:5.6.2" - dedent: "npm:^0.7.0" - npmlog: "npm:^6.0.2" - checksum: 10/c1b4ce4973bd8fff66a1632891f69ce4c20858d304cc02502df1576235b879cb4d3dd04b4be4b1835058f445c44d572554b206cf35ec4c1a3b76de396949bff1 - languageName: node - linkType: hard - -"@lerna/filter-packages@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/filter-packages@npm:5.6.2" - dependencies: - "@lerna/validation-error": "npm:5.6.2" - multimatch: "npm:^5.0.0" - npmlog: "npm:^6.0.2" - checksum: 10/b5b4c3b1d1ae6d889802ead0e682aecb8a12c1cbb3738a95e68013e9c7fd04cc0e495e249ef69eb52e65c69bca760d357d265642b1e066857c898ff1415978bd - languageName: node - linkType: hard - -"@lerna/get-npm-exec-opts@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/get-npm-exec-opts@npm:5.6.2" - dependencies: - npmlog: "npm:^6.0.2" - checksum: 10/3430e602db853e075490e6b080d46340940acf354fb5513da19af2a8ad60c8fa397de7cbcbe0bda8a4266e9d995bc7cba1698d092933c5feaef134585eef9f08 - languageName: node - linkType: hard - -"@lerna/get-packed@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/get-packed@npm:5.6.2" - dependencies: - fs-extra: "npm:^9.1.0" - ssri: "npm:^9.0.1" - tar: "npm:^6.1.0" - checksum: 10/12637d74cf654214fb6adfe444370d90d66f5aa2fdbcfc6bedd4168e24a8e91346ad22f1386630b635452b3a0089c91cd3ea141f6cddfd8d111ba7b94dbbaac8 - languageName: node - linkType: hard - -"@lerna/github-client@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/github-client@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@octokit/plugin-enterprise-rest": "npm:^6.0.1" - "@octokit/rest": "npm:^19.0.3" - git-url-parse: "npm:^13.1.0" - npmlog: "npm:^6.0.2" - checksum: 10/08a7386af70bacec5b1c2ec7ba09a0cae407e54c65d33c89444b4460df48dc325772fe77b38ce7c5355295e24ba64d0d64e53ae3ca76ddd4b930af1f5b38507c - languageName: node - linkType: hard - -"@lerna/gitlab-client@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/gitlab-client@npm:5.6.2" - dependencies: - node-fetch: "npm:^2.6.1" - npmlog: "npm:^6.0.2" - checksum: 10/ad9e45621b727858f4ea87a5d624da41cd6784e616d247b86275fb08fbfb4c9974c5f698f59ac0272ec1d0a848bba5f04ef2fbc32c62ca3a77ecd3b0415bd298 - languageName: node - linkType: hard - -"@lerna/global-options@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/global-options@npm:5.6.2" - checksum: 10/7cb542edef4f06c98dc5a1f797a442e4a1f8bde444046bc5258b0908ecd888ac7fe75902c90c20898feb90e685dee2e3518dc5c85a8155504373ec3f4634f3db - languageName: node - linkType: hard - -"@lerna/has-npm-version@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/has-npm-version@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - semver: "npm:^7.3.4" - checksum: 10/98ca1161618a84e0509b9c988f3dd2e147225564d31820ea7b94332388afb7650b510ad902919c5ec9a0ec95b27aab81b4c3067769d106c801426620018a7aa4 - languageName: node - linkType: hard - -"@lerna/import@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/import@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/prompt": "npm:5.6.2" - "@lerna/pulse-till-done": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - dedent: "npm:^0.7.0" - fs-extra: "npm:^9.1.0" - p-map-series: "npm:^2.1.0" - checksum: 10/e30cf50673324034b732b24052d278cbb541a173bf3d18a85413351eff7e56cab6a2a4fc1cfaae15b58465df9eaa8c7d3b85e53a4e75789fe42cd268646b6d2d - languageName: node - linkType: hard - -"@lerna/info@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/info@npm:5.6.2" - dependencies: - "@lerna/command": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - envinfo: "npm:^7.7.4" - checksum: 10/0124b7b1fe75e9bee4f4d4e13216a61869ad918ac9dfbad79aa49e3dd4657a67945aceae6632452b08580d1370823af0ce15ac6fd7134b9042f69624c531be57 - languageName: node - linkType: hard - -"@lerna/init@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/init@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/project": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - p-map: "npm:^4.0.0" - write-json-file: "npm:^4.3.0" - checksum: 10/b7eca9be4b1bddf6139740901adf245bfecd5e27b3d743b28ab3f7a71f0351a248b95b331e695ffc34d29c03536fb48bf7a690ca3bbeacc7599cc9527d1c9bae - languageName: node - linkType: hard - -"@lerna/link@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/link@npm:5.6.2" - dependencies: - "@lerna/command": "npm:5.6.2" - "@lerna/package-graph": "npm:5.6.2" - "@lerna/symlink-dependencies": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - p-map: "npm:^4.0.0" - slash: "npm:^3.0.0" - checksum: 10/5d4d3cf7cd90e30797cd0961d835984f5f4f01de508c89cd4870462bd64b65f6a2cf01a2f0df738ce612f45154d3ba4fbfbe73d24f21c0b0015d8c3263b93a80 - languageName: node - linkType: hard - -"@lerna/list@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/list@npm:5.6.2" - dependencies: - "@lerna/command": "npm:5.6.2" - "@lerna/filter-options": "npm:5.6.2" - "@lerna/listable": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - checksum: 10/969b4a458e26bb12533549577fc3c95b62f7a982e04c77bf0755b99a1280d51a0b6288d9a42f1cb05d2f84e852c0fac6a388a5ab735daf1eaa478d9a5e4244f3 - languageName: node - linkType: hard - -"@lerna/listable@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/listable@npm:5.6.2" - dependencies: - "@lerna/query-graph": "npm:5.6.2" - chalk: "npm:^4.1.0" - columnify: "npm:^1.6.0" - checksum: 10/1f8abfd41bc07dc7e0be04a80e8b049d4a4c2234e1fe2c3d110e7028698fdc1985197904a535c24aedcb3444a3d7d58461c6830e7804c83ddccb611a3141adb4 - languageName: node - linkType: hard - -"@lerna/log-packed@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/log-packed@npm:5.6.2" - dependencies: - byte-size: "npm:^7.0.0" - columnify: "npm:^1.6.0" - has-unicode: "npm:^2.0.1" - npmlog: "npm:^6.0.2" - checksum: 10/bbb43bd521bd431298048556a0ca1b83819d6352a06c4792a121403ab5cc2a467c7e89848cec72c7e348af12d3eac1e65e95d1372bedad2ef4a68aaa5d624e5a - languageName: node - linkType: hard - -"@lerna/npm-conf@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/npm-conf@npm:5.6.2" - dependencies: - config-chain: "npm:^1.1.12" - pify: "npm:^5.0.0" - checksum: 10/3d097715b4c7394ab208e82e7a17cbe058fe3e78af26b7090da906f7b8c346a7a846c145c1e0538d624fdf292b94a91d085196722e17db47e268fe088698ad44 - languageName: node - linkType: hard - -"@lerna/npm-dist-tag@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/npm-dist-tag@npm:5.6.2" - dependencies: - "@lerna/otplease": "npm:5.6.2" - npm-package-arg: "npm:8.1.1" - npm-registry-fetch: "npm:^13.3.0" - npmlog: "npm:^6.0.2" - checksum: 10/f50f8b090d197b773b467853d54f2993dd99721cfd8dc17f4af587bc0f53a6c1d879175673f34471d2778b114bc97fcb86bfade1d1aafa349ade92f78878dbf5 - languageName: node - linkType: hard - -"@lerna/npm-install@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/npm-install@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/get-npm-exec-opts": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - npm-package-arg: "npm:8.1.1" - npmlog: "npm:^6.0.2" - signal-exit: "npm:^3.0.3" - write-pkg: "npm:^4.0.0" - checksum: 10/6878ee7420edb0353ae8b755b10ae33100980b108cbeaa5848f4b5d2c19c836dbe2d93b401365fe05baf080808c8ad259a05bb78d52b177fc21d6c24bdf41b27 - languageName: node - linkType: hard - -"@lerna/npm-publish@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/npm-publish@npm:5.6.2" - dependencies: - "@lerna/otplease": "npm:5.6.2" - "@lerna/run-lifecycle": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - libnpmpublish: "npm:^6.0.4" - npm-package-arg: "npm:8.1.1" - npmlog: "npm:^6.0.2" - pify: "npm:^5.0.0" - read-package-json: "npm:^5.0.1" - checksum: 10/87ec165e2c5976fd04e41bbed0cf796317813d4ef50cc42a1c96c25d96f761333d34fa575702f2979b3c828ea7df87d21064521fc4137da9d16f67803192c902 - languageName: node - linkType: hard - -"@lerna/npm-run-script@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/npm-run-script@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - "@lerna/get-npm-exec-opts": "npm:5.6.2" - npmlog: "npm:^6.0.2" - checksum: 10/b8319fe926484afd28f7fa68d92cca438a6429841bec06c843ca673bff044da15380c0077530bc7dd11b10c413a7404c6f7597f0ec15a33137ff5dbb1b9f98f2 - languageName: node - linkType: hard - -"@lerna/otplease@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/otplease@npm:5.6.2" - dependencies: - "@lerna/prompt": "npm:5.6.2" - checksum: 10/a8eaf9a3104d2d869dac773001e7b82b5825ae1753e1ed5ec953f11930bfc61ec7131a3e802a735cf88e6d61c945ac7bf52a5ae3a3937c40be11ef34b0f85a06 - languageName: node - linkType: hard - -"@lerna/output@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/output@npm:5.6.2" - dependencies: - npmlog: "npm:^6.0.2" - checksum: 10/34494135cf13cf024bb325c85f91e33f1d295df941afa659bdab3896862a9b69165ad6afdefc30945576577960f83c8e2374d2d5feb79e9a34b757ccffce2d9f - languageName: node - linkType: hard - -"@lerna/pack-directory@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/pack-directory@npm:5.6.2" - dependencies: - "@lerna/get-packed": "npm:5.6.2" - "@lerna/package": "npm:5.6.2" - "@lerna/run-lifecycle": "npm:5.6.2" - "@lerna/temp-write": "npm:5.6.2" - npm-packlist: "npm:^5.1.1" - npmlog: "npm:^6.0.2" - tar: "npm:^6.1.0" - checksum: 10/1231c9d0d1573267616364a50ef736be6edfdcf82600aee0d89ba8ddae891a32ad8d6d041af92ea685dee95ab7d4662098d62c61201d071a8ec9b4e19dd28e80 - languageName: node - linkType: hard - -"@lerna/package-graph@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/package-graph@npm:5.6.2" - dependencies: - "@lerna/prerelease-id-from-version": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - npm-package-arg: "npm:8.1.1" - npmlog: "npm:^6.0.2" - semver: "npm:^7.3.4" - checksum: 10/e49c40cc11a2f116d9bbe570439c5df56036cf0685218fe15417275bf7ead9c46334109d280cbbac61e76252e962d8fe23a2b8e507824d767d13860bb821836d - languageName: node - linkType: hard - -"@lerna/package@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/package@npm:5.6.2" - dependencies: - load-json-file: "npm:^6.2.0" - npm-package-arg: "npm:8.1.1" - write-pkg: "npm:^4.0.0" - checksum: 10/7549aacacfb6792a84f5e2452235c1873a970fe2f39e6dc755fee2b2a393f96b2c14ed45e568383e73d8ff1ebc33328502694e3a60f858e23c85c373ba29af1c - languageName: node - linkType: hard - -"@lerna/prerelease-id-from-version@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/prerelease-id-from-version@npm:5.6.2" - dependencies: - semver: "npm:^7.3.4" - checksum: 10/0b48944fc17941061036d7ed93829ca9555897b5073177cb6435cda852da433095df4a76c0b37842788ea5a4536a5300adec2bc23d55daeb8a0b0ca53de16268 - languageName: node - linkType: hard - -"@lerna/profiler@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/profiler@npm:5.6.2" - dependencies: - fs-extra: "npm:^9.1.0" - npmlog: "npm:^6.0.2" - upath: "npm:^2.0.1" - checksum: 10/a66e0c763b1b0477cdfb0d8c06da0693bf142aaa4cd694022e35a9f7b016126780b685494c862cc3f3a175b14f31f1fc9902f924aa48d1243ad3e41088a661f1 - languageName: node - linkType: hard - -"@lerna/project@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/project@npm:5.6.2" - dependencies: - "@lerna/package": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - cosmiconfig: "npm:^7.0.0" - dedent: "npm:^0.7.0" - dot-prop: "npm:^6.0.1" - glob-parent: "npm:^5.1.1" - globby: "npm:^11.0.2" - js-yaml: "npm:^4.1.0" - load-json-file: "npm:^6.2.0" - npmlog: "npm:^6.0.2" - p-map: "npm:^4.0.0" - resolve-from: "npm:^5.0.0" - write-json-file: "npm:^4.3.0" - checksum: 10/9e2ce6deddd04fd2d6b55d6bd9cef34c3e2a92ce5205f044d8eb447b6f5eddb190576e871ca8fda2e82656636fe8f2cfd8a4b5f8a282183a913a2ac8dd846096 - languageName: node - linkType: hard - -"@lerna/prompt@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/prompt@npm:5.6.2" - dependencies: - inquirer: "npm:^8.2.4" - npmlog: "npm:^6.0.2" - checksum: 10/a6f9352f223493d2eeb975f0eeb8981184a6981e2166a49bed792cebd811bf896234bf818b6e8260a6cf2cb2e5e0e26bf3c25475a159dc9b044f3708252b52b8 - languageName: node - linkType: hard - -"@lerna/publish@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/publish@npm:5.6.2" - dependencies: - "@lerna/check-working-tree": "npm:5.6.2" - "@lerna/child-process": "npm:5.6.2" - "@lerna/collect-updates": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/describe-ref": "npm:5.6.2" - "@lerna/log-packed": "npm:5.6.2" - "@lerna/npm-conf": "npm:5.6.2" - "@lerna/npm-dist-tag": "npm:5.6.2" - "@lerna/npm-publish": "npm:5.6.2" - "@lerna/otplease": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - "@lerna/pack-directory": "npm:5.6.2" - "@lerna/prerelease-id-from-version": "npm:5.6.2" - "@lerna/prompt": "npm:5.6.2" - "@lerna/pulse-till-done": "npm:5.6.2" - "@lerna/run-lifecycle": "npm:5.6.2" - "@lerna/run-topologically": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - "@lerna/version": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - libnpmaccess: "npm:^6.0.3" - npm-package-arg: "npm:8.1.1" - npm-registry-fetch: "npm:^13.3.0" - npmlog: "npm:^6.0.2" - p-map: "npm:^4.0.0" - p-pipe: "npm:^3.1.0" - pacote: "npm:^13.6.1" - semver: "npm:^7.3.4" - checksum: 10/aa58a585e89ff9ccf00d0737e3162df81c82624eed44f7c4efa4629bb9cae09a299755f6fd70142354a099f705bc692a2637c2923365a703a071e576d270ce61 - languageName: node - linkType: hard - -"@lerna/pulse-till-done@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/pulse-till-done@npm:5.6.2" - dependencies: - npmlog: "npm:^6.0.2" - checksum: 10/923995424e6399947fa752d0eb7b33852e6f77d0c17280c2fef43e757f47f28e07227708bc2ce1d8dc81c8afee2e1509cee1d7c3d08ab8f615498770974f8f0d - languageName: node - linkType: hard - -"@lerna/query-graph@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/query-graph@npm:5.6.2" - dependencies: - "@lerna/package-graph": "npm:5.6.2" - checksum: 10/a582795283760828417e3554ec015c68c815690bb7b29d7cf368a3a9d82f5150b8e6dbf02356cf4e4539b581d9879609876577ec87f3e4cc7a4caf605b2a042d - languageName: node - linkType: hard - -"@lerna/resolve-symlink@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/resolve-symlink@npm:5.6.2" - dependencies: - fs-extra: "npm:^9.1.0" - npmlog: "npm:^6.0.2" - read-cmd-shim: "npm:^3.0.0" - checksum: 10/19a95bb295ff9154f3661d36b54abfd5e415c0fb85a669a2fc7b600a180de13877b310d230c7782d8d5441324c5527c311f7a4afef57d6b8be04cbce5cd94927 - languageName: node - linkType: hard - -"@lerna/rimraf-dir@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/rimraf-dir@npm:5.6.2" - dependencies: - "@lerna/child-process": "npm:5.6.2" - npmlog: "npm:^6.0.2" - path-exists: "npm:^4.0.0" - rimraf: "npm:^3.0.2" - checksum: 10/b0ec7dc69e3caa4c4eae88b8feedf248feff603e50d082a5f363fc0a1f604fc7b76d2067d69c79fdaa20675e3d5a87b59baaab6225c73dc1322b8705ce58030b - languageName: node - linkType: hard - -"@lerna/run-lifecycle@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/run-lifecycle@npm:5.6.2" - dependencies: - "@lerna/npm-conf": "npm:5.6.2" - "@npmcli/run-script": "npm:^4.1.7" - npmlog: "npm:^6.0.2" - p-queue: "npm:^6.6.2" - checksum: 10/04164f03376272a47f8919f175ad7a4af3b5c57b014183e9b14b0c52aae686884a8716b145fc043c7012a3b2aa5e62b59f2762b8c4679552965d74a1bd73bbb7 - languageName: node - linkType: hard - -"@lerna/run-topologically@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/run-topologically@npm:5.6.2" - dependencies: - "@lerna/query-graph": "npm:5.6.2" - p-queue: "npm:^6.6.2" - checksum: 10/d10b59ddff43c0f8387bcd7f9618d135ae6f33ba23d74d9d2fa16cece4209759f8ada46e1050cff07ad82388eda4774a7f0a1690bac4b36ce8f3a23c2718d0d3 - languageName: node - linkType: hard - -"@lerna/run@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/run@npm:5.6.2" - dependencies: - "@lerna/command": "npm:5.6.2" - "@lerna/filter-options": "npm:5.6.2" - "@lerna/npm-run-script": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - "@lerna/profiler": "npm:5.6.2" - "@lerna/run-topologically": "npm:5.6.2" - "@lerna/timer": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - p-map: "npm:^4.0.0" - checksum: 10/d6350a09fd2b15d978f8c32e4d80e3ee5617c23fbc297ee8c88b3a31313684ab5350734b0263d5aa1e70ab22a02439465256dfc54821c23b09a47240dceeb858 - languageName: node - linkType: hard - -"@lerna/symlink-binary@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/symlink-binary@npm:5.6.2" - dependencies: - "@lerna/create-symlink": "npm:5.6.2" - "@lerna/package": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - p-map: "npm:^4.0.0" - checksum: 10/f4d633677cde5b27e580c064ffca60b46be6808afcab5bd327e3c4e4d0cb7a924d79d5022f87f1e2209014687c75cb7c59d8514cab3911f4e14a5b5bbbf96fec - languageName: node - linkType: hard - -"@lerna/symlink-dependencies@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/symlink-dependencies@npm:5.6.2" - dependencies: - "@lerna/create-symlink": "npm:5.6.2" - "@lerna/resolve-symlink": "npm:5.6.2" - "@lerna/symlink-binary": "npm:5.6.2" - fs-extra: "npm:^9.1.0" - p-map: "npm:^4.0.0" - p-map-series: "npm:^2.1.0" - checksum: 10/f1de8b38288f42647a0c663b8d6c701bf80acadaaf566830f736d3aae4b9f6dc0bac2fb3a771a266c62bcc72dd3b02b9ab5c2b4ccba40ad9e91894c08a168df8 - languageName: node - linkType: hard - -"@lerna/temp-write@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/temp-write@npm:5.6.2" - dependencies: - graceful-fs: "npm:^4.1.15" - is-stream: "npm:^2.0.0" - make-dir: "npm:^3.0.0" - temp-dir: "npm:^1.0.0" - uuid: "npm:^8.3.2" - checksum: 10/9a3ef13e08230a88de046aaaba0efdc2b5e27f16abd97af03b395bc2cf40ec52d8b6850d25a913b955046f52013c4a99b3e75a48397356d0a9a86b0f97afa905 - languageName: node - linkType: hard - -"@lerna/timer@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/timer@npm:5.6.2" - checksum: 10/3eb43f371f5e357a42ec0a69722b13feff3967c88057334562366ffae40ce5ee7750718a498037e1f0ab9d438274357c4033561f068a76b1a6f98861a5eeae0c - languageName: node - linkType: hard - -"@lerna/validation-error@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/validation-error@npm:5.6.2" - dependencies: - npmlog: "npm:^6.0.2" - checksum: 10/3871cbacc7668ab2b0498f3d394ea65fa721257402cffa89efb97f6bed89d11504f554d25007d079e679181bcbbf773432745733654f8415e901c7d08a6ae06b - languageName: node - linkType: hard - -"@lerna/version@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/version@npm:5.6.2" - dependencies: - "@lerna/check-working-tree": "npm:5.6.2" - "@lerna/child-process": "npm:5.6.2" - "@lerna/collect-updates": "npm:5.6.2" - "@lerna/command": "npm:5.6.2" - "@lerna/conventional-commits": "npm:5.6.2" - "@lerna/github-client": "npm:5.6.2" - "@lerna/gitlab-client": "npm:5.6.2" - "@lerna/output": "npm:5.6.2" - "@lerna/prerelease-id-from-version": "npm:5.6.2" - "@lerna/prompt": "npm:5.6.2" - "@lerna/run-lifecycle": "npm:5.6.2" - "@lerna/run-topologically": "npm:5.6.2" - "@lerna/temp-write": "npm:5.6.2" - "@lerna/validation-error": "npm:5.6.2" - "@nrwl/devkit": "npm:>=14.8.1 < 16" - chalk: "npm:^4.1.0" - dedent: "npm:^0.7.0" - load-json-file: "npm:^6.2.0" - minimatch: "npm:^3.0.4" - npmlog: "npm:^6.0.2" - p-map: "npm:^4.0.0" - p-pipe: "npm:^3.1.0" - p-reduce: "npm:^2.1.0" - p-waterfall: "npm:^2.1.1" - semver: "npm:^7.3.4" - slash: "npm:^3.0.0" - write-json-file: "npm:^4.3.0" - checksum: 10/13d69bc58367f7fefbf2612d4bb65f03337486cff32f9ffdddadf0d34f53726fef3eb4d57df71244f9ae3e694d575694d7ab9b88a196e48c2177e46681b1dfd0 - languageName: node - linkType: hard - -"@lerna/write-log-file@npm:5.6.2": - version: 5.6.2 - resolution: "@lerna/write-log-file@npm:5.6.2" - dependencies: - npmlog: "npm:^6.0.2" - write-file-atomic: "npm:^4.0.1" - checksum: 10/814e9cf20ac28be49b22720be7bef8f708b28c344d54a0664cb8c44bbcb11387c4f89abf1050cfc81b41fa770099c748ac97fdb99d8a016c9e2c3ca801f27a30 - languageName: node - linkType: hard - -"@microsoft/api-extractor-model@npm:7.33.4": - version: 7.33.4 - resolution: "@microsoft/api-extractor-model@npm:7.33.4" - dependencies: - "@microsoft/tsdoc": "npm:~0.16.0" - "@microsoft/tsdoc-config": "npm:~0.18.1" - "@rushstack/node-core-library": "npm:5.20.3" - checksum: 10/60888f94ee2b121aac3a1e443c6922080b130226d956c98eb44de5deba80c887d0382cd9ade8ccd46d8a55af27182fb937f7ef0a17dccac286253eb35cc8111d - languageName: node - linkType: hard - -"@microsoft/api-extractor@npm:^7.55.0": - version: 7.57.7 - resolution: "@microsoft/api-extractor@npm:7.57.7" - dependencies: - "@microsoft/api-extractor-model": "npm:7.33.4" - "@microsoft/tsdoc": "npm:~0.16.0" - "@microsoft/tsdoc-config": "npm:~0.18.1" - "@rushstack/node-core-library": "npm:5.20.3" - "@rushstack/rig-package": "npm:0.7.2" - "@rushstack/terminal": "npm:0.22.3" - "@rushstack/ts-command-line": "npm:5.3.3" - diff: "npm:~8.0.2" - lodash: "npm:~4.17.23" - minimatch: "npm:10.2.3" - resolve: "npm:~1.22.1" - semver: "npm:~7.7.4" - source-map: "npm:~0.6.1" - typescript: "npm:5.9.3" - bin: - api-extractor: bin/api-extractor - checksum: 10/052a137ad8b9b81034b14c9225429c517783112a550a4bff3d8588f4626d269790df163eb7dd55573a44de607051571e2bf03eb6384bf2dc505a41e98de94153 - languageName: node - linkType: hard - -"@microsoft/tsdoc-config@npm:~0.18.1": - version: 0.18.1 - resolution: "@microsoft/tsdoc-config@npm:0.18.1" -"@microsoft/tsdoc-config@npm:~0.18.1": - version: 0.18.1 - resolution: "@microsoft/tsdoc-config@npm:0.18.1" - dependencies: - "@microsoft/tsdoc": "npm:0.16.0" - ajv: "npm:~8.18.0" - ajv: "npm:~8.18.0" - jju: "npm:~1.4.0" - resolve: "npm:~1.22.2" - checksum: 10/1912c4d80af10c548897dafd2b76127a53d5154001fb63029d4414d57022bf0f0aced325d8a3a0970454bf651d506236b4d26c1c473a933ea77382bce67b1236 - checksum: 10/1912c4d80af10c548897dafd2b76127a53d5154001fb63029d4414d57022bf0f0aced325d8a3a0970454bf651d506236b4d26c1c473a933ea77382bce67b1236 - languageName: node - linkType: hard - -"@microsoft/tsdoc@npm:0.16.0, @microsoft/tsdoc@npm:~0.16.0": - version: 0.16.0 - resolution: "@microsoft/tsdoc@npm:0.16.0" - checksum: 10/1eaad3605234dc7e44898c15d1ba3c97fb968af1117025400cba572ce268da05afc36634d1fb9e779457af3ff7f13330aee07a962510a4d9c6612c13f71ee41e - languageName: node - linkType: hard - -"@mswjs/interceptors@npm:^0.41.0": - version: 0.41.3 - resolution: "@mswjs/interceptors@npm:0.41.3" +"@mswjs/interceptors@npm:^0.41.0": + version: 0.41.9 + resolution: "@mswjs/interceptors@npm:0.41.9" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -2944,7 +2407,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10/96b6c535fd27c8aed57f2dad380ea31e09026bf6ef960420bc0e30a2ccff269c8121f21f20423f4edd2ef1ed7db6173295950a3c4529693e6bca12eca1be4347 + checksum: 10/aed6a80913d345e37a0cd58f1318736d765d5295cefae35ea104f6bfc5b224f96eb71635593f470579c1251efb1d18ad426e884ba01f85cfe3ff135e3b82e7a2 languageName: node linkType: hard @@ -2959,17 +2422,22 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:16.2.1": - version: 16.2.1 - resolution: "@next/env@npm:16.2.1" - checksum: 10/c4f19f1767d7a1e8e9ff93cdee7e3b6a923d26d9d71f44124a797f03703ab9a18508b5ede997cc99d0307f2e0d0d1c426e9673a6c11ea10e170b87462a572236 +"@napi-rs/wasm-runtime@npm:^1.1.4": + version: 1.1.5 + resolution: "@napi-rs/wasm-runtime@npm:1.1.5" + dependencies: + "@tybys/wasm-util": "npm:^0.10.2" + peerDependencies: + "@emnapi/core": ^1.7.1 + "@emnapi/runtime": ^1.7.1 + checksum: 10/57a4b68f05f15b79bf45240ac173d3eaf72620d1b73261e7db407aa7ba8eb68e670fb1612d2ceef6b8cc500970a5ed6995c71c77661027971012ed2459ce307f languageName: node linkType: hard -"@next/env@npm:16.2.7": - version: 16.2.7 - resolution: "@next/env@npm:16.2.7" - checksum: 10/2db7b07fc9b2e5c8bd9b88f6a470f7d6c3b471b0dfcf12b9928ba9635aa98afaf7fd809d37bcd2ec5fb3a8f1a415073735f738533dcb9d7e67a4806fb7f2cb4f +"@next/env@npm:16.2.9": + version: 16.2.9 + resolution: "@next/env@npm:16.2.9" + checksum: 10/5cb54e8a956dbeb72eb5db966a7280fdc08693f4ffbd98c425a43f4d03abfa3c66868c1035b3203572990bdcd46d385eb8edcf41ee31dde5359743264001bce9 languageName: node linkType: hard @@ -2982,58 +2450,58 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-darwin-arm64@npm:16.2.1" +"@next/swc-darwin-arm64@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-darwin-arm64@npm:16.2.9" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-darwin-x64@npm:16.2.1" +"@next/swc-darwin-x64@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-darwin-x64@npm:16.2.9" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-linux-arm64-gnu@npm:16.2.1" +"@next/swc-linux-arm64-gnu@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-linux-arm64-gnu@npm:16.2.9" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-linux-arm64-musl@npm:16.2.1" +"@next/swc-linux-arm64-musl@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-linux-arm64-musl@npm:16.2.9" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-linux-x64-gnu@npm:16.2.1" +"@next/swc-linux-x64-gnu@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-linux-x64-gnu@npm:16.2.9" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-linux-x64-musl@npm:16.2.1" +"@next/swc-linux-x64-musl@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-linux-x64-musl@npm:16.2.9" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-win32-arm64-msvc@npm:16.2.1" +"@next/swc-win32-arm64-msvc@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-win32-arm64-msvc@npm:16.2.9" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:16.2.1": - version: 16.2.1 - resolution: "@next/swc-win32-x64-msvc@npm:16.2.1" +"@next/swc-win32-x64-msvc@npm:16.2.9": + version: 16.2.9 + resolution: "@next/swc-win32-x64-msvc@npm:16.2.9" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -3312,6 +2780,13 @@ __metadata: languageName: node linkType: hard +"@npmcli/redact@npm:^3.0.0": + version: 3.2.2 + resolution: "@npmcli/redact@npm:3.2.2" + checksum: 10/06769db8807c342e45985379a2786f41c367953a200dfba31029d14d147fae36fe8b428b930678555dcbdb30488f471e972e927f42e3ddd5ca31f5726c1214e3 + languageName: node + linkType: hard + "@npmcli/redact@npm:^4.0.0": version: 4.0.0 resolution: "@npmcli/redact@npm:4.0.0" @@ -3319,9 +2794,9 @@ __metadata: languageName: node linkType: hard -"@npmcli/run-script@npm:^4.1.0, @npmcli/run-script@npm:^4.1.3, @npmcli/run-script@npm:^4.1.7": - version: 4.2.1 - resolution: "@npmcli/run-script@npm:4.2.1" +"@npmcli/run-script@npm:10.0.3": + version: 10.0.3 + resolution: "@npmcli/run-script@npm:10.0.3" dependencies: "@npmcli/node-gyp": "npm:^5.0.0" "@npmcli/package-json": "npm:^7.0.0" @@ -3556,67 +3031,242 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0, @octokit/types@npm:^13.7.0, @octokit/types@npm:^13.8.0": - version: 13.10.0 - resolution: "@octokit/types@npm:13.10.0" - dependencies: - "@octokit/openapi-types": "npm:^24.2.0" - checksum: 10/32f8f5010d7faae128b0cdd0c221f0ca8c3781fe44483ecd87162b3da507db667f7369acda81340f6e2c9c374d9a938803409c6085c2c01d98210b6c58efb99a +"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0, @octokit/types@npm:^13.7.0, @octokit/types@npm:^13.8.0": + version: 13.10.0 + resolution: "@octokit/types@npm:13.10.0" + dependencies: + "@octokit/openapi-types": "npm:^24.2.0" + checksum: 10/32f8f5010d7faae128b0cdd0c221f0ca8c3781fe44483ecd87162b3da507db667f7369acda81340f6e2c9c374d9a938803409c6085c2c01d98210b6c58efb99a + languageName: node + linkType: hard + +"@open-draft/deferred-promise@npm:^2.2.0": + version: 2.2.0 + resolution: "@open-draft/deferred-promise@npm:2.2.0" + checksum: 10/bc3bb1668a555bb87b33383cafcf207d9561e17d2ca0d9e61b7ce88e82b66e36a333d3676c1d39eb5848022c03c8145331fcdc828ba297f88cb1de9c5cef6c19 + languageName: node + linkType: hard + +"@open-draft/logger@npm:^0.3.0": + version: 0.3.0 + resolution: "@open-draft/logger@npm:0.3.0" + dependencies: + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.0" + checksum: 10/7a280f170bcd4e91d3eedbefe628efd10c3bd06dd2461d06a7fdbced89ef457a38785847f88cc630fb4fd7dfa176d6f77aed17e5a9b08000baff647433b5ff78 + languageName: node + linkType: hard + +"@open-draft/until@npm:^2.0.0": + version: 2.1.0 + resolution: "@open-draft/until@npm:2.1.0" + checksum: 10/622be42950afc8e89715d0fd6d56cbdcd13e36625e23b174bd3d9f06f80e25f9adf75d6698af93bca1e1bf465b9ce00ec05214a12189b671fb9da0f58215b6f4 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff + languageName: node + linkType: hard + +"@pkgr/core@npm:^0.3.6": + version: 0.3.6 + resolution: "@pkgr/core@npm:0.3.6" + checksum: 10/29082aa13d36f13fc41cdc64cb1feb36b630de0d6ebde84e2ff68e3d7a7f1dce4462cca91f76176c46e50bbca6b1e7f1fd9cf907af12d5d70da83bc981ca4ccf + languageName: node + linkType: hard + +"@rjsf/utils@npm:*": + version: 6.6.2 + resolution: "@rjsf/utils@npm:6.6.2" + dependencies: + "@x0k/json-schema-merge": "npm:^1.0.3" + fast-equals: "npm:^6.0.0" + fast-uri: "npm:^3.1.2" + jsonpointer: "npm:^5.0.1" + lodash: "npm:^4.18.1" + lodash-es: "npm:^4.18.1" + react-is: "npm:^18.3.1" + peerDependencies: + react: ">=18" + checksum: 10/a6482a18655b08f0bbad5ce8442583ae98d84a5af46ad1cc9b93d7f3288966a4bd45422313ad80ad7b4a9b8553c80ab6cc1ccb0b9433e3b9a3ec42975d85c2b0 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.62.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-android-arm64@npm:4.62.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.62.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.62.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.62.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.62.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.62.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.62.0" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.62.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.62.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.62.0" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.62.0" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.62.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.62.0" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.62.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.62.0" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.62.0" + conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@open-draft/deferred-promise@npm:^2.2.0": - version: 2.2.0 - resolution: "@open-draft/deferred-promise@npm:2.2.0" - checksum: 10/bc3bb1668a555bb87b33383cafcf207d9561e17d2ca0d9e61b7ce88e82b66e36a333d3676c1d39eb5848022c03c8145331fcdc828ba297f88cb1de9c5cef6c19 +"@rollup/rollup-linux-x64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.62.0" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@open-draft/logger@npm:^0.3.0": - version: 0.3.0 - resolution: "@open-draft/logger@npm:0.3.0" - dependencies: - is-node-process: "npm:^1.2.0" - outvariant: "npm:^1.4.0" - checksum: 10/7a280f170bcd4e91d3eedbefe628efd10c3bd06dd2461d06a7fdbced89ef457a38785847f88cc630fb4fd7dfa176d6f77aed17e5a9b08000baff647433b5ff78 +"@rollup/rollup-linux-x64-musl@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.62.0" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@open-draft/until@npm:^2.0.0": - version: 2.1.0 - resolution: "@open-draft/until@npm:2.1.0" - checksum: 10/622be42950afc8e89715d0fd6d56cbdcd13e36625e23b174bd3d9f06f80e25f9adf75d6698af93bca1e1bf465b9ce00ec05214a12189b671fb9da0f58215b6f4 +"@rollup/rollup-openbsd-x64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-openbsd-x64@npm:4.62.0" + conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10/115e8ceeec6bc69dff2048b35c0ab4f8bbee12d8bb6c1f4af758604586d802b6e669dcb02dda61d078de42c2b4ddce41b3d9e726d7daa6b4b850f4adbf7333ff +"@rollup/rollup-openharmony-arm64@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.62.0" + conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@pkgr/core@npm:^0.3.6": - version: 0.3.6 - resolution: "@pkgr/core@npm:0.3.6" - checksum: 10/29082aa13d36f13fc41cdc64cb1feb36b630de0d6ebde84e2ff68e3d7a7f1dce4462cca91f76176c46e50bbca6b1e7f1fd9cf907af12d5d70da83bc981ca4ccf +"@rollup/rollup-win32-arm64-msvc@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.62.0" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rjsf/utils@npm:*": - version: 6.4.1 - resolution: "@rjsf/utils@npm:6.4.1" - dependencies: - "@x0k/json-schema-merge": "npm:^1.0.3" - fast-equals: "npm:^6.0.0" - fast-uri: "npm:^3.1.2" - jsonpointer: "npm:^5.0.1" - lodash: "npm:^4.17.23" - lodash-es: "npm:^4.17.23" - react-is: "npm:^18.3.1" - peerDependencies: - react: ">=18" - checksum: 10/b8967b731f00b312b213d7265ee2542ab6adea86bef71c02c8c3465b2e992e557b2a36631f0fbadbcc4b4b311c5e3d2be08dc509ce53c34aa691ee01e6bdf620 +"@rollup/rollup-win32-ia32-msvc@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.62.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.62.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.62.0": + version: 4.62.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.62.0" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -3634,11 +3284,10 @@ __metadata: languageName: node linkType: hard -"@rushstack/node-core-library@npm:5.20.3": - version: 5.20.3 - resolution: "@rushstack/node-core-library@npm:5.20.3" +"@rushstack/node-core-library@npm:5.23.1": + version: 5.23.1 + resolution: "@rushstack/node-core-library@npm:5.23.1" dependencies: - ajv: "npm:~8.18.0" ajv: "npm:~8.18.0" ajv-draft-04: "npm:~1.0.0" ajv-formats: "npm:~3.0.1" @@ -3652,13 +3301,10 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10/16479e853e3c15fca644a05b50734e1356f0180a88a0d164fe7a7c75b90da9e7da44befe455361ed8ef786ad1b00b6c3662f40e08cb06dc081bf5fb574c74642 + checksum: 10/77e30e2848e33ff56d07ca9c93488ff7afffc3fdccbc754f27a205809d91a71950fd39c1dc9f6a86e6ecba83e36f470a920b8b4f3a6b319cfd4fadc48891ca8d languageName: node linkType: hard -"@rushstack/problem-matcher@npm:0.2.1": - version: 0.2.1 - resolution: "@rushstack/problem-matcher@npm:0.2.1" "@rushstack/problem-matcher@npm:0.2.1": version: 0.2.1 resolution: "@rushstack/problem-matcher@npm:0.2.1" @@ -3668,26 +3314,24 @@ __metadata: "@types/node": optional: true checksum: 10/62fda91629577a2f57de19be357cd0990da145ff4933f4d2cd48f423cc03b92fca06dd8916dcbaf1d307a201c104847c77066d45d79fd3c323c4949f0c99bf44 - checksum: 10/62fda91629577a2f57de19be357cd0990da145ff4933f4d2cd48f423cc03b92fca06dd8916dcbaf1d307a201c104847c77066d45d79fd3c323c4949f0c99bf44 languageName: node linkType: hard -"@rushstack/rig-package@npm:0.7.2": - version: 0.7.2 - resolution: "@rushstack/rig-package@npm:0.7.2" +"@rushstack/rig-package@npm:0.7.3": + version: 0.7.3 + resolution: "@rushstack/rig-package@npm:0.7.3" dependencies: jju: "npm:~1.4.0" resolve: "npm:~1.22.1" - strip-json-comments: "npm:~3.1.1" - checksum: 10/d500714d77792797aaf98afaa7c6133b6886626096f738e1cfd9c18a15ac05fbb3ac6b2cc0798a21e9e9836ebae7f4542c951e4541cfdbee3a280f3f072cf93e + checksum: 10/46cbdf1b4538640a6c93825ddaa7693b45cd7f640b34106ab554319b5d3fae18f65a3c91576c6cce005a1250722159c329ce5f4b1729bead594d9f634cd18616 languageName: node linkType: hard -"@rushstack/terminal@npm:0.22.3": - version: 0.22.3 - resolution: "@rushstack/terminal@npm:0.22.3" +"@rushstack/terminal@npm:0.24.0": + version: 0.24.0 + resolution: "@rushstack/terminal@npm:0.24.0" dependencies: - "@rushstack/node-core-library": "npm:5.20.3" + "@rushstack/node-core-library": "npm:5.23.1" "@rushstack/problem-matcher": "npm:0.2.1" supports-color: "npm:~8.1.1" peerDependencies: @@ -3695,68 +3339,50 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10/d4a8a2b1743d5e8a45f318be4665bf1fcb4bfbe9013a0d48f6c9f89960142da1bcad3544bbcd38fc29a1d061ff4664cb0b4ab87c8e9cb0d0f7479b6be70fa47b + checksum: 10/796fc5c031df2035d10b764257e2c58cd330b04241eb28a67505a726df41e895d3bb91de76c6b1db76ebbf0098396188b02aaeaa35dd2cb14ed25112e3335b2e languageName: node linkType: hard -"@rushstack/ts-command-line@npm:5.3.3": - version: 5.3.3 - resolution: "@rushstack/ts-command-line@npm:5.3.3" +"@rushstack/ts-command-line@npm:5.3.10": + version: 5.3.10 + resolution: "@rushstack/ts-command-line@npm:5.3.10" dependencies: - "@rushstack/terminal": "npm:0.22.3" + "@rushstack/terminal": "npm:0.24.0" "@types/argparse": "npm:1.0.38" argparse: "npm:~1.0.9" string-argv: "npm:~0.3.1" - checksum: 10/a0266554ef6abc710f3dfbcb91006be1c3593760b4142bd6357349c79f096b3a7f7a5ec723c4f60293dc4357c54d820cf90e6f351c269d0a0591c6654432a0a4 + checksum: 10/669da3f5ee4b17f2aac08cabb1386050436d14f8a7af78d1a2013ed801552c526f3037d3370c3940b33fbe55d4bc24c9b5b84e140b250e067a545655d5c2104a languageName: node linkType: hard -"@shikijs/engine-oniguruma@npm:^3.23.0": - version: 3.23.0 - resolution: "@shikijs/engine-oniguruma@npm:3.23.0" "@shikijs/engine-oniguruma@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/engine-oniguruma@npm:3.23.0" dependencies: - "@shikijs/types": "npm:3.23.0" "@shikijs/types": "npm:3.23.0" "@shikijs/vscode-textmate": "npm:^10.0.2" checksum: 10/edd8983be86f6b055793813e80ecf31c3cefdd896f3742797ae03f2cbb9f467ac736c4b152d4a5c42dbd17da17d24ff798a15d37bcf6205d7f8cd8f44e2a11e0 - checksum: 10/edd8983be86f6b055793813e80ecf31c3cefdd896f3742797ae03f2cbb9f467ac736c4b152d4a5c42dbd17da17d24ff798a15d37bcf6205d7f8cd8f44e2a11e0 languageName: node linkType: hard -"@shikijs/langs@npm:^3.23.0": - version: 3.23.0 - resolution: "@shikijs/langs@npm:3.23.0" "@shikijs/langs@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/langs@npm:3.23.0" dependencies: "@shikijs/types": "npm:3.23.0" - checksum: 10/115b1afb9eb4eb300eb68b146e442f7e6d196878798c5b9d75dd53a6ef1e1c3b27e325353a10973e3fa47d88630e937b8a3cf3b37b6eef80bf2aec3d51657bb3 - "@shikijs/types": "npm:3.23.0" checksum: 10/115b1afb9eb4eb300eb68b146e442f7e6d196878798c5b9d75dd53a6ef1e1c3b27e325353a10973e3fa47d88630e937b8a3cf3b37b6eef80bf2aec3d51657bb3 languageName: node linkType: hard -"@shikijs/themes@npm:^3.23.0": - version: 3.23.0 - resolution: "@shikijs/themes@npm:3.23.0" "@shikijs/themes@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/themes@npm:3.23.0" dependencies: "@shikijs/types": "npm:3.23.0" - checksum: 10/741987380445b788aea6cc1e03492bc06950f1a0461edf45083bd226889c5ba78c7ed1af9724afacab7d6471777f0c0e8871577183dfb2cfbceb255663374835 - "@shikijs/types": "npm:3.23.0" checksum: 10/741987380445b788aea6cc1e03492bc06950f1a0461edf45083bd226889c5ba78c7ed1af9724afacab7d6471777f0c0e8871577183dfb2cfbceb255663374835 languageName: node linkType: hard -"@shikijs/types@npm:3.23.0, @shikijs/types@npm:^3.23.0": - version: 3.23.0 - resolution: "@shikijs/types@npm:3.23.0" "@shikijs/types@npm:3.23.0, @shikijs/types@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/types@npm:3.23.0" @@ -3764,7 +3390,6 @@ __metadata: "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" checksum: 10/18b5703d445d53dd6782a3e2c7332009302c6c046f301d9cd99f1a26b9c8329b3e502b096894bffac61f78835c533827bab2ce78a39666ab87b78f8d7ca11f7b - checksum: 10/18b5703d445d53dd6782a3e2c7332009302c6c046f301d9cd99f1a26b9c8329b3e502b096894bffac61f78835c533827bab2ce78a39666ab87b78f8d7ca11f7b languageName: node linkType: hard @@ -3834,9 +3459,6 @@ __metadata: linkType: hard "@sinclair/typebox@npm:^0.27.8": - version: 0.27.10 - resolution: "@sinclair/typebox@npm:0.27.10" - checksum: 10/1498c5ef1375787e6272528615d5c262afb60873191d2441316359817b1c411917063c8be102ef15b0b5c62243a9daa7aefc8426f20eb406b67038b3eaa0695a version: 0.27.10 resolution: "@sinclair/typebox@npm:0.27.10" checksum: 10/1498c5ef1375787e6272528615d5c262afb60873191d2441316359817b1c411917063c8be102ef15b0b5c62243a9daa7aefc8426f20eb406b67038b3eaa0695a @@ -3882,7 +3504,6 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^13.0.5": "@sinonjs/fake-timers@npm:^13.0.5": version: 13.0.5 resolution: "@sinonjs/fake-timers@npm:13.0.5" @@ -3892,12 +3513,12 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^15.0.0": - version: 15.1.1 - resolution: "@sinonjs/fake-timers@npm:15.1.1" +"@sinonjs/fake-timers@npm:^15.4.0": + version: 15.4.0 + resolution: "@sinonjs/fake-timers@npm:15.4.0" dependencies: "@sinonjs/commons": "npm:^3.0.1" - checksum: 10/f262d613ea7f7cdb1b5d90c0cae01b7c6b797d6d0f1ca0fe30b7b69012e3076bb8a0f69d735bc69d2824b9bb1efb8554ca9765b4a6bb22defdec9ce79e7cd8a4 + checksum: 10/3960a9fe065f38a4228c66d184eeb101e8a6af9cbfc8454dd5d45ac397201da72134048d4e808a25993494885b172dd6deecdad9949bbf4c1d3a220ef561f6cc languageName: node linkType: hard @@ -3911,12 +3532,12 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.0.0-beta.3, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.1.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -3939,12 +3560,43 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/cli@npm:^2.1.0, @sitecore-content-sdk/cli@workspace:packages/cli": +"@sitecore-content-sdk/analytics-core@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/analytics-core@npm:2.1.0" + dependencies: + "@sitecore-content-sdk/core": "npm:^2.1.0" + debug: "npm:^4.4.3" + isbot: "npm:^5.1.39" + checksum: 10/7470251836399e1205477719f493bf238c6267686dd7131334376356377f55d0c7c67fe231c32e8067fc89df67150be063dc020069a2f2af9709d020cbd402d0 + languageName: node + linkType: hard + +"@sitecore-content-sdk/cli@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/cli@npm:2.1.0" + dependencies: + "@sitecore-content-sdk/content": "npm:^2.1.0" + "@sitecore-content-sdk/core": "npm:^2.1.0" + chokidar: "npm:^4.0.3" + dotenv: "npm:^16.5.0" + dotenv-expand: "npm:^12.0.2" + inquirer: "npm:^12.9.6" + resolve: "npm:^1.22.10" + tmp: "npm:^0.2.3" + tsx: "npm:^4.19.4" + yargs: "npm:^17.7.2" + bin: + sitecore-tools: dist/cjs/bin/sitecore-tools.js + checksum: 10/e5d2d999a3ff6b5027da972f4db75266f23be93368bfc6e878012620b136fbfe64203c3998ac894da3557f754a7a696401a54959d4bc240eef78167c17e08a64 + languageName: node + linkType: hard + +"@sitecore-content-sdk/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/content": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/inquirer": "npm:^9.0.9" @@ -3983,12 +3635,13 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/content@npm:2.0.0-beta.3, @sitecore-content-sdk/content@workspace:packages/content": +"@sitecore-content-sdk/content@npm:2.1.0-beta.1, @sitecore-content-sdk/content@workspace:packages/content": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/content@workspace:packages/content" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.3" + "@json-render/core": "npm:^0.19.0" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/events": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/chai": "npm:^5.2.2" "@types/chai-spies": "npm:^1.0.6" @@ -4028,11 +3681,27 @@ __metadata: tsx: "npm:^4.19.4" typescript: "npm:~5.8.3" peerDependencies: - "@sitecore-content-sdk/events": ^2.0.0-beta.3 + "@sitecore-content-sdk/events": 2.1.0-beta.1 languageName: unknown linkType: soft -"@sitecore-content-sdk/core@npm:2.0.0-beta.3, @sitecore-content-sdk/core@workspace:packages/core": +"@sitecore-content-sdk/content@npm:^2.1.0, @sitecore-content-sdk/content@npm:^2.1.1": + version: 2.1.1 + resolution: "@sitecore-content-sdk/content@npm:2.1.1" + dependencies: + "@sitecore-content-sdk/core": "npm:^2.1.0" + chalk: "npm:^4.1.2" + debug: "npm:^4.4.0" + glob: "npm:^11.0.2" + graphql: "npm:^16.11.0" + url-parse: "npm:^1.5.10" + peerDependencies: + "@sitecore-content-sdk/events": ^2.1.0 + checksum: 10/87e20ac725c389ad81f923db84212789ebb5031a190c4e55230604010397198dd59eff8595467c3a456ba19e9c1cc3b57e80e79819969298ba287e474b491257 + languageName: node + linkType: hard + +"@sitecore-content-sdk/core@npm:2.1.0-beta.1, @sitecore-content-sdk/core@workspace:packages/core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/core@workspace:packages/core" dependencies: @@ -4070,14 +3739,27 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/events@npm:2.0.0-beta.3, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/core@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/core@npm:2.1.0" + dependencies: + debug: "npm:^4.4.0" + graphql: "npm:^16.11.0" + graphql-request: "npm:^6.1.0" + memory-cache: "npm:^0.2.0" + url-parse: "npm:^1.5.10" + checksum: 10/943e3bef8e2ea13783e7148252746ecfb453c51c1daa74fcde1b17aaed541a1c4272a35bdd065aa4712e656f649795ee59b43dba573b4721517b67ccae84e7d2 + languageName: node + linkType: hard + +"@sitecore-content-sdk/events@npm:2.1.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: "@jest/globals": "npm:^30.2.0" "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -4099,16 +3781,55 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/nextjs@npm:^2.1.0, @sitecore-content-sdk/nextjs@workspace:packages/nextjs": +"@sitecore-content-sdk/events@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/events@npm:2.1.0" + dependencies: + "@sitecore-content-sdk/analytics-core": "npm:^2.1.0" + "@sitecore-content-sdk/core": "npm:^2.1.0" + debug: "npm:^4.4.3" + checksum: 10/16bc271c8409d638e4f12145ff5c56b5e5f0225e831b9b640d45aa3b51c0cd88ca3b6e5f8c27f0917d3b3efb0bc981e8772ac46d518d17b1a730efcb67c620ff + languageName: node + linkType: hard + +"@sitecore-content-sdk/nextjs@npm:^2.1.0": + version: 2.1.1 + resolution: "@sitecore-content-sdk/nextjs@npm:2.1.1" + dependencies: + "@babel/parser": "npm:^7.27.2" + "@sitecore-content-sdk/content": "npm:^2.1.1" + "@sitecore-content-sdk/core": "npm:^2.1.0" + "@sitecore-content-sdk/events": "npm:^2.1.0" + "@sitecore-content-sdk/react": "npm:^2.1.0" + recast: "npm:^0.23.11" + regex-parser: "npm:^2.3.1" + sync-disk-cache: "npm:^2.1.0" + peerDependencies: + "@sitecore-content-sdk/analytics-core": ^2.1.0 + "@sitecore-content-sdk/events": ^2.1.0 + "@sitecore-content-sdk/personalize": ^2.1.0 + next: ^16.2.0 + react: ^19.2.1 + react-dom: ^19.2.1 + typescript: ^5.4.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/e5c428714a41334afc5c7592609a225b591653abae8d7e5175fbf95b588344c8139a8f9be94e0ca02acc2a13f85baffaa3ff4a1fa43938ee84fad3d21a0c7e36 + languageName: node + linkType: hard + +"@sitecore-content-sdk/nextjs@workspace:packages/nextjs": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: "@babel/parser": "npm:^7.27.2" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/personalize": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/react": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/content": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/events": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/personalize": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/react": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" "@testing-library/react": "npm:^16.3.0" @@ -4152,10 +3873,10 @@ __metadata: ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.3 - "@sitecore-content-sdk/events": ^2.0.0-beta.3 - "@sitecore-content-sdk/personalize": ^2.0.0-beta.3 - next: ^16.1.1 + "@sitecore-content-sdk/analytics-core": 2.1.0-beta.1 + "@sitecore-content-sdk/events": 2.1.0-beta.1 + "@sitecore-content-sdk/personalize": 2.1.0-beta.1 + next: ^16.2.0 react: ^19.2.1 react-dom: ^19.2.1 typescript: ^5.4.0 @@ -4165,14 +3886,14 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.0.0-beta.3, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.1.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: "@jest/types": "npm:^29.6.3" - "@sitecore-content-sdk/analytics-core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/events": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/events": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" "@types/debug": "npm:^4.1.12" "@types/jest": "npm:^29.5.12" @@ -4194,13 +3915,28 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/react@npm:2.0.0-beta.3, @sitecore-content-sdk/react@workspace:packages/react": +"@sitecore-content-sdk/personalize@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/personalize@npm:2.1.0" + dependencies: + "@sitecore-content-sdk/analytics-core": "npm:^2.1.0" + "@sitecore-content-sdk/core": "npm:^2.1.0" + "@sitecore-content-sdk/events": "npm:^2.1.0" + debug: "npm:^4.4.3" + checksum: 10/975bcc3218629485d80130e1863db78673b4c4cd5c19b1449417a3f86bf2ab6b4ef7cf22aea055f70aec9206d1ab6eb435e6c373eb2c6ddf5417b52acd315af1 + languageName: node + linkType: hard + +"@sitecore-content-sdk/react@npm:2.1.0-beta.1, @sitecore-content-sdk/react@workspace:packages/react": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/react@workspace:packages/react" dependencies: - "@sitecore-content-sdk/content": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" - "@sitecore-content-sdk/search": "npm:0.2.0-beta.3" + "@json-render/core": "npm:^0.19.0" + "@json-render/react": "npm:^0.19.0" + "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/content": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/search": "npm:^0.3.0" "@sitecore-feaas/clientside": "npm:^0.6.0" "@stylistic/eslint-plugin": "npm:^5.2.2" "@testing-library/dom": "npm:^10.4.0" @@ -4238,21 +3974,49 @@ __metadata: sinon-chai: "npm:^3.7.0" ts-node: "npm:^10.9.2" typescript: "npm:~5.8.3" - zod: "npm:^4.3.6" peerDependencies: - "@sitecore-content-sdk/analytics-core": ^2.0.0-beta.3 - "@sitecore-content-sdk/events": ^2.0.0-beta.3 + "@sitecore-content-sdk/analytics-core": 2.1.0-beta.1 + "@sitecore-content-sdk/events": 2.1.0-beta.1 "@sitecore-feaas/clientside": ^0.6.0 react: ^19.2.1 react-dom: ^19.2.1 languageName: unknown linkType: soft -"@sitecore-content-sdk/search@npm:0.2.0-beta.3, @sitecore-content-sdk/search@workspace:packages/search": +"@sitecore-content-sdk/react@npm:^2.1.0": + version: 2.1.0 + resolution: "@sitecore-content-sdk/react@npm:2.1.0" + dependencies: + "@sitecore-content-sdk/content": "npm:^2.1.0" + "@sitecore-content-sdk/core": "npm:^2.1.0" + "@sitecore-content-sdk/search": "npm:^0.3.0" + fast-deep-equal: "npm:^3.1.3" + peerDependencies: + "@sitecore-content-sdk/analytics-core": ^2.1.0 + "@sitecore-content-sdk/events": ^2.1.0 + "@sitecore-feaas/clientside": ^0.6.0 + react: ^19.2.1 + react-dom: ^19.2.1 + checksum: 10/740bc0d1848f76a3d9fda211007df47d46bf1e99d33c5a1ae5e65dc000c76674aa8cdf9f9f8c65a00e11cf42ffd1da93a262d4b494951bdf524b01d1ddb3a9be + languageName: node + linkType: hard + +"@sitecore-content-sdk/search@npm:^0.3.0": + version: 0.3.0 + resolution: "@sitecore-content-sdk/search@npm:0.3.0" + dependencies: + "@sitecore-content-sdk/analytics-core": "npm:^2.1.0" + "@sitecore-content-sdk/core": "npm:^2.1.0" + checksum: 10/a5591861101eadd66c7de666ba17121e2bdde3ea8408df95d6d7f3a1d13144a4dbc5dc0f3654a077fd386f0e27eafbb044eb70af34711c522ee710ee0bc11146 + languageName: node + linkType: hard + +"@sitecore-content-sdk/search@workspace:packages/search": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/search@workspace:packages/search" dependencies: - "@sitecore-content-sdk/core": "npm:2.0.0-beta.3" + "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" + "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" "@types/chai": "npm:^5.2.3" "@types/mocha": "npm:^10.0.10" "@types/proxyquire": "npm:^1.3.31" @@ -4293,22 +4057,17 @@ __metadata: linkType: hard "@stylistic/eslint-plugin@npm:^5.2.2": - version: 5.10.0 - resolution: "@stylistic/eslint-plugin@npm:5.10.0" version: 5.10.0 resolution: "@stylistic/eslint-plugin@npm:5.10.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" "@typescript-eslint/types": "npm:^8.56.0" - "@typescript-eslint/types": "npm:^8.56.0" eslint-visitor-keys: "npm:^4.2.1" espree: "npm:^10.4.0" estraverse: "npm:^5.3.0" picomatch: "npm:^4.0.3" peerDependencies: eslint: ^9.0.0 || ^10.0.0 - checksum: 10/d55706b09a55defbc5afbfc251c4ecc0acfcbc3e741c34bec461365b399d2c9d89a6cafac069356ea03e231f3b12d5cc9550e66603ac343c29e07f2b2cd30464 - eslint: ^9.0.0 || ^10.0.0 checksum: 10/d55706b09a55defbc5afbfc251c4ecc0acfcbc3e741c34bec461365b399d2c9d89a6cafac069356ea03e231f3b12d5cc9550e66603ac343c29e07f2b2cd30464 languageName: node linkType: hard @@ -4419,7 +4178,7 @@ __metadata: languageName: node linkType: hard -"@tybys/wasm-util@npm:^0.10.1": +"@tybys/wasm-util@npm:^0.10.2": version: 0.10.2 resolution: "@tybys/wasm-util@npm:0.10.2" dependencies: @@ -4521,14 +4280,11 @@ __metadata: linkType: hard "@types/debug@npm:^4.1.12": - version: 4.1.13 - resolution: "@types/debug@npm:4.1.13" version: 4.1.13 resolution: "@types/debug@npm:4.1.13" dependencies: "@types/ms": "npm:*" checksum: 10/5091d4ebda85236e6f4a6ecea552860e521e11d1d388d3f6255b40726f5a4a7cf1baa0d09f60853838e4cac6c12a13b14114d5f422ccecaee4d1d07dab349900 - checksum: 10/5091d4ebda85236e6f4a6ecea552860e521e11d1d388d3f6255b40726f5a4a7cf1baa0d09f60853838e4cac6c12a13b14114d5f422ccecaee4d1d07dab349900 languageName: node linkType: hard @@ -4592,12 +4348,12 @@ __metadata: linkType: hard "@types/inquirer@npm:^9.0.8, @types/inquirer@npm:^9.0.9": - version: 9.0.9 - resolution: "@types/inquirer@npm:9.0.9" + version: 9.0.10 + resolution: "@types/inquirer@npm:9.0.10" dependencies: "@types/through": "npm:*" rxjs: "npm:^7.2.0" - checksum: 10/015ee6fa65d1d79c070c889906be3eceedd77aa08a4aea3d17b188c9d169b459994244525f054b60de0e28fe9c492df75527a7b63da16e06fcc09e54039ac652 + checksum: 10/2fd61509eb32bf9eef4b9a3e0fc8929d30ce8d2bdbbceb8a4e65c11831187fefcdd08641ec7f2228feac2ec75b1460deb5f4f91ad66c99b614b239edd8e4da3e languageName: node linkType: hard @@ -4717,20 +4473,27 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 25.5.0 - resolution: "@types/node@npm:25.5.0" + version: 25.9.3 + resolution: "@types/node@npm:25.9.3" dependencies: - undici-types: "npm:~7.18.0" - checksum: 10/b1e8116bd8c9ff62e458b76d28a59cf7631537bb17e8961464bf754dd5b07b46f1620f568b2f89970505af9eef478dd74c614651b454c1ea95949ec472c64fcb + undici-types: "npm:>=7.24.0 <7.24.7" + checksum: 10/40c5f5c91450689b255dbf8cbd6cf0bb05e1013a353830b15eb9b5c9cda6448510be572d1ecadc38c8a4ac472e61381de9ae28df82094abece59f4c1eccffbc5 + languageName: node + linkType: hard + +"@types/node@npm:^12.7.1": + version: 12.20.55 + resolution: "@types/node@npm:12.20.55" + checksum: 10/1f916a06fff02faadb09a16ed6e31820ce170798b202ef0b14fc244bfbd721938c54a3a99836e185e4414ca461fe96c5bb5c67c3d248f153555b7e6347f061dd languageName: node linkType: hard "@types/node@npm:^24.10.4": - version: 24.12.0 - resolution: "@types/node@npm:24.12.0" + version: 24.13.2 + resolution: "@types/node@npm:24.13.2" dependencies: - undici-types: "npm:~7.16.0" - checksum: 10/e9dcf8a378af5a636353b6d88a6fae018504bab776410ac6b5411e29afbe601ba9d7957356556fc27268a62814ca4085974f785613482c18f739686efcd49655 + undici-types: "npm:~7.18.0" + checksum: 10/2b8cb95ada191ecc788fef91d7cdb86dff24f6895b510e1fb495cc001832709fa8fc3e1eeb27d64bda52b3f74f9e91acf42431e98e3e6876de1e53acce7f7602 languageName: node linkType: hard @@ -4758,11 +4521,11 @@ __metadata: linkType: hard "@types/react@npm:^19.2.7": - version: 19.2.14 - resolution: "@types/react@npm:19.2.14" + version: 19.2.17 + resolution: "@types/react@npm:19.2.17" dependencies: csstype: "npm:^3.2.2" - checksum: 10/fbff239089ee64b6bd9b00543594db498278b06de527ef1b0f71bb0eb09cc4445a71b5dd3c0d3d0257255c4eed94406be40a74ad4a987ade8a8d5dd65c82bc5f + checksum: 10/8debb092bd0bb9c99176a31824c7587c27c775a6526b60060e7b9e8dc2af53aee2ffadc7a1e3845953792437db5699d34a9054e198c9d4e54a74728ff47c6725 languageName: node linkType: hard @@ -4892,23 +4655,23 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": - version: 8.57.2 - resolution: "@typescript-eslint/eslint-plugin@npm:8.57.2" +"@typescript-eslint/eslint-plugin@npm:8.61.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": + version: 8.61.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.61.0" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.57.2" - "@typescript-eslint/type-utils": "npm:8.57.2" - "@typescript-eslint/utils": "npm:8.57.2" - "@typescript-eslint/visitor-keys": "npm:8.57.2" + "@typescript-eslint/scope-manager": "npm:8.61.0" + "@typescript-eslint/type-utils": "npm:8.61.0" + "@typescript-eslint/utils": "npm:8.61.0" + "@typescript-eslint/visitor-keys": "npm:8.61.0" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.5.0" peerDependencies: - "@typescript-eslint/parser": ^8.57.2 + "@typescript-eslint/parser": ^8.61.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10/0735281c26b1e9b3b9ccce3b872ef9eedcfbbffa6b766470de55735cd7a796b064376189378a65c19c62fa59f24187cecb563fb56d194129673be5cd8d3949e9 + typescript: ">=4.8.4 <6.1.0" + checksum: 10/ca7fbaa2f03ec15bdbf39d2e4d42f1b682085f23830591d1d6c3d9f497fdda497341b2fa67c8d366514a3c22807557e45e7afe1ee70cef527b184250e5422e8f languageName: node linkType: hard @@ -4928,19 +4691,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/parser@npm:^8.39.0": - version: 8.57.2 - resolution: "@typescript-eslint/parser@npm:8.57.2" +"@typescript-eslint/parser@npm:8.61.0, @typescript-eslint/parser@npm:^8.39.0": + version: 8.61.0 + resolution: "@typescript-eslint/parser@npm:8.61.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.57.2" - "@typescript-eslint/types": "npm:8.57.2" - "@typescript-eslint/typescript-estree": "npm:8.57.2" - "@typescript-eslint/visitor-keys": "npm:8.57.2" + "@typescript-eslint/scope-manager": "npm:8.61.0" + "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/typescript-estree": "npm:8.61.0" + "@typescript-eslint/visitor-keys": "npm:8.61.0" debug: "npm:^4.4.3" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10/c9a8a03767ac1b1e8d0bee938fb636b30b7043a776d20353b37be32011ed2b174aa906c27b574fe90bbc48d0235320a4339fb5868f02f5a262a58531e244e1f8 + typescript: ">=4.8.4 <6.1.0" + checksum: 10/82060c36786339867d63337708a08bd4bc65569313998bd086dbe6b901664082c7e40d6b6e085296a459cd4fc1d064479ef570b51e1eb113688bb152a7a6d689 languageName: node linkType: hard @@ -4957,16 +4720,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/project-service@npm:8.57.2" +"@typescript-eslint/project-service@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/project-service@npm:8.61.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.57.2" - "@typescript-eslint/types": "npm:^8.57.2" + "@typescript-eslint/tsconfig-utils": "npm:^8.61.0" + "@typescript-eslint/types": "npm:^8.61.0" debug: "npm:^4.4.3" peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10/e7f47d5394c6ca3c92fa373bd3a149ad13549944d0d7a12f0ddef6dc921b017cf283876bb272b1f6c4f90e6c84bac7b140ac08acee3a3f26dfd8b43556e0949e + typescript: ">=4.8.4 <6.1.0" + checksum: 10/b7d7e973b565f604af43b8afb3ca1c3fbe6fcf16863bde83b42417a196ba9f3a5a3f5d39bf57ed96b8ce577047064d93c353ecb21db5e95dce69f81335c9cd81 languageName: node linkType: hard @@ -4980,13 +4743,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/scope-manager@npm:8.57.2" +"@typescript-eslint/scope-manager@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/scope-manager@npm:8.61.0" dependencies: - "@typescript-eslint/types": "npm:8.57.2" - "@typescript-eslint/visitor-keys": "npm:8.57.2" - checksum: 10/2c1498e25481ee6b1de5c6cbf8fbbdfa9e5e940a91ddd12687dd17d68de5df81c286861f0378282fbe49172dd44094bfeb5b63f80faf37e54dce8fd2ddc7f733 + "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/visitor-keys": "npm:8.61.0" + checksum: 10/295e306665d64f0330fede3fe72febd65c67c3083d747149b66097aa6f7d517f25731dc1dbec900b15768c40f92b082f501296e7524855fe82697f40b8d23ce1 languageName: node linkType: hard @@ -4999,12 +4762,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.57.2, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.57.2" +"@typescript-eslint/tsconfig-utils@npm:8.61.0, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.61.0" peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10/2a6e27d541fd0071e5ca14116ba210eab77a8f74cedda32dfce4acce951632971b066261e4b151d34dd0cfad357cb4fd2f7d0b38c9e82a155c226e5c12bd41b3 + typescript: ">=4.8.4 <6.1.0" + checksum: 10/f678ff5ec887a27d8e590e0c67403b12e372a027ab036dcfc1e3ef614d3bed7a3c455a65fa0a87ff7dae5b0ad1c49cf4aa40639cc368d7eb424efe8349d9cb9f languageName: node linkType: hard @@ -5024,19 +4787,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/type-utils@npm:8.57.2" +"@typescript-eslint/type-utils@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/type-utils@npm:8.61.0" dependencies: - "@typescript-eslint/types": "npm:8.57.2" - "@typescript-eslint/typescript-estree": "npm:8.57.2" - "@typescript-eslint/utils": "npm:8.57.2" + "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/typescript-estree": "npm:8.61.0" + "@typescript-eslint/utils": "npm:8.61.0" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.5.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10/9df6b587143b2dd3f9fc17dab74e877013cc3ce21a7266af6d85bef05b4602103aa2c0d612a69f479225b67f089dad2a92f8ebbd474a9df0a256caeaa5aa165b + typescript: ">=4.8.4 <6.1.0" + checksum: 10/8290e5fc26241dfd5aeeffad0fb9857a3fa1f9c8107dfb01638970297e0e17be6088f0fd2d6fc7d450e9879afaa7e23f4111182bcf0b625eba74fdf13100b19e languageName: node linkType: hard @@ -5047,10 +4810,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.57.2, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/types@npm:8.57.2" - checksum: 10/6d30ff13c8ebe01ba8bcce5f1a5ba52f2c6546056e056f50e010394461d2fffda46dad2a4ebdff59c179873abd274242d8f6a8246c60d7ce244d02ad87bb07f8 +"@typescript-eslint/types@npm:8.61.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/types@npm:8.61.0" + checksum: 10/8e1e1cf5d092beed1a974b3b5d7cc20219ad3e4501b85bbef5bec1c81ab50b09ee70093ad2195c3061c499e804d63aac38dcc20293342b1fa774ba743c0d63bf languageName: node linkType: hard @@ -5074,23 +4837,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/typescript-estree@npm:8.57.2" +"@typescript-eslint/typescript-estree@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.61.0" dependencies: - "@typescript-eslint/project-service": "npm:8.57.2" - "@typescript-eslint/tsconfig-utils": "npm:8.57.2" - "@typescript-eslint/types": "npm:8.57.2" - "@typescript-eslint/visitor-keys": "npm:8.57.2" + "@typescript-eslint/project-service": "npm:8.61.0" + "@typescript-eslint/tsconfig-utils": "npm:8.61.0" + "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/visitor-keys": "npm:8.61.0" debug: "npm:^4.4.3" minimatch: "npm:^10.2.2" - minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" tinyglobby: "npm:^0.2.15" ts-api-utils: "npm:^2.5.0" peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10/85d3f2799ee3915b4a06014d90db4aab28c0c0377b40aced5b49acb5f038862e5447f6c355d85368a8f587820f7840e945fa8a9d5347234f1fb070a97c2a9c1d + typescript: ">=4.8.4 <6.1.0" + checksum: 10/6d5ab7850226de23ab26d94388f729e792413f5a9e704c8c685b66eb20946efeb290cda91195f062e1065bc20129ec8f5955768316660132087347e66dec0d1a languageName: node linkType: hard @@ -5109,18 +4871,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/utils@npm:8.57.2" +"@typescript-eslint/utils@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/utils@npm:8.61.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.57.2" - "@typescript-eslint/types": "npm:8.57.2" - "@typescript-eslint/typescript-estree": "npm:8.57.2" + "@typescript-eslint/scope-manager": "npm:8.61.0" + "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/typescript-estree": "npm:8.61.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10/48851aa53b403e67dce7504859f3e5a9e008d43fa8c98dc2b0fc900980c1f77dda60648f957fd69a78cea44e0a94d1d7e807fcbc30ffca3fb688d04be2acd898 + typescript: ">=4.8.4 <6.1.0" + checksum: 10/50ff451edb8e5dee92bbab11a75cbd570715623d89094d0541ddfbef208248e82d2f9478d1e09fb9c94496069afd4db9521384b77f7aaa63970f7edfebddfba9 languageName: node linkType: hard @@ -5134,13 +4896,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.57.2": - version: 8.57.2 - resolution: "@typescript-eslint/visitor-keys@npm:8.57.2" +"@typescript-eslint/visitor-keys@npm:8.61.0": + version: 8.61.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.61.0" dependencies: - "@typescript-eslint/types": "npm:8.57.2" + "@typescript-eslint/types": "npm:8.61.0" eslint-visitor-keys: "npm:^5.0.0" - checksum: 10/8059dcb05694b31b3372f022e25493c4a3fd1a9952720bcb3eac1911828f0b81b3f8aeb11a30dc646880e16396317512747a9b2afee059c0472ea5c4d4546d29 + checksum: 10/243018d9d8b1918d2863e50eec6628c792ccda05ad5534f5153fc783b7f54cdb8a58d758eb74260d113274bfab8bb38ad4664f3db9e7d3f844cdffbe6e47e285 languageName: node linkType: hard @@ -5452,6 +5214,13 @@ __metadata: languageName: node linkType: hard +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10/a32641fb7a8ba0ad6f65efda80a632c965a2567f52c988897bffc47f473c4e9c3f0166de19d939866b1ed58ec50ce36f697d54a476589ca2706f8b5605ed41f0 + languageName: node + linkType: hard + "acorn-globals@npm:^7.0.0": version: 7.0.1 resolution: "acorn-globals@npm:7.0.1" @@ -5472,27 +5241,20 @@ __metadata: linkType: hard "acorn-walk@npm:^8.0.2, acorn-walk@npm:^8.1.1": - version: 8.3.5 - resolution: "acorn-walk@npm:8.3.5" version: 8.3.5 resolution: "acorn-walk@npm:8.3.5" dependencies: acorn: "npm:^8.11.0" checksum: 10/f52a158a1c1f00c82702c7eb9b8ae8aad79748a7689241dcc2d797dce680f1dcb15c78f312f687eeacdfb3a4cac4b87d04af470f0201bd56c6661fca6f94b195 - checksum: 10/f52a158a1c1f00c82702c7eb9b8ae8aad79748a7689241dcc2d797dce680f1dcb15c78f312f687eeacdfb3a4cac4b87d04af470f0201bd56c6661fca6f94b195 languageName: node linkType: hard "acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1": - version: 8.16.0 - resolution: "acorn@npm:8.16.0" -"acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.4.1, acorn@npm:^8.8.1": - version: 8.16.0 - resolution: "acorn@npm:8.16.0" + version: 8.17.0 + resolution: "acorn@npm:8.17.0" bin: acorn: bin/acorn - checksum: 10/690c673bb4d61b38ef82795fab58526471ad7f7e67c0e40c4ff1e10ecd80ce5312554ef633c9995bfc4e6d170cef165711f9ca9e49040b62c0c66fbf2dd3df2b - checksum: 10/690c673bb4d61b38ef82795fab58526471ad7f7e67c0e40c4ff1e10ecd80ce5312554ef633c9995bfc4e6d170cef165711f9ca9e49040b62c0c66fbf2dd3df2b + checksum: 10/2eea1588075124df569b15995423204055c5575ad992283025dddfcb557a0340de7d75cc1bc25dca8df148c60c4222e576e0e519965f0ec7f86f6085c8428824 languageName: node linkType: hard @@ -5556,18 +5318,30 @@ __metadata: linkType: hard "ajv@npm:^6.14.0": - version: 6.14.0 - resolution: "ajv@npm:6.14.0" + version: 6.15.0 + resolution: "ajv@npm:6.15.0" dependencies: fast-deep-equal: "npm:^3.1.1" fast-json-stable-stringify: "npm:^2.0.0" json-schema-traverse: "npm:^0.4.1" uri-js: "npm:^4.2.2" - checksum: 10/c71f14dd2b6f2535d043f74019c8169f7aeb1106bafbb741af96f34fdbf932255c919ddd46344043d03b62ea0ccb319f83667ec5eedf612393f29054fe5ce4a5 + checksum: 10/0916dda09c152fb5857bc1cc7ce61718e9cec5b7faeff44a74f5e324eed8a556e1a84856724ea322a067b436ecad9f74ac8295fd395449788cca52f0c25bd5fb languageName: node linkType: hard -"ajv@npm:^8.0.0, ajv@npm:~8.18.0": +"ajv@npm:^8.0.0": + version: 8.20.0 + resolution: "ajv@npm:8.20.0" + dependencies: + fast-deep-equal: "npm:^3.1.3" + fast-uri: "npm:^3.0.1" + json-schema-traverse: "npm:^1.0.0" + require-from-string: "npm:^2.0.2" + checksum: 10/5ce59c0537f4c2aca9a758b412659ec70acb4d5dde971c10ecf21d2e3d799f99acdb4a08e1f5fb2e067c8542930398aae793bb996bb07d3feb81dae22fe2ada9 + languageName: node + linkType: hard + +"ajv@npm:~8.18.0": version: 8.18.0 resolution: "ajv@npm:8.18.0" dependencies: @@ -5602,7 +5376,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.2.2": "ansi-regex@npm:^6.2.2": version: 6.2.2 resolution: "ansi-regex@npm:6.2.2" @@ -5908,20 +5681,20 @@ __metadata: linkType: hard "axe-core@npm:^4.10.0": - version: 4.12.0 - resolution: "axe-core@npm:4.12.0" - checksum: 10/fbc3cc4d0cb24c259cc8e925d8f9862bc1d1b8c651dced134cc0f98064113b47db7159e3a8aca80f2ccffa9d55ee948023ccbbb922ac9fa209172fd4332ceb6f + version: 4.12.1 + resolution: "axe-core@npm:4.12.1" + checksum: 10/08c2ff348524b57dfb68f4e866b1a6363147203b721c734ed00090c16eb911cd015cf71fd96a8bafe42609715f649d21b2e188fae622ac8ef6144b144582f6c4 languageName: node linkType: hard -"axios@npm:^1.0.0": - version: 1.13.6 - resolution: "axios@npm:1.13.6" +"axios@npm:1.16.0": + version: 1.16.0 + resolution: "axios@npm:1.16.0" dependencies: - follow-redirects: "npm:^1.15.11" + follow-redirects: "npm:^1.16.0" form-data: "npm:^4.0.5" - proxy-from-env: "npm:^1.1.0" - checksum: 10/a7ed83c2af3ef21d64609df0f85e76893a915a864c5934df69241001d0578082d6521a0c730bf37518ee458821b5695957cb10db9fc705f2a8996c8686ea7a89 + proxy-from-env: "npm:^2.1.0" + checksum: 10/cf8b521ff732c21550b38c8739aef556ea5e14b268468bb89e4307416ab262b462e582474e810963a3d61c2392ab47ad35c11d05eff027de7c97113bc7411623 languageName: node linkType: hard @@ -6045,19 +5818,19 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:1.5.1, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10/669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.9.0, baseline-browser-mapping@npm:^2.9.19": - version: 2.10.10 - resolution: "baseline-browser-mapping@npm:2.10.10" +"baseline-browser-mapping@npm:^2.10.12, baseline-browser-mapping@npm:^2.9.19": + version: 2.10.37 + resolution: "baseline-browser-mapping@npm:2.10.37" bin: baseline-browser-mapping: dist/cli.cjs - checksum: 10/c956e25cd566bf36dcd594a3f3050d33ed24309bd84572469ac2f4332942efd30b8b014241484a3b6101bd975d32d58def2df9f7881d66ff11e0ab8069ae99ae + checksum: 10/5bf887e82a238ab2ec216283ab2c44291d5ad917b4c4d21bb88f466be513223bda20d746c2758cdb0be5b3dad1e52ee8b468dbc196096da92673d6904992af66 languageName: node linkType: hard @@ -6121,20 +5894,11 @@ __metadata: linkType: hard "brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2": - version: 2.0.2 - resolution: "brace-expansion@npm:2.0.2" + version: 2.1.1 + resolution: "brace-expansion@npm:2.1.1" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10/01dff195e3646bc4b0d27b63d9bab84d2ebc06121ff5013ad6e5356daa5a9d6b60fa26cf73c74797f2dc3fbec112af13578d51f75228c1112b26c790a87b0488 - languageName: node - linkType: hard - -"brace-expansion@npm:^5.0.2": - version: 5.0.5 - resolution: "brace-expansion@npm:5.0.5" - dependencies: - balanced-match: "npm:^4.0.2" - checksum: 10/f259b2ddf04489da9512ad637ba6b4ef2d77abd4445d20f7f1714585f153435200a53fa6a2e4a5ee974df14ddad4cd16421f6f803e96e8b452bd48598878d0ee + checksum: 10/4681c533dc4e6c77b3ad795b38683d297fd03c739a17bfb2a338529fa7dcf4540683a79dcd662905f4c5b0db7cfda18daafcd18dd1bbf7c3b076fe0c9c3487eb languageName: node linkType: hard @@ -6218,40 +5982,7 @@ __metadata: languageName: node linkType: hard -"byte-size@npm:^7.0.0": - version: 7.0.1 - resolution: "byte-size@npm:7.0.1" - checksum: 10/823afd08712cb58da94a37307c4a2e695c8db3fde6e24a3a4267552f526d57c976daaf4f027a724f89e395a8673f4af4be1e5bb5bb30ba00dadfde58909158cf - languageName: node - linkType: hard - -"cacache@npm:^16.0.0, cacache@npm:^16.0.6, cacache@npm:^16.1.0": - version: 16.1.3 - resolution: "cacache@npm:16.1.3" - dependencies: - "@npmcli/fs": "npm:^2.1.0" - "@npmcli/move-file": "npm:^2.0.0" - chownr: "npm:^2.0.0" - fs-minipass: "npm:^2.1.0" - glob: "npm:^8.0.1" - infer-owner: "npm:^1.0.4" - lru-cache: "npm:^7.7.1" - minipass: "npm:^3.1.6" - minipass-collect: "npm:^1.0.2" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - mkdirp: "npm:^1.0.4" - p-map: "npm:^4.0.0" - promise-inflight: "npm:^1.0.1" - rimraf: "npm:^3.0.2" - ssri: "npm:^9.0.0" - tar: "npm:^6.1.11" - unique-filename: "npm:^2.0.0" - checksum: 10/a14524d90e377ee691d63a81173b33c473f8bc66eb299c64290b58e1d41b28842397f8d6c15a01b4c57ca340afcec019ae112a45c2f67a79f76130d326472e92 - languageName: node - linkType: hard - -"cacache@npm:^20.0.1": +"cacache@npm:^20.0.0, cacache@npm:^20.0.1": version: 20.0.4 resolution: "cacache@npm:20.0.4" dependencies: @@ -6266,7 +5997,6 @@ __metadata: p-map: "npm:^7.0.2" ssri: "npm:^13.0.0" checksum: 10/02c1b4c57dc2473e6f4654220c9405b73ae5fcdb392f82a7cf535468a52b842690cdb3694861d13bbe4dc067d5f8abe9697b4f791ae5b65cd73d62abad1e3e54 - checksum: 10/02c1b4c57dc2473e6f4654220c9405b73ae5fcdb392f82a7cf535468a52b842690cdb3694861d13bbe4dc067d5f8abe9697b4f791ae5b65cd73d62abad1e3e54 languageName: node linkType: hard @@ -6346,10 +6076,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001759": - version: 1.0.30001781 - resolution: "caniuse-lite@npm:1.0.30001781" - checksum: 10/13000e0bbb340d5b7d2543293c84439acf3ac6b643ae9a5b69e92138c95471b5e97067a8eb2b28e79b67d1390cc2a51ba2d5c474f745f7f1f7a955392fd0ec4f +"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001782": + version: 1.0.30001799 + resolution: "caniuse-lite@npm:1.0.30001799" + checksum: 10/eb90443f1e4e4ac7cfe3686d43f0d132c0b552d0d896c0520e7306f2ddf743b4dd5380a7b8adc5ca8d250247966a6cf32cb042930dbc1df452e8623ad92c57e2 languageName: node linkType: hard @@ -6486,7 +6216,7 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.2.0": +"ci-info@npm:^4.0.0, ci-info@npm:^4.2.0": version: 4.4.0 resolution: "ci-info@npm:4.4.0" checksum: 10/dfded0c630267d89660c8abb988ac8395a382bdfefedcc03e3e2858523312c5207db777c239c34774e3fcff11f015477c19d2ac8a58ea58aa476614a2e64f434 @@ -7145,8 +6875,6 @@ __metadata: linkType: hard "dedent@npm:^1.0.0": - version: 1.7.2 - resolution: "dedent@npm:1.7.2" version: 1.7.2 resolution: "dedent@npm:1.7.2" peerDependencies: @@ -7155,7 +6883,6 @@ __metadata: babel-plugin-macros: optional: true checksum: 10/30b9062290dca72b0f5a6cd3667633448cef8cd0dec602eab61015741269ad49df90cabf0521f9a32d134ceab4e21aa7f097258c55cc3baadef94874686d6480 - checksum: 10/30b9062290dca72b0f5a6cd3667633448cef8cd0dec602eab61015741269ad49df90cabf0521f9a32d134ceab4e21aa7f097258c55cc3baadef94874686d6480 languageName: node linkType: hard @@ -7342,9 +7069,6 @@ __metadata: linkType: hard "diff@npm:~8.0.2": - version: 8.0.4 - resolution: "diff@npm:8.0.4" - checksum: 10/b4036ceda0d1e10683a2313079ed52c5e6b09553ae29da87bce81d98714d9725dbf3c0f6f7c3b1f16eec049fe17087e38ee329e732580fa62f6ec1c2487b2435 version: 8.0.4 resolution: "diff@npm:8.0.4" checksum: 10/b4036ceda0d1e10683a2313079ed52c5e6b09553ae29da87bce81d98714d9725dbf3c0f6f7c3b1f16eec049fe17087e38ee329e732580fa62f6ec1c2487b2435 @@ -7455,10 +7179,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.263": - version: 1.5.325 - resolution: "electron-to-chromium@npm:1.5.325" - checksum: 10/5cf705b7abe78b49c1e0a7b3c424636712c671a89c2172b646bc5ca41d377c094ef5efe16e5988f4d5256b3027d48ffd3dc5e6e1e96977b2c24f33c63bd8065f +"electron-to-chromium@npm:^1.5.328": + version: 1.5.372 + resolution: "electron-to-chromium@npm:1.5.372" + checksum: 10/cf4dde2ce99fea74416377c029904ddf543ad821be98e6e27be724fa2e4db2962948d1147560ca7810ac1bd6cb1636feb9b34e3654f14c75ecbddfa5fb57ea1d languageName: node linkType: hard @@ -7643,8 +7367,8 @@ __metadata: linkType: hard "es-iterator-helpers@npm:^1.2.1": - version: 1.3.1 - resolution: "es-iterator-helpers@npm:1.3.1" + version: 1.3.3 + resolution: "es-iterator-helpers@npm:1.3.3" dependencies: call-bind: "npm:^1.0.9" call-bound: "npm:^1.0.4" @@ -7662,8 +7386,7 @@ __metadata: internal-slot: "npm:^1.1.0" iterator.prototype: "npm:^1.1.5" math-intrinsics: "npm:^1.1.0" - safe-array-concat: "npm:^1.1.3" - checksum: 10/38106c081426faa6a8c27f44ee653d81350944b449fad81caa032cc02c31280be11fd302d065da3b062534390040c58e8aab55ff897b5ef1ddf060079570c70d + checksum: 10/53a45f693088f51d8aeda4034f1be9d7d4fc8505ee58f70bbb237a63729efccf2f96225e15e2b2ac7815104739e6d244019637609ee7c9ee171b8248585ecfae languageName: node linkType: hard @@ -7731,36 +7454,36 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:~0.27.0": - version: 0.27.4 - resolution: "esbuild@npm:0.27.4" - dependencies: - "@esbuild/aix-ppc64": "npm:0.27.4" - "@esbuild/android-arm": "npm:0.27.4" - "@esbuild/android-arm64": "npm:0.27.4" - "@esbuild/android-x64": "npm:0.27.4" - "@esbuild/darwin-arm64": "npm:0.27.4" - "@esbuild/darwin-x64": "npm:0.27.4" - "@esbuild/freebsd-arm64": "npm:0.27.4" - "@esbuild/freebsd-x64": "npm:0.27.4" - "@esbuild/linux-arm": "npm:0.27.4" - "@esbuild/linux-arm64": "npm:0.27.4" - "@esbuild/linux-ia32": "npm:0.27.4" - "@esbuild/linux-loong64": "npm:0.27.4" - "@esbuild/linux-mips64el": "npm:0.27.4" - "@esbuild/linux-ppc64": "npm:0.27.4" - "@esbuild/linux-riscv64": "npm:0.27.4" - "@esbuild/linux-s390x": "npm:0.27.4" - "@esbuild/linux-x64": "npm:0.27.4" - "@esbuild/netbsd-arm64": "npm:0.27.4" - "@esbuild/netbsd-x64": "npm:0.27.4" - "@esbuild/openbsd-arm64": "npm:0.27.4" - "@esbuild/openbsd-x64": "npm:0.27.4" - "@esbuild/openharmony-arm64": "npm:0.27.4" - "@esbuild/sunos-x64": "npm:0.27.4" - "@esbuild/win32-arm64": "npm:0.27.4" - "@esbuild/win32-ia32": "npm:0.27.4" - "@esbuild/win32-x64": "npm:0.27.4" +"esbuild@npm:^0.27.0": + version: 0.27.7 + resolution: "esbuild@npm:0.27.7" + dependencies: + "@esbuild/aix-ppc64": "npm:0.27.7" + "@esbuild/android-arm": "npm:0.27.7" + "@esbuild/android-arm64": "npm:0.27.7" + "@esbuild/android-x64": "npm:0.27.7" + "@esbuild/darwin-arm64": "npm:0.27.7" + "@esbuild/darwin-x64": "npm:0.27.7" + "@esbuild/freebsd-arm64": "npm:0.27.7" + "@esbuild/freebsd-x64": "npm:0.27.7" + "@esbuild/linux-arm": "npm:0.27.7" + "@esbuild/linux-arm64": "npm:0.27.7" + "@esbuild/linux-ia32": "npm:0.27.7" + "@esbuild/linux-loong64": "npm:0.27.7" + "@esbuild/linux-mips64el": "npm:0.27.7" + "@esbuild/linux-ppc64": "npm:0.27.7" + "@esbuild/linux-riscv64": "npm:0.27.7" + "@esbuild/linux-s390x": "npm:0.27.7" + "@esbuild/linux-x64": "npm:0.27.7" + "@esbuild/netbsd-arm64": "npm:0.27.7" + "@esbuild/netbsd-x64": "npm:0.27.7" + "@esbuild/openbsd-arm64": "npm:0.27.7" + "@esbuild/openbsd-x64": "npm:0.27.7" + "@esbuild/openharmony-arm64": "npm:0.27.7" + "@esbuild/sunos-x64": "npm:0.27.7" + "@esbuild/win32-arm64": "npm:0.27.7" + "@esbuild/win32-ia32": "npm:0.27.7" + "@esbuild/win32-x64": "npm:0.27.7" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7816,40 +7539,40 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/32b46ec22ef78bae6cc141145022a4c0209852c07151f037fbefccc2033ca54e7f33705f8fca198eb7026f400142f64c2dbc9f0d0ce9c0a638ebc472a04abc4a + checksum: 10/262b16c4a33cb70e9f054759a7ce420541649315eef7b064172c795021ccce322e56c3f5fd52e8842873f1c23745f3ab62311a24860950bd5406ba77b36b8529 languageName: node linkType: hard "esbuild@npm:~0.28.0": - version: 0.28.0 - resolution: "esbuild@npm:0.28.0" - dependencies: - "@esbuild/aix-ppc64": "npm:0.28.0" - "@esbuild/android-arm": "npm:0.28.0" - "@esbuild/android-arm64": "npm:0.28.0" - "@esbuild/android-x64": "npm:0.28.0" - "@esbuild/darwin-arm64": "npm:0.28.0" - "@esbuild/darwin-x64": "npm:0.28.0" - "@esbuild/freebsd-arm64": "npm:0.28.0" - "@esbuild/freebsd-x64": "npm:0.28.0" - "@esbuild/linux-arm": "npm:0.28.0" - "@esbuild/linux-arm64": "npm:0.28.0" - "@esbuild/linux-ia32": "npm:0.28.0" - "@esbuild/linux-loong64": "npm:0.28.0" - "@esbuild/linux-mips64el": "npm:0.28.0" - "@esbuild/linux-ppc64": "npm:0.28.0" - "@esbuild/linux-riscv64": "npm:0.28.0" - "@esbuild/linux-s390x": "npm:0.28.0" - "@esbuild/linux-x64": "npm:0.28.0" - "@esbuild/netbsd-arm64": "npm:0.28.0" - "@esbuild/netbsd-x64": "npm:0.28.0" - "@esbuild/openbsd-arm64": "npm:0.28.0" - "@esbuild/openbsd-x64": "npm:0.28.0" - "@esbuild/openharmony-arm64": "npm:0.28.0" - "@esbuild/sunos-x64": "npm:0.28.0" - "@esbuild/win32-arm64": "npm:0.28.0" - "@esbuild/win32-ia32": "npm:0.28.0" - "@esbuild/win32-x64": "npm:0.28.0" + version: 0.28.1 + resolution: "esbuild@npm:0.28.1" + dependencies: + "@esbuild/aix-ppc64": "npm:0.28.1" + "@esbuild/android-arm": "npm:0.28.1" + "@esbuild/android-arm64": "npm:0.28.1" + "@esbuild/android-x64": "npm:0.28.1" + "@esbuild/darwin-arm64": "npm:0.28.1" + "@esbuild/darwin-x64": "npm:0.28.1" + "@esbuild/freebsd-arm64": "npm:0.28.1" + "@esbuild/freebsd-x64": "npm:0.28.1" + "@esbuild/linux-arm": "npm:0.28.1" + "@esbuild/linux-arm64": "npm:0.28.1" + "@esbuild/linux-ia32": "npm:0.28.1" + "@esbuild/linux-loong64": "npm:0.28.1" + "@esbuild/linux-mips64el": "npm:0.28.1" + "@esbuild/linux-ppc64": "npm:0.28.1" + "@esbuild/linux-riscv64": "npm:0.28.1" + "@esbuild/linux-s390x": "npm:0.28.1" + "@esbuild/linux-x64": "npm:0.28.1" + "@esbuild/netbsd-arm64": "npm:0.28.1" + "@esbuild/netbsd-x64": "npm:0.28.1" + "@esbuild/openbsd-arm64": "npm:0.28.1" + "@esbuild/openbsd-x64": "npm:0.28.1" + "@esbuild/openharmony-arm64": "npm:0.28.1" + "@esbuild/sunos-x64": "npm:0.28.1" + "@esbuild/win32-arm64": "npm:0.28.1" + "@esbuild/win32-ia32": "npm:0.28.1" + "@esbuild/win32-x64": "npm:0.28.1" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7905,7 +7628,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/49eafc8906cc4a760a1704556bd3b301f808fcdcf2725190383f151741226bf2a2898a03da75a06a896d6217dadc4f3f3168983557ee31bae602e2e37779a83a + checksum: 10/aaa4a922644afffac45e735c99caf343f881e2d36abcc6b6fb53c230bd69940504a5bb6b0041bdd1a690e748ebc681d3308a7d178987c523d74c63c2c280bac8 languageName: node linkType: hard @@ -8230,10 +7953,6 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": - version: 5.0.1 - resolution: "eslint-visitor-keys@npm:5.0.1" - checksum: 10/f9cc1a57b75e0ef949545cac33d01e8367e302de4c1483266ed4d8646ee5c306376660196bbb38b004e767b7043d1e661cb4336b49eff634a1bbe75c1db709ec "eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": version: 5.0.1 resolution: "eslint-visitor-keys@npm:5.0.1" @@ -8242,19 +7961,14 @@ __metadata: linkType: hard "eslint@npm:^9.32.0, eslint@npm:^9.39.1": - version: 9.39.4 - resolution: "eslint@npm:9.39.4" version: 9.39.4 resolution: "eslint@npm:9.39.4" dependencies: "@eslint-community/eslint-utils": "npm:^4.8.0" "@eslint-community/regexpp": "npm:^4.12.1" "@eslint/config-array": "npm:^0.21.2" - "@eslint/config-array": "npm:^0.21.2" "@eslint/config-helpers": "npm:^0.4.2" - "@eslint/core": "npm:^0.17.0" - "@eslint/eslintrc": "npm:^3.3.5" - "@eslint/js": "npm:9.39.4" + "@eslint/core": "npm:^0.17.0" "@eslint/eslintrc": "npm:^3.3.5" "@eslint/js": "npm:9.39.4" "@eslint/plugin-kit": "npm:^0.4.1" @@ -8263,7 +7977,6 @@ __metadata: "@humanwhocodes/retry": "npm:^0.4.2" "@types/estree": "npm:^1.0.6" ajv: "npm:^6.14.0" - ajv: "npm:^6.14.0" chalk: "npm:^4.0.0" cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" @@ -8283,7 +7996,6 @@ __metadata: json-stable-stringify-without-jsonify: "npm:^1.0.1" lodash.merge: "npm:^4.6.2" minimatch: "npm:^3.1.5" - minimatch: "npm:^3.1.5" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" peerDependencies: @@ -8294,7 +8006,6 @@ __metadata: bin: eslint: bin/eslint.js checksum: 10/de95093d710e62e8c7e753220d985587c40f4a05247ed4393ffb6e6cb43a60e825a47fc5b4263151bb2fc5871a206a31d563ccbabdceee1711072ae12db90adf - checksum: 10/de95093d710e62e8c7e753220d985587c40f4a05247ed4393ffb6e6cb43a60e825a47fc5b4263151bb2fc5871a206a31d563ccbabdceee1711072ae12db90adf languageName: node linkType: hard @@ -8310,17 +8021,12 @@ __metadata: linkType: hard "espree@npm:^11.0.0": - version: 11.2.0 - resolution: "espree@npm:11.2.0" version: 11.2.0 resolution: "espree@npm:11.2.0" dependencies: - acorn: "npm:^8.16.0" acorn: "npm:^8.16.0" acorn-jsx: "npm:^5.3.2" eslint-visitor-keys: "npm:^5.0.1" - checksum: 10/5cc4233b8f150010c70713669ef8231f07fe9b6391870cfa0a292a07f723ed5c2922064b978e627f7cabf7753280e64c5bde41d3840caaa40946989df7009a51 - eslint-visitor-keys: "npm:^5.0.1" checksum: 10/5cc4233b8f150010c70713669ef8231f07fe9b6391870cfa0a292a07f723ed5c2922064b978e627f7cabf7753280e64c5bde41d3840caaa40946989df7009a51 languageName: node linkType: hard @@ -8424,17 +8130,24 @@ __metadata: languageName: node linkType: hard -"expect@npm:30.3.0": - version: 30.3.0 - resolution: "expect@npm:30.3.0" +"expect-type@npm:^1.2.1": + version: 1.3.0 + resolution: "expect-type@npm:1.3.0" + checksum: 10/a5fada3d0c621649261f886e7d93e6bf80ce26d8a86e5d517e38301b8baec8450ab2cb94ba6e7a0a6bf2fc9ee55f54e1b06938ef1efa52ddcfeffbfa01acbbcc + languageName: node + linkType: hard + +"expect@npm:30.4.1": + version: 30.4.1 + resolution: "expect@npm:30.4.1" dependencies: - "@jest/expect-utils": "npm:30.3.0" + "@jest/expect-utils": "npm:30.4.1" "@jest/get-type": "npm:30.1.0" - jest-matcher-utils: "npm:30.3.0" - jest-message-util: "npm:30.3.0" - jest-mock: "npm:30.3.0" - jest-util: "npm:30.3.0" - checksum: 10/607748963fd2cf2b95ec848d59086afdff5e6b690d1ddd907f84514687f32a787896281ba49a5fda2af819238bec7fdeaf258814997d2b08eedc0968de57f3bd + jest-matcher-utils: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + checksum: 10/f25051e5073c55369199ec3108ac01c60074bd09dad0b5a6c9fe40596438051cb52607e0e97505126422535b8d0dacab13aa29bb14e9fac71630bc710201a3f1 languageName: node linkType: hard @@ -8582,14 +8295,11 @@ __metadata: linkType: hard "filelist@npm:^1.0.4": - version: 1.0.6 - resolution: "filelist@npm:1.0.6" version: 1.0.6 resolution: "filelist@npm:1.0.6" dependencies: minimatch: "npm:^5.0.1" checksum: 10/84a0be69efe6724c105f18c34e8a772370d9c45e53a1ba8ced7eecf4addd2c5a357347d94bfd8bfa9cbc36b09392cad70d82206305263e26bba184eea4ca8042 - checksum: 10/84a0be69efe6724c105f18c34e8a772370d9c45e53a1ba8ced7eecf4addd2c5a357347d94bfd8bfa9cbc36b09392cad70d82206305263e26bba184eea4ca8042 languageName: node linkType: hard @@ -8672,18 +8382,15 @@ __metadata: linkType: hard "flatted@npm:^3.2.9": - version: 3.4.2 - resolution: "flatted@npm:3.4.2" - checksum: 10/a9e78fe5c2c1fcd98209a015ccee3a6caa953e01729778e83c1fe92e68601a63e1e69cd4e573010ca99eaf585a581b80ccf1018b99283e6cbc2117bcba1e030f version: 3.4.2 resolution: "flatted@npm:3.4.2" checksum: 10/a9e78fe5c2c1fcd98209a015ccee3a6caa953e01729778e83c1fe92e68601a63e1e69cd4e573010ca99eaf585a581b80ccf1018b99283e6cbc2117bcba1e030f languageName: node linkType: hard -"follow-redirects@npm:^1.15.11": - version: 1.15.11 - resolution: "follow-redirects@npm:1.15.11" +"follow-redirects@npm:1.16.0, follow-redirects@npm:^1.16.0": + version: 1.16.0 + resolution: "follow-redirects@npm:1.16.0" peerDependenciesMeta: debug: optional: true @@ -8720,7 +8427,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0, form-data@npm:^4.0.5": +"form-data@npm:4.0.5": version: 4.0.5 resolution: "form-data@npm:4.0.5" dependencies: @@ -8733,6 +8440,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0, form-data@npm:^4.0.5": + version: 4.0.6 + resolution: "form-data@npm:4.0.6" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.4" + mime-types: "npm:^2.1.35" + checksum: 10/de6614c8537c92fa5fa3ee7e827758f98f5a9c033f348b7de81855ef36e5cb867e75d9f405d9483ab8d724a4a20d4e79926a299fa8dbba38f530eb659f0884e4 + languageName: node + linkType: hard + "fromentries@npm:^1.2.0": version: 1.3.2 resolution: "fromentries@npm:1.3.2" @@ -8747,14 +8467,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.1.0, fs-extra@npm:^11.3.0, fs-extra@npm:~11.3.0": - version: 11.3.4 - resolution: "fs-extra@npm:11.3.4" +"fs-extra@npm:^11.2.0, fs-extra@npm:^11.3.0, fs-extra@npm:~11.3.0": + version: 11.3.5 + resolution: "fs-extra@npm:11.3.5" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/1b8deea9c540a2efe63c750bc9e1ba6238115579d1571d67fe8fb58e3fb6df19aba29fd4ebb81217cf0bf5bce0df30ca68dbc3e06f6652b856edd385ce0ff649 + checksum: 10/dc8408818eec8b03efad8742d079ecab749a2f7bc9f208e429b447fcac7632fae52e09312d6d42218efe7e2efa97f03ff232d639ade4aa7fcd8c00ebe9ad0c0c languageName: node linkType: hard @@ -8823,16 +8543,19 @@ __metadata: linkType: hard "function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": - version: 1.1.8 - resolution: "function.prototype.name@npm:1.1.8" + version: 1.2.0 + resolution: "function.prototype.name@npm:1.2.0" dependencies: - call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" - define-properties: "npm:^1.2.1" + call-bind: "npm:^1.0.9" + call-bound: "npm:^1.0.4" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" functions-have-names: "npm:^1.2.3" - hasown: "npm:^2.0.2" + has-property-descriptors: "npm:^1.0.2" + hasown: "npm:^2.0.4" is-callable: "npm:^1.2.7" - checksum: 10/25b9e5bea936732a6f0c0c08db58cc0d609ac1ed458c6a07ead46b32e7b9bf3fe5887796c3f83d35994efbc4fdde81c08ac64135b2c399b8f2113968d44082bc + is-document.all: "npm:^1.0.0" + checksum: 10/ad662230bc2b9e971625222b462142b34aa23c70ca58fb4fa72e226bb9106a5752be5c7d8986de7ce5cfb959e5317200d70d88d96359605a165ed1c8cb515223 languageName: node linkType: hard @@ -8966,12 +8689,12 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.10.0, get-tsconfig@npm:^4.7.5": - version: 4.13.7 - resolution: "get-tsconfig@npm:4.13.7" +"get-tsconfig@npm:^4.10.0": + version: 4.14.0 + resolution: "get-tsconfig@npm:4.14.0" dependencies: resolve-pkg-maps: "npm:^1.0.0" - checksum: 10/e23622bd3c5766a2fe43a28cb7a490ebb175eb7f429e4ec53688e3b7a40882fb0ec6f3b753f237757d63861ccd8033232d1d76f8960a683af8e09099e7c897fe + checksum: 10/f5626971905ca386c9ddeb302504e8a2301b9c05641803267223ebd50b7c81aaf9324d29cf9f4e4ff3f245632c3392aa83719dc6cb3e98b4e4a1702a27c5cc5d languageName: node linkType: hard @@ -9089,18 +8812,12 @@ __metadata: linkType: hard "glob@npm:^13.0.0": - version: 13.0.6 - resolution: "glob@npm:13.0.6" version: 13.0.6 resolution: "glob@npm:13.0.6" dependencies: minimatch: "npm:^10.2.2" minipass: "npm:^7.1.3" path-scurry: "npm:^2.0.2" - checksum: 10/201ad69e5f0aa74e1d8c00a481581f8b8c804b6a4fbfabeeb8541f5d756932800331daeba99b58fb9e4cd67e12ba5a7eba5b82fb476691588418060b84353214 - minimatch: "npm:^10.2.2" - minipass: "npm:^7.1.3" - path-scurry: "npm:^2.0.2" checksum: 10/201ad69e5f0aa74e1d8c00a481581f8b8c804b6a4fbfabeeb8541f5d756932800331daeba99b58fb9e4cd67e12ba5a7eba5b82fb476691588418060b84353214 languageName: node linkType: hard @@ -9205,9 +8922,9 @@ __metadata: linkType: hard "graphql@npm:^16.11.0": - version: 16.13.2 - resolution: "graphql@npm:16.13.2" - checksum: 10/9ede86f0a7227d47a41e2076e0132839f66fbd14899abfd818d7de2ab81076ee249c8788ad42243367d68a027a8763cc4c7be2e441ccfb03a8549490c7b66c2f + version: 16.14.2 + resolution: "graphql@npm:16.14.2" + checksum: 10/cd9a2581508f82621112a4dc625714e81ce725ab0a62502d97c14f61fee180e85276ac64bf9094f4fbf237737832ad4bb970aa26088f9dffece56f8919a49a29 languageName: node linkType: hard @@ -9310,7 +9027,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.2, hasown@npm:^2.0.3": +"hasown@npm:^2.0.2, hasown@npm:^2.0.3, hasown@npm:^2.0.4": version: 2.0.4 resolution: "hasown@npm:2.0.4" dependencies: @@ -9328,6 +9045,15 @@ __metadata: languageName: node linkType: hard +"heimdalljs@npm:^0.2.6": + version: 0.2.6 + resolution: "heimdalljs@npm:0.2.6" + dependencies: + rsvp: "npm:~3.2.1" + checksum: 10/ae31e21241a30224657c01c05b4c1cba6b370e502e6b9f00de81df7387b2df960b6c526fe7d1e9456b9375bdc64c3178dcae1ad88cd0d68a1ba295935627ac23 + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -9467,7 +9193,6 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.7.0, iconv-lite@npm:^0.7.2": "iconv-lite@npm:^0.7.0, iconv-lite@npm:^0.7.2": version: 0.7.2 resolution: "iconv-lite@npm:0.7.2" @@ -9812,6 +9537,15 @@ __metadata: languageName: node linkType: hard +"is-document.all@npm:^1.0.0": + version: 1.0.0 + resolution: "is-document.all@npm:1.0.0" + dependencies: + call-bound: "npm:^1.0.4" + checksum: 10/c76fa391105f180e9d34bf219ab1db325b4f883d2d82c789dbf9a628e4213c97411f038f36b7d096d85f5ddc1fda6e22e9d8d7c65b89ad1ee5d4d1e5a2a4c077 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -10128,9 +9862,9 @@ __metadata: linkType: hard "isbot@npm:^5.1.39": - version: 5.1.41 - resolution: "isbot@npm:5.1.41" - checksum: 10/309ac6904dd0d21e731c76960cd947cf3b2695d0c127c01e7bbc3688aeb60349b27371fd6f4174138b06903bf3c626cf97eae726ee8e80a90ca3f55a20dc5790 + version: 5.1.43 + resolution: "isbot@npm:5.1.43" + checksum: 10/bbe588f40bc6a880c6db5fbcf5a56c9062eeb827e0335fe4b3dd0c5ef9e9fc152a78f2414a07b1e68101f1bd59866c513bac29087f758b62abf20acc34dd0462 languageName: node linkType: hard @@ -10141,10 +9875,10 @@ __metadata: languageName: node linkType: hard -"isexe@npm:^4.0.0": - version: 4.0.0 - resolution: "isexe@npm:4.0.0" - checksum: 10/2ead327ef596042ef9c9ec5f236b316acfaedb87f4bb61b3c3d574fb2e9c8a04b67305e04733bde52c24d9622fdebd3270aadb632adfbf9cadef88fe30f479e5 +"isexe@npm:^3.1.1": + version: 3.1.5 + resolution: "isexe@npm:3.1.5" + checksum: 10/ae479f6b0505507f1a73c1d57be6d0546e59fc9202bb0d5b31ba8ff5f3e79dd385bce57a2e60c5645aba567018cbbfc663519cd28367e9962242d97dd151d2ab languageName: node linkType: hard @@ -10271,14 +10005,10 @@ __metadata: linkType: hard "jackspeak@npm:^4.1.1": - version: 4.2.3 - resolution: "jackspeak@npm:4.2.3" version: 4.2.3 resolution: "jackspeak@npm:4.2.3" dependencies: "@isaacs/cliui": "npm:^9.0.0" - checksum: 10/b88e3fe5fa04d34f0f939a15b7cef4a8589999b7a366ef89a3e0f2c45d2a7666066b67cbf46d57c3a4796a76d27b9d869b23d96a803dd834200d222c2a70de7e - "@isaacs/cliui": "npm:^9.0.0" checksum: 10/b88e3fe5fa04d34f0f939a15b7cef4a8589999b7a366ef89a3e0f2c45d2a7666066b67cbf46d57c3a4796a76d27b9d869b23d96a803dd834200d222c2a70de7e languageName: node linkType: hard @@ -10399,15 +10129,15 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:30.3.0": - version: 30.3.0 - resolution: "jest-diff@npm:30.3.0" +"jest-diff@npm:30.4.1, jest-diff@npm:>=30.0.0 < 31": + version: 30.4.1 + resolution: "jest-diff@npm:30.4.1" dependencies: - "@jest/diff-sequences": "npm:30.3.0" + "@jest/diff-sequences": "npm:30.4.0" "@jest/get-type": "npm:30.1.0" chalk: "npm:^4.1.2" - pretty-format: "npm:30.3.0" - checksum: 10/9f566259085e6badd525dc48ee6de3792cfae080abd66e170ac230359cf32c4334d92f0f48b577a31ad2a6aed4aefde81f5f4366ab44a96f78bcde975e5cc26e + pretty-format: "npm:30.4.1" + checksum: 10/594212df96bf101170afdb7eebd188d6d7d27241cbdd18b61d95f1142a3c94ae3b270377d15e719fb3c5efe4458d32acba8ad13dd6230dd7d6917a9eebb32625 languageName: node linkType: hard @@ -10487,25 +10217,25 @@ __metadata: languageName: node linkType: hard -"jest-haste-map@npm:30.3.0": - version: 30.3.0 - resolution: "jest-haste-map@npm:30.3.0" +"jest-haste-map@npm:30.4.1": + version: 30.4.1 + resolution: "jest-haste-map@npm:30.4.1" dependencies: - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" "@types/node": "npm:*" anymatch: "npm:^3.1.3" fb-watchman: "npm:^2.0.2" fsevents: "npm:^2.3.3" graceful-fs: "npm:^4.2.11" - jest-regex-util: "npm:30.0.1" - jest-util: "npm:30.3.0" - jest-worker: "npm:30.3.0" + jest-regex-util: "npm:30.4.0" + jest-util: "npm:30.4.1" + jest-worker: "npm:30.4.1" picomatch: "npm:^4.0.3" walker: "npm:^1.0.8" dependenciesMeta: fsevents: optional: true - checksum: 10/0e0cc449d57414ac2d1f9ece64a98ffc4b4041fe3fba7cf9aaeb71089f7101583b1752e88aa4440d6fa71f86ef50d630be4f31f922cdf404d78655cb9811493b + checksum: 10/d26404c7258d03fa423604191bca39707438ca1e62a9a471c92fcd468fa386cbdce2c50b3834fb830b25836e3eee34e3070d22b016b42f0ab626c157f5726eeb languageName: node linkType: hard @@ -10542,15 +10272,15 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:30.3.0": - version: 30.3.0 - resolution: "jest-matcher-utils@npm:30.3.0" +"jest-matcher-utils@npm:30.4.1": + version: 30.4.1 + resolution: "jest-matcher-utils@npm:30.4.1" dependencies: "@jest/get-type": "npm:30.1.0" chalk: "npm:^4.1.2" - jest-diff: "npm:30.3.0" - pretty-format: "npm:30.3.0" - checksum: 10/8aeef24fe2a21a3a22eb26a805c0a4c8ca8961aa1ebc07d680bf55b260f593814467bdfb60b271a3c239a411b2468f352c279cef466e35fd024d901ffa6cc942 + jest-diff: "npm:30.4.1" + pretty-format: "npm:30.4.1" + checksum: 10/4da6e5c7fe5903fae7394233ea4b892567fb027065670c03096d01be0b389f858055c5ade20d59e82fedec6f3287e6f1720de526cd9a9ad3495432320adb9194 languageName: node linkType: hard @@ -10566,20 +10296,21 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:30.3.0": - version: 30.3.0 - resolution: "jest-message-util@npm:30.3.0" +"jest-message-util@npm:30.4.1": + version: 30.4.1 + resolution: "jest-message-util@npm:30.4.1" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" "@types/stack-utils": "npm:^2.0.3" chalk: "npm:^4.1.2" graceful-fs: "npm:^4.2.11" + jest-util: "npm:30.4.1" picomatch: "npm:^4.0.3" - pretty-format: "npm:30.3.0" + pretty-format: "npm:30.4.1" slash: "npm:^3.0.0" stack-utils: "npm:^2.0.6" - checksum: 10/886577543ec60b421d21987190c5e393ff3652f4f2f2b504776d73f932518827b026ab8e6ffdb1f21ff5142ddf160ba4794e56d96143baeb4ae6939e040a10bd + checksum: 10/f83894efa37aa9c61c0a559b1027ecdb0d0cd8afd3e8ea74e797c707d58daea814e72f04b6db0bb6a148c12ae203e9c6e6c5544832ca5fae286c4f80c18ddc3f languageName: node linkType: hard @@ -10600,14 +10331,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:30.3.0": - version: 30.3.0 - resolution: "jest-mock@npm:30.3.0" +"jest-mock@npm:30.4.1": + version: 30.4.1 + resolution: "jest-mock@npm:30.4.1" dependencies: - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" "@types/node": "npm:*" - jest-util: "npm:30.3.0" - checksum: 10/9d2a9e52c2aebc486e9accaf641efa5c6589666e883b5ac1987261d0e2c105a06b885c22aeeb1cd7582e421970c95e34fe0b41bc4a8c06d7e3e4c27651e76ad1 + jest-util: "npm:30.4.1" + checksum: 10/8d0c2794130217b9030b888ce380fe57d82388eec19351bd666440ba46f1e24a7e2bdf42cbe9bcfda2b881d4c0ea09db3c80131b9ab788fb5224af2a1339b422 languageName: node linkType: hard @@ -10734,32 +10465,32 @@ __metadata: languageName: node linkType: hard -"jest-snapshot@npm:30.3.0": - version: 30.3.0 - resolution: "jest-snapshot@npm:30.3.0" +"jest-snapshot@npm:30.4.1": + version: 30.4.1 + resolution: "jest-snapshot@npm:30.4.1" dependencies: "@babel/core": "npm:^7.27.4" "@babel/generator": "npm:^7.27.5" "@babel/plugin-syntax-jsx": "npm:^7.27.1" "@babel/plugin-syntax-typescript": "npm:^7.27.1" "@babel/types": "npm:^7.27.3" - "@jest/expect-utils": "npm:30.3.0" + "@jest/expect-utils": "npm:30.4.1" "@jest/get-type": "npm:30.1.0" - "@jest/snapshot-utils": "npm:30.3.0" - "@jest/transform": "npm:30.3.0" - "@jest/types": "npm:30.3.0" + "@jest/snapshot-utils": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" babel-preset-current-node-syntax: "npm:^1.2.0" chalk: "npm:^4.1.2" - expect: "npm:30.3.0" + expect: "npm:30.4.1" graceful-fs: "npm:^4.2.11" - jest-diff: "npm:30.3.0" - jest-matcher-utils: "npm:30.3.0" - jest-message-util: "npm:30.3.0" - jest-util: "npm:30.3.0" - pretty-format: "npm:30.3.0" + jest-diff: "npm:30.4.1" + jest-matcher-utils: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-util: "npm:30.4.1" + pretty-format: "npm:30.4.1" semver: "npm:^7.7.2" synckit: "npm:^0.11.8" - checksum: 10/d9f75c436587410cc8170a710d53a632e148a648ec82476ef9e618d8067246e48af7c460773304ad53eecf748b118619a6afd87212f86d680d3439787b4fec39 + checksum: 10/6135108d3e0e9fb93ed10fd9ad91d8dbe56f90a9ea84c32a0b551518f8c71f363299dcc301717f3ed82cfe2a276d7993d2b3ccfabea3e8020d49ae8b0f9b6cd8 languageName: node linkType: hard @@ -10791,17 +10522,17 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:30.3.0": - version: 30.3.0 - resolution: "jest-util@npm:30.3.0" +"jest-util@npm:30.4.1": + version: 30.4.1 + resolution: "jest-util@npm:30.4.1" dependencies: - "@jest/types": "npm:30.3.0" + "@jest/types": "npm:30.4.1" "@types/node": "npm:*" chalk: "npm:^4.1.2" ci-info: "npm:^4.2.0" graceful-fs: "npm:^4.2.11" picomatch: "npm:^4.0.3" - checksum: 10/4b016004637f6a53d6f54c993dc8904a4d6abe93acb8dd70622dc2ca80290a03692e834af1068969b486426e87d411144705edd4d772bb715a826d7e15b5a4b3 + checksum: 10/603093e12076906afcf28be514d5b7ac4e3c0e26997b0047614cf2a308b65d773137304a1fb011d747517e881aeed067f6606b9937f5b838d67f6e5734b49ebe languageName: node linkType: hard @@ -10849,16 +10580,16 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:30.3.0": - version: 30.3.0 - resolution: "jest-worker@npm:30.3.0" +"jest-worker@npm:30.4.1": + version: 30.4.1 + resolution: "jest-worker@npm:30.4.1" dependencies: "@types/node": "npm:*" "@ungap/structured-clone": "npm:^1.3.0" - jest-util: "npm:30.3.0" + jest-util: "npm:30.4.1" merge-stream: "npm:^2.0.0" supports-color: "npm:^8.1.1" - checksum: 10/6198e7462617e8f544b1ba593970fb7656e990aa87a2259f693edde106b5aecf63bae692e8d6adc4313efcaba283b15fc25f6834cacca12cf241da0ece722060 + checksum: 10/ff6af73c9097fc07e90490d3e1e354c702390ef66f7f40054a15dd6d56809a25634179969ff80bde782a6c645f49fa48bf3aacfe7d05af7315c48020f9b2b1cd languageName: node linkType: hard @@ -11445,10 +11176,10 @@ __metadata: languageName: node linkType: hard -"lodash-es@npm:^4.17.23": - version: 4.17.23 - resolution: "lodash-es@npm:4.17.23" - checksum: 10/1feae200df22eb0bd93ca86d485e77784b8a9fb1d13e91b66e9baa7a7e5e04be088c12a7e20c2250fc0bd3db1bc0ef0affc7d9e3810b6af2455a3c6bf6dde59e +"lodash-es@npm:^4.18.1": + version: 4.18.1 + resolution: "lodash-es@npm:4.18.1" + checksum: 10/8bfad225ef09ef42b04283cdaf7830efcc2ba29ae41b56501c74422155ee1ccaa1f0f6e8319def3451a1fe54dec501c8e4bee622bae2b2d98ac993731e0a5cce languageName: node linkType: hard @@ -11480,10 +11211,10 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:^4.17.23, lodash@npm:~4.17.23": - version: 4.17.23 - resolution: "lodash@npm:4.17.23" - checksum: 10/82504c88250f58da7a5a4289f57a4f759c44946c005dd232821c7688b5fcfbf4a6268f6a6cdde4b792c91edd2f3b5398c1d2a0998274432cff76def48735e233 +"lodash.startcase@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.startcase@npm:4.4.0" + checksum: 10/3091048a54a2f92bcf2c6441d2bd9a706fb133d5f461ae7c310d6dca1530338a06c91e9e42a5b14b12e875ddae1814d448050dc02afe2cec09b3995d8e836837 languageName: node linkType: hard @@ -11539,9 +11270,9 @@ __metadata: linkType: hard "lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1": - version: 11.2.7 - resolution: "lru-cache@npm:11.2.7" - checksum: 10/fbff4b8dee8189dde9b52cdfb3ea89b4c9cec094c1538cd30d1f47299477ff312efdb35f7994477ec72328f8e754e232b26a143feda1bd1f79ff22da6664d2c5 + version: 11.5.1 + resolution: "lru-cache@npm:11.5.1" + checksum: 10/02c4f73967d91fb101f4accf8ebac9e0541e08e16d987bdb9e9737f13e5f2c4bc33c593b98ec30e4486bf899bc97edb36fbd133684b36087336559e41edafdea languageName: node linkType: hard @@ -11632,15 +11363,13 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^15.0.0": - version: 15.0.5 - resolution: "make-fetch-happen@npm:15.0.5" +"make-fetch-happen@npm:^15.0.0, make-fetch-happen@npm:^15.0.1, make-fetch-happen@npm:^15.0.4": + version: 15.0.6 + resolution: "make-fetch-happen@npm:15.0.6" dependencies: - "@gar/promise-retry": "npm:^1.0.0" "@gar/promise-retry": "npm:^1.0.0" "@npmcli/agent": "npm:^4.0.0" "@npmcli/redact": "npm:^4.0.0" - "@npmcli/redact": "npm:^4.0.0" cacache: "npm:^20.0.1" http-cache-semantics: "npm:^4.1.1" minipass: "npm:^7.0.2" @@ -11650,7 +11379,7 @@ __metadata: negotiator: "npm:^1.0.0" proc-log: "npm:^6.0.0" ssri: "npm:^13.0.0" - checksum: 10/d2649effb06c00cb2b266057cb1c8c1e99cfc8d1378e7d9c26cc8f00be41bc63d59b77a5576ed28f8105acc57fb16220b64217f8d3a6a066a594c004aa163afa + checksum: 10/01bc13d8e851212d55bbf0510dd5f94b9f239f253dfbd84275e23d1798ba7323a08b3c16031b83676fe75381ad5c70d17542c7b6828c98ee6e0d3eacdaf83ebb languageName: node linkType: hard @@ -11678,8 +11407,8 @@ __metadata: linkType: hard "markdown-it@npm:^14.1.1": - version: 14.1.1 - resolution: "markdown-it@npm:14.1.1" + version: 14.2.0 + resolution: "markdown-it@npm:14.2.0" dependencies: argparse: "npm:^2.0.1" entities: "npm:^4.4.0" @@ -11689,7 +11418,7 @@ __metadata: uc.micro: "npm:^2.1.0" bin: markdown-it: bin/markdown-it.mjs - checksum: 10/088822c8aa9346ba4af6a205f6ee0f4baae55e3314f040dc5c28c897d57d0f979840c71872b3582a6a6e572d8c851c54e323c82f4559011dfa2e96224fc20fc2 + checksum: 10/f5cdb7ca9c8115114137201590b2697ee6ca69d2d4701a0313696629d5de4022525eedcad31800263c70f04dfba0a8eae835b6aa84b65e94ae32d63d04e66211 languageName: node linkType: hard @@ -11722,9 +11451,6 @@ __metadata: linkType: hard "meow@npm:^14.0.0": - version: 14.1.0 - resolution: "meow@npm:14.1.0" - checksum: 10/c6a22b3912a6bc849dee0d6475cd8bb63b9307e26919ca3ace28dc1aaf3d30257071de32bba496f7b5eec3e62b03a6b7731e3d04d18efb3c3103b829aad52ca5 version: 14.1.0 resolution: "meow@npm:14.1.0" checksum: 10/c6a22b3912a6bc849dee0d6475cd8bb63b9307e26919ca3ace28dc1aaf3d30257071de32bba496f7b5eec3e62b03a6b7731e3d04d18efb3c3103b829aad52ca5 @@ -11788,7 +11514,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:2.1.35, mime-types@npm:^2.1.12": +"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.35": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -11811,16 +11537,11 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:10.2.3": - version: 10.2.3 - resolution: "minimatch@npm:10.2.3" "minimatch@npm:10.2.3": version: 10.2.3 resolution: "minimatch@npm:10.2.3" dependencies: brace-expansion: "npm:^5.0.2" - checksum: 10/186c6a6ce9f7a79ae7776efc799c32d1a6670ebbcc2a8756e6cb6ec4aab7439a6ca6e592e1a6aac5f21674eefae5b19821b8fa95072a4f4567da1ae40eb6075d - brace-expansion: "npm:^5.0.2" checksum: 10/186c6a6ce9f7a79ae7776efc799c32d1a6670ebbcc2a8756e6cb6ec4aab7439a6ca6e592e1a6aac5f21674eefae5b19821b8fa95072a4f4567da1ae40eb6075d languageName: node linkType: hard @@ -11834,49 +11555,38 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.1.1, minimatch@npm:^10.2.2, minimatch@npm:^10.2.4": - version: 10.2.4 - resolution: "minimatch@npm:10.2.4" +"minimatch@npm:3.1.4": + version: 3.1.4 + resolution: "minimatch@npm:3.1.4" dependencies: - brace-expansion: "npm:^5.0.2" - checksum: 10/aea4874e521c55bb60744685bbffe3d152e5460f84efac3ea936e6bbe2ceba7deb93345fec3f9bb17f7b6946776073a64d40ae32bf5f298ad690308121068a1f + brace-expansion: "npm:^1.1.7" + checksum: 10/8d679c9df6caad31465c7681ae72b5e0f5d3b4fda6235c4473b14819f4d72ff8924ebd73ce991cc50be4b370daca51cc4d8c7fea6a3aa05108702ede115ab4c9 languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2, minimatch@npm:^3.1.5": - version: 3.1.5 - resolution: "minimatch@npm:3.1.5" "minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2, minimatch@npm:^3.1.5": version: 3.1.5 resolution: "minimatch@npm:3.1.5" dependencies: brace-expansion: "npm:^1.1.7" checksum: 10/b11a7ee5773cd34c1a0c8436cdbe910901018fb4b6cb47aa508a18d567f6efd2148507959e35fba798389b161b8604a2d704ccef751ea36bd4582f9852b7d63f - checksum: 10/b11a7ee5773cd34c1a0c8436cdbe910901018fb4b6cb47aa508a18d567f6efd2148507959e35fba798389b161b8604a2d704ccef751ea36bd4582f9852b7d63f languageName: node linkType: hard "minimatch@npm:^5.0.1": - version: 5.1.9 - resolution: "minimatch@npm:5.1.9" version: 5.1.9 resolution: "minimatch@npm:5.1.9" dependencies: brace-expansion: "npm:^2.0.1" checksum: 10/23b4feb64dcb77ba93b70a72be551eb2e2677ac02178cf1ed3d38836cc4cd84802d90b77f60ef87f2bac64d270d2d8eba242e428f0554ea4e36bfdb7e9d25d0c - checksum: 10/23b4feb64dcb77ba93b70a72be551eb2e2677ac02178cf1ed3d38836cc4cd84802d90b77f60ef87f2bac64d270d2d8eba242e428f0554ea4e36bfdb7e9d25d0c languageName: node linkType: hard "minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": - version: 9.0.9 - resolution: "minimatch@npm:9.0.9" version: 9.0.9 resolution: "minimatch@npm:9.0.9" dependencies: brace-expansion: "npm:^2.0.2" - checksum: 10/b91fad937deaffb68a45a2cb731ff3cff1c3baf9b6469c879477ed16f15c8f4ce39d63a3f75c2455107c2fdff0f3ab597d97dc09e2e93b883aafcf926ef0c8f9 - brace-expansion: "npm:^2.0.2" checksum: 10/b91fad937deaffb68a45a2cb731ff3cff1c3baf9b6469c879477ed16f15c8f4ce39d63a3f75c2455107c2fdff0f3ab597d97dc09e2e93b883aafcf926ef0c8f9 languageName: node linkType: hard @@ -11924,23 +11634,17 @@ __metadata: linkType: hard "minipass-fetch@npm:^5.0.0": - version: 5.0.2 - resolution: "minipass-fetch@npm:5.0.2" version: 5.0.2 resolution: "minipass-fetch@npm:5.0.2" dependencies: - iconv-lite: "npm:^0.7.2" iconv-lite: "npm:^0.7.2" minipass: "npm:^7.0.3" minipass-sized: "npm:^2.0.0" - minipass-sized: "npm:^2.0.0" minizlib: "npm:^3.0.1" dependenciesMeta: - iconv-lite: iconv-lite: optional: true checksum: 10/4f3f65ea5b20a3a287765ebf21cc73e62031f754944272df2a3039296cc75a8fc2dc50b8a3c4f39ce3ac6e5cc583e8dc664d12c6ab98e0883d263e49f344bc86 - checksum: 10/4f3f65ea5b20a3a287765ebf21cc73e62031f754944272df2a3039296cc75a8fc2dc50b8a3c4f39ce3ac6e5cc583e8dc664d12c6ab98e0883d263e49f344bc86 languageName: node linkType: hard @@ -11980,7 +11684,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": +"minipass@npm:^3.0.0": version: 3.3.6 resolution: "minipass@npm:3.3.6" dependencies: @@ -11989,13 +11693,6 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0": - version: 5.0.0 - resolution: "minipass@npm:5.0.0" - checksum: 10/61682162d29f45d3152b78b08bab7fb32ca10899bc5991ffe98afc18c9e9543bd1e3be94f8b8373ba6262497db63607079dc242ea62e43e7b2270837b7347c93 - languageName: node - linkType: hard - "minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2, minipass@npm:^7.1.3": version: 7.1.3 resolution: "minipass@npm:7.1.3" @@ -12003,16 +11700,6 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": - version: 2.1.2 - resolution: "minizlib@npm:2.1.2" - dependencies: - minipass: "npm:^3.0.0" - yallist: "npm:^4.0.0" - checksum: 10/ae0f45436fb51344dcb87938446a32fbebb540d0e191d63b35e1c773d47512e17307bf54aa88326cc6d176594d00e4423563a091f7266c2f9a6872cdc1e234d1 - languageName: node - linkType: hard - "minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" @@ -12022,6 +11709,17 @@ __metadata: languageName: node linkType: hard +"mkdirp@npm:^0.5.0": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: "npm:^1.2.6" + bin: + mkdirp: bin/cmd.js + checksum: 10/0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 + languageName: node + linkType: hard + "mocha@npm:^11.2.2, mocha@npm:^11.7.5": version: 11.7.6 resolution: "mocha@npm:11.7.6" @@ -12135,26 +11833,24 @@ __metadata: languageName: node linkType: hard -"next@npm:^16.1.1": - version: 16.2.1 - resolution: "next@npm:16.2.1" +"next@npm:^16.2.0": + version: 16.2.9 + resolution: "next@npm:16.2.9" dependencies: - "@next/env": "npm:16.2.1" - "@next/swc-darwin-arm64": "npm:16.2.1" - "@next/swc-darwin-x64": "npm:16.2.1" - "@next/swc-linux-arm64-gnu": "npm:16.2.1" - "@next/swc-linux-arm64-musl": "npm:16.2.1" - "@next/swc-linux-x64-gnu": "npm:16.2.1" - "@next/swc-linux-x64-musl": "npm:16.2.1" - "@next/swc-win32-arm64-msvc": "npm:16.2.1" - "@next/swc-win32-x64-msvc": "npm:16.2.1" + "@next/env": "npm:16.2.9" + "@next/swc-darwin-arm64": "npm:16.2.9" + "@next/swc-darwin-x64": "npm:16.2.9" + "@next/swc-linux-arm64-gnu": "npm:16.2.9" + "@next/swc-linux-arm64-musl": "npm:16.2.9" + "@next/swc-linux-x64-gnu": "npm:16.2.9" + "@next/swc-linux-x64-musl": "npm:16.2.9" + "@next/swc-win32-arm64-msvc": "npm:16.2.9" + "@next/swc-win32-x64-msvc": "npm:16.2.9" "@swc/helpers": "npm:0.5.15" baseline-browser-mapping: "npm:^2.9.19" - baseline-browser-mapping: "npm:^2.9.19" caniuse-lite: "npm:^1.0.30001579" postcss: "npm:8.4.31" sharp: "npm:^0.34.5" - sharp: "npm:^0.34.5" styled-jsx: "npm:5.1.6" peerDependencies: "@opentelemetry/api": ^1.1.0 @@ -12193,7 +11889,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 10/319c0b18173a90e53b5e5ffafa8a8fecb7cc340b77728796743edd996c7ee7652201892bff60c32f6a3b75abdff1449b77f13f6fab8fd56d4f9da47cf0fb9299 + checksum: 10/07903fdd4cc68beb7bff8f04f85bda52b3ebec6112702d6761e7e5d17fa2843ac12cd7cbdb369bfb5fadd6cd6b3620c22c9865cb46f8e7615134238ac1422672 languageName: node linkType: hard @@ -12208,26 +11904,13 @@ __metadata: linkType: hard "nock@npm:^14.0.10": - version: 14.0.11 - resolution: "nock@npm:14.0.11" + version: 14.0.15 + resolution: "nock@npm:14.0.15" dependencies: - "@mswjs/interceptors": "npm:^0.41.0" "@mswjs/interceptors": "npm:^0.41.0" json-stringify-safe: "npm:^5.0.1" propagate: "npm:^2.0.0" - checksum: 10/2fc579f3bee5148ebfacdfc7588a1f45205b139dcc6e32a5bef436f74f479383c7445a9812f433908600f62a0e142ff4bbbe7da7d5e9ed1781bad278b792cf98 - languageName: node - linkType: hard - -"node-exports-info@npm:^1.6.0": - version: 1.6.0 - resolution: "node-exports-info@npm:1.6.0" - dependencies: - array.prototype.flatmap: "npm:^1.3.3" - es-errors: "npm:^1.3.0" - object.entries: "npm:^1.1.9" - semver: "npm:^6.3.1" - checksum: 10/0a1667d535f499ac1fe6c6d22f8146bc8b68abc76fa355856219202f6cf5f386027e0ff054e66a22d08be02acbc63fcdc9f98d0fbc97993f5eabc66408fdadad + checksum: 10/e51737b43e51dc642e339f36c2125b18ef262492d4b8ba5ca0aaeff983fc29c42a02aeb5e19b80940f7dbde59a413ae7298ef1f6c11fe6d7dbe54d7f3a558d83 languageName: node linkType: hard @@ -12243,7 +11926,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0": +"node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -12257,7 +11940,7 @@ __metadata: languageName: node linkType: hard -"node-gyp@npm:^12.1.0, node-gyp@npm:latest": +"node-gyp@npm:^12.1.0": version: 12.4.0 resolution: "node-gyp@npm:12.4.0" dependencies: @@ -12277,6 +11960,26 @@ __metadata: languageName: node linkType: hard +"node-gyp@npm:latest": + version: 13.0.0 + resolution: "node-gyp@npm:13.0.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^6.25.0" + which: "npm:^7.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10/12b7b0204d07493c347f59734aaee7531f41540c820ad0e40604e96838ab277f33fb1d70500283dbb66ee02182ebad231b6a13c75644d83e6c94c1ef28009c6a + languageName: node + linkType: hard + "node-int64@npm:^0.4.0": version: 0.4.0 resolution: "node-int64@npm:0.4.0" @@ -12293,10 +11996,21 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.27": - version: 2.0.36 - resolution: "node-releases@npm:2.0.36" - checksum: 10/b31ead96e328b1775f07cad80c17b0601d0ee2894650b737e7ab5cbeb14e284e82dbc37ef38f1d915fa46dd7909781bd933d19b79cfe31b352573fac6da377aa +"node-releases@npm:^2.0.36": + version: 2.0.47 + resolution: "node-releases@npm:2.0.47" + checksum: 10/6ba88359ea4a653bcb428c7ac47a3b9093d0fe873d0fbda8b6714df6d1829630769d1c737d5938458af6a101b4bb7c70bcf7e38f36db469a17a348261d26f822 + languageName: node + linkType: hard + +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" + dependencies: + abbrev: "npm:^5.0.0" + bin: + nopt: bin/nopt.js + checksum: 10/8021371365e78a2cbab015cac50d8449aa2cc411f0b8f2edb466c1336c3dfee4e61c5bf5bde22ee7dcea80d5f4510a7a8705ed3646c8d782f28b550c62bc4fdf languageName: node linkType: hard @@ -13139,8 +12853,8 @@ __metadata: linkType: hard "pacote@npm:^21.0.0, pacote@npm:^21.0.2": - version: 21.5.0 - resolution: "pacote@npm:21.5.0" + version: 21.5.1 + resolution: "pacote@npm:21.5.1" dependencies: "@gar/promise-retry": "npm:^1.0.0" "@npmcli/git": "npm:^7.0.0" @@ -13161,7 +12875,7 @@ __metadata: tar: "npm:^7.4.3" bin: pacote: bin/index.js - checksum: 10/5d31a986728ce10dea688887d31b98eaa8f08be15b9458c6d69257c3f576771dfca56475a7c49251675fcb827dfc1647c1dd69b29e84b40dae35efd9ee257307 + checksum: 10/30054b7ea3d50943cf13804ff006ecda8ff2c1f9c22b3a5ee2c9b5170ac374052c3d6a70621219e3dcd69a728b795afe3677488e2d541dd9911be1b35e4d1f8e languageName: node linkType: hard @@ -13295,9 +13009,6 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^2.0.0, path-scurry@npm:^2.0.2": - version: 2.0.2 - resolution: "path-scurry@npm:2.0.2" "path-scurry@npm:^2.0.0, path-scurry@npm:^2.0.2": version: 2.0.2 resolution: "path-scurry@npm:2.0.2" @@ -13305,7 +13016,6 @@ __metadata: lru-cache: "npm:^11.0.0" minipass: "npm:^7.1.2" checksum: 10/2b4257422bcb870a4c2d205b3acdbb213a72f5e2250f61c80f79c9d014d010f82bdf8584441612c8e1fa4eb098678f5704a66fa8377d72646bad4be38e57a2c3 - checksum: 10/2b4257422bcb870a4c2d205b3acdbb213a72f5e2250f61c80f79c9d014d010f82bdf8584441612c8e1fa4eb098678f5704a66fa8377d72646bad4be38e57a2c3 languageName: node linkType: hard @@ -13361,16 +13071,13 @@ __metadata: linkType: hard "picomatch@npm:^2.0.4, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": - version: 2.3.2 - resolution: "picomatch@npm:2.3.2" - checksum: 10/b788ef8148a2415b9dec12f0bb350ae6a5830f8f1950e472abc2f5225494debf7d1b75eb031df0ceaea9e8ec3e7bad599e8dbf3c60d61b42be429ba41bff4426 version: 2.3.2 resolution: "picomatch@npm:2.3.2" checksum: 10/b788ef8148a2415b9dec12f0bb350ae6a5830f8f1950e472abc2f5225494debf7d1b75eb031df0ceaea9e8ec3e7bad599e8dbf3c60d61b42be429ba41bff4426 languageName: node linkType: hard -"picomatch@npm:^4.0.3": +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": version: 4.0.4 resolution: "picomatch@npm:4.0.4" checksum: 10/f6ef80a3590827ce20378ae110ac78209cc4f74d39236370f1780f957b7ee41c12acde0e4651b90f39983506fd2f5e449994716f516db2e9752924aff8de93ce @@ -13422,12 +13129,12 @@ __metadata: linkType: hard "postcss-selector-parser@npm:^7.0.0": - version: 7.1.1 - resolution: "postcss-selector-parser@npm:7.1.1" + version: 7.1.4 + resolution: "postcss-selector-parser@npm:7.1.4" dependencies: cssesc: "npm:^3.0.0" util-deprecate: "npm:^1.0.2" - checksum: 10/bb3c6455b20af26a556e3021e21101d8470252644e673c1612f7348ff8dd41b11321329f0694cf299b5b94863f823480b72d3e2f4bd3a89dc43e2d8c0dbad341 + checksum: 10/c6d36b37defd387d65b5e0b778cd441303d147eb4a4b7135c6a0452fa777310027218db3ff71a19fb831d067bcdf45a776c6ed71d83bd2ced8afc2e3d5b03385 languageName: node linkType: hard @@ -13485,14 +13192,15 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:30.3.0": - version: 30.3.0 - resolution: "pretty-format@npm:30.3.0" +"pretty-format@npm:30.4.1": + version: 30.4.1 + resolution: "pretty-format@npm:30.4.1" dependencies: "@jest/schemas": "npm:30.4.1" ansi-styles: "npm:^5.2.0" - react-is: "npm:^18.3.1" - checksum: 10/b288db630841f2464554c5cfa7d7faf519ad7b5c05c3818e764c7cb486bcf59f240ea5576c748f8ca6625623c5856a8906642255bbe89d6cfa1a9090b0fbc6b9 + react-is-18: "npm:react-is@^18.3.1" + react-is-19: "npm:react-is@^19.2.5" + checksum: 10/60311ef47a646eeaec0432efe66290cb6f0d2eccb123a28ad4ab6d7e53087bc62db91cfd54c3cc00c89d6875aefb2bf6264381b6c9411ce6bff3d6aa8280abad languageName: node linkType: hard @@ -13532,6 +13240,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10/97cd9f4a8a0d84e42ee91e106e5ba5edcb954521e8dbe26ee6ad31396e5c12cc2be5e5b6be7b53fa5a69959afbacd32719106e2d6f45802e34b31d9a3a01ec20 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -14021,19 +13736,19 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^2.0.0-next.5": - version: 2.0.0-next.6 - resolution: "resolve@npm:2.0.0-next.6" +"resolve@npm:^2.0.0-next.5, resolve@npm:^2.0.0-next.6": + version: 2.0.0-next.7 + resolution: "resolve@npm:2.0.0-next.7" dependencies: es-errors: "npm:^1.3.0" - is-core-module: "npm:^2.16.1" + is-core-module: "npm:^2.16.2" node-exports-info: "npm:^1.6.0" object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10/c95cb98b8d3f9e2a979e6eb8b7e0b0e13f08da62607a45207275f151d640152244568a9a9cd01662a21e3746792177cbf9be1dacb88f7355edf4db49d9ee27e5 + checksum: 10/0a6fbd452518c128355a72e3773e65d047128bbc5045d954eca7f911683abfb1b0177494ff8734ca74f2a7a4e3a6bfad9cd6d19a2bde0fe9851025a2734d4a0f languageName: node linkType: hard @@ -14051,19 +13766,19 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": - version: 2.0.0-next.6 - resolution: "resolve@patch:resolve@npm%3A2.0.0-next.6#optional!builtin::version=2.0.0-next.6&hash=c3c19d" +"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin, resolve@patch:resolve@npm%3A^2.0.0-next.6#optional!builtin": + version: 2.0.0-next.7 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.7#optional!builtin::version=2.0.0-next.7&hash=c3c19d" dependencies: es-errors: "npm:^1.3.0" - is-core-module: "npm:^2.16.1" + is-core-module: "npm:^2.16.2" node-exports-info: "npm:^1.6.0" object-keys: "npm:^1.1.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10/1b26738af76c80b341075e6bf4b202ef85f85f4a2cbf2934246c3b5f20c682cf352833fc6e32579c6967419226d3ab63e8d321328da052c87a31eaad91e3571a + checksum: 10/2c6dd4194c8aa900db299020fcad253239c670ea82a65c8387f1d2885e8dcf6742b64c439e3c811e046a5252eba94f2092b7867e34b7f895e733be48d575192b languageName: node linkType: hard @@ -14103,34 +13818,34 @@ __metadata: linkType: hard "rollup@npm:^4.43.0": - version: 4.61.1 - resolution: "rollup@npm:4.61.1" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.61.1" - "@rollup/rollup-android-arm64": "npm:4.61.1" - "@rollup/rollup-darwin-arm64": "npm:4.61.1" - "@rollup/rollup-darwin-x64": "npm:4.61.1" - "@rollup/rollup-freebsd-arm64": "npm:4.61.1" - "@rollup/rollup-freebsd-x64": "npm:4.61.1" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.61.1" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.61.1" - "@rollup/rollup-linux-arm64-gnu": "npm:4.61.1" - "@rollup/rollup-linux-arm64-musl": "npm:4.61.1" - "@rollup/rollup-linux-loong64-gnu": "npm:4.61.1" - "@rollup/rollup-linux-loong64-musl": "npm:4.61.1" - "@rollup/rollup-linux-ppc64-gnu": "npm:4.61.1" - "@rollup/rollup-linux-ppc64-musl": "npm:4.61.1" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.61.1" - "@rollup/rollup-linux-riscv64-musl": "npm:4.61.1" - "@rollup/rollup-linux-s390x-gnu": "npm:4.61.1" - "@rollup/rollup-linux-x64-gnu": "npm:4.61.1" - "@rollup/rollup-linux-x64-musl": "npm:4.61.1" - "@rollup/rollup-openbsd-x64": "npm:4.61.1" - "@rollup/rollup-openharmony-arm64": "npm:4.61.1" - "@rollup/rollup-win32-arm64-msvc": "npm:4.61.1" - "@rollup/rollup-win32-ia32-msvc": "npm:4.61.1" - "@rollup/rollup-win32-x64-gnu": "npm:4.61.1" - "@rollup/rollup-win32-x64-msvc": "npm:4.61.1" + version: 4.62.0 + resolution: "rollup@npm:4.62.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.62.0" + "@rollup/rollup-android-arm64": "npm:4.62.0" + "@rollup/rollup-darwin-arm64": "npm:4.62.0" + "@rollup/rollup-darwin-x64": "npm:4.62.0" + "@rollup/rollup-freebsd-arm64": "npm:4.62.0" + "@rollup/rollup-freebsd-x64": "npm:4.62.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.62.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.62.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.62.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.62.0" + "@rollup/rollup-linux-loong64-gnu": "npm:4.62.0" + "@rollup/rollup-linux-loong64-musl": "npm:4.62.0" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.62.0" + "@rollup/rollup-linux-ppc64-musl": "npm:4.62.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.62.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.62.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.62.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.62.0" + "@rollup/rollup-linux-x64-musl": "npm:4.62.0" + "@rollup/rollup-openbsd-x64": "npm:4.62.0" + "@rollup/rollup-openharmony-arm64": "npm:4.62.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.62.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.62.0" + "@rollup/rollup-win32-x64-gnu": "npm:4.62.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.62.0" "@types/estree": "npm:1.0.9" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -14188,7 +13903,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10/ab011372007dc91036646316c93e30e407f5b98c112be19d7392b96a99c5d72917ec1590df3fe354314f59462ed2a6b465fd5c6a7dd6fffbb919787f7a01755d + checksum: 10/b64ae92d0dd242f1aec42d73b71f9989c485a84833da62160ef38947b0ae3c68e3de15a6ea1663327247b3d6e2311e2162412ef27a6b16fb7ddbba4a837f0673 languageName: node linkType: hard @@ -14227,6 +13942,13 @@ __metadata: languageName: node linkType: hard +"rsvp@npm:~3.2.1": + version: 3.2.1 + resolution: "rsvp@npm:3.2.1" + checksum: 10/c85d086bfd92b8997846221b447e41612edb6e5e7566725058af82d7e67a002e7338da8e44de98d0ebaca1519d049a6b620e0078d9ab1c8d1403131fe48e7f42 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -14366,12 +14088,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.7.1, semver@npm:^7.7.2, semver@npm:^7.7.3": - version: 7.7.4 - resolution: "semver@npm:7.7.4" +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3, semver@npm:^7.7.1, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.0": + version: 7.8.4 + resolution: "semver@npm:7.8.4" bin: semver: bin/semver.js - checksum: 10/26bdc6d58b29528f4142d29afb8526bc335f4fc04c4a10f2b98b217f277a031c66736bf82d3d3bb354a2f6a3ae50f18fd62b053c4ac3f294a3d10a61f5075b75 + checksum: 10/a9c139031d4143932adfacfd2165d6489848c3b84c26d5fc2beef88c6d54c01191ef553e3f71049ccc47df85f0df30748907f84005f46f326095003171c5b673 languageName: node linkType: hard @@ -14428,15 +14150,6 @@ __metadata: languageName: node linkType: hard -"shallow-clone@npm:^3.0.0": - version: 3.0.1 - resolution: "shallow-clone@npm:3.0.1" - dependencies: - kind-of: "npm:^6.0.2" - checksum: 10/e066bd540cfec5e1b0f78134853e0d892d1c8945fb9a926a579946052e7cb0c70ca4fc34f875a8083aa7910d751805d36ae64af250a6de6f3d28f9fa7be6c21b - languageName: node - linkType: hard - "sharp@npm:^0.34.5": version: 0.34.5 resolution: "sharp@npm:0.34.5" @@ -14537,7 +14250,7 @@ __metadata: languageName: node linkType: hard -"side-channel-list@npm:^1.0.0": +"side-channel-list@npm:^1.0.1": version: 1.0.1 resolution: "side-channel-list@npm:1.0.1" dependencies: @@ -14573,15 +14286,15 @@ __metadata: linkType: hard "side-channel@npm:^1.1.0": - version: 1.1.0 - resolution: "side-channel@npm:1.1.0" + version: 1.1.1 + resolution: "side-channel@npm:1.1.1" dependencies: es-errors: "npm:^1.3.0" - object-inspect: "npm:^1.13.3" - side-channel-list: "npm:^1.0.0" + object-inspect: "npm:^1.13.4" + side-channel-list: "npm:^1.0.1" side-channel-map: "npm:^1.0.1" side-channel-weakmap: "npm:^1.0.2" - checksum: 10/7d53b9db292c6262f326b6ff3bc1611db84ece36c2c7dc0e937954c13c73185b0406c56589e2bb8d071d6fee468e14c39fb5d203ee39be66b7b8174f179afaba + checksum: 10/5fa6393ff6ad25d8b4a38e9ba095481e498c8ebe5ab78481c1455146255a3d18ca37a6f936595cc671a6149134cdc295bbd2fa017620bdc73cbc7380634fa2fc languageName: node linkType: hard @@ -14795,9 +14508,6 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.23 - resolution: "spdx-license-ids@npm:3.0.23" - checksum: 10/fead6be44478e4dd73a0721ae569f4a04f358846d8d82e8d92efae64aca928592e380cf17e8b84c25c948f3a7d8a0b4fc781a1830f3911ca87d52733265176b5 version: 3.0.23 resolution: "spdx-license-ids@npm:3.0.23" checksum: 10/fead6be44478e4dd73a0721ae569f4a04f358846d8d82e8d92efae64aca928592e380cf17e8b84c25c948f3a7d8a0b4fc781a1830f3911ca87d52733265176b5 @@ -14829,12 +14539,12 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^13.0.0": - version: 13.0.1 - resolution: "ssri@npm:13.0.1" +"ssri@npm:12.0.0, ssri@npm:^12.0.0": + version: 12.0.0 + resolution: "ssri@npm:12.0.0" dependencies: minipass: "npm:^7.0.3" - checksum: 10/ae560d0378d074006a71b06af71bfbe84a3fe1ac6e16c1f07575f69e670d40170507fe52b21bcc23399429bc6a15f4bc3ea8d9bc88e9dfd7e87de564e6da6a72 + checksum: 10/7024c1a6e39b3f18aa8f1c8290e884fe91b0f9ca5a6c6d410544daad54de0ba664db879afe16412e187c6c292fd60b937f047ee44292e5c2af2dcc6d8e1a9b48 languageName: node linkType: hard @@ -15042,14 +14752,10 @@ __metadata: linkType: hard "strip-ansi@npm:^7.0.1": - version: 7.2.0 - resolution: "strip-ansi@npm:7.2.0" version: 7.2.0 resolution: "strip-ansi@npm:7.2.0" dependencies: ansi-regex: "npm:^6.2.2" - checksum: 10/96da3bc6d73cfba1218625a3d66cf7d37a69bf0920d8735b28f9eeaafcdb6c1fe8440e1ae9eb1ba0ca355dbe8702da872e105e2e939fa93e7851b3cb5dd7d316 - ansi-regex: "npm:^6.2.2" checksum: 10/96da3bc6d73cfba1218625a3d66cf7d37a69bf0920d8735b28f9eeaafcdb6c1fe8440e1ae9eb1ba0ca355dbe8702da872e105e2e939fa93e7851b3cb5dd7d316 languageName: node linkType: hard @@ -15148,6 +14854,19 @@ __metadata: languageName: node linkType: hard +"sync-disk-cache@npm:^2.1.0": + version: 2.1.0 + resolution: "sync-disk-cache@npm:2.1.0" + dependencies: + debug: "npm:^4.1.1" + heimdalljs: "npm:^0.2.6" + mkdirp: "npm:^0.5.0" + rimraf: "npm:^3.0.0" + username-sync: "npm:^1.0.2" + checksum: 10/13ba687c7322c6480a0595da0510912067a289a60bcf9b0b6ee54149f7d7315cd6410ad36952ba74a507f69405dcc2c2e369c7a8325065e43c434fef63fc36f7 + languageName: node + linkType: hard + "synckit@npm:^0.11.13, synckit@npm:^0.11.8": version: 0.11.13 resolution: "synckit@npm:0.11.13" @@ -15183,16 +14902,16 @@ __metadata: languageName: node linkType: hard -"tar@npm:^7.5.4": - version: 7.5.13 - resolution: "tar@npm:7.5.13" +"tar@npm:^7.4.3, tar@npm:^7.5.4": + version: 7.5.16 + resolution: "tar@npm:7.5.16" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/2bc2b6f0349038a6621dbba1c4522d45752d5071b2994692257113c2050cd23fafc30308f820e5f8ad6fda3f7d7f92adc9a432aa733daa04c42af2061c021c3f + checksum: 10/fafa22efceb9f056bf29ddc47d9bd90bb82fe3ce57b8d1242fc45771251741964cebba69d4e14a24fd1643f3c7f68478e945a19def534703cf370c2d9dca2e09 languageName: node linkType: hard @@ -15427,13 +15146,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^2.1.0, ts-api-utils@npm:^2.4.0": +"ts-api-utils@npm:^2.1.0, ts-api-utils@npm:^2.5.0": version: 2.5.0 resolution: "ts-api-utils@npm:2.5.0" peerDependencies: typescript: ">=4.8.4" checksum: 10/d5f1936f5618c6ab6942a97b78802217540ced00e7501862ae1f578d9a3aa189fc06050e64cb8951d21f7088e5fd35f53d2bf0d0370a883861c7b05e993ebc44 - checksum: 10/d5f1936f5618c6ab6942a97b78802217540ced00e7501862ae1f578d9a3aa189fc06050e64cb8951d21f7088e5fd35f53d2bf0d0370a883861c7b05e993ebc44 languageName: node linkType: hard @@ -15699,45 +15417,43 @@ __metadata: linkType: hard "typedoc-plugin-markdown@npm:^4.6.3": - version: 4.11.0 - resolution: "typedoc-plugin-markdown@npm:4.11.0" + version: 4.12.0 + resolution: "typedoc-plugin-markdown@npm:4.12.0" peerDependencies: typedoc: 0.28.x - checksum: 10/dbde98e76536f6bd713041cfc22b07adacdec8a314da9d1848b5daa9e0de46ef471718b916a2938cac627bc45b0609a3d1765b795aa96aeea2771093207d4faf + checksum: 10/218162f8d096dc46e47c6e841db1a978090fef644ccb7abbca69b7f9d367364592ac59b252a4e47340a2e9c6c433bf7d1287ed4c6be78188f8ba851560d74dc3 languageName: node linkType: hard "typedoc@npm:^0.28.4": - version: 0.28.18 - resolution: "typedoc@npm:0.28.18" + version: 0.28.19 + resolution: "typedoc@npm:0.28.19" dependencies: - "@gerrit0/mini-shiki": "npm:^3.23.0" "@gerrit0/mini-shiki": "npm:^3.23.0" lunr: "npm:^2.3.9" markdown-it: "npm:^14.1.1" - minimatch: "npm:^10.2.4" - yaml: "npm:^2.8.2" + minimatch: "npm:^10.2.5" + yaml: "npm:^2.8.3" peerDependencies: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x bin: typedoc: bin/typedoc - checksum: 10/d7342aeeda34a69e853463e5300a7a06ec42757e2942f3de4a2c882f08f1d4e4d03662a4e8e8259e14c1a0b948b57a6e833d6e391ba41987c4cd400d4d76aeab + checksum: 10/b16fdc717b3fad61e7b776f9eae1a762bfb651ccc7ef8b0deacc64d290bc56c2cfe76214e711fa8e0401da01046d97e14293f6798d8e9f15cc71892ad122d309 languageName: node linkType: hard "typescript-eslint@npm:^8.46.0": - version: 8.60.1 - resolution: "typescript-eslint@npm:8.60.1" + version: 8.61.0 + resolution: "typescript-eslint@npm:8.61.0" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.60.1" - "@typescript-eslint/parser": "npm:8.60.1" - "@typescript-eslint/typescript-estree": "npm:8.60.1" - "@typescript-eslint/utils": "npm:8.60.1" + "@typescript-eslint/eslint-plugin": "npm:8.61.0" + "@typescript-eslint/parser": "npm:8.61.0" + "@typescript-eslint/typescript-estree": "npm:8.61.0" + "@typescript-eslint/utils": "npm:8.61.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/e12091ab2540b817c76b0ec6aad92e341f810310bec2b24bc95780aee106049c05363998f6ea52ed066130c8afc41dca1627f56e4c1df1dd519f4d4ca0ce4910 + checksum: 10/5a21c6ef76400ea30a47629087787834abc1c17e4b406465dfd8c204ef635556f8e3a775d89c46f9eb175ebd6a218284685e935877a2b148c482f0478627bdf9 languageName: node linkType: hard @@ -15823,13 +15539,6 @@ __metadata: languageName: node linkType: hard -"unicorn-magic@npm:^0.3.0": - version: 0.3.0 - resolution: "unicorn-magic@npm:0.3.0" - checksum: 10/bdd7d7c522f9456f32a0b77af23f8854f9a7db846088c3868ec213f9550683ab6a2bdf3803577eacbafddb4e06900974385841ccb75338d17346ccef45f9cb01 - languageName: node - linkType: hard - "undici@npm:^6.25.0": version: 6.26.0 resolution: "undici@npm:6.26.0" @@ -15837,12 +15546,10 @@ __metadata: languageName: node linkType: hard -"unique-slug@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-slug@npm:3.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10/26fc5bc209a875956dd5e84ca39b89bc3be777b112504667c35c861f9547df95afc80439358d836b878b6d91f6ee21fe5ba1a966e9ec2e9f071ddf3fd67d45ee +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10/bdd7d7c522f9456f32a0b77af23f8854f9a7db846088c3868ec213f9550683ab6a2bdf3803577eacbafddb4e06900974385841ccb75338d17346ccef45f9cb01 languageName: node linkType: hard @@ -15971,7 +15678,6 @@ __metadata: languageName: node linkType: hard -"uri-js@npm:^4.2.2": "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -15981,7 +15687,7 @@ __metadata: languageName: node linkType: hard -"url-parse@npm:^1.5.3": +"url-parse@npm:^1.5.10, url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10" dependencies: @@ -15991,6 +15697,13 @@ __metadata: languageName: node linkType: hard +"username-sync@npm:^1.0.2": + version: 1.0.3 + resolution: "username-sync@npm:1.0.3" + checksum: 10/1a2aaa8629d018daebd8500272a3064041e18ed157eb32d098dab6ea7dc111b2904222c61bb50f25340d378f003aacbefb3fd6313dd42586137532bc38befe8e + languageName: node + linkType: hard + "util-deprecate@npm:1.0.2, util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -16373,6 +16086,17 @@ __metadata: languageName: node linkType: hard +"which@npm:^5.0.0": + version: 5.0.0 + resolution: "which@npm:5.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10/6ec99e89ba32c7e748b8a3144e64bfc74aa63e2b2eacbb61a0060ad0b961eb1a632b08fb1de067ed59b002cec3e21de18299216ebf2325ef0f78e0f121e14e90 + languageName: node + linkType: hard + "which@npm:^6.0.0": version: 6.0.1 resolution: "which@npm:6.0.1" @@ -16384,14 +16108,14 @@ __metadata: languageName: node linkType: hard -"which@npm:^6.0.0": - version: 6.0.1 - resolution: "which@npm:6.0.1" +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" dependencies: isexe: "npm:^4.0.0" bin: node-which: bin/which.js - checksum: 10/dbea77c7d3058bf6c78bf9659d2dce4d2b57d39a15b826b2af6ac2e5a219b99dc8a831b79fdbc453c0598adb4f3f84cf9c2491fd52beb9f5d2dececcad117f68 + checksum: 10/913a43ac10df37602ba9795a004dd7ab12ba7dd592aca1f08ec333be1fdd6a49bbf119a88c3f8d0ea70eeb6251726e77069251424d73000299a0a840ed000732 languageName: node linkType: hard @@ -16520,8 +16244,8 @@ __metadata: linkType: hard "ws@npm:^8.11.0, ws@npm:^8.18.0": - version: 8.20.0 - resolution: "ws@npm:8.20.0" + version: 8.21.0 + resolution: "ws@npm:8.21.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -16530,7 +16254,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/b7ab934b21ffdea9f25a5af5097e8c1ec7625db553bca026c5a23e35b7c236f3fb89782f2b57fab9da553864512f9aa7d245827ef998d26ffa1b2187a19a6d10 + checksum: 10/088411956432c8f876158409d5a285cb9ad1382f593391f51d3a599bd0a5b277f876609ebd00fc3596321c4a4c9064d6fffe1ebad960e8ea7fd9ae25324f35c2 languageName: node linkType: hard @@ -16597,26 +16321,12 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^1.10.0": - version: 1.10.3 - resolution: "yaml@npm:1.10.3" - checksum: 10/e2ef2feb92c708138f016c69777a0f1e45f6d3c5e7cbcda30807a98a37eda2e008bd4fa57352b043c65245a4c799d0c99d1f9b3425de40e70929e26d2ea38215 - languageName: node - linkType: hard - -"yaml@npm:^2.8.2": - version: 2.8.3 - resolution: "yaml@npm:2.8.3" +"yaml@npm:2.9.0, yaml@npm:^2.8.3": + version: 2.9.0 + resolution: "yaml@npm:2.9.0" bin: yaml: bin.mjs - checksum: 10/ecad41d39d34fae5cc17ea2d4b7f7f55faacd45cbce8983ba22d48d1ed1a92ed242ea49ea813a79ac39a69f75f9c5a03e7b5395fd954d55476f25e21a47c141d - languageName: node - linkType: hard - -"yargs-parser@npm:20.2.4": - version: 20.2.4 - resolution: "yargs-parser@npm:20.2.4" - checksum: 10/db8f251ae40e24782d5c089ed86883ba3c0ce7f3c174002a67ec500802f928df9d505fea5d04829769221ce20b0f69f6fb1138fbb2e2fb102e3e9d426d20edab + checksum: 10/9a95e8e08651c3d292ab6a5befeb5f57b76801caa097c75bb45c9a70ce19c1b11f57e87a6ef84a579ea070ed2c2c8ac541c88c0ae684d544d5f42c7e77d11b7b languageName: node linkType: hard @@ -16727,8 +16437,8 @@ __metadata: linkType: hard "zod@npm:^4.3.6": - version: 4.3.6 - resolution: "zod@npm:4.3.6" - checksum: 10/25fc0f62e01b557b4644bf0b393bbaf47542ab30877c37837ea8caf314a8713d220c7d7fe51f68ffa72f0e1018ddfa34d96f1973d23033f5a2a5a9b6b9d9da01 + version: 4.4.3 + resolution: "zod@npm:4.4.3" + checksum: 10/804b9a42aa8f35f2b3c5a8dff906291cb749115f83ee2afe3576d70b5b5c53c965365c7f4967690647a9c54af9838ff232a85ff9577a0a36c44b68bc6cdefe36 languageName: node linkType: hard From 52c92a772d51b21db07d83c488dca82fe5e680eb Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 19:38:18 +0300 Subject: [PATCH 19/32] chore: caret --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2c81dfefb1..a76b012bd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2296,7 +2296,7 @@ __metadata: languageName: node linkType: hard -"@json-render/core@npm:0.19.0, @json-render/core@npm:^0.19.0": +"@json-render/core@npm:0.19.0": version: 0.19.0 resolution: "@json-render/core@npm:0.19.0" dependencies: @@ -2307,7 +2307,7 @@ __metadata: languageName: node linkType: hard -"@json-render/react@npm:^0.19.0": +"@json-render/react@npm:0.19.0": version: 0.19.0 resolution: "@json-render/react@npm:0.19.0" dependencies: @@ -3639,7 +3639,7 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-content-sdk/content@workspace:packages/content" dependencies: - "@json-render/core": "npm:^0.19.0" + "@json-render/core": "npm:0.19.0" "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" "@sitecore-content-sdk/events": "npm:2.1.0-beta.1" "@stylistic/eslint-plugin": "npm:^5.2.2" @@ -3931,8 +3931,8 @@ __metadata: version: 0.0.0-use.local resolution: "@sitecore-content-sdk/react@workspace:packages/react" dependencies: - "@json-render/core": "npm:^0.19.0" - "@json-render/react": "npm:^0.19.0" + "@json-render/core": "npm:0.19.0" + "@json-render/react": "npm:0.19.0" "@sitecore-content-sdk/analytics-core": "npm:2.1.0-beta.1" "@sitecore-content-sdk/content": "npm:2.1.0-beta.1" "@sitecore-content-sdk/core": "npm:2.1.0-beta.1" From 0ac7356bbe6db52fb02c7a07b62414a09df283ed Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 19:59:05 +0300 Subject: [PATCH 20/32] chore: minor improvements --- .../cli/src/scripts/project/atoms/update.ts | 4 +- .../cli/src/scripts/project/atoms/utils.ts | 9 +- .../cli/src/scripts/project/atoms/validate.ts | 2 +- .../content/api/content-sdk-content.api.md | 200 +++++------------- .../constants.ts | 0 .../events.test.ts | 0 .../events.ts | 0 .../index.ts | 0 .../types.ts | 0 packages/content/src/atoms/index.ts | 2 +- packages/content/src/atoms/types.ts | 5 +- packages/nextjs/api/content-sdk-nextjs.api.md | 60 +++--- packages/react/api/content-sdk-react.api.md | 164 +++++++------- .../DesignLibraryLowCodeComponent.test.tsx | 2 +- .../DesignLibraryLowCodeComponent.tsx | 2 +- 15 files changed, 171 insertions(+), 279 deletions(-) rename packages/content/src/atoms/{desing-library-bridge => design-library-bridge}/constants.ts (100%) rename packages/content/src/atoms/{desing-library-bridge => design-library-bridge}/events.test.ts (100%) rename packages/content/src/atoms/{desing-library-bridge => design-library-bridge}/events.ts (100%) rename packages/content/src/atoms/{desing-library-bridge => design-library-bridge}/index.ts (100%) rename packages/content/src/atoms/{desing-library-bridge => design-library-bridge}/types.ts (100%) diff --git a/packages/cli/src/scripts/project/atoms/update.ts b/packages/cli/src/scripts/project/atoms/update.ts index fab749464a..a2e5c97caa 100644 --- a/packages/cli/src/scripts/project/atoms/update.ts +++ b/packages/cli/src/scripts/project/atoms/update.ts @@ -20,11 +20,11 @@ export type UpdateArgs = { /** * Handler for `sitecore-tools project atoms update`. - * Regenerates `.sitecore/atom-versions.lock.json` from current atom definitions. + * Regenerates `.sitecore/atoms.lock.json` from current atom definitions. */ export async function handler() { - const currentAtoms = await loadCurrentAtoms(); const catalog = loadCatalog(); + const currentAtoms = await loadCurrentAtoms(catalog); const catalogData = catalog.data as Record; const catalogVersion = typeof catalogData?.version === 'string' ? catalogData.version : undefined; diff --git a/packages/cli/src/scripts/project/atoms/utils.ts b/packages/cli/src/scripts/project/atoms/utils.ts index aba647d13d..9fac8c0c38 100644 --- a/packages/cli/src/scripts/project/atoms/utils.ts +++ b/packages/cli/src/scripts/project/atoms/utils.ts @@ -94,19 +94,20 @@ export function loadCatalog(): CatalogLoadResult { * Load the current atom definitions from the project's atoms module. * Uses tsx to import TypeScript at runtime. * Returns a map of atom name to { version, schemaHash }. + * @param {CatalogLoadResult} [catalog] - Optional pre-loaded catalog. If omitted, the catalog is loaded from disk. * @internal */ -export async function loadCurrentAtoms(): Promise { +export async function loadCurrentAtoms(catalog?: CatalogLoadResult): Promise { const modulePath = resolveAtomsModulePath(); if (!modulePath) throw new Error( `Atoms module not found at ${ATOMS_MODULE_PATH}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx} and export a catalog.` ); - const catalog = loadCatalog(); + const resolvedCatalog = catalog ?? loadCatalog(); const result: AtomsInfoMap = {}; - const componentNames = catalog.componentNames ?? []; - const components = catalog.data?.components ?? {}; + const componentNames = resolvedCatalog.componentNames ?? []; + const components = resolvedCatalog.data?.components ?? {}; for (const name of componentNames) { // Use the component's full data (props schema, slots, etc.) for schema hash except the version, which is pulled out separately. diff --git a/packages/cli/src/scripts/project/atoms/validate.ts b/packages/cli/src/scripts/project/atoms/validate.ts index f64303066b..8c1760e0b3 100644 --- a/packages/cli/src/scripts/project/atoms/validate.ts +++ b/packages/cli/src/scripts/project/atoms/validate.ts @@ -80,7 +80,7 @@ async function validateLockFile(): Promise { } const current = currentAtoms[name]; - const currentVersion = current.version || undefined; + const currentVersion = current.version ?? undefined; // Check per-atom version drift (skip only when neither side declares a version) if (entry.version !== undefined || currentVersion !== undefined) { diff --git a/packages/content/api/content-sdk-content.api.md b/packages/content/api/content-sdk-content.api.md index 2c2afcb15b..c39a4db88c 100644 --- a/packages/content/api/content-sdk-content.api.md +++ b/packages/content/api/content-sdk-content.api.md @@ -17,9 +17,14 @@ import { GraphQLRequestClientConfig } from '@sitecore-content-sdk/core'; import { GraphQLRequestClientFactory } from '@sitecore-content-sdk/core'; import { GraphQLRequestClientFactoryConfig } from '@sitecore-content-sdk/core'; import { RetryStrategy } from '@sitecore-content-sdk/core'; +import { Spec } from '@json-render/core'; // @internal -export type Action = SetStateAction | CallAction; +export interface ActionCatalogEntry { + description: string; + name: string; + paramsSchema: object; +} // @internal export const addComponentPreviewHandler: (importMap: ImportEntry[], callback: (error: unknown | null, Component: unknown) => void) => (() => void) | undefined; @@ -40,35 +45,37 @@ export function addStyleElement(stylesContent: string): void; export function applyMediaUrlRewrite(value: T, transform: (s: string) => string): T; // @internal -export type AtomInfo = { +export interface AtomCatalogActionEntry { + description: string | undefined; name: string; - version?: number; - type: AtomType; - description: string; - props: Record; - allowedChildren: string[]; - defaultChildren?: SerializedDefaultChild[]; - htmlEvents?: string[]; - customEvents?: Record; -}; - -// @internal -export type AtomType = 'atom' | 'atom-child'; + paramsSchema?: object; +} // @internal -export type Binding = ExpressionBinding | EventBinding; +export interface AtomCatalogComponentEntry { + allowedChildren?: string[]; + allowedParents?: string[]; + description?: string; + example?: unknown; + name: string; + propsSchema: object; + slots: string[]; + version?: string; +} // @internal -export interface CallAction { - args?: Primitive[]; - call: string; +export interface AtomCatalogEntry { + description: string; + name: string; + propsSchema: object; + slots: string[]; } // @internal -export type CallbackInfo = { - description: string; - params?: Record; -}; +export interface AtomsCatalogPayload { + actions: ActionCatalogEntry[]; + components: AtomCatalogEntry[]; +} // @public export class CdpHelper { @@ -359,11 +366,8 @@ export interface DictionaryServiceConfig extends CacheOptions, GraphQLServiceCon } // @internal -interface Document_2 { +interface Document_2 extends Spec { name: string; - props?: unknown; - root: Node_2; - state?: Record; } export { Document_2 as Document } @@ -444,19 +448,6 @@ export enum EditMode { Metadata = "metadata" } -// @internal -interface Element_2 { - bindings?: Record; - children?: Node_2[]; - for?: ForLoop; - id?: string; - show?: ShowNode; - staticProps?: Record; - type: string; - version?: number; -} -export { Element_2 as Element } - // @internal export const EMPTY_DATE_FIELD_VALUE = "0001-01-01T00:00:00Z"; @@ -504,25 +495,9 @@ export interface ErrorPagesServiceConfig extends GraphQLServiceConfig { language: string; } -// @internal -export function evaluateShowNode(node: ShowNode, ctx: ResolveContext): boolean; - -// @internal -export interface EventBinding { - actions: Action[]; - arguments: string[]; - bindType: 'event'; -} - // @internal const executeScriptElements: (rootElement: HTMLElement) => void; -// @internal -export interface ExpressionBinding { - bindType: 'expression'; - value: string; -} - // Warning: (ae-forgotten-export) The symbol "_extractFiles" needs to be exported by the entry point api-surface.d.ts // // @public @@ -550,13 +525,6 @@ export interface FieldMetadata { // @internal export function filterComponentsByType(components: ComponentFileWithType[], allowedTypes: ComponentType[]): ComponentFileWithType[]; -// @internal -export interface ForLoop { - as: string; - each: string; - key?: string; -} - declare namespace form { export { executeScriptElements, @@ -642,10 +610,10 @@ export const getContentStylesheetLink: (layoutData: LayoutServiceData, sitecoreE // @internal export function getDefaultMediaUrlTransformer(edgeUrl: string): (value: string) => string; -// Warning: (ae-forgotten-export) The symbol "DesignLibraryAtomsRegistryEvent" needs to be exported by the entry point api-surface.d.ts +// Warning: (ae-forgotten-export) The symbol "DesignLibraryAtomsCatalogEvent" needs to be exported by the entry point api-surface.d.ts // // @internal -export function getDesignLibraryAtomsRegistryEvent(atomsRegistry: AtomInfo[], callbackRegistry: Record): DesignLibraryAtomsRegistryEvent; +export function getDesignLibraryAtomsCatalogEvent(payload: SerializedCatalog): DesignLibraryAtomsCatalogEvent; // Warning: (ae-forgotten-export) The symbol "DesignLibraryComponentPropsEvent" needs to be exported by the entry point api-surface.d.ts // @@ -750,16 +718,6 @@ export { GraphQLRequestClientFactory } export { GraphQLRequestClientFactoryConfig } -// @internal -export function hasFor(node: Element_2): node is Element_2 & { - for: ForLoop; -}; - -// @internal -export function hasShow(node: Element_2): node is Element_2 & { - show: ShowNode; -}; - // @internal export const HIDDEN_RENDERING_NAME = "Hidden Rendering"; @@ -790,9 +748,6 @@ export interface ImportEntryInfo { // @internal export const INVALID_SECRET_HTML_MESSAGE = "Missing or invalid secret"; -// @internal -export function isCallAction(action: Action): action is CallAction; - // @internal export function isDesignLibraryMode(mode: unknown): mode is DesignLibraryMode; @@ -802,33 +757,9 @@ export const isDynamicPlaceholder: (placeholder: string) => boolean; // @public export const isEditorActive: () => boolean; -// @internal -export function isElement(node: Node_2): node is Element_2; - -// @internal -export function isEventBinding(binding: Binding): binding is EventBinding; - -// @internal -export function isExpressionBinding(binding: Binding): binding is ExpressionBinding; - // @public export function isFieldValueEmpty(field: GenericFieldValue | Partial | null | undefined): field is null | undefined; -// @internal -export function isPrimitive(value: unknown): value is Primitive; - -// @internal -export function isSetStateAction(action: Action): action is SetStateAction; - -// @internal -export function isShowAnd(node: ShowNode): node is ShowAnd; - -// @internal -export function isShowComparison(node: ShowNode): node is ShowComparison; - -// @internal -export function isShowOr(node: ShowNode): node is ShowOr; - // @public export interface Item { // (undocumented) @@ -945,10 +876,6 @@ export type ModuleExports = { namespaceExport: string | null; }; -// @internal -type Node_2 = Element_2 | Primitive; -export { Node_2 as Node } - // @public export function normalizePersonalizedRewrite(pathname: string): string; @@ -1056,9 +983,6 @@ export const postToDesignLibrary: (evt: DesignLibraryEvent) => void; // @internal export const PREVIEW_KEY = "sc_preview"; -// @internal -export type Primitive = string | number | boolean | null; - // @internal export const QUERY_PARAM_EDITING_SECRET = "secret"; @@ -1142,21 +1066,6 @@ const replaceMediaUrlPrefix: (url: string, mediaUrlPrefix?: RegExp) => string; // @public export const resetEditorChromes: () => void; -// @internal -export interface ResolveContext { - event?: unknown; - item?: unknown; - props: Record; - scope?: Record; - state: Record; -} - -// @internal -export const resolveIfTemplate: (value: unknown, ctx: ResolveContext) => unknown; - -// @internal -export function resolveTemplateString(template: string, ctx: ResolveContext): unknown; - export { RetryStrategy } // @public @@ -1242,6 +1151,13 @@ export const sendAtomsErrorEvent: (error: unknown, type: DesignLibraryAtomsError // @internal export const sendErrorEvent: (uid: string, error: unknown, type: DesignLibraryPreviewError) => void; +// @internal +export interface SerializedCatalog { + actions: AtomCatalogActionEntry[]; + components: AtomCatalogComponentEntry[]; + version?: string; +} + // @internal export interface ServerComponentPreviewEventArgs extends DesignLibraryEvent { // (undocumented) @@ -1255,31 +1171,6 @@ export interface ServerComponentPreviewEventArgs extends DesignLibraryEvent { name: typeof DESIGN_LIBRARY_COMPONENT_PREVIEW_EVENT_NAME; } -// @internal -export interface SetStateAction { - setState: Record; -} - -// @internal -export interface ShowAnd { - and: ShowNode[]; -} - -// @internal -export interface ShowComparison { - left: string; - op: 'eq' | 'ne'; - right: string; -} - -// @internal -export type ShowNode = ShowComparison | ShowAnd | ShowOr; - -// @internal -export interface ShowOr { - or: ShowNode[]; -} - // @public export const SITE_KEY = "sc_site"; @@ -1306,6 +1197,11 @@ export type SitecoreCliConfigInput = { componentMap?: GenerateMapArgs & { generator?: GenerateMapFunction; }; + atoms?: { + validation?: { + breakOnError?: boolean; + }; + }; }; // Warning: (ae-forgotten-export) The symbol "BaseSitecoreClient" needs to be exported by the entry point api-surface.d.ts @@ -1369,6 +1265,13 @@ export type SitecoreClientInit = Omit; @@ -1568,7 +1471,6 @@ export type WriteImportMapArgsInternal = WriteImportMapArgs & { // Warnings were encountered during analysis: // // src/client/sitecore-client.ts:68:3 - (ae-forgotten-export) The symbol "PageModeName" needs to be exported by the entry point api-surface.d.ts -// src/editing/atoms-builder/atoms-builder.ts:95:3 - (ae-forgotten-export) The symbol "SerializedDefaultChild" needs to be exported by the entry point api-surface.d.ts // src/editing/codegen/preview.ts:115:3 - (ae-forgotten-export) The symbol "ComponentImport_2" needs to be exported by the entry point api-surface.d.ts // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "ComponentMapTemplate" which is marked as @internal // src/tools/generate-map.ts:24:3 - (ae-incompatible-release-tags) The symbol "mapTemplate" is marked as @public, but its signature references "EnhancedComponentMapTemplate" which is marked as @internal diff --git a/packages/content/src/atoms/desing-library-bridge/constants.ts b/packages/content/src/atoms/design-library-bridge/constants.ts similarity index 100% rename from packages/content/src/atoms/desing-library-bridge/constants.ts rename to packages/content/src/atoms/design-library-bridge/constants.ts diff --git a/packages/content/src/atoms/desing-library-bridge/events.test.ts b/packages/content/src/atoms/design-library-bridge/events.test.ts similarity index 100% rename from packages/content/src/atoms/desing-library-bridge/events.test.ts rename to packages/content/src/atoms/design-library-bridge/events.test.ts diff --git a/packages/content/src/atoms/desing-library-bridge/events.ts b/packages/content/src/atoms/design-library-bridge/events.ts similarity index 100% rename from packages/content/src/atoms/desing-library-bridge/events.ts rename to packages/content/src/atoms/design-library-bridge/events.ts diff --git a/packages/content/src/atoms/desing-library-bridge/index.ts b/packages/content/src/atoms/design-library-bridge/index.ts similarity index 100% rename from packages/content/src/atoms/desing-library-bridge/index.ts rename to packages/content/src/atoms/design-library-bridge/index.ts diff --git a/packages/content/src/atoms/desing-library-bridge/types.ts b/packages/content/src/atoms/design-library-bridge/types.ts similarity index 100% rename from packages/content/src/atoms/desing-library-bridge/types.ts rename to packages/content/src/atoms/design-library-bridge/types.ts diff --git a/packages/content/src/atoms/index.ts b/packages/content/src/atoms/index.ts index 38d9dc123f..6bce6fa4fd 100644 --- a/packages/content/src/atoms/index.ts +++ b/packages/content/src/atoms/index.ts @@ -14,4 +14,4 @@ export { sendAtomsErrorEvent, DesignLibraryAtomsError, addDocumentUpdateHandler, -} from './desing-library-bridge'; +} from './design-library-bridge'; diff --git a/packages/content/src/atoms/types.ts b/packages/content/src/atoms/types.ts index 8cee8a2bad..7195c9aa7b 100644 --- a/packages/content/src/atoms/types.ts +++ b/packages/content/src/atoms/types.ts @@ -62,8 +62,11 @@ export interface SitecoreComponentMeta { allowedParents?: string[]; } +/** + * A document is a JSON object that conforms to the JSON Schema specification. + * @internal + */ export interface Document extends Spec { /** Human-readable identifier of the document. */ name: string; } - diff --git a/packages/nextjs/api/content-sdk-nextjs.api.md b/packages/nextjs/api/content-sdk-nextjs.api.md index a378567aae..9ac3535330 100644 --- a/packages/nextjs/api/content-sdk-nextjs.api.md +++ b/packages/nextjs/api/content-sdk-nextjs.api.md @@ -7,11 +7,13 @@ import { AnalyticsAdapter } from '@sitecore-content-sdk/analytics-core/internal'; import { AppPlaceholder } from '@sitecore-content-sdk/react'; import { AppPlaceholderProps } from '@sitecore-content-sdk/react'; -import { ArgMeta } from '@sitecore-content-sdk/react'; -import { AtomChild } from '@sitecore-content-sdk/react'; -import { AtomMetadata } from '@sitecore-content-sdk/react'; -import { AtomSchemaInput } from '@sitecore-content-sdk/react'; -import { AtomType } from '@sitecore-content-sdk/react'; +import { AtomActionDefinition } from '@sitecore-content-sdk/react'; +import { AtomActionHandler } from '@sitecore-content-sdk/react'; +import { AtomComponentDefinition } from '@sitecore-content-sdk/react'; +import { AtomsActionsMap } from '@sitecore-content-sdk/react'; +import { AtomsCatalogInput } from '@sitecore-content-sdk/react'; +import { AtomsComponentsMap } from '@sitecore-content-sdk/react'; +import { AtomsConfig } from '@sitecore-content-sdk/react'; import { BYOCClientWrapper } from '@sitecore-content-sdk/react'; import { BYOCComponent } from '@sitecore-content-sdk/react'; import { BYOCComponentParams } from '@sitecore-content-sdk/react'; @@ -20,8 +22,6 @@ import { BYOCServerWrapper } from '@sitecore-content-sdk/react'; import { BYOCWrapper as BYOCWrapper_2 } from '@sitecore-content-sdk/react'; import { CacheClient } from '@sitecore-content-sdk/core'; import { CacheOptions } from '@sitecore-content-sdk/core'; -import { CallbackArgZodTuple } from '@sitecore-content-sdk/react'; -import { CallbackPropKeys } from '@sitecore-content-sdk/react'; import { CdpHelper } from '@sitecore-content-sdk/content/personalize'; import { ClientEditingChromesUpdate } from '@sitecore-content-sdk/react'; import { ComponentFields } from '@sitecore-content-sdk/content/layout'; @@ -32,22 +32,21 @@ import { ComponentMap } from '@sitecore-content-sdk/react'; import { ComponentParams } from '@sitecore-content-sdk/content/layout'; import { ComponentRendering } from '@sitecore-content-sdk/content/layout'; import { constants } from '@sitecore-content-sdk/core'; -import { createAtom } from '@sitecore-content-sdk/react'; import { createGraphQLClientFactory } from '@sitecore-content-sdk/content/client'; import { DateField } from '@sitecore-content-sdk/react'; import { DateFieldSchema } from '@sitecore-content-sdk/react'; import { dateFieldSchema } from '@sitecore-content-sdk/react'; import { DeepRequired } from '@sitecore-content-sdk/content/config'; -import { DefaultChild } from '@sitecore-content-sdk/react'; import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-content-sdk/react'; import { DefaultEmptyFieldEditingComponentText } from '@sitecore-content-sdk/react'; import { DefaultRetryStrategy } from '@sitecore-content-sdk/content/client'; +import { defineAtomsCatalog as defineAtomsCatalog_2 } from '@sitecore-content-sdk/react'; +import { defineAtomsRegistry as defineAtomsRegistry_2 } from '@sitecore-content-sdk/react'; import { DesignLibrary } from '@sitecore-content-sdk/react'; import { DesignLibraryRenderPreviewData } from '@sitecore-content-sdk/content/editing'; import { DictionaryPhrases } from '@sitecore-content-sdk/content/i18n'; import { DictionaryService } from '@sitecore-content-sdk/content/i18n'; import { DictionaryServiceConfig } from '@sitecore-content-sdk/content/i18n'; -import { EditableComponentProps } from '@sitecore-content-sdk/react'; import { EDITING_COMPONENT_ID } from '@sitecore-content-sdk/content/layout'; import { EDITING_COMPONENT_PLACEHOLDER } from '@sitecore-content-sdk/content/layout'; import { EditingRenderQueryParams } from '@sitecore-content-sdk/content/editing'; @@ -85,7 +84,6 @@ import { getComponentList } from '@sitecore-content-sdk/content/node-tools'; import { getContentStylesheetLink } from '@sitecore-content-sdk/content/layout'; import { getDesignLibraryStylesheetLinks } from '@sitecore-content-sdk/react'; import { getEdgeProxyContentUrl } from '@sitecore-content-sdk/content/client'; -import { getFieldMeta } from '@sitecore-content-sdk/react'; import { getFieldValue } from '@sitecore-content-sdk/content/layout'; import { getGroomedVariantIds } from '@sitecore-content-sdk/content/personalize'; import { getPersonalizedRewrite } from '@sitecore-content-sdk/content/personalize'; @@ -201,9 +199,9 @@ import { Text as Text_2 } from '@sitecore-content-sdk/react'; import { TextField } from '@sitecore-content-sdk/react'; import { TextFieldSchema } from '@sitecore-content-sdk/react'; import { textFieldSchema } from '@sitecore-content-sdk/react'; +import { useBoundProp } from '@sitecore-content-sdk/react'; import { useSitecore } from '@sitecore-content-sdk/react'; import { withAppPlaceholder } from '@sitecore-content-sdk/react'; -import { withArgMeta } from '@sitecore-content-sdk/react'; import { withDatasourceCheck } from '@sitecore-content-sdk/react'; import { withEditorChromes } from '@sitecore-content-sdk/react'; import { withEmptyFieldEditingComponent } from '@sitecore-content-sdk/react'; @@ -244,6 +242,20 @@ export class AppRouterMultisiteProxy extends MultisiteProxy { protected shouldWarnWhenDisabled(_res: NextResponse): void; } +export { AtomActionDefinition } + +export { AtomActionHandler } + +export { AtomComponentDefinition } + +export { AtomsActionsMap } + +export { AtomsCatalogInput } + +export { AtomsComponentsMap } + +export { AtomsConfig } + // @public export class BotTrackingProxy extends ProxyBase { constructor(config: BotTrackingProxyConfig); @@ -298,10 +310,6 @@ export type CachedPageParams = { export { CacheOptions } -export { CallbackArgZodTuple } - -export { CallbackPropKeys } - export { CdpHelper } export { ClientEditingChromesUpdate } @@ -377,8 +385,6 @@ export { ComponentRendering } export { constants } -export { createAtom } - // Warning: (ae-forgotten-export) The symbol "EditingConfigRouteHandlerOptions" needs to be exported by the entry point api-surface.d.ts // // @public @@ -434,8 +440,6 @@ export { dateFieldSchema } const debug_2: Record; export { debug_2 as debug } -export { DefaultChild } - export { DefaultEmptyFieldEditingComponentImage } export { DefaultEmptyFieldEditingComponentText } @@ -448,6 +452,12 @@ export { DefaultRetryStrategy } // @public export const defaultServerImportEntries: ImportEntry[]; +// @public +export const defineAtomsCatalog: typeof defineAtomsCatalog_2; + +// @public +export const defineAtomsRegistry: typeof defineAtomsRegistry_2; + // @public export const defineCliConfig: (cliConfig: SitecoreCliConfigInput) => SitecoreCliConfig; @@ -472,8 +482,6 @@ export { DictionaryService } export { DictionaryServiceConfig } -export { EditableComponentProps } - export { EDITING_COMPONENT_ID } export { EDITING_COMPONENT_PLACEHOLDER } @@ -622,8 +630,6 @@ export { getDesignLibraryStylesheetLinks } export { getEdgeProxyContentUrl } -export { getFieldMeta } - export { getFieldValue } export { getGroomedVariantIds } @@ -901,6 +907,8 @@ export type PreviewProxyConfig = { client: SitecoreClient; }; +export { PropMeta } + // @public export type ProxiesContext = Map; @@ -1184,6 +1192,8 @@ export { TextFieldSchema } export { textFieldSchema } +export { useBoundProp } + // @public export function useComponentProps(componentUid: string | undefined): ComponentData | undefined; @@ -1191,8 +1201,6 @@ export { useSitecore } export { withAppPlaceholder } -export { withArgMeta } - export { withDatasourceCheck } export { withEditorChromes } diff --git a/packages/react/api/content-sdk-react.api.md b/packages/react/api/content-sdk-react.api.md index fdd8de311e..e7ecee9e3a 100644 --- a/packages/react/api/content-sdk-react.api.md +++ b/packages/react/api/content-sdk-react.api.md @@ -4,20 +4,22 @@ ```ts -import { AtomType } from '@sitecore-content-sdk/content/editing'; import { CacheClient } from '@sitecore-content-sdk/core'; import { CacheOptions } from '@sitecore-content-sdk/core'; +import { Catalog } from '@json-render/core'; import { ClientError } from '@sitecore-content-sdk/core'; import { ComponentFields } from '@sitecore-content-sdk/content/layout'; import { ComponentParams } from '@sitecore-content-sdk/content/layout'; +import type { ComponentRenderer } from '@json-render/react'; import { ComponentRendering } from '@sitecore-content-sdk/content/layout'; import { ComponentType } from 'react'; import { constants } from '@sitecore-content-sdk/core'; import { debug as debug_2 } from '@sitecore-content-sdk/search'; import { DefaultRetryStrategy } from '@sitecore-content-sdk/content/client'; +import { defineRegistry } from '@json-render/react'; +import type { DefineRegistryResult } from '@json-render/react'; import { DictionaryPhrases } from '@sitecore-content-sdk/content/i18n'; import { DictionaryService } from '@sitecore-content-sdk/content/i18n'; -import type { Document as Document_2 } from '@sitecore-content-sdk/content/atoms'; import { EditMode } from '@sitecore-content-sdk/content/layout'; import { enableDebug } from '@sitecore-content-sdk/core'; import { EnhancedOmit } from '@sitecore-content-sdk/core/tools'; @@ -34,6 +36,7 @@ import { GraphQLClientError } from '@sitecore-content-sdk/content/client'; import { GraphQLRequestClient } from '@sitecore-content-sdk/content/client'; import { GraphQLRequestClientFactoryConfig } from '@sitecore-content-sdk/content/client'; import { ImportEntry } from '@sitecore-content-sdk/content/codegen'; +import type { InferCatalogInput } from '@json-render/core'; import { isEditorActive } from '@sitecore-content-sdk/content/editing'; import { Item } from '@sitecore-content-sdk/content/layout'; import { JSX as JSX_2 } from 'react'; @@ -51,15 +54,19 @@ import { Page } from '@sitecore-content-sdk/content/client'; import { PageMode } from '@sitecore-content-sdk/content/client'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; +import type { ReactSchema } from '@json-render/react'; import { RefAttributes } from 'react'; import { resetEditorChromes } from '@sitecore-content-sdk/content/editing'; import { RetryStrategy } from '@sitecore-content-sdk/content/client'; import { RouteData } from '@sitecore-content-sdk/content/layout'; +import { SchemaType } from '@json-render/core'; import { SearchDocument } from '@sitecore-content-sdk/search'; import { SearchParameters } from '@sitecore-content-sdk/search'; +import { SitecoreComponentMeta } from '@sitecore-content-sdk/content/atoms'; import { SitecoreConfig } from '@sitecore-content-sdk/content/config'; import { SitePathService } from '@sitecore-content-sdk/content/site'; import { SitePathServiceConfig } from '@sitecore-content-sdk/content/site'; +import { useBoundProp as useBoundProp_2 } from '@json-render/react'; import { z } from 'zod'; // @public @@ -68,46 +75,42 @@ export const AppPlaceholder: (props: AppPlaceholderProps) => React_2.JSX.Element // @public export type AppPlaceholderProps = Omit & Required>; +// Warning: (ae-forgotten-export) The symbol "BaseAction" needs to be exported by the entry point api-surface.d.ts +// // @public -export type ArgMeta = { - argName: string; -}; +export type AtomActionDefinition = BaseAction; // @public -export type AtomChild = AtomMetadata | 'text' | 'atom'; +export type AtomActionHandler = (params: Record) => Promise | void; +// Warning: (ae-forgotten-export) The symbol "BaseComponent" needs to be exported by the entry point api-surface.d.ts +// // @public -export type AtomMetadata = { - name: string; - version?: number; - type: AtomType; - description: string; - props: z.ZodObject; - component: (props: unknown) => React.ReactNode; - htmlEvents?: string[]; - customEvents?: Record; - allowedChildren?: AtomChild[]; - defaultChildren?: DefaultChild[]; -}; +export type AtomComponentDefinition = BaseComponent & SitecoreComponentMeta; // @public -export type AtomSchemaInput = { - name: string; - description: string; - type?: AtomType; - version?: number; - props: { - [K in keyof EditableComponentProps]?: z.ZodType[K]>; - }; - htmlEvents?: CallbackPropKeys>[]; - customEvents?: { - [K in CallbackPropKeys>]?: CallbackArgZodTuple[K]>>; - }; - allowedChildren?: AtomChild[]; - defaultChildren?: DefaultChild[]; +export type AtomsActionsMap = Record; + +// Warning: (ae-forgotten-export) The symbol "BaseCatalog" needs to be exported by the entry point api-surface.d.ts +// +// @public +export type AtomsCatalogInput = BaseCatalog & { + version?: string; + components: Record; + actions: Record; }; -export { AtomType } +// Warning: (ae-forgotten-export) The symbol "AtomsComponentRenderer" needs to be exported by the entry point api-surface.d.ts +// +// @public +export type AtomsComponentsMap = Record; + +// @public +export interface AtomsConfig { + catalog: Catalog; + navigate?: (path: string) => void; + registry: DefineRegistryResult; +} // @public export class BYOCComponent extends React_2.Component { @@ -155,16 +158,6 @@ export { CacheClient } export { CacheOptions } -// @public -export type CallbackArgZodTuple = F extends (...args: infer A) => unknown ? { - [I in keyof A]: z.ZodType; -} : never; - -// @public -export type CallbackPropKeys = { - [K in keyof T & string]: NonNullable extends (...args: any[]) => unknown ? K : never; -}[keyof T & string]; - // @public export const ClientEditingChromesUpdate: () => JSX_2.Element; @@ -181,9 +174,6 @@ export { ComponentRendering } export { constants } -// @public -export function createAtom(component: C, schema: AtomSchemaInput): AtomMetadata; - // @public export const DateField: React_2.FC; @@ -211,12 +201,6 @@ export const dateFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ export { debug_2 as debug } -// @public -export type DefaultChild = AtomMetadata | { - atom: AtomMetadata; - props?: Record; -}; - // @public export const DefaultEmptyFieldEditingComponentImage: React_2.FC<{ [key: string]: unknown; @@ -231,6 +215,36 @@ export const DefaultEmptyFieldEditingComponentText: React_2.FC<{ export { DefaultRetryStrategy } +// Warning: (ae-forgotten-export) The symbol "Exact" needs to be exported by the entry point api-surface.d.ts +// +// @public +export function defineAtomsCatalog(input: Exact): Catalog< { +spec: SchemaType<"object", { +root: SchemaType<"string", unknown>; +elements: SchemaType<"record", SchemaType<"object", { +type: SchemaType<"ref", string>; +props: SchemaType<"propsOf", string>; +children: SchemaType<"array", SchemaType<"string", unknown>>; +visible: SchemaType<"any", unknown>; +}>>; +}>; +catalog: SchemaType<"object", { +components: SchemaType<"map", { +props: SchemaType<"zod", unknown>; +slots: SchemaType<"array", SchemaType<"string", unknown>>; +description: SchemaType<"string", unknown>; +example: SchemaType<"any", unknown>; +}>; +actions: SchemaType<"map", { +params: SchemaType<"zod", unknown>; +description: SchemaType<"string", unknown>; +}>; +}>; +}, Exact>; + +// @public +export const defineAtomsRegistry: typeof defineRegistry; + // @public export const DesignLibrary: () => React_2.JSX.Element | null; @@ -268,11 +282,6 @@ export type DynamicComponent = React.ComponentType<{ params?: ComponentParams; }>; -// Warning: (ae-forgotten-export) The symbol "PropsOfComponent" needs to be exported by the entry point api-surface.d.ts -// -// @public -export type EditableComponentProps = Omit, 'children' | 'ref'>; - // @public export const EditingScripts: () => React_2.JSX.Element; @@ -371,9 +380,6 @@ export { getContentStylesheetLink } export { getDesignLibraryStylesheetLinks } -// @internal -export function getFieldMeta(schemaOrJsonSchema: z.ZodType | Record): Record | undefined; - export { getFieldValue } export { GraphQLClientError } @@ -637,10 +643,7 @@ export const SitecoreProviderReactContext: React_2.Context Promise; @@ -652,30 +655,6 @@ export { SitePathService } export { SitePathServiceConfig } -// @internal -export type StudioComponentParams = { - componentRef?: string; -}; - -// @internal -export const StudioComponentServerWrapper: (props: StudioComponentServerWrapperProps) => Promise; - -// @internal -export type StudioComponentServerWrapperProps = { - componentRef: string; - fieldNames?: string; -}; - -// @internal -const StudioComponentWrapper: (props: StudioComponentWrapperProps) => JSX_2.Element | null; -export { StudioComponentWrapper as StudioComponentClientWrapper } -export { StudioComponentWrapper } - -// @internal -export type StudioComponentWrapperProps = { - document?: Document_2 | null; -}; - // Warning: (ae-forgotten-export) The symbol "TextProps" needs to be exported by the entry point api-surface.d.ts // // @public @@ -696,6 +675,9 @@ export const textFieldSchema: (extra?: z.ZodRawShape) => z.ZodObject<{ value: z.ZodOptional>; }, z.core.$strip>; +// @public +export const useBoundProp: typeof useBoundProp_2; + // @public export const useInfiniteSearch: (options: UseInfiniteSearchOptions) => UseInfiniteSearchState; @@ -756,9 +738,6 @@ export function useSitecore(options?: UseSitecoreOptions): SitecoreProviderState // @public export const withAppPlaceholder: (Component: ComponentType) => (props: W) => React_2.JSX.Element; -// @public -export function withArgMeta(schema: T, meta: ArgMeta): T; - // Warning: (ae-forgotten-export) The symbol "WithDatasourceCheckOptions" needs to be exported by the entry point api-surface.d.ts // Warning: (ae-forgotten-export) The symbol "WithDatasourceCheckProps" needs to be exported by the entry point api-surface.d.ts // @@ -802,8 +781,7 @@ export function withSitecore(options?: UseSitecoreOptions): ', () => { mode: { name: 'normal', isDesignLibrary: false, - designLibrary: { isVariantGeneration: false }, + designLibrary: { isVariantGeneration: false, isLowCode: false }, isNormal: true, isPreview: false, isEditing: false, diff --git a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx index 7dfe7bfa5e..32d05be7de 100644 --- a/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx +++ b/packages/react/src/components/DesignLibrary/DesignLibraryLowCodeComponent.tsx @@ -29,7 +29,7 @@ export const __mockDependencies = (mocks: any) => { * Facilitates the communication between the Design Studio and the Rendering Host when previewing a low code component built with the Atoms. * - On mount, it serializes the atoms catalog and sends it to the Design Studio via the `atoms:catalog` event. * - Receives Component model data updates via document update handler and renders the low code component - * via {@link StudioComponentWrapper} (same client path as Studio / NCC preview elsewhere). + * via `StudioComponentWrapper` (same client path as Studio / NCC preview elsewhere). * @internal */ export const DesignLibraryLowCodeComponent = () => { From 6cc1497ee8bbd48b60885a83f05f67fc56428b3c Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Mon, 15 Jun 2026 20:36:59 +0300 Subject: [PATCH 21/32] chore: update git ignore files to keep the atoms lock --- .../gitignore | 3 +- .../src/templates/nextjs-app-router/gitignore | 5 +- .../src/templates/nextjs/gitignore | 5 +- yarn.lock | 880 +++++++++++++++++- 4 files changed, 873 insertions(+), 20 deletions(-) diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/gitignore b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/gitignore index 162d77c44a..0a3a3a3838 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/gitignore +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router-cache-components/gitignore @@ -22,4 +22,5 @@ .vercel # sitecore temp files (regenerated by `sitecore-tools project component generate-map`) -.sitecore/ +.sitecore/* +!.sitecore/atoms.lock.json \ No newline at end of file diff --git a/packages/create-content-sdk-app/src/templates/nextjs-app-router/gitignore b/packages/create-content-sdk-app/src/templates/nextjs-app-router/gitignore index 1bab76c255..c2553a1bca 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs-app-router/gitignore +++ b/packages/create-content-sdk-app/src/templates/nextjs-app-router/gitignore @@ -21,5 +21,6 @@ # vercel .vercel -# sitecore temp files -.sitecore/ +# sitecore files +.sitecore/* +!.sitecore/atoms.lock.json \ No newline at end of file diff --git a/packages/create-content-sdk-app/src/templates/nextjs/gitignore b/packages/create-content-sdk-app/src/templates/nextjs/gitignore index 1bab76c255..9aa0b92590 100644 --- a/packages/create-content-sdk-app/src/templates/nextjs/gitignore +++ b/packages/create-content-sdk-app/src/templates/nextjs/gitignore @@ -21,5 +21,6 @@ # vercel .vercel -# sitecore temp files -.sitecore/ +# sitecore files +.sitecore/* +!.sitecore/atoms.lock.json diff --git a/yarn.lock b/yarn.lock index a76b012bd6..aed99cab02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 8 cacheKey: 10 +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: 10/bdc35758b552bcf045733ac047fb7f9a07c4678b944c641adfbd41f798b4b91fffd0fdc0df2578d9b0afc7b4d636aa6e110ead5d6281a2adc1ab90efd7f057f8 + languageName: node + linkType: hard + "@asamuzakjp/css-color@npm:^3.2.0": version: 3.2.0 resolution: "@asamuzakjp/css-color@npm:3.2.0" @@ -709,7 +716,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/core@npm:^1.1.0": +"@emnapi/core@npm:^1.1.0, @emnapi/core@npm:^1.10.0": version: 1.11.1 resolution: "@emnapi/core@npm:1.11.1" dependencies: @@ -737,7 +744,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.7.0": +"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.10.0, @emnapi/runtime@npm:^1.7.0": version: 1.11.1 resolution: "@emnapi/runtime@npm:1.11.1" dependencies: @@ -764,7 +771,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/wasi-threads@npm:1.2.2": +"@emnapi/wasi-threads@npm:1.2.2, @emnapi/wasi-threads@npm:^1.2.1": version: 1.2.2 resolution: "@emnapi/wasi-threads@npm:1.2.2" dependencies: @@ -773,6 +780,13 @@ __metadata: languageName: node linkType: hard +"@epic-web/invariant@npm:^1.0.0": + version: 1.0.0 + resolution: "@epic-web/invariant@npm:1.0.0" + checksum: 10/28b36a7447f60b84f9d6a23571480042170ef4239a577577ad8447f64a2e4f1a4e57e6fe1b592e61534c5ab53ff67776130e6c88a68cbd997eb6e9c9759a5934 + languageName: node + linkType: hard + "@es-joy/jsdoccomment@npm:~0.52.0": version: 0.52.0 resolution: "@es-joy/jsdoccomment@npm:0.52.0" @@ -1217,7 +1231,7 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.5": +"@eslint/eslintrc@npm:^3, @eslint/eslintrc@npm:^3.3.5": version: 3.3.5 resolution: "@eslint/eslintrc@npm:3.3.5" dependencies: @@ -1258,6 +1272,38 @@ __metadata: languageName: node linkType: hard +"@formatjs/fast-memoize@npm:3.1.6, @formatjs/fast-memoize@npm:^3.1.0": + version: 3.1.6 + resolution: "@formatjs/fast-memoize@npm:3.1.6" + checksum: 10/7dec3e82586d4c4889671c6081d7b0d87b2c229ba551d8328f96ae8ab58b2cd6056fc5d360c0b0c9688b7164f12ef517428016fbce15b950987a77005e481824 + languageName: node + linkType: hard + +"@formatjs/icu-messageformat-parser@npm:3.5.11, @formatjs/icu-messageformat-parser@npm:^3.4.0": + version: 3.5.11 + resolution: "@formatjs/icu-messageformat-parser@npm:3.5.11" + dependencies: + "@formatjs/icu-skeleton-parser": "npm:2.1.10" + checksum: 10/59b8323b9615edb4ca6463d09108581469de7566f2aec106f1d87b259333cdbf9577d9c22a990bc454f3f461c799f4add683a9ff7185af6fa225fcd29ec336da + languageName: node + linkType: hard + +"@formatjs/icu-skeleton-parser@npm:2.1.10": + version: 2.1.10 + resolution: "@formatjs/icu-skeleton-parser@npm:2.1.10" + checksum: 10/ec30d106ce38de80f4128d0cdfac15699628652807695843254bf0d31650bd0dc4b57e48691d164556234494d59b2816e710fa12321234c85b803c5cda32bedf + languageName: node + linkType: hard + +"@formatjs/intl-localematcher@npm:^0.8.1": + version: 0.8.10 + resolution: "@formatjs/intl-localematcher@npm:0.8.10" + dependencies: + "@formatjs/fast-memoize": "npm:3.1.6" + checksum: 10/d9d3f408363091bf35950a842f58c662d88d9f54d9a53b1238cb673a73d1345412d78e01a2d56ec58cef81b9c66a8ce0ee78ed9c31976bd56dc68a38ef3cbc0f + languageName: node + linkType: hard + "@gar/promise-retry@npm:^1.0.0, @gar/promise-retry@npm:^1.0.2": version: 1.0.3 resolution: "@gar/promise-retry@npm:1.0.3" @@ -3064,6 +3110,150 @@ __metadata: languageName: node linkType: hard +"@parcel/watcher-android-arm64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-android-arm64@npm:2.5.6" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-arm64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-darwin-arm64@npm:2.5.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-x64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-darwin-x64@npm:2.5.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-freebsd-x64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-freebsd-x64@npm:2.5.6" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm-glibc@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.5.6" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm-musl@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-arm-musl@npm:2.5.6" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-glibc@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.5.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-musl@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.5.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-glibc@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.5.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-musl@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.5.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-win32-arm64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-win32-arm64@npm:2.5.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-win32-ia32@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-win32-ia32@npm:2.5.6" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@parcel/watcher-win32-x64@npm:2.5.6": + version: 2.5.6 + resolution: "@parcel/watcher-win32-x64@npm:2.5.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher@npm:^2.4.1": + version: 2.5.6 + resolution: "@parcel/watcher@npm:2.5.6" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.5.6" + "@parcel/watcher-darwin-arm64": "npm:2.5.6" + "@parcel/watcher-darwin-x64": "npm:2.5.6" + "@parcel/watcher-freebsd-x64": "npm:2.5.6" + "@parcel/watcher-linux-arm-glibc": "npm:2.5.6" + "@parcel/watcher-linux-arm-musl": "npm:2.5.6" + "@parcel/watcher-linux-arm64-glibc": "npm:2.5.6" + "@parcel/watcher-linux-arm64-musl": "npm:2.5.6" + "@parcel/watcher-linux-x64-glibc": "npm:2.5.6" + "@parcel/watcher-linux-x64-musl": "npm:2.5.6" + "@parcel/watcher-win32-arm64": "npm:2.5.6" + "@parcel/watcher-win32-ia32": "npm:2.5.6" + "@parcel/watcher-win32-x64": "npm:2.5.6" + detect-libc: "npm:^2.0.3" + is-glob: "npm:^4.0.3" + node-addon-api: "npm:^7.0.0" + node-gyp: "npm:latest" + picomatch: "npm:^4.0.3" + dependenciesMeta: + "@parcel/watcher-android-arm64": + optional: true + "@parcel/watcher-darwin-arm64": + optional: true + "@parcel/watcher-darwin-x64": + optional: true + "@parcel/watcher-freebsd-x64": + optional: true + "@parcel/watcher-linux-arm-glibc": + optional: true + "@parcel/watcher-linux-arm-musl": + optional: true + "@parcel/watcher-linux-arm64-glibc": + optional: true + "@parcel/watcher-linux-arm64-musl": + optional: true + "@parcel/watcher-linux-x64-glibc": + optional: true + "@parcel/watcher-linux-x64-musl": + optional: true + "@parcel/watcher-win32-arm64": + optional: true + "@parcel/watcher-win32-ia32": + optional: true + "@parcel/watcher-win32-x64": + optional: true + checksum: 10/00e027ef6bd67239bd63d63d062363a0263377a3de3114c29f0f717076616b3a03fd67902a70ba52dbb7241efc27498a8f1da983aa41280c454b9c1246a6f191 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3355,6 +3545,13 @@ __metadata: languageName: node linkType: hard +"@schummar/icu-type-parser@npm:1.21.5": + version: 1.21.5 + resolution: "@schummar/icu-type-parser@npm:1.21.5" + checksum: 10/4e83edf93bf49c414d9e2f5bb0457dfd012586d4e24f3850a4cc5b6a18c5329868e8a4b1cb0d4ad8490f175fd21d00229213eb9b2e799809015d85788e567139 + languageName: node + linkType: hard + "@shikijs/engine-oniguruma@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/engine-oniguruma@npm:3.23.0" @@ -3532,7 +3729,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.1.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.1.0-beta.1, @sitecore-content-sdk/analytics-core@npm:^2.1.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: @@ -3591,7 +3788,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/cli@workspace:packages/cli": +"@sitecore-content-sdk/cli@npm:^2.1.0-beta.1, @sitecore-content-sdk/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: @@ -3752,7 +3949,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/events@npm:2.1.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/events@npm:2.1.0-beta.1, @sitecore-content-sdk/events@npm:^2.1.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: @@ -3819,7 +4016,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/nextjs@workspace:packages/nextjs": +"@sitecore-content-sdk/nextjs@npm:^2.1.0-beta.1, @sitecore-content-sdk/nextjs@workspace:packages/nextjs": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: @@ -3886,7 +4083,7 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.1.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.1.0-beta.1, @sitecore-content-sdk/personalize@npm:^2.1.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: @@ -4056,6 +4253,15 @@ __metadata: languageName: node linkType: hard +"@sitecore/components@npm:~2.1.0": + version: 2.1.1 + resolution: "@sitecore/components@npm:2.1.1" + peerDependencies: + "@sitecore/byoc": ^0.3.3 + checksum: 10/581a6905d5bb96e44807bd3de66902ab11077187b9800bde2eae13c75a99479b77d80fce24755decf1e3da850f76cb80b92b555c58830b59527f3c0587832441 + languageName: node + linkType: hard + "@stylistic/eslint-plugin@npm:^5.2.2": version: 5.10.0 resolution: "@stylistic/eslint-plugin@npm:5.10.0" @@ -4072,6 +4278,149 @@ __metadata: languageName: node linkType: hard +"@swc/core-darwin-arm64@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-darwin-arm64@npm:1.15.41" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@swc/core-darwin-x64@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-darwin-x64@npm:1.15.41" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@swc/core-linux-arm-gnueabihf@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.41" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@swc/core-linux-arm64-gnu@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-arm64-gnu@npm:1.15.41" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-arm64-musl@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-arm64-musl@npm:1.15.41" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@swc/core-linux-ppc64-gnu@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-ppc64-gnu@npm:1.15.41" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-s390x-gnu@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-s390x-gnu@npm:1.15.41" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-x64-gnu@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-x64-gnu@npm:1.15.41" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@swc/core-linux-x64-musl@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-linux-x64-musl@npm:1.15.41" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@swc/core-win32-arm64-msvc@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-win32-arm64-msvc@npm:1.15.41" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@swc/core-win32-ia32-msvc@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-win32-ia32-msvc@npm:1.15.41" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@swc/core-win32-x64-msvc@npm:1.15.41": + version: 1.15.41 + resolution: "@swc/core-win32-x64-msvc@npm:1.15.41" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/core@npm:^1.15.2": + version: 1.15.41 + resolution: "@swc/core@npm:1.15.41" + dependencies: + "@swc/core-darwin-arm64": "npm:1.15.41" + "@swc/core-darwin-x64": "npm:1.15.41" + "@swc/core-linux-arm-gnueabihf": "npm:1.15.41" + "@swc/core-linux-arm64-gnu": "npm:1.15.41" + "@swc/core-linux-arm64-musl": "npm:1.15.41" + "@swc/core-linux-ppc64-gnu": "npm:1.15.41" + "@swc/core-linux-s390x-gnu": "npm:1.15.41" + "@swc/core-linux-x64-gnu": "npm:1.15.41" + "@swc/core-linux-x64-musl": "npm:1.15.41" + "@swc/core-win32-arm64-msvc": "npm:1.15.41" + "@swc/core-win32-ia32-msvc": "npm:1.15.41" + "@swc/core-win32-x64-msvc": "npm:1.15.41" + "@swc/counter": "npm:^0.1.3" + "@swc/types": "npm:^0.1.26" + peerDependencies: + "@swc/helpers": ">=0.5.17" + dependenciesMeta: + "@swc/core-darwin-arm64": + optional: true + "@swc/core-darwin-x64": + optional: true + "@swc/core-linux-arm-gnueabihf": + optional: true + "@swc/core-linux-arm64-gnu": + optional: true + "@swc/core-linux-arm64-musl": + optional: true + "@swc/core-linux-ppc64-gnu": + optional: true + "@swc/core-linux-s390x-gnu": + optional: true + "@swc/core-linux-x64-gnu": + optional: true + "@swc/core-linux-x64-musl": + optional: true + "@swc/core-win32-arm64-msvc": + optional: true + "@swc/core-win32-ia32-msvc": + optional: true + "@swc/core-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: 10/4d4b47885468bb6635a06040afa566e5568b55c7a43facceadca39ebd093a96e5d11701d8a92872321ae527f873701de10a9be2b3d96c1a7188e3e862c4072c4 + languageName: node + linkType: hard + +"@swc/counter@npm:^0.1.3": + version: 0.1.3 + resolution: "@swc/counter@npm:0.1.3" + checksum: 10/df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598 + languageName: node + linkType: hard + "@swc/helpers@npm:0.5.15": version: 0.5.15 resolution: "@swc/helpers@npm:0.5.15" @@ -4081,6 +4430,179 @@ __metadata: languageName: node linkType: hard +"@swc/types@npm:^0.1.26": + version: 0.1.26 + resolution: "@swc/types@npm:0.1.26" + dependencies: + "@swc/counter": "npm:^0.1.3" + checksum: 10/07de03b9da3928cdf69bda70bf2c809dd86f16ef23e357759e577bbd975529cb20218c2e54e72b00585abae2b5e04e39b8947cea7a6f4de2d40a7633be441919 + languageName: node + linkType: hard + +"@tailwindcss/node@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/node@npm:4.3.1" + dependencies: + "@jridgewell/remapping": "npm:^2.3.5" + enhanced-resolve: "npm:5.21.6" + jiti: "npm:^2.7.0" + lightningcss: "npm:1.32.0" + magic-string: "npm:^0.30.21" + source-map-js: "npm:^1.2.1" + tailwindcss: "npm:4.3.1" + checksum: 10/582a718fec1429249ff4ae0fdc005720ef70e5f60676e3eab145d5291a50805262f95b086458dc3589762bd8cd200f9d1dbb5d156c0e9b0270fe8ad7de0010b7 + languageName: node + linkType: hard + +"@tailwindcss/oxide-android-arm64@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-android-arm64@npm:4.3.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-arm64@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.3.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-x64@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-darwin-x64@npm:4.3.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-freebsd-x64@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.3.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-musl@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.3.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-gnu@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.3.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-musl@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.3.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-wasm32-wasi@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.3.1" + dependencies: + "@emnapi/core": "npm:^1.10.0" + "@emnapi/runtime": "npm:^1.10.0" + "@emnapi/wasi-threads": "npm:^1.2.1" + "@napi-rs/wasm-runtime": "npm:^1.1.4" + "@tybys/wasm-util": "npm:^0.10.2" + tslib: "npm:^2.8.1" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-x64-msvc@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.3.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide@npm:4.3.1": + version: 4.3.1 + resolution: "@tailwindcss/oxide@npm:4.3.1" + dependencies: + "@tailwindcss/oxide-android-arm64": "npm:4.3.1" + "@tailwindcss/oxide-darwin-arm64": "npm:4.3.1" + "@tailwindcss/oxide-darwin-x64": "npm:4.3.1" + "@tailwindcss/oxide-freebsd-x64": "npm:4.3.1" + "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.3.1" + "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.3.1" + "@tailwindcss/oxide-linux-arm64-musl": "npm:4.3.1" + "@tailwindcss/oxide-linux-x64-gnu": "npm:4.3.1" + "@tailwindcss/oxide-linux-x64-musl": "npm:4.3.1" + "@tailwindcss/oxide-wasm32-wasi": "npm:4.3.1" + "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.3.1" + "@tailwindcss/oxide-win32-x64-msvc": "npm:4.3.1" + dependenciesMeta: + "@tailwindcss/oxide-android-arm64": + optional: true + "@tailwindcss/oxide-darwin-arm64": + optional: true + "@tailwindcss/oxide-darwin-x64": + optional: true + "@tailwindcss/oxide-freebsd-x64": + optional: true + "@tailwindcss/oxide-linux-arm-gnueabihf": + optional: true + "@tailwindcss/oxide-linux-arm64-gnu": + optional: true + "@tailwindcss/oxide-linux-arm64-musl": + optional: true + "@tailwindcss/oxide-linux-x64-gnu": + optional: true + "@tailwindcss/oxide-linux-x64-musl": + optional: true + "@tailwindcss/oxide-wasm32-wasi": + optional: true + "@tailwindcss/oxide-win32-arm64-msvc": + optional: true + "@tailwindcss/oxide-win32-x64-msvc": + optional: true + checksum: 10/598a1f2d038fcb1c35281d043570cefc4c2dac9aedac50e31272c26c1b2cb714457b03835106959afe2761dc88ee46b0e719edb922783942ae493ad71959a012 + languageName: node + linkType: hard + +"@tailwindcss/postcss@npm:^4": + version: 4.3.1 + resolution: "@tailwindcss/postcss@npm:4.3.1" + dependencies: + "@alloc/quick-lru": "npm:^5.2.0" + "@tailwindcss/node": "npm:4.3.1" + "@tailwindcss/oxide": "npm:4.3.1" + postcss: "npm:8.5.15" + tailwindcss: "npm:4.3.1" + checksum: 10/e1922f337b1c4542f92d54e5b8f71fd6a73f9c02bbfd4361015e0046f0a6c09a354a721d2129af9d1286d96ff1d7bc2e6caabadbb37154816c2fdedba906d180 + languageName: node + linkType: hard + "@testing-library/dom@npm:^10.4.0": version: 10.4.1 resolution: "@testing-library/dom@npm:10.4.1" @@ -5399,7 +5921,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.3 resolution: "ansi-styles@npm:6.2.3" checksum: 10/c49dad7639f3e48859bd51824c93b9eb0db628afc243c51c3dd2410c4a15ede1a83881c6c7341aa2b159c4f90c11befb38f2ba848c07c66c9f9de4bcd7cb9f30 @@ -6450,6 +6972,36 @@ __metadata: languageName: node linkType: hard +"content-sdk-nextjs-app-router@workspace:samples/nextjs-app-router": + version: 0.0.0-use.local + resolution: "content-sdk-nextjs-app-router@workspace:samples/nextjs-app-router" + dependencies: + "@eslint/eslintrc": "npm:^3" + "@sitecore-content-sdk/analytics-core": "npm:^2.1.0-beta.1" + "@sitecore-content-sdk/cli": "npm:^2.1.0-beta.1" + "@sitecore-content-sdk/events": "npm:^2.1.0-beta.1" + "@sitecore-content-sdk/nextjs": "npm:^2.1.0-beta.1" + "@sitecore-content-sdk/personalize": "npm:^2.1.0-beta.1" + "@sitecore-feaas/clientside": "npm:^0.6.0" + "@sitecore/components": "npm:~2.1.0" + "@tailwindcss/postcss": "npm:^4" + "@types/node": "npm:^24.10.4" + "@types/react": "npm:^19.2.7" + "@types/react-dom": "npm:^19.2.3" + cross-env: "npm:^10.0.0" + eslint: "npm:^9.33.0" + eslint-config-next: "npm:16.2.2" + next: "npm:^16.2.0" + next-intl: "npm:^4.3.5" + npm-run-all2: "npm:^8.0.4" + react: "npm:^19.2.1" + react-dom: "npm:^19.2.1" + tailwindcss: "npm:^4" + typescript: "npm:~5.8.3" + zod: "npm:^4.3.6" + languageName: unknown + linkType: soft + "conventional-changelog-angular@npm:7.0.0": version: 7.0.0 resolution: "conventional-changelog-angular@npm:7.0.0" @@ -6657,6 +7209,19 @@ __metadata: languageName: node linkType: hard +"cross-env@npm:^10.0.0": + version: 10.1.0 + resolution: "cross-env@npm:10.1.0" + dependencies: + "@epic-web/invariant": "npm:^1.0.0" + cross-spawn: "npm:^7.0.6" + bin: + cross-env: dist/bin/cross-env.js + cross-env-shell: dist/bin/cross-env-shell.js + checksum: 10/0e5d8bdefbbcd000460b69755e0eeb22953510abac8375e4f8b638ff7c45406141acfd57b8a4c1d1cf0b5ea42f33451b302062fb9b34408753b4d465e901b845 + languageName: node + linkType: hard + "cross-fetch@npm:^3.1.5": version: 3.2.0 resolution: "cross-fetch@npm:3.2.0" @@ -7033,7 +7598,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.1.2": +"detect-libc@npm:^2.0.3, detect-libc@npm:^2.1.2": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766 @@ -7225,6 +7790,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:5.21.6": + version: 5.21.6 + resolution: "enhanced-resolve@npm:5.21.6" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.3.3" + checksum: 10/7599496622b1718727409bc1944e19ad72492add909a3f9d905bbc9d81bade42b8bece156bfbe30ee8d1bbc8b24abc9d8bf194f4d46173a6adc919e95766753a + languageName: node + linkType: hard + "enquirer@npm:2.3.6, enquirer@npm:~2.3.6": version: 2.3.6 resolution: "enquirer@npm:2.3.6" @@ -7960,7 +8535,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.32.0, eslint@npm:^9.39.1": +"eslint@npm:^9.32.0, eslint@npm:^9.33.0, eslint@npm:^9.39.1": version: 9.39.4 resolution: "eslint@npm:9.39.4" dependencies: @@ -8895,7 +9470,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 @@ -9202,6 +9777,15 @@ __metadata: languageName: node linkType: hard +"icu-minify@npm:^4.13.0": + version: 4.13.0 + resolution: "icu-minify@npm:4.13.0" + dependencies: + "@formatjs/icu-messageformat-parser": "npm:^3.4.0" + checksum: 10/3472867f81f0686185ef88e99633d08b465e3b4bf825d821225c325bc06d999cd843fcdea7188686182ad191c6f8b743238c57fb33ef678455950b805f87ae5c + languageName: node + linkType: hard + "ieee754@npm:1.2.1, ieee754@npm:^1.1.13": version: 1.2.1 resolution: "ieee754@npm:1.2.1" @@ -9414,6 +9998,16 @@ __metadata: languageName: node linkType: hard +"intl-messageformat@npm:^11.1.0": + version: 11.2.8 + resolution: "intl-messageformat@npm:11.2.8" + dependencies: + "@formatjs/fast-memoize": "npm:3.1.6" + "@formatjs/icu-messageformat-parser": "npm:3.5.11" + checksum: 10/451585274cdb1ff798b8d9bfa9aed6623870d2a3bf7c315a4cc37e57793d13a51ec09da705a228f7a64a1267fd6afe91b5bf2fd0c4223fd4f1d158a2adb82d41 + languageName: node + linkType: hard + "ip-address@npm:^10.1.1": version: 10.2.0 resolution: "ip-address@npm:10.2.0" @@ -10624,6 +11218,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^2.7.0": + version: 2.7.0 + resolution: "jiti@npm:2.7.0" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10/6d75a8dbd61dbee031aa0937fabb748ff8ddf370b971958cc704f5cf26b4c5bdc9dcd0563059b2627a2bd41d946fa0bc64f912fdc8981ca7945a9d63c74ad0f9 + languageName: node + linkType: hard + "jju@npm:~1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -11101,6 +11704,126 @@ __metadata: languageName: node linkType: hard +"lightningcss-android-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-android-arm64@npm:1.32.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-arm64@npm:1.32.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-darwin-x64@npm:1.32.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-freebsd-x64@npm:1.32.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-linux-x64-musl@npm:1.32.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:1.32.0": + version: 1.32.0 + resolution: "lightningcss@npm:1.32.0" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-android-arm64: "npm:1.32.0" + lightningcss-darwin-arm64: "npm:1.32.0" + lightningcss-darwin-x64: "npm:1.32.0" + lightningcss-freebsd-x64: "npm:1.32.0" + lightningcss-linux-arm-gnueabihf: "npm:1.32.0" + lightningcss-linux-arm64-gnu: "npm:1.32.0" + lightningcss-linux-arm64-musl: "npm:1.32.0" + lightningcss-linux-x64-gnu: "npm:1.32.0" + lightningcss-linux-x64-musl: "npm:1.32.0" + lightningcss-win32-arm64-msvc: "npm:1.32.0" + lightningcss-win32-x64-msvc: "npm:1.32.0" + dependenciesMeta: + lightningcss-android-arm64: + optional: true + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10/098e61007f0d0ec8b5c50884e33b543b551d1ff21bc7b062434b6638fd0b8596858f823b60dfc2a4aa756f3cb120ad79f2b7f4a55b1bda2c0269ab8cf476f114 + languageName: node + linkType: hard + "lines-and-columns@npm:2.0.3": version: 2.0.3 resolution: "lines-and-columns@npm:2.0.3" @@ -11310,7 +12033,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.17": +"magic-string@npm:^0.30.17, magic-string@npm:^0.30.21": version: 0.30.21 resolution: "magic-string@npm:0.30.21" dependencies: @@ -11443,6 +12166,13 @@ __metadata: languageName: node linkType: hard +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: 10/2e34a1e35e6eb2e342f788f75f96c16f115b81ff6dd39e6c2f48c78b464dbf5b1a4c6ebfae4c573bd0f8dbe8c57d72bb357c60523be184655260d25855c03902 + languageName: node + linkType: hard + "meow@npm:^13.2.0": version: 13.2.0 resolution: "meow@npm:13.2.0" @@ -11833,6 +12563,35 @@ __metadata: languageName: node linkType: hard +"next-intl-swc-plugin-extractor@npm:^4.13.0": + version: 4.13.0 + resolution: "next-intl-swc-plugin-extractor@npm:4.13.0" + checksum: 10/b2f11364c4b9a8d8a82a04f97c742b294f6f3226ac85f8a7d549fd9a4e9520232575df188d017317ba2647170434d6001678e1efbb1011d11792c7f889289be5 + languageName: node + linkType: hard + +"next-intl@npm:^4.3.5": + version: 4.13.0 + resolution: "next-intl@npm:4.13.0" + dependencies: + "@formatjs/intl-localematcher": "npm:^0.8.1" + "@parcel/watcher": "npm:^2.4.1" + "@swc/core": "npm:^1.15.2" + icu-minify: "npm:^4.13.0" + negotiator: "npm:^1.0.0" + next-intl-swc-plugin-extractor: "npm:^4.13.0" + po-parser: "npm:^2.1.1" + use-intl: "npm:^4.13.0" + peerDependencies: + next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/b0b7c53e63133068c9241cc8d54ac38a1a0b2d5599d9ade2ff0ddd5079ccbf5db5939bc185777ab2a7fe3649b849a1870e53967b4d8873f06231f989309323fc + languageName: node + linkType: hard + "next@npm:^16.2.0": version: 16.2.9 resolution: "next@npm:16.2.9" @@ -11914,6 +12673,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^7.0.0": + version: 7.1.1 + resolution: "node-addon-api@npm:7.1.1" + dependencies: + node-gyp: "npm:latest" + checksum: 10/ee1e1ed6284a2f8cd1d59ac6175ecbabf8978dcf570345e9a8095a9d0a2b9ced591074ae77f9009287b00c402352b38aa9322a34f2199cdc9f567b842a636b94 + languageName: node + linkType: hard + "node-exports-info@npm:^1.6.0": version: 1.6.0 resolution: "node-exports-info@npm:1.6.0" @@ -12229,6 +12997,27 @@ __metadata: languageName: node linkType: hard +"npm-run-all2@npm:^8.0.4": + version: 8.0.4 + resolution: "npm-run-all2@npm:8.0.4" + dependencies: + ansi-styles: "npm:^6.2.1" + cross-spawn: "npm:^7.0.6" + memorystream: "npm:^0.3.1" + picomatch: "npm:^4.0.2" + pidtree: "npm:^0.6.0" + read-package-json-fast: "npm:^4.0.0" + shell-quote: "npm:^1.7.3" + which: "npm:^5.0.0" + bin: + npm-run-all: bin/npm-run-all/index.js + npm-run-all2: bin/npm-run-all/index.js + run-p: bin/run-p/index.js + run-s: bin/run-s/index.js + checksum: 10/6e485f0d71fdfcf5bd872e5bbb8440ea5816ebeed88e6e39ff6e758d76adefa516b3101ed7ddc8de117d61b499f176417c87b0c595394e15733738834ad6ef0a + languageName: node + linkType: hard + "npm-run-path@npm:4.0.1, npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -13084,6 +13873,15 @@ __metadata: languageName: node linkType: hard +"pidtree@npm:^0.6.0": + version: 0.6.1 + resolution: "pidtree@npm:0.6.1" + bin: + pidtree: bin/pidtree.js + checksum: 10/2e543dac94bea903c29c57ec113244bb66e878245c8fa28b074e517c3947426dacee97a18e0dfdbcbde00909472d65a7fca9eaf722269b279f593eb582dc03d1 + languageName: node + linkType: hard + "pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" @@ -13121,6 +13919,13 @@ __metadata: languageName: node linkType: hard +"po-parser@npm:^2.1.1": + version: 2.1.1 + resolution: "po-parser@npm:2.1.1" + checksum: 10/7bcf0055f4441256fd1cd2c09c4e2512207385f6a1a8952d9f7911ffccd0366720cfbd2be7aa11fb337f4b72c8a53866b8ba5a42da65f6ec19401df68bac8997 + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0, possible-typed-array-names@npm:^1.1.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" @@ -13149,7 +13954,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.5.6": +"postcss@npm:8.5.15, postcss@npm:^8.5.6": version: 8.5.15 resolution: "postcss@npm:8.5.15" dependencies: @@ -13483,6 +14288,16 @@ __metadata: languageName: node linkType: hard +"read-package-json-fast@npm:^4.0.0": + version: 4.0.0 + resolution: "read-package-json-fast@npm:4.0.0" + dependencies: + json-parse-even-better-errors: "npm:^4.0.0" + npm-normalize-package-bin: "npm:^4.0.0" + checksum: 10/bf0becd7d0b652dcc5874b466d1dbd98313180e89505c072f35ff48a1ad6bdaf2427143301e1924d64e4af5064cda8be5df16f14de882f03130e29051bbaab87 + languageName: node + linkType: hard + "read-pkg-up@npm:^3.0.0": version: 3.0.0 resolution: "read-pkg-up@npm:3.0.0" @@ -14250,6 +15065,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.7.3": + version: 1.8.4 + resolution: "shell-quote@npm:1.8.4" + checksum: 10/a3e3796385f2cd5cf0b78207a4439f0c7395c0833fc75b2473084b5d298c109c5c0fa687fcd1c04e4b4484866e5bb8eaae7efae443b80fff71ea7e29baf11f0c + languageName: node + linkType: hard + "side-channel-list@npm:^1.0.1": version: 1.0.1 resolution: "side-channel-list@npm:1.0.1" @@ -14876,6 +15698,20 @@ __metadata: languageName: node linkType: hard +"tailwindcss@npm:4.3.1, tailwindcss@npm:^4": + version: 4.3.1 + resolution: "tailwindcss@npm:4.3.1" + checksum: 10/6e117b80d4b30ddc85cb91ecb2f0cdca825523186ffe09c92cd3dfba363f6b9eb71d4ce617e6d89c06f2cf93fc1ba12a0a61abc3068d37b376fdc9bc5be7c19e + languageName: node + linkType: hard + +"tapable@npm:^2.3.3": + version: 2.3.3 + resolution: "tapable@npm:2.3.3" + checksum: 10/21fb64a7ae1a0e11d855a6c33a22ae5ecf7e2f23170c942da673b44bf4c3aae8aa52451ef2792d0ce36c7feca13dceafa4f135105d66fc06912632488c0913fd + languageName: node + linkType: hard + "tar-stream@npm:2.2.0": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" @@ -15697,6 +16533,20 @@ __metadata: languageName: node linkType: hard +"use-intl@npm:^4.13.0": + version: 4.13.0 + resolution: "use-intl@npm:4.13.0" + dependencies: + "@formatjs/fast-memoize": "npm:^3.1.0" + "@schummar/icu-type-parser": "npm:1.21.5" + icu-minify: "npm:^4.13.0" + intl-messageformat: "npm:^11.1.0" + peerDependencies: + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + checksum: 10/60bd67a6619294bd233bf376ef1e6ba508b3ac25bac72c469cb3c116be13b2eaa0e68195117a5adcf9b83bcc1c502ff02a15db7d7a1003c711a499302f994605 + languageName: node + linkType: hard + "username-sync@npm:^1.0.2": version: 1.0.3 resolution: "username-sync@npm:1.0.3" From 6851907822e3cefbc719bfc25aa8e443705618b5 Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:48:27 +0300 Subject: [PATCH 22/32] Render atoms based on layout data (#517) --- .../StudioComponentServerWrapper.tsx | 3 ++ .../Placeholder/placeholder-utils.test.tsx | 43 ++++++++++++++++--- .../Placeholder/placeholder-utils.tsx | 15 ++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx index 1593235b7b..cfd4d76e3a 100644 --- a/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx +++ b/packages/react/src/components/DesignLibrary/StudioComponentServerWrapper.tsx @@ -14,6 +14,9 @@ export type StudioComponentServerWrapperProps = { * Pipe separated relative paths to the Studio component layout JSON in MMS with the last segment as the variant name. The path matching `FieldNames` will be used, or `default` if no match. */ componentRef: string; + /** + * Field name to match against the last segment of the `componentRef` paths. If no match is found, the `default` path will be used. + */ fieldNames?: string; }; diff --git a/packages/react/src/components/Placeholder/placeholder-utils.test.tsx b/packages/react/src/components/Placeholder/placeholder-utils.test.tsx index 935484453e..9e242c41a5 100644 --- a/packages/react/src/components/Placeholder/placeholder-utils.test.tsx +++ b/packages/react/src/components/Placeholder/placeholder-utils.test.tsx @@ -183,7 +183,7 @@ describe('placeholder-utils', () => { }); it('should extract styles from DetailedRenderingParams object', () => { - const rendering = ({ + const rendering = { componentName: 'TestComponent', uid: 'test-uid', params: { @@ -191,7 +191,7 @@ describe('placeholder-utils', () => { Value: { value: 'White-Background' }, }, }, - } as unknown) as ComponentRendering; + } as unknown as ComponentRendering; const result = getSXAParams(rendering); @@ -201,14 +201,14 @@ describe('placeholder-utils', () => { }); it('should combine object GridParameters and Styles params', () => { - const rendering = ({ + const rendering = { componentName: 'TestComponent', uid: 'test-uid', params: { GridParameters: { Value: { value: 'col-lg-6' } }, Styles: { Value: { value: 'White-Background' } }, }, - } as unknown) as ComponentRendering; + } as unknown as ComponentRendering; const result = getSXAParams(rendering); @@ -216,7 +216,6 @@ describe('placeholder-utils', () => { styles: 'col-lg-6 White-Background', }); }); - }); describe('getChildComponentProps', () => { @@ -384,6 +383,40 @@ describe('placeholder-utils', () => { expect(consoleWarnStub.calledOnce).to.be.true; }); + it('should return StudioComponentServerWrapper when ComponentRef is in params', () => { + const rendering: ComponentRendering = { + componentName: 'Sample', + uid: 'test-uid', + params: { + ComponentRef: 'api/media/v2/delivery/abc/component/def/default', + fieldNames: 'default', + }, + }; + + const result = getComponentForRendering(rendering, 'test-placeholder', componentMap); + + expect(result?.component).to.be.a('function'); + expect(result?.isEmpty).to.be.false; + expect(result?.componentType).to.equal('server'); + }); + + it('should return StudioComponentServerWrapper when ComponentRef is in params without fieldNames', () => { + const rendering: ComponentRendering = { + componentName: 'Sample', + uid: 'test-uid', + params: { + ComponentRef: + 'api/media/v2/delivery/abc/component/def/default|api/media/v2/delivery/abc/component/def/sample', + }, + }; + + const result = getComponentForRendering(rendering, 'test-placeholder', componentMap); + + expect(result?.component).to.be.a('function'); + expect(result?.isEmpty).to.be.false; + expect(result?.componentType).to.equal('server'); + }); + it('should return null when componentMap is not provided', () => { const rendering: ComponentRendering = { componentName: 'TestComponent', diff --git a/packages/react/src/components/Placeholder/placeholder-utils.tsx b/packages/react/src/components/Placeholder/placeholder-utils.tsx index ae3bcb8310..b352827b18 100644 --- a/packages/react/src/components/Placeholder/placeholder-utils.tsx +++ b/packages/react/src/components/Placeholder/placeholder-utils.tsx @@ -20,6 +20,7 @@ import { FEAAS_COMPONENT_RENDERING_NAME, FEAAS_WRAPPER_RENDERING_NAME, } from '../FEaaS'; +import { StudioComponentServerWrapper } from '../DesignLibrary/StudioComponentServerWrapper'; import { ChildComponentProps, PlaceholderProps, ComponentForRendering } from './models'; /** @@ -169,6 +170,19 @@ export const getComponentForRendering = ( }; } + if (renderingDefinition.params?.ComponentRef) { + return { + component: (props: ChildComponentProps) => ( + + ), + isEmpty: false, + componentType: 'server', + }; + } + let component = null; if (!componentMap || componentMap.size === 0) { console.warn( @@ -251,4 +265,3 @@ export const getComponentForRendering = ( isEmpty: false, }; }; - From 32cc36c1f23084a56385d7d2f8025f72bbdefbc6 Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 12:57:28 +0300 Subject: [PATCH 23/32] chore: config updates --- packages/cli/tsconfig.json | 1 - packages/content/tsconfig.json | 1 - packages/nextjs/tsconfig.json | 1 - packages/react/tsconfig.json | 1 - 4 files changed, 4 deletions(-) diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 34752f9fa1..72141fe07a 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./src", "target": "es6", "module": "commonjs", "forceConsistentCasingInFileNames": true, diff --git a/packages/content/tsconfig.json b/packages/content/tsconfig.json index 51cf31ee1c..7c8692c69d 100644 --- a/packages/content/tsconfig.json +++ b/packages/content/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./src", "target": "es2017", "skipLibCheck": true, "useUnknownInCatchVariables": false, diff --git a/packages/nextjs/tsconfig.json b/packages/nextjs/tsconfig.json index d8a54eae81..a7b03b78c9 100644 --- a/packages/nextjs/tsconfig.json +++ b/packages/nextjs/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./src", "target": "ES2017", "module": "CommonJS", "jsx": "react", diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index 10d4242ac0..f82d428bfe 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./src", "target": "ES2017", "module": "commonjs", "jsx": "react", From 0384eacdaa7a276efc3988ca431c2424f4d7db2c Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 13:24:51 +0300 Subject: [PATCH 24/32] chore: cli command alias --- packages/cli/src/scripts/project/atoms/update.ts | 3 ++- packages/cli/src/scripts/project/atoms/validate.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/scripts/project/atoms/update.ts b/packages/cli/src/scripts/project/atoms/update.ts index a2e5c97caa..4583e4a2fa 100644 --- a/packages/cli/src/scripts/project/atoms/update.ts +++ b/packages/cli/src/scripts/project/atoms/update.ts @@ -1,7 +1,7 @@ import { AtomLockEntry, AtomVersionsLock } from './types'; import { loadCatalog, loadCurrentAtoms, writeLockFile } from './utils'; -export const command = 'update'; +export const command = ['update', 'u']; export const describe = 'Regenerate the atom versions lock file from the current atom definitions. Run after intentional schema changes.'; @@ -11,6 +11,7 @@ export const builder = { requiresArg: false, type: 'string', describe: 'Path to the `sitecore.cli.config` file.', + alias: 'c', }, }; diff --git a/packages/cli/src/scripts/project/atoms/validate.ts b/packages/cli/src/scripts/project/atoms/validate.ts index 8c1760e0b3..1254a5a5cd 100644 --- a/packages/cli/src/scripts/project/atoms/validate.ts +++ b/packages/cli/src/scripts/project/atoms/validate.ts @@ -2,7 +2,7 @@ import loadCliConfig from '../../../utils/load-config'; import { ValidateResult } from './types'; import { loadCatalog, loadCurrentAtoms, readLockFile } from './utils'; -export const command = 'validate'; +export const command = ['validate', 'v']; export const describe = 'Validate that the current atom implementations match the lock file. Fails if any atom has changed without a version bump.'; @@ -12,6 +12,7 @@ export const builder = { requiresArg: false, type: 'string', describe: 'Path to the `sitecore.cli.config` file.', + alias: 'c', }, }; From b002a0656889bf9eb4f98e102b2ee482106288d9 Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 13:30:20 +0300 Subject: [PATCH 25/32] chore: cli types update --- packages/cli/src/scripts/project/atoms/types.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/scripts/project/atoms/types.ts b/packages/cli/src/scripts/project/atoms/types.ts index 872a1b547e..d7ec96b188 100644 --- a/packages/cli/src/scripts/project/atoms/types.ts +++ b/packages/cli/src/scripts/project/atoms/types.ts @@ -5,6 +5,7 @@ export interface AtomLockEntry { /** Semver version of this component. Absent when not declared on the component. */ version?: string; + /** Hash of the component's schema, used to detect changes in the schema. */ hash: string; } @@ -15,7 +16,9 @@ export interface AtomLockEntry { export interface AtomVersionsLock { /** Catalog root version from `defineAtomsCatalog`. Absent when not declared. */ version?: string; + /** Timestamp of when the lock file was generated. */ generated: string; + /** Map of atom name to its lock entry. */ atoms: Record; } @@ -24,7 +27,9 @@ export interface AtomVersionsLock { * @internal */ export interface AtomInfo { + /** Semver version of this component. Absent when not declared on the component. */ version: string; + /** Hash of the component's schema, used to detect changes in the schema. */ schemaHash: string; } @@ -49,7 +54,8 @@ export interface CatalogLoadResult { * @internal */ export interface ValidateResult { + /** Whether the lock file is valid (all hashes and versions match). */ valid: boolean; + /** List of issues found during validation, if any. */ issues: string[]; } - From 89e8c52e49ab23da31a062bf63b7d53d51c76b1b Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 13:53:06 +0300 Subject: [PATCH 26/32] chore: error messages --- .../cli/src/scripts/project/atoms/utils.ts | 15 +++------- .../scripts/project/atoms/validate.test.ts | 10 ++----- .../cli/src/scripts/project/atoms/validate.ts | 28 ++++++------------- packages/core/src/constants.ts | 17 +++++++++++ 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/packages/cli/src/scripts/project/atoms/utils.ts b/packages/cli/src/scripts/project/atoms/utils.ts index 9fac8c0c38..2a98bfe872 100644 --- a/packages/cli/src/scripts/project/atoms/utils.ts +++ b/packages/cli/src/scripts/project/atoms/utils.ts @@ -1,6 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; +import { constants } from '@sitecore-content-sdk/core'; import { ensureSitecoreDirectory } from '../../../utils/ensure-sitecore-directory'; import { AtomsInfoMap, AtomVersionsLock, CatalogLoadResult } from './types'; import { ATOMS_MODULE_PATH, LOCK_FILE_DIR, LOCK_FILE_NAME } from './constants'; @@ -73,19 +74,14 @@ export function resolveAtomsModulePath(): string | null { export function loadCatalog(): CatalogLoadResult { const modulePath = resolveAtomsModulePath(); if (!modulePath) { - throw new Error( - `Atoms module not found at ${ATOMS_MODULE_PATH}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx}` - ); + throw new Error(constants.ERROR_MESSAGES.MV_010(ATOMS_MODULE_PATH)); } const tsx = require('tsx/cjs/api'); const atomsModule = tsx.require(modulePath, __filename); const catalog = atomsModule.catalog ?? atomsModule.default?.catalog; - if (!catalog) - throw new Error( - `Atoms module at ${modulePath} does not export "catalog". Export the result of defineAtomsCatalog as "catalog".` - ); + if (!catalog) throw new Error(constants.ERROR_MESSAGES.MV_011(modulePath)); return catalog; } @@ -99,10 +95,7 @@ export function loadCatalog(): CatalogLoadResult { */ export async function loadCurrentAtoms(catalog?: CatalogLoadResult): Promise { const modulePath = resolveAtomsModulePath(); - if (!modulePath) - throw new Error( - `Atoms module not found at ${ATOMS_MODULE_PATH}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx} and export a catalog.` - ); + if (!modulePath) throw new Error(constants.ERROR_MESSAGES.MV_010(ATOMS_MODULE_PATH)); const resolvedCatalog = catalog ?? loadCatalog(); const result: AtomsInfoMap = {}; diff --git a/packages/cli/src/scripts/project/atoms/validate.test.ts b/packages/cli/src/scripts/project/atoms/validate.test.ts index 2af578da47..3a6111a5a8 100644 --- a/packages/cli/src/scripts/project/atoms/validate.test.ts +++ b/packages/cli/src/scripts/project/atoms/validate.test.ts @@ -1,6 +1,7 @@ /* eslint-disable no-unused-expressions, @typescript-eslint/no-unused-expressions */ import { expect } from 'chai'; import sinon from 'sinon'; +import { constants } from '@sitecore-content-sdk/core'; import * as utilsModule from './utils'; import * as loadConfigModule from '../../../utils/load-config'; import { handler } from './validate'; @@ -69,8 +70,7 @@ describe('atoms/validate handler', () => { await handler({}); - expect(consoleErrorStub.calledWith('[atoms validate] Lock file validation failed:')).to.be - .true; + expect(consoleErrorStub.calledWith(constants.ERROR_MESSAGES.IE_008)).to.be.true; const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); expect(errorArgs.some((msg) => msg.includes('Lock file not found'))).to.be.true; }); @@ -311,11 +311,7 @@ describe('atoms/validate handler', () => { } // Both the header and individual issues should be logged before the throw - expect(consoleErrorStub.calledWith('[atoms validate] Lock file validation failed:')).to.be - .true; - const errorArgs = consoleErrorStub.getCalls().map((c) => String(c.args[0])); - expect(errorArgs.length).to.be.greaterThan(1); + expect(consoleErrorStub.calledWith(constants.ERROR_MESSAGES.IE_008)).to.be.true; }); }); }); - diff --git a/packages/cli/src/scripts/project/atoms/validate.ts b/packages/cli/src/scripts/project/atoms/validate.ts index 1254a5a5cd..18dee218dd 100644 --- a/packages/cli/src/scripts/project/atoms/validate.ts +++ b/packages/cli/src/scripts/project/atoms/validate.ts @@ -1,3 +1,4 @@ +import { constants } from '@sitecore-content-sdk/core'; import loadCliConfig from '../../../utils/load-config'; import { ValidateResult } from './types'; import { loadCatalog, loadCurrentAtoms, readLockFile } from './utils'; @@ -32,15 +33,12 @@ export async function handler(argv: ValidateArgs) { const result = await validateLockFile(); if (!result.valid) { - console.error('[atoms validate] Lock file validation failed:'); + console.error(constants.ERROR_MESSAGES.IE_008); for (const issue of result.issues) { console.error(` - ${issue}`); } - if (breakOnError) - throw new Error( - 'Atom validation failed. See issues above. You see this error because `breakOnError` is enabled in your CLI config.' - ); + if (breakOnError) throw new Error(constants.ERROR_MESSAGES.IE_009); } else console.log('[atoms validate] atoms.lock.json is up to date.'); } @@ -54,7 +52,7 @@ async function validateLockFile(): Promise { if (!lock) return { valid: false, - issues: ['Lock file not found. Run `sitecore-tools project atoms update` to generate it.'], + issues: [constants.ERROR_MESSAGES.MV_012], }; const currentAtoms = await loadCurrentAtoms(); @@ -67,16 +65,14 @@ async function validateLockFile(): Promise { if (catalogVersion !== lock.version) { const lockSide = lock.version !== undefined ? `"${lock.version}"` : 'not set'; const currentSide = catalogVersion !== undefined ? `"${catalogVersion}"` : 'not set'; - issues.push( - `Catalog version mismatch: lock file has ${lockSide}, current is ${currentSide}.` - ); + issues.push(constants.ERROR_MESSAGES.IV_008(lockSide, currentSide)); } } // Check for atoms in lock but missing from current definitions for (const [name, entry] of Object.entries(lock.atoms)) { if (!currentAtoms[name]) { - issues.push(`Atom "${name}" is in the lock file but not found in current definitions.`); + issues.push(constants.ERROR_MESSAGES.MV_013(name)); continue; } @@ -88,24 +84,18 @@ async function validateLockFile(): Promise { if (entry.version !== currentVersion) { const lockSide = entry.version !== undefined ? `"${entry.version}"` : 'not set'; const currentSide = currentVersion !== undefined ? `"${currentVersion}"` : 'not set'; - issues.push( - `Atom "${name}" version mismatch: lock file has ${lockSide}, current is ${currentSide}.` - ); + issues.push(constants.ERROR_MESSAGES.IV_009(name, lockSide, currentSide)); } } if (current.schemaHash !== entry.hash) { - issues.push(`Atom "${name}" schema has changed.`); + issues.push(constants.ERROR_MESSAGES.IV_010(name)); } } // Check for new atoms not in lock file for (const name of Object.keys(currentAtoms)) { - if (!lock.atoms[name]) - issues.push( - `Atom "${name}" is new and not in the lock file. ` + - `Run \`sitecore-tools project atoms update\` to add it.` - ); + if (!lock.atoms[name]) issues.push(constants.ERROR_MESSAGES.MV_014(name)); } return { diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index fab9569dc0..d72b455d7a 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -51,6 +51,11 @@ export const ERROR_MESSAGES = { `[IV-006] "extensionData" supports maximum ${maxAttributes} attributes. Reduce the number of attributes.`, IV_007: (siteName: string) => `[IV-007] Site "${siteName}" does not exist or site item tree is missing.`, + IV_008: (lockSide: string, currentSide: string) => + `[IV-008] Catalog version mismatch: lock file has ${lockSide}, current is ${currentSide}.`, + IV_009: (name: string, lockSide: string, currentSide: string) => + `[IV-009] Atom "${name}" version mismatch: lock file has ${lockSide}, current is ${currentSide}.`, + IV_010: (name: string) => `[IV-010] Atom "${name}" schema has changed.`, /** IE errors are related to incorrect execution */ IE_001: (pluginName: string, dependency: string) => @@ -65,6 +70,9 @@ export const ERROR_MESSAGES = { IE_006: '[IE-006] Unable to set the "sc_cid_personalize" cookie because the visitor ID could not be retrieved from the server. Make sure to set the correct values for "contextId" and "siteName". If the issue persists, try again later or use try-catch blocks to handle this error.', IE_007: (hostName: string) => `[IE-007] Could not resolve site for host "${hostName}".`, + IE_008: '[IE-008] Lock file validation failed:', + IE_009: + '[IE-009] Atom validation failed. See issues above. You see this error because `breakOnError` is enabled in your CLI config.', /** MV errors are related to missing values */ MV_001: '[MV-001] "contextId" is required.', @@ -77,6 +85,15 @@ export const ERROR_MESSAGES = { MV_007: '[MV-007] Provide either "contextId" or both "apiHost" and "apiKey".', MV_008: '[MV-008] Verify that sitecore.config is properly imported and correctly referenced.', MV_009: '[MV-009] "language" is required.', + MV_010: (modulePath: string) => + `[MV-010] Atoms module not found at ${modulePath}.{ts,tsx}. Ensure your atoms are defined in src/atoms/index.{ts,tsx} and export a catalog.`, + MV_011: (modulePath: string) => + `[MV-011] Atoms module at ${modulePath} does not export "catalog". Export the result of defineAtomsCatalog as "catalog".`, + MV_012: '[MV-012] Lock file not found. Run `sitecore-tools project atoms update` to generate it.', + MV_013: (name: string) => + `[MV-013] Atom "${name}" is in the lock file but not found in current definitions.`, + MV_014: (name: string) => + `[MV-014] Atom "${name}" is new and not in the lock file. Run \`sitecore-tools project atoms update\` to add it.`, /** Generic follow-up when the user should contact support */ CONTACT_SUPPORT: 'If the issue persists, please contact Sitecore Support.', From e92c7700facdb13f4f09fa591c159f0cbc8ae470 Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 18:34:57 +0300 Subject: [PATCH 27/32] chore: remove not needed exclude entry --- packages/nextjs/src/config-cli/define-cli-config.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/nextjs/src/config-cli/define-cli-config.ts b/packages/nextjs/src/config-cli/define-cli-config.ts index 6f8e05db82..ade2bfefeb 100644 --- a/packages/nextjs/src/config-cli/define-cli-config.ts +++ b/packages/nextjs/src/config-cli/define-cli-config.ts @@ -33,12 +33,6 @@ function addDefaultScaffoldTemplates(cliConfig: SitecoreCliConfigInput) { cliConfig.scaffold.templates.unshift(defaultTemplate, byocTemplate); } -/** - * Default exclude patterns for the component map generator. - * Atoms components are rendered via the json-render registry, not the Sitecore component map. - */ -const DEFAULT_COMPONENT_MAP_EXCLUDE = ['**/atoms/**']; - /** * Add the framework-specific implementaion of the component map generator to the CLI configuration. * @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object @@ -48,6 +42,6 @@ function addDefaultComponentMapGenerator(cliConfig: SitecoreCliConfigInput) { generator: generateMap, paths: ['src/components'], ...cliConfig.componentMap, - exclude: [...DEFAULT_COMPONENT_MAP_EXCLUDE, ...(cliConfig.componentMap?.exclude || [])], + exclude: [...(cliConfig.componentMap?.exclude || [])], }; } From 45429b2e990d83f4745e3800e04389ed04836efa Mon Sep 17 00:00:00 2001 From: sc-nikolaoslazaridis Date: Tue, 16 Jun 2026 18:36:30 +0300 Subject: [PATCH 28/32] chore: typo --- packages/nextjs/src/config-cli/define-cli-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/src/config-cli/define-cli-config.ts b/packages/nextjs/src/config-cli/define-cli-config.ts index ade2bfefeb..6cb96a51d1 100644 --- a/packages/nextjs/src/config-cli/define-cli-config.ts +++ b/packages/nextjs/src/config-cli/define-cli-config.ts @@ -34,7 +34,7 @@ function addDefaultScaffoldTemplates(cliConfig: SitecoreCliConfigInput) { } /** - * Add the framework-specific implementaion of the component map generator to the CLI configuration. + * Add the framework-specific implementation of the component map generator to the CLI configuration. * @param {SitecoreCliConfigInput} cliConfig - The CLI configuration object */ function addDefaultComponentMapGenerator(cliConfig: SitecoreCliConfigInput) { From a947ef2987468fd60baeb56d08f03c6296990e2b Mon Sep 17 00:00:00 2001 From: MenKNas Date: Tue, 16 Jun 2026 18:43:24 +0300 Subject: [PATCH 29/32] Fix api extractor issue --- packages/core/api/content-sdk-core.api.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/core/api/content-sdk-core.api.md b/packages/core/api/content-sdk-core.api.md index d7c0a877dc..17fa5fa248 100644 --- a/packages/core/api/content-sdk-core.api.md +++ b/packages/core/api/content-sdk-core.api.md @@ -122,6 +122,9 @@ const ERROR_MESSAGES: { readonly IV_005: "[IV-005] Incorrect value for \"expiryDate\". Format the value according to ISO 8601."; readonly IV_006: (maxAttributes: number) => string; readonly IV_007: (siteName: string) => string; + readonly IV_008: (lockSide: string, currentSide: string) => string; + readonly IV_009: (name: string, lockSide: string, currentSide: string) => string; + readonly IV_010: (name: string) => string; readonly IE_001: (pluginName: string, dependency: string) => string; readonly IE_002: "[IE-002] SDK not initialized. You must first initialize the SDK using \"initContentSdk()\"."; readonly IE_003: "[IE-003] Timeout exceeded. The server did not respond within the allotted time."; @@ -129,6 +132,8 @@ const ERROR_MESSAGES: { readonly IE_005: "[IE-005] Unable to set the \"sc_cid\" cookie because the client ID could not be retrieved from the server. Make sure to set the correct values for \"contextId\" and \"siteName\". If the issue persists, try again later or use try-catch blocks to handle this error."; readonly IE_006: "[IE-006] Unable to set the \"sc_cid_personalize\" cookie because the visitor ID could not be retrieved from the server. Make sure to set the correct values for \"contextId\" and \"siteName\". If the issue persists, try again later or use try-catch blocks to handle this error."; readonly IE_007: (hostName: string) => string; + readonly IE_008: "[IE-008] Lock file validation failed:"; + readonly IE_009: "[IE-009] Atom validation failed. See issues above. You see this error because `breakOnError` is enabled in your CLI config."; readonly MV_001: "[MV-001] \"contextId\" is required."; readonly MV_002: "[MV-002] \"siteName\" is required."; readonly MV_003: "[MV-003] \"identifiers\" is required."; @@ -138,6 +143,11 @@ const ERROR_MESSAGES: { readonly MV_007: "[MV-007] Provide either \"contextId\" or both \"apiHost\" and \"apiKey\"."; readonly MV_008: "[MV-008] Verify that sitecore.config is properly imported and correctly referenced."; readonly MV_009: "[MV-009] \"language\" is required."; + readonly MV_010: (modulePath: string) => string; + readonly MV_011: (modulePath: string) => string; + readonly MV_012: "[MV-012] Lock file not found. Run `sitecore-tools project atoms update` to generate it."; + readonly MV_013: (name: string) => string; + readonly MV_014: (name: string) => string; readonly CONTACT_SUPPORT: "If the issue persists, please contact Sitecore Support."; }; From f750e40ffe9d5347f4bd0bb4cdc8f611110024a6 Mon Sep 17 00:00:00 2001 From: MenKNas Date: Tue, 16 Jun 2026 19:26:09 +0300 Subject: [PATCH 30/32] Minor fixes --- CHANGELOG.md | 3 +-- packages/react/src/atoms/field-schemas.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e17152c5bf..426623caf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -543,5 +543,4 @@ Our versioning strategy is as follows: ### 🧹 Chores -* `[template/nextjs]` Clean package.json scripts ([#75](https://github.com/Sitecore/content-sdk/pull/75)) -* Upgrade 3rd party dependencies ([#88](https://github.com/Sitecore/content-sdk/pull/88)) ([#92](https://github.com/Sitecore/content-sdk/pull/92)) \ No newline at end of file +* `[template/nextjs]` Clean package.json scripts ([#75](https://github.com/Sitecore/content-sdk/pull/75)) \ No newline at end of file diff --git a/packages/react/src/atoms/field-schemas.ts b/packages/react/src/atoms/field-schemas.ts index d96256ed6c..d0e824d8a8 100644 --- a/packages/react/src/atoms/field-schemas.ts +++ b/packages/react/src/atoms/field-schemas.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; import { withPropMeta } from './schema-utils'; /** - * Zod schema for a Sitecore Single-Line Text or Multi-Line Text field. + * Zod schema for a Sitecore Single-Line Text. * Mirrors the Sitecore Text component (`Text.tsx` in `@sitecore-content-sdk/react`). * @param {z.ZodRawShape} [extra] - Optional additional shape to merge into the schema. * @returns A ZodObject with `value?: string | number` and the DS control hint attached. From 753cd251045c5595395b0daa86939f9b33083e43 Mon Sep 17 00:00:00 2001 From: MenKNas Date: Tue, 16 Jun 2026 19:33:10 +0300 Subject: [PATCH 31/32] Fix yarn.lock --- yarn.lock | 1062 ++++++----------------------------------------------- 1 file changed, 113 insertions(+), 949 deletions(-) diff --git a/yarn.lock b/yarn.lock index aed99cab02..78531180f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,13 +5,6 @@ __metadata: version: 8 cacheKey: 10 -"@alloc/quick-lru@npm:^5.2.0": - version: 5.2.0 - resolution: "@alloc/quick-lru@npm:5.2.0" - checksum: 10/bdc35758b552bcf045733ac047fb7f9a07c4678b944c641adfbd41f798b4b91fffd0fdc0df2578d9b0afc7b4d636aa6e110ead5d6281a2adc1ab90efd7f057f8 - languageName: node - linkType: hard - "@asamuzakjp/css-color@npm:^3.2.0": version: 3.2.0 resolution: "@asamuzakjp/css-color@npm:3.2.0" @@ -716,7 +709,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/core@npm:^1.1.0, @emnapi/core@npm:^1.10.0": +"@emnapi/core@npm:^1.1.0": version: 1.11.1 resolution: "@emnapi/core@npm:1.11.1" dependencies: @@ -744,7 +737,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.10.0, @emnapi/runtime@npm:^1.7.0": +"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.7.0": version: 1.11.1 resolution: "@emnapi/runtime@npm:1.11.1" dependencies: @@ -771,7 +764,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/wasi-threads@npm:1.2.2, @emnapi/wasi-threads@npm:^1.2.1": +"@emnapi/wasi-threads@npm:1.2.2": version: 1.2.2 resolution: "@emnapi/wasi-threads@npm:1.2.2" dependencies: @@ -780,13 +773,6 @@ __metadata: languageName: node linkType: hard -"@epic-web/invariant@npm:^1.0.0": - version: 1.0.0 - resolution: "@epic-web/invariant@npm:1.0.0" - checksum: 10/28b36a7447f60b84f9d6a23571480042170ef4239a577577ad8447f64a2e4f1a4e57e6fe1b592e61534c5ab53ff67776130e6c88a68cbd997eb6e9c9759a5934 - languageName: node - linkType: hard - "@es-joy/jsdoccomment@npm:~0.52.0": version: 0.52.0 resolution: "@es-joy/jsdoccomment@npm:0.52.0" @@ -1231,7 +1217,7 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3, @eslint/eslintrc@npm:^3.3.5": +"@eslint/eslintrc@npm:^3.3.5": version: 3.3.5 resolution: "@eslint/eslintrc@npm:3.3.5" dependencies: @@ -1272,38 +1258,6 @@ __metadata: languageName: node linkType: hard -"@formatjs/fast-memoize@npm:3.1.6, @formatjs/fast-memoize@npm:^3.1.0": - version: 3.1.6 - resolution: "@formatjs/fast-memoize@npm:3.1.6" - checksum: 10/7dec3e82586d4c4889671c6081d7b0d87b2c229ba551d8328f96ae8ab58b2cd6056fc5d360c0b0c9688b7164f12ef517428016fbce15b950987a77005e481824 - languageName: node - linkType: hard - -"@formatjs/icu-messageformat-parser@npm:3.5.11, @formatjs/icu-messageformat-parser@npm:^3.4.0": - version: 3.5.11 - resolution: "@formatjs/icu-messageformat-parser@npm:3.5.11" - dependencies: - "@formatjs/icu-skeleton-parser": "npm:2.1.10" - checksum: 10/59b8323b9615edb4ca6463d09108581469de7566f2aec106f1d87b259333cdbf9577d9c22a990bc454f3f461c799f4add683a9ff7185af6fa225fcd29ec336da - languageName: node - linkType: hard - -"@formatjs/icu-skeleton-parser@npm:2.1.10": - version: 2.1.10 - resolution: "@formatjs/icu-skeleton-parser@npm:2.1.10" - checksum: 10/ec30d106ce38de80f4128d0cdfac15699628652807695843254bf0d31650bd0dc4b57e48691d164556234494d59b2816e710fa12321234c85b803c5cda32bedf - languageName: node - linkType: hard - -"@formatjs/intl-localematcher@npm:^0.8.1": - version: 0.8.10 - resolution: "@formatjs/intl-localematcher@npm:0.8.10" - dependencies: - "@formatjs/fast-memoize": "npm:3.1.6" - checksum: 10/d9d3f408363091bf35950a842f58c662d88d9f54d9a53b1238cb673a73d1345412d78e01a2d56ec58cef81b9c66a8ce0ee78ed9c31976bd56dc68a38ef3cbc0f - languageName: node - linkType: hard - "@gar/promise-retry@npm:^1.0.0, @gar/promise-retry@npm:^1.0.2": version: 1.0.3 resolution: "@gar/promise-retry@npm:1.0.3" @@ -3110,150 +3064,6 @@ __metadata: languageName: node linkType: hard -"@parcel/watcher-android-arm64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-android-arm64@npm:2.5.6" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@parcel/watcher-darwin-arm64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-darwin-arm64@npm:2.5.6" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@parcel/watcher-darwin-x64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-darwin-x64@npm:2.5.6" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@parcel/watcher-freebsd-x64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-freebsd-x64@npm:2.5.6" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@parcel/watcher-linux-arm-glibc@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-arm-glibc@npm:2.5.6" - conditions: os=linux & cpu=arm & libc=glibc - languageName: node - linkType: hard - -"@parcel/watcher-linux-arm-musl@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-arm-musl@npm:2.5.6" - conditions: os=linux & cpu=arm & libc=musl - languageName: node - linkType: hard - -"@parcel/watcher-linux-arm64-glibc@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.5.6" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@parcel/watcher-linux-arm64-musl@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-arm64-musl@npm:2.5.6" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@parcel/watcher-linux-x64-glibc@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-x64-glibc@npm:2.5.6" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@parcel/watcher-linux-x64-musl@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-linux-x64-musl@npm:2.5.6" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@parcel/watcher-win32-arm64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-win32-arm64@npm:2.5.6" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@parcel/watcher-win32-ia32@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-win32-ia32@npm:2.5.6" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@parcel/watcher-win32-x64@npm:2.5.6": - version: 2.5.6 - resolution: "@parcel/watcher-win32-x64@npm:2.5.6" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@parcel/watcher@npm:^2.4.1": - version: 2.5.6 - resolution: "@parcel/watcher@npm:2.5.6" - dependencies: - "@parcel/watcher-android-arm64": "npm:2.5.6" - "@parcel/watcher-darwin-arm64": "npm:2.5.6" - "@parcel/watcher-darwin-x64": "npm:2.5.6" - "@parcel/watcher-freebsd-x64": "npm:2.5.6" - "@parcel/watcher-linux-arm-glibc": "npm:2.5.6" - "@parcel/watcher-linux-arm-musl": "npm:2.5.6" - "@parcel/watcher-linux-arm64-glibc": "npm:2.5.6" - "@parcel/watcher-linux-arm64-musl": "npm:2.5.6" - "@parcel/watcher-linux-x64-glibc": "npm:2.5.6" - "@parcel/watcher-linux-x64-musl": "npm:2.5.6" - "@parcel/watcher-win32-arm64": "npm:2.5.6" - "@parcel/watcher-win32-ia32": "npm:2.5.6" - "@parcel/watcher-win32-x64": "npm:2.5.6" - detect-libc: "npm:^2.0.3" - is-glob: "npm:^4.0.3" - node-addon-api: "npm:^7.0.0" - node-gyp: "npm:latest" - picomatch: "npm:^4.0.3" - dependenciesMeta: - "@parcel/watcher-android-arm64": - optional: true - "@parcel/watcher-darwin-arm64": - optional: true - "@parcel/watcher-darwin-x64": - optional: true - "@parcel/watcher-freebsd-x64": - optional: true - "@parcel/watcher-linux-arm-glibc": - optional: true - "@parcel/watcher-linux-arm-musl": - optional: true - "@parcel/watcher-linux-arm64-glibc": - optional: true - "@parcel/watcher-linux-arm64-musl": - optional: true - "@parcel/watcher-linux-x64-glibc": - optional: true - "@parcel/watcher-linux-x64-musl": - optional: true - "@parcel/watcher-win32-arm64": - optional: true - "@parcel/watcher-win32-ia32": - optional: true - "@parcel/watcher-win32-x64": - optional: true - checksum: 10/00e027ef6bd67239bd63d63d062363a0263377a3de3114c29f0f717076616b3a03fd67902a70ba52dbb7241efc27498a8f1da983aa41280c454b9c1246a6f191 - languageName: node - linkType: hard - "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3545,13 +3355,6 @@ __metadata: languageName: node linkType: hard -"@schummar/icu-type-parser@npm:1.21.5": - version: 1.21.5 - resolution: "@schummar/icu-type-parser@npm:1.21.5" - checksum: 10/4e83edf93bf49c414d9e2f5bb0457dfd012586d4e24f3850a4cc5b6a18c5329868e8a4b1cb0d4ad8490f175fd21d00229213eb9b2e799809015d85788e567139 - languageName: node - linkType: hard - "@shikijs/engine-oniguruma@npm:^3.23.0": version: 3.23.0 resolution: "@shikijs/engine-oniguruma@npm:3.23.0" @@ -3729,7 +3532,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/analytics-core@npm:2.1.0-beta.1, @sitecore-content-sdk/analytics-core@npm:^2.1.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": +"@sitecore-content-sdk/analytics-core@npm:2.1.0-beta.1, @sitecore-content-sdk/analytics-core@workspace:packages/analytics-core": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/analytics-core@workspace:packages/analytics-core" dependencies: @@ -3788,7 +3591,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/cli@npm:^2.1.0-beta.1, @sitecore-content-sdk/cli@workspace:packages/cli": +"@sitecore-content-sdk/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/cli@workspace:packages/cli" dependencies: @@ -3949,7 +3752,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/events@npm:2.1.0-beta.1, @sitecore-content-sdk/events@npm:^2.1.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": +"@sitecore-content-sdk/events@npm:2.1.0-beta.1, @sitecore-content-sdk/events@workspace:packages/events": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/events@workspace:packages/events" dependencies: @@ -4016,7 +3819,7 @@ __metadata: languageName: node linkType: hard -"@sitecore-content-sdk/nextjs@npm:^2.1.0-beta.1, @sitecore-content-sdk/nextjs@workspace:packages/nextjs": +"@sitecore-content-sdk/nextjs@workspace:packages/nextjs": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/nextjs@workspace:packages/nextjs" dependencies: @@ -4083,7 +3886,7 @@ __metadata: languageName: unknown linkType: soft -"@sitecore-content-sdk/personalize@npm:2.1.0-beta.1, @sitecore-content-sdk/personalize@npm:^2.1.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": +"@sitecore-content-sdk/personalize@npm:2.1.0-beta.1, @sitecore-content-sdk/personalize@workspace:packages/personalize": version: 0.0.0-use.local resolution: "@sitecore-content-sdk/personalize@workspace:packages/personalize" dependencies: @@ -4253,15 +4056,6 @@ __metadata: languageName: node linkType: hard -"@sitecore/components@npm:~2.1.0": - version: 2.1.1 - resolution: "@sitecore/components@npm:2.1.1" - peerDependencies: - "@sitecore/byoc": ^0.3.3 - checksum: 10/581a6905d5bb96e44807bd3de66902ab11077187b9800bde2eae13c75a99479b77d80fce24755decf1e3da850f76cb80b92b555c58830b59527f3c0587832441 - languageName: node - linkType: hard - "@stylistic/eslint-plugin@npm:^5.2.2": version: 5.10.0 resolution: "@stylistic/eslint-plugin@npm:5.10.0" @@ -4278,149 +4072,6 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-darwin-arm64@npm:1.15.41" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-darwin-x64@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-darwin-x64@npm:1.15.41" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@swc/core-linux-arm-gnueabihf@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.15.41" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@swc/core-linux-arm64-gnu@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-arm64-gnu@npm:1.15.41" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-arm64-musl@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-arm64-musl@npm:1.15.41" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@swc/core-linux-ppc64-gnu@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-ppc64-gnu@npm:1.15.41" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-s390x-gnu@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-s390x-gnu@npm:1.15.41" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-x64-gnu@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-x64-gnu@npm:1.15.41" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-x64-musl@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-linux-x64-musl@npm:1.15.41" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@swc/core-win32-arm64-msvc@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-win32-arm64-msvc@npm:1.15.41" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-win32-ia32-msvc@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-win32-ia32-msvc@npm:1.15.41" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@swc/core-win32-x64-msvc@npm:1.15.41": - version: 1.15.41 - resolution: "@swc/core-win32-x64-msvc@npm:1.15.41" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@swc/core@npm:^1.15.2": - version: 1.15.41 - resolution: "@swc/core@npm:1.15.41" - dependencies: - "@swc/core-darwin-arm64": "npm:1.15.41" - "@swc/core-darwin-x64": "npm:1.15.41" - "@swc/core-linux-arm-gnueabihf": "npm:1.15.41" - "@swc/core-linux-arm64-gnu": "npm:1.15.41" - "@swc/core-linux-arm64-musl": "npm:1.15.41" - "@swc/core-linux-ppc64-gnu": "npm:1.15.41" - "@swc/core-linux-s390x-gnu": "npm:1.15.41" - "@swc/core-linux-x64-gnu": "npm:1.15.41" - "@swc/core-linux-x64-musl": "npm:1.15.41" - "@swc/core-win32-arm64-msvc": "npm:1.15.41" - "@swc/core-win32-ia32-msvc": "npm:1.15.41" - "@swc/core-win32-x64-msvc": "npm:1.15.41" - "@swc/counter": "npm:^0.1.3" - "@swc/types": "npm:^0.1.26" - peerDependencies: - "@swc/helpers": ">=0.5.17" - dependenciesMeta: - "@swc/core-darwin-arm64": - optional: true - "@swc/core-darwin-x64": - optional: true - "@swc/core-linux-arm-gnueabihf": - optional: true - "@swc/core-linux-arm64-gnu": - optional: true - "@swc/core-linux-arm64-musl": - optional: true - "@swc/core-linux-ppc64-gnu": - optional: true - "@swc/core-linux-s390x-gnu": - optional: true - "@swc/core-linux-x64-gnu": - optional: true - "@swc/core-linux-x64-musl": - optional: true - "@swc/core-win32-arm64-msvc": - optional: true - "@swc/core-win32-ia32-msvc": - optional: true - "@swc/core-win32-x64-msvc": - optional: true - peerDependenciesMeta: - "@swc/helpers": - optional: true - checksum: 10/4d4b47885468bb6635a06040afa566e5568b55c7a43facceadca39ebd093a96e5d11701d8a92872321ae527f873701de10a9be2b3d96c1a7188e3e862c4072c4 - languageName: node - linkType: hard - -"@swc/counter@npm:^0.1.3": - version: 0.1.3 - resolution: "@swc/counter@npm:0.1.3" - checksum: 10/df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598 - languageName: node - linkType: hard - "@swc/helpers@npm:0.5.15": version: 0.5.15 resolution: "@swc/helpers@npm:0.5.15" @@ -4430,179 +4081,6 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.26": - version: 0.1.26 - resolution: "@swc/types@npm:0.1.26" - dependencies: - "@swc/counter": "npm:^0.1.3" - checksum: 10/07de03b9da3928cdf69bda70bf2c809dd86f16ef23e357759e577bbd975529cb20218c2e54e72b00585abae2b5e04e39b8947cea7a6f4de2d40a7633be441919 - languageName: node - linkType: hard - -"@tailwindcss/node@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/node@npm:4.3.1" - dependencies: - "@jridgewell/remapping": "npm:^2.3.5" - enhanced-resolve: "npm:5.21.6" - jiti: "npm:^2.7.0" - lightningcss: "npm:1.32.0" - magic-string: "npm:^0.30.21" - source-map-js: "npm:^1.2.1" - tailwindcss: "npm:4.3.1" - checksum: 10/582a718fec1429249ff4ae0fdc005720ef70e5f60676e3eab145d5291a50805262f95b086458dc3589762bd8cd200f9d1dbb5d156c0e9b0270fe8ad7de0010b7 - languageName: node - linkType: hard - -"@tailwindcss/oxide-android-arm64@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-android-arm64@npm:4.3.1" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@tailwindcss/oxide-darwin-arm64@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.3.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@tailwindcss/oxide-darwin-x64@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-darwin-x64@npm:4.3.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@tailwindcss/oxide-freebsd-x64@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.3.1" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.3.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.3.1" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@tailwindcss/oxide-linux-arm64-musl@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.3.1" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@tailwindcss/oxide-linux-x64-gnu@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.3.1" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@tailwindcss/oxide-linux-x64-musl@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.3.1" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@tailwindcss/oxide-wasm32-wasi@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.3.1" - dependencies: - "@emnapi/core": "npm:^1.10.0" - "@emnapi/runtime": "npm:^1.10.0" - "@emnapi/wasi-threads": "npm:^1.2.1" - "@napi-rs/wasm-runtime": "npm:^1.1.4" - "@tybys/wasm-util": "npm:^0.10.2" - tslib: "npm:^2.8.1" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.3.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@tailwindcss/oxide-win32-x64-msvc@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.3.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"@tailwindcss/oxide@npm:4.3.1": - version: 4.3.1 - resolution: "@tailwindcss/oxide@npm:4.3.1" - dependencies: - "@tailwindcss/oxide-android-arm64": "npm:4.3.1" - "@tailwindcss/oxide-darwin-arm64": "npm:4.3.1" - "@tailwindcss/oxide-darwin-x64": "npm:4.3.1" - "@tailwindcss/oxide-freebsd-x64": "npm:4.3.1" - "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.3.1" - "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.3.1" - "@tailwindcss/oxide-linux-arm64-musl": "npm:4.3.1" - "@tailwindcss/oxide-linux-x64-gnu": "npm:4.3.1" - "@tailwindcss/oxide-linux-x64-musl": "npm:4.3.1" - "@tailwindcss/oxide-wasm32-wasi": "npm:4.3.1" - "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.3.1" - "@tailwindcss/oxide-win32-x64-msvc": "npm:4.3.1" - dependenciesMeta: - "@tailwindcss/oxide-android-arm64": - optional: true - "@tailwindcss/oxide-darwin-arm64": - optional: true - "@tailwindcss/oxide-darwin-x64": - optional: true - "@tailwindcss/oxide-freebsd-x64": - optional: true - "@tailwindcss/oxide-linux-arm-gnueabihf": - optional: true - "@tailwindcss/oxide-linux-arm64-gnu": - optional: true - "@tailwindcss/oxide-linux-arm64-musl": - optional: true - "@tailwindcss/oxide-linux-x64-gnu": - optional: true - "@tailwindcss/oxide-linux-x64-musl": - optional: true - "@tailwindcss/oxide-wasm32-wasi": - optional: true - "@tailwindcss/oxide-win32-arm64-msvc": - optional: true - "@tailwindcss/oxide-win32-x64-msvc": - optional: true - checksum: 10/598a1f2d038fcb1c35281d043570cefc4c2dac9aedac50e31272c26c1b2cb714457b03835106959afe2761dc88ee46b0e719edb922783942ae493ad71959a012 - languageName: node - linkType: hard - -"@tailwindcss/postcss@npm:^4": - version: 4.3.1 - resolution: "@tailwindcss/postcss@npm:4.3.1" - dependencies: - "@alloc/quick-lru": "npm:^5.2.0" - "@tailwindcss/node": "npm:4.3.1" - "@tailwindcss/oxide": "npm:4.3.1" - postcss: "npm:8.5.15" - tailwindcss: "npm:4.3.1" - checksum: 10/e1922f337b1c4542f92d54e5b8f71fd6a73f9c02bbfd4361015e0046f0a6c09a354a721d2129af9d1286d96ff1d7bc2e6caabadbb37154816c2fdedba906d180 - languageName: node - linkType: hard - "@testing-library/dom@npm:^10.4.0": version: 10.4.1 resolution: "@testing-library/dom@npm:10.4.1" @@ -5177,23 +4655,23 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.61.0, @typescript-eslint/eslint-plugin@npm:^8.39.0": - version: 8.61.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.61.0" +"@typescript-eslint/eslint-plugin@npm:8.61.1, @typescript-eslint/eslint-plugin@npm:^8.39.0": + version: 8.61.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.61.1" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.61.0" - "@typescript-eslint/type-utils": "npm:8.61.0" - "@typescript-eslint/utils": "npm:8.61.0" - "@typescript-eslint/visitor-keys": "npm:8.61.0" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/type-utils": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.5.0" peerDependencies: - "@typescript-eslint/parser": ^8.61.0 + "@typescript-eslint/parser": ^8.61.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/ca7fbaa2f03ec15bdbf39d2e4d42f1b682085f23830591d1d6c3d9f497fdda497341b2fa67c8d366514a3c22807557e45e7afe1ee70cef527b184250e5422e8f + checksum: 10/5434b78781f750eb1e2918f960ff3a6a3fae36951591456b1a309695a5c6c027d914038d7c2c71e614611b5c46a3be85b4b004581be0bcfb1be84e741b0e98a8 languageName: node linkType: hard @@ -5213,19 +4691,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.61.0, @typescript-eslint/parser@npm:^8.39.0": - version: 8.61.0 - resolution: "@typescript-eslint/parser@npm:8.61.0" +"@typescript-eslint/parser@npm:8.61.1, @typescript-eslint/parser@npm:^8.39.0": + version: 8.61.1 + resolution: "@typescript-eslint/parser@npm:8.61.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.61.0" - "@typescript-eslint/types": "npm:8.61.0" - "@typescript-eslint/typescript-estree": "npm:8.61.0" - "@typescript-eslint/visitor-keys": "npm:8.61.0" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" debug: "npm:^4.4.3" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/82060c36786339867d63337708a08bd4bc65569313998bd086dbe6b901664082c7e40d6b6e085296a459cd4fc1d064479ef570b51e1eb113688bb152a7a6d689 + checksum: 10/cdaca9bb78bd6cc7210e88b28c42af11b23a47393a97ed37350e64a3846d036ebd178583fd2a54216974a740b3f6932274bdaf72046e6307ef26aee3ebe35cec languageName: node linkType: hard @@ -5242,16 +4720,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/project-service@npm:8.61.0" +"@typescript-eslint/project-service@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/project-service@npm:8.61.1" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.61.0" - "@typescript-eslint/types": "npm:^8.61.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.61.1" + "@typescript-eslint/types": "npm:^8.61.1" debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10/b7d7e973b565f604af43b8afb3ca1c3fbe6fcf16863bde83b42417a196ba9f3a5a3f5d39bf57ed96b8ce577047064d93c353ecb21db5e95dce69f81335c9cd81 + checksum: 10/51eb5cbd74748d08512db976bbeabdb9352f44e220621dce3bc96837bc309c7d266df49007be196e57950cf9e12e2c574649cbf14aa2e518734ee55ff7d86f2c languageName: node linkType: hard @@ -5265,13 +4743,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/scope-manager@npm:8.61.0" +"@typescript-eslint/scope-manager@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/scope-manager@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:8.61.0" - "@typescript-eslint/visitor-keys": "npm:8.61.0" - checksum: 10/295e306665d64f0330fede3fe72febd65c67c3083d747149b66097aa6f7d517f25731dc1dbec900b15768c40f92b082f501296e7524855fe82697f40b8d23ce1 + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" + checksum: 10/69c1d5b403b2e6adbae7e24856628941e912304ac728b6beae959df98092707adf3f60e1d0c9b90badd758d76174e9d44a7eba55608693c976e5cba5cc47593c languageName: node linkType: hard @@ -5284,12 +4762,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.61.0, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.61.0" +"@typescript-eslint/tsconfig-utils@npm:8.61.1, @typescript-eslint/tsconfig-utils@npm:^8.39.0, @typescript-eslint/tsconfig-utils@npm:^8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.61.1" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10/f678ff5ec887a27d8e590e0c67403b12e372a027ab036dcfc1e3ef614d3bed7a3c455a65fa0a87ff7dae5b0ad1c49cf4aa40639cc368d7eb424efe8349d9cb9f + checksum: 10/ee81d01809178d6fd88a478bdf8fb546063c55f01385c6443fe7b93ebe9ba26ecda4f0eb804b2fc0a189dc34a51e89690477fb68cd099cd3952902f376d641c6 languageName: node linkType: hard @@ -5309,19 +4787,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/type-utils@npm:8.61.0" +"@typescript-eslint/type-utils@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/type-utils@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:8.61.0" - "@typescript-eslint/typescript-estree": "npm:8.61.0" - "@typescript-eslint/utils": "npm:8.61.0" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.5.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/8290e5fc26241dfd5aeeffad0fb9857a3fa1f9c8107dfb01638970297e0e17be6088f0fd2d6fc7d450e9879afaa7e23f4111182bcf0b625eba74fdf13100b19e + checksum: 10/01c62227479d94ed3745e3bef5a0c870586d75b6ed550e4beb84b25df23de29558e0bdb6ff291e457977254764766c5d252b75a03d4ad592998269dba69a32a6 languageName: node linkType: hard @@ -5332,10 +4810,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.61.0, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/types@npm:8.61.0" - checksum: 10/8e1e1cf5d092beed1a974b3b5d7cc20219ad3e4501b85bbef5bec1c81ab50b09ee70093ad2195c3061c499e804d63aac38dcc20293342b1fa774ba743c0d63bf +"@typescript-eslint/types@npm:8.61.1, @typescript-eslint/types@npm:^8.34.1, @typescript-eslint/types@npm:^8.39.0, @typescript-eslint/types@npm:^8.46.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/types@npm:8.61.1" + checksum: 10/9aa036b27d1874533bf1b931151adaaebb4380cfd14cc71395ab8d2c00c7420218e42811c2b5a68c9fa54cd048e1ab47085afd8b44cd5d736fb5cb8edd300d64 languageName: node linkType: hard @@ -5359,14 +4837,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.61.0" +"@typescript-eslint/typescript-estree@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.61.1" dependencies: - "@typescript-eslint/project-service": "npm:8.61.0" - "@typescript-eslint/tsconfig-utils": "npm:8.61.0" - "@typescript-eslint/types": "npm:8.61.0" - "@typescript-eslint/visitor-keys": "npm:8.61.0" + "@typescript-eslint/project-service": "npm:8.61.1" + "@typescript-eslint/tsconfig-utils": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/visitor-keys": "npm:8.61.1" debug: "npm:^4.4.3" minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" @@ -5374,7 +4852,7 @@ __metadata: ts-api-utils: "npm:^2.5.0" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10/6d5ab7850226de23ab26d94388f729e792413f5a9e704c8c685b66eb20946efeb290cda91195f062e1065bc20129ec8f5955768316660132087347e66dec0d1a + checksum: 10/36b8b68e7fe9bdba0bb24b46a43a29e39f0cd45440ea190644e230d10e96b0bab9289027668910ff976100d68a9b3bc222618bf96b99bae6fd0053eb560a1257 languageName: node linkType: hard @@ -5393,18 +4871,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/utils@npm:8.61.0" +"@typescript-eslint/utils@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/utils@npm:8.61.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.61.0" - "@typescript-eslint/types": "npm:8.61.0" - "@typescript-eslint/typescript-estree": "npm:8.61.0" + "@typescript-eslint/scope-manager": "npm:8.61.1" + "@typescript-eslint/types": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/50ff451edb8e5dee92bbab11a75cbd570715623d89094d0541ddfbef208248e82d2f9478d1e09fb9c94496069afd4db9521384b77f7aaa63970f7edfebddfba9 + checksum: 10/7c8886801f73fc09ecf585b0e8f33799e4a341d51b00db0467f05853f84b808bb98a35e92eab49f28d5d24a1c915959d834214776f0ff6f0cfa5abb3f2e11496 languageName: node linkType: hard @@ -5418,13 +4896,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.61.0": - version: 8.61.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.61.0" +"@typescript-eslint/visitor-keys@npm:8.61.1": + version: 8.61.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.61.1" dependencies: - "@typescript-eslint/types": "npm:8.61.0" + "@typescript-eslint/types": "npm:8.61.1" eslint-visitor-keys: "npm:^5.0.0" - checksum: 10/243018d9d8b1918d2863e50eec6628c792ccda05ad5534f5153fc783b7f54cdb8a58d758eb74260d113274bfab8bb38ad4664f3db9e7d3f844cdffbe6e47e285 + checksum: 10/7d99c6ae9e91d32b8cc3662ead0e393c912351b5786ece62e1dc198a6b0e9813bb7eae44772970512e7e424a342c86529d9f3dae7c8ab83ac95b5dc33826647a languageName: node linkType: hard @@ -5921,7 +5399,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": +"ansi-styles@npm:^6.1.0": version: 6.2.3 resolution: "ansi-styles@npm:6.2.3" checksum: 10/c49dad7639f3e48859bd51824c93b9eb0db628afc243c51c3dd2410c4a15ede1a83881c6c7341aa2b159c4f90c11befb38f2ba848c07c66c9f9de4bcd7cb9f30 @@ -6972,36 +6450,6 @@ __metadata: languageName: node linkType: hard -"content-sdk-nextjs-app-router@workspace:samples/nextjs-app-router": - version: 0.0.0-use.local - resolution: "content-sdk-nextjs-app-router@workspace:samples/nextjs-app-router" - dependencies: - "@eslint/eslintrc": "npm:^3" - "@sitecore-content-sdk/analytics-core": "npm:^2.1.0-beta.1" - "@sitecore-content-sdk/cli": "npm:^2.1.0-beta.1" - "@sitecore-content-sdk/events": "npm:^2.1.0-beta.1" - "@sitecore-content-sdk/nextjs": "npm:^2.1.0-beta.1" - "@sitecore-content-sdk/personalize": "npm:^2.1.0-beta.1" - "@sitecore-feaas/clientside": "npm:^0.6.0" - "@sitecore/components": "npm:~2.1.0" - "@tailwindcss/postcss": "npm:^4" - "@types/node": "npm:^24.10.4" - "@types/react": "npm:^19.2.7" - "@types/react-dom": "npm:^19.2.3" - cross-env: "npm:^10.0.0" - eslint: "npm:^9.33.0" - eslint-config-next: "npm:16.2.2" - next: "npm:^16.2.0" - next-intl: "npm:^4.3.5" - npm-run-all2: "npm:^8.0.4" - react: "npm:^19.2.1" - react-dom: "npm:^19.2.1" - tailwindcss: "npm:^4" - typescript: "npm:~5.8.3" - zod: "npm:^4.3.6" - languageName: unknown - linkType: soft - "conventional-changelog-angular@npm:7.0.0": version: 7.0.0 resolution: "conventional-changelog-angular@npm:7.0.0" @@ -7209,19 +6657,6 @@ __metadata: languageName: node linkType: hard -"cross-env@npm:^10.0.0": - version: 10.1.0 - resolution: "cross-env@npm:10.1.0" - dependencies: - "@epic-web/invariant": "npm:^1.0.0" - cross-spawn: "npm:^7.0.6" - bin: - cross-env: dist/bin/cross-env.js - cross-env-shell: dist/bin/cross-env-shell.js - checksum: 10/0e5d8bdefbbcd000460b69755e0eeb22953510abac8375e4f8b638ff7c45406141acfd57b8a4c1d1cf0b5ea42f33451b302062fb9b34408753b4d465e901b845 - languageName: node - linkType: hard - "cross-fetch@npm:^3.1.5": version: 3.2.0 resolution: "cross-fetch@npm:3.2.0" @@ -7598,7 +7033,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.3, detect-libc@npm:^2.1.2": +"detect-libc@npm:^2.1.2": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" checksum: 10/b736c8d97d5d46164c0d1bed53eb4e6a3b1d8530d460211e2d52f1c552875e706c58a5376854e4e54f8b828c9cada58c855288c968522eb93ac7696d65970766 @@ -7745,9 +7180,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.5.328": - version: 1.5.372 - resolution: "electron-to-chromium@npm:1.5.372" - checksum: 10/cf4dde2ce99fea74416377c029904ddf543ad821be98e6e27be724fa2e4db2962948d1147560ca7810ac1bd6cb1636feb9b34e3654f14c75ecbddfa5fb57ea1d + version: 1.5.373 + resolution: "electron-to-chromium@npm:1.5.373" + checksum: 10/f255e8de2e97a69b658b7ddbdf5d9fef9663f98665832eb7ccf583d054a13012c7e90331771abe866a5b4fa3583059bfa699fa381c51e7022d58fc5e997e6077 languageName: node linkType: hard @@ -7790,16 +7225,6 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:5.21.6": - version: 5.21.6 - resolution: "enhanced-resolve@npm:5.21.6" - dependencies: - graceful-fs: "npm:^4.2.4" - tapable: "npm:^2.3.3" - checksum: 10/7599496622b1718727409bc1944e19ad72492add909a3f9d905bbc9d81bade42b8bece156bfbe30ee8d1bbc8b24abc9d8bf194f4d46173a6adc919e95766753a - languageName: node - linkType: hard - "enquirer@npm:2.3.6, enquirer@npm:~2.3.6": version: 2.3.6 resolution: "enquirer@npm:2.3.6" @@ -7865,6 +7290,18 @@ __metadata: languageName: node linkType: hard +"es-abstract-get@npm:^1.0.0": + version: 1.0.0 + resolution: "es-abstract-get@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.2" + is-callable: "npm:^1.2.7" + object-inspect: "npm:^1.13.4" + checksum: 10/a2bfa7536529a21c8590670a69c0c4583e531f92dbc420c13f680bf906215f510d9d14b18e2e4a26e8b07100ab28719811dbce822e43fe87305a5a9069cda24e + languageName: node + linkType: hard + "es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0, es-abstract@npm:^1.24.2": version: 1.24.2 resolution: "es-abstract@npm:1.24.2" @@ -8012,13 +7449,15 @@ __metadata: linkType: hard "es-to-primitive@npm:^1.3.0": - version: 1.3.0 - resolution: "es-to-primitive@npm:1.3.0" + version: 1.3.1 + resolution: "es-to-primitive@npm:1.3.1" dependencies: + es-abstract-get: "npm:^1.0.0" + es-errors: "npm:^1.3.0" is-callable: "npm:^1.2.7" - is-date-object: "npm:^1.0.5" - is-symbol: "npm:^1.0.4" - checksum: 10/17faf35c221aad59a16286cbf58ef6f080bf3c485dff202c490d074d8e74da07884e29b852c245d894eac84f73c58330ec956dfd6d02c0b449d75eb1012a3f9b + is-date-object: "npm:^1.1.0" + is-symbol: "npm:^1.1.1" + checksum: 10/f8ec95e7425583211d7940a455d507832daa04579db50738340b6c59fcf1b9aa832cf6a69891fdafff88b740457c420bdc95867211808f06ae0338d67b09b032 languageName: node linkType: hard @@ -8535,7 +7974,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.32.0, eslint@npm:^9.33.0, eslint@npm:^9.39.1": +"eslint@npm:^9.32.0, eslint@npm:^9.39.1": version: 9.39.4 resolution: "eslint@npm:9.39.4" dependencies: @@ -9470,7 +8909,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10/bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 @@ -9777,15 +9216,6 @@ __metadata: languageName: node linkType: hard -"icu-minify@npm:^4.13.0": - version: 4.13.0 - resolution: "icu-minify@npm:4.13.0" - dependencies: - "@formatjs/icu-messageformat-parser": "npm:^3.4.0" - checksum: 10/3472867f81f0686185ef88e99633d08b465e3b4bf825d821225c325bc06d999cd843fcdea7188686182ad191c6f8b743238c57fb33ef678455950b805f87ae5c - languageName: node - linkType: hard - "ieee754@npm:1.2.1, ieee754@npm:^1.1.13": version: 1.2.1 resolution: "ieee754@npm:1.2.1" @@ -9998,16 +9428,6 @@ __metadata: languageName: node linkType: hard -"intl-messageformat@npm:^11.1.0": - version: 11.2.8 - resolution: "intl-messageformat@npm:11.2.8" - dependencies: - "@formatjs/fast-memoize": "npm:3.1.6" - "@formatjs/icu-messageformat-parser": "npm:3.5.11" - checksum: 10/451585274cdb1ff798b8d9bfa9aed6623870d2a3bf7c315a4cc37e57793d13a51ec09da705a228f7a64a1267fd6afe91b5bf2fd0c4223fd4f1d158a2adb82d41 - languageName: node - linkType: hard - "ip-address@npm:^10.1.1": version: 10.2.0 resolution: "ip-address@npm:10.2.0" @@ -10112,7 +9532,7 @@ __metadata: languageName: node linkType: hard -"is-date-object@npm:^1.0.5, is-date-object@npm:^1.1.0": +"is-date-object@npm:^1.1.0": version: 1.1.0 resolution: "is-date-object@npm:1.1.0" dependencies: @@ -10356,7 +9776,7 @@ __metadata: languageName: node linkType: hard -"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.1": +"is-symbol@npm:^1.1.1": version: 1.1.1 resolution: "is-symbol@npm:1.1.1" dependencies: @@ -11218,15 +10638,6 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^2.7.0": - version: 2.7.0 - resolution: "jiti@npm:2.7.0" - bin: - jiti: lib/jiti-cli.mjs - checksum: 10/6d75a8dbd61dbee031aa0937fabb748ff8ddf370b971958cc704f5cf26b4c5bdc9dcd0563059b2627a2bd41d946fa0bc64f912fdc8981ca7945a9d63c74ad0f9 - languageName: node - linkType: hard - "jju@npm:~1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -11704,126 +11115,6 @@ __metadata: languageName: node linkType: hard -"lightningcss-android-arm64@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-android-arm64@npm:1.32.0" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"lightningcss-darwin-arm64@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-darwin-arm64@npm:1.32.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"lightningcss-darwin-x64@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-darwin-x64@npm:1.32.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"lightningcss-freebsd-x64@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-freebsd-x64@npm:1.32.0" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"lightningcss-linux-arm-gnueabihf@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-linux-arm-gnueabihf@npm:1.32.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"lightningcss-linux-arm64-gnu@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-linux-arm64-gnu@npm:1.32.0" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"lightningcss-linux-arm64-musl@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-linux-arm64-musl@npm:1.32.0" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"lightningcss-linux-x64-gnu@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-linux-x64-gnu@npm:1.32.0" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"lightningcss-linux-x64-musl@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-linux-x64-musl@npm:1.32.0" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"lightningcss-win32-arm64-msvc@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-win32-arm64-msvc@npm:1.32.0" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"lightningcss-win32-x64-msvc@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss-win32-x64-msvc@npm:1.32.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"lightningcss@npm:1.32.0": - version: 1.32.0 - resolution: "lightningcss@npm:1.32.0" - dependencies: - detect-libc: "npm:^2.0.3" - lightningcss-android-arm64: "npm:1.32.0" - lightningcss-darwin-arm64: "npm:1.32.0" - lightningcss-darwin-x64: "npm:1.32.0" - lightningcss-freebsd-x64: "npm:1.32.0" - lightningcss-linux-arm-gnueabihf: "npm:1.32.0" - lightningcss-linux-arm64-gnu: "npm:1.32.0" - lightningcss-linux-arm64-musl: "npm:1.32.0" - lightningcss-linux-x64-gnu: "npm:1.32.0" - lightningcss-linux-x64-musl: "npm:1.32.0" - lightningcss-win32-arm64-msvc: "npm:1.32.0" - lightningcss-win32-x64-msvc: "npm:1.32.0" - dependenciesMeta: - lightningcss-android-arm64: - optional: true - lightningcss-darwin-arm64: - optional: true - lightningcss-darwin-x64: - optional: true - lightningcss-freebsd-x64: - optional: true - lightningcss-linux-arm-gnueabihf: - optional: true - lightningcss-linux-arm64-gnu: - optional: true - lightningcss-linux-arm64-musl: - optional: true - lightningcss-linux-x64-gnu: - optional: true - lightningcss-linux-x64-musl: - optional: true - lightningcss-win32-arm64-msvc: - optional: true - lightningcss-win32-x64-msvc: - optional: true - checksum: 10/098e61007f0d0ec8b5c50884e33b543b551d1ff21bc7b062434b6638fd0b8596858f823b60dfc2a4aa756f3cb120ad79f2b7f4a55b1bda2c0269ab8cf476f114 - languageName: node - linkType: hard - "lines-and-columns@npm:2.0.3": version: 2.0.3 resolution: "lines-and-columns@npm:2.0.3" @@ -12033,7 +11324,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.17, magic-string@npm:^0.30.21": +"magic-string@npm:^0.30.17": version: 0.30.21 resolution: "magic-string@npm:0.30.21" dependencies: @@ -12166,13 +11457,6 @@ __metadata: languageName: node linkType: hard -"memorystream@npm:^0.3.1": - version: 0.3.1 - resolution: "memorystream@npm:0.3.1" - checksum: 10/2e34a1e35e6eb2e342f788f75f96c16f115b81ff6dd39e6c2f48c78b464dbf5b1a4c6ebfae4c573bd0f8dbe8c57d72bb357c60523be184655260d25855c03902 - languageName: node - linkType: hard - "meow@npm:^13.2.0": version: 13.2.0 resolution: "meow@npm:13.2.0" @@ -12563,35 +11847,6 @@ __metadata: languageName: node linkType: hard -"next-intl-swc-plugin-extractor@npm:^4.13.0": - version: 4.13.0 - resolution: "next-intl-swc-plugin-extractor@npm:4.13.0" - checksum: 10/b2f11364c4b9a8d8a82a04f97c742b294f6f3226ac85f8a7d549fd9a4e9520232575df188d017317ba2647170434d6001678e1efbb1011d11792c7f889289be5 - languageName: node - linkType: hard - -"next-intl@npm:^4.3.5": - version: 4.13.0 - resolution: "next-intl@npm:4.13.0" - dependencies: - "@formatjs/intl-localematcher": "npm:^0.8.1" - "@parcel/watcher": "npm:^2.4.1" - "@swc/core": "npm:^1.15.2" - icu-minify: "npm:^4.13.0" - negotiator: "npm:^1.0.0" - next-intl-swc-plugin-extractor: "npm:^4.13.0" - po-parser: "npm:^2.1.1" - use-intl: "npm:^4.13.0" - peerDependencies: - next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10/b0b7c53e63133068c9241cc8d54ac38a1a0b2d5599d9ade2ff0ddd5079ccbf5db5939bc185777ab2a7fe3649b849a1870e53967b4d8873f06231f989309323fc - languageName: node - linkType: hard - "next@npm:^16.2.0": version: 16.2.9 resolution: "next@npm:16.2.9" @@ -12673,15 +11928,6 @@ __metadata: languageName: node linkType: hard -"node-addon-api@npm:^7.0.0": - version: 7.1.1 - resolution: "node-addon-api@npm:7.1.1" - dependencies: - node-gyp: "npm:latest" - checksum: 10/ee1e1ed6284a2f8cd1d59ac6175ecbabf8978dcf570345e9a8095a9d0a2b9ced591074ae77f9009287b00c402352b38aa9322a34f2199cdc9f567b842a636b94 - languageName: node - linkType: hard - "node-exports-info@npm:^1.6.0": version: 1.6.0 resolution: "node-exports-info@npm:1.6.0" @@ -12997,27 +12243,6 @@ __metadata: languageName: node linkType: hard -"npm-run-all2@npm:^8.0.4": - version: 8.0.4 - resolution: "npm-run-all2@npm:8.0.4" - dependencies: - ansi-styles: "npm:^6.2.1" - cross-spawn: "npm:^7.0.6" - memorystream: "npm:^0.3.1" - picomatch: "npm:^4.0.2" - pidtree: "npm:^0.6.0" - read-package-json-fast: "npm:^4.0.0" - shell-quote: "npm:^1.7.3" - which: "npm:^5.0.0" - bin: - npm-run-all: bin/npm-run-all/index.js - npm-run-all2: bin/npm-run-all/index.js - run-p: bin/run-p/index.js - run-s: bin/run-s/index.js - checksum: 10/6e485f0d71fdfcf5bd872e5bbb8440ea5816ebeed88e6e39ff6e758d76adefa516b3101ed7ddc8de117d61b499f176417c87b0c595394e15733738834ad6ef0a - languageName: node - linkType: hard - "npm-run-path@npm:4.0.1, npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -13873,15 +13098,6 @@ __metadata: languageName: node linkType: hard -"pidtree@npm:^0.6.0": - version: 0.6.1 - resolution: "pidtree@npm:0.6.1" - bin: - pidtree: bin/pidtree.js - checksum: 10/2e543dac94bea903c29c57ec113244bb66e878245c8fa28b074e517c3947426dacee97a18e0dfdbcbde00909472d65a7fca9eaf722269b279f593eb582dc03d1 - languageName: node - linkType: hard - "pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" @@ -13919,13 +13135,6 @@ __metadata: languageName: node linkType: hard -"po-parser@npm:^2.1.1": - version: 2.1.1 - resolution: "po-parser@npm:2.1.1" - checksum: 10/7bcf0055f4441256fd1cd2c09c4e2512207385f6a1a8952d9f7911ffccd0366720cfbd2be7aa11fb337f4b72c8a53866b8ba5a42da65f6ec19401df68bac8997 - languageName: node - linkType: hard - "possible-typed-array-names@npm:^1.0.0, possible-typed-array-names@npm:^1.1.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" @@ -13954,7 +13163,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.15, postcss@npm:^8.5.6": +"postcss@npm:^8.5.6": version: 8.5.15 resolution: "postcss@npm:8.5.15" dependencies: @@ -14288,16 +13497,6 @@ __metadata: languageName: node linkType: hard -"read-package-json-fast@npm:^4.0.0": - version: 4.0.0 - resolution: "read-package-json-fast@npm:4.0.0" - dependencies: - json-parse-even-better-errors: "npm:^4.0.0" - npm-normalize-package-bin: "npm:^4.0.0" - checksum: 10/bf0becd7d0b652dcc5874b466d1dbd98313180e89505c072f35ff48a1ad6bdaf2427143301e1924d64e4af5064cda8be5df16f14de882f03130e29051bbaab87 - languageName: node - linkType: hard - "read-pkg-up@npm:^3.0.0": version: 3.0.0 resolution: "read-pkg-up@npm:3.0.0" @@ -15065,13 +14264,6 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.7.3": - version: 1.8.4 - resolution: "shell-quote@npm:1.8.4" - checksum: 10/a3e3796385f2cd5cf0b78207a4439f0c7395c0833fc75b2473084b5d298c109c5c0fa687fcd1c04e4b4484866e5bb8eaae7efae443b80fff71ea7e29baf11f0c - languageName: node - linkType: hard - "side-channel-list@npm:^1.0.1": version: 1.0.1 resolution: "side-channel-list@npm:1.0.1" @@ -15698,20 +14890,6 @@ __metadata: languageName: node linkType: hard -"tailwindcss@npm:4.3.1, tailwindcss@npm:^4": - version: 4.3.1 - resolution: "tailwindcss@npm:4.3.1" - checksum: 10/6e117b80d4b30ddc85cb91ecb2f0cdca825523186ffe09c92cd3dfba363f6b9eb71d4ce617e6d89c06f2cf93fc1ba12a0a61abc3068d37b376fdc9bc5be7c19e - languageName: node - linkType: hard - -"tapable@npm:^2.3.3": - version: 2.3.3 - resolution: "tapable@npm:2.3.3" - checksum: 10/21fb64a7ae1a0e11d855a6c33a22ae5ecf7e2f23170c942da673b44bf4c3aae8aa52451ef2792d0ce36c7feca13dceafa4f135105d66fc06912632488c0913fd - languageName: node - linkType: hard - "tar-stream@npm:2.2.0": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" @@ -16279,17 +15457,17 @@ __metadata: linkType: hard "typescript-eslint@npm:^8.46.0": - version: 8.61.0 - resolution: "typescript-eslint@npm:8.61.0" + version: 8.61.1 + resolution: "typescript-eslint@npm:8.61.1" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.61.0" - "@typescript-eslint/parser": "npm:8.61.0" - "@typescript-eslint/typescript-estree": "npm:8.61.0" - "@typescript-eslint/utils": "npm:8.61.0" + "@typescript-eslint/eslint-plugin": "npm:8.61.1" + "@typescript-eslint/parser": "npm:8.61.1" + "@typescript-eslint/typescript-estree": "npm:8.61.1" + "@typescript-eslint/utils": "npm:8.61.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10/5a21c6ef76400ea30a47629087787834abc1c17e4b406465dfd8c204ef635556f8e3a775d89c46f9eb175ebd6a218284685e935877a2b148c482f0478627bdf9 + checksum: 10/33a798da178f8942a5fb188a991a2eaa9047d7ca95178c67df2565531379b2a587c14ff836716a8b11ed3328c34af5e3b3ea985fa4d2a521a1bb605c2f0e0aa4 languageName: node linkType: hard @@ -16376,9 +15554,9 @@ __metadata: linkType: hard "undici@npm:^6.25.0": - version: 6.26.0 - resolution: "undici@npm:6.26.0" - checksum: 10/a1715ee4304f58fecd61e0a8c3bd7064435cfbc98b3ec1414dba5e89de97d436b7e88dd094b06ff8440428bf36b56163fc88972118890826039865edf58bdfcf + version: 6.27.0 + resolution: "undici@npm:6.27.0" + checksum: 10/30c18cdb235edf4dd36f8aa3ace1ffaf44060289a7d62ad44c33180d2d74a224015d25574812f62ce9c625b5beb1b0b766495b650fedf356aca11eed7ce2c816 languageName: node linkType: hard @@ -16533,20 +15711,6 @@ __metadata: languageName: node linkType: hard -"use-intl@npm:^4.13.0": - version: 4.13.0 - resolution: "use-intl@npm:4.13.0" - dependencies: - "@formatjs/fast-memoize": "npm:^3.1.0" - "@schummar/icu-type-parser": "npm:1.21.5" - icu-minify: "npm:^4.13.0" - intl-messageformat: "npm:^11.1.0" - peerDependencies: - react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 - checksum: 10/60bd67a6619294bd233bf376ef1e6ba508b3ac25bac72c469cb3c116be13b2eaa0e68195117a5adcf9b83bcc1c502ff02a15db7d7a1003c711a499302f994605 - languageName: node - linkType: hard - "username-sync@npm:^1.0.2": version: 1.0.3 resolution: "username-sync@npm:1.0.3" From 0ebb1afb7315a80587c64a2e1afa4381ffdafd1d Mon Sep 17 00:00:00 2001 From: Nikolaos Lazaridis <101863865+sc-nikolaoslazaridis@users.noreply.github.com> Date: Fri, 19 Jun 2026 18:17:06 +0300 Subject: [PATCH 32/32] fix: isEditing to true when in lowCode mode (#526) --- packages/content/src/client/sitecore-client.test.ts | 2 +- packages/content/src/client/sitecore-client.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/content/src/client/sitecore-client.test.ts b/packages/content/src/client/sitecore-client.test.ts index 9c0224d193..7bbe6e5f19 100644 --- a/packages/content/src/client/sitecore-client.test.ts +++ b/packages/content/src/client/sitecore-client.test.ts @@ -1374,7 +1374,7 @@ describe('SitecoreClient', () => { name: DesignLibraryMode.LowCode, isNormal: false, isPreview: false, - isEditing: false, + isEditing: true, isDesignLibrary: true, designLibrary: { isVariantGeneration: false, diff --git a/packages/content/src/client/sitecore-client.ts b/packages/content/src/client/sitecore-client.ts index a3a5f90544..340ea0f2ad 100644 --- a/packages/content/src/client/sitecore-client.ts +++ b/packages/content/src/client/sitecore-client.ts @@ -781,6 +781,7 @@ export class SitecoreClient implements BaseSitecoreClient { case DesignLibraryMode.LowCode: pageMode.isDesignLibrary = true; pageMode.designLibrary.isLowCode = true; + pageMode.isEditing = true; break; default: