diff --git a/ai-context/backend-developer-guide.md b/ai-context/backend-developer-guide.md index 51130b914db..39df85b3f09 100644 --- a/ai-context/backend-developer-guide.md +++ b/ai-context/backend-developer-guide.md @@ -107,19 +107,19 @@ export namespace UpdateUserUseCase { } ``` -You MUST ALWAYS use `createAbstraction` instead of `createAbstraction`. +You MUST ALWAYS use `createAbstraction` instead of `new Abstraction`. **Use case implementation** **CRITICAL RULES:** + 1. Use case class MUST implement the abstraction's `.Interface` type 2. Use case method return types MUST use the abstraction's `.Error` namespace type 3. Constructor parameters MUST use `.Interface` types from their abstractions -4. Always use `createImplementation` to wire up the use case +4. Always use `Abstraction.createImplementation` to wire up the use case ```typescript // UpdateUserUseCase.ts -import { createImplementation } from "@webiny/feature/api"; import { Result } from "@webiny/feature/api"; // Import abstraction you're implementing @@ -144,7 +144,10 @@ export class UpdateUserUseCase implements UseCaseAbstraction.Interface { } // Return type MUST use the abstraction's .Error namespace type - async execute(id: string, data: Record): Promise> { + async execute( + id: string, + data: Record + ): Promise> { // Implementation goes here const result = await this.repository.update(id, data); if (result.isFail()) { @@ -156,8 +159,7 @@ export class UpdateUserUseCase implements UseCaseAbstraction.Interface { } // Wire up with createImplementation -export const UpdateUserUseCaseImpl = createImplementation({ - abstraction: UseCaseAbstraction, +export const UpdateUserUseCaseImpl = UseCaseAbstraction.createImplementation({ implementation: UpdateUserUseCase, dependencies: [SupportedLanguagesProvider, UserRepository] }); @@ -197,8 +199,7 @@ class CreatePageValidationDecoratorImpl { } } -export const CreatePageValidationDecorator = createDecorator({ - abstraction: CreatePageUseCase, +export const CreatePageValidationDecorator = CreatePageUseCase.createDecorator({ decorator: CreatePageValidationDecoratorImpl, dependencies: [] }); @@ -234,50 +235,50 @@ Every feature should define domain-specific errors that extend `BaseError` from ### Error Definition Pattern -Create a `shared/errors.ts` file in your feature with domain-specific errors: +Create a `errors.ts` file in your feature with domain-specific errors. If using `data`, make sure there's a type defined and passed to base class generic: `BaseError`. ```typescript // features/apiKeys/shared/errors.ts import { BaseError } from "@webiny/feature/api"; // Wrap storage/infrastructure errors -export class ApiKeyStorageError extends BaseError { - override readonly code = "API_KEY_STORAGE_ERROR" as const; - - constructor(error: Error) { - super({ - message: error.message, - data: {} - }); - } +export class ApiKeyPersistenceError extends BaseError { + override readonly code = "API_KEY_STORAGE_ERROR" as const; + + constructor(error: Error) { + super({ + message: error.message, + data: {} + }); + } } // Domain-specific not found error export class ApiKeyNotFoundError extends BaseError { - override readonly code = "API_KEY_NOT_FOUND" as const; + override readonly code = "API_KEY_NOT_FOUND" as const; - constructor() { - super({ - message: `API key was not found!`, - data: {} - }); - } + constructor() { + super({ + message: `API key was not found!`, + data: {} + }); + } } // Authorization error with optional message type NotAuthorizedErrorData = { - message?: string; + message?: string; }; export class NotAuthorizedError extends BaseError { - override readonly code = "NOT_AUTHORIZED" as const; + override readonly code = "NOT_AUTHORIZED" as const; - constructor(data: NotAuthorizedErrorData = {}) { - super({ - message: data.message || "Not authorized to perform this action", - data - }); - } + constructor(data: NotAuthorizedErrorData = {}) { + super({ + message: data.message || "Not authorized to perform this action", + data + }); + } } ``` @@ -289,11 +290,11 @@ Define error unions in your abstraction files to provide type safety: // features/apiKeys/shared/abstractions.ts import { createAbstraction } from "@webiny/feature/api"; import { Result } from "@webiny/feature/api"; -import { ApiKeyNotFoundError, ApiKeyStorageError } from "./errors.js"; +import { ApiKeyNotFoundError, ApiKeyPersistenceError } from "./errors.js"; // Define possible errors for this repository export interface IApiKeysRepositoryErrors { - base: ApiKeyNotFoundError | ApiKeyStorageError; + base: ApiKeyNotFoundError | ApiKeyPersistenceError; } // Create error union type @@ -301,15 +302,15 @@ type RepositoryError = IApiKeysRepositoryErrors[keyof IApiKeysRepositoryErrors]; // Use in interface export interface IApiKeysRepository { - get(id: string): Promise>; - create(data: ApiKey): Promise>; + get(id: string): Promise>; + create(data: ApiKey): Promise>; } export const ApiKeysRepository = createAbstraction("ApiKeysRepository"); export namespace ApiKeysRepository { - export type Interface = IApiKeysRepository; - export type Error = RepositoryError; // Export for consumers + export type Interface = IApiKeysRepository; + export type Error = RepositoryError; // Export for consumers } ``` @@ -319,28 +320,29 @@ export namespace ApiKeysRepository { // features/apiKeys/shared/ApiKeysRepository.ts import { Result } from "@webiny/feature/api"; import { ApiKeysRepository as RepositoryAbstraction } from "./abstractions.js"; -import { ApiKeyNotFoundError, ApiKeyStorageError } from "./errors.js"; +import { ApiKeyNotFoundError, ApiKeyPersistenceError } from "./errors.js"; class ApiKeysRepositoryImpl implements RepositoryAbstraction.Interface { - async get(id: string): Promise> { - try { - const apiKey = await this.storageOperations.getApiKey({ id }); - if (apiKey) { - return Result.ok(apiKey); - } - // Return domain-specific error - return Result.fail(new ApiKeyNotFoundError()); - } catch (error) { - // Wrap infrastructure errors - return Result.fail(new ApiKeyStorageError(error as Error)); - } + async get(id: string): Promise> { + try { + const apiKey = await this.storageOperations.getApiKey({ id }); + if (apiKey) { + return Result.ok(apiKey); + } + // Return domain-specific error + return Result.fail(new ApiKeyNotFoundError()); + } catch (error) { + // Wrap infrastructure errors + return Result.fail(new ApiKeyPersistenceError(error as Error)); } + } } ``` ### Error Handling Benefits This pattern provides: + - **Type safety** - Consumers know exactly which errors can occur - **Domain context** - Specific error codes and messages - **Consistent wrapping** - Infrastructure errors wrapped in domain errors @@ -348,11 +350,10 @@ This pattern provides: ### Use Case Error Handling Pattern -**CRITICAL:** Use cases MUST extend repository errors with their own use-case-specific errors. - Every use case abstraction must define: + 1. An extendable error interface for use-case-specific errors -2. A union type combining use-case errors with repository errors +2. A union type combining domain errors that can be returned from the use case 3. An exported error type in the namespace ```typescript @@ -364,28 +365,29 @@ import { NotAuthorizedError, ApiKeyValidationError } from "../shared/errors.js"; // 1. Define extendable interface for use-case-specific errors export interface ICreateApiKeyErrors { - notAuthorized: NotAuthorizedError; - validation: ApiKeyValidationError; + notAuthorized: NotAuthorizedError; + validation: ApiKeyValidationError; } // 2. Create union of use-case errors + repository errors -type CreateApiKeyError = ICreateApiKeyErrors[keyof ICreateApiKeyErrors] | ApiKeysRepository.Error; +type CreateApiKeyError = ICreateApiKeyErrors[keyof ICreateApiKeyErrors]; // 3. Use in interface export interface ICreateApiKey { - execute(input: CreateApiKeyInput): Promise>; + execute(input: CreateApiKeyInput): Promise>; } export const CreateApiKey = createAbstraction("CreateApiKey"); // 4. Export error type in namespace export namespace CreateApiKey { - export type Interface = ICreateApiKey; - export type Error = CreateApiKeyError; // Consumers can use CreateApiKey.Error + export type Interface = ICreateApiKey; + export type Error = CreateApiKeyError; // Consumers can use CreateApiKey.Error } ``` This pattern ensures: + - All repository errors (storage, not found, etc.) are automatically included - Use case can add its own specific errors (validation, authorization, business rules) - Type safety throughout the error handling chain @@ -400,30 +402,30 @@ import { ApiKeysRepository } from "../shared/abstractions.js"; import { NotAuthorizedError, ApiKeyValidationError } from "../shared/errors.js"; class CreateApiKeyUseCaseImpl implements CreateApiKey.Interface { - constructor( - private identityContext: IdentityContext.Interface, - private repository: ApiKeysRepository.Interface - ) {} - - async execute(input: CreateApiKeyInput): Promise> { - // Use-case specific error - if (!hasPermission) { - return Result.fail(new NotAuthorizedError()); - } - - // Validation error - if (!validation.success) { - return Result.fail(new ApiKeyValidationError(validation.error.message)); - } - - // Repository errors are automatically included in CreateApiKey.Error - const result = await this.repository.create(apiKey); - if (result.isFail()) { - return Result.fail(result.error); // Could be ApiKeyStorageError - } - - return Result.ok(result.value); + constructor( + private identityContext: IdentityContext.Interface, + private repository: ApiKeysRepository.Interface + ) {} + + async execute(input: CreateApiKeyInput): Promise> { + // Use-case specific error + if (!hasPermission) { + return Result.fail(new NotAuthorizedError()); + } + + // Validation error + if (!validation.success) { + return Result.fail(new ApiKeyValidationError(validation.error.message)); } + + // Repository errors are domain errors, so it is safe to return them as-is. + const result = await this.repository.create(apiKey); + if (result.isFail()) { + return Result.fail(result.error); // Could be ApiKeyPersistenceError + } + + return Result.ok(result.value); + } } ``` @@ -440,7 +442,7 @@ return Result.fail(new DomainSpecificError()); // Check result if (result.isFail()) { - return Result.fail(result.error); + return Result.fail(result.error); } // Access value @@ -456,20 +458,21 @@ Never use `result.isError()`, `result.getError()`, or `result.getValue()` - thes ```typescript // shared/errors.ts export class ApiKeyValidationError extends BaseError<{ message: string }> { - override readonly code = "API_KEY_VALIDATION_ERROR" as const; - - constructor(message: string) { - super({ - message, - data: { message } - }); - } + // E.g. Cms/Model/ValidationError + override readonly code = "{App}/{Domain}/ValidationError" as const; + + constructor(message: string) { + super({ + message, + data: { message } + }); + } } // In use case const validation = schema.safeParse(input); if (!validation.success) { - return Result.fail(new ApiKeyValidationError(validation.error.errors[0].message)); + return Result.fail(new ApiKeyValidationError(validation.error.errors[0].message)); } ``` @@ -486,44 +489,42 @@ Events must follow this structure with handler abstractions: ```typescript // features/teams/CreateTeam/events.ts import { createAbstraction } from "@webiny/feature/api"; -import { DomainEvent } from "@webiny/api-core"; -import type { IEventHandler } from "@webiny/api-core"; +import { DomainEvent } from "@webiny/api-core/features/EventPublisher"; +import type { IEventHandler } from "@webiny/api-core/features/EventPublisher"; import type { TeamBeforeCreatePayload, TeamAfterCreatePayload } from "./abstractions.js"; // Before event with handler abstraction export class TeamBeforeCreateEvent extends DomainEvent { - eventType = "team.beforeCreate" as const; + eventType = "team.beforeCreate" as const; - getHandlerAbstraction() { - return TeamBeforeCreateHandler; - } + getHandlerAbstraction() { + return TeamBeforeCreateHandler; + } } -export const TeamBeforeCreateHandler = createAbstraction>( - "TeamBeforeCreateHandler" -); +export const TeamBeforeCreateHandler = + createAbstraction>("TeamBeforeCreateHandler"); export namespace TeamBeforeCreateHandler { - export type Interface = IEventHandler; - export type Event = TeamBeforeCreateEvent; + export type Interface = IEventHandler; + export type Event = TeamBeforeCreateEvent; } // After event with handler abstraction export class TeamAfterCreateEvent extends DomainEvent { - eventType = "team.afterCreate" as const; + eventType = "team.afterCreate" as const; - getHandlerAbstraction() { - return TeamAfterCreateHandler; - } + getHandlerAbstraction() { + return TeamAfterCreateHandler; + } } -export const TeamAfterCreateHandler = createAbstraction>( - "TeamAfterCreateHandler" -); +export const TeamAfterCreateHandler = + createAbstraction>("TeamAfterCreateHandler"); export namespace TeamAfterCreateHandler { - export type Interface = IEventHandler; - export type Event = TeamAfterCreateEvent; + export type Interface = IEventHandler; + export type Event = TeamAfterCreateEvent; } ``` @@ -534,13 +535,13 @@ Define event payloads in the abstraction file: ```typescript // features/teams/CreateTeam/abstractions.ts export interface TeamBeforeCreatePayload { - team: Team; - input: CreateTeamInput; + team: Team; + input: CreateTeamInput; } export interface TeamAfterCreatePayload { - team: Team; - input: CreateTeamInput; + team: Team; + input: CreateTeamInput; } ``` @@ -550,46 +551,44 @@ Publish events from use cases using EventPublisher: ```typescript // features/teams/CreateTeam/CreateTeamUseCase.ts -import { EventPublisher } from "@webiny/api-core"; +import { EventPublisher } from "@webiny/api-core/features/EventPublisher"; import { TeamBeforeCreateEvent, TeamAfterCreateEvent } from "./events.js"; class CreateTeamUseCaseImpl implements CreateTeam.Interface { - constructor( - private eventPublisher: EventPublisher.Interface, - private repository: TeamsRepository.Interface - ) {} - - async execute(input: CreateTeamInput): Promise> { - const team = createTeamFromInput(input); - - // Publish before event - await this.eventPublisher.publish( - new TeamBeforeCreateEvent({ team, input }) - ); - - const result = await this.repository.create(team); - if (result.isFail()) { - return Result.fail(result.error); - } - - // Publish after event - await this.eventPublisher.publish( - new TeamAfterCreateEvent({ team, input }) - ); - - return Result.ok(team); + constructor( + private eventPublisher: EventPublisher.Interface, + private repository: TeamsRepository.Interface + ) {} + + async execute(input: CreateTeamInput): Promise> { + const team = createTeamFromInput(input); + + // Publish before event + await this.eventPublisher.publish(new TeamBeforeCreateEvent({ team, input })); + + const result = await this.repository.create(team); + if (result.isFail()) { + return Result.fail(result.error); } + + // Publish after event + await this.eventPublisher.publish(new TeamAfterCreateEvent({ team, input })); + + return Result.ok(team); + } } ``` ### Event Naming Convention Follow these naming conventions: + - Event types use `entityName.action` format (e.g., `"team.beforeCreate"`, `"team.afterUpdate"`) - Handler abstractions use `EntityActionHandler` format (e.g., `TeamBeforeCreateHandler`) - Event classes use `EntityActionEvent` format (e.g., `TeamBeforeCreateEvent`) **Critical Rules:** + 1. **ALWAYS create handler abstractions** - Every event must have a `createAbstraction` for its handler 2. **Use `eventType` property** - Not `static type` 3. **Implement `getHandlerAbstraction()`** - This method returns the handler abstraction @@ -597,24 +596,26 @@ Follow these naming conventions: 5. **ALWAYS export namespace with Interface and Event types** - Every handler abstraction MUST export a namespace containing both the Interface type and the Event class **Example of correct namespace export:** + ```typescript -export const TeamBeforeCreateHandler = createAbstraction>( - "TeamBeforeCreateHandler" -); +export const TeamBeforeCreateHandler = + createAbstraction>("TeamBeforeCreateHandler"); export namespace TeamBeforeCreateHandler { - export type Interface = IEventHandler; - export type Event = TeamBeforeCreateEvent; + export type Interface = IEventHandler; + export type Event = TeamBeforeCreateEvent; } ``` This namespace pattern enables: + - Type-safe access to handler interface via `TeamBeforeCreateHandler.Interface` - Type-safe access to event class via `TeamBeforeCreateHandler.Event` - Proper event handler registration in DI container - Clear coupling between events and their handlers This pattern enables: + - Type-safe event handling with DI - Decoupled event producers and consumers - Testable event handlers diff --git a/ai-context/core-features-reference.md b/ai-context/core-features-reference.md index 43d22007c63..10290ee2ea0 100644 --- a/ai-context/core-features-reference.md +++ b/ai-context/core-features-reference.md @@ -12,23 +12,23 @@ This document provides the correct import paths and type definitions for commonl ## Features ### TenantContext -- **Import:** `import { TenantContext } from "@webiny/api-tenancy/features/TenantContext"` -- **Interface Type:** See `packages/api-tenancy/src/features/TenantContext/abstractions.ts` +- **Import:** `import { TenantContext } from "@webiny/api-core/features/TenantContext"` +- **Interface Type:** See `packages/api-core/src/features/TenantContext/abstractions.ts` - **Usage:** Access current tenant information ### IdentityContext -- **Import:** `import { IdentityContext } from "@webiny/api-security/features/IdentityContext"` -- **Interface Type:** See `packages/api-security/src/features/IdentityContext/abstractions.ts` +- **Import:** `import { IdentityContext } from "@webiny/api-core/features/IdentityContext"` +- **Interface Type:** See `packages/api-core/src/features/IdentityContext/abstractions.ts` - **Usage:** Access current user identity and permissions ### EventPublisher -- **Import:** `import { EventPublisher } from "@webiny/api-core"` +- **Import:** `import { EventPublisher } from "@webiny/api-core/features/EventPublisher"` - **Interface Type:** See `packages/api-core/src/event-publisher/abstractions.ts` - **Usage:** Publish domain events ### WcpContext -- **Import:** `import { WcpContext } from "@webiny/api-wcp/features/WcpContext"` -- **Interface Type:** See `packages/api-wcp/src/features/WcpContext/abstractions.ts` +- **Import:** `import { WcpContext } from "@webiny/api-core/features/WcpContext"` +- **Interface Type:** See `packages/api-core/src/features/WcpContext/abstractions.ts` - **Usage:** WCP (Webiny Control Panel) integration for seats/tenants management ### GetSettings @@ -43,6 +43,94 @@ This document provides the correct import paths and type definitions for commonl --- +## Headless CMS Features + +### Content Entry Features + +#### GetEntryById +- **Import:** `import { GetEntryByIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetEntryById"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/GetEntryById/abstractions.ts` +- **Usage:** Fetch single entry by exact revision ID + +#### GetEntry +- **Import:** `import { GetEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetEntry"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/GetEntry/abstractions.ts` +- **Usage:** Get single entry by query parameters (where + sort) + +#### ListLatestEntries +- **Import:** `import { ListLatestEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/ListEntries/abstractions.ts` +- **Usage:** List latest entries (manage API) + +#### ListPublishedEntries +- **Import:** `import { ListPublishedEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/ListEntries/abstractions.ts` +- **Usage:** List published entries (read API) + +#### ListDeletedEntries +- **Import:** `import { ListDeletedEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/ListEntries/abstractions.ts` +- **Usage:** List deleted entries (manage API) + +#### CreateEntry +- **Import:** `import { CreateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/CreateEntry"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/CreateEntry/abstractions.ts` +- **Usage:** Create new content entry + +#### UpdateEntry +- **Import:** `import { UpdateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/UpdateEntry"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/UpdateEntry/abstractions.ts` +- **Usage:** Update existing content entry + +#### DeleteEntry +- **Import:** `import { DeleteEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/DeleteEntry"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/DeleteEntry/abstractions.ts` +- **Usage:** Delete content entry + +#### ListEntriesRepository +- **Import:** `import { ListEntriesRepository } from "@webiny/api-headless-cms/features/contentEntry/ListEntries"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentEntry/ListEntries/abstractions.ts` +- **Usage:** Repository for fetching entries from storage + +### Content Model Features + +#### GetModel +- **Import:** `import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/GetModel/abstractions.ts` +- **Usage:** Retrieve single model by ID with access control + +#### ListModels +- **Import:** `import { ListModelsUseCase } from "@webiny/api-headless-cms/features/contentModel/ListModels"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/ListModels/abstractions.ts` +- **Usage:** List all accessible content models + +#### GetModelRepository +- **Import:** `import { GetModelRepository } from "@webiny/api-headless-cms/features/contentModel/GetModel"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/GetModel/abstractions.ts` +- **Usage:** Fetch model from cache (plugin + DB models) + +#### ListModelsRepository +- **Import:** `import { ListModelsRepository } from "@webiny/api-headless-cms/features/contentModel/ListModels"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/ListModels/abstractions.ts` +- **Usage:** Fetch all models from cache + +#### ModelsFetcher +- **Import:** `import { ModelsFetcher } from "@webiny/api-headless-cms/features/contentModel/shared"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/shared/abstractions.ts` +- **Usage:** Centralized model fetching with caching and access control + +#### ModelCache +- **Import:** `import { ModelCache } from "@webiny/api-headless-cms/features/contentModel/shared"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/shared/abstractions.ts` +- **Usage:** Cache for content models + +#### PluginModelsProvider +- **Import:** `import { PluginModelsProvider } from "@webiny/api-headless-cms/features/contentModel/shared"` +- **Interface Type:** See `packages/api-headless-cms/src/features/contentModel/shared/abstractions.ts` +- **Usage:** Access to plugin-defined models + +--- + ## Notes - Always import abstractions from the feature path (not from package root) diff --git a/ai-context/di-container.md b/ai-context/di-container.md index 1ea3a4e13a9..5adf82017cf 100644 --- a/ai-context/di-container.md +++ b/ai-context/di-container.md @@ -1,16 +1,61 @@ ```ts // Abstraction.ts +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import type { Constructor, Dependencies, GetInterface, MapDependencies } from "./types.js"; +import { Metadata } from "./Metadata.js"; + +type DropLast = T extends [...infer P, any] ? [...P] : never; + +type Implementation, I extends Constructor> = I & { + __abstraction: A; +}; + // eslint-disable-next-line @typescript-eslint/no-unused-vars export class Abstraction { public readonly token: symbol; constructor(name: string) { - this.token = Symbol.for(name); + this.token = Symbol(name); } toString(): string { return this.token.description || this.token.toString(); } + + createImplementation>>(params: { + implementation: I; + dependencies: Dependencies; + }): Implementation { + const metadata = new Metadata(params.implementation); + metadata.setAbstraction(this); + metadata.setDependencies(params.dependencies); + + return params.implementation as Implementation; + } + + createDecorator(params: { + decorator: I; + dependencies: MapDependencies>>; + }): Implementation { + const metadata = new Metadata(params.decorator); + metadata.setAbstraction(this); + metadata.setDependencies(params.dependencies as any); + metadata.setAttribute("IS_DECORATOR", true); + + return params.decorator as Implementation; + } + + createComposite>>(params: { + implementation: I; + dependencies: Dependencies; + }): Implementation { + const metadata = new Metadata(params.implementation); + metadata.setAbstraction(this); + metadata.setDependencies(params.dependencies); + metadata.setAttribute("IS_COMPOSITE", true); + + return params.implementation as Implementation; + } } ``` diff --git a/ai-context/event-publisher.md b/ai-context/event-publisher.md index d5bcb2ed6fd..e31a8024d50 100644 --- a/ai-context/event-publisher.md +++ b/ai-context/event-publisher.md @@ -24,7 +24,7 @@ export class EventPublisher implements Abstraction.Interface { ```ts // __tests__/EventPublisher.test.ts import { describe, it, expect, beforeEach } from "vitest"; -import { Container, Abstraction, createImplementation } from "@webiny/di"; +import { Container, Abstraction } from "@webiny/di"; import { EventPublisherFeature } from "../feature"; import { EventPublisher as EventPublisherAbstraction } from "../abstractions"; import type { DomainEvent, IEventHandler } from "../abstractions"; @@ -123,8 +123,7 @@ class SendNotificationHandler implements IEventHandler { } } -export const SendNotificationHandlerImpl = createImplementation({ - abstraction: PagePublishedHandler, +export const SendNotificationHandlerImpl = PagePublishedHandler.createImplementation({ implementation: SendNotificationHandler, dependencies: [] }); @@ -138,8 +137,7 @@ class UpdateSearchIndexHandler implements IEventHandler { } } -export const UpdateSearchIndexHandlerImpl = createImplementation({ - abstraction: PagePublishedHandler, +export const UpdateSearchIndexHandlerImpl = PagePublishedHandler.createImplementation({ implementation: UpdateSearchIndexHandler, dependencies: [] }); @@ -155,8 +153,7 @@ class LogPagePublishedHandler implements IEventHandler { } } -export const LogPagePublishedHandlerImpl = createImplementation({ - abstraction: PagePublishedHandler, +export const LogPagePublishedHandlerImpl = PagePublishedHandler.createImplementation({ implementation: LogPagePublishedHandler, dependencies: [] }); @@ -174,8 +171,7 @@ class SendWelcomeEmailHandler implements IEventHandler { } } -export const SendWelcomeEmailHandlerImpl = createImplementation({ - abstraction: UserRegisteredHandler, +export const SendWelcomeEmailHandlerImpl = UserRegisteredHandler.createImplementation({ implementation: SendWelcomeEmailHandler, dependencies: [] }); @@ -189,8 +185,7 @@ class CreateUserProfileHandler implements IEventHandler { } } -export const CreateUserProfileHandlerImpl = createImplementation({ - abstraction: UserRegisteredHandler, +export const CreateUserProfileHandlerImpl = UserRegisteredHandler.createImplementation({ implementation: CreateUserProfileHandler, dependencies: [] }); @@ -204,8 +199,7 @@ class TrackUserRegistrationHandler implements IEventHandler } } -export const TrackUserRegistrationHandlerImpl = createImplementation({ - abstraction: UserRegisteredHandler, +export const TrackUserRegistrationHandlerImpl = UserRegisteredHandler.createImplementation({ implementation: TrackUserRegistrationHandler, dependencies: [] }); @@ -223,8 +217,7 @@ class ProcessPaymentHandler implements IEventHandler { } } -export const ProcessPaymentHandlerImpl = createImplementation({ - abstraction: OrderPlacedHandler, +export const ProcessPaymentHandlerImpl = OrderPlacedHandler.createImplementation({ implementation: ProcessPaymentHandler, dependencies: [] }); @@ -238,8 +231,7 @@ class SendOrderConfirmationHandler implements IEventHandler { } } -export const SendOrderConfirmationHandlerImpl = createImplementation({ - abstraction: OrderPlacedHandler, +export const SendOrderConfirmationHandlerImpl = OrderPlacedHandler.createImplementation({ implementation: SendOrderConfirmationHandler, dependencies: [] }); @@ -253,8 +245,7 @@ class UpdateInventoryHandler implements IEventHandler { } } -export const UpdateInventoryHandlerImpl = createImplementation({ - abstraction: OrderPlacedHandler, +export const UpdateInventoryHandlerImpl = OrderPlacedHandler.createImplementation({ implementation: UpdateInventoryHandler, dependencies: [] }); @@ -541,8 +532,7 @@ describe("EventPublisher", () => { } } - const FailingHandlerImpl = createImplementation({ - abstraction: PagePublishedHandler, + const FailingHandlerImpl = PagePublishedHandler.createImplementation({ implementation: FailingHandler, dependencies: [] }); diff --git a/ai-context/simple-models.md b/ai-context/simple-models.md index 6b7330b7fca..e7432e289b7 100644 --- a/ai-context/simple-models.md +++ b/ai-context/simple-models.md @@ -1,7 +1,6 @@ ```ts // Page/PageModelBuilder.ts import { ModelBuilder as Builder } from "~/models/ModelBuilder.js"; -import { createImplementation } from "@webiny/di"; import { PageSchema, PageModelBuilder as BuilderAbstraction, type IPage } from "./abstractions"; class PageModelBuilderImpl implements BuilderAbstraction.Interface { @@ -15,8 +14,7 @@ class PageModelBuilderImpl implements BuilderAbstraction.Interface { } } -export const PageModelBuilder = createImplementation({ - abstraction: BuilderAbstraction, +export const PageModelBuilder = BuilderAbstraction.createImplementation({ implementation: PageModelBuilderImpl, dependencies: [] }); @@ -24,7 +22,6 @@ export const PageModelBuilder = createImplementation({ ```ts // Page/PageModelFactory.ts -import { createImplementation } from "@webiny/di"; import { PageModelFactory as FactoryAbstraction, PageModelBuilder, @@ -49,8 +46,7 @@ class PageModelFactoryImpl implements FactoryAbstraction.Interface { } } -export const PageModelFactory = createImplementation({ - abstraction: FactoryAbstraction, +export const PageModelFactory = FactoryAbstraction.createImplementation({ implementation: PageModelFactoryImpl, dependencies: [PageModelBuilder] }); @@ -58,7 +54,6 @@ export const PageModelFactory = createImplementation({ ```ts // Page/__tests__/PageModelBuilderDecorator.ts -import { createDecorator } from "@webiny/di"; import { PageModelBuilder as BuilderAbstraction } from "../abstractions"; class PageModelBuilderDecoratorImpl implements BuilderAbstraction.Interface { @@ -81,8 +76,7 @@ class PageModelBuilderDecoratorImpl implements BuilderAbstraction.Interface { } } -export const PageModelBuilderDecorator = createDecorator({ - abstraction: BuilderAbstraction, +export const PageModelBuilderDecorator = BuilderAbstraction.createDecorator({ decorator: PageModelBuilderDecoratorImpl, dependencies: [] }); @@ -100,7 +94,6 @@ declare module "~/simple/Page/abstractions.js" { ```ts // Page/__tests__/PageModelBuilderDecorator2.ts -import { createDecorator } from "@webiny/di"; import { PageModelBuilder as BuilderAbstraction } from "../abstractions"; class PageModelBuilderDecorator2Impl implements BuilderAbstraction.Interface { @@ -117,8 +110,7 @@ class PageModelBuilderDecorator2Impl implements BuilderAbstraction.Interface { } } -export const PageModelBuilderDecorator2 = createDecorator({ - abstraction: BuilderAbstraction, +export const PageModelBuilderDecorator2 = BuilderAbstraction.createDecorator({ decorator: PageModelBuilderDecorator2Impl, dependencies: [] }); diff --git a/package.json b/package.json index e761babdb54..0e065882ee1 100644 --- a/package.json +++ b/package.json @@ -258,7 +258,7 @@ "packageManager": "yarn@4.10.2", "dependencies": { "@types/hoist-non-react-statics": "^3.3.7", - "@webiny/di": "^0.2.1" + "@webiny/di": "^0.2.3" }, "engines": { "node": ">=22.0.0" diff --git a/packages/admin-ui/src/DataTable/DataTable.tsx b/packages/admin-ui/src/DataTable/DataTable.tsx index ada54d034bd..8378e81a070 100644 --- a/packages/admin-ui/src/DataTable/DataTable.tsx +++ b/packages/admin-ui/src/DataTable/DataTable.tsx @@ -206,6 +206,10 @@ const defineColumns = ( ...firstColumn, accessorKey: firstColumn.id as string, header: props => { + if (!props) { + return null; + } + return (
( ); }, cell: props => { + if (!props) { + return null; + } return (
=> { __type: { type: "string" }, - webinyVersion: { - type: "string" - }, tenant: { type: "string" }, diff --git a/packages/api-headless-cms-ddb/src/definitions/group.ts b/packages/api-headless-cms-ddb/src/definitions/group.ts index e56accf3014..84debb9e1cd 100644 --- a/packages/api-headless-cms-ddb/src/definitions/group.ts +++ b/packages/api-headless-cms-ddb/src/definitions/group.ts @@ -22,9 +22,6 @@ export const createGroupEntity = (params: Params): Entity => { TYPE: { type: "string" }, - webinyVersion: { - type: "string" - }, id: { type: "string" }, diff --git a/packages/api-headless-cms-ddb/src/operations/model/index.ts b/packages/api-headless-cms-ddb/src/operations/model/index.ts index 1d2aff62485..f6ba30eabca 100644 --- a/packages/api-headless-cms-ddb/src/operations/model/index.ts +++ b/packages/api-headless-cms-ddb/src/operations/model/index.ts @@ -14,6 +14,7 @@ import { cleanupItem } from "@webiny/db-dynamodb/utils/cleanup.js"; import type { QueryAllParams } from "@webiny/db-dynamodb/utils/query.js"; import { queryAllClean } from "@webiny/db-dynamodb/utils/query.js"; import { deleteItem, put } from "@webiny/db-dynamodb"; +import { convertException } from "@webiny/utils"; interface PartitionKeysParams { tenant: string; @@ -76,7 +77,7 @@ export const createModelsStorageOperations = ( return model; } catch (ex) { throw new WebinyError(`Could not create CMS Content Model.`, "CREATE_MODEL_ERROR", { - error: ex, + error: convertException(ex), model, keys }); diff --git a/packages/api-headless-cms-workflows/__tests__/__cms/models.ts b/packages/api-headless-cms-workflows/__tests__/__cms/models.ts new file mode 100644 index 00000000000..47050cb8365 --- /dev/null +++ b/packages/api-headless-cms-workflows/__tests__/__cms/models.ts @@ -0,0 +1,40 @@ +import { createModelGroupPlugin, createModelPlugin } from "@webiny/api-headless-cms"; + +export const groupPlugin = createModelGroupPlugin({ + id: "default", + name: "Default", + description: "", + slug: "default", + icon: "fa/fas" +}); + +export const group = groupPlugin.contentModelGroup; + +export const modelPlugin = createModelPlugin({ + modelId: "author", + name: "Author", + group: { + id: group.id, + name: group.name + }, + titleFieldId: "name", + description: "", + singularApiName: "Author", + pluralApiName: "Authors", + layout: [["name"]], + fields: [ + { + id: "name", + storageId: "text@name", + fieldId: "name", + type: "text", + label: "Name" + } + ] +}); + +export const model = modelPlugin.contentModel; + +export const createModelsPlugins = () => { + return [modelPlugin, groupPlugin]; +}; diff --git a/packages/api-headless-cms-workflows/__tests__/__handler/context.ts b/packages/api-headless-cms-workflows/__tests__/__handler/context.ts new file mode 100644 index 00000000000..cea351aeb29 --- /dev/null +++ b/packages/api-headless-cms-workflows/__tests__/__handler/context.ts @@ -0,0 +1,15 @@ +import { useContextHandler, type UseContextHandlerParams } from "@webiny/testing"; +import { createModelsPlugins } from "../__cms/models.js"; +import { PluginsContainer } from "@webiny/plugins"; +import type { Context } from "~/types.js"; +import { createHeadlessCmsWorkflows } from "~/index.js"; +import { createWorkflows } from "@webiny/api-workflows"; + +export const createContextHandler = (params?: UseContextHandlerParams) => { + const container = new PluginsContainer(params?.plugins || []); + container.register([...createModelsPlugins(), createWorkflows(), createHeadlessCmsWorkflows()]); + return useContextHandler({ + ...params, + plugins: container.all() + }); +}; diff --git a/packages/api-headless-cms-workflows/__tests__/__workflows/workflow.ts b/packages/api-headless-cms-workflows/__tests__/__workflows/workflow.ts new file mode 100644 index 00000000000..c1065b243e5 --- /dev/null +++ b/packages/api-headless-cms-workflows/__tests__/__workflows/workflow.ts @@ -0,0 +1,24 @@ +import { FULL_ACCESS_TEAM_ID } from "@webiny/testing"; +import type { Context } from "~/types.js"; + +export const createWorkflow = async (context: Pick) => { + const id = `workflow-1`; + + const workflow = await context.workflows.storeWorkflow("test", id, { + name: "Test Workflow", + steps: [ + { + id: "step-1", + title: "Step 1", + description: "This is step 1", + color: "blue", + teams: [{ id: FULL_ACCESS_TEAM_ID }], + notifications: [{ id: "notif-1" }] + } + ] + }); + return { + id, + workflow + }; +}; diff --git a/packages/api-headless-cms-workflows/__tests__/state/state.onAfterCreate.test.ts b/packages/api-headless-cms-workflows/__tests__/state/state.onAfterCreate.test.ts new file mode 100644 index 00000000000..8b6ffdd0d9f --- /dev/null +++ b/packages/api-headless-cms-workflows/__tests__/state/state.onAfterCreate.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import { createContextHandler } from "~tests/__handler/context.js"; +import { model as modelDefinition } from "~tests/__cms/models.js"; +import { createWorkflow } from "~tests/__workflows/workflow.js"; + +describe("state onAfterCreate", () => { + it("should attach a state to cms entry", async () => { + const { context: createContext } = createContextHandler(); + + const context = await createContext(); + + const { workflow } = await createWorkflow(context); + + const model = await context.cms.getModel(modelDefinition.modelId); + + const entry = await context.cms.createEntry(model, { + name: "John Doe" + }); + + const [items] = await context.cms.listLatestEntries(model); + + expect(items).toMatchObject([ + { + id: entry.id, + values: { + name: "John Doe" + } + } + ]); + + const state = await context.workflowState.createState( + workflow.app, + entry.id, + `CMS: ${entry.values.name}` + ); + expect(state.app).toEqual(workflow.app); + expect(state.targetRevisionId).toEqual(entry.id); + + const [itemsAfterState] = await context.cms.listLatestEntries(model); + + expect(itemsAfterState).toMatchObject([ + { + id: entry.id, + values: { + name: "John Doe" + } + } + ]); + }); +}); diff --git a/packages/api-headless-cms-workflows/src/utils/state.ts b/packages/api-headless-cms-workflows/src/utils/state.ts index 533f20b4581..9f4708e5f20 100644 --- a/packages/api-headless-cms-workflows/src/utils/state.ts +++ b/packages/api-headless-cms-workflows/src/utils/state.ts @@ -1,11 +1,8 @@ import type { IEntryState } from "@webiny/api-headless-cms/types/index.js"; import type { IWorkflowStateModel } from "@webiny/api-workflows/context/abstractions/WorkflowState.js"; -export const getStateValues = (state: IWorkflowStateModel): IEntryState | undefined => { - const activeStep = state.getActiveStep(); - if (!activeStep) { - return undefined; - } +export const getStateValues = (state: IWorkflowStateModel): IEntryState => { + const activeStep = state.currentStep; return { workflowId: state.workflowId, diff --git a/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts b/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts index f9e9d9e3c9a..c9cf37c4141 100644 --- a/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts +++ b/packages/api-headless-cms/__tests__/contentAPI/contentEntryMetaField.test.ts @@ -117,7 +117,6 @@ describe("Content Entry Meta Field", () => { slug: "test-category" }, status: "draft", - webinyVersion: "5.27.0", meta: createMetaData() }); diff --git a/packages/api-headless-cms/package.json b/packages/api-headless-cms/package.json index 5beec44b84f..19d1b1800bb 100644 --- a/packages/api-headless-cms/package.json +++ b/packages/api-headless-cms/package.json @@ -24,7 +24,7 @@ "@graphql-tools/schema": "^10.0.30", "@webiny/api": "0.0.0", "@webiny/api-core": "0.0.0", - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/error": "0.0.0", "@webiny/feature": "0.0.0", "@webiny/handler": "0.0.0", diff --git a/packages/api-mailer/package.json b/packages/api-mailer/package.json index cbd5cdd76ac..4a1c5b83003 100644 --- a/packages/api-mailer/package.json +++ b/packages/api-mailer/package.json @@ -24,7 +24,7 @@ "@webiny/pubsub": "0.0.0", "crypto-js": "^4.2.0", "lodash": "^4.17.21", - "nodemailer": "^7.0.10", + "nodemailer": "^7.0.11", "zod": "^3.25.76" }, "devDependencies": { diff --git a/packages/api-website-builder-workflows/src/graphql/page.ts b/packages/api-website-builder-workflows/src/graphql/page.ts index c1412d991ca..5286e5e9eff 100644 --- a/packages/api-website-builder-workflows/src/graphql/page.ts +++ b/packages/api-website-builder-workflows/src/graphql/page.ts @@ -14,20 +14,12 @@ export const createWebsiteBuilderPageGraphQLExtension = () => { return true; }, typeDefs: /* GraphQL */ ` - # CmsEntryStateType - enum WbPageStateType { - pending - inReview - rejected - approved - } - - # CmsEntryState + # transferred from CmsEntryState - maybe we can share it somehow in the future? type WbPageState { workflowId: String stepId: ID stepName: String - state: WbPageStateType + state: WorkflowStateStateValue } extend type WbPage { diff --git a/packages/api-website-builder-workflows/src/state/index.ts b/packages/api-website-builder-workflows/src/state/index.ts index 53da8f3ea93..0869310acaf 100644 --- a/packages/api-website-builder-workflows/src/state/index.ts +++ b/packages/api-website-builder-workflows/src/state/index.ts @@ -1,6 +1,7 @@ import type { Context, IWbPageState } from "~/types.js"; import type { IWorkflowState } from "@webiny/api-workflows"; import { getStateValues } from "~/utils/state.js"; +import { WB_PAGE_APP } from "~/constants.js"; interface IParams { context: Context; @@ -11,6 +12,9 @@ export const attachStateLifecycleEvents = ({ context }: IParams) => { state: IWorkflowState, values: IWbPageState | undefined ): Promise => { + if (state.app !== WB_PAGE_APP) { + return; + } try { await context.websiteBuilder.pages.update(state.targetRevisionId, { state: values diff --git a/packages/api-website-builder-workflows/src/utils/state.ts b/packages/api-website-builder-workflows/src/utils/state.ts index 52ee7ff573d..10b1d979dd2 100644 --- a/packages/api-website-builder-workflows/src/utils/state.ts +++ b/packages/api-website-builder-workflows/src/utils/state.ts @@ -1,12 +1,8 @@ import type { IWorkflowStateModel } from "@webiny/api-workflows"; import type { IWbPageState } from "~/types.js"; -export const getStateValues = (state: IWorkflowStateModel): IWbPageState | undefined => { - const activeStep = state.getActiveStep(); - if (!activeStep) { - return undefined; - } - +export const getStateValues = (state: IWorkflowStateModel): IWbPageState => { + const activeStep = state.currentStep; return { workflowId: state.workflowId, stepId: activeStep.id, diff --git a/packages/api-website-builder-workflows/src/websiteBuilder/index.ts b/packages/api-website-builder-workflows/src/websiteBuilder/index.ts index c6d44077a53..50efa304f45 100644 --- a/packages/api-website-builder-workflows/src/websiteBuilder/index.ts +++ b/packages/api-website-builder-workflows/src/websiteBuilder/index.ts @@ -1,5 +1,5 @@ import type { Context } from "~/types.js"; -import { attachUpdatePageLifecycleEvents } from "./updatePage.js"; +// import { attachUpdatePageLifecycleEvents } from "./updatePage.js"; import { attachDeletePageLifecycleEvents } from "./deletePage.js"; import { attachPublishPageLifecycleEvents } from "./publishPage.js"; @@ -8,7 +8,7 @@ interface IParams { } export const attachLifecycleEvents = (params: IParams) => { - attachUpdatePageLifecycleEvents(params); + // attachUpdatePageLifecycleEvents(params); attachDeletePageLifecycleEvents(params); attachPublishPageLifecycleEvents(params); }; diff --git a/packages/api-website-builder-workflows/src/websiteBuilder/updatePage.ts b/packages/api-website-builder-workflows/src/websiteBuilder/updatePage.ts index 64d9429d466..19b9b329326 100644 --- a/packages/api-website-builder-workflows/src/websiteBuilder/updatePage.ts +++ b/packages/api-website-builder-workflows/src/websiteBuilder/updatePage.ts @@ -11,6 +11,7 @@ export const attachUpdatePageLifecycleEvents = (params: IParams) => { const { context } = params; context.websiteBuilder.pages.onPageBeforeUpdate.subscribe(async ({ original }) => { let state: IWorkflowState; + try { state = await context.workflowState.getTargetState(WB_PAGE_APP, original.id); } catch { @@ -18,11 +19,17 @@ export const attachUpdatePageLifecycleEvents = (params: IParams) => { return; } throw new WebinyError({ - message: "Cannot update page because it is currently in a workflow.", - code: "ENTRY_IN_WORKFLOW", + message: "Cannot update page because it has a workflow attached.", + code: "PAGE_IN_WORKFLOW", data: { state: { - ...state + id: state.id, + app: state.app, + state: state.state, + comment: state.comment, + targetRevisionId: state.targetRevisionId, + steps: state.steps, + title: state.title } } }); diff --git a/packages/api-website-builder/src/context/pages/PagesStorage.ts b/packages/api-website-builder/src/context/pages/PagesStorage.ts index 16a91692a4f..53e6b6c9c98 100644 --- a/packages/api-website-builder/src/context/pages/PagesStorage.ts +++ b/packages/api-website-builder/src/context/pages/PagesStorage.ts @@ -148,6 +148,7 @@ export class PagesStorage implements WbPagesStorageOperations { tenant: entry.tenant, locale: entry.locale, webinyVersion: entry.webinyVersion, + // TODO figure out if we should make mapper extendable so Pages do not know about states? state: entry.state, ...entry.values } as WbPage; diff --git a/packages/api/package.json b/packages/api/package.json index 1d3e4ccadb1..6d44f76c971 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -14,7 +14,7 @@ "license": "MIT", "dependencies": { "@webiny/aws-sdk": "0.0.0", - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/plugins": "0.0.0", "@webiny/utils": "0.0.0" }, diff --git a/packages/app-aco/package.json b/packages/app-aco/package.json index 353027ec981..cb7b85d9a7f 100644 --- a/packages/app-aco/package.json +++ b/packages/app-aco/package.json @@ -19,6 +19,7 @@ "@webiny/app-headless-cms-common": "0.0.0", "@webiny/app-security": "0.0.0", "@webiny/app-utils": "0.0.0", + "@webiny/feature": "0.0.0", "@webiny/form": "0.0.0", "@webiny/icons": "0.0.0", "@webiny/plugins": "0.0.0", @@ -32,7 +33,6 @@ "lodash": "^4.17.21", "mobx": "^6.15.0", "mobx-react-lite": "^3.4.3", - "pako": "^2.1.0", "react": "18.2.0", "react-dom": "18.2.0", "react-hotkeyz": "^1.0.4", @@ -43,10 +43,13 @@ "devDependencies": { "@types/react": "18.2.79", "@webiny/build-tools": "0.0.0", + "@webiny/di": "^0.2.3", + "@webiny/wcp": "0.0.0", "apollo-client": "^2.6.10", "apollo-link": "^1.2.14", "rimraf": "^6.0.1", "typescript": "5.9.3", + "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.2.4" }, "publishConfig": { diff --git a/packages/app-aco/src/app.tsx b/packages/app-aco/src/app.tsx index d1836e7b698..d8b09d413be 100644 --- a/packages/app-aco/src/app.tsx +++ b/packages/app-aco/src/app.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { FolderModelProviderModule } from "~/features/index.js"; +import { FolderModelProviderModule } from "~/features/folders/folderModelProvider/FolderModelContext.js"; export const AdvancedContentOrganisation = () => { return ; diff --git a/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchConfigs.tsx b/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchConfigs.tsx index d7cc5d2aa12..36e863beceb 100644 --- a/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchConfigs.tsx +++ b/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchConfigs.tsx @@ -13,7 +13,7 @@ const { AdvancedSearch } = AcoConfig; export const AdvancedSearchConfigs = React.memo(() => { return ( - + <> } /> } /> } /> @@ -42,7 +42,7 @@ export const AdvancedSearchConfigs = React.memo(() => { type={FieldType.PREDEFINED_VALUES} element={} /> - + ); }); diff --git a/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchDebounceRender.tsx b/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchDebounceRender.tsx deleted file mode 100644 index 8b4f9244d99..00000000000 --- a/packages/app-aco/src/components/AdvancedSearch/AdvancedSearchDebounceRender.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import { useEffect, useMemo, useState } from "react"; -import debounce from "lodash/debounce.js"; -import { useAcoConfig } from "~/config/index.js"; - -interface Props { - children?: React.ReactNode; -} -/** - * AdvancedSearchDebounceRenderer is a wrapper component that delays the rendering of its children - * until all necessary configurations are fully loaded. - * - * This component uses a debounced render approach to prevent excessive re-renders as configurations - * like `acoConfigs` are loaded. By deferring the - * initial render until these configurations are ready, we ensure that the children are rendered - * only when the app's environment is fully configured. - */ -export const AdvancedSearchDebounceRenderer = ({ children }: Props) => { - const [render, setRender] = useState(false); - const acoConfigs = useAcoConfig(); - - const debouncedRender = useMemo(() => { - return debounce(() => { - setRender(true); - }, 10); - }, [setRender]); - - useEffect(() => { - if (render) { - return; - } - - debouncedRender(); - - return () => { - debouncedRender.cancel(); - }; - }, [acoConfigs]); - - return <>{render ? children : null}; -}; diff --git a/packages/app-aco/src/components/AdvancedSearch/index.tsx b/packages/app-aco/src/components/AdvancedSearch/index.tsx index 9b308b0a359..2bdc2d1038f 100644 --- a/packages/app-aco/src/components/AdvancedSearch/index.tsx +++ b/packages/app-aco/src/components/AdvancedSearch/index.tsx @@ -3,31 +3,30 @@ import React, { useMemo } from "react"; import type { AdvancedSearchProps } from "./AdvancedSearch.js"; import { AdvancedSearch as AdvancedSearchComponent } from "./AdvancedSearch.js"; import { AdvancedSearchConfigs } from "./AdvancedSearchConfigs.js"; -import { AcoWithConfig, useAcoConfig } from "~/config/index.js"; export * from "./GraphQLInputMapper.js"; export * from "./gateways/index.js"; import type { FieldRaw } from "./domain/index.js"; import { Field, FieldMapper } from "./domain/index.js"; -import { AdvancedSearchDebounceRenderer } from "./AdvancedSearchDebounceRender.js"; +import type { FieldRendererConfig } from "~/config/advanced-search/FieldRenderer.js"; export * from "./useFilterRepository.js"; export * from "./useInputField.js"; interface AdvancedSearchWithFieldRenderersProps extends Omit { + fieldRenderers: FieldRendererConfig[]; fields: FieldRaw[]; } const AdvancedSearchWithFieldRenderers = ({ + fieldRenderers, fields, ...props }: AdvancedSearchWithFieldRenderersProps) => { - const { advancedSearch } = useAcoConfig(); - const fieldsWithRenderer = useMemo(() => { const fieldDTOs = FieldMapper.toDTO(fields.map(field => Field.createFromRaw(field))); return fieldDTOs.map(field => { - const config = advancedSearch.fieldRenderers.find(config => config.type === field.type); + const config = fieldRenderers.find(config => config.type === field.type); const element = config?.element ?? null; return { ...field, element }; }); @@ -39,11 +38,7 @@ const AdvancedSearchWithFieldRenderers = ({ export const AdvancedSearch = (props: AdvancedSearchWithFieldRenderersProps) => { return ( <> - - - - - + ); diff --git a/packages/app-aco/src/components/Extensions/Extensions.tsx b/packages/app-aco/src/components/Extensions/Extensions.tsx index a1fc8c01d92..d703674512b 100644 --- a/packages/app-aco/src/components/Extensions/Extensions.tsx +++ b/packages/app-aco/src/components/Extensions/Extensions.tsx @@ -2,14 +2,14 @@ import React from "react"; import { CompositionScope } from "@webiny/app-admin"; import { ModelProvider, Fields } from "@webiny/app-headless-cms-common"; import { Bind, BindPrefix } from "@webiny/form"; -import { useFolderModel, useGetFolderExtensionsFields } from "~/features/index.js"; +import { useFolderExtensionsFields } from "~/features/folders/getFolderExtensionsFields/index.js"; +import { useFolderModel } from "~/hooks/useFolderModel.js"; export const Extensions = () => { - const { getFolderExtensionsFields } = useGetFolderExtensionsFields(); - const { fields } = getFolderExtensionsFields(); + const { fields } = useFolderExtensionsFields(); const folderModel = useFolderModel(); - if (!fields.length) { + if (fields.length === 0 || !folderModel) { return null; } diff --git a/packages/app-aco/src/components/FolderGrid/FolderGridItem.tsx b/packages/app-aco/src/components/FolderGrid/FolderGridItem.tsx index eeb824e5b76..68fcfbf4618 100644 --- a/packages/app-aco/src/components/FolderGrid/FolderGridItem.tsx +++ b/packages/app-aco/src/components/FolderGrid/FolderGridItem.tsx @@ -3,16 +3,16 @@ import { cn, IconButton, Text, TimeAgo } from "@webiny/admin-ui"; import { FolderIcon, FolderSharedIcon } from "../FolderIcons/index.js"; import { ReactComponent as MoreVerticalIcon } from "@webiny/icons/more_vert.svg"; import { OptionsMenu } from "@webiny/app-admin"; -import { useAcoConfig } from "~/config/index.js"; +import type { FolderActionConfig } from "~/config/index.js"; import { useFolder } from "~/hooks/index.js"; -export interface FolderProps { +export interface FolderGridItemProps { + folderActions: FolderActionConfig[]; onClick: (id: string) => void; } -export const FolderGridItem = ({ onClick }: FolderProps) => { +export const FolderGridItem = ({ folderActions, onClick }: FolderGridItemProps) => { const { folder } = useFolder(); - const { folder: folderConfig } = useAcoConfig(); const { id, title, hasNonInheritedPermissions, canManagePermissions, canManageStructure } = folder; @@ -54,7 +54,7 @@ export const FolderGridItem = ({ onClick }: FolderProps) => { ])} > void; + onFolderClick: (data: FolderDto) => void; } export const List = ({ folders, + folderActions, onFolderClick, focusedFolderId, hiddenFolderIds, - enableActions + enableActions, + dropConfirmation = false }: ListProps) => { const { listFoldersByParentIds, loading } = useListFoldersByParentIds(); const { updateFolder } = useUpdateFolder(); @@ -43,10 +45,9 @@ export const List = ({ const { getFolderAncestors } = useGetFolderAncestors(); const { showSnackbar } = useSnackbar(); - const [treeData, setTreeData] = useState[]>([]); + const [treeData, setTreeData] = useState[]>([]); const [openFolderIds, setOpenFolderIds] = useState([ROOT_FOLDER]); const { showDialog: showConfirmMoveFolderDialog } = useConfirmMoveFolderDialog(); - const { folder: folderConfigs } = useAcoConfig(); useEffect(() => { setTreeData(createTreeData(folders, focusedFolderId, hiddenFolderIds)); @@ -90,7 +91,7 @@ export const List = ({ const runDrop = async () => handleDrop(newTree, options); // If drop confirmation is enabled, show dialog before proceeding - if (folderConfigs.dropConfirmation) { + if (dropConfirmation) { const { dragSourceId, dropTargetId } = options; const folder = folders.find(f => f.id === dragSourceId); const targetFolder = folders.find(f => f.id === dropTargetId); @@ -110,7 +111,7 @@ export const List = ({ await runDrop(); } }, - [folders, folderConfigs.dropConfirmation, showConfirmMoveFolderDialog] + [folders, dropConfirmation, showConfirmMoveFolderDialog] ); const sort = useMemo( @@ -123,12 +124,12 @@ export const List = ({ [] ); - const canDrag: TreeProps["canDrag"] = useCallback( - (node: NodeDto) => node.id !== ROOT_FOLDER && canManageStructure(node.id), + const canDrag: TreeProps["canDrag"] = useCallback( + (node: NodeDto) => node.id !== ROOT_FOLDER && canManageStructure(node.id), [canManageStructure] ); - const canDrop: TreeProps["canDrop"] = (_, options: DropOptions) => { + const canDrop: TreeProps["canDrop"] = (_, options: DropOptions) => { const { dragSourceId, dropTargetId } = options; const dropTagetAncestorIds = getFolderAncestors(dropTargetId).map(item => item.id); @@ -136,24 +137,24 @@ export const List = ({ return !(dragSourceId && dropTagetAncestorIds.includes(dragSourceId)); }; - const nodeRenderer: TreeProps["renderer"] = node => { + const nodeRenderer: TreeProps["renderer"] = node => { const folder = folders.find(folder => folder.id === node.id); return ( - + ); }; const handleNodeClick = useCallback( - (node: WithDefaultNodeData) => { + (node: WithDefaultNodeData) => { onFolderClick(node); }, [onFolderClick] ); return ( - + nodes={treeData} rootId={"0"} defaultOpenNodeIds={openFolderIds} diff --git a/packages/app-aco/src/components/FolderTree/List/utils.ts b/packages/app-aco/src/components/FolderTree/List/utils.ts index cdfbcf078b0..fc7ddd63e0d 100644 --- a/packages/app-aco/src/components/FolderTree/List/utils.ts +++ b/packages/app-aco/src/components/FolderTree/List/utils.ts @@ -1,6 +1,6 @@ -import type { FolderItem } from "~/types.js"; import { ROOT_FOLDER } from "~/constants.js"; import type { NodeDto } from "@webiny/admin-ui"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; /** * Transform an array of folders returned by folders cache into an array of elements for the tree component. @@ -11,10 +11,10 @@ import type { NodeDto } from "@webiny/admin-ui"; * @return array of elements to render the tree component. */ export const createTreeData = ( - folders: FolderItem[] = [], + folders: FolderDto[] = [], focusedNodeId?: string, hiddenFolderIds: string[] = [] -): NodeDto[] => { +): NodeDto[] => { return folders .map(item => { const { id, parentId, title } = item; @@ -41,7 +41,7 @@ export const createTreeData = ( * @return array of ids of open folders. */ export const createInitialOpenList = ( - folders: FolderItem[] = [], + folders: FolderDto[] = [], openIds: string[] = [], focusedId?: string ): string[] => { @@ -64,7 +64,7 @@ export const createInitialOpenList = ( result.push(currentId); } // Get the folder object for the current id - const folder = folderMap.get(currentId) as FolderItem | undefined; + const folder = folderMap.get(currentId) as FolderDto | undefined; // Get the parent id of the current folder const parentId = folder?.parentId; // Stop if there is no parent or we've already added this parent diff --git a/packages/app-aco/src/components/FolderTree/MenuActions/MenuActions.tsx b/packages/app-aco/src/components/FolderTree/MenuActions/MenuActions.tsx index 3fd74522bc4..0e8656bb1cd 100644 --- a/packages/app-aco/src/components/FolderTree/MenuActions/MenuActions.tsx +++ b/packages/app-aco/src/components/FolderTree/MenuActions/MenuActions.tsx @@ -2,12 +2,15 @@ import React from "react"; import { OptionsMenu } from "@webiny/app-admin"; import { cn, IconButton } from "@webiny/admin-ui"; import { ReactComponent as MoreVerticalIcon } from "@webiny/icons/more_vert.svg"; -import { useAcoConfig } from "~/config/index.js"; +import type { FolderActionConfig } from "~/config/index.js"; import { useFolder } from "~/hooks/index.js"; -export const MenuActions = () => { +interface MenuActionsProps { + folderActions: FolderActionConfig[]; +} + +export const MenuActions = ({ folderActions }: MenuActionsProps) => { const { folder } = useFolder(); - const { folder: folderConfig } = useAcoConfig(); // If the user cannot manage folder structure, no need to show the menu. if (!folder.canManageStructure) { @@ -26,7 +29,7 @@ export const MenuActions = () => { > } size={"xs"} variant={"ghost"} />} - actions={folderConfig.actions} + actions={folderActions} data-testid={"folder.tree.menu-action"} />
diff --git a/packages/app-aco/src/components/FolderTree/Node/Node.tsx b/packages/app-aco/src/components/FolderTree/Node/Node.tsx index 0f304768acc..afe1efd980e 100644 --- a/packages/app-aco/src/components/FolderTree/Node/Node.tsx +++ b/packages/app-aco/src/components/FolderTree/Node/Node.tsx @@ -6,6 +6,7 @@ import { ReactComponent as HomeIcon } from "@webiny/icons/home.svg"; import { MenuActions } from "../MenuActions/index.js"; import { ROOT_FOLDER } from "~/constants.js"; import { useFolder } from "~/hooks/index.js"; +import type { FolderActionConfig } from "~/config/AcoConfig.js"; interface FolderProps { text: string; @@ -41,10 +42,11 @@ export const FolderNode = ({ }; type NodeProps = { + folderActions: FolderActionConfig[]; enableActions?: boolean; }; -export const Node = ({ enableActions }: NodeProps) => { +export const Node = ({ folderActions, enableActions }: NodeProps) => { const { folder } = useFolder(); const { hasNonInheritedPermissions, canManagePermissions, title, id } = folder || {}; @@ -58,7 +60,7 @@ export const Node = ({ enableActions }: NodeProps) => { hasNonInheritedPermissions={hasNonInheritedPermissions} canManagePermissions={canManagePermissions} /> - {enableActions && !isRoot && } + {enableActions && !isRoot && } ); }; diff --git a/packages/app-aco/src/components/FolderTree/index.tsx b/packages/app-aco/src/components/FolderTree/index.tsx index 222698b28cf..dda38e20726 100644 --- a/packages/app-aco/src/components/FolderTree/index.tsx +++ b/packages/app-aco/src/components/FolderTree/index.tsx @@ -1,33 +1,38 @@ import React, { useMemo } from "react"; import { Tooltip } from "@webiny/admin-ui"; -import { useGetFolderHierarchy, useGetFolderLevelPermission } from "~/features/index.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useGetFolderLevelPermission } from "~/features/folders/getFolderLevelPermission/index.js"; +import { useLoadFolderHierarchy } from "~/features/folders/loadFolderHierarchy/index.js"; import { ButtonCreate } from "./ButtonCreate/index.js"; import { Loader } from "./Loader/index.js"; import { List } from "./List/index.js"; -import type { FolderItem } from "~/types.js"; import { ROOT_FOLDER } from "~/constants.js"; -import { AcoWithConfig } from "~/config/index.js"; +import type { FolderActionConfig } from "~/config/AcoConfig.js"; export { Loader }; export interface FolderTreeProps { - onFolderClick: (data: FolderItem) => void; + folderActions?: FolderActionConfig[]; + onFolderClick: (data: FolderDto) => void; enableCreate?: boolean; rootFolderLabel?: string; enableActions?: boolean; + dropConfirmation?: boolean; focusedFolderId?: string; hiddenFolderIds?: string[]; } export const FolderTree = ({ + folderActions = [], focusedFolderId, hiddenFolderIds, enableActions, enableCreate, onFolderClick, + dropConfirmation, rootFolderLabel }: FolderTreeProps) => { - const { folders, getIsFolderLoading } = useGetFolderHierarchy(); + const { folders, getIsFolderLoading } = useLoadFolderHierarchy(); const { getFolderLevelPermission: canManageStructure } = useGetFolderLevelPermission("canManageStructure"); @@ -65,18 +70,18 @@ export const FolderTree = ({ return (
- - - {enableCreate && ( -
{createButton}
- )} -
+ + {enableCreate && ( +
{createButton}
+ )}
); }; diff --git a/packages/app-aco/src/components/Table/components/Table/Table.tsx b/packages/app-aco/src/components/Table/components/Table/Table.tsx index 56e1e7b591b..58c8e2ee37f 100644 --- a/packages/app-aco/src/components/Table/components/Table/Table.tsx +++ b/packages/app-aco/src/components/Table/components/Table/Table.tsx @@ -10,10 +10,11 @@ import { import { ColumnsVisibilityLocalStorageGateway } from "./gateways/index.js"; import { TablePresenter } from "./TablePresenter.js"; import { TableInner } from "./TableInner.js"; -import { useAcoConfig } from "~/config/index.js"; import type { TableRow } from "~/types.js"; +import { ColumnConfig } from "~/config/table/Column.js"; export interface TableProps { + columns: ColumnConfig[]; data: T[]; loading?: boolean; nameColumnId?: string; @@ -25,15 +26,13 @@ export interface TableProps { sorting: DataTableSorting; } -export const Table = ({ namespace, ...props }: TableProps) => { - const { table } = useAcoConfig(); - +export const Table = ({ columns, namespace, ...props }: TableProps) => { const columnsRepo = useMemo(() => { return columnsRepositoryFactory.getRepository( namespace, - table.columns.map(column => Column.createFromConfig(column)) + columns.map(column => Column.createFromConfig(column)) ); - }, [namespace, table.columns]); + }, [namespace, columns]); const visibilityRepo = useMemo(() => { const columnsVisibilityLocalStorage = new ColumnsVisibilityLocalStorageGateway(namespace); diff --git a/packages/app-aco/src/components/Table/createTableData.ts b/packages/app-aco/src/components/Table/createTableData.ts index 629269ebccb..2d86487605b 100644 --- a/packages/app-aco/src/components/Table/createTableData.ts +++ b/packages/app-aco/src/components/Table/createTableData.ts @@ -1,16 +1,18 @@ +import type { FolderDto } from "~/domain/folder/FolderDto.js"; import type { FolderTableRow, RecordTableRow } from "~/table.types.js"; -import type { FolderItem } from "~/types.js"; -export const createRecordsData = (items: T[]): RecordTableRow[] => { +export const createRecordsData = ( + items: T[] +): RecordTableRow[] => { return items.map(item => ({ id: item.id, $type: "RECORD", - $selectable: true, + $selectable: item.$selectable !== undefined ? item.$selectable : true, data: item })); }; -export const createFoldersData = (items: FolderItem[]): FolderTableRow[] => { +export const createFoldersData = (items: FolderDto[]): FolderTableRow[] => { return items.map(item => ({ id: item.id, $type: "FOLDER", diff --git a/packages/app-aco/src/config/AcoConfig.tsx b/packages/app-aco/src/config/AcoConfig.tsx index 9c8ef74f1e0..1947347a05d 100644 --- a/packages/app-aco/src/config/AcoConfig.tsx +++ b/packages/app-aco/src/config/AcoConfig.tsx @@ -15,21 +15,18 @@ export type { ActionConfig as FolderActionConfig } from "./folder/Action.js"; export type { ColumnConfig as TableColumnConfig } from "./table/Column.js"; export type { SortingConfig as TableSortingConfig } from "./table/Sorting.js"; -const base = createConfigurableComponent("AcoConfig"); +const base = createConfigurableComponent("Aco"); export const AcoConfig = Object.assign(base.Config, { AdvancedSearch, Folder, Record, Table }); -export const AcoWithConfig = base.WithConfig; -interface AcoConfig { +export interface AcoConfig { advancedSearch: AdvancedSearchConfig; record: RecordConfig; folder: FolderConfig; table: TableConfig; } -export function useAcoConfig() { - const config = base.useConfig(); - +export function useAcoConfig(config: Record): AcoConfig { const advancedSearch = config.advancedSearch || {}; const folder = config.folder || {}; const record = config.record || {}; diff --git a/packages/app-aco/src/contexts/acoList.tsx b/packages/app-aco/src/contexts/acoList.tsx index 13c38983ac9..60d46697afa 100644 --- a/packages/app-aco/src/contexts/acoList.tsx +++ b/packages/app-aco/src/contexts/acoList.tsx @@ -3,8 +3,11 @@ import dotPropImmutable from "dot-prop-immutable"; import pick from "lodash/pick.js"; import { useStateIfMounted } from "@webiny/app-admin"; import { useSecurity } from "@webiny/app-security"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useGetDescendantFolders } from "~/features/folders/getDescendantFolders/index.js"; +import { useListFoldersByParentIds } from "~/features/folders/listFoldersByParentIds/index.js"; +import { useLoadFolderHierarchy } from "~/features/folders/loadFolderHierarchy/index.js"; import type { - FolderItem, GenericSearchData, ListMeta, ListSearchRecordsQueryVariables, @@ -12,19 +15,14 @@ import type { SearchRecordItem } from "~/types.js"; import { useAcoApp, useNavigateFolder } from "~/hooks/index.js"; -import { - useGetDescendantFolders, - useGetFolderHierarchy, - useListFoldersByParentIds -} from "~/features/index.js"; import { FoldersContext } from "~/contexts/folders.js"; import { SearchRecordsContext } from "~/contexts/records.js"; import { sortTableItems, validateOrGetDefaultDbSort } from "~/sorting.js"; import { ROOT_FOLDER } from "~/constants.js"; export interface AcoListContextData { - currentFolder?: FolderItem; - folders: FolderItem[]; + currentFolder?: FolderDto; + folders: FolderDto[]; hideFilters: () => void; isListLoading: boolean; isListLoadingMore: boolean; @@ -83,9 +81,9 @@ const initializeAcoListState = (): State => { }; const getCurrentFolderList = ( - folders?: FolderItem[] | null, + folders?: FolderDto[] | null, currentFolderId?: string -): FolderItem[] | [] => { +): FolderDto[] | [] => { if (!folders) { return []; } @@ -126,11 +124,7 @@ export const AcoListProvider = ({ children, ...props }: AcoListProviderProps) => const { identity } = useSecurity(); const { currentFolderId } = useNavigateFolder(); const { folderIdPath, folderIdInPath } = useAcoApp(); - const { - folders: originalFolders, - getIsFolderLoading, - getFolderHierarchy - } = useGetFolderHierarchy(); + const { folders: originalFolders, loadFolderHierarchy } = useLoadFolderHierarchy(); const { getDescendantFolders } = useGetDescendantFolders(); const { listFoldersByParentIds } = useListFoldersByParentIds(); const folderContext = useContext(FoldersContext); @@ -140,7 +134,7 @@ export const AcoListProvider = ({ children, ...props }: AcoListProviderProps) => throw new Error("useAcoList must be used within a ACOProvider"); } - const [folders, setFolders] = useStateIfMounted([]); + const [folders, setFolders] = useStateIfMounted([]); const [records, setRecords] = useStateIfMounted([]); const [listTitle, setListTitle] = useStateIfMounted(undefined); const [state, setState] = useStateIfMounted>(initializeAcoListState()); @@ -156,7 +150,7 @@ export const AcoListProvider = ({ children, ...props }: AcoListProviderProps) => useEffect(() => { // The folders collection is empty, it must be the first render, let's load the full hierarchy. if (folders.length === 0) { - getFolderHierarchy(currentFolderId); + loadFolderHierarchy(currentFolderId); } else { // Otherwise let's load only the current folder sub-tree listFoldersByParentIds([currentFolderId]); @@ -407,9 +401,7 @@ export const AcoListProvider = ({ children, ...props }: AcoListProviderProps) => currentFolder, records, listTitle, - isListLoading: Boolean( - recordsLoading.INIT || recordsLoading.LIST || getIsFolderLoading(currentFolderId) - ), + isListLoading: Boolean(recordsLoading.INIT || recordsLoading.LIST), isListLoadingMore: Boolean(recordsLoading.LIST_MORE), meta, setSearchQuery(query) { diff --git a/packages/app-aco/src/contexts/app.tsx b/packages/app-aco/src/contexts/app.tsx index d29b46d8b9e..f99f4936312 100644 --- a/packages/app-aco/src/contexts/app.tsx +++ b/packages/app-aco/src/contexts/app.tsx @@ -6,6 +6,7 @@ import { FoldersProvider as FoldersContextProvider } from "./folders.js"; import { SearchRecordsProvider as SearchRecordsContextProvider } from "./records.js"; import { AcoListProvider } from "~/contexts/acoList.js"; import { NavigateFolderProvider } from "~/contexts/navigateFolder.js"; +import type { ColumnConfig } from "~/config/table/Column.js"; export interface AcoAppProviderContext { app: AcoApp; @@ -25,8 +26,9 @@ export type AcoAppProviderProps = { client: ApolloClient; navigateToFolder: (folderId: string) => void; createNavigateFolderStorageKey: () => string; - own?: boolean; model: AcoModel; + columns: ColumnConfig[]; + own?: boolean; getFields?: () => AcoModelField[]; }; @@ -52,6 +54,7 @@ export const AcoAppProvider = ({ id, client, model, + columns, getFields, navigateToFolder, createNavigateFolderStorageKey, @@ -82,7 +85,7 @@ export const AcoAppProvider = ({ return ( - + (undefined); interface FolderProviderProps { - folder: FolderItem | undefined; + folder: FolderDto | undefined; children: React.ReactNode; } diff --git a/packages/app-aco/src/contexts/folders.tsx b/packages/app-aco/src/contexts/folders.tsx index daee31fb154..e2209d304a0 100644 --- a/packages/app-aco/src/contexts/folders.tsx +++ b/packages/app-aco/src/contexts/folders.tsx @@ -1,6 +1,9 @@ +import { DiContainerProvider } from "@webiny/app"; +import { useContainer } from "@webiny/app"; import type { ReactNode } from "react"; import React, { useContext, useMemo } from "react"; import { AcoAppContext } from "~/contexts/app.js"; +import { FoldersFeature } from "~/features/folders/feature.js"; interface FoldersContext { type?: string | null; @@ -24,11 +27,25 @@ export const FoldersProvider = ({ children, ...props }: Props) => { throw Error(`FoldersProvider requires a "type" prop or an AcoAppContext to be available!`); } + const container = useContainer(); + + const routeContainer = useMemo(() => { + const childContainer = container.createChildContainer(); + + FoldersFeature.register(childContainer, { type }); + + return childContainer; + }, []); + const context = useMemo(() => { return { type }; }, [type]); - return {children}; + return ( + + {children} + + ); }; diff --git a/packages/app-aco/src/contexts/records.tsx b/packages/app-aco/src/contexts/records.tsx index 3424db59c4a..58a2d4877cb 100644 --- a/packages/app-aco/src/contexts/records.tsx +++ b/packages/app-aco/src/contexts/records.tsx @@ -33,7 +33,7 @@ import { validateOrGetDefaultDbSort } from "~/sorting.js"; import { useAcoApp } from "~/hooks/index.js"; import { parseIdentifier } from "@webiny/utils"; import { useStateIfMounted } from "@webiny/app-admin"; -import { useAcoConfig } from "~/config/index.js"; +import type { ColumnConfig } from "~/config/table/Column.js"; interface ListTagsParams { where?: ListTagsWhereQueryVariables; @@ -63,6 +63,7 @@ export const SearchRecordsContext = React.createContext { +export const SearchRecordsProvider = ({ columns, children }: Props) => { const { app, client } = useAcoApp(); const { model } = app; @@ -106,8 +107,7 @@ export const SearchRecordsProvider = ({ children }: Props) => { * Retrieve all `fieldIds` from the ACO configuration, considering field `name`: * The result is a deduplicated flat array of unique field IDs. */ - const { table } = useAcoConfig(); - const fieldIds = table.columns.map(config => config.name).filter(Boolean); + const fieldIds = columns.map(config => config.name).filter(Boolean); const [records, setRecords] = useStateIfMounted([]); const [tags, setTags] = useStateIfMounted([]); diff --git a/packages/app-aco/src/dialogs/useConfirmMoveFolderDialog.tsx b/packages/app-aco/src/dialogs/useConfirmMoveFolderDialog.tsx index f0d2953f4a9..fbfca8be4d5 100644 --- a/packages/app-aco/src/dialogs/useConfirmMoveFolderDialog.tsx +++ b/packages/app-aco/src/dialogs/useConfirmMoveFolderDialog.tsx @@ -1,10 +1,10 @@ import { useDialogs } from "@webiny/app-admin"; -import { FolderItem } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; interface ShowDialogParams { - folder: FolderItem; - targetFolder: FolderItem; - onAccept: (folder: FolderItem, targetFolder: FolderItem) => Promise; + folder: FolderDto; + targetFolder: FolderDto; + onAccept: (folder: FolderDto, targetFolder: FolderDto) => Promise; } interface UseConfirmMoveFolderDialogResponse { diff --git a/packages/app-aco/src/dialogs/useCreateDialog.tsx b/packages/app-aco/src/dialogs/useCreateDialog.tsx index 561cbb48670..ddf584ad771 100644 --- a/packages/app-aco/src/dialogs/useCreateDialog.tsx +++ b/packages/app-aco/src/dialogs/useCreateDialog.tsx @@ -6,9 +6,9 @@ import type { GenericFormData } from "@webiny/form"; import { Bind, useForm } from "@webiny/form"; import { validation } from "@webiny/validation"; import { Extensions, FolderTree } from "~/components/index.js"; -import { useCreateFolder } from "~/features/index.js"; import { ROOT_FOLDER } from "~/constants.js"; -import type { FolderItem } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useCreateFolder } from "~/features/folders/createFolder/index.js"; import { ParentFolderField } from "./ParentFolderField.js"; interface ShowDialogParams { @@ -92,7 +92,7 @@ export const useCreateDialog = (): UseCreateDialogResponse => { const { createFolder } = useCreateFolder(); const { showSnackbar } = useSnackbar(); - const onAccept = useCallback(async (data: FolderItem) => { + const onAccept = useCallback(async (data: FolderDto) => { try { await createFolder({ ...data, diff --git a/packages/app-aco/src/dialogs/useDeleteDialog.tsx b/packages/app-aco/src/dialogs/useDeleteDialog.tsx index 580543d12dc..28bfb83b970 100644 --- a/packages/app-aco/src/dialogs/useDeleteDialog.tsx +++ b/packages/app-aco/src/dialogs/useDeleteDialog.tsx @@ -1,11 +1,11 @@ import { useSnackbar } from "@webiny/app-admin"; import { useDialogs } from "@webiny/app-admin"; -import { useDeleteFolder } from "~/features/index.js"; -import type { FolderItem } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useDeleteFolder } from "~/features/folders/deleteFolder/index.js"; import { useCallback } from "react"; interface ShowDialogParams { - folder: FolderItem; + folder: FolderDto; } interface UseDeleteDialogResponse { @@ -17,9 +17,9 @@ export const useDeleteDialog = (): UseDeleteDialogResponse => { const { deleteFolder } = useDeleteFolder(); const { showSnackbar } = useSnackbar(); - const onAccept = useCallback(async (folder: FolderItem) => { + const onAccept = useCallback(async (folder: FolderDto) => { try { - await deleteFolder(folder); + await deleteFolder(folder.id); showSnackbar(`The folder "${folder.title}" was deleted successfully.`); } catch (error) { showSnackbar(error.message); diff --git a/packages/app-aco/src/dialogs/useEditDialog.tsx b/packages/app-aco/src/dialogs/useEditDialog.tsx index 92f809863fb..193f6ce52bd 100644 --- a/packages/app-aco/src/dialogs/useEditDialog.tsx +++ b/packages/app-aco/src/dialogs/useEditDialog.tsx @@ -6,12 +6,12 @@ import { Bind } from "@webiny/form"; import { validation } from "@webiny/validation"; import { Extensions, FolderTree } from "~/components/index.js"; import { ROOT_FOLDER } from "~/constants.js"; -import { useUpdateFolder } from "~/features/index.js"; -import type { FolderItem } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useUpdateFolder } from "~/features/folders/updateFolder/index.js"; import { ParentFolderField } from "./ParentFolderField.js"; interface ShowDialogParams { - folder: FolderItem; + folder: FolderDto; } interface UseEditDialogResponse { @@ -19,7 +19,7 @@ interface UseEditDialogResponse { } interface FormComponentProps { - folder: FolderItem; + folder: FolderDto; } const FormComponent = ({ folder }: FormComponentProps) => { @@ -74,7 +74,7 @@ export const useEditDialog = (): UseEditDialogResponse => { const { updateFolder } = useUpdateFolder(); const { showSnackbar } = useSnackbar(); - const onAccept = useCallback(async (folder: FolderItem, data: GenericFormData) => { + const onAccept = useCallback(async (folder: FolderDto, data: GenericFormData) => { try { await updateFolder({ ...folder, diff --git a/packages/app-aco/src/dialogs/useSetPermissionsDialog.tsx b/packages/app-aco/src/dialogs/useSetPermissionsDialog.tsx index 96d266acaa3..1f97cae6f2c 100644 --- a/packages/app-aco/src/dialogs/useSetPermissionsDialog.tsx +++ b/packages/app-aco/src/dialogs/useSetPermissionsDialog.tsx @@ -4,14 +4,15 @@ import { Grid } from "@webiny/admin-ui"; import { useDialogs, useSnackbar } from "@webiny/app-admin"; import type { GenericFormData } from "@webiny/form"; import { useBind } from "@webiny/form"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { useUpdateFolder } from "~/features/folders/updateFolder/index.js"; import { UsersTeamsMultiAutocomplete } from "./DialogSetPermissions/UsersTeamsMultiAutocomplete.js"; import { UsersTeamsSelection } from "./DialogSetPermissions/UsersTeamsSelection.js"; import { LIST_FOLDER_LEVEL_PERMISSIONS_TARGETS } from "./DialogSetPermissions/graphql.js"; -import { useUpdateFolder } from "~/features/index.js"; -import type { FolderItem, FolderLevelPermissionsTarget, FolderPermission } from "~/types.js"; +import type { FolderLevelPermissionsTarget, FolderPermission } from "~/types.js"; interface ShowDialogParams { - folder: FolderItem; + folder: FolderDto; } interface UseSetPermissionsDialogResponse { @@ -19,7 +20,7 @@ interface UseSetPermissionsDialogResponse { } interface FormComponentProps { - folder: FolderItem; + folder: FolderDto; } interface UpdatePermissionCallableParams { @@ -116,7 +117,7 @@ export const useSetPermissionsDialog = (): UseSetPermissionsDialogResponse => { const { updateFolder } = useUpdateFolder(); const { showSnackbar } = useSnackbar(); - const onAccept = useCallback(async (folder: FolderItem, data: Partial) => { + const onAccept = useCallback(async (folder: FolderDto, data: Partial) => { const updateData = { ...folder, ...data }; try { diff --git a/packages/app-aco/src/features/folders/Folder.ts b/packages/app-aco/src/domain/folder/Folder.ts similarity index 100% rename from packages/app-aco/src/features/folders/Folder.ts rename to packages/app-aco/src/domain/folder/Folder.ts diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/FolderGqlDto.ts b/packages/app-aco/src/domain/folder/FolderDto.ts similarity index 62% rename from packages/app-aco/src/features/folders/getFolderHierarchy/FolderGqlDto.ts rename to packages/app-aco/src/domain/folder/FolderDto.ts index f802d031281..809b7375e3f 100644 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/FolderGqlDto.ts +++ b/packages/app-aco/src/domain/folder/FolderDto.ts @@ -1,22 +1,23 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; +import type { FolderPermission } from "~/types.js"; +import { FolderIdentityDto } from "./FolderIdentity.js"; -export interface FolderGqlDto { +export interface FolderDto { id: string; title: string; slug: string; + type: string; + parentId: string | null; + path: string; permissions: FolderPermission[]; hasNonInheritedPermissions: boolean; canManagePermissions: boolean; canManageStructure: boolean; canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; + createdBy: FolderIdentityDto; createdOn: string; - savedBy: CmsIdentity; + savedBy: FolderIdentityDto; savedOn: string; - modifiedBy: CmsIdentity | null; + modifiedBy: FolderIdentityDto | null; modifiedOn: string | null; extensions: Record; } diff --git a/packages/app-aco/src/domain/folder/FolderDtoMapper.ts b/packages/app-aco/src/domain/folder/FolderDtoMapper.ts new file mode 100644 index 00000000000..777a6bcd789 --- /dev/null +++ b/packages/app-aco/src/domain/folder/FolderDtoMapper.ts @@ -0,0 +1,31 @@ +import { ROOT_FOLDER } from "~/constants.js"; +import type { FolderDto } from "./FolderDto.js"; +import { FolderIdentity } from "./FolderIdentity.js"; +import type { Folder } from "./Folder.js"; + +export class FolderDtoMapper { + static toDTO(folder: Folder): FolderDto { + return { + id: folder.id, + // System fields + createdBy: FolderIdentity.from(folder.createdBy), + createdOn: folder.createdOn ?? "", + modifiedBy: FolderIdentity.from(folder.modifiedBy), + modifiedOn: folder.modifiedOn ?? "", + savedBy: FolderIdentity.from(folder.savedBy), + savedOn: folder.savedOn ?? "", + // Folder fields + title: folder.title, + parentId: folder.parentId ?? ROOT_FOLDER, + permissions: folder.permissions ?? [], + path: folder.path, + slug: folder.slug, + type: folder.type, + hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false, + canManageContent: folder.canManageContent ?? false, + canManagePermissions: folder.canManagePermissions ?? false, + canManageStructure: folder.canManageStructure ?? false, + extensions: folder.extensions ?? {} + }; + } +} diff --git a/packages/app-aco/src/domain/folder/FolderIdentity.ts b/packages/app-aco/src/domain/folder/FolderIdentity.ts new file mode 100644 index 00000000000..cfed172d210 --- /dev/null +++ b/packages/app-aco/src/domain/folder/FolderIdentity.ts @@ -0,0 +1,29 @@ +import type { CmsIdentity } from "@webiny/app-headless-cms-common/types/index.js"; + +export type FolderIdentityDto = { + id: string; + displayName: string; + type: string; +}; + +export class FolderIdentity { + static createEmpty(): FolderIdentityDto { + return { + id: "", + displayName: "", + type: "" + }; + } + + static from(identity: CmsIdentity | null | undefined): FolderIdentityDto { + if (!identity) { + return FolderIdentity.createEmpty(); + } + + return { + id: identity.id, + displayName: identity.displayName, + type: identity.type + }; + } +} diff --git a/packages/app-aco/src/domain/folder/RootFolder.ts b/packages/app-aco/src/domain/folder/RootFolder.ts new file mode 100644 index 00000000000..1dc5e1ebcf8 --- /dev/null +++ b/packages/app-aco/src/domain/folder/RootFolder.ts @@ -0,0 +1,28 @@ +import { ROOT_FOLDER } from "@webiny/shared-aco"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderIdentity } from "./FolderIdentity.js"; + +export class RootFolder { + static create(): FolderDto { + return { + id: ROOT_FOLDER, + title: "Home", + permissions: [], + parentId: "0", + path: ROOT_FOLDER, + slug: "", + createdOn: "", + createdBy: FolderIdentity.createEmpty(), + hasNonInheritedPermissions: false, + canManagePermissions: true, + canManageStructure: true, + canManageContent: true, + savedOn: "", + savedBy: FolderIdentity.createEmpty(), + modifiedOn: null, + modifiedBy: null, + type: "$ROOT", + extensions: {} + }; + } +} diff --git a/packages/app-aco/src/features/folders/abstractions.ts b/packages/app-aco/src/features/folders/abstractions.ts new file mode 100644 index 00000000000..c4475cac199 --- /dev/null +++ b/packages/app-aco/src/features/folders/abstractions.ts @@ -0,0 +1,47 @@ +import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; +import type { ILoadingRepository } from "@webiny/app-utils"; +import { createAbstraction } from "@webiny/feature/admin"; +import { Folder } from "~/domain/folder/Folder.js"; +import { IListCache, LoadedCache } from "./cache/index.js"; + +export type FolderPermissionName = + | "canManagePermissions" + | "canManageStructure" + | "canManageContent"; + +export interface IFolderModelProvider { + getModel(): Promise; + getGraphQLSelection(): Promise; +} +export const FolderModelProvider = createAbstraction("FolderModel"); +export namespace FolderModelProvider { + export type Interface = IFolderModelProvider; +} + +export interface IFoldersContext { + type: string; +} + +export const FoldersContext = createAbstraction("FoldersContext"); + +export namespace FoldersContext { + export type Interface = IFoldersContext; +} + +export const FoldersCache = createAbstraction>("FoldersCache"); +export namespace FoldersCache { + export type Interface = IListCache; +} + +export const LoadedFoldersCache = createAbstraction("LoadedFoldersCache"); +export namespace LoadedFoldersCache { + export type Interface = LoadedCache; +} + +export const FoldersLoadingRepository = createAbstraction( + "FoldersLoadingRepository" +); + +export namespace FoldersLoadingRepository { + export type Interface = ILoadingRepository; +} diff --git a/packages/app-aco/src/features/folders/cache/FoldersCacheFactory.ts b/packages/app-aco/src/features/folders/cache/FoldersCacheFactory.ts index 0913e692e7a..6897a9cb2ac 100644 --- a/packages/app-aco/src/features/folders/cache/FoldersCacheFactory.ts +++ b/packages/app-aco/src/features/folders/cache/FoldersCacheFactory.ts @@ -1,4 +1,4 @@ -import type { Folder } from "../Folder.js"; +import type { Folder } from "~/domain/folder/Folder.js"; import { type IListCache, ListCache } from "~/features/folders/cache/ListCache.js"; export class FoldersCacheFactory { diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolder.test.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolder.test.ts index 53281244b3b..dfb483b66f6 100644 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolder.test.ts +++ b/packages/app-aco/src/features/folders/createFolder/CreateFolder.test.ts @@ -1,17 +1,22 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; -import { CreateFolder } from "./CreateFolder.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import type { FolderGqlDto } from "~/features/folders/listFolders/FolderGqlDto.js"; -import type { ICreateFolderGateway } from "~/features/folders/createFolder/ICreateFolderGateway.js"; +import { Container } from "@webiny/di"; +import { FoldersCache } from "../abstractions.js"; +import { FoldersContext } from "../abstractions.js"; +import { CreateFolderUseCase } from "./abstractions.js"; +import { CreateFolderFeature } from "./feature.js"; +import { CreateFolderGateway } from "./abstractions.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; -class CreateFolderMockGateway implements ICreateFolderGateway { +class CreateFolderMockGateway implements CreateFolderGateway.Interface { async execute() { return { id: "any-folder-id", title: "New Folder", slug: "new-folder", type: "abc" - } as FolderGqlDto; // We don't care about the rest of the props, hence the type assertion. + } as FolderDto; } } @@ -19,17 +24,27 @@ describe("CreateFolder", () => { const type = "abc"; const gateway = new CreateFolderMockGateway(); - const foldersCache = folderCacheFactory.getCache(type); + let container: Container; + const foldersCache = new ListCache(); beforeEach(() => { + container = new Container(); foldersCache.clear(); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + + CreateFolderFeature.register(container); + + // Replace the feature gateway with a mock + container.registerInstance(CreateFolderGateway, gateway); }); it("should be able to create a new folder", async () => { const spy = vi.spyOn(gateway, "execute"); - const createFolder = CreateFolder.getInstance(type, gateway); + const createFolder = container.resolve(CreateFolderUseCase); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); await createFolder.execute({ title: "New Folder", @@ -40,7 +55,7 @@ describe("CreateFolder", () => { }); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); const item = foldersCache.getItem(folder => folder.slug === "new-folder"); diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolder.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolder.ts deleted file mode 100644 index 8a72d2f7e62..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolder.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { ICreateFolderUseCase } from "./ICreateFolderUseCase.js"; -import type { ICreateFolderGateway } from "./ICreateFolderGateway.js"; -import { CreateFolderRepository } from "./CreateFolderRepository.js"; -import { CreateFolderUseCase } from "./CreateFolderUseCase.js"; -import { CreateFolderUseCaseWithLoading } from "./CreateFolderUseCaseWithLoading.js"; -import { folderCacheFactory } from "../cache/index.js"; - -export class CreateFolder { - public static getInstance(type: string, gateway: ICreateFolderGateway): ICreateFolderUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new CreateFolderRepository(foldersCache, gateway, type); - const useCase = new CreateFolderUseCase(repository); - return new CreateFolderUseCaseWithLoading(loadingRepository, useCase); - } -} diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolderGqlGateway.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolderGqlGateway.ts index de567c6b76f..33ded207805 100644 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolderGqlGateway.ts +++ b/packages/app-aco/src/features/folders/createFolder/CreateFolderGqlGateway.ts @@ -1,13 +1,15 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { ICreateFolderGateway } from "./ICreateFolderGateway.js"; -import type { FolderDto } from "./FolderDto.js"; -import type { AcoError, FolderItem } from "~/types.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import type { FolderGatewayDto } from "./abstractions.js"; +import { CreateFolderGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; export interface CreateFolderResponse { aco: { createFolder: { - data: FolderItem; + data: FolderDto; error: AcoError | null; }; }; @@ -15,7 +17,7 @@ export interface CreateFolderResponse { export interface CreateFolderVariables { data: Omit< - FolderItem, + FolderDto, | "id" | "path" | "createdOn" @@ -46,21 +48,20 @@ export const CREATE_FOLDER = (FOLDER_FIELDS: string) => gql` } `; -export class CreateFolderGqlGateway implements ICreateFolderGateway { - private client: ApolloClient; - private modelFields: string; +class CreateFolderGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } + async execute(folder: FolderGatewayDto) { + const fields = await this.folderModelProvider.getGraphQLSelection(); - async execute(folder: FolderDto) { const { data: response } = await this.client.mutate< CreateFolderResponse, CreateFolderVariables >({ - mutation: CREATE_FOLDER(this.modelFields), + mutation: CREATE_FOLDER(fields), variables: { data: { ...folder @@ -81,3 +82,8 @@ export class CreateFolderGqlGateway implements ICreateFolderGateway { return data; } } + +export const CreateFolderGqlGateway = GatewayAbstraction.createImplementation({ + implementation: CreateFolderGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolderRepository.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolderRepository.ts index 5215a20a2d6..29b8747928f 100644 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolderRepository.ts +++ b/packages/app-aco/src/features/folders/createFolder/CreateFolderRepository.ts @@ -1,26 +1,23 @@ -import type { ICreateFolderRepository } from "./ICreateFolderRepository.js"; -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; -import type { ICreateFolderGateway } from "./ICreateFolderGateway.js"; -import type { FolderDto } from "./FolderDto.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import type { FolderGatewayDto } from "./abstractions.js"; +import { CreateFolderGateway } from "./abstractions.js"; +import { CreateFolderRepository as RepositoryAbstraction } from "./abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { Folder } from "~/domain/folder/Folder.js"; -export class CreateFolderRepository implements ICreateFolderRepository { - private cache: ListCache; - private gateway: ICreateFolderGateway; - private readonly type: string; - - constructor(cache: ListCache, gateway: ICreateFolderGateway, type: string) { - this.cache = cache; - this.gateway = gateway; - this.type = type; - } +class CreateFolderRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private foldersContext: FoldersContext.Interface, + private gateway: CreateFolderGateway.Interface + ) {} async execute(folder: Folder) { - const dto: FolderDto = { + const dto: FolderGatewayDto = { title: folder.title, slug: folder.slug, permissions: folder.permissions, - type: this.type, + type: this.foldersContext.type, parentId: folder.parentId, extensions: folder.extensions }; @@ -29,3 +26,8 @@ export class CreateFolderRepository implements ICreateFolderRepository { this.cache.addItems([Folder.create(result)]); } } + +export const CreateFolderRepository = RepositoryAbstraction.createImplementation({ + implementation: CreateFolderRepositoryImpl, + dependencies: [FoldersCache, FoldersContext, CreateFolderGateway] +}); diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCase.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCase.ts index 923f9d8cf86..e2f1c86433f 100644 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCase.ts +++ b/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCase.ts @@ -1,15 +1,13 @@ -import type { CreateFolderParams, ICreateFolderUseCase } from "./ICreateFolderUseCase.js"; -import type { ICreateFolderRepository } from "./ICreateFolderRepository.js"; -import { Folder } from "../Folder.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { + CreateFolderUseCase as UseCaseAbstraction, + CreateFolderRepository +} from "./abstractions.js"; -export class CreateFolderUseCase implements ICreateFolderUseCase { - private repository: ICreateFolderRepository; +class CreateFolderUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: CreateFolderRepository.Interface) {} - constructor(repository: ICreateFolderRepository) { - this.repository = repository; - } - - async execute(params: CreateFolderParams) { + async execute(params: UseCaseAbstraction.Params) { await this.repository.execute( Folder.create({ title: params.title, @@ -22,3 +20,8 @@ export class CreateFolderUseCase implements ICreateFolderUseCase { ); } } + +export const CreateFolderUseCase = UseCaseAbstraction.createImplementation({ + implementation: CreateFolderUseCaseImpl, + dependencies: [CreateFolderRepository] +}); diff --git a/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCaseWithLoading.ts deleted file mode 100644 index 7d56c368587..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/CreateFolderUseCaseWithLoading.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; -import type { CreateFolderParams, ICreateFolderUseCase } from "./ICreateFolderUseCase.js"; -import { LoadingActionsEnum } from "~/types.js"; - -export class CreateFolderUseCaseWithLoading implements ICreateFolderUseCase { - private loadingRepository: ILoadingRepository; - private useCase: ICreateFolderUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: ICreateFolderUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } - - async execute(params: CreateFolderParams) { - await this.loadingRepository.runCallBack( - this.useCase.execute(params), - LoadingActionsEnum.create - ); - } -} diff --git a/packages/app-aco/src/features/folders/createFolder/FolderDto.ts b/packages/app-aco/src/features/folders/createFolder/FolderDto.ts deleted file mode 100644 index b5a43e77be7..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/FolderDto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface FolderDto { - title: string; - slug: string; - permissions: FolderPermission[]; - type: string; - parentId: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/createFolder/FolderGqlDto.ts b/packages/app-aco/src/features/folders/createFolder/FolderGqlDto.ts deleted file mode 100644 index f802d031281..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/FolderGqlDto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; - -export interface FolderGqlDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/createFolder/ICreateFolderGateway.ts b/packages/app-aco/src/features/folders/createFolder/ICreateFolderGateway.ts deleted file mode 100644 index 396be8c1795..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/ICreateFolderGateway.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { FolderDto } from "./FolderDto.js"; -import type { FolderGqlDto } from "./FolderGqlDto.js"; - -export interface ICreateFolderGateway { - execute: (folderDto: FolderDto) => Promise; -} diff --git a/packages/app-aco/src/features/folders/createFolder/ICreateFolderRepository.ts b/packages/app-aco/src/features/folders/createFolder/ICreateFolderRepository.ts deleted file mode 100644 index ae1fd6101cc..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/ICreateFolderRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Folder } from "../Folder.js"; - -export interface ICreateFolderRepository { - execute: (folder: Folder) => Promise; -} diff --git a/packages/app-aco/src/features/folders/createFolder/ICreateFolderUseCase.ts b/packages/app-aco/src/features/folders/createFolder/ICreateFolderUseCase.ts deleted file mode 100644 index 5a6d2ee3083..00000000000 --- a/packages/app-aco/src/features/folders/createFolder/ICreateFolderUseCase.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface CreateFolderParams { - title: string; - slug: string; - type: string; - parentId: string | null; - permissions: FolderPermission[]; - extensions?: Record; -} - -export interface ICreateFolderUseCase { - execute: (params: CreateFolderParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/createFolder/abstractions.ts b/packages/app-aco/src/features/folders/createFolder/abstractions.ts new file mode 100644 index 00000000000..4b4019f3fe7 --- /dev/null +++ b/packages/app-aco/src/features/folders/createFolder/abstractions.ts @@ -0,0 +1,58 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { Folder } from "~/domain/folder/Folder.js"; +import type { FolderPermission } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; + +// Use Case + +export interface CreateFolderParams { + title: string; + slug: string; + type: string; + parentId: string | null; + permissions: FolderPermission[]; + extensions?: Record; +} + +export interface ICreateFolderUseCase { + execute: (params: CreateFolderParams) => Promise; +} + +export const CreateFolderUseCase = createAbstraction("CreateFolderUseCase"); + +export namespace CreateFolderUseCase { + export type Interface = ICreateFolderUseCase; + export type Params = CreateFolderParams; +} + +// Repository + +export interface ICreateFolderRepository { + execute: (folder: Folder) => Promise; +} + +export const CreateFolderRepository = + createAbstraction("CreateFolderRepository"); + +export namespace CreateFolderRepository { + export type Interface = ICreateFolderRepository; +} + +// Gateway +export interface ICreateFolderGateway { + execute: (folderDto: FolderGatewayDto) => Promise; +} + +export const CreateFolderGateway = createAbstraction("CreateFolderGateway"); +export namespace CreateFolderGateway { + export type Interface = ICreateFolderGateway; +} + +export interface FolderGatewayDto { + title: string; + slug: string; + permissions: FolderPermission[]; + type: string; + parentId: string | null; + extensions: Record; +} diff --git a/packages/app-aco/src/features/folders/createFolder/feature.ts b/packages/app-aco/src/features/folders/createFolder/feature.ts new file mode 100644 index 00000000000..77a7428d77e --- /dev/null +++ b/packages/app-aco/src/features/folders/createFolder/feature.ts @@ -0,0 +1,19 @@ +import { createFeature } from "@webiny/feature/admin"; +import { CreateFolderUseCase as UseCase } from "./abstractions.js"; +import { CreateFolderUseCase } from "./CreateFolderUseCase.js"; +import { CreateFolderRepository } from "./CreateFolderRepository.js"; +import { CreateFolderGqlGateway } from "./CreateFolderGqlGateway.js"; + +export const CreateFolderFeature = createFeature({ + name: "CreateFolder", + register(container) { + container.register(CreateFolderUseCase); + container.register(CreateFolderRepository).inSingletonScope(); + container.register(CreateFolderGqlGateway); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/createFolder/useCreateFolder.ts b/packages/app-aco/src/features/folders/createFolder/useCreateFolder.ts index 7a0308334ac..ea95bc67d03 100644 --- a/packages/app-aco/src/features/folders/createFolder/useCreateFolder.ts +++ b/packages/app-aco/src/features/folders/createFolder/useCreateFolder.ts @@ -1,25 +1,13 @@ -import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { CreateFolderGqlGateway } from "./CreateFolderGqlGateway.js"; -import type { CreateFolderParams } from "./ICreateFolderUseCase.js"; -import { CreateFolder } from "./CreateFolder.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; +import { useFeature } from "@webiny/app"; +import { CreateFolderUseCase } from "~/features/folders/createFolder/abstractions.js"; +import { CreateFolderFeature } from "./feature.js"; export const useCreateFolder = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new CreateFolderGqlGateway(client, fields); - - const createFolder = useCallback( - (params: CreateFolderParams) => { - const instance = CreateFolder.getInstance(type, gateway); - return instance.execute(params); - }, - [type, gateway] - ); + const { useCase } = useFeature(CreateFolderFeature); return { - createFolder + createFolder: (folder: CreateFolderUseCase.Params) => { + return useCase.execute(folder); + } }; }; diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.test.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.test.ts index b5fef353551..bee030e43a9 100644 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.test.ts +++ b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.test.ts @@ -1,10 +1,13 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; -import { DeleteFolder } from "./DeleteFolder.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { Folder } from "../Folder.js"; -import { type IDeleteFolderGateway } from "~/features/folders/deleteFolder/IDeleteFolderGateway.js"; - -class DeleteFolderMockGateway implements IDeleteFolderGateway { +import { Container } from "@webiny/di"; +import { FoldersCache } from "../abstractions.js"; +import { FoldersContext } from "../abstractions.js"; +import { DeleteFolderUseCase, DeleteFolderGateway } from "./abstractions.js"; +import { DeleteFolderFeature } from "./feature.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { ListCache } from "~/features/folders/cache/index.js"; + +class DeleteFolderMockGateway implements DeleteFolderGateway.Interface { async execute() {} } @@ -12,10 +15,22 @@ describe("DeleteFolder", () => { const type = "abc"; const gateway = new DeleteFolderMockGateway(); - const foldersCache = folderCacheFactory.getCache(type); + let container: Container; + const foldersCache = new ListCache(); beforeEach(() => { foldersCache.clear(); + + container = new Container(); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + + DeleteFolderFeature.register(container); + + // Mock the gateway + container.registerInstance(DeleteFolderGateway, gateway); + foldersCache.addItems([ Folder.create({ id: "any-folder-id", @@ -30,22 +45,15 @@ describe("DeleteFolder", () => { it("should be able to delete a folder", async () => { const spy = vi.spyOn(gateway, "execute"); - const deleteFolder = DeleteFolder.getInstance(type, gateway); + const deleteFolder = container.resolve(DeleteFolderUseCase); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); const item = foldersCache.getItem(folder => folder.id === "any-folder-id"); expect(item?.id).toEqual("any-folder-id"); - await deleteFolder.execute({ - id: "any-folder-id", - title: "New Folder", - slug: "new-folder", - parentId: null, - permissions: [], - type - }); + await deleteFolder.execute("any-folder-id"); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); }); }); diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.ts deleted file mode 100644 index ceb553c401b..00000000000 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolder.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IDeleteFolderUseCase } from "./IDeleteFolderUseCase.js"; -import { DeleteFolderRepository } from "./DeleteFolderRepository.js"; -import { DeleteFolderUseCase } from "./DeleteFolderUseCase.js"; -import { DeleteFolderUseCaseWithLoading } from "./DeleteFolderUseCaseWithLoading.js"; -import type { IDeleteFolderGateway } from "./IDeleteFolderGateway.js"; -import { folderCacheFactory } from "../cache/index.js"; - -export class DeleteFolder { - public static getInstance(type: string, gateway: IDeleteFolderGateway): IDeleteFolderUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new DeleteFolderRepository(foldersCache, gateway); - const useCase = new DeleteFolderUseCase(repository); - return new DeleteFolderUseCaseWithLoading(loadingRepository, useCase); - } -} diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderGqlGateway.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderGqlGateway.ts index 341185e2bfe..7b20acbb528 100644 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderGqlGateway.ts +++ b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderGqlGateway.ts @@ -1,6 +1,6 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { IDeleteFolderGateway } from "./IDeleteFolderGateway.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import { DeleteFolderGateway as GatewayAbstraction } from "./abstractions.js"; import type { AcoError } from "~/types.js"; export interface DeleteFolderVariables { @@ -31,12 +31,8 @@ export const DELETE_FOLDER = gql` } `; -export class DeleteFolderGqlGateway implements IDeleteFolderGateway { - private client: ApolloClient; - - constructor(client: ApolloClient) { - this.client = client; - } +class DeleteFolderGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor(private client: ApolloClient.Interface) {} async execute(id: string) { const { data: response } = await this.client.mutate< @@ -62,3 +58,8 @@ export class DeleteFolderGqlGateway implements IDeleteFolderGateway { return; } } + +export const DeleteFolderGqlGateway = GatewayAbstraction.createImplementation({ + implementation: DeleteFolderGqlGatewayImpl, + dependencies: [ApolloClient] +}); diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderRepository.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderRepository.ts index e335456e7d4..903fcbd6c8c 100644 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderRepository.ts +++ b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderRepository.ts @@ -1,19 +1,20 @@ -import type { IDeleteFolderRepository } from "./IDeleteFolderRepository.js"; -import type { ListCache } from "../cache/index.js"; -import type { Folder } from "../Folder.js"; -import type { IDeleteFolderGateway } from "./IDeleteFolderGateway.js"; +import { DeleteFolderGateway } from "./abstractions.js"; +import { DeleteFolderRepository as RepositoryAbstraction } from "./abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; -export class DeleteFolderRepository implements IDeleteFolderRepository { - private cache: ListCache; - private gateway: IDeleteFolderGateway; +class DeleteFolderRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private gateway: DeleteFolderGateway.Interface + ) {} - constructor(cache: ListCache, gateway: IDeleteFolderGateway) { - this.cache = cache; - this.gateway = gateway; - } - - async execute(folder: Folder) { - await this.gateway.execute(folder.id); - this.cache.removeItems(f => f.id === folder.id); + async execute(id: string) { + await this.gateway.execute(id); + this.cache.removeItems(f => f.id === id); } } + +export const DeleteFolderRepository = RepositoryAbstraction.createImplementation({ + implementation: DeleteFolderRepositoryImpl, + dependencies: [FoldersCache, DeleteFolderGateway] +}); diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCase.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCase.ts index 3c4cff39504..f8d5ae7b125 100644 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCase.ts +++ b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCase.ts @@ -1,24 +1,17 @@ -import type { DeleteFolderParams, IDeleteFolderUseCase } from "./IDeleteFolderUseCase.js"; -import type { IDeleteFolderRepository } from "./IDeleteFolderRepository.js"; -import { Folder } from "../Folder.js"; +import { + DeleteFolderUseCase as UseCaseAbstraction, + DeleteFolderRepository +} from "./abstractions.js"; -export class DeleteFolderUseCase implements IDeleteFolderUseCase { - private repository: IDeleteFolderRepository; +class DeleteFolderUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: DeleteFolderRepository.Interface) {} - constructor(repository: IDeleteFolderRepository) { - this.repository = repository; - } - - async execute(params: DeleteFolderParams) { - await this.repository.execute( - Folder.create({ - id: params.id, - title: params.title, - slug: params.slug, - type: params.type, - parentId: params.parentId, - permissions: params.permissions - }) - ); + async execute(id: string) { + await this.repository.execute(id); } } + +export const DeleteFolderUseCase = UseCaseAbstraction.createImplementation({ + implementation: DeleteFolderUseCaseImpl, + dependencies: [DeleteFolderRepository] +}); diff --git a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCaseWithLoading.ts deleted file mode 100644 index f435ff25985..00000000000 --- a/packages/app-aco/src/features/folders/deleteFolder/DeleteFolderUseCaseWithLoading.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; -import { LoadingActionsEnum } from "~/types.js"; -import type { DeleteFolderParams, IDeleteFolderUseCase } from "./IDeleteFolderUseCase.js"; - -export class DeleteFolderUseCaseWithLoading implements IDeleteFolderUseCase { - private loadingRepository: ILoadingRepository; - private useCase: IDeleteFolderUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: IDeleteFolderUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } - - async execute(params: DeleteFolderParams) { - await this.loadingRepository.runCallBack( - this.useCase.execute(params), - LoadingActionsEnum.delete - ); - } -} diff --git a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderGateway.ts b/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderGateway.ts deleted file mode 100644 index 852a065ec5e..00000000000 --- a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderGateway.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IDeleteFolderGateway { - execute: (id: string) => Promise; -} diff --git a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderRepository.ts b/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderRepository.ts deleted file mode 100644 index 37b13a79395..00000000000 --- a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Folder } from "../Folder.js"; - -export interface IDeleteFolderRepository { - execute: (folder: Folder) => Promise; -} diff --git a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderUseCase.ts b/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderUseCase.ts deleted file mode 100644 index 1ebda8010e8..00000000000 --- a/packages/app-aco/src/features/folders/deleteFolder/IDeleteFolderUseCase.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface DeleteFolderParams { - id: string; - title: string; - slug: string; - type: string; - parentId: string | null; - permissions: FolderPermission[]; -} - -export interface IDeleteFolderUseCase { - execute: (params: DeleteFolderParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/deleteFolder/abstractions.ts b/packages/app-aco/src/features/folders/deleteFolder/abstractions.ts new file mode 100644 index 00000000000..7c7fec4f32c --- /dev/null +++ b/packages/app-aco/src/features/folders/deleteFolder/abstractions.ts @@ -0,0 +1,37 @@ +import { createAbstraction } from "@webiny/feature/admin"; + +// Use Case + +export interface IDeleteFolderUseCase { + execute: (id: string) => Promise; +} + +export const DeleteFolderUseCase = createAbstraction("DeleteFolderUseCase"); + +export namespace DeleteFolderUseCase { + export type Interface = IDeleteFolderUseCase; +} + +// Repository + +export interface IDeleteFolderRepository { + execute: (id: string) => Promise; +} + +export const DeleteFolderRepository = + createAbstraction("DeleteFolderRepository"); + +export namespace DeleteFolderRepository { + export type Interface = IDeleteFolderRepository; +} + +// Gateway +export interface IDeleteFolderGateway { + execute: (id: string) => Promise; +} + +export const DeleteFolderGateway = createAbstraction("DeleteFolderGateway"); + +export namespace DeleteFolderGateway { + export type Interface = IDeleteFolderGateway; +} diff --git a/packages/app-aco/src/features/folders/deleteFolder/feature.ts b/packages/app-aco/src/features/folders/deleteFolder/feature.ts new file mode 100644 index 00000000000..493863072a0 --- /dev/null +++ b/packages/app-aco/src/features/folders/deleteFolder/feature.ts @@ -0,0 +1,19 @@ +import { createFeature } from "@webiny/feature/admin"; +import { DeleteFolderUseCase as UseCase } from "./abstractions.js"; +import { DeleteFolderUseCase } from "./DeleteFolderUseCase.js"; +import { DeleteFolderRepository } from "./DeleteFolderRepository.js"; +import { DeleteFolderGqlGateway } from "./DeleteFolderGqlGateway.js"; + +export const DeleteFolderFeature = createFeature({ + name: "DeleteFolder", + register(container) { + container.register(DeleteFolderUseCase); + container.register(DeleteFolderRepository).inSingletonScope(); + container.register(DeleteFolderGqlGateway); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/deleteFolder/useDeleteFolder.ts b/packages/app-aco/src/features/folders/deleteFolder/useDeleteFolder.ts index 68c9409d98e..73fd630d4b6 100644 --- a/packages/app-aco/src/features/folders/deleteFolder/useDeleteFolder.ts +++ b/packages/app-aco/src/features/folders/deleteFolder/useDeleteFolder.ts @@ -1,24 +1,12 @@ -import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { DeleteFolderGqlGateway } from "./DeleteFolderGqlGateway.js"; -import type { DeleteFolderParams } from "./IDeleteFolderUseCase.js"; -import { DeleteFolder } from "./DeleteFolder.js"; -import { useFoldersType } from "~/hooks/index.js"; +import { useFeature } from "@webiny/app"; +import { DeleteFolderFeature } from "./feature.js"; export const useDeleteFolder = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const gateway = new DeleteFolderGqlGateway(client); - - const deleteFolder = useCallback( - (params: DeleteFolderParams) => { - const instance = DeleteFolder.getInstance(type, gateway); - return instance.execute(params); - }, - [type, gateway] - ); + const { useCase } = useFeature(DeleteFolderFeature); return { - deleteFolder + deleteFolder: (id: string) => { + return useCase.execute(id); + } }; }; diff --git a/packages/app-aco/src/features/folders/feature.ts b/packages/app-aco/src/features/folders/feature.ts new file mode 100644 index 00000000000..d08c9035eef --- /dev/null +++ b/packages/app-aco/src/features/folders/feature.ts @@ -0,0 +1,50 @@ +import { loadingRepositoryFactory } from "@webiny/app-utils"; +import { createFeature } from "@webiny/feature/admin"; +import { loadedFolderCacheFactory } from "~/features/folders/cache/index.js"; +import { folderCacheFactory } from "~/features/folders/cache/index.js"; +import { GetDescendantFoldersFeature } from "~/features/folders/getDescendantFolders/feature.js"; +import { GetFolderAncestorsFeature } from "~/features/folders/getFolderAncestors/feature.js"; +import { GetFolderExtensionsFieldsFeature } from "~/features/folders/getFolderExtensionsFields/feature.js"; +import { GetFolderLevelPermissionFeature } from "~/features/folders/getFolderLevelPermission/feature.js"; +import { ListFoldersFeature } from "~/features/folders/listFolders/feature.js"; +import { ListFoldersByParentIdsFeature } from "~/features/folders/listFoldersByParentIds/feature.js"; +import { LoadFolderHierarchyFeature } from "~/features/folders/loadFolderHierarchy/feature.js"; +import { LoadedFoldersCache } from "./abstractions.js"; +import { FoldersCache } from "./abstractions.js"; +import { FoldersContext } from "./abstractions.js"; +import { FoldersLoadingRepository } from "./abstractions.js"; +import { DeleteFolderFeature } from "./deleteFolder/feature.js"; +import { CreateFolderFeature } from "./createFolder/feature.js"; +import { GetFolderFeature } from "./getFolder/feature.js"; +import { UpdateFolderFeature } from "./updateFolder/feature.js"; + +export const FoldersFeature = createFeature({ + name: "Folders", + register(container, context: FoldersContext.Interface) { + // FoldersContext is used by other features to load the right folders type. + container.registerInstance(FoldersContext, context); + + container.registerInstance(FoldersCache, folderCacheFactory.getCache(context.type)); + + container.registerInstance( + LoadedFoldersCache, + loadedFolderCacheFactory.getCache(context.type) + ); + + const loadingRepository = loadingRepositoryFactory.getRepository(context.type); + container.registerInstance(FoldersLoadingRepository, loadingRepository); + + // Folders features + CreateFolderFeature.register(container); + UpdateFolderFeature.register(container); + DeleteFolderFeature.register(container); + GetFolderFeature.register(container); + GetDescendantFoldersFeature.register(container); + GetFolderAncestorsFeature.register(container); + GetFolderExtensionsFieldsFeature.register(container); + GetFolderLevelPermissionFeature.register(container); + LoadFolderHierarchyFeature.register(container); + ListFoldersFeature.register(container); + ListFoldersByParentIdsFeature.register(container); + } +}); diff --git a/packages/app-aco/src/features/folders/folderModelProvider/FolderModelContext.tsx b/packages/app-aco/src/features/folders/folderModelProvider/FolderModelContext.tsx new file mode 100644 index 00000000000..7e8d7ae8c8d --- /dev/null +++ b/packages/app-aco/src/features/folders/folderModelProvider/FolderModelContext.tsx @@ -0,0 +1,27 @@ +import { createProvider } from "@webiny/app"; +import { useContainer } from "@webiny/app"; +import React, { useEffect } from "react"; +import { Plugin } from "@webiny/app"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { FolderModelProviderFeature } from "./feature.js"; + +const AcoFolderModelProvider = createProvider(Original => { + return function AcoFolderProvider({ children }) { + const container = useContainer(); + + useEffect(() => { + FolderModelProviderFeature.register(container); + + const provider = container.resolve(FolderModelProvider); + + // Trigger loading of a model + provider.getModel(); + }, []); + + return {children}; + }; +}); + +export const FolderModelProviderModule = () => { + return ; +}; diff --git a/packages/app-aco/src/features/folders/getFolderModel/FolderModelDto.ts b/packages/app-aco/src/features/folders/folderModelProvider/FolderModelDto.ts similarity index 100% rename from packages/app-aco/src/features/folders/getFolderModel/FolderModelDto.ts rename to packages/app-aco/src/features/folders/folderModelProvider/FolderModelDto.ts diff --git a/packages/app-aco/src/features/folders/folderModelProvider/FolderModelProvider.ts b/packages/app-aco/src/features/folders/folderModelProvider/FolderModelProvider.ts new file mode 100644 index 00000000000..3dfeb490f79 --- /dev/null +++ b/packages/app-aco/src/features/folders/folderModelProvider/FolderModelProvider.ts @@ -0,0 +1,60 @@ +import { createFieldsList } from "@webiny/app-headless-cms-common"; +import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; +import { GetFolderModelRepository } from "./abstractions.js"; +import { FolderModelProvider as Provider } from "~/features/folders/abstractions.js"; + +class FolderModelProviderImpl implements Provider.Interface { + constructor(private repository: GetFolderModelRepository.Interface) {} + + async getModel(): Promise { + await this.repository.load(); + + const model = this.repository.getModel(); + if (!model) { + // Something went seriously wrong! + throw new Error("Unable to load File model!"); + } + + return model; + } + + async getGraphQLSelection(): Promise { + const model = await this.getModel(); + + const fields = createFieldsList({ model, fields: model.fields }); + + return /* GraphQL */ `{ + id + createdOn + createdBy { + id + displayName + } + savedOn + savedBy { + id + displayName + } + modifiedOn + modifiedBy { + id + displayName + } + permissions { + target + level + inheritedFrom + } + hasNonInheritedPermissions + canManagePermissions + canManageStructure + canManageContent + ${fields} + }`; + } +} + +export const FolderModelProvider = Provider.createImplementation({ + implementation: FolderModelProviderImpl, + dependencies: [GetFolderModelRepository] +}); diff --git a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelGqlGateway.ts b/packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelGqlGateway.ts similarity index 70% rename from packages/app-aco/src/features/folders/getFolderModel/GetFolderModelGqlGateway.ts rename to packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelGqlGateway.ts index 0067473f96a..1e665548db1 100644 --- a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelGqlGateway.ts +++ b/packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelGqlGateway.ts @@ -1,7 +1,7 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import { GetFolderModelGateway as GatewayAbstraction } from "./abstractions.js"; import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; -import type { IGetFolderModelGateway } from "./IGetFolderModelGateway.js"; import type { AcoError } from "~/types.js"; export interface GetFolderModelResponse { @@ -29,12 +29,8 @@ export const GET_FOLDER_MODEL = gql` } `; -export class GetFolderModelGqlGateway implements IGetFolderModelGateway { - private client: ApolloClient; - - constructor(client: ApolloClient) { - this.client = client; - } +class GetFolderModelGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor(private client: ApolloClient.Interface) {} async execute() { const { data: response } = await this.client.query({ @@ -55,3 +51,8 @@ export class GetFolderModelGqlGateway implements IGetFolderModelGateway { return data; } } + +export const GetFolderModelGqlGateway = GatewayAbstraction.createImplementation({ + implementation: GetFolderModelGqlGatewayImpl, + dependencies: [ApolloClient] +}); diff --git a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelRepository.ts b/packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelRepository.ts similarity index 55% rename from packages/app-aco/src/features/folders/getFolderModel/GetFolderModelRepository.ts rename to packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelRepository.ts index 76c52fd781f..204b4bc9cc2 100644 --- a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelRepository.ts +++ b/packages/app-aco/src/features/folders/folderModelProvider/GetFolderModelRepository.ts @@ -1,14 +1,14 @@ import { makeAutoObservable, runInAction } from "mobx"; -import type { IGetFolderModelRepository } from "./IGetFolderModelRepository.js"; -import type { IGetFolderModelGateway } from "./IGetFolderModelGateway.js"; +import { + GetFolderModelRepository as RepositoryAbstraction, + GetFolderModelGateway +} from "./abstractions.js"; import type { FolderModelDto } from "./FolderModelDto.js"; -export class GetFolderModelRepository implements IGetFolderModelRepository { +class GetFolderModelRepositoryImpl implements RepositoryAbstraction.Interface { private model: FolderModelDto | undefined; - private gateway: IGetFolderModelGateway; - constructor(gateway: IGetFolderModelGateway) { - this.gateway = gateway; + constructor(private gateway: GetFolderModelGateway.Interface) { this.model = undefined; makeAutoObservable(this); } @@ -30,3 +30,8 @@ export class GetFolderModelRepository implements IGetFolderModelRepository { return Boolean(this.model); } } + +export const GetFolderModelRepository = RepositoryAbstraction.createImplementation({ + implementation: GetFolderModelRepositoryImpl, + dependencies: [GetFolderModelGateway] +}); diff --git a/packages/app-aco/src/features/folders/folderModelProvider/abstractions.ts b/packages/app-aco/src/features/folders/folderModelProvider/abstractions.ts new file mode 100644 index 00000000000..6d52f9afddc --- /dev/null +++ b/packages/app-aco/src/features/folders/folderModelProvider/abstractions.ts @@ -0,0 +1,31 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderModelDto } from "./FolderModelDto.js"; + +export interface IGetFolderModelUseCase { + execute: () => Promise; +} + +export interface IGetFolderModelRepository { + load: () => Promise; + getModel: () => FolderModelDto | undefined; + hasModel: () => boolean; +} + +export interface IGetFolderModelGateway { + execute: () => Promise; +} + +export const GetFolderModelRepository = createAbstraction( + "GetFolderModelRepository" +); + +export namespace GetFolderModelRepository { + export type Interface = IGetFolderModelRepository; +} + +export const GetFolderModelGateway = + createAbstraction("GetFolderModelGateway"); + +export namespace GetFolderModelGateway { + export type Interface = IGetFolderModelGateway; +} diff --git a/packages/app-aco/src/features/folders/folderModelProvider/feature.ts b/packages/app-aco/src/features/folders/folderModelProvider/feature.ts new file mode 100644 index 00000000000..fca86728c77 --- /dev/null +++ b/packages/app-aco/src/features/folders/folderModelProvider/feature.ts @@ -0,0 +1,19 @@ +import { createFeature } from "@webiny/feature/admin"; +import { FolderModelProvider as ProviderAbstraction } from "~/features/folders/abstractions.js"; +import { FolderModelProvider } from "./FolderModelProvider.js"; +import { GetFolderModelRepository } from "./GetFolderModelRepository.js"; +import { GetFolderModelGqlGateway } from "./GetFolderModelGqlGateway.js"; + +export const FolderModelProviderFeature = createFeature({ + name: "FolderModelProvider", + register(container) { + container.register(FolderModelProvider); + container.register(GetFolderModelRepository).inSingletonScope(); + container.register(GetFolderModelGqlGateway); + }, + resolve(container) { + return { + provider: container.resolve(ProviderAbstraction) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/folderModelProvider/index.ts b/packages/app-aco/src/features/folders/folderModelProvider/index.ts new file mode 100644 index 00000000000..ea9ebfb8417 --- /dev/null +++ b/packages/app-aco/src/features/folders/folderModelProvider/index.ts @@ -0,0 +1 @@ +export * from "./abstractions.js"; diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/FolderDto.ts b/packages/app-aco/src/features/folders/getDescendantFolders/FolderDto.ts deleted file mode 100644 index 29d78c050bf..00000000000 --- a/packages/app-aco/src/features/folders/getDescendantFolders/FolderDto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - type: string; - parentId: string | null; -} diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.test.ts b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.test.ts index 36d1802b9a1..3fab7caa28e 100644 --- a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.test.ts +++ b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.test.ts @@ -1,14 +1,19 @@ -import { describe, it, expect, beforeEach } from "vitest"; -import { GetDescendantFolders } from "./GetDescendantFolders.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { Folder } from "../Folder.js"; +import { describe, it, expect } from "vitest"; +import { Container } from "@webiny/di"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { GetDescendantFoldersUseCase } from "~/features/folders/getDescendantFolders/abstractions.js"; +import { GetDescendantFoldersFeature } from "~/features/folders/getDescendantFolders/feature.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; describe("GetDescendantFolders", () => { const type = "abc"; - const foldersCache = folderCacheFactory.getCache(type); - beforeEach(() => { - foldersCache.clear(); + function setupTest() { + const container = new Container(); + const foldersCache = new ListCache(); + foldersCache.addItems([ Folder.create({ id: "folder-1", @@ -51,14 +56,19 @@ describe("GetDescendantFolders", () => { type }) ]); - }); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + + GetDescendantFoldersFeature.register(container); + + return { container, foldersCache, useCase: container.resolve(GetDescendantFoldersUseCase) }; + } it("should return all descendants of a folder", async () => { - const getDescendantFolders = GetDescendantFolders.getInstance(type); + const { useCase } = setupTest(); - const descendants = getDescendantFolders.execute({ - id: "folder-2" - }); + const descendants = useCase.execute("folder-2"); expect(descendants).toEqual([ { @@ -97,11 +107,9 @@ describe("GetDescendantFolders", () => { }); it("should return the folder it self in case no descendants are found", async () => { - const getDescendantFolders = GetDescendantFolders.getInstance(type); + const { useCase } = setupTest(); - const descendants = getDescendantFolders.execute({ - id: "folder-1" - }); + const descendants = useCase.execute("folder-1"); expect(descendants).toEqual([ { @@ -116,11 +124,9 @@ describe("GetDescendantFolders", () => { }); it("should return empty array if folder does not exist", async () => { - const getDescendantFolders = GetDescendantFolders.getInstance(type); + const { useCase } = setupTest(); - const descendants = getDescendantFolders.execute({ - id: "non-existent-folder" - }); + const descendants = useCase.execute("non-existent-folder"); expect(descendants).toEqual([]); }); diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.ts b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.ts deleted file mode 100644 index 34d8f0772c4..00000000000 --- a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFolders.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { IGetDescendantFoldersUseCase } from "./IGetDescendantFoldersUseCase.js"; -import { GetDescendantFoldersRepository } from "./GetDescendantFoldersRepository.js"; -import { GetDescendantFoldersUseCase } from "./GetDescendantFoldersUseCase.js"; -import { folderCacheFactory } from "../cache/index.js"; - -export class GetDescendantFolders { - public static getInstance(type: string): IGetDescendantFoldersUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const repository = new GetDescendantFoldersRepository(foldersCache); - return new GetDescendantFoldersUseCase(repository); - } -} diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersRepository.ts b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersRepository.ts index f0fe5036755..338d0fd81b7 100644 --- a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersRepository.ts +++ b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersRepository.ts @@ -1,16 +1,12 @@ -import type { IGetDescendantFoldersRepository } from "./IGetDescendantFoldersRepository.js"; -import type { ListCache } from "../cache/index.js"; -import type { Folder } from "../Folder.js"; +import { GetDescendantFoldersRepository as RepositoryAbstraction } from "./abstractions.js"; +import type { FolderDto } from "./abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; import { ROOT_FOLDER } from "~/constants.js"; -export class GetDescendantFoldersRepository implements IGetDescendantFoldersRepository { - private readonly cache: ListCache; +class GetDescendantFoldersRepositoryImpl implements RepositoryAbstraction.Interface { + constructor(private cache: FoldersCache.Interface) {} - constructor(cache: ListCache) { - this.cache = cache; - } - - execute(id: string): Folder[] { + execute(id: string): FolderDto[] { const currentFolders = this.cache.getItems(); if (!id || id === ROOT_FOLDER || !currentFolders.length) { @@ -18,7 +14,7 @@ export class GetDescendantFoldersRepository implements IGetDescendantFoldersRepo } const folderMap = new Map(currentFolders.map(folder => [folder.id, folder])); - const result: Folder[] = []; + const result: FolderDto[] = []; const findChildren = (folderId: string) => { const folder = folderMap.get(folderId); @@ -26,7 +22,14 @@ export class GetDescendantFoldersRepository implements IGetDescendantFoldersRepo return; } - result.push(folder); + result.push({ + id: folder.id, + title: folder.title, + slug: folder.slug, + permissions: folder.permissions, + type: folder.type, + parentId: folder.parentId + }); currentFolders.forEach(child => { if (child.parentId === folder.id) { @@ -40,3 +43,8 @@ export class GetDescendantFoldersRepository implements IGetDescendantFoldersRepo return result; } } + +export const GetDescendantFoldersRepository = RepositoryAbstraction.createImplementation({ + implementation: GetDescendantFoldersRepositoryImpl, + dependencies: [FoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersUseCase.ts b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersUseCase.ts index 1fb784e39a2..6a7f7d2646a 100644 --- a/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersUseCase.ts +++ b/packages/app-aco/src/features/folders/getDescendantFolders/GetDescendantFoldersUseCase.ts @@ -1,26 +1,17 @@ -import type { IGetDescendantFoldersRepository } from "./IGetDescendantFoldersRepository.js"; -import type { - GetDescendantFoldersParams, - IGetDescendantFoldersUseCase -} from "./IGetDescendantFoldersUseCase.js"; +import { + GetDescendantFoldersUseCase as UseCaseAbstraction, + GetDescendantFoldersRepository +} from "./abstractions.js"; -export class GetDescendantFoldersUseCase implements IGetDescendantFoldersUseCase { - private repository: IGetDescendantFoldersRepository; +class GetDescendantFoldersUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: GetDescendantFoldersRepository.Interface) {} - constructor(repository: IGetDescendantFoldersRepository) { - this.repository = repository; - } - - execute(params: GetDescendantFoldersParams) { - const folders = this.repository.execute(params.id); - - return folders.map(folder => ({ - id: folder.id, - title: folder.title, - slug: folder.slug, - permissions: folder.permissions, - type: folder.type, - parentId: folder.parentId - })); + execute(id: string) { + return this.repository.execute(id); } } + +export const GetDescendantFoldersUseCase = UseCaseAbstraction.createImplementation({ + implementation: GetDescendantFoldersUseCaseImpl, + dependencies: [GetDescendantFoldersRepository] +}); diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersRepository.ts b/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersRepository.ts deleted file mode 100644 index 70c478267bb..00000000000 --- a/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Folder } from "../Folder.js"; - -export interface IGetDescendantFoldersRepository { - execute: (id: string) => Folder[]; -} diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersUseCase.ts b/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersUseCase.ts deleted file mode 100644 index 22c6748532a..00000000000 --- a/packages/app-aco/src/features/folders/getDescendantFolders/IGetDescendantFoldersUseCase.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { FolderDto } from "./FolderDto.js"; - -export interface GetDescendantFoldersParams { - id: string; -} - -export interface IGetDescendantFoldersUseCase { - execute: (params: GetDescendantFoldersParams) => FolderDto[]; -} diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/abstractions.ts b/packages/app-aco/src/features/folders/getDescendantFolders/abstractions.ts new file mode 100644 index 00000000000..4872f9703d4 --- /dev/null +++ b/packages/app-aco/src/features/folders/getDescendantFolders/abstractions.ts @@ -0,0 +1,40 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderPermission } from "~/types.js"; + +// DTOs +export interface FolderDto { + id: string; + title: string; + slug: string; + permissions: FolderPermission[]; + type: string; + parentId: string | null; +} + +// Use Case + +export interface IGetDescendantFoldersUseCase { + execute: (id: string) => FolderDto[]; +} + +export const GetDescendantFoldersUseCase = createAbstraction( + "GetDescendantFoldersUseCase" +); + +export namespace GetDescendantFoldersUseCase { + export type Interface = IGetDescendantFoldersUseCase; +} + +// Repository + +export interface IGetDescendantFoldersRepository { + execute: (id: string) => FolderDto[]; +} + +export const GetDescendantFoldersRepository = createAbstraction( + "GetDescendantFoldersRepository" +); + +export namespace GetDescendantFoldersRepository { + export type Interface = IGetDescendantFoldersRepository; +} diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/feature.ts b/packages/app-aco/src/features/folders/getDescendantFolders/feature.ts new file mode 100644 index 00000000000..ca844f64f11 --- /dev/null +++ b/packages/app-aco/src/features/folders/getDescendantFolders/feature.ts @@ -0,0 +1,17 @@ +import { createFeature } from "@webiny/feature/admin"; +import { GetDescendantFoldersUseCase as UseCase } from "./abstractions.js"; +import { GetDescendantFoldersUseCase } from "./GetDescendantFoldersUseCase.js"; +import { GetDescendantFoldersRepository } from "./GetDescendantFoldersRepository.js"; + +export const GetDescendantFoldersFeature = createFeature({ + name: "GetDescendantFolders", + register(container) { + container.register(GetDescendantFoldersUseCase); + container.register(GetDescendantFoldersRepository).inSingletonScope(); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/getDescendantFolders/useGetDescendantFolders.ts b/packages/app-aco/src/features/folders/getDescendantFolders/useGetDescendantFolders.ts index ef74067a271..f8ec028565b 100644 --- a/packages/app-aco/src/features/folders/getDescendantFolders/useGetDescendantFolders.ts +++ b/packages/app-aco/src/features/folders/getDescendantFolders/useGetDescendantFolders.ts @@ -1,19 +1,12 @@ -import { useCallback } from "react"; -import { GetDescendantFolders } from "./GetDescendantFolders.js"; -import { useFoldersType } from "~/hooks/index.js"; +import { useFeature } from "@webiny/app"; +import { GetDescendantFoldersFeature } from "./feature.js"; export const useGetDescendantFolders = () => { - const type = useFoldersType(); - - const getDescendantFolders = useCallback( - (id: string) => { - const instance = GetDescendantFolders.getInstance(type); - return instance.execute({ id }); - }, - [type] - ); + const { useCase } = useFeature(GetDescendantFoldersFeature); return { - getDescendantFolders + getDescendantFolders: (id: string) => { + return useCase.execute(id); + } }; }; diff --git a/packages/app-aco/src/features/folders/getFolder/GetFolder.ts b/packages/app-aco/src/features/folders/getFolder/GetFolder.ts deleted file mode 100644 index b7e5bf39096..00000000000 --- a/packages/app-aco/src/features/folders/getFolder/GetFolder.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IGetFolderUseCase } from "./IGetFolderUseCase.js"; -import type { IGetFolderGateway } from "./IGetFolderGateway.js"; -import { GetFolderRepository } from "./GetFolderRepository.js"; -import { GetFolderUseCase } from "./GetFolderUseCase.js"; -import { GetFolderUseCaseWithLoading } from "./GetFolderUseCaseWithLoading.js"; -import { folderCacheFactory } from "../cache/index.js"; - -export class GetFolder { - public static getInstance(type: string, gateway: IGetFolderGateway): IGetFolderUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new GetFolderRepository(foldersCache, gateway); - const useCase = new GetFolderUseCase(repository); - return new GetFolderUseCaseWithLoading(loadingRepository, useCase); - } -} diff --git a/packages/app-aco/src/features/folders/getFolder/GetFolderGqlGateway.ts b/packages/app-aco/src/features/folders/getFolder/GetFolderGqlGateway.ts index 6f4b1093938..55d898a2b97 100644 --- a/packages/app-aco/src/features/folders/getFolder/GetFolderGqlGateway.ts +++ b/packages/app-aco/src/features/folders/getFolder/GetFolderGqlGateway.ts @@ -1,14 +1,21 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { IGetFolderGateway } from "./IGetFolderGateway.js"; -import type { FolderItem, AcoError } from "~/types.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { GetFolderGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; export interface GetFolderResponse { aco: { - getFolder: { - data: FolderItem | null; - error: AcoError | null; - }; + getFolder: + | { + data: FolderDto; + error: null; + } + | { + data: null; + error: AcoError; + }; }; } @@ -31,25 +38,20 @@ export const GET_FOLDER = (FOLDER_FIELDS: string) => gql` } `; -export class GetFolderGqlGateway implements IGetFolderGateway { - private client: ApolloClient; - private modelFields: string; - - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } +class GetFolderGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} async execute(id: string) { - if (!id) { - throw new Error("Folder `id` is mandatory"); - } + const fields = await this.folderModelProvider.getGraphQLSelection(); const { data: response } = await this.client.query< GetFolderResponse, GetFolderQueryVariables >({ - query: GET_FOLDER(this.modelFields), + query: GET_FOLDER(fields), variables: { id }, fetchPolicy: "network-only" }); @@ -67,3 +69,8 @@ export class GetFolderGqlGateway implements IGetFolderGateway { return data; } } + +export const GetFolderGqlGateway = GatewayAbstraction.createImplementation({ + implementation: GetFolderGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/getFolder/GetFolderRepository.ts b/packages/app-aco/src/features/folders/getFolder/GetFolderRepository.ts index 3617ada92f1..664121e702e 100644 --- a/packages/app-aco/src/features/folders/getFolder/GetFolderRepository.ts +++ b/packages/app-aco/src/features/folders/getFolder/GetFolderRepository.ts @@ -1,19 +1,21 @@ -import { Folder } from "../Folder.js"; -import type { ListCache } from "../cache/index.js"; -import type { IGetFolderRepository } from "./IGetFolderRepository.js"; -import type { IGetFolderGateway } from "./IGetFolderGateway.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { GetFolderRepository as RepositoryAbstraction } from "./abstractions.js"; +import { GetFolderGateway } from "./abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; -export class GetFolderRepository implements IGetFolderRepository { - private cache: ListCache; - private gateway: IGetFolderGateway; - - constructor(cache: ListCache, gateway: IGetFolderGateway) { - this.cache = cache; - this.gateway = gateway; - } +class GetFolderRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private gateway: GetFolderGateway.Interface + ) {} async execute(id: string) { const response = await this.gateway.execute(id); this.cache.addItems([Folder.create(response)]); } } + +export const GetFolderRepository = RepositoryAbstraction.createImplementation({ + implementation: GetFolderRepositoryImpl, + dependencies: [FoldersCache, GetFolderGateway] +}); diff --git a/packages/app-aco/src/features/folders/getFolder/GetFolderUseCase.ts b/packages/app-aco/src/features/folders/getFolder/GetFolderUseCase.ts index 3c7ebd157f9..3e8353d0791 100644 --- a/packages/app-aco/src/features/folders/getFolder/GetFolderUseCase.ts +++ b/packages/app-aco/src/features/folders/getFolder/GetFolderUseCase.ts @@ -1,14 +1,14 @@ -import type { GetFolderParams, IGetFolderUseCase } from "./IGetFolderUseCase.js"; -import type { IGetFolderRepository } from "./IGetFolderRepository.js"; +import { GetFolderUseCase as UseCaseAbstraction, GetFolderRepository } from "./abstractions.js"; -export class GetFolderUseCase implements IGetFolderUseCase { - private repository: IGetFolderRepository; +class GetFolderUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: GetFolderRepository.Interface) {} - constructor(repository: IGetFolderRepository) { - this.repository = repository; - } - - async execute(params: GetFolderParams) { - await this.repository.execute(params.id); + async execute(id: string) { + await this.repository.execute(id); } } + +export const GetFolderUseCase = UseCaseAbstraction.createImplementation({ + implementation: GetFolderUseCaseImpl, + dependencies: [GetFolderRepository] +}); diff --git a/packages/app-aco/src/features/folders/getFolder/GetFolderUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/getFolder/GetFolderUseCaseWithLoading.ts deleted file mode 100644 index e8d7cdd8089..00000000000 --- a/packages/app-aco/src/features/folders/getFolder/GetFolderUseCaseWithLoading.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { GetFolderParams, IGetFolderUseCase } from "./IGetFolderUseCase.js"; -import type { ILoadingRepository } from "@webiny/app-utils"; -import { LoadingActionsEnum } from "~/types.js"; - -export class GetFolderUseCaseWithLoading implements IGetFolderUseCase { - private loadingRepository: ILoadingRepository; - private useCase: IGetFolderUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: IGetFolderUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } - - async execute(params: GetFolderParams) { - await this.loadingRepository.runCallBack( - this.useCase.execute(params), - LoadingActionsEnum.get - ); - } -} diff --git a/packages/app-aco/src/features/folders/getFolder/IGetFolderGateway.ts b/packages/app-aco/src/features/folders/getFolder/IGetFolderGateway.ts deleted file mode 100644 index b5353622775..00000000000 --- a/packages/app-aco/src/features/folders/getFolder/IGetFolderGateway.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; -} - -export interface IGetFolderGateway { - execute: (id: string) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolder/IGetFolderRepository.ts b/packages/app-aco/src/features/folders/getFolder/IGetFolderRepository.ts deleted file mode 100644 index 48bcaf7f250..00000000000 --- a/packages/app-aco/src/features/folders/getFolder/IGetFolderRepository.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IGetFolderRepository { - execute: (id: string) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolder/IGetFolderUseCase.ts b/packages/app-aco/src/features/folders/getFolder/IGetFolderUseCase.ts deleted file mode 100644 index 22cf8bcc44a..00000000000 --- a/packages/app-aco/src/features/folders/getFolder/IGetFolderUseCase.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface GetFolderParams { - id: string; -} - -export interface IGetFolderUseCase { - execute: (params: GetFolderParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolder/abstractions.ts b/packages/app-aco/src/features/folders/getFolder/abstractions.ts new file mode 100644 index 00000000000..14b5806609e --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolder/abstractions.ts @@ -0,0 +1,38 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; + +// Use Case + +export interface IGetFolderUseCase { + execute: (id: string) => Promise; +} + +export const GetFolderUseCase = createAbstraction("GetFolderUseCase"); + +export namespace GetFolderUseCase { + export type Interface = IGetFolderUseCase; +} + +// Repository + +export interface IGetFolderRepository { + execute: (id: string) => Promise; +} + +export const GetFolderRepository = createAbstraction("GetFolderRepository"); + +export namespace GetFolderRepository { + export type Interface = IGetFolderRepository; +} + +// Gateway + +export interface IGetFolderGateway { + execute: (id: string) => Promise; +} + +export const GetFolderGateway = createAbstraction("GetFolderGateway"); + +export namespace GetFolderGateway { + export type Interface = IGetFolderGateway; +} diff --git a/packages/app-aco/src/features/folders/getFolder/feature.ts b/packages/app-aco/src/features/folders/getFolder/feature.ts new file mode 100644 index 00000000000..dc34e8eaee8 --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolder/feature.ts @@ -0,0 +1,19 @@ +import { createFeature } from "@webiny/feature/admin"; +import { GetFolderUseCase as UseCase } from "./abstractions.js"; +import { GetFolderUseCase } from "./GetFolderUseCase.js"; +import { GetFolderRepository } from "./GetFolderRepository.js"; +import { GetFolderGqlGateway } from "./GetFolderGqlGateway.js"; + +export const GetFolderFeature = createFeature({ + name: "GetFolder", + register(container) { + container.register(GetFolderUseCase); + container.register(GetFolderRepository).inSingletonScope(); + container.register(GetFolderGqlGateway); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/getFolder/useGetFolder.ts b/packages/app-aco/src/features/folders/getFolder/useGetFolder.ts index eb18db6d11e..5396f88ba5f 100644 --- a/packages/app-aco/src/features/folders/getFolder/useGetFolder.ts +++ b/packages/app-aco/src/features/folders/getFolder/useGetFolder.ts @@ -1,25 +1,12 @@ -import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { GetFolderGqlGateway } from "./GetFolderGqlGateway.js"; -import type { GetFolderParams } from "./IGetFolderUseCase.js"; -import { GetFolder } from "./GetFolder.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; +import { useFeature } from "@webiny/app"; +import { GetFolderFeature } from "./feature.js"; export const useGetFolder = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new GetFolderGqlGateway(client, fields); - - const getFolder = useCallback( - (params: GetFolderParams) => { - const instance = GetFolder.getInstance(type, gateway); - return instance.execute(params); - }, - [type, gateway] - ); + const { useCase } = useFeature(GetFolderFeature); return { - getFolder + getFolder: (id: string) => { + return useCase.execute(id); + } }; }; diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/FolderDto.ts b/packages/app-aco/src/features/folders/getFolderAncestors/FolderDto.ts deleted file mode 100644 index 942b54e1266..00000000000 --- a/packages/app-aco/src/features/folders/getFolderAncestors/FolderDto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { FolderPermission } from "@webiny/shared-aco/flp/flp.types.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - type: string; - parentId: string | null; -} diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.test.ts b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.test.ts index 06ef3d11510..f503ba45313 100644 --- a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.test.ts +++ b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.test.ts @@ -1,14 +1,19 @@ -import { describe, it, expect, beforeEach } from "vitest"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { Folder } from "../Folder.js"; -import { GetFolderAncestors } from "./GetFolderAncestors.js"; +import { describe, it, expect } from "vitest"; +import { Container } from "@webiny/di"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { GetFolderAncestorsFeature } from "~/features/folders/getFolderAncestors/feature.js"; +import { GetFolderAncestorsUseCase } from "~/features/folders/getFolderAncestors/abstractions.js"; describe("GetFolderAncestors", () => { const type = "abc"; - const foldersCache = folderCacheFactory.getCache(type); - beforeEach(() => { - foldersCache.clear(); + function setupTest() { + const container = new Container(); + const foldersCache = new ListCache(); + foldersCache.addItems([ Folder.create({ id: "folder-1", @@ -51,14 +56,19 @@ describe("GetFolderAncestors", () => { type }) ]); - }); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + + GetFolderAncestorsFeature.register(container); + + return { container, foldersCache, useCase: container.resolve(GetFolderAncestorsUseCase) }; + } it("should return all ancestors of a folder", () => { - const getFolderAncestors = GetFolderAncestors.getInstance(type); + const { useCase } = setupTest(); - const ancestors = getFolderAncestors.execute({ - id: "folder-4" - }); + const ancestors = useCase.execute("folder-4"); expect(ancestors).toEqual([ { @@ -89,11 +99,9 @@ describe("GetFolderAncestors", () => { }); it("should return an empty array if the folder has no ancestors", () => { - const getFolderAncestors = GetFolderAncestors.getInstance(type); + const { useCase } = setupTest(); - const ancestors = getFolderAncestors.execute({ - id: "folder-1" - }); + const ancestors = useCase.execute("folder-1"); expect(ancestors).toEqual([ { @@ -108,11 +116,9 @@ describe("GetFolderAncestors", () => { }); it("should return an empty array if the folder does not exist", () => { - const getFolderAncestors = GetFolderAncestors.getInstance(type); + const { useCase } = setupTest(); - const ancestors = getFolderAncestors.execute({ - id: "non-existing-folder" - }); + const ancestors = useCase.execute("non-existing-folder"); expect(ancestors).toEqual([]); }); diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.ts b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.ts deleted file mode 100644 index 4755b73be38..00000000000 --- a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestors.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { folderCacheFactory } from "../cache/index.js"; -import { GetFolderAncestorsRepository } from "./GetFolderAncestorsRepository.js"; -import { GetFolderAncestorsUseCase } from "./GetFolderAncestorsUseCase.js"; -import type { IGetFolderAncestorsUseCase } from "./IGetFolderAncestorsUseCase.js"; - -export class GetFolderAncestors { - public static getInstance(type: string): IGetFolderAncestorsUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const repository = new GetFolderAncestorsRepository(foldersCache); - return new GetFolderAncestorsUseCase(repository); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsRepository.ts b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsRepository.ts index 7e3e4a50e79..088f7659680 100644 --- a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsRepository.ts +++ b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsRepository.ts @@ -1,14 +1,11 @@ -import type { IGetFolderAncestorsRepository } from "./IGetFolderAncestorsRepository.js"; -import type { Folder, ListCache } from "~/features/index.js"; +import { GetFolderAncestorsRepository as RepositoryAbstraction } from "./abstractions.js"; +import type { FolderDto } from "./abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; -export class GetFolderAncestorsRepository implements IGetFolderAncestorsRepository { - private readonly cache: ListCache; +class GetFolderAncestorsRepositoryImpl implements RepositoryAbstraction.Interface { + constructor(private cache: FoldersCache.Interface) {} - constructor(cache: ListCache) { - this.cache = cache; - } - - execute(id: string) { + execute(id: string): FolderDto[] { const currentFolders = this.cache.getItems(); if (!currentFolders.length) { @@ -16,7 +13,7 @@ export class GetFolderAncestorsRepository implements IGetFolderAncestorsReposito } const folderMap = new Map(currentFolders.map(folder => [folder.id, folder])); - const result: Folder[] = []; + const result: FolderDto[] = []; let currentFolderId: string | null = id; @@ -25,10 +22,24 @@ export class GetFolderAncestorsRepository implements IGetFolderAncestorsReposito if (!folder) { break; } - result.push(folder); + + result.push({ + id: folder.id, + title: folder.title, + slug: folder.slug, + permissions: folder.permissions, + type: folder.type, + parentId: folder.parentId + }); + currentFolderId = folder.parentId ?? null; } return result; } } + +export const GetFolderAncestorsRepository = RepositoryAbstraction.createImplementation({ + implementation: GetFolderAncestorsRepositoryImpl, + dependencies: [FoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsUseCase.ts b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsUseCase.ts index 11eaec1e5f3..f4e9c972d65 100644 --- a/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsUseCase.ts +++ b/packages/app-aco/src/features/folders/getFolderAncestors/GetFolderAncestorsUseCase.ts @@ -1,26 +1,17 @@ -import type { - GetFolderAncestorsParams, - IGetFolderAncestorsUseCase -} from "./IGetFolderAncestorsUseCase.js"; -import type { IGetFolderAncestorsRepository } from "./IGetFolderAncestorsRepository.js"; +import { + GetFolderAncestorsUseCase as UseCaseAbstraction, + GetFolderAncestorsRepository +} from "./abstractions.js"; -export class GetFolderAncestorsUseCase implements IGetFolderAncestorsUseCase { - private repository: IGetFolderAncestorsRepository; +class GetFolderAncestorsUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: GetFolderAncestorsRepository.Interface) {} - constructor(repository: IGetFolderAncestorsRepository) { - this.repository = repository; - } - - execute(params: GetFolderAncestorsParams) { - const folders = this.repository.execute(params.id); - - return folders.map(folder => ({ - id: folder.id, - title: folder.title, - slug: folder.slug, - permissions: folder.permissions, - type: folder.type, - parentId: folder.parentId - })); + execute(id: string) { + return this.repository.execute(id); } } + +export const GetFolderAncestorsUseCase = UseCaseAbstraction.createImplementation({ + implementation: GetFolderAncestorsUseCaseImpl, + dependencies: [GetFolderAncestorsRepository] +}); diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsRepository.ts b/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsRepository.ts deleted file mode 100644 index 8cee8673f2f..00000000000 --- a/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Folder } from "~/features/index.js"; - -export interface IGetFolderAncestorsRepository { - execute: (id: string) => Folder[]; -} diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsUseCase.ts b/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsUseCase.ts deleted file mode 100644 index 5c6f9d5eb7d..00000000000 --- a/packages/app-aco/src/features/folders/getFolderAncestors/IGetFolderAncestorsUseCase.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { FolderDto } from "./FolderDto.js"; - -export interface GetFolderAncestorsParams { - id: string; -} - -export interface IGetFolderAncestorsUseCase { - execute: (params: GetFolderAncestorsParams) => FolderDto[]; -} diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/abstractions.ts b/packages/app-aco/src/features/folders/getFolderAncestors/abstractions.ts new file mode 100644 index 00000000000..dd7fe9333e1 --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderAncestors/abstractions.ts @@ -0,0 +1,40 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderPermission } from "@webiny/shared-aco/flp/flp.types.js"; + +// DTOs +export interface FolderDto { + id: string; + title: string; + slug: string; + permissions: FolderPermission[]; + type: string; + parentId: string | null; +} + +// Use Case + +export interface IGetFolderAncestorsUseCase { + execute: (id: string) => FolderDto[]; +} + +export const GetFolderAncestorsUseCase = createAbstraction( + "GetFolderAncestorsUseCase" +); + +export namespace GetFolderAncestorsUseCase { + export type Interface = IGetFolderAncestorsUseCase; +} + +// Repository + +export interface IGetFolderAncestorsRepository { + execute: (id: string) => FolderDto[]; +} + +export const GetFolderAncestorsRepository = createAbstraction( + "GetFolderAncestorsRepository" +); + +export namespace GetFolderAncestorsRepository { + export type Interface = IGetFolderAncestorsRepository; +} diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/feature.ts b/packages/app-aco/src/features/folders/getFolderAncestors/feature.ts new file mode 100644 index 00000000000..f0e97cd2b2e --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderAncestors/feature.ts @@ -0,0 +1,17 @@ +import { createFeature } from "@webiny/feature/admin"; +import { GetFolderAncestorsUseCase as UseCase } from "./abstractions.js"; +import { GetFolderAncestorsUseCase } from "./GetFolderAncestorsUseCase.js"; +import { GetFolderAncestorsRepository } from "./GetFolderAncestorsRepository.js"; + +export const GetFolderAncestorsFeature = createFeature({ + name: "GetFolderAncestors", + register(container) { + container.register(GetFolderAncestorsUseCase); + container.register(GetFolderAncestorsRepository).inSingletonScope(); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/getFolderAncestors/useGetFolderAncestors.ts b/packages/app-aco/src/features/folders/getFolderAncestors/useGetFolderAncestors.ts index 99539a05866..f44b1f8e0ca 100644 --- a/packages/app-aco/src/features/folders/getFolderAncestors/useGetFolderAncestors.ts +++ b/packages/app-aco/src/features/folders/getFolderAncestors/useGetFolderAncestors.ts @@ -1,19 +1,12 @@ -import { useCallback } from "react"; -import { useFoldersType } from "~/hooks/index.js"; -import { GetFolderAncestors } from "~/features/folders/getFolderAncestors/GetFolderAncestors.js"; +import { useFeature } from "@webiny/app"; +import { GetFolderAncestorsFeature } from "./feature.js"; export const useGetFolderAncestors = () => { - const type = useFoldersType(); - - const getFolderAncestors = useCallback( - (id: string) => { - const instance = GetFolderAncestors.getInstance(type); - return instance.execute({ id }); - }, - [type] - ); + const { useCase } = useFeature(GetFolderAncestorsFeature); return { - getFolderAncestors + getFolderAncestors: (id: string) => { + return useCase.execute(id); + } }; }; diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.test.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.test.ts index 0a8ac36a901..9669dbb87bf 100644 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.test.ts +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.test.ts @@ -1,6 +1,13 @@ import { describe, it, expect } from "vitest"; -import { GetFolderExtensionsFields } from "./GetFolderExtensionsFields.js"; import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; +import { Container } from "@webiny/di"; +import { GetFolderExtensionsFieldsFeature } from "~/features/folders/getFolderExtensionsFields/feature.js"; +import { GetFolderExtensionsFieldsUseCase } from "~/features/folders/getFolderExtensionsFields/abstractions.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import type { Folder } from "~/domain/folder/Folder.js"; describe("GetFolderExtensionsFields", () => { const model = { @@ -133,39 +140,52 @@ describe("GetFolderExtensionsFields", () => { webinyVersion: "0.0.0" } as unknown as CmsModel; - it("CMS: should return fields from `global`, `cms` and the provided model namespace", () => { - const instance = GetFolderExtensionsFields.getInstance(model, "cms", "article"); + function setupTest(type: string) { + const container = new Container(); + const foldersCache = new ListCache(); - const result = instance.execute(); + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); - expect(result.fields.map(field => field.id)).toEqual([ + GetFolderExtensionsFieldsFeature.register(container); + container.registerInstance(FolderModelProvider, { + getModel: () => Promise.resolve(model), + getGraphQLSelection: () => Promise.resolve("") + }); + + return { container, useCase: container.resolve(GetFolderExtensionsFieldsUseCase) }; + } + + it("CMS: should return fields from `global`, `cms` and the provided model namespace", async () => { + const { useCase } = setupTest("cms:article"); + + const fields = await useCase.execute(); + + expect(fields.map(field => field.id)).toEqual([ "globalField", "cms_cmsField", "cms_article_authorArticleField" ]); }); - it("CMS: should return fields from `global` and `cms` namespace if not fields found for the provided model namespace", () => { - const instance = GetFolderExtensionsFields.getInstance(model, "cms", "other"); - const result = instance.execute(); - expect(result.fields.map(field => field.id)).toEqual(["globalField", "cms_cmsField"]); - }); + it("CMS: should return fields from `global` and `cms` namespace if not fields found for the provided model namespace", async () => { + const { useCase } = setupTest("cms:other"); - it("PAGE BUILDER: should return fields from `global` and `pb_page` namespace", () => { - const instance = GetFolderExtensionsFields.getInstance(model, "PbPage", ""); - const result = instance.execute(); - expect(result.fields.map(field => field.id)).toEqual(["globalField", "pb_page_pageField"]); + const fields = await useCase.execute(); + expect(fields.map(field => field.id)).toEqual(["globalField", "cms_cmsField"]); }); - it("FILE MANAGER: should return fields from `global` and `fm_file` namespace", () => { - const instance = GetFolderExtensionsFields.getInstance(model, "FmFile", ""); - const result = instance.execute(); - expect(result.fields.map(field => field.id)).toEqual(["globalField", "fm_file_fileField"]); + it("FILE MANAGER: should return fields from `global` and `fm_file` namespace", async () => { + const { useCase } = setupTest("FmFile"); + + const fields = await useCase.execute(); + expect(fields.map(field => field.id)).toEqual(["globalField", "fm_file_fileField"]); }); - it("ANY OTHER APP: should return fields from `global` namespace", () => { - const instance = GetFolderExtensionsFields.getInstance(model, "Any", ""); - const result = instance.execute(); - expect(result.fields.map(field => field.id)).toEqual(["globalField"]); + it("ANY OTHER APP: should return fields from `global` namespace", async () => { + const { useCase } = setupTest("Any"); + + const fields = await useCase.execute(); + expect(fields.map(field => field.id)).toEqual(["globalField"]); }); }); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.ts deleted file mode 100644 index 707ee997903..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFields.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { IGetFolderExtensionsFieldsUseCase } from "./IGetFolderExtensionsFieldsUseCase.js"; -import { GetFolderExtensionsFieldsRepository } from "./GetFolderExtensionsFieldsRepository.js"; - -import { GetFolderExtensionsFieldsUseCase } from "./GetFolderExtensionsFieldsUseCase.js"; -import { GetFolderExtensionsFieldsUseCaseWithNamespace } from "./GetFolderExtensionsFieldsUseCaseWithNamespace.js"; -import { GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId } from "./GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId.js"; -import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; - -export class GetFolderExtensionsFields { - public static getInstance( - folderModel: CmsModel, - folderType: string, - modelId: string - ): IGetFolderExtensionsFieldsUseCase { - const repository = new GetFolderExtensionsFieldsRepository(folderModel); - const useCase = new GetFolderExtensionsFieldsUseCase(repository); - - switch (folderType) { - case "cms": - return new GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId( - "cms", - modelId, - useCase - ); - - case "PbPage": - return new GetFolderExtensionsFieldsUseCaseWithNamespace("pb_page", useCase); - - case "FmFile": - return new GetFolderExtensionsFieldsUseCaseWithNamespace("fm_file", useCase); - - default: - return new GetFolderExtensionsFieldsUseCaseWithNamespace("global", useCase); - } - } -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsRepository.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsRepository.ts deleted file mode 100644 index e0fb2e447c5..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsRepository.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IGetFolderExtensionsFieldsRepository } from "./IGetFolderExtensionsFieldsRepository.js"; -import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js"; - -export class GetFolderExtensionsFieldsRepository implements IGetFolderExtensionsFieldsRepository { - private model: CmsModel; - - constructor(model: CmsModel) { - this.model = model; - } - - execute() { - return this.model.fields.find(f => f.fieldId === "extensions") || undefined; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCase.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCase.ts index a6aa366a18d..b1406a27f60 100644 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCase.ts +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCase.ts @@ -1,20 +1,38 @@ -import type { IGetFolderExtensionsFieldsUseCase } from "./IGetFolderExtensionsFieldsUseCase.js"; -import type { IGetFolderExtensionsFieldsRepository } from "./IGetFolderExtensionsFieldsRepository.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { + GetFolderExtensionsFieldsUseCase as UseCaseAbstraction, + FolderExtensionsFieldFilter +} from "./abstractions.js"; -export class GetFolderExtensionsFieldsUseCase implements IGetFolderExtensionsFieldsUseCase { - private repository: IGetFolderExtensionsFieldsRepository; +class GetFolderExtensionsFieldsUseCaseImpl implements UseCaseAbstraction.Interface { + constructor( + private modelProvider: FolderModelProvider.Interface, + private filters: FolderExtensionsFieldFilter.Interface[] + ) {} - constructor(repository: IGetFolderExtensionsFieldsRepository) { - this.repository = repository; - } + async execute() { + const model = await this.modelProvider.getModel(); + const extensionsField = model.fields.find(f => f.fieldId === "extensions"); + const allFields = extensionsField?.settings?.fields || []; + + // Filter to only fields with tags + const fieldsWithTags = allFields.filter(field => field.tags?.length); - execute() { - const extensionsField = this.repository.execute(); + // Collect all matching fieldIds from all filters + const matchingFieldIds = new Set(); + this.filters.forEach(filter => { + const matchedFields = filter.filter(fieldsWithTags); + matchedFields.forEach(field => matchingFieldIds.add(field.fieldId)); + }); - const fields = extensionsField?.settings?.fields || []; + // Return fields in original order that match any filter + const fields = fieldsWithTags.filter(field => matchingFieldIds.has(field.fieldId)); - return { - fields: fields.filter(field => field.tags?.length) - }; + return fields; } } + +export const GetFolderExtensionsFieldsUseCase = UseCaseAbstraction.createImplementation({ + implementation: GetFolderExtensionsFieldsUseCaseImpl, + dependencies: [FolderModelProvider, [FolderExtensionsFieldFilter, { multiple: true }]] +}); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespace.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespace.ts deleted file mode 100644 index ed8e32729cf..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespace.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { IGetFolderExtensionsFieldsUseCase } from "./IGetFolderExtensionsFieldsUseCase.js"; - -export class GetFolderExtensionsFieldsUseCaseWithNamespace - implements IGetFolderExtensionsFieldsUseCase -{ - private namespace: string; - private useCase: IGetFolderExtensionsFieldsUseCase; - - constructor(namespace: string, useCase: IGetFolderExtensionsFieldsUseCase) { - this.namespace = namespace; - this.useCase = useCase; - } - - execute() { - const { fields: extensionsFields } = this.useCase.execute(); - - const fields = extensionsFields.filter(field => { - if (field.tags!.includes("$namespace:global")) { - return true; // Always include fields with this tag - } - - return field.tags!.includes(`$namespace:${this.namespace}`); - }); - - return { - fields - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId.ts deleted file mode 100644 index b65487e198b..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { IGetFolderExtensionsFieldsUseCase } from "./IGetFolderExtensionsFieldsUseCase.js"; - -export class GetFolderExtensionsFieldsUseCaseWithNamespaceAndModelId - implements IGetFolderExtensionsFieldsUseCase -{ - private namespace: string; - private modelId: string; - private useCase: IGetFolderExtensionsFieldsUseCase; - - constructor(namespace: string, modelId: string, useCase: IGetFolderExtensionsFieldsUseCase) { - this.namespace = namespace; - this.modelId = modelId; - this.useCase = useCase; - } - - execute() { - const { fields: extensionsFields } = this.useCase.execute(); - - const fields = extensionsFields.filter(field => { - if (field.tags!.includes("$namespace:global")) { - return true; // Always include fields with this tag - } - - const hasModelIdTag = field.tags!.some(tag => tag.startsWith("$modelId:")); - - return hasModelIdTag - ? field.tags!.includes(`$modelId:${this.modelId}`) - : field.tags!.includes(`$namespace:${this.namespace}`); - }); - - return { - fields - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsRepository.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsRepository.ts deleted file mode 100644 index 9492c9be7d1..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; - -export interface IGetFolderExtensionsFieldsRepository { - execute: () => CmsModelField | undefined; -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsUseCase.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsUseCase.ts deleted file mode 100644 index 593c1c77483..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/IGetFolderExtensionsFieldsUseCase.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; - -export interface IGetFolderExtensionsFieldsUseCase { - execute: () => { - fields: CmsModelField[]; - }; -} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/abstractions.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/abstractions.ts new file mode 100644 index 00000000000..190d67b538f --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/abstractions.ts @@ -0,0 +1,29 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; + +// Use Case + +export interface IGetFolderExtensionsFieldsUseCase { + execute(): Promise; +} + +export const GetFolderExtensionsFieldsUseCase = + createAbstraction("GetFolderExtensionsFieldsUseCase"); + +export namespace GetFolderExtensionsFieldsUseCase { + export type Interface = IGetFolderExtensionsFieldsUseCase; +} + +// Field Filter + +export interface IFolderExtensionsFieldFilter { + filter: (fields: CmsModelField[]) => CmsModelField[]; +} + +export const FolderExtensionsFieldFilter = createAbstraction( + "FolderExtensionsFieldFilter" +); + +export namespace FolderExtensionsFieldFilter { + export type Interface = IFolderExtensionsFieldFilter; +} diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/feature.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/feature.ts new file mode 100644 index 00000000000..6b63484e0a2 --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/feature.ts @@ -0,0 +1,24 @@ +import { createFeature } from "@webiny/feature/admin"; +import { GetFolderExtensionsFieldsUseCase as UseCase } from "./abstractions.js"; +import { GetFolderExtensionsFieldsUseCase } from "./GetFolderExtensionsFieldsUseCase.js"; +import { GlobalNamespaceFilter } from "./filters/GlobalNamespaceFilter.js"; +import { FmFileNamespaceFilter } from "./filters/FmFileNamespaceFilter.js"; +import { CmsNamespaceFilter } from "./filters/CmsNamespaceFilter.js"; + +export const GetFolderExtensionsFieldsFeature = createFeature({ + name: "GetFolderExtensionsFields", + register(container) { + // Register base implementation + container.register(GetFolderExtensionsFieldsUseCase); + + // Register all filters - they will all run and results will be combined + container.register(GlobalNamespaceFilter); + container.register(FmFileNamespaceFilter); + container.register(CmsNamespaceFilter); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/CmsNamespaceFilter.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/CmsNamespaceFilter.ts new file mode 100644 index 00000000000..00de8ab3c9e --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/CmsNamespaceFilter.ts @@ -0,0 +1,29 @@ +import { FolderExtensionsFieldFilter } from "../abstractions.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; + +class CmsNamespaceFilterImpl implements FolderExtensionsFieldFilter.Interface { + constructor(private foldersContext: FoldersContext.Interface) {} + + filter(fields: CmsModelField[]): CmsModelField[] { + const [folderType, modelId] = this.foldersContext.type.split(":"); + + // Only apply this filter if folder type is cms + if (folderType !== "cms") { + return []; + } + + return fields.filter(field => { + const hasModelIdTag = field.tags!.some(tag => tag.startsWith("$modelId:")); + + return hasModelIdTag + ? field.tags!.includes(`$modelId:${modelId}`) + : field.tags!.includes("$namespace:cms"); + }); + } +} + +export const CmsNamespaceFilter = FolderExtensionsFieldFilter.createImplementation({ + implementation: CmsNamespaceFilterImpl, + dependencies: [FoldersContext] +}); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/FmFileNamespaceFilter.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/FmFileNamespaceFilter.ts new file mode 100644 index 00000000000..79d40425eec --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/FmFileNamespaceFilter.ts @@ -0,0 +1,25 @@ +import { FolderExtensionsFieldFilter } from "../abstractions.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; + +class FmFileNamespaceFilterImpl implements FolderExtensionsFieldFilter.Interface { + constructor(private foldersContext: FoldersContext.Interface) {} + + filter(fields: CmsModelField[]): CmsModelField[] { + const [folderType] = this.foldersContext.type.split(":"); + + // Only apply this filter if folder type is FmFile + if (folderType !== "FmFile") { + return []; + } + + return fields.filter(field => { + return field.tags!.includes("$namespace:fm_file"); + }); + } +} + +export const FmFileNamespaceFilter = FolderExtensionsFieldFilter.createImplementation({ + implementation: FmFileNamespaceFilterImpl, + dependencies: [FoldersContext] +}); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/GlobalNamespaceFilter.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/GlobalNamespaceFilter.ts new file mode 100644 index 00000000000..33c041914ea --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/filters/GlobalNamespaceFilter.ts @@ -0,0 +1,15 @@ +import { FolderExtensionsFieldFilter } from "../abstractions.js"; +import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; + +class GlobalNamespaceFilterImpl implements FolderExtensionsFieldFilter.Interface { + filter(fields: CmsModelField[]): CmsModelField[] { + return fields.filter(field => { + return field.tags!.includes("$namespace:global"); + }); + } +} + +export const GlobalNamespaceFilter = FolderExtensionsFieldFilter.createImplementation({ + implementation: GlobalNamespaceFilterImpl, + dependencies: [] +}); diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/index.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/index.ts index 3563b119934..12c9382b427 100644 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/index.ts +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/index.ts @@ -1 +1 @@ -export * from "./useGetFolderExtensionsFields.js"; +export * from "./useFolderExtensionsFields.js"; diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/useFolderExtensionsFields.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/useFolderExtensionsFields.ts new file mode 100644 index 00000000000..c263b34a8bd --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderExtensionsFields/useFolderExtensionsFields.ts @@ -0,0 +1,17 @@ +import { useState, useEffect } from "react"; +import { useFeature } from "@webiny/app"; +import type { CmsModelField } from "@webiny/app-headless-cms-common/types/index.js"; +import { GetFolderExtensionsFieldsFeature } from "./feature.js"; + +export const useFolderExtensionsFields = () => { + const { useCase } = useFeature(GetFolderExtensionsFieldsFeature); + const [fields, setFields] = useState([]); + + useEffect(() => { + useCase.execute().then(fields => { + setFields(fields); + }); + }, []); + + return { fields }; +}; diff --git a/packages/app-aco/src/features/folders/getFolderExtensionsFields/useGetFolderExtensionsFields.ts b/packages/app-aco/src/features/folders/getFolderExtensionsFields/useGetFolderExtensionsFields.ts deleted file mode 100644 index 2fa3e5976ce..00000000000 --- a/packages/app-aco/src/features/folders/getFolderExtensionsFields/useGetFolderExtensionsFields.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useCallback } from "react"; -import { useFoldersType } from "~/hooks/index.js"; -import { useFolderModel } from "~/features/index.js"; -import { GetFolderExtensionsFields } from "./GetFolderExtensionsFields.js"; - -export const useGetFolderExtensionsFields = () => { - const [type, modelId] = useFoldersType().split(":"); - const model = useFolderModel(); - - const getFolderExtensionsFields = useCallback(() => { - const instance = GetFolderExtensionsFields.getInstance(model, type, modelId); - return instance.execute(); - }, [type, modelId, model.id]); - - return { - getFolderExtensionsFields - }; -}; diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/FolderDto.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/FolderDto.ts deleted file mode 100644 index 2bd42b46c05..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/FolderDto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; -import type { Folder } from "../Folder.js"; -import { ROOT_FOLDER } from "~/constants.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - type: string; - parentId: string; - path: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity; - modifiedOn: string; - extensions: Record; -} - -export class FolderDtoMapper { - static toDTO(folder: Folder): FolderDto { - return { - id: folder.id, - title: folder.title, - canManageContent: folder.canManageContent ?? false, - canManagePermissions: folder.canManagePermissions ?? false, - canManageStructure: folder.canManageStructure ?? false, - createdBy: this.createIdentity(folder.createdBy), - createdOn: folder.createdOn ?? "", - hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false, - modifiedBy: this.createIdentity(folder.modifiedBy), - modifiedOn: folder.modifiedOn ?? "", - parentId: folder.parentId ?? ROOT_FOLDER, - path: folder.path, - permissions: folder.permissions ?? [], - savedBy: this.createIdentity(folder.savedBy), - savedOn: folder.savedOn ?? "", - slug: folder.slug, - type: folder.type, - extensions: folder.extensions ?? {} - }; - } - - private static createIdentity(identity?: CmsIdentity | null): CmsIdentity { - return { - id: identity?.id || "", - displayName: identity?.displayName || "", - type: identity?.type || "" - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.test.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.test.ts deleted file mode 100644 index e2c9295795a..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.test.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; -import { GetFolderHierarchy } from "./GetFolderHierarchy.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { loadedFolderCacheFactory } from "../cache/LoadedFoldersCacheFactory.js"; -import { - GetFolderHierarchyGatewayResponse, - IGetFolderHierarchyGateway -} from "~/features/folders/getFolderHierarchy/IGetFolderHierarchyGateway.js"; - -describe("GetFolderHierarchy", () => { - const type = "abc"; - - const foldersCache = folderCacheFactory.getCache(type); - const loadedFoldersCache = loadedFolderCacheFactory.getCache(type); - - beforeEach(() => { - foldersCache.clear(); - loadedFoldersCache.clear(); - vi.resetAllMocks(); - }); - - class GetFolderHierarchyMockGateway implements IGetFolderHierarchyGateway { - mockResponse: GetFolderHierarchyGatewayResponse; - - // Had to use `any` as the mock folders passed in the tests below are also partial objects. - constructor(mockResponse: any) { - this.mockResponse = mockResponse as GetFolderHierarchyGatewayResponse; - } - - async execute() { - return this.mockResponse; - } - } - - it("should update the list of folders in both `cache` and `loadedCache` when `parents` and `children` are returned by the gateway", async () => { - const gateway = new GetFolderHierarchyMockGateway({ - parents: [ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-2", - parentId: "folder-1", - type - }, - { - id: "folder-3", - title: "Folder 3", - slug: "folder-3", - parentId: "folder-2", - type - } - ], - siblings: [ - { - id: "folder-4", - title: "Folder 4", - slug: "folder-4", - parentId: "folder-3", - type - }, - { - id: "folder-5", - title: "Folder 5", - slug: "folder-5", - parentId: "folder-3", - type - } - ] - }); - - const spy = vi.spyOn(gateway, "execute"); - - const getFolderHierarchy = GetFolderHierarchy.getInstance(type, gateway); - - expect(foldersCache.hasItems()).toBeFalse(); - expect(loadedFoldersCache.hasItems()).toBeFalse(); - await getFolderHierarchy.useCase.execute({ id: "folder-0" }); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith({ type, id: "folder-0" }); - - expect(foldersCache.hasItems()).toBeTrue(); - expect(foldersCache.count()).toEqual(5); - // We are storing only the parent folders id in the loadedFoldersCache - expect(loadedFoldersCache.count()).toEqual(3); - expect(loadedFoldersCache.getItems()).toEqual(["folder-1", "folder-2", "folder-3"]); - - // This call should be idempotent: the number of elements in cache should not change - await getFolderHierarchy.useCase.execute({ id: "folder-0" }); - expect(foldersCache.count()).toEqual(5); - expect(loadedFoldersCache.count()).toEqual(3); - }); - - it("should only update the list of folders in `cache` when `children` are returned by the gateway", async () => { - const gateway = new GetFolderHierarchyMockGateway({ - parents: [], - siblings: [ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-2", - parentId: null, - type - } - ] - }); - - const spy = vi.spyOn(gateway, "execute"); - - const getFolderHierarchy = GetFolderHierarchy.getInstance(type, gateway); - - expect(foldersCache.hasItems()).toBeFalse(); - expect(loadedFoldersCache.hasItems()).toBeFalse(); - await getFolderHierarchy.useCase.execute({ id: "folder-0" }); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith({ type, id: "folder-0" }); - - expect(foldersCache.hasItems()).toBeTrue(); - expect(foldersCache.count()).toEqual(2); - // We are NOT storing any folder loadedFoldersCache - expect(loadedFoldersCache.hasItems()).toBeFalse(); - }); - - it("should handle gateway errors gracefully", async () => { - class GetFolderHierarchyErrorMockGateway implements IGetFolderHierarchyGateway { - async execute(): Promise { - throw new Error("Gateway error"); - } - } - - const gateway = new GetFolderHierarchyErrorMockGateway(); - const spy = vi.spyOn(gateway, "execute"); - - const getFolderHierarchy = GetFolderHierarchy.getInstance(type, gateway); - - expect(foldersCache.hasItems()).toBeFalse(); - - await expect(getFolderHierarchy.useCase.execute({ id: "folder-0" })).rejects.toThrow( - "Gateway error" - ); - - expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); - }); - - it("should clear cache when type changes", async () => { - const gatewayAbc = new GetFolderHierarchyMockGateway({ - parents: [ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-2", - parentId: "folder-1", - type - }, - { - id: "folder-3", - title: "Folder 3", - slug: "folder-3", - parentId: "folder-2", - type - } - ], - siblings: [ - { - id: "folder-4", - title: "Folder 4", - slug: "folder-4", - parentId: "folder-3", - type - }, - { - id: "folder-5", - title: "Folder 5", - slug: "folder-5", - parentId: "folder-3", - type - } - ] - }); - - const newType = "xyz"; - const gatewayXyz = new GetFolderHierarchyMockGateway({ - parents: [ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-2", - parentId: "folder-1", - type - } - ], - siblings: [ - { - id: "folder-3", - title: "Folder 3", - slug: "folder-4", - parentId: "folder-2", - type - } - ] - }); - - const spyAbc = vi.spyOn(gatewayAbc, "execute"); - const spyXzy = vi.spyOn(gatewayXyz, "execute"); - - const getFolderHierarchyAbc = GetFolderHierarchy.getInstance(type, gatewayAbc); - - expect(foldersCache.hasItems()).toBeFalse(); - - await getFolderHierarchyAbc.useCase.execute({ id: "folder-0" }); - - expect(spyAbc).toHaveBeenCalledTimes(1); - expect(foldersCache.count()).toEqual(5); - expect(loadedFoldersCache.count()).toEqual(3); - - const foldersCacheXyz = folderCacheFactory.getCache(newType); - const loadedFoldersCacheXyz = loadedFolderCacheFactory.getCache(newType); - const getFolderHierarchyXyz = GetFolderHierarchy.getInstance(newType, gatewayXyz); - - expect(foldersCacheXyz.hasItems()).toBeFalse(); - expect(loadedFoldersCacheXyz.hasItems()).toBeFalse(); - - await getFolderHierarchyXyz.useCase.execute({ id: "folder-0" }); - - expect(spyXzy).toHaveBeenCalledTimes(1); - expect(foldersCacheXyz.count()).toEqual(3); - expect(loadedFoldersCacheXyz.count()).toEqual(2); - }); -}); diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.ts deleted file mode 100644 index 87cc2e694e9..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchy.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { LoadingRepository } from "@webiny/app-utils"; -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IGetFolderHierarchyUseCase } from "./IGetFolderHierarchyUseCase.js"; -import type { IGetFolderHierarchyGateway } from "./IGetFolderHierarchyGateway.js"; -import { GetFolderHierarchyUseCaseWithLoading } from "./GetFolderHierarchyUseCaseWithLoading.js"; -import { GetFolderHierarchyRepository } from "./GetFolderHierarchyRepository.js"; -import { GetFolderHierarchyUseCase } from "./GetFolderHierarchyUseCase.js"; -import type { ListCache } from "../cache/index.js"; -import { folderCacheFactory, loadedFolderCacheFactory } from "../cache/index.js"; -import type { Folder } from "../Folder.js"; - -interface IGetFolderHierarchyInstance { - useCase: IGetFolderHierarchyUseCase; - folders: ListCache; - loading: LoadingRepository; -} - -export class GetFolderHierarchy { - public static getInstance( - type: string, - gateway: IGetFolderHierarchyGateway - ): IGetFolderHierarchyInstance { - const foldersCache = folderCacheFactory.getCache(type); - const loadedCache = loadedFolderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new GetFolderHierarchyRepository( - foldersCache, - loadedCache, - gateway, - type - ); - const useCase = new GetFolderHierarchyUseCase(repository); - const useCaseWithLoading = new GetFolderHierarchyUseCaseWithLoading( - loadingRepository, - useCase - ); - - return { - useCase: useCaseWithLoading, - folders: foldersCache, - loading: loadingRepository - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyGqlGateway.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyGqlGateway.ts deleted file mode 100644 index 87334bdbd92..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyGqlGateway.ts +++ /dev/null @@ -1,119 +0,0 @@ -import type ApolloClient from "apollo-client"; -import gql from "graphql-tag"; -import type { - GetFolderHierarchyGatewayParams, - IGetFolderHierarchyGateway -} from "./IGetFolderHierarchyGateway.js"; -import type { AcoError, FolderItem } from "~/types.js"; -import { ROOT_FOLDER } from "~/constants.js"; - -interface GetFolderHierarchyResponseData { - parents: FolderItem[]; - siblings: FolderItem[]; -} - -export interface GetFolderHierarchyResponse { - aco: { - getFolderHierarchy: { - data: GetFolderHierarchyResponseData | null; - error: AcoError | null; - }; - }; -} - -export interface GetFolderHierarchyQueryVariables { - type: string; - id: string; -} - -export const GET_FOLDER_HIERARCHY = (FOLDER_FIELDS: string) => gql` - query GetFolderHierarchy($type: String!, $id: ID!) { - aco { - getFolderHierarchy(type: $type, id: $id) { - data { - parents ${FOLDER_FIELDS} - siblings ${FOLDER_FIELDS} - } - error { - code - data - message - } - } - } - } -`; - -export class GetFolderHierarchyGqlGateway implements IGetFolderHierarchyGateway { - private client: ApolloClient; - private modelFields: string; - - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } - - async execute(params: GetFolderHierarchyGatewayParams) { - const { data: response } = await this.client.query< - GetFolderHierarchyResponse, - GetFolderHierarchyQueryVariables - >({ - query: GET_FOLDER_HIERARCHY(this.modelFields), - variables: { - ...params - }, - fetchPolicy: "network-only" - }); - - if (!response) { - throw new Error( - `Network error while listing folder hierarchy for the provided type/id: ${params.type}/${params.id}.` - ); - } - - const { data, error } = response.aco.getFolderHierarchy; - - if (!data) { - throw new Error( - error?.message || - `Could not fetch folder hierarchy for the provided type/id: ${params.type}/${params.id}.` - ); - } - - return { - parents: [this.getRootFolder(), ...data.parents], - siblings: data.siblings - }; - } - - private getRootFolder(): FolderItem { - return { - id: ROOT_FOLDER, - title: "Home", - permissions: [], - parentId: "0", - path: ROOT_FOLDER, - slug: "", - createdOn: "", - createdBy: { - id: "", - displayName: "", - type: "" - }, - hasNonInheritedPermissions: false, - canManagePermissions: true, - canManageStructure: true, - canManageContent: true, - savedOn: "", - savedBy: { - id: "", - displayName: "", - type: "" - }, - modifiedOn: null, - modifiedBy: null, - type: "$ROOT", - extensions: {} - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyRepository.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyRepository.ts deleted file mode 100644 index 92b224cd65b..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyRepository.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { ListCache, LoadedCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; -import type { - GetFolderHierarchyRepositoryParams, - IGetFolderHierarchyRepository -} from "./IGetFolderHierarchyRepository.js"; -import type { IGetFolderHierarchyGateway } from "./IGetFolderHierarchyGateway.js"; - -export class GetFolderHierarchyRepository implements IGetFolderHierarchyRepository { - private cache: ListCache; - private loadedCache: LoadedCache; - private gateway: IGetFolderHierarchyGateway; - private readonly type: string; - - constructor( - cache: ListCache, - loadedCache: LoadedCache, - gateway: IGetFolderHierarchyGateway, - type: string - ) { - this.cache = cache; - this.loadedCache = loadedCache; - this.gateway = gateway; - this.type = type; - } - - async execute(params: GetFolderHierarchyRepositoryParams) { - if (this.loadedCache.getItem(item => item === params.id)) { - return; - } - - const response = await this.gateway.execute({ type: this.type, id: params.id }); - - const { parents = [], siblings = [] } = response; - - if (parents.length > 0) { - this.loadedCache.addItems(parents.map(parent => parent.id)); - } - - this.cache.addItems([...parents, ...siblings].map(item => Folder.create(item))); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCase.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCase.ts deleted file mode 100644 index ff51b9f3533..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCase.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { - GetFolderHierarchyUseCaseParams, - IGetFolderHierarchyUseCase -} from "./IGetFolderHierarchyUseCase.js"; -import type { IGetFolderHierarchyRepository } from "./IGetFolderHierarchyRepository.js"; - -export class GetFolderHierarchyUseCase implements IGetFolderHierarchyUseCase { - private repository: IGetFolderHierarchyRepository; - - constructor(repository: IGetFolderHierarchyRepository) { - this.repository = repository; - } - - async execute({ id }: GetFolderHierarchyUseCaseParams) { - await this.repository.execute({ - id - }); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCaseWithLoading.ts deleted file mode 100644 index 41ab24af228..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/GetFolderHierarchyUseCaseWithLoading.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; -import type { - GetFolderHierarchyUseCaseParams, - IGetFolderHierarchyUseCase -} from "./IGetFolderHierarchyUseCase.js"; -import { LoadingActionsEnum } from "~/types.js"; - -export class GetFolderHierarchyUseCaseWithLoading implements IGetFolderHierarchyUseCase { - private loadingRepository: ILoadingRepository; - private useCase: IGetFolderHierarchyUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: IGetFolderHierarchyUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } - - async execute(params: GetFolderHierarchyUseCaseParams) { - await this.loadingRepository.runCallBack( - this.useCase.execute(params), - LoadingActionsEnum.init - ); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyGateway.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyGateway.ts deleted file mode 100644 index a060e48ad1e..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyGateway.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { FolderGqlDto } from "./FolderGqlDto.js"; - -export interface GetFolderHierarchyGatewayParams { - type: string; - id: string; -} - -export interface GetFolderHierarchyGatewayResponse { - parents: FolderGqlDto[]; - siblings: FolderGqlDto[]; -} - -export interface IGetFolderHierarchyGateway { - execute: ( - params: GetFolderHierarchyGatewayParams - ) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyRepository.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyRepository.ts deleted file mode 100644 index 5b6ecf37475..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyRepository.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface GetFolderHierarchyRepositoryParams { - id: string; -} - -export interface IGetFolderHierarchyRepository { - execute: (params: GetFolderHierarchyRepositoryParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyUseCase.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyUseCase.ts deleted file mode 100644 index 35d0c6b5090..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/IGetFolderHierarchyUseCase.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface GetFolderHierarchyUseCaseParams { - id: string; -} - -export interface IGetFolderHierarchyUseCase { - execute: (params: GetFolderHierarchyUseCaseParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/index.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/index.ts deleted file mode 100644 index 71573d012e6..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./useGetFolderHierarchy.js"; diff --git a/packages/app-aco/src/features/folders/getFolderHierarchy/useGetFolderHierarchy.ts b/packages/app-aco/src/features/folders/getFolderHierarchy/useGetFolderHierarchy.ts deleted file mode 100644 index 8bb60bba6c8..00000000000 --- a/packages/app-aco/src/features/folders/getFolderHierarchy/useGetFolderHierarchy.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; -import { autorun } from "mobx"; -import { useApolloClient } from "@apollo/react-hooks"; -import { GetFolderHierarchyGqlGateway } from "./GetFolderHierarchyGqlGateway.js"; -import { GetFolderHierarchy } from "./GetFolderHierarchy.js"; -import { FolderDtoMapper } from "./FolderDto.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; -import type { FolderItem } from "~/types.js"; -import { LoadingActionsEnum } from "~/types.js"; - -export const useGetFolderHierarchy = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new GetFolderHierarchyGqlGateway(client, fields); - - const [vm, setVm] = useState<{ - folders: FolderItem[]; - }>({ - folders: [] - }); - - const { - useCase, - folders: foldersCache, - loading: loadingState - } = useMemo(() => { - return GetFolderHierarchy.getInstance(type, gateway); - }, [type, gateway]); - - const getFolderHierarchy = useCallback( - (id: string) => { - return useCase.execute({ id }); - }, - [useCase] - ); - - const getIsFolderLoading = useCallback( - (action = LoadingActionsEnum.init) => { - if (!loadingState) { - return true; - } - - return loadingState.isLoading(action); - }, - [loadingState] - ); - - useEffect(() => { - return autorun(() => { - const folders = foldersCache.getItems().map(folder => FolderDtoMapper.toDTO(folder)); - - setVm(vm => ({ - ...vm, - folders - })); - }); - }, [foldersCache]); - - return { - ...vm, - getFolderHierarchy, - getIsFolderLoading - }; -}; diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/FolderPermissionName.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/FolderPermissionName.ts deleted file mode 100644 index a9483befe9b..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/FolderPermissionName.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type FolderPermissionName = - | "canManagePermissions" - | "canManageStructure" - | "canManageContent"; diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.ts index 4775e4743d3..73205cd1980 100644 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.ts +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.test.ts @@ -1,14 +1,50 @@ -import { describe, it, expect, beforeEach } from "vitest"; -import { GetFolderLevelPermission } from "./GetFolderLevelPermission.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { Folder } from "../Folder.js"; +import { describe, it, expect } from "vitest"; +import { Container } from "@webiny/di"; +import { createTestWcpLicense } from "@webiny/wcp/testing/createTestWcpLicense.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { GetFolderLevelPermissionFeature } from "~/features/folders/getFolderLevelPermission/feature.js"; +import { GetFolderLevelPermissionUseCase } from "~/features/folders/getFolderLevelPermission/abstractions.js"; +import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import type { ILicense } from "@webiny/wcp/types"; +import { License, WCP_FEATURE_LABEL } from "@webiny/wcp"; + +class WcpServiceMock implements WcpService.Interface { + private readonly license: ILicense; + + constructor(flpEnabled: boolean) { + this.license = License.fromLicenseDto( + createTestWcpLicense({ folderLevelPermissions: flpEnabled }) + ); + } + + canUseFeature(featureName: keyof typeof WCP_FEATURE_LABEL): boolean { + return this.license.canUseFeature(featureName); + } + + getProject(): ILicense { + return this.license; + } + + isLoaded(): boolean { + return true; + } + + loadProject(): Promise { + return Promise.resolve(undefined); + } +} describe("GetFolderLevelPermission", () => { const type = "abc"; - const foldersCache = folderCacheFactory.getCache(type); - beforeEach(() => { - foldersCache.clear(); + function setupTest(params: { flpEnabled: boolean }) { + const { flpEnabled } = params; + const container = new Container(); + const foldersCache = new ListCache(); + foldersCache.addItems([ Folder.create({ id: "folder-canManageContent", @@ -46,181 +82,113 @@ describe("GetFolderLevelPermission", () => { type }) ]); - }); + + container.registerInstance(WcpService, new WcpServiceMock(flpEnabled)); + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + + GetFolderLevelPermissionFeature.register(container); + + return { + container, + foldersCache, + useCase: container.resolve(GetFolderLevelPermissionUseCase) + }; + } it("should return true in case a specific permission is set at folder level and FLP is enabled", async () => { + const { useCase } = setupTest({ flpEnabled: true }); + // canManagePermissions { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManagePermissions", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-canManagePermissions" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-canManagePermissions", "canManagePermissions"); + expect(result).toBe(true); } // canManageStructure { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageStructure", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-canManageStructure" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-canManageStructure", "canManageStructure"); + expect(result).toBe(true); } // canManageStructure { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageContent", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-canManageContent" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-canManageContent", "canManageContent"); + + expect(result).toBe(true); } }); it("should return false in case a specific permission is not set at folder level and FLP is enabled", async () => { + const { useCase } = setupTest({ flpEnabled: true }); // canManagePermissions { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManagePermissions", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeFalse(); + const result = useCase.execute("folder-no-permissions", "canManagePermissions"); + expect(result).toBe(false); } // canManageStructure { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageStructure", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeFalse(); + const result = useCase.execute("folder-no-permissions", "canManageStructure"); + + expect(result).toBe(false); } - // canManageStructure + // canManageContent { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageContent", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeFalse(); + const result = useCase.execute("folder-no-permissions", "canManageContent"); + + expect(result).toBe(false); } }); it("should return always false in case the folder is not found", async () => { + const { useCase } = setupTest({ flpEnabled: true }); + // canManagePermissions { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManagePermissions", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "not-existing-folder" - }); - expect(result).toBeFalse(); + const result = useCase.execute("not-existing-folder", "canManagePermissions"); + + expect(result).toBe(false); } // canManageStructure { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageStructure", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "not-existing-folder" - }); - expect(result).toBeFalse(); + const result = useCase.execute("not-existing-folder", "canManageStructure"); + + expect(result).toBe(false); } - // canManageStructure + // canManageContent { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageContent", - true - ); - - const result = getFolderLevelPermission.execute({ - id: "not-existing-folder" - }); - expect(result).toBeFalse(); + const result = useCase.execute("not-existing-folder", "canManageContent"); + + expect(result).toBe(false); } }); it("should return always true in case FLP is not enabled", async () => { + const { useCase } = setupTest({ flpEnabled: false }); + // canManagePermissions { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManagePermissions", - false - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-no-permissions", "canManagePermissions"); + + expect(result).toBe(true); } // canManageStructure { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageStructure", - false - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-no-permissions", "canManageStructure"); + + expect(result).toBe(true); } - // canManageStructure + // canManageContent { - const getFolderLevelPermission = GetFolderLevelPermission.getInstance( - type, - "canManageContent", - false - ); - - const result = getFolderLevelPermission.execute({ - id: "folder-no-permissions" - }); - expect(result).toBeTrue(); + const result = useCase.execute("folder-no-permissions", "canManageContent"); + + expect(result).toBe(true); } }); }); diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.ts deleted file mode 100644 index 315d7ef7736..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermission.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { IGetFolderLevelPermissionUseCase } from "./IGetFolderLevelPermissionUseCase.js"; -import { GetFolderLevelPermissionRepository } from "./GetFolderLevelPermissionRepository.js"; -import { GetFolderLevelPermissionWithFlpUseCase } from "./GetFolderLevelPermissionWithFlpUseCase.js"; -import { GetFolderLevelPermissionUseCase } from "./GetFolderLevelPermissionUseCase.js"; -import type { FolderPermissionName } from "./FolderPermissionName.js"; -import { folderCacheFactory } from "../cache/index.js"; - -export class GetFolderLevelPermission { - public static getInstance( - type: string, - permissionName: FolderPermissionName, - canUseFlp: boolean - ): IGetFolderLevelPermissionUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const repository = new GetFolderLevelPermissionRepository(foldersCache, permissionName); - - if (canUseFlp) { - return new GetFolderLevelPermissionWithFlpUseCase(repository); - } - - return new GetFolderLevelPermissionUseCase(); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionRepository.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionRepository.ts deleted file mode 100644 index 86644b2e22e..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionRepository.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { ListCache } from "../cache/index.js"; -import type { IGetFolderLevelPermissionRepository } from "./IGetFolderLevelPermissionRepository.js"; -import type { FolderPermissionName } from "./FolderPermissionName.js"; -import type { Folder } from "../Folder.js"; - -export class GetFolderLevelPermissionRepository implements IGetFolderLevelPermissionRepository { - private cache: ListCache; - private readonly permissionName: FolderPermissionName; - - constructor(cache: ListCache, permissionName: FolderPermissionName) { - this.cache = cache; - this.permissionName = permissionName; - } - - execute(id: string) { - const folder = this.cache.getItem(folder => folder.id === id); - - if (!folder) { - return false; - } - - return folder[this.permissionName] ?? false; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionUseCase.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionUseCase.ts index 6a499c77b9a..8cae74d5b2a 100644 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionUseCase.ts +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionUseCase.ts @@ -1,7 +1,12 @@ -import type { IGetFolderLevelPermissionUseCase } from "./IGetFolderLevelPermissionUseCase.js"; +import { GetFolderLevelPermissionUseCase as UseCaseAbstraction } from "./abstractions.js"; -export class GetFolderLevelPermissionUseCase implements IGetFolderLevelPermissionUseCase { +class GetFolderLevelPermissionUseCaseImpl implements UseCaseAbstraction.Interface { execute() { return true; } } + +export const GetFolderLevelPermissionUseCase = UseCaseAbstraction.createImplementation({ + implementation: GetFolderLevelPermissionUseCaseImpl, + dependencies: [] +}); diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionWithFlpUseCase.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionWithFlpUseCase.ts deleted file mode 100644 index 798daf20b30..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/GetFolderLevelPermissionWithFlpUseCase.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { - GetFolderLevelPermissionParams, - IGetFolderLevelPermissionUseCase -} from "./IGetFolderLevelPermissionUseCase.js"; -import type { IGetFolderLevelPermissionRepository } from "./IGetFolderLevelPermissionRepository.js"; - -export class GetFolderLevelPermissionWithFlpUseCase implements IGetFolderLevelPermissionUseCase { - private repository: IGetFolderLevelPermissionRepository; - - constructor(repository: IGetFolderLevelPermissionRepository) { - this.repository = repository; - } - - execute(params: GetFolderLevelPermissionParams) { - return this.repository.execute(params.id); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionRepository.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionRepository.ts deleted file mode 100644 index 69585a37ed3..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionRepository.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IGetFolderLevelPermissionRepository { - execute: (id: string) => boolean; -} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionUseCase.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionUseCase.ts deleted file mode 100644 index 443205e3c38..00000000000 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/IGetFolderLevelPermissionUseCase.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface GetFolderLevelPermissionParams { - id: string; -} - -export interface IGetFolderLevelPermissionUseCase { - execute: (params: GetFolderLevelPermissionParams) => boolean; -} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/abstractions.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/abstractions.ts new file mode 100644 index 00000000000..e419046f3eb --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/abstractions.ts @@ -0,0 +1,14 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import { FolderPermissionName } from "../abstractions.js"; + +export interface IGetFolderLevelPermissionUseCase { + execute: (id: string, permissionName: FolderPermissionName) => boolean; +} + +export const GetFolderLevelPermissionUseCase = createAbstraction( + "GetFolderLevelPermissionUseCase" +); + +export namespace GetFolderLevelPermissionUseCase { + export type Interface = IGetFolderLevelPermissionUseCase; +} diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.ts new file mode 100644 index 00000000000..14a06904ba3 --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/decorators/GetFolderLevelPermissionWithFlpDecorator.ts @@ -0,0 +1,33 @@ +import type { FolderPermissionName } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { GetFolderLevelPermissionUseCase as UseCaseAbstraction } from "../abstractions.js"; +import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js"; + +class GetFolderLevelPermissionWithFlpDecoratorImpl implements UseCaseAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private wcp: WcpService.Interface, + private decoratee: UseCaseAbstraction.Interface + ) {} + + execute(id: string, permissionName: FolderPermissionName) { + // Check if WCP allows folder-level permissions feature + if (!this.wcp.getProject().canUseFolderLevelPermissions()) { + return this.decoratee.execute(id, permissionName); + } + + // Check the permissions + const folder = this.cache.getItem(folder => folder.id === id); + + if (!folder) { + return false; + } + + return folder[permissionName] ?? false; + } +} + +export const GetFolderLevelPermissionWithFlpDecorator = UseCaseAbstraction.createDecorator({ + decorator: GetFolderLevelPermissionWithFlpDecoratorImpl, + dependencies: [FoldersCache, WcpService] +}); diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/feature.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/feature.ts new file mode 100644 index 00000000000..9faa5713d4f --- /dev/null +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/feature.ts @@ -0,0 +1,20 @@ +import { createFeature } from "@webiny/feature/admin"; +import { GetFolderLevelPermissionUseCase as UseCase } from "./abstractions.js"; +import { GetFolderLevelPermissionUseCase } from "./GetFolderLevelPermissionUseCase.js"; +import { GetFolderLevelPermissionWithFlpDecorator } from "./decorators/GetFolderLevelPermissionWithFlpDecorator.js"; + +export const GetFolderLevelPermissionFeature = createFeature({ + name: "GetFolderLevelPermission", + register(container) { + // Register base use case + container.register(GetFolderLevelPermissionUseCase); + + // Register decorator + container.registerDecorator(GetFolderLevelPermissionWithFlpDecorator); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/getFolderLevelPermission/useGetFolderLevelPermission.ts b/packages/app-aco/src/features/folders/getFolderLevelPermission/useGetFolderLevelPermission.ts index b608ddeeb18..dfc74a8e3ea 100644 --- a/packages/app-aco/src/features/folders/getFolderLevelPermission/useGetFolderLevelPermission.ts +++ b/packages/app-aco/src/features/folders/getFolderLevelPermission/useGetFolderLevelPermission.ts @@ -1,23 +1,16 @@ import { useCallback } from "react"; -import { useWcp } from "@webiny/app-admin"; -import { GetFolderLevelPermission } from "./GetFolderLevelPermission.js"; -import type { FolderPermissionName } from "./FolderPermissionName.js"; -import { useFoldersType } from "~/hooks/index.js"; +import { useFeature } from "@webiny/app"; +import { GetFolderLevelPermissionFeature } from "./feature.js"; +import { FolderPermissionName } from "../abstractions.js"; export const useGetFolderLevelPermission = (permissionName: FolderPermissionName) => { - const type = useFoldersType(); - const wcp = useWcp(); + const { useCase } = useFeature(GetFolderLevelPermissionFeature); const getFolderLevelPermission = useCallback( (id: string) => { - const instance = GetFolderLevelPermission.getInstance( - type, - permissionName, - wcp.canUseFolderLevelPermissions() - ); - return instance.execute({ id }); + return useCase.execute(id, permissionName); }, - [type, wcp] + [useCase] ); return { diff --git a/packages/app-aco/src/features/folders/getFolderModel/FolderModelContext.tsx b/packages/app-aco/src/features/folders/getFolderModel/FolderModelContext.tsx deleted file mode 100644 index 323ff9b8875..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/FolderModelContext.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useState, useCallback, useMemo, useEffect } from "react"; -import { autorun, toJS } from "mobx"; -import { useApolloClient } from "@apollo/react-hooks"; -import { OverlayLoader } from "@webiny/admin-ui"; -import type { FolderModelDto } from "~/features/index.js"; -import { GetFolderModelGqlGateway } from "~/features/folders/getFolderModel/GetFolderModelGqlGateway.js"; -import { GetFolderModel } from "~/features/folders/getFolderModel/GetFolderModel.js"; -import type { Decorator, GenericComponent } from "@webiny/app"; -import { Plugin } from "@webiny/app"; - -export const FolderModelContext = React.createContext(undefined); - -const acoFolderModelProvider: Decorator< - GenericComponent<{ children: React.ReactNode }> -> = Original => { - return function AcoFolderProvider({ children }) { - const client = useApolloClient(); - const gateway = new GetFolderModelGqlGateway(client); - - const [model, setModel] = useState(undefined); - - const { useCase, repository } = useMemo(() => { - return GetFolderModel.getInstance(gateway); - }, [gateway]); - - const getFolderModel = useCallback(() => { - return useCase.execute(); - }, [useCase]); - - useEffect(() => { - if (model) { - return; - } - - getFolderModel(); - }, []); - - useEffect(() => { - return autorun(() => { - const model = repository.getModel(); - setModel(state => { - if (model) { - return { ...toJS(model) }; - } - return state; - }); - }); - }, []); - - if (!model) { - return ; - } - - return ( - - {children} - - ); - }; -}; - -export const FolderModelProviderModule = () => { - return ; -}; diff --git a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.test.ts b/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.test.ts deleted file mode 100644 index cbfb2017dac..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; -import { GetFolderModel } from "./GetFolderModel.js"; -import type { IGetFolderModelGateway } from "~/features/folders/getFolderModel/IGetFolderModelGateway.js"; -import type { FolderModelDto } from "~/features/index.js"; - -describe("GetFolderModel", () => { - const mockFolderModel = { - id: "folder-model", - group: "custom-group", - version: "0.0.0", - fields: [ - { - id: "field1", - label: "Field 1", - type: "string", - fieldId: "field1" - }, - { - id: "field2", - label: "Field 2", - type: "string", - fieldId: "field2" - } - ], - lockedFields: [], - icon: "icon", - name: "Folder", - modelId: "folder-model", - singularApiName: "folderModel", - pluralApiName: "foldersModel", - titleFieldId: null, - descriptionFieldId: null, - imageFieldId: null - } as unknown as FolderModelDto; - - class GetFolderModelMockGateway implements IGetFolderModelGateway { - async execute() { - return mockFolderModel; - } - } - - const gateway = new GetFolderModelMockGateway(); - - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("should be able to get the folders model", async () => { - const spy = vi.spyOn(gateway, "execute"); - - const { useCase, repository } = GetFolderModel.getInstance(gateway); - - expect(repository.getModel()).toBeUndefined(); - - await useCase.execute(); - - expect(spy).toHaveBeenCalledTimes(1); - expect(repository.getModel()).toEqual(mockFolderModel); - }); - - it("should handle gateway errors gracefully", async () => { - class GetFolderModelErrorMockGateway implements IGetFolderModelGateway { - async execute(): Promise { - throw new Error("Gateway error"); - } - } - - const errorGateway = new GetFolderModelErrorMockGateway(); - - const spy = vi.spyOn(errorGateway, "execute"); - - const { useCase, repository } = GetFolderModel.getInstance(errorGateway); - - expect(repository.getModel()).toBeUndefined(); - - await expect(useCase.execute()).rejects.toThrow("Gateway error"); - - expect(spy).toHaveBeenCalledTimes(1); - expect(repository.getModel()).toBeUndefined(); - }); - - it("should cache folders model after listing", async () => { - const { useCase, repository } = GetFolderModel.getInstance(gateway); - - expect(repository.getModel()).toBeUndefined(); - - await useCase.execute(); - - expect(gateway.execute).toHaveBeenCalledTimes(1); - expect(repository.getModel()).toEqual(mockFolderModel); - - // Execute again, it should NOT execute the gateway again - await useCase.execute(); - expect(gateway.execute).toHaveBeenCalledTimes(1); - expect(repository.getModel()).toEqual(mockFolderModel); - }); -}); diff --git a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.ts b/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.ts deleted file mode 100644 index 644901c53f0..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModel.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { IGetFolderModelGateway } from "./IGetFolderModelGateway.js"; -import { GetFolderModelRepository } from "./GetFolderModelRepository.js"; -import { GetFolderModelUseCase } from "./GetFolderModelUseCase.js"; -import type { IGetFolderModelUseCase } from "./IGetFolderModelUseCase.js"; -import type { IGetFolderModelRepository } from "./IGetFolderModelRepository.js"; - -interface IGetFolderModelInstance { - useCase: IGetFolderModelUseCase; - repository: IGetFolderModelRepository; -} - -export class GetFolderModel { - public static getInstance(gateway: IGetFolderModelGateway): IGetFolderModelInstance { - const repository = new GetFolderModelRepository(gateway); - const useCase = new GetFolderModelUseCase(repository); - - return { - useCase, - repository - }; - } -} diff --git a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelUseCase.ts b/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelUseCase.ts deleted file mode 100644 index fa013c6da75..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/GetFolderModelUseCase.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IGetFolderModelRepository } from "./IGetFolderModelRepository.js"; -import type { IGetFolderModelUseCase } from "./IGetFolderModelUseCase.js"; - -export class GetFolderModelUseCase implements IGetFolderModelUseCase { - private repository: IGetFolderModelRepository; - - constructor(repository: IGetFolderModelRepository) { - this.repository = repository; - } - - async execute() { - await this.repository.load(); - } -} diff --git a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelGateway.ts b/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelGateway.ts deleted file mode 100644 index 6438ac4cacb..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelGateway.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { FolderModelDto } from "./FolderModelDto.js"; - -export interface IGetFolderModelGateway { - execute: () => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelRepository.ts b/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelRepository.ts deleted file mode 100644 index ccc4772913f..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelRepository.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { FolderModelDto } from "./FolderModelDto.js"; - -export interface IGetFolderModelRepository { - load: () => Promise; - getModel: () => FolderModelDto | undefined; - hasModel: () => boolean; -} diff --git a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelUseCase.ts b/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelUseCase.ts deleted file mode 100644 index 75463cdcf3d..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/IGetFolderModelUseCase.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IGetFolderModelUseCase { - execute: () => Promise; -} diff --git a/packages/app-aco/src/features/folders/getFolderModel/index.ts b/packages/app-aco/src/features/folders/getFolderModel/index.ts deleted file mode 100644 index 668d07992ba..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./useFolderModel.js"; -export type * from "./FolderModelDto.js"; -export * from "./FolderModelContext.js"; diff --git a/packages/app-aco/src/features/folders/getFolderModel/useFolderModel.ts b/packages/app-aco/src/features/folders/getFolderModel/useFolderModel.ts deleted file mode 100644 index 14de54b43a9..00000000000 --- a/packages/app-aco/src/features/folders/getFolderModel/useFolderModel.ts +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; -import { FolderModelContext } from "./FolderModelContext.js"; - -export function useFolderModel() { - const context = React.useContext(FolderModelContext); - if (!context) { - throw Error(`Missing "FolderModelContext" in the component tree!`); - } - - return context; -} diff --git a/packages/app-aco/src/features/folders/index.ts b/packages/app-aco/src/features/folders/index.ts deleted file mode 100644 index 5b473d352db..00000000000 --- a/packages/app-aco/src/features/folders/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export * from "./Folder.js"; -export * from "./cache/index.js"; -export * from "./createFolder/index.js"; -export * from "./deleteFolder/index.js"; -export * from "./getDescendantFolders/index.js"; -export * from "./getFolder/index.js"; -export * from "./getFolderAncestors/index.js"; -export * from "./getFolderExtensionsFields/index.js"; -export * from "./getFolderHierarchy/index.js"; -export * from "./getFolderLevelPermission/index.js"; -export * from "./getFolderModel/index.js"; -export * from "./listFolders/index.js"; -export * from "./listFoldersByParentIds/index.js"; -export * from "./updateFolder/index.js"; diff --git a/packages/app-aco/src/features/folders/listFolders/FolderDto.ts b/packages/app-aco/src/features/folders/listFolders/FolderDto.ts deleted file mode 100644 index 2bd42b46c05..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/FolderDto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; -import type { Folder } from "../Folder.js"; -import { ROOT_FOLDER } from "~/constants.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - type: string; - parentId: string; - path: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity; - modifiedOn: string; - extensions: Record; -} - -export class FolderDtoMapper { - static toDTO(folder: Folder): FolderDto { - return { - id: folder.id, - title: folder.title, - canManageContent: folder.canManageContent ?? false, - canManagePermissions: folder.canManagePermissions ?? false, - canManageStructure: folder.canManageStructure ?? false, - createdBy: this.createIdentity(folder.createdBy), - createdOn: folder.createdOn ?? "", - hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false, - modifiedBy: this.createIdentity(folder.modifiedBy), - modifiedOn: folder.modifiedOn ?? "", - parentId: folder.parentId ?? ROOT_FOLDER, - path: folder.path, - permissions: folder.permissions ?? [], - savedBy: this.createIdentity(folder.savedBy), - savedOn: folder.savedOn ?? "", - slug: folder.slug, - type: folder.type, - extensions: folder.extensions ?? {} - }; - } - - private static createIdentity(identity?: CmsIdentity | null): CmsIdentity { - return { - id: identity?.id || "", - displayName: identity?.displayName || "", - type: identity?.type || "" - }; - } -} diff --git a/packages/app-aco/src/features/folders/listFolders/FolderGqlDto.ts b/packages/app-aco/src/features/folders/listFolders/FolderGqlDto.ts deleted file mode 100644 index f802d031281..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/FolderGqlDto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; - -export interface FolderGqlDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/listFolders/IListFoldersGateway.ts b/packages/app-aco/src/features/folders/listFolders/IListFoldersGateway.ts deleted file mode 100644 index 11341bf3ffa..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/IListFoldersGateway.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { FolderGqlDto } from "./FolderGqlDto.js"; - -export interface ListFoldersGatewayParams { - type: string; -} - -export interface IListFoldersGateway { - execute: (params: ListFoldersGatewayParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFolders/IListFoldersRepository.ts b/packages/app-aco/src/features/folders/listFolders/IListFoldersRepository.ts deleted file mode 100644 index f9aedf93d50..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/IListFoldersRepository.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IListFoldersRepository { - execute: () => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFolders/IListFoldersUseCase.ts b/packages/app-aco/src/features/folders/listFolders/IListFoldersUseCase.ts deleted file mode 100644 index 7024ce59ede..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/IListFoldersUseCase.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IListFoldersUseCase { - execute: () => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFolders/ListFolders.test.ts b/packages/app-aco/src/features/folders/listFolders/ListFolders.test.ts index 999a92d906b..32cc8f91764 100644 --- a/packages/app-aco/src/features/folders/listFolders/ListFolders.test.ts +++ b/packages/app-aco/src/features/folders/listFolders/ListFolders.test.ts @@ -1,143 +1,137 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; -import { ListFolders } from "./ListFolders.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import type { IListFoldersGateway } from "~/features/folders/listFolders/IListFoldersGateway.js"; -import type { FolderGqlDto } from "~/features/folders/listFolders/FolderGqlDto.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { ListFoldersGateway } from "./abstractions.js"; +import { Container } from "@webiny/di"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { ListFoldersFeature } from "~/features/folders/listFolders/feature.js"; +import { ListFoldersUseCase } from "./abstractions.js"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { LoadingRepository } from "@webiny/app-utils"; + +const type = "abc"; + +class ListFoldersMockGateway implements ListFoldersGateway.Interface { + async execute() { + return [ + { + id: "folder-1", + title: "Folder 1", + slug: "folder-1", + type + }, + { + id: "folder-2", + title: "Folder 2", + slug: "folder-1", + type + }, + { + id: "folder-3", + title: "Folder 3", + slug: "folder-3", + type + } + ] as FolderDto[]; + } +} describe("ListFolders", () => { - class ListFoldersMockGateway implements IListFoldersGateway { - async execute() { - return [ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-1", - type - }, - { - id: "folder-3", - title: "Folder 3", - slug: "folder-3", - type - } - ] as FolderGqlDto[]; - } - } + function setupTest(gateway: ListFoldersGateway.Interface) { + const container = new Container(); + const foldersCache = new ListCache(); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + container.registerInstance(FoldersLoadingRepository, new LoadingRepository()); - const type = "abc"; + ListFoldersFeature.register(container); + container.registerInstance(ListFoldersGateway, gateway); - const foldersCache = folderCacheFactory.getCache(type); + return { container, foldersCache, useCase: container.resolve(ListFoldersUseCase) }; + } beforeEach(() => { - foldersCache.clear(); vi.clearAllMocks(); }); it("should be able to list folders", async () => { const gateway = new ListFoldersMockGateway(); - const listFolders = ListFolders.getInstance(type, gateway); + const { useCase, foldersCache } = setupTest(gateway); const spy = vi.spyOn(gateway, "execute"); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await listFolders.useCase.execute(); + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); const items = foldersCache.getItems(); expect(items.length).toEqual(3); }); it("should return empty array if no folders are found", async () => { - class ListFoldersEmptyMockGateway implements IListFoldersGateway { + class ListFoldersEmptyMockGateway implements ListFoldersGateway.Interface { async execute() { return []; } } const emptyGateway = new ListFoldersEmptyMockGateway(); - const listFolders = ListFolders.getInstance(type, emptyGateway); + const { useCase, foldersCache } = setupTest(emptyGateway); const spy = vi.spyOn(emptyGateway, "execute"); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await listFolders.useCase.execute(); + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); const items = foldersCache.getItems(); expect(items.length).toEqual(0); }); it("should handle gateway errors gracefully", async () => { - class ListFoldersErrorMockGateway implements IListFoldersGateway { - async execute(): Promise { + class ListFoldersErrorMockGateway implements ListFoldersGateway.Interface { + async execute(): Promise { throw new Error("Gateway error"); } } const errorGateway = new ListFoldersErrorMockGateway(); - const listFolders = ListFolders.getInstance(type, errorGateway); + const { useCase, foldersCache } = setupTest(errorGateway); const spy = vi.spyOn(errorGateway, "execute"); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await expect(listFolders.useCase.execute()).rejects.toThrow("Gateway error"); + await expect(useCase.execute()).rejects.toThrow("Gateway error"); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); }); it("should NOT cache folders after listing", async () => { const gateway = new ListFoldersMockGateway(); - const listFolders = ListFolders.getInstance(type, gateway); + const { useCase, foldersCache } = setupTest(gateway); const spy = vi.spyOn(gateway, "execute"); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await listFolders.useCase.execute(); + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); const items = foldersCache.getItems(); expect(items.length).toEqual(3); // Execute again, it should execute the gateway again - await listFolders.useCase.execute(); - expect(spy).toHaveBeenCalledTimes(2); - }); - - it("should clear cache when type changes", async () => { - const gateway = new ListFoldersMockGateway(); - const listFolders = ListFolders.getInstance(type, gateway); - const spy = vi.spyOn(gateway, "execute"); - - expect(foldersCache.hasItems()).toBeFalse(); - - await listFolders.useCase.execute(); - - expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeTrue(); - - const newType = "xyz"; - const newFoldersCache = folderCacheFactory.getCache(newType); - const newListFolders = ListFolders.getInstance(newType, gateway); - - expect(newFoldersCache.hasItems()).toBeFalse(); - - await newListFolders.useCase.execute(); - + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(2); - expect(newFoldersCache.hasItems()).toBeTrue(); }); }); diff --git a/packages/app-aco/src/features/folders/listFolders/ListFolders.ts b/packages/app-aco/src/features/folders/listFolders/ListFolders.ts deleted file mode 100644 index f5b0423b483..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/ListFolders.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { LoadingRepository } from "@webiny/app-utils"; -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IListFoldersUseCase } from "./IListFoldersUseCase.js"; -import type { IListFoldersGateway } from "./IListFoldersGateway.js"; -import { ListFoldersRepository } from "./ListFoldersRepository.js"; -import { ListFoldersUseCaseWithLoading } from "./ListFoldersUseCaseWithLoading.js"; -import { ListFoldersUseCase } from "./ListFoldersUseCase.js"; -import type { ListCache } from "../cache/index.js"; -import { folderCacheFactory } from "../cache/index.js"; -import type { Folder } from "../Folder.js"; - -interface IListFoldersInstance { - useCase: IListFoldersUseCase; - folders: ListCache; - loading: LoadingRepository; -} - -export class ListFolders { - public static getInstance(type: string, gateway: IListFoldersGateway): IListFoldersInstance { - const foldersCache = folderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new ListFoldersRepository(foldersCache, gateway, type); - const useCase = new ListFoldersUseCase(repository); - const useCaseWithLoading = new ListFoldersUseCaseWithLoading(loadingRepository, useCase); - - return { - useCase: useCaseWithLoading, - folders: foldersCache, - loading: loadingRepository - }; - } -} diff --git a/packages/app-aco/src/features/folders/listFolders/ListFoldersCompressedGqlGateway.ts b/packages/app-aco/src/features/folders/listFolders/ListFoldersCompressedGqlGateway.ts deleted file mode 100644 index cd49120cafc..00000000000 --- a/packages/app-aco/src/features/folders/listFolders/ListFoldersCompressedGqlGateway.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type ApolloClient from "apollo-client"; -import gql from "graphql-tag"; -import pako from "pako"; -import type { IListFoldersGateway, ListFoldersGatewayParams } from "./IListFoldersGateway.js"; -import type { AcoError, FolderItem } from "~/types.js"; -import { ROOT_FOLDER } from "~/constants.js"; - -export interface ListFoldersCompressedResponse { - aco: { - listFoldersCompressed: { - data: { - value: string; - } | null; - error: AcoError | null; - }; - }; -} - -export interface ListFoldersCompressedQueryVariables { - type: string; - limit: number; - sort?: Record; - after?: string | null; -} - -export const LIST_FOLDERS_COMPRESSED = gql` - query ListFoldersCompressed($type: String!, $limit: Int!) { - aco { - listFoldersCompressed(where: { type: $type }, limit: $limit) { - data { - value - } - error { - code - data - message - } - } - } - } -`; - -export class ListFoldersCompressedGqlGateway implements IListFoldersGateway { - private client: ApolloClient; - - constructor(client: ApolloClient) { - this.client = client; - } - - async execute(params: ListFoldersGatewayParams) { - const { data: response } = await this.client.query< - ListFoldersCompressedResponse, - ListFoldersCompressedQueryVariables - >({ - query: LIST_FOLDERS_COMPRESSED, - variables: { - ...params, - limit: 100000 - }, - fetchPolicy: "network-only" - }); - - if (!response) { - throw new Error("Network error while listing folders."); - } - - const { data, error } = response.aco.listFoldersCompressed; - - if (!data) { - throw new Error(error?.message || "Could not fetch folders"); - } - - const value = pako.inflate(Buffer.from(data.value, "base64"), { to: "string" }); - const folders = JSON.parse(value); - - return [this.getRootFolder(), ...(folders || [])]; - } - - private getRootFolder(): FolderItem { - return { - id: ROOT_FOLDER, - title: "Home", - permissions: [], - parentId: "0", - path: ROOT_FOLDER, - slug: "", - createdOn: "", - createdBy: { - id: "", - displayName: "", - type: "" - }, - hasNonInheritedPermissions: false, - canManagePermissions: true, - canManageStructure: true, - canManageContent: true, - savedOn: "", - savedBy: { - id: "", - displayName: "", - type: "" - }, - modifiedOn: null, - modifiedBy: null, - type: "$ROOT", - extensions: {} - }; - } -} diff --git a/packages/app-aco/src/features/folders/listFolders/ListFoldersGqlGateway.ts b/packages/app-aco/src/features/folders/listFolders/ListFoldersGqlGateway.ts index d26ffaa7268..7ed8ba9df67 100644 --- a/packages/app-aco/src/features/folders/listFolders/ListFoldersGqlGateway.ts +++ b/packages/app-aco/src/features/folders/listFolders/ListFoldersGqlGateway.ts @@ -1,13 +1,15 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { IListFoldersGateway, ListFoldersGatewayParams } from "./IListFoldersGateway.js"; -import type { AcoError, FolderItem } from "~/types.js"; -import { ROOT_FOLDER } from "~/constants.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { RootFolder } from "~/domain/folder/RootFolder.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { ListFoldersGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; export interface ListFoldersResponse { aco: { listFolders: { - data: FolderItem[] | null; + data: FolderDto[] | null; error: AcoError | null; }; }; @@ -35,23 +37,22 @@ export const LIST_FOLDERS = (FOLDER_FIELDS: string) => gql` } `; -export class ListFoldersGqlGateway implements IListFoldersGateway { - private client: ApolloClient; - private modelFields: string; +class ListFoldersGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } + async execute(type: string) { + const fields = await this.folderModelProvider.getGraphQLSelection(); - async execute(params: ListFoldersGatewayParams) { const { data: response } = await this.client.query< ListFoldersResponse, ListFoldersQueryVariables >({ - query: LIST_FOLDERS(this.modelFields), + query: LIST_FOLDERS(fields), variables: { - ...params, + type, limit: 10000 }, fetchPolicy: "network-only" @@ -67,37 +68,11 @@ export class ListFoldersGqlGateway implements IListFoldersGateway { throw new Error(error?.message || "Could not fetch folders"); } - return [this.getRootFolder(), ...(data || [])]; - } - - private getRootFolder(): FolderItem { - return { - id: ROOT_FOLDER, - title: "Home", - permissions: [], - parentId: "0", - path: ROOT_FOLDER, - slug: "", - createdOn: "", - createdBy: { - id: "", - displayName: "", - type: "" - }, - hasNonInheritedPermissions: false, - canManagePermissions: true, - canManageStructure: true, - canManageContent: true, - savedOn: "", - savedBy: { - id: "", - displayName: "", - type: "" - }, - modifiedOn: null, - modifiedBy: null, - type: "$ROOT", - extensions: {} - }; + return [RootFolder.create(), ...(data || [])]; } } + +export const ListFoldersGqlGateway = GatewayAbstraction.createImplementation({ + implementation: ListFoldersGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/listFolders/ListFoldersRepository.ts b/packages/app-aco/src/features/folders/listFolders/ListFoldersRepository.ts index 99692fad3be..ac387ca5b0d 100644 --- a/packages/app-aco/src/features/folders/listFolders/ListFoldersRepository.ts +++ b/packages/app-aco/src/features/folders/listFolders/ListFoldersRepository.ts @@ -1,22 +1,25 @@ -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; -import type { IListFoldersGateway } from "./IListFoldersGateway.js"; -import type { IListFoldersRepository } from "./IListFoldersRepository.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { + ListFoldersRepository as RepositoryAbstraction, + ListFoldersGateway +} from "./abstractions.js"; +import { FoldersCache, FoldersContext } from "~/features/folders/abstractions.js"; -export class ListFoldersRepository implements IListFoldersRepository { - private cache: ListCache; - private gateway: IListFoldersGateway; - private type: string; - - constructor(cache: ListCache, gateway: IListFoldersGateway, type: string) { - this.cache = cache; - this.gateway = gateway; - this.type = type; - } +class ListFoldersRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private foldersContext: FoldersContext.Interface, + private cache: FoldersCache.Interface, + private gateway: ListFoldersGateway.Interface + ) {} async execute() { - const items = await this.gateway.execute({ type: this.type }); + const items = await this.gateway.execute(this.foldersContext.type); this.cache.clear(); this.cache.addItems(items.map(item => Folder.create(item))); } } + +export const ListFoldersRepository = RepositoryAbstraction.createImplementation({ + implementation: ListFoldersRepositoryImpl, + dependencies: [FoldersContext, FoldersCache, ListFoldersGateway] +}); diff --git a/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCase.ts b/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCase.ts index 44ecf06a61c..a3bfc3641cd 100644 --- a/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCase.ts +++ b/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCase.ts @@ -1,14 +1,14 @@ -import type { IListFoldersUseCase } from "./IListFoldersUseCase.js"; -import type { IListFoldersRepository } from "./IListFoldersRepository.js"; +import { ListFoldersUseCase as UseCaseAbstraction, ListFoldersRepository } from "./abstractions.js"; -export class ListFoldersUseCase implements IListFoldersUseCase { - private repository: IListFoldersRepository; - - constructor(repository: IListFoldersRepository) { - this.repository = repository; - } +class ListFoldersUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: ListFoldersRepository.Interface) {} async execute() { await this.repository.execute(); } } + +export const ListFoldersUseCase = UseCaseAbstraction.createImplementation({ + implementation: ListFoldersUseCaseImpl, + dependencies: [ListFoldersRepository] +}); diff --git a/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCaseWithLoading.ts index 993b6dbc90f..2311a0a4a57 100644 --- a/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCaseWithLoading.ts +++ b/packages/app-aco/src/features/folders/listFolders/ListFoldersUseCaseWithLoading.ts @@ -1,17 +1,19 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { ListFoldersUseCase as UseCaseAbstraction } from "./abstractions.js"; import { LoadingActionsEnum } from "~/types.js"; -import type { IListFoldersUseCase } from "./IListFoldersUseCase.js"; -export class ListFoldersUseCaseWithLoading implements IListFoldersUseCase { - private loadingRepository: ILoadingRepository; - private useCase: IListFoldersUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: IListFoldersUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } +class ListFoldersUseCaseWithLoadingImpl implements UseCaseAbstraction.Interface { + constructor( + private loadingRepository: FoldersLoadingRepository.Interface, + private decoratee: UseCaseAbstraction.Interface + ) {} async execute() { - await this.loadingRepository.runCallBack(this.useCase.execute(), LoadingActionsEnum.list); + await this.loadingRepository.runCallBack(this.decoratee.execute(), LoadingActionsEnum.list); } } + +export const ListFoldersUseCaseWithLoading = UseCaseAbstraction.createDecorator({ + decorator: ListFoldersUseCaseWithLoadingImpl, + dependencies: [FoldersLoadingRepository] +}); diff --git a/packages/app-aco/src/features/folders/listFolders/abstractions.ts b/packages/app-aco/src/features/folders/listFolders/abstractions.ts new file mode 100644 index 00000000000..d40523cb696 --- /dev/null +++ b/packages/app-aco/src/features/folders/listFolders/abstractions.ts @@ -0,0 +1,33 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; + +export interface IListFoldersUseCase { + execute: () => Promise; +} + +export interface IListFoldersRepository { + execute: () => Promise; +} + +export interface IListFoldersGateway { + execute: (type: string) => Promise; +} + +export const ListFoldersUseCase = createAbstraction("ListFoldersUseCase"); + +export namespace ListFoldersUseCase { + export type Interface = IListFoldersUseCase; +} + +export const ListFoldersRepository = + createAbstraction("ListFoldersRepository"); + +export namespace ListFoldersRepository { + export type Interface = IListFoldersRepository; +} + +export const ListFoldersGateway = createAbstraction("ListFoldersGateway"); + +export namespace ListFoldersGateway { + export type Interface = IListFoldersGateway; +} diff --git a/packages/app-aco/src/features/folders/listFolders/feature.ts b/packages/app-aco/src/features/folders/listFolders/feature.ts new file mode 100644 index 00000000000..90644d10285 --- /dev/null +++ b/packages/app-aco/src/features/folders/listFolders/feature.ts @@ -0,0 +1,30 @@ +import { createFeature } from "@webiny/feature/admin"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { ListFoldersUseCase as UseCase } from "./abstractions.js"; +import { ListFoldersUseCase } from "./ListFoldersUseCase.js"; +import { ListFoldersRepository } from "./ListFoldersRepository.js"; +import { ListFoldersGqlGateway } from "./ListFoldersGqlGateway.js"; +import { ListFoldersUseCaseWithLoading } from "./ListFoldersUseCaseWithLoading.js"; + +export const ListFoldersFeature = createFeature({ + name: "ListFolders", + register(container) { + // Register base use case + container.register(ListFoldersUseCase); + + // Register repository + container.register(ListFoldersRepository).inSingletonScope(); + + // Register gateway + container.register(ListFoldersGqlGateway); + + // Register decorator + container.registerDecorator(ListFoldersUseCaseWithLoading); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase), + loading: container.resolve(FoldersLoadingRepository) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/listFolders/index.ts b/packages/app-aco/src/features/folders/listFolders/index.ts index 37ccb09edfe..fb5838f8abc 100644 --- a/packages/app-aco/src/features/folders/listFolders/index.ts +++ b/packages/app-aco/src/features/folders/listFolders/index.ts @@ -1,2 +1,2 @@ export * from "./useListFolders.js"; -export * from "./FolderDto.js"; +export * from "./abstractions.js"; diff --git a/packages/app-aco/src/features/folders/listFolders/useListFolders.ts b/packages/app-aco/src/features/folders/listFolders/useListFolders.ts index 3d90a51c816..94a1135506b 100644 --- a/packages/app-aco/src/features/folders/listFolders/useListFolders.ts +++ b/packages/app-aco/src/features/folders/listFolders/useListFolders.ts @@ -1,20 +1,19 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { autorun } from "mobx"; -import { useApolloClient } from "@apollo/react-hooks"; -import { ListFoldersGqlGateway } from "./ListFoldersGqlGateway.js"; -import { ListFolders } from "./ListFolders.js"; -import { FolderDtoMapper } from "./FolderDto.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; -import type { FolderItem } from "~/types.js"; +import { useFeature, useContainer } from "@webiny/app"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderDtoMapper } from "~/domain/folder/FolderDtoMapper.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { ListFoldersFeature } from "./feature.js"; export const useListFolders = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new ListFoldersGqlGateway(client, fields); + const container = useContainer(); + const { useCase, loading } = useFeature(ListFoldersFeature); + + const foldersCache = container.resolve(FoldersCache); const [vm, setVm] = useState<{ - folders: FolderItem[]; + folders: FolderDto[]; loading: Record; }>({ folders: [], @@ -23,14 +22,6 @@ export const useListFolders = () => { } }); - const { - useCase, - folders: foldersCache, - loading - } = useMemo(() => { - return ListFolders.getInstance(type, gateway); - }, [type, gateway]); - const listFolders = useCallback(() => { return useCase.execute(); }, [useCase]); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderDto.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderDto.ts deleted file mode 100644 index 2bd42b46c05..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderDto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; -import type { Folder } from "../Folder.js"; -import { ROOT_FOLDER } from "~/constants.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - type: string; - parentId: string; - path: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity; - modifiedOn: string; - extensions: Record; -} - -export class FolderDtoMapper { - static toDTO(folder: Folder): FolderDto { - return { - id: folder.id, - title: folder.title, - canManageContent: folder.canManageContent ?? false, - canManagePermissions: folder.canManagePermissions ?? false, - canManageStructure: folder.canManageStructure ?? false, - createdBy: this.createIdentity(folder.createdBy), - createdOn: folder.createdOn ?? "", - hasNonInheritedPermissions: folder.hasNonInheritedPermissions ?? false, - modifiedBy: this.createIdentity(folder.modifiedBy), - modifiedOn: folder.modifiedOn ?? "", - parentId: folder.parentId ?? ROOT_FOLDER, - path: folder.path, - permissions: folder.permissions ?? [], - savedBy: this.createIdentity(folder.savedBy), - savedOn: folder.savedOn ?? "", - slug: folder.slug, - type: folder.type, - extensions: folder.extensions ?? {} - }; - } - - private static createIdentity(identity?: CmsIdentity | null): CmsIdentity { - return { - id: identity?.id || "", - displayName: identity?.displayName || "", - type: identity?.type || "" - }; - } -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderGqlDto.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderGqlDto.ts deleted file mode 100644 index f802d031281..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/FolderGqlDto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; - -export interface FolderGqlDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsGateway.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsGateway.ts deleted file mode 100644 index 0209d1dde8a..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsGateway.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { FolderGqlDto } from "./FolderGqlDto.js"; - -export interface ListFoldersByParentIdsGatewayParams { - type: string; - parentIds: string[]; -} - -export interface IListFoldersByParentIdsGateway { - execute: (params: ListFoldersByParentIdsGatewayParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsRepository.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsRepository.ts deleted file mode 100644 index 612fdf0b820..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsRepository.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ListFoldersByParentIdsRepositoryParams { - parentIds: string[]; -} - -export interface IListFoldersByParentIdsRepository { - execute: (params: ListFoldersByParentIdsRepositoryParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsUseCase.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsUseCase.ts deleted file mode 100644 index 9d9ae8ead1f..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/IListFoldersByParentIdsUseCase.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ListFoldersByParentIdsUseCaseParams { - parentIds?: string[]; -} - -export interface IListFoldersByParentIdsUseCase { - execute: (params: ListFoldersByParentIdsUseCaseParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.test.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.test.ts index 1ce6f052547..f8895d891ca 100644 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.test.ts +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.test.ts @@ -1,33 +1,56 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; -import { ListFoldersByParentIds } from "./ListFoldersByParentIds.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { loadedFolderCacheFactory } from "../cache/LoadedFoldersCacheFactory.js"; +import { Container } from "@webiny/di"; +import { LoadingRepository } from "@webiny/app-utils"; import { ROOT_FOLDER } from "~/constants.js"; -import type { IListFoldersByParentIdsGateway } from "~/features/folders/listFoldersByParentIds/IListFoldersByParentIdsGateway.js"; -import { FolderGqlDto } from "~/features/folders/listFolders/FolderGqlDto.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { LoadedCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { ListFoldersByParentIdsFeature } from "./feature.js"; +import { ListFoldersByParentIdsGateway } from "./abstractions.js"; +import { ListFoldersByParentIdsUseCase } from "./abstractions.js"; +import { LoadedFoldersCache } from "~/features/folders/abstractions.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; describe("ListFoldersByParentIds", () => { const type = "abc"; - const foldersCache = folderCacheFactory.getCache(type); - const loadedFoldersCache = loadedFolderCacheFactory.getCache(type); + function setupTest(gateway: ListFoldersByParentIdsGateway.Interface) { + const container = new Container(); + const foldersCache = new ListCache(); + const loadedFoldersCache = new LoadedCache(); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + container.registerInstance(LoadedFoldersCache, loadedFoldersCache); + container.registerInstance(FoldersLoadingRepository, new LoadingRepository()); + + ListFoldersByParentIdsFeature.register(container); + container.registerInstance(ListFoldersByParentIdsGateway, gateway); + + return { + container, + foldersCache, + useCase: container.resolve(ListFoldersByParentIdsUseCase) + }; + } beforeEach(() => { - foldersCache.clear(); - loadedFoldersCache.clear(); vi.resetAllMocks(); }); - class ListFoldersByParentIdsMockGateway implements IListFoldersByParentIdsGateway { - mockResponse: FolderGqlDto[]; + class ListFoldersByParentIdsMockGateway implements ListFoldersByParentIdsGateway.Interface { + mockResponse: FolderDto[]; // Had to use `any` as the mock folders passed in the tests below are also partial objects. constructor(mockResponse: any) { - this.mockResponse = mockResponse as FolderGqlDto[]; + this.mockResponse = mockResponse as FolderDto[]; } setMockResponse(mockResponse: any) { - this.mockResponse = mockResponse as FolderGqlDto[]; + this.mockResponse = mockResponse as FolderDto[]; } async execute() { @@ -62,19 +85,19 @@ describe("ListFoldersByParentIds", () => { const spy = vi.spyOn(gateway, "execute"); - const listByParentIdFolders = ListFoldersByParentIds.getInstance(type, gateway); + const { useCase, foldersCache } = setupTest(gateway); - expect(foldersCache.hasItems()).toBeFalse(); - await listByParentIdFolders.useCase.execute({ parentIds: undefined }); + expect(foldersCache.hasItems()).toBe(false); + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith({ parentIds: [ROOT_FOLDER], type }); + expect(spy).toHaveBeenCalledWith(type, [ROOT_FOLDER]); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); expect(foldersCache.count()).toEqual(3); // This call should be idempotent: the number of elements in cache should not change - await listByParentIdFolders.useCase.execute({ parentIds: undefined }); + await useCase.execute(); expect(foldersCache.count()).toEqual(3); }); @@ -105,15 +128,15 @@ describe("ListFoldersByParentIds", () => { const spy = vi.spyOn(gateway, "execute"); - const listByParentIdFolders = ListFoldersByParentIds.getInstance(type, gateway); + const { useCase, foldersCache } = setupTest(gateway); - expect(foldersCache.hasItems()).toBeFalse(); - await listByParentIdFolders.useCase.execute({ parentIds: ["folder-0"] }); + expect(foldersCache.hasItems()).toBe(false); + await useCase.execute(["folder-0"]); expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith({ parentIds: ["folder-0"], type }); + expect(spy).toHaveBeenCalledWith(type, ["folder-0"]); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); expect(foldersCache.count()).toEqual(3); // The number of folders in cache should increase, since we are changing the parentIds. @@ -141,7 +164,7 @@ describe("ListFoldersByParentIds", () => { } ]); - await listByParentIdFolders.useCase.execute({ parentIds: ["folder-1"] }); + await useCase.execute(["folder-1"]); expect(foldersCache.count()).toEqual(6); }); @@ -165,10 +188,10 @@ describe("ListFoldersByParentIds", () => { const spy = vi.spyOn(gateway, "execute"); - const listByParentIdFolders = ListFoldersByParentIds.getInstance(type, gateway); + const { useCase } = setupTest(gateway); // Execute the useCase 3 times and check the gateway is invoked only when needed - await listByParentIdFolders.useCase.execute({ parentIds: ["folder-0", "folder-1"] }); + await useCase.execute(["folder-0", "folder-1"]); gateway.setMockResponse([ { @@ -180,23 +203,18 @@ describe("ListFoldersByParentIds", () => { } ]); - await listByParentIdFolders.useCase.execute({ - parentIds: ["folder-0", "folder-1", "folder-2"] - }); - await listByParentIdFolders.useCase.execute({ - parentIds: ["folder-0", "folder-1", "folder-2"] - }); - - expect(spy).toHaveBeenNthCalledWith(1, { - parentIds: ["folder-0", "folder-1"], - type - }); - expect(spy).toHaveBeenNthCalledWith(2, { parentIds: ["folder-2"], type }); + await useCase.execute(["folder-0", "folder-1", "folder-2"]); + await useCase.execute(["folder-0", "folder-1", "folder-2"]); + + expect(spy).toHaveBeenNthCalledWith(1, type, ["folder-0", "folder-1"]); + expect(spy).toHaveBeenNthCalledWith(2, type, ["folder-2"]); expect(gateway.execute).not.toHaveBeenCalledTimes(3); }); it("should return empty array if no folders are found", async () => { - class ListFoldersByParentIdsEmptyMockGateway implements IListFoldersByParentIdsGateway { + class ListFoldersByParentIdsEmptyMockGateway + implements ListFoldersByParentIdsGateway.Interface + { async execute() { return []; } @@ -205,22 +223,24 @@ describe("ListFoldersByParentIds", () => { const gateway = new ListFoldersByParentIdsEmptyMockGateway(); const spy = vi.spyOn(gateway, "execute"); - const listByParentIdFolders = ListFoldersByParentIds.getInstance(type, gateway); + const { useCase, foldersCache } = setupTest(gateway); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await listByParentIdFolders.useCase.execute({}); + await useCase.execute(); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); const items = foldersCache.getItems(); expect(items.length).toEqual(0); }); it("should handle gateway errors gracefully", async () => { - class ListFoldersByParentIdsErrorMockGateway implements IListFoldersByParentIdsGateway { - async execute(): Promise { + class ListFoldersByParentIdsErrorMockGateway + implements ListFoldersByParentIdsGateway.Interface + { + async execute(): Promise { throw new Error("Gateway error"); } } @@ -228,88 +248,13 @@ describe("ListFoldersByParentIds", () => { const errorGateway = new ListFoldersByParentIdsErrorMockGateway(); const spy = vi.spyOn(errorGateway, "execute"); - const listByParentIdFolders = ListFoldersByParentIds.getInstance(type, errorGateway); + const { useCase, foldersCache } = setupTest(errorGateway); - expect(foldersCache.hasItems()).toBeFalse(); + expect(foldersCache.hasItems()).toBe(false); - await expect(listByParentIdFolders.useCase.execute({})).rejects.toThrow("Gateway error"); + await expect(useCase.execute()).rejects.toThrow("Gateway error"); expect(spy).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeFalse(); - }); - - it("should clear cache when type changes", async () => { - const gatewayAbc = new ListFoldersByParentIdsMockGateway([ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-1", - parentId: null, - type - }, - { - id: "folder-3", - title: "Folder 3", - slug: "folder-3", - parentId: null, - type - } - ]); - - const spyA = vi.spyOn(gatewayAbc, "execute"); - - const newType = "xyz"; - - const gatewayXyz = new ListFoldersByParentIdsMockGateway([ - { - id: "folder-1", - title: "Folder 1", - slug: "folder-1", - parentId: null, - type: newType - }, - { - id: "folder-2", - title: "Folder 2", - slug: "folder-1", - parentId: null, - type: newType - }, - { - id: "folder-3", - title: "Folder 3", - slug: "folder-3", - parentId: null, - type: newType - } - ]); - - const spyX = vi.spyOn(gatewayXyz, "execute"); - - const listFoldersByParentId = ListFoldersByParentIds.getInstance(type, gatewayAbc); - - expect(foldersCache.hasItems()).toBeFalse(); - - await listFoldersByParentId.useCase.execute({}); - - expect(spyA).toHaveBeenCalledTimes(1); - expect(foldersCache.hasItems()).toBeTrue(); - - const newFoldersCache = folderCacheFactory.getCache(newType); - const newListFoldersByParentId = ListFoldersByParentIds.getInstance(newType, gatewayXyz); - - expect(newFoldersCache.hasItems()).toBeFalse(); - - await newListFoldersByParentId.useCase.execute({}); - - expect(spyX).toHaveBeenCalledTimes(1); - expect(newFoldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(false); }); }); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.ts deleted file mode 100644 index 1831bc3d0a5..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIds.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { LoadingRepository } from "@webiny/app-utils"; -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IListFoldersByParentIdsGateway } from "./IListFoldersByParentIdsGateway.js"; -import type { IListFoldersByParentIdsUseCase } from "./IListFoldersByParentIdsUseCase.js"; -import { ListFoldersByParentIdsRepository } from "./ListFoldersByParentIdsRepository.js"; -import { ListFoldersByParentIdsRepositoryWithLoadedCache } from "./ListFoldersByParentIdsRepositoryWithLoadedCache.js"; -import { ListFoldersByParentIdsUseCase } from "./ListFoldersByParentIdsUseCase.js"; -import type { ListCache } from "../cache/index.js"; -import { folderCacheFactory, loadedFolderCacheFactory } from "../cache/index.js"; -import type { Folder } from "../Folder.js"; -import { ListFoldersByParentIdsUseCaseWithLoading } from "~/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCaseWithLoading.js"; - -interface IListFoldersByParentIdsInstance { - useCase: IListFoldersByParentIdsUseCase; - folders: ListCache; - loading: LoadingRepository; -} - -export class ListFoldersByParentIds { - public static getInstance( - type: string, - gateway: IListFoldersByParentIdsGateway - ): IListFoldersByParentIdsInstance { - const foldersCache = folderCacheFactory.getCache(type); - const loadedCache = loadedFolderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - const repository = new ListFoldersByParentIdsRepository(foldersCache, gateway, type); - const repositoryWithLoadedCache = new ListFoldersByParentIdsRepositoryWithLoadedCache( - loadedCache, - repository - ); - const useCase = new ListFoldersByParentIdsUseCase(repositoryWithLoadedCache); - const useCaseWithLoading = new ListFoldersByParentIdsUseCaseWithLoading( - loadingRepository, - loadedCache, - useCase - ); - - return { - useCase: useCaseWithLoading, - folders: foldersCache, - loading: loadingRepository - }; - } -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.ts index 1be9471512d..f770ebcd670 100644 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.ts +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsGqlGateway.ts @@ -1,16 +1,15 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { - IListFoldersByParentIdsGateway, - ListFoldersByParentIdsGatewayParams -} from "./IListFoldersByParentIdsGateway.js"; -import type { AcoError, FolderItem } from "~/types.js"; -import { ROOT_FOLDER } from "~/constants.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { RootFolder } from "~/domain/folder/RootFolder.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { ListFoldersByParentIdsGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; export interface ListFoldersByParentIdsResponse { aco: { listFolders: { - data: FolderItem[] | null; + data: FolderDto[] | null; error: AcoError | null; }; }; @@ -39,23 +38,22 @@ export const LIST_FOLDERS_BY_PARENT_IDS = (FOLDER_FIELDS: string) => gql` } `; -export class ListFoldersByParentIdsGqlGateway implements IListFoldersByParentIdsGateway { - private client: ApolloClient; - private modelFields: string; +class ListFoldersByParentIdsGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } + async execute(type: string, parentIds: string[]) { + const fields = await this.folderModelProvider.getGraphQLSelection(); - async execute({ parentIds, ...params }: ListFoldersByParentIdsGatewayParams) { const { data: response } = await this.client.query< ListFoldersByParentIdsResponse, ListFoldersByParentIdsQueryVariables >({ - query: LIST_FOLDERS_BY_PARENT_IDS(this.modelFields), + query: LIST_FOLDERS_BY_PARENT_IDS(fields), variables: { - ...params, + type, parentIds_in: parentIds, limit: 10000 }, @@ -72,37 +70,11 @@ export class ListFoldersByParentIdsGqlGateway implements IListFoldersByParentIds throw new Error(error?.message || "Could not fetch folders"); } - return [this.getRootFolder(), ...(data || [])]; - } - - private getRootFolder(): FolderItem { - return { - id: ROOT_FOLDER, - title: "Home", - permissions: [], - parentId: "0", - path: ROOT_FOLDER, - slug: "", - createdOn: "", - createdBy: { - id: "", - displayName: "", - type: "" - }, - hasNonInheritedPermissions: false, - canManagePermissions: true, - canManageStructure: true, - canManageContent: true, - savedOn: "", - savedBy: { - id: "", - displayName: "", - type: "" - }, - modifiedOn: null, - modifiedBy: null, - type: "$ROOT", - extensions: {} - }; + return [RootFolder.create(), ...(data || [])]; } } + +export const ListFoldersByParentIdsGqlGateway = GatewayAbstraction.createImplementation({ + implementation: ListFoldersByParentIdsGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepository.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepository.ts index db9c51845b4..7b9c77d10ed 100644 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepository.ts +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepository.ts @@ -1,24 +1,24 @@ -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; -import type { IListFoldersByParentIdsGateway } from "./IListFoldersByParentIdsGateway.js"; -import type { - IListFoldersByParentIdsRepository, - ListFoldersByParentIdsRepositoryParams -} from "./IListFoldersByParentIdsRepository.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { + ListFoldersByParentIdsRepository as RepositoryAbstraction, + ListFoldersByParentIdsGateway +} from "./abstractions.js"; +import { FoldersCache, FoldersContext } from "~/features/folders/abstractions.js"; -export class ListFoldersByParentIdsRepository implements IListFoldersByParentIdsRepository { - private cache: ListCache; - private gateway: IListFoldersByParentIdsGateway; - private readonly type: string; +class ListFoldersByParentIdsRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private foldersContext: FoldersContext.Interface, + private cache: FoldersCache.Interface, + private gateway: ListFoldersByParentIdsGateway.Interface + ) {} - constructor(cache: ListCache, gateway: IListFoldersByParentIdsGateway, type: string) { - this.cache = cache; - this.gateway = gateway; - this.type = type; - } - - async execute(params: ListFoldersByParentIdsRepositoryParams) { - const items = await this.gateway.execute({ type: this.type, parentIds: params.parentIds }); + async execute(parentIds: string[]) { + const items = await this.gateway.execute(this.foldersContext.type, parentIds); this.cache.addItems(items.map(item => Folder.create(item))); } } + +export const ListFoldersByParentIdsRepository = RepositoryAbstraction.createImplementation({ + implementation: ListFoldersByParentIdsRepositoryImpl, + dependencies: [FoldersContext, FoldersCache, ListFoldersByParentIdsGateway] +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepositoryWithLoadedCache.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepositoryWithLoadedCache.ts deleted file mode 100644 index 1893c34884c..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsRepositoryWithLoadedCache.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { LoadedCache } from "../cache/index.js"; -import type { - IListFoldersByParentIdsRepository, - ListFoldersByParentIdsRepositoryParams -} from "./IListFoldersByParentIdsRepository.js"; - -export class ListFoldersByParentIdsRepositoryWithLoadedCache - implements IListFoldersByParentIdsRepository -{ - private loadedCache: LoadedCache; - private repository: IListFoldersByParentIdsRepository; - - constructor(loadedCache: LoadedCache, repository: IListFoldersByParentIdsRepository) { - this.loadedCache = loadedCache; - this.repository = repository; - } - - async execute(params: ListFoldersByParentIdsRepositoryParams) { - if (this.loadedCache.count() === 0) { - await this.repository.execute(params); - this.loadedCache.addItems(params.parentIds); - return; - } - - const missingParentIds = params.parentIds.filter( - parentId => !this.loadedCache.getItems().includes(parentId) - ); - - if (missingParentIds.length === 0) { - return; - } - - this.loadedCache.addItems(missingParentIds); - - await this.repository.execute({ - parentIds: missingParentIds - }); - } -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCase.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCase.ts index 7bc80377d8f..6ea931d7ad2 100644 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCase.ts +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCase.ts @@ -1,21 +1,14 @@ -import type { IListFoldersByParentIdsRepository } from "./IListFoldersByParentIdsRepository.js"; -import type { - IListFoldersByParentIdsUseCase, - ListFoldersByParentIdsUseCaseParams -} from "./IListFoldersByParentIdsUseCase.js"; +import { + ListFoldersByParentIdsUseCase as UseCaseAbstraction, + ListFoldersByParentIdsRepository +} from "./abstractions.js"; import { ROOT_FOLDER } from "~/constants.js"; -export class ListFoldersByParentIdsUseCase implements IListFoldersByParentIdsUseCase { - private repository: IListFoldersByParentIdsRepository; +class ListFoldersByParentIdsUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: ListFoldersByParentIdsRepository.Interface) {} - constructor(repository: IListFoldersByParentIdsRepository) { - this.repository = repository; - } - - async execute(params: ListFoldersByParentIdsUseCaseParams) { - await this.repository.execute({ - parentIds: this.getParentIds(params.parentIds) - }); + async execute(parentIds?: string[]) { + await this.repository.execute(this.getParentIds(parentIds)); } private getParentIds(parentIds?: string[]) { @@ -26,3 +19,8 @@ export class ListFoldersByParentIdsUseCase implements IListFoldersByParentIdsUse return parentIds; } } + +export const ListFoldersByParentIdsUseCase = UseCaseAbstraction.createImplementation({ + implementation: ListFoldersByParentIdsUseCaseImpl, + dependencies: [ListFoldersByParentIdsRepository] +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCaseWithLoading.ts deleted file mode 100644 index 0fb24c7007e..00000000000 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/ListFoldersByParentIdsUseCaseWithLoading.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; -import type { - IListFoldersByParentIdsUseCase, - ListFoldersByParentIdsUseCaseParams -} from "./IListFoldersByParentIdsUseCase.js"; -import type { LoadedCache } from "~/features/index.js"; -import { LoadingActionsEnum } from "~/types.js"; - -export class ListFoldersByParentIdsUseCaseWithLoading implements IListFoldersByParentIdsUseCase { - private loadingRepository: ILoadingRepository; - private loadedCache: LoadedCache; - private useCase: IListFoldersByParentIdsUseCase; - - constructor( - loadingRepository: ILoadingRepository, - loadedCache: LoadedCache, - useCase: IListFoldersByParentIdsUseCase - ) { - this.loadingRepository = loadingRepository; - this.loadedCache = loadedCache; - this.useCase = useCase; - } - - async execute(params: ListFoldersByParentIdsUseCaseParams) { - let action: string = LoadingActionsEnum.init; - - if (params.parentIds?.length) { - action = params.parentIds - .filter(parentId => !this.loadedCache.getItems().includes(parentId)) - .join(":"); - } - - if (action) { - await this.loadingRepository.runCallBack(this.useCase.execute(params), action); - } else { - await this.useCase.execute(params); - } - } -} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/abstractions.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/abstractions.ts new file mode 100644 index 00000000000..a1271684916 --- /dev/null +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/abstractions.ts @@ -0,0 +1,37 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; + +export interface IListFoldersByParentIdsUseCase { + execute: (parentIds?: string[]) => Promise; +} + +export interface IListFoldersByParentIdsRepository { + execute: (parentIds: string[]) => Promise; +} + +export interface IListFoldersByParentIdsGateway { + execute: (type: string, parentIds: string[]) => Promise; +} + +export const ListFoldersByParentIdsUseCase = createAbstraction( + "ListFoldersByParentIdsUseCase" +); + +export namespace ListFoldersByParentIdsUseCase { + export type Interface = IListFoldersByParentIdsUseCase; +} + +export const ListFoldersByParentIdsRepository = + createAbstraction("ListFoldersByParentIdsRepository"); + +export namespace ListFoldersByParentIdsRepository { + export type Interface = IListFoldersByParentIdsRepository; +} + +export const ListFoldersByParentIdsGateway = createAbstraction( + "ListFoldersByParentIdsGateway" +); + +export namespace ListFoldersByParentIdsGateway { + export type Interface = IListFoldersByParentIdsGateway; +} diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/RepositoryWithLoadedCache.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/RepositoryWithLoadedCache.ts new file mode 100644 index 00000000000..90c29684438 --- /dev/null +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/RepositoryWithLoadedCache.ts @@ -0,0 +1,38 @@ +import { ListFoldersByParentIdsRepository as RepositoryAbstraction } from "../abstractions.js"; +import { LoadedFoldersCache } from "~/features/folders/abstractions.js"; + +class ListFoldersByParentIdsRepositoryWithLoadedCacheImpl + implements RepositoryAbstraction.Interface +{ + constructor( + private loadedCache: LoadedFoldersCache.Interface, + private decoratee: RepositoryAbstraction.Interface + ) {} + + async execute(parentIds: string[]) { + if (this.loadedCache.count() === 0) { + await this.decoratee.execute(parentIds); + this.loadedCache.addItems(parentIds); + return; + } + + // Find folder IDs that are not in the cache + const missingParentIds = parentIds.filter( + parentId => !this.loadedCache.getItems().includes(parentId) + ); + + if (missingParentIds.length === 0) { + // Nothing new to load. + return; + } + + this.loadedCache.addItems(missingParentIds); + + await this.decoratee.execute(missingParentIds); + } +} + +export const RepositoryWithLoadedCache = RepositoryAbstraction.createDecorator({ + decorator: ListFoldersByParentIdsRepositoryWithLoadedCacheImpl, + dependencies: [LoadedFoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/UseCaseWithLoading.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/UseCaseWithLoading.ts new file mode 100644 index 00000000000..0cb820d055c --- /dev/null +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/decorators/UseCaseWithLoading.ts @@ -0,0 +1,32 @@ +import { ListFoldersByParentIdsUseCase as UseCaseAbstraction } from "../abstractions.js"; +import { FoldersLoadingRepository, LoadedFoldersCache } from "~/features/folders/abstractions.js"; +import { LoadingActionsEnum } from "~/types.js"; + +class ListFoldersByParentIdsUseCaseWithLoadingImpl implements UseCaseAbstraction.Interface { + constructor( + private loadingRepository: FoldersLoadingRepository.Interface, + private loadedCache: LoadedFoldersCache.Interface, + private decoratee: UseCaseAbstraction.Interface + ) {} + + async execute(parentIds?: string[]) { + let action: string = LoadingActionsEnum.init; + + if (parentIds?.length) { + action = parentIds + .filter(parentId => !this.loadedCache.getItems().includes(parentId)) + .join(":"); + } + + if (action) { + await this.loadingRepository.runCallBack(this.decoratee.execute(parentIds), action); + } else { + await this.decoratee.execute(parentIds); + } + } +} + +export const UseCaseWithLoading = UseCaseAbstraction.createDecorator({ + decorator: ListFoldersByParentIdsUseCaseWithLoadingImpl, + dependencies: [FoldersLoadingRepository, LoadedFoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/feature.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/feature.ts new file mode 100644 index 00000000000..b9db38da403 --- /dev/null +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/feature.ts @@ -0,0 +1,34 @@ +import { createFeature } from "@webiny/feature/admin"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { ListFoldersByParentIdsUseCase as UseCase } from "./abstractions.js"; +import { ListFoldersByParentIdsUseCase } from "./ListFoldersByParentIdsUseCase.js"; +import { ListFoldersByParentIdsRepository } from "./ListFoldersByParentIdsRepository.js"; +import { ListFoldersByParentIdsGqlGateway } from "./ListFoldersByParentIdsGqlGateway.js"; +import { RepositoryWithLoadedCache } from "./decorators/RepositoryWithLoadedCache.js"; +import { UseCaseWithLoading } from "./decorators/UseCaseWithLoading.js"; + +export const ListFoldersByParentIdsFeature = createFeature({ + name: "ListFoldersByParentIds", + register(container) { + // Register base use case + container.register(ListFoldersByParentIdsUseCase); + + // Register base repository + container.register(ListFoldersByParentIdsRepository).inSingletonScope(); + + // Register gateway + container.register(ListFoldersByParentIdsGqlGateway); + + // Register repository decorator + container.registerDecorator(RepositoryWithLoadedCache); + + // Register use case decorator + container.registerDecorator(UseCaseWithLoading); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase), + loading: container.resolve(FoldersLoadingRepository) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/listFoldersByParentIds/useListFoldersByParentIds.ts b/packages/app-aco/src/features/folders/listFoldersByParentIds/useListFoldersByParentIds.ts index 1ea230748cb..207e3ee4971 100644 --- a/packages/app-aco/src/features/folders/listFoldersByParentIds/useListFoldersByParentIds.ts +++ b/packages/app-aco/src/features/folders/listFoldersByParentIds/useListFoldersByParentIds.ts @@ -1,38 +1,29 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { autorun } from "mobx"; -import { useApolloClient } from "@apollo/react-hooks"; -import { ListFoldersByParentIdsGqlGateway } from "./ListFoldersByParentIdsGqlGateway.js"; -import { ListFoldersByParentIds } from "./ListFoldersByParentIds.js"; -import { FolderDtoMapper } from "./FolderDto.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; -import type { FolderItem } from "~/types.js"; +import { useFeature, useContainer } from "@webiny/app"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderDtoMapper } from "~/domain/folder/FolderDtoMapper.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { ListFoldersByParentIdsFeature } from "./feature.js"; import { ROOT_FOLDER } from "~/constants.js"; export const useListFoldersByParentIds = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new ListFoldersByParentIdsGqlGateway(client, fields); + const container = useContainer(); + const { useCase, loading: loadingState } = useFeature(ListFoldersByParentIdsFeature); + + const foldersCache = container.resolve(FoldersCache); const [vm, setVm] = useState<{ - folders: FolderItem[]; + folders: FolderDto[]; loading: string[]; }>({ folders: [], loading: [] }); - const { - useCase, - folders: foldersCache, - loading: loadingState - } = useMemo(() => { - return ListFoldersByParentIds.getInstance(type, gateway); - }, [type, gateway]); - const listFoldersByParentIds = useCallback( (parentIds?: string[]) => { - return useCase.execute({ parentIds }); + return useCase.execute(parentIds); }, [useCase] ); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchy.test.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchy.test.ts new file mode 100644 index 00000000000..60d075e71c9 --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchy.test.ts @@ -0,0 +1,180 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { Container } from "@webiny/di"; +import { ListCache, LoadedCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { + FoldersCache, + FoldersContext, + FoldersLoadingRepository, + LoadedFoldersCache +} from "../abstractions.js"; +import { LoadingRepository } from "@webiny/app-utils"; +import type { LoadFolderHierarchyGatewayResponse } from "./abstractions.js"; +import { LoadFolderHierarchyGateway, LoadFolderHierarchyUseCase } from "./abstractions.js"; +import { LoadFolderHierarchyFeature } from "./feature.js"; + +describe("GetFolderHierarchy", () => { + const type = "abc"; + + function setupTest(gateway: LoadFolderHierarchyGateway.Interface) { + const container = new Container(); + const foldersCache = new ListCache(); + const loadedFoldersCache = new LoadedCache(); + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + container.registerInstance(LoadedFoldersCache, loadedFoldersCache); + container.registerInstance(FoldersLoadingRepository, new LoadingRepository()); + + LoadFolderHierarchyFeature.register(container); + container.registerInstance(LoadFolderHierarchyGateway, gateway); + + return { + container, + foldersCache, + loadedFoldersCache, + useCase: container.resolve(LoadFolderHierarchyUseCase) + }; + } + + beforeEach(() => { + vi.resetAllMocks(); + }); + + class GetFolderHierarchyMockGateway implements LoadFolderHierarchyGateway.Interface { + mockResponse: LoadFolderHierarchyGatewayResponse; + + // Had to use `any` as the mock folders passed in the tests below are also partial objects. + constructor(mockResponse: any) { + this.mockResponse = mockResponse as LoadFolderHierarchyGatewayResponse; + } + + async execute() { + return this.mockResponse; + } + } + + it("should update the list of folders in both `cache` and `loadedCache` when `parents` and `children` are returned by the gateway", async () => { + const gateway = new GetFolderHierarchyMockGateway({ + parents: [ + { + id: "folder-1", + title: "Folder 1", + slug: "folder-1", + parentId: null, + type + }, + { + id: "folder-2", + title: "Folder 2", + slug: "folder-2", + parentId: "folder-1", + type + }, + { + id: "folder-3", + title: "Folder 3", + slug: "folder-3", + parentId: "folder-2", + type + } + ], + siblings: [ + { + id: "folder-4", + title: "Folder 4", + slug: "folder-4", + parentId: "folder-3", + type + }, + { + id: "folder-5", + title: "Folder 5", + slug: "folder-5", + parentId: "folder-3", + type + } + ] + }); + + const spy = vi.spyOn(gateway, "execute"); + + const { useCase, foldersCache, loadedFoldersCache } = setupTest(gateway); + + expect(foldersCache.hasItems()).toBe(false); + expect(loadedFoldersCache.hasItems()).toBe(false); + await useCase.execute("folder-0"); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(type, "folder-0"); + + expect(foldersCache.hasItems()).toBe(true); + expect(foldersCache.count()).toEqual(5); + // We are storing only the parent folders id in the loadedFoldersCache + expect(loadedFoldersCache.count()).toEqual(3); + expect(loadedFoldersCache.getItems()).toEqual(["folder-1", "folder-2", "folder-3"]); + + // This call should be idempotent: the number of elements in cache should not change + await useCase.execute("folder-0"); + expect(foldersCache.count()).toEqual(5); + expect(loadedFoldersCache.count()).toEqual(3); + }); + + it("should only update the list of folders in `cache` when `children` are returned by the gateway", async () => { + const gateway = new GetFolderHierarchyMockGateway({ + parents: [], + siblings: [ + { + id: "folder-1", + title: "Folder 1", + slug: "folder-1", + parentId: null, + type + }, + { + id: "folder-2", + title: "Folder 2", + slug: "folder-2", + parentId: null, + type + } + ] + }); + + const spy = vi.spyOn(gateway, "execute"); + + const { useCase, foldersCache, loadedFoldersCache } = setupTest(gateway); + + expect(foldersCache.hasItems()).toBe(false); + expect(loadedFoldersCache.hasItems()).toBe(false); + await useCase.execute("folder-0"); + + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(type, "folder-0"); + + expect(foldersCache.hasItems()).toBe(true); + expect(foldersCache.count()).toEqual(2); + // We are NOT storing any folder loadedFoldersCache + expect(loadedFoldersCache.hasItems()).toBe(false); + }); + + it("should handle gateway errors gracefully", async () => { + class GetFolderHierarchyErrorMockGateway implements LoadFolderHierarchyGateway.Interface { + async execute(): LoadFolderHierarchyGateway.Return { + throw new Error("Gateway error"); + } + } + + const gateway = new GetFolderHierarchyErrorMockGateway(); + const spy = vi.spyOn(gateway, "execute"); + + const { useCase, foldersCache } = setupTest(gateway); + + expect(foldersCache.hasItems()).toBe(false); + + await expect(useCase.execute("folder-0")).rejects.toThrow("Gateway error"); + + expect(spy).toHaveBeenCalledTimes(1); + expect(foldersCache.hasItems()).toBe(false); + }); +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyGqlGateway.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyGqlGateway.ts new file mode 100644 index 00000000000..58ea73dc6fd --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyGqlGateway.ts @@ -0,0 +1,92 @@ +import gql from "graphql-tag"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { RootFolder } from "~/domain/folder/RootFolder.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import { LoadFolderHierarchyGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; + +interface LoadFolderHierarchyResponseData { + parents: FolderDto[]; + siblings: FolderDto[]; +} + +export interface LoadFolderHierarchyResponse { + aco: { + getFolderHierarchy: { + data: LoadFolderHierarchyResponseData | null; + error: AcoError | null; + }; + }; +} + +export interface LoadFolderHierarchyQueryVariables { + type: string; + id: string; +} + +export const LOAD_FOLDER_HIERARCHY = (FOLDER_FIELDS: string) => gql` + query GetFolderHierarchy($type: String!, $id: ID!) { + aco { + getFolderHierarchy(type: $type, id: $id) { + data { + parents ${FOLDER_FIELDS} + siblings ${FOLDER_FIELDS} + } + error { + code + data + message + } + } + } + } +`; + +class LoadFolderHierarchyGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} + + async execute(type: string, id: string) { + const fields = await this.folderModelProvider.getGraphQLSelection(); + + const { data: response } = await this.client.query< + LoadFolderHierarchyResponse, + LoadFolderHierarchyQueryVariables + >({ + query: LOAD_FOLDER_HIERARCHY(fields), + variables: { + type, + id + }, + fetchPolicy: "network-only" + }); + + if (!response) { + throw new Error( + `Network error while loading folder hierarchy for the provided type/id: ${type}/${id}.` + ); + } + + const { data, error } = response.aco.getFolderHierarchy; + + if (!data) { + throw new Error( + error?.message || + `Could not load folder hierarchy for the provided type/id: ${type}/${id}.` + ); + } + + return { + parents: [RootFolder.create(), ...data.parents], + siblings: data.siblings + }; + } +} + +export const LoadFolderHierarchyGqlGateway = GatewayAbstraction.createImplementation({ + implementation: LoadFolderHierarchyGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyRepository.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyRepository.ts new file mode 100644 index 00000000000..a6014e7627e --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyRepository.ts @@ -0,0 +1,40 @@ +import { Folder } from "~/domain/folder/Folder.js"; +import { + LoadFolderHierarchyRepository as RepositoryAbstraction, + LoadFolderHierarchyGateway +} from "./abstractions.js"; +import { + FoldersCache, + LoadedFoldersCache, + FoldersContext +} from "~/features/folders/abstractions.js"; + +class LoadFolderHierarchyRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private loadedCache: LoadedFoldersCache.Interface, + private gateway: LoadFolderHierarchyGateway.Interface, + private foldersContext: FoldersContext.Interface + ) {} + + async execute(id: string) { + if (this.loadedCache.getItem(item => item === id)) { + return; + } + + const response = await this.gateway.execute(this.foldersContext.type, id); + + const { parents = [], siblings = [] } = response; + + if (parents.length > 0) { + this.loadedCache.addItems(parents.map(parent => parent.id)); + } + + this.cache.addItems([...parents, ...siblings].map(item => Folder.create(item))); + } +} + +export const LoadFolderHierarchyRepository = RepositoryAbstraction.createImplementation({ + implementation: LoadFolderHierarchyRepositoryImpl, + dependencies: [FoldersCache, LoadedFoldersCache, LoadFolderHierarchyGateway, FoldersContext] +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCase.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCase.ts new file mode 100644 index 00000000000..9eda3d0849d --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCase.ts @@ -0,0 +1,17 @@ +import { + LoadFolderHierarchyUseCase as UseCaseAbstraction, + LoadFolderHierarchyRepository +} from "./abstractions.js"; + +class LoadFolderHierarchyUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: LoadFolderHierarchyRepository.Interface) {} + + async execute(id: string) { + await this.repository.execute(id); + } +} + +export const LoadFolderHierarchyUseCase = UseCaseAbstraction.createImplementation({ + implementation: LoadFolderHierarchyUseCaseImpl, + dependencies: [LoadFolderHierarchyRepository] +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCaseWithLoading.ts new file mode 100644 index 00000000000..8e49a72a3d1 --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/LoadFolderHierarchyUseCaseWithLoading.ts @@ -0,0 +1,22 @@ +import { FoldersLoadingRepository } from "../abstractions.js"; +import { LoadFolderHierarchyUseCase as UseCaseAbstraction } from "./abstractions.js"; +import { LoadingActionsEnum } from "~/types.js"; + +class LoadFolderHierarchyUseCaseWithLoadingImpl implements UseCaseAbstraction.Interface { + constructor( + private loadingRepository: FoldersLoadingRepository.Interface, + private decoratee: UseCaseAbstraction.Interface + ) {} + + async execute(id: string) { + await this.loadingRepository.runCallBack( + this.decoratee.execute(id), + LoadingActionsEnum.init + ); + } +} + +export const LoadFolderHierarchyUseCaseWithLoading = UseCaseAbstraction.createDecorator({ + decorator: LoadFolderHierarchyUseCaseWithLoadingImpl, + dependencies: [FoldersLoadingRepository] +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/abstractions.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/abstractions.ts new file mode 100644 index 00000000000..82ede21441b --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/abstractions.ts @@ -0,0 +1,44 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; + +export interface LoadFolderHierarchyGatewayResponse { + parents: FolderDto[]; + siblings: FolderDto[]; +} + +export interface ILoadFolderHierarchyUseCase { + execute: (id: string) => Promise; +} + +export interface ILoadFolderHierarchyRepository { + execute: (id: string) => Promise; +} + +export interface ILoadFolderHierarchyGateway { + execute: (type: string, id: string) => Promise; +} + +export const LoadFolderHierarchyUseCase = createAbstraction( + "LoadFolderHierarchyUseCase" +); + +export namespace LoadFolderHierarchyUseCase { + export type Interface = ILoadFolderHierarchyUseCase; +} + +export const LoadFolderHierarchyRepository = createAbstraction( + "LoadFolderHierarchyRepository" +); + +export namespace LoadFolderHierarchyRepository { + export type Interface = ILoadFolderHierarchyRepository; +} + +export const LoadFolderHierarchyGateway = createAbstraction( + "LoadFolderHierarchyGateway" +); + +export namespace LoadFolderHierarchyGateway { + export type Interface = ILoadFolderHierarchyGateway; + export type Return = Promise; +} diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/feature.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/feature.ts new file mode 100644 index 00000000000..f1372021a8b --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/feature.ts @@ -0,0 +1,30 @@ +import { createFeature } from "@webiny/feature/admin"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { LoadFolderHierarchyUseCase as UseCase } from "./abstractions.js"; +import { LoadFolderHierarchyUseCase } from "./LoadFolderHierarchyUseCase.js"; +import { LoadFolderHierarchyRepository } from "./LoadFolderHierarchyRepository.js"; +import { LoadFolderHierarchyGqlGateway } from "./LoadFolderHierarchyGqlGateway.js"; +import { LoadFolderHierarchyUseCaseWithLoading } from "./LoadFolderHierarchyUseCaseWithLoading.js"; + +export const LoadFolderHierarchyFeature = createFeature({ + name: "LoadFolderHierarchy", + register(container) { + // Register base use case + container.register(LoadFolderHierarchyUseCase); + + // Register repository + container.register(LoadFolderHierarchyRepository).inSingletonScope(); + + // Register gateway + container.register(LoadFolderHierarchyGqlGateway); + + // Register decorator + container.registerDecorator(LoadFolderHierarchyUseCaseWithLoading); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase), + loading: container.resolve(FoldersLoadingRepository) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/index.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/index.ts new file mode 100644 index 00000000000..fbbb22aa71c --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/index.ts @@ -0,0 +1 @@ +export * from "./useLoadFolderHierarchy.js"; diff --git a/packages/app-aco/src/features/folders/loadFolderHierarchy/useLoadFolderHierarchy.ts b/packages/app-aco/src/features/folders/loadFolderHierarchy/useLoadFolderHierarchy.ts new file mode 100644 index 00000000000..94929283529 --- /dev/null +++ b/packages/app-aco/src/features/folders/loadFolderHierarchy/useLoadFolderHierarchy.ts @@ -0,0 +1,65 @@ +import { useContainer } from "@webiny/app"; +import { useCallback, useEffect, useState } from "react"; +import { autorun } from "mobx"; +import { useFeature } from "@webiny/app"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderDtoMapper } from "~/domain/folder/FolderDtoMapper.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { LoadingActionsEnum } from "~/types.js"; +import { LoadFolderHierarchyFeature } from "./feature.js"; + +export const useLoadFolderHierarchy = () => { + const container = useContainer(); + const { useCase, loading } = useFeature(LoadFolderHierarchyFeature); + + const foldersCache = container.resolve(FoldersCache); + + const [vm, setVm] = useState<{ + folders: FolderDto[]; + loading: Record; + }>({ + folders: [], + loading: { + INIT: true + } + }); + + const loadFolderHierarchy = useCallback( + (id: string) => { + return useCase.execute(id); + }, + [useCase] + ); + + const getIsFolderLoading = useCallback((action = LoadingActionsEnum.init) => { + return loading.isLoading(action); + }, []); + + useEffect(() => { + return autorun(() => { + const folders = foldersCache.getItems().map(folder => FolderDtoMapper.toDTO(folder)); + + setVm(vm => ({ + ...vm, + folders + })); + }); + }, [foldersCache]); + + useEffect(() => { + return autorun(() => { + const loadingState = loading.get(); + + setVm(vm => ({ + ...vm, + loading: loadingState + })); + }); + }, [loading]); + + return { + ...vm, + getIsFolderLoading, + loadFolderHierarchy + }; +}; diff --git a/packages/app-aco/src/features/folders/updateFolder/FolderDto.ts b/packages/app-aco/src/features/folders/updateFolder/FolderDto.ts deleted file mode 100644 index 688fb01a75c..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/FolderDto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface FolderDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - parentId: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/updateFolder/FolderGqlDto.ts b/packages/app-aco/src/features/folders/updateFolder/FolderGqlDto.ts deleted file mode 100644 index f802d031281..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/FolderGqlDto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { CmsIdentity, FolderPermission } from "~/types.js"; - -export interface FolderGqlDto { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; - extensions: Record; -} diff --git a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderGateway.ts b/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderGateway.ts deleted file mode 100644 index b5d7578e453..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderGateway.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { FolderDto } from "./FolderDto.js"; -import type { FolderGqlDto } from "./FolderGqlDto.js"; - -export interface IUpdateFolderGateway { - execute: (folder: FolderDto) => Promise; -} diff --git a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderRepository.ts b/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderRepository.ts deleted file mode 100644 index b8fdaefc745..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderRepository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Folder } from "../Folder.js"; - -export interface IUpdateFolderRepository { - execute: (folder: Folder) => Promise; -} diff --git a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderUseCase.ts b/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderUseCase.ts deleted file mode 100644 index 3466f7ad2e9..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/IUpdateFolderUseCase.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { FolderPermission } from "~/types.js"; - -export interface UpdateFolderParams { - id: string; - title: string; - slug: string; - type: string; - parentId: string | null; - permissions: FolderPermission[]; - extensions?: Record; -} - -export interface IUpdateFolderUseCase { - execute: (params: UpdateFolderParams) => Promise; -} diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.test.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.test.ts index 95b82d052a5..ee29563e842 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.test.ts +++ b/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.test.ts @@ -1,43 +1,72 @@ import { describe, it, expect, beforeEach, vi } from "vitest"; -import { UpdateFolder } from "./UpdateFolder.js"; -import { folderCacheFactory } from "../cache/FoldersCacheFactory.js"; -import { Folder } from "../Folder.js"; import { type FolderPermission } from "@webiny/shared-aco/flp/flp.types.js"; +import { LoadingRepository } from "@webiny/app-utils"; import { ROOT_FOLDER } from "~/constants.js"; -import type { IUpdateFolderGateway } from "~/features/folders/updateFolder/IUpdateFolderGateway.js"; -import type { FolderGqlDto } from "~/features/folders/updateFolder/FolderGqlDto.js"; - -class UpdateFolderMockGateway implements IUpdateFolderGateway { - mockResponse: FolderGqlDto; - - // Had to use `any` as the mock folders passed in the tests below are also partial objects. - constructor(mockResponse: any) { - this.mockResponse = mockResponse as FolderGqlDto; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { ListCache } from "~/features/folders/cache/index.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { Container } from "@webiny/di"; +import { FoldersContext } from "~/features/folders/abstractions.js"; +import { FoldersCache } from "~/features/folders/abstractions.js"; +import { UpdateFolderFeature } from "~/features/folders/updateFolder/feature.js"; +import { UpdateFolderUseCase } from "~/features/folders/updateFolder/abstractions.js"; +import { UpdateFolderGateway } from "~/features/folders/updateFolder/abstractions.js"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; + +class UpdateFolderMockGateway implements UpdateFolderGateway.Interface { + mockResponse: Partial; + + constructor(mockResponse: Partial) { + this.mockResponse = mockResponse; } async execute() { - return this.mockResponse; + return this.mockResponse as FolderDto; } } +interface SetupTestParams { + gateway: UpdateFolderGateway.Interface; + folders?: Folder[]; + foldersCache?: ListCache; +} + describe("UpdateFolder", () => { const type = "abc"; - const foldersCache = folderCacheFactory.getCache(type); + function setupTest(params: SetupTestParams) { + const container = new Container(); + const foldersCache = params.foldersCache ? params.foldersCache : new ListCache(); + + if (!params.foldersCache) { + foldersCache.addItems([ + Folder.create({ + id: "any-folder-id", + title: "Any Folder", + slug: "any-folder", + parentId: null, + permissions: [], + type + }) + ]); + } + + if (params.folders) { + foldersCache.addItems(params.folders); + } + + container.registerInstance(FoldersContext, { type }); + container.registerInstance(FoldersCache, foldersCache); + container.registerInstance(FoldersLoadingRepository, new LoadingRepository()); + + UpdateFolderFeature.register(container); + container.registerInstance(UpdateFolderGateway, params.gateway); + + return { container, foldersCache, updateFolder: container.resolve(UpdateFolderUseCase) }; + } beforeEach(() => { vi.clearAllMocks(); - foldersCache.clear(); - foldersCache.addItems([ - Folder.create({ - id: "any-folder-id", - title: "Any Folder", - slug: "any-folder", - parentId: null, - permissions: [], - type - }) - ]); }); it("should be able to update a folder", async () => { @@ -50,11 +79,11 @@ describe("UpdateFolder", () => { type }); - const spy = vi.spyOn(gateway, "execute"); + const { updateFolder, foldersCache } = setupTest({ gateway }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const spy = vi.spyOn(gateway, "execute"); - expect(foldersCache.hasItems()).toBeTrue(); + expect(foldersCache.hasItems()).toBe(true); const item = foldersCache.getItem(folder => folder.id === "any-folder-id"); expect(item?.id).toEqual("any-folder-id"); expect(item?.title).toEqual("Any Folder"); @@ -79,32 +108,6 @@ describe("UpdateFolder", () => { expect(updatedItem?.parentId).toEqual("another-id"); }); - it("should not update a folder if id is missing", async () => { - const gateway = new UpdateFolderMockGateway(null); - const spy = vi.spyOn(gateway, "execute"); - - const updateFolder = UpdateFolder.getInstance(type, gateway); - - await updateFolder.execute({ - id: "", - title: "Updated Folder", - slug: "updated-folder", - parentId: "another-id", - permissions: [], - type - }); - - expect(spy).toHaveBeenCalledTimes(1); - const updatedItem = foldersCache.getItem(folder => folder.id === "any-folder-id"); - - expect(updatedItem).toBeDefined(); - expect(updatedItem?.id).toEqual("any-folder-id"); - expect(updatedItem?.type).toEqual(type); - expect(updatedItem?.title).toEqual("Any Folder"); - expect(updatedItem?.slug).toEqual("any-folder"); - expect(updatedItem?.parentId).toEqual(null); - }); - it("should propagate `permissions` changes to child folders", async () => { const parentFolder = Folder.create({ id: "parent-folder-id", @@ -142,6 +145,7 @@ describe("UpdateFolder", () => { type }); + const foldersCache = new ListCache(); foldersCache.addItems([parentFolder, childFolder1, childFolder2, childFolder3]); // Let's update parentFolder, the change should be propagated to all it's children (childFolder1, childFolder2 and childFolder3). @@ -160,7 +164,7 @@ describe("UpdateFolder", () => { type }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway, foldersCache }); await updateFolder.execute({ id: parentFolder.id, @@ -209,7 +213,7 @@ describe("UpdateFolder", () => { type }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway, foldersCache }); await updateFolder.execute({ id: childFolder1.id, @@ -265,7 +269,7 @@ describe("UpdateFolder", () => { type }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway, foldersCache }); await updateFolder.execute({ id: childFolder1.id, @@ -343,11 +347,12 @@ describe("UpdateFolder", () => { type }); - foldersCache.addItems([parentFolder, childFolder1, childFolder2, childFolder3]); - // Let's update parentFolder, the change should be propagated to all it's children (childFolder1, childFolder2 and childFolder3). const newParentPath: string = `${ROOT_FOLDER}/parent-folder-edit`; + const foldersCache = new ListCache(); + foldersCache.addItems([parentFolder, childFolder1, childFolder2, childFolder3]); + { const gateway = new UpdateFolderMockGateway({ id: parentFolder.id, @@ -359,7 +364,7 @@ describe("UpdateFolder", () => { type }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway, foldersCache }); await updateFolder.execute({ id: parentFolder.id, @@ -396,7 +401,7 @@ describe("UpdateFolder", () => { type }); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway, foldersCache }); await updateFolder.execute({ id: childFolder1.id, @@ -419,8 +424,8 @@ describe("UpdateFolder", () => { }); it("should handle gateway errors gracefully", async () => { - class UpdateFolderErrorMockGateway implements IUpdateFolderGateway { - async execute(): Promise { + class UpdateFolderErrorMockGateway implements UpdateFolderGateway.Interface { + async execute(): Promise { throw new Error("Gateway error"); } } @@ -428,7 +433,7 @@ describe("UpdateFolder", () => { const gateway = new UpdateFolderErrorMockGateway(); const spy = vi.spyOn(gateway, "execute"); - const updateFolder = UpdateFolder.getInstance(type, gateway); + const { updateFolder } = setupTest({ gateway }); await expect( updateFolder.execute({ diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.ts deleted file mode 100644 index daef630b502..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolder.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { loadingRepositoryFactory } from "@webiny/app-utils"; -import type { IUpdateFolderUseCase } from "./IUpdateFolderUseCase.js"; -import type { IUpdateFolderGateway } from "./IUpdateFolderGateway.js"; -import { UpdateFolderRepository } from "./UpdateFolderRepository.js"; -import { UpdateFolderRepositoryWithPermissionsChange } from "./UpdateFolderRepositoryWithPermissionsChange.js"; -import { UpdateFolderUseCase } from "./UpdateFolderUseCase.js"; -import { UpdateFolderUseCaseWithLoading } from "./UpdateFolderUseCaseWithLoading.js"; -import { UpdateFolderUseCaseWithoutInheritedPermissions } from "./UpdateFolderUseCaseWithoutInheritedPermissions.js"; -import { folderCacheFactory } from "../cache/index.js"; -import { UpdateFolderRepositoryWithPathChange } from "~/features/folders/updateFolder/UpdateFolderRepositoryWithPathChange.js"; - -export class UpdateFolder { - public static getInstance(type: string, gateway: IUpdateFolderGateway): IUpdateFolderUseCase { - const foldersCache = folderCacheFactory.getCache(type); - const loadingRepository = loadingRepositoryFactory.getRepository(type); - - const repository = new UpdateFolderRepository(foldersCache, gateway); - const repositoryWithPathChange = new UpdateFolderRepositoryWithPathChange( - foldersCache, - repository - ); - const repositoryWithPermissionsChange = new UpdateFolderRepositoryWithPermissionsChange( - foldersCache, - repositoryWithPathChange - ); - - const useCase = new UpdateFolderUseCase(repositoryWithPermissionsChange); - const useCaseWithoutInheritedPermissions = - new UpdateFolderUseCaseWithoutInheritedPermissions(useCase); - return new UpdateFolderUseCaseWithLoading( - loadingRepository, - useCaseWithoutInheritedPermissions - ); - } -} diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderGqlGateway.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderGqlGateway.ts index 8d3ff0da2d6..2aabd954efb 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderGqlGateway.ts +++ b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderGqlGateway.ts @@ -1,14 +1,16 @@ -import type ApolloClient from "apollo-client"; import gql from "graphql-tag"; -import type { IUpdateFolderGateway } from "./IUpdateFolderGateway.js"; -import type { FolderDto } from "./FolderDto.js"; -import type { AcoError, FolderItem } from "~/types.js"; +import { ApolloClient } from "@webiny/app-admin/features/apolloClient/abstraction.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import { FolderModelProvider } from "~/features/folders/abstractions.js"; +import type { FolderGatewayDto } from "./abstractions.js"; +import { UpdateFolderGateway as GatewayAbstraction } from "./abstractions.js"; +import type { AcoError } from "~/types.js"; import { ROOT_FOLDER } from "~/constants.js"; export interface UpdateFolderResponse { aco: { updateFolder: { - data: FolderItem; + data: FolderDto; error: AcoError | null; }; }; @@ -18,7 +20,7 @@ export interface UpdateFolderVariables { id: string; data: Partial< Omit< - FolderItem, + FolderDto, "id" | "createdOn" | "createdBy" | "savedOn" | "savedBy" | "modifiedOn" | "modifiedBy" > >; @@ -39,23 +41,22 @@ export const UPDATE_FOLDER = (FOLDER_FIELDS: string) => gql` } `; -export class UpdateFolderGqlGateway implements IUpdateFolderGateway { - private client: ApolloClient; - private modelFields: string; +class UpdateFolderGqlGatewayImpl implements GatewayAbstraction.Interface { + constructor( + private client: ApolloClient.Interface, + private folderModelProvider: FolderModelProvider.Interface + ) {} - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; - } + async execute(folder: FolderGatewayDto) { + const fields = await this.folderModelProvider.getGraphQLSelection(); - async execute(folder: FolderDto) { const { id, title, slug, permissions, parentId, extensions } = folder; const { data: response } = await this.client.mutate< UpdateFolderResponse, UpdateFolderVariables >({ - mutation: UPDATE_FOLDER(this.modelFields), + mutation: UPDATE_FOLDER(fields), variables: { id, data: { @@ -81,3 +82,8 @@ export class UpdateFolderGqlGateway implements IUpdateFolderGateway { return data; } } + +export const UpdateFolderGqlGateway = GatewayAbstraction.createImplementation({ + implementation: UpdateFolderGqlGatewayImpl, + dependencies: [ApolloClient, FolderModelProvider] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepository.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepository.ts index 1d1b8f9f545..4a50c4bdfd3 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepository.ts +++ b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepository.ts @@ -1,29 +1,25 @@ -import type { IUpdateFolderRepository } from "./IUpdateFolderRepository.js"; -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; -import type { IUpdateFolderGateway } from "./IUpdateFolderGateway.js"; -import type { FolderDto } from "./FolderDto.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { FoldersCache } from "../abstractions.js"; +import { + UpdateFolderRepository as RepositoryAbstraction, + UpdateFolderGateway +} from "./abstractions.js"; -export class UpdateFolderRepository implements IUpdateFolderRepository { - private cache: ListCache; - private gateway: IUpdateFolderGateway; - - constructor(cache: ListCache, gateway: IUpdateFolderGateway) { - this.cache = cache; - this.gateway = gateway; - } +class UpdateFolderRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private gateway: UpdateFolderGateway.Interface + ) {} async execute(folder: Folder) { - const dto: FolderDto = { + const result = await this.gateway.execute({ id: folder.id, title: folder.title, slug: folder.slug, - permissions: folder.permissions, + extensions: folder.extensions, parentId: folder.parentId, - extensions: folder.extensions - }; - - const result = await this.gateway.execute(dto); + permissions: folder.permissions + }); this.cache.updateItems(f => { if (f.id === folder.id) { @@ -34,3 +30,8 @@ export class UpdateFolderRepository implements IUpdateFolderRepository { }); } } + +export const UpdateFolderRepository = RepositoryAbstraction.createImplementation({ + implementation: UpdateFolderRepositoryImpl, + dependencies: [FoldersCache, UpdateFolderGateway] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCase.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCase.ts index e1994d81bc3..417f90360dc 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCase.ts +++ b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCase.ts @@ -1,25 +1,19 @@ -import type { UpdateFolderParams, IUpdateFolderUseCase } from "./IUpdateFolderUseCase.js"; -import type { IUpdateFolderRepository } from "./IUpdateFolderRepository.js"; -import { Folder } from "../Folder.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import type { UpdateFolderParams } from "./abstractions.js"; +import { + UpdateFolderUseCase as UseCaseAbstraction, + UpdateFolderRepository +} from "./abstractions.js"; -export class UpdateFolderUseCase implements IUpdateFolderUseCase { - private repository: IUpdateFolderRepository; +class UpdateFolderUseCaseImpl implements UseCaseAbstraction.Interface { + constructor(private repository: UpdateFolderRepository.Interface) {} - constructor(repository: IUpdateFolderRepository) { - this.repository = repository; - } - - async execute(params: UpdateFolderParams) { - await this.repository.execute( - Folder.create({ - id: params.id, - title: params.title, - slug: params.slug, - type: params.type, - parentId: params.parentId, - permissions: params.permissions, - extensions: params.extensions - }) - ); + async execute(folder: UpdateFolderParams) { + await this.repository.execute(Folder.create(folder)); } } + +export const UpdateFolderUseCase = UseCaseAbstraction.createImplementation({ + implementation: UpdateFolderUseCaseImpl, + dependencies: [UpdateFolderRepository] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithLoading.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithLoading.ts deleted file mode 100644 index 367e720472e..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithLoading.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ILoadingRepository } from "@webiny/app-utils"; -import { LoadingActionsEnum } from "~/types.js"; -import type { IUpdateFolderUseCase, UpdateFolderParams } from "./IUpdateFolderUseCase.js"; - -export class UpdateFolderUseCaseWithLoading implements IUpdateFolderUseCase { - private loadingRepository: ILoadingRepository; - private useCase: IUpdateFolderUseCase; - - constructor(loadingRepository: ILoadingRepository, useCase: IUpdateFolderUseCase) { - this.loadingRepository = loadingRepository; - this.useCase = useCase; - } - - async execute(params: UpdateFolderParams) { - await this.loadingRepository.runCallBack( - this.useCase.execute(params), - LoadingActionsEnum.update - ); - } -} diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithoutInheritedPermissions.ts b/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithoutInheritedPermissions.ts deleted file mode 100644 index 0a105cf828d..00000000000 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderUseCaseWithoutInheritedPermissions.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { IUpdateFolderUseCase, UpdateFolderParams } from "./IUpdateFolderUseCase.js"; - -export class UpdateFolderUseCaseWithoutInheritedPermissions implements IUpdateFolderUseCase { - private useCase: IUpdateFolderUseCase; - - constructor(useCase: IUpdateFolderUseCase) { - this.useCase = useCase; - } - - async execute(params: UpdateFolderParams) { - // We must omit all inherited permissions. - const permissions = params.permissions.filter(p => !p.inheritedFrom); - - await this.useCase.execute({ - ...params, - permissions - }); - } -} diff --git a/packages/app-aco/src/features/folders/updateFolder/abstractions.ts b/packages/app-aco/src/features/folders/updateFolder/abstractions.ts new file mode 100644 index 00000000000..55164f46bdd --- /dev/null +++ b/packages/app-aco/src/features/folders/updateFolder/abstractions.ts @@ -0,0 +1,55 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { Folder } from "~/domain/folder/Folder.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import type { FolderPermission } from "~/types.js"; + +export interface FolderGatewayDto { + id: string; + title: string; + slug: string; + permissions: FolderPermission[]; + parentId: string | null; + extensions: Record; +} + +export interface UpdateFolderParams { + id: string; + title: string; + slug: string; + type: string; + parentId: string | null; + permissions: FolderPermission[]; + extensions?: Record; +} + +export interface IUpdateFolderUseCase { + execute: (params: UpdateFolderParams) => Promise; +} + +export interface IUpdateFolderRepository { + execute: (folder: Folder) => Promise; +} + +export interface IUpdateFolderGateway { + execute: (folder: FolderGatewayDto) => Promise; +} + +export const UpdateFolderUseCase = createAbstraction("UpdateFolderUseCase"); + +export namespace UpdateFolderUseCase { + export type Interface = IUpdateFolderUseCase; + export type Params = UpdateFolderParams; +} + +export const UpdateFolderRepository = + createAbstraction("UpdateFolderRepository"); + +export namespace UpdateFolderRepository { + export type Interface = IUpdateFolderRepository; +} + +export const UpdateFolderGateway = createAbstraction("UpdateFolderGateway"); + +export namespace UpdateFolderGateway { + export type Interface = IUpdateFolderGateway; +} diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPathChange.ts b/packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPathChange.ts similarity index 77% rename from packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPathChange.ts rename to packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPathChange.ts index fa7d8fc0152..28bd4caa012 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPathChange.ts +++ b/packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPathChange.ts @@ -1,23 +1,20 @@ import { Path } from "@webiny/shared-aco"; -import type { IUpdateFolderRepository } from "./IUpdateFolderRepository.js"; -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; +import { FoldersCache } from "../../abstractions.js"; +import { UpdateFolderRepository as RepositoryAbstraction } from "../abstractions.js"; +import { Folder } from "~/domain/folder/Folder.js"; -export class UpdateFolderRepositoryWithPathChange implements IUpdateFolderRepository { - private cache: ListCache; - private decoretee: IUpdateFolderRepository; - - constructor(cache: ListCache, decoretee: IUpdateFolderRepository) { - this.cache = cache; - this.decoretee = decoretee; - } +class UpdateFolderRepositoryWithPathChangeImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private decoratee: RepositoryAbstraction.Interface + ) {} async execute(folder: Folder) { const folderPath = folder.path; const cachedFolderPath = this.cache.getItem(f => f.id === folder.id)?.path; // Update the folder - await this.decoretee.execute(folder); + await this.decoratee.execute(folder); // If the folder is not in the cache, we can't proceed to update its children paths. if (!cachedFolderPath) { @@ -77,3 +74,8 @@ export class UpdateFolderRepositoryWithPathChange implements IUpdateFolderReposi return this.cache.getItems().filter(f => f.parentId === folder.id); } } + +export const UpdateFolderRepositoryWithPathChange = RepositoryAbstraction.createDecorator({ + decorator: UpdateFolderRepositoryWithPathChangeImpl, + dependencies: [FoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPermissionsChange.ts b/packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.ts similarity index 78% rename from packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPermissionsChange.ts rename to packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.ts index 6369504e868..d2df997c701 100644 --- a/packages/app-aco/src/features/folders/updateFolder/UpdateFolderRepositoryWithPermissionsChange.ts +++ b/packages/app-aco/src/features/folders/updateFolder/decorators/RepositoryWithPermissionsChange.ts @@ -1,24 +1,21 @@ import isEqual from "lodash/isEqual.js"; import { Permissions } from "@webiny/shared-aco"; -import type { IUpdateFolderRepository } from "./IUpdateFolderRepository.js"; -import type { ListCache } from "../cache/index.js"; -import { Folder } from "../Folder.js"; +import { Folder } from "~/domain/folder/Folder.js"; +import { FoldersCache } from "../../abstractions.js"; +import { UpdateFolderRepository as RepositoryAbstraction } from "../abstractions.js"; -export class UpdateFolderRepositoryWithPermissionsChange implements IUpdateFolderRepository { - private cache: ListCache; - private decoretee: IUpdateFolderRepository; - - constructor(cache: ListCache, decoretee: IUpdateFolderRepository) { - this.cache = cache; - this.decoretee = decoretee; - } +class UpdateFolderRepositoryWithPermissionsChangeImpl implements RepositoryAbstraction.Interface { + constructor( + private cache: FoldersCache.Interface, + private decoratee: RepositoryAbstraction.Interface + ) {} async execute(folder: Folder) { const folderPermissions = [...folder.permissions]; const cachedFolderPermissions = this.cache.getItem(f => f.id === folder.id)?.permissions; // Let's run the original use case and update the folder. - await this.decoretee.execute(folder); + await this.decoratee.execute(folder); if (!cachedFolderPermissions) { // If the folder is not in the cache, we can't proceed to update its children permissions. @@ -70,3 +67,8 @@ export class UpdateFolderRepositoryWithPermissionsChange implements IUpdateFolde return this.cache.getItems().filter(f => f.parentId === folder.id); } } + +export const UpdateFolderRepositoryWithPermissionsChange = RepositoryAbstraction.createDecorator({ + decorator: UpdateFolderRepositoryWithPermissionsChangeImpl, + dependencies: [FoldersCache] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithLoading.ts b/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithLoading.ts new file mode 100644 index 00000000000..965f45c233c --- /dev/null +++ b/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithLoading.ts @@ -0,0 +1,22 @@ +import { FoldersLoadingRepository } from "../../abstractions.js"; +import { UpdateFolderUseCase as UseCaseAbstraction } from "../abstractions.js"; +import { LoadingActionsEnum } from "~/types.js"; + +class UpdateFolderUseCaseWithLoadingImpl implements UseCaseAbstraction.Interface { + constructor( + private loadingRepository: FoldersLoadingRepository.Interface, + private decoratee: UseCaseAbstraction.Interface + ) {} + + async execute(folder: UseCaseAbstraction.Params) { + await this.loadingRepository.runCallBack( + this.decoratee.execute(folder), + LoadingActionsEnum.update + ); + } +} + +export const UpdateFolderUseCaseWithLoading = UseCaseAbstraction.createDecorator({ + decorator: UpdateFolderUseCaseWithLoadingImpl, + dependencies: [FoldersLoadingRepository] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithoutInheritedPermissions.ts b/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithoutInheritedPermissions.ts new file mode 100644 index 00000000000..cda71a29335 --- /dev/null +++ b/packages/app-aco/src/features/folders/updateFolder/decorators/UseCaseWithoutInheritedPermissions.ts @@ -0,0 +1,20 @@ +import { UpdateFolderUseCase as UseCaseAbstraction } from "../abstractions.js"; + +class UpdateFolderUseCaseWithoutInheritedPermissionsImpl implements UseCaseAbstraction.Interface { + constructor(private decoratee: UseCaseAbstraction.Interface) {} + + async execute(folder: UseCaseAbstraction.Params) { + // We must omit all inherited permissions. + const filteredPermissions = folder.permissions.filter(p => !p.inheritedFrom); + + await this.decoratee.execute({ + ...folder, + permissions: filteredPermissions + }); + } +} + +export const UpdateFolderUseCaseWithoutInheritedPermissions = UseCaseAbstraction.createDecorator({ + decorator: UpdateFolderUseCaseWithoutInheritedPermissionsImpl, + dependencies: [] +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/feature.ts b/packages/app-aco/src/features/folders/updateFolder/feature.ts new file mode 100644 index 00000000000..beab6293060 --- /dev/null +++ b/packages/app-aco/src/features/folders/updateFolder/feature.ts @@ -0,0 +1,38 @@ +import { createFeature } from "@webiny/feature/admin"; +import { FoldersLoadingRepository } from "~/features/folders/abstractions.js"; +import { UpdateFolderUseCase as UseCase } from "./abstractions.js"; +import { UpdateFolderUseCase } from "./UpdateFolderUseCase.js"; +import { UpdateFolderRepository } from "./UpdateFolderRepository.js"; +import { UpdateFolderGqlGateway } from "./UpdateFolderGqlGateway.js"; +import { UpdateFolderRepositoryWithPathChange } from "./decorators/RepositoryWithPathChange.js"; +import { UpdateFolderRepositoryWithPermissionsChange } from "./decorators/RepositoryWithPermissionsChange.js"; +import { UpdateFolderUseCaseWithoutInheritedPermissions } from "./decorators/UseCaseWithoutInheritedPermissions.js"; +import { UpdateFolderUseCaseWithLoading } from "./decorators/UseCaseWithLoading.js"; + +export const UpdateFolderFeature = createFeature({ + name: "UpdateFolder", + register(container) { + // Register base use case + container.register(UpdateFolderUseCase); + + // Register base repository + container.register(UpdateFolderRepository).inSingletonScope(); + + // Register gateway + container.register(UpdateFolderGqlGateway); + + // Register repository decorators (innermost first) + container.registerDecorator(UpdateFolderRepositoryWithPathChange); + container.registerDecorator(UpdateFolderRepositoryWithPermissionsChange); + + // Register use case decorators (innermost first) + container.registerDecorator(UpdateFolderUseCaseWithoutInheritedPermissions); + container.registerDecorator(UpdateFolderUseCaseWithLoading); + }, + resolve(container) { + return { + useCase: container.resolve(UseCase), + loading: container.resolve(FoldersLoadingRepository) + }; + } +}); diff --git a/packages/app-aco/src/features/folders/updateFolder/useUpdateFolder.ts b/packages/app-aco/src/features/folders/updateFolder/useUpdateFolder.ts index c812ae3d07a..95ca832613a 100644 --- a/packages/app-aco/src/features/folders/updateFolder/useUpdateFolder.ts +++ b/packages/app-aco/src/features/folders/updateFolder/useUpdateFolder.ts @@ -1,25 +1,30 @@ -import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { UpdateFolderGqlGateway } from "./UpdateFolderGqlGateway.js"; -import { UpdateFolder } from "./UpdateFolder.js"; -import type { UpdateFolderParams } from "./IUpdateFolderUseCase.js"; -import { useFoldersType, useGetFolderGraphQLSelection } from "~/hooks/index.js"; +import { useCallback, useEffect, useState } from "react"; +import { autorun } from "mobx"; +import { useFeature } from "@webiny/app"; +import { UpdateFolderFeature } from "./feature.js"; +import { UpdateFolderParams } from "./abstractions.js"; export const useUpdateFolder = () => { - const client = useApolloClient(); - const type = useFoldersType(); - const fields = useGetFolderGraphQLSelection(); - const gateway = new UpdateFolderGqlGateway(client, fields); + const { useCase, loading: loadingState } = useFeature(UpdateFolderFeature); + + const [loading, setLoading] = useState(false); const updateFolder = useCallback( - (params: UpdateFolderParams) => { - const instance = UpdateFolder.getInstance(type, gateway); - return instance.execute(params); + (folder: UpdateFolderParams) => { + return useCase.execute(folder); }, - [type, gateway] + [useCase] ); + useEffect(() => { + return autorun(() => { + const isLoading = loadingState.isLoading("update"); + setLoading(isLoading); + }); + }, [loadingState]); + return { - updateFolder + updateFolder, + loading }; }; diff --git a/packages/app-aco/src/features/index.ts b/packages/app-aco/src/features/index.ts deleted file mode 100644 index c534f0c19eb..00000000000 --- a/packages/app-aco/src/features/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./folders/index.js"; diff --git a/packages/app-aco/src/hooks/index.ts b/packages/app-aco/src/hooks/index.ts index 0b4766df475..c3f2926a1fe 100644 --- a/packages/app-aco/src/hooks/index.ts +++ b/packages/app-aco/src/hooks/index.ts @@ -1,9 +1,7 @@ export * from "./useAcoList.js"; export * from "./useAcoApp.js"; export * from "./useFolder.js"; -export * from "./useFolders.js"; export * from "./useFoldersType.js"; -export * from "./useGetFolderGraphQLSelection.js"; export * from "./useRecords.js"; export * from "./useTags.js"; export * from "./useNavigateFolder.js"; diff --git a/packages/app-aco/src/hooks/useFolderModel.ts b/packages/app-aco/src/hooks/useFolderModel.ts new file mode 100644 index 00000000000..6609d4e294f --- /dev/null +++ b/packages/app-aco/src/hooks/useFolderModel.ts @@ -0,0 +1,18 @@ +import { useEffect } from "react"; +import { useState } from "react"; +import { useFeature } from "@webiny/app-admin"; +import { CmsModel } from "@webiny/app-headless-cms-common/types"; +import { FolderModelProviderFeature } from "~/features/folders/folderModelProvider/feature.js"; + +export function useFolderModel() { + const { provider } = useFeature(FolderModelProviderFeature); + const [model, setModel] = useState(null); + + useEffect(() => { + provider.getModel().then(model => { + setModel(model); + }); + }, []); + + return model; +} diff --git a/packages/app-aco/src/hooks/useFolders.ts b/packages/app-aco/src/hooks/useFolders.ts deleted file mode 100644 index 8c2f1354ee4..00000000000 --- a/packages/app-aco/src/hooks/useFolders.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - useCreateFolder, - useDeleteFolder, - useGetDescendantFolders, - useGetFolder, - useGetFolderLevelPermission, - useListFolders, - useUpdateFolder -} from "~/features/index.js"; - -/** - * Custom hook to manage folder operations. - * - * @deprecated This hook is deprecated. Use the individual hooks directly from "~/features" instead. - */ -export const useFolders = () => { - const { createFolder } = useCreateFolder(); - const { deleteFolder } = useDeleteFolder(); - const { listFolders, folders, loading } = useListFolders(); - const { updateFolder } = useUpdateFolder(); - const { getDescendantFolders } = useGetDescendantFolders(); - const { getFolder } = useGetFolder(); - const { getFolderLevelPermission: canManageStructure } = - useGetFolderLevelPermission("canManageStructure"); - const { getFolderLevelPermission: canManagePermissions } = - useGetFolderLevelPermission("canManagePermissions"); - const { getFolderLevelPermission: canManageContent } = - useGetFolderLevelPermission("canManageContent"); - - console.warn( - "useFolders() hook is deprecated. Please use the appropriate feature-based hooks instead. Learn more: https://webiny.link/app-aco-folders-features" - ); - - return { - folders, - loading, - listFolders, - getFolder, - getDescendantFolders, - createFolder, - updateFolder, - deleteFolder, - folderLevelPermissions: { - canManageStructure, - canManagePermissions, - canManageContent - } - }; -}; diff --git a/packages/app-aco/src/hooks/useGetFolderGraphQLSelection.ts b/packages/app-aco/src/hooks/useGetFolderGraphQLSelection.ts deleted file mode 100644 index a389e5264e4..00000000000 --- a/packages/app-aco/src/hooks/useGetFolderGraphQLSelection.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createFieldsList } from "@webiny/app-headless-cms-common"; -import { useFolderModel } from "~/features/index.js"; - -export const useGetFolderGraphQLSelection = () => { - const model = useFolderModel(); - - if (!model) { - throw Error(`useGetFolderGraphQLSelection requires an FolderModelContext to be available!`); - } - - const fields = createFieldsList({ model, fields: model.fields }); - - return /* GraphQL */ `{ - id - createdOn - createdBy { - id - displayName - } - savedOn - savedBy { - id - displayName - } - modifiedOn - modifiedBy { - id - displayName - } - permissions { - target - level - inheritedFrom - } - hasNonInheritedPermissions - canManagePermissions - canManageStructure - canManageContent - ${fields} - }`; -}; diff --git a/packages/app-aco/src/index.ts b/packages/app-aco/src/index.ts index 03eff69f8f3..a2495017ebd 100644 --- a/packages/app-aco/src/index.ts +++ b/packages/app-aco/src/index.ts @@ -3,8 +3,24 @@ export * from "./components/index.js"; export * from "./config/index.js"; export * from "./contexts/index.js"; export * from "./hooks/index.js"; -export * from "./features/index.js"; export * from "./dialogs/index.js"; export * from "./sorting.js"; export type { TableRow, FolderTableRow, RecordTableRow } from "./table.types.js"; export type { ListMeta } from "./types.js"; + +// Export domain +export * from "./domain/folder/FolderDto.js"; +export * from "./domain/folder/FolderDtoMapper.js"; + +// Export feature hooks +export * from "./features/folders/createFolder/useCreateFolder.js"; +export * from "./features/folders/updateFolder/useUpdateFolder.js"; +export * from "./features/folders/deleteFolder/useDeleteFolder.js"; +export * from "./features/folders/getFolder/useGetFolder.js"; +export * from "./features/folders/getDescendantFolders/useGetDescendantFolders.js"; +export * from "./features/folders/getFolderAncestors/useGetFolderAncestors.js"; +export * from "./features/folders/getFolderLevelPermission/useGetFolderLevelPermission.js"; +export * from "./features/folders/getFolderExtensionsFields/useFolderExtensionsFields.js"; +export * from "./features/folders/loadFolderHierarchy/useLoadFolderHierarchy.js"; +export * from "./features/folders/listFolders/useListFolders.js"; +export * from "./features/folders/listFoldersByParentIds/useListFoldersByParentIds.js"; diff --git a/packages/app-aco/src/table.types.ts b/packages/app-aco/src/table.types.ts index 5be0f4bccf2..7f8da9a3aea 100644 --- a/packages/app-aco/src/table.types.ts +++ b/packages/app-aco/src/table.types.ts @@ -1,4 +1,6 @@ -import type { CmsIdentity, FolderItem, GenericSearchData, Location } from "~/types.js"; +import type { FolderDto } from "~/domain/folder/FolderDto.js"; +import type { FolderIdentityDto } from "~/domain/folder/FolderIdentity.js"; +import type { GenericSearchData, Location } from "~/types.js"; export interface SearchRecordItem { id: string; @@ -9,11 +11,11 @@ export interface SearchRecordItem; @@ -27,7 +29,7 @@ export interface TableRow { data: TData; } -export interface FolderTableRow extends TableRow { +export interface FolderTableRow extends TableRow { $type: "FOLDER"; } diff --git a/packages/app-aco/src/types.ts b/packages/app-aco/src/types.ts index 6da337bd700..2824fc97bc4 100644 --- a/packages/app-aco/src/types.ts +++ b/packages/app-aco/src/types.ts @@ -1,10 +1,8 @@ import type { - CmsIdentity, CmsModel, CmsModelField, CmsModelFieldSettings } from "@webiny/app-headless-cms-common/types/index.js"; -import type { FolderPermission } from "@webiny/shared-aco/types.js"; export type { CmsIdentity } from "@webiny/app-headless-cms-common/types/index.js"; export type * from "@webiny/shared-aco/flp/flp.types.js"; @@ -19,27 +17,6 @@ export interface FolderLevelPermissionsTarget> { meta: TMeta; } -export interface FolderItem { - id: string; - title: string; - slug: string; - permissions: FolderPermission[]; - hasNonInheritedPermissions: boolean; - canManagePermissions: boolean; - canManageStructure: boolean; - canManageContent: boolean; - type: string; - parentId: string | null; - path: string; - createdBy: CmsIdentity; - createdOn: string; - savedBy: CmsIdentity; - savedOn: string; - modifiedBy: CmsIdentity | null; - modifiedOn: string | null; - extensions: Record; -} - export type GenericSearchData = { [key: string]: any; }; @@ -90,103 +67,6 @@ export interface ListMeta { hasMoreItems: boolean; } -export interface ListFoldersResponse { - aco: { - listFolders: { - data: FolderItem[] | null; - error: AcoError | null; - }; - }; -} - -export interface ListFoldersQueryVariables { - type: string; - limit: number; - sort?: Record; - after?: string | null; -} - -export interface GetFolderResponse { - aco: { - getFolder: { - data: FolderItem | null; - error: AcoError | null; - }; - }; -} - -export interface GetFolderQueryVariables { - id: string; -} - -export interface UpdateFolderResponse { - aco: { - updateFolder: { - data: FolderItem; - error: AcoError | null; - }; - }; -} - -export interface UpdateFolderVariables { - id: string; - data: Partial< - Omit< - FolderItem, - "id" | "createdOn" | "createdBy" | "savedOn" | "savedBy" | "modifiedOn" | "modifiedBy" - > - >; -} - -export interface CreateFolderResponse { - aco: { - createFolder: { - data: FolderItem; - error: AcoError | null; - }; - }; -} - -export interface CreateFolderVariables { - data: Omit< - FolderItem, - | "id" - | "createdOn" - | "createdBy" - | "savedOn" - | "savedBy" - | "modifiedOn" - | "modifiedBy" - | "hasNonInheritedPermissions" - | "canManageContent" - | "canManagePermissions" - | "canManageStructure" - >; -} - -export interface DeleteFolderVariables { - id: string; -} - -export interface DeleteFolderResponse { - aco: { - deleteFolder: { - data: boolean; - error: AcoError | null; - }; - }; -} - -export interface DndFolderItemData { - isFocused?: boolean; -} - -/** - * This type will be removed when all apps migrate to the CMS. - * @deprecated - */ -export type AcoAppMode = "aco" | "cms"; - /** * Apps. */ diff --git a/packages/app-aco/tsconfig.build.json b/packages/app-aco/tsconfig.build.json index f643e99fc22..bd50911f89c 100644 --- a/packages/app-aco/tsconfig.build.json +++ b/packages/app-aco/tsconfig.build.json @@ -8,12 +8,14 @@ { "path": "../app-headless-cms-common/tsconfig.build.json" }, { "path": "../app-security/tsconfig.build.json" }, { "path": "../app-utils/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" }, { "path": "../form/tsconfig.build.json" }, { "path": "../plugins/tsconfig.build.json" }, { "path": "../react-properties/tsconfig.build.json" }, { "path": "../shared-aco/tsconfig.build.json" }, { "path": "../utils/tsconfig.build.json" }, - { "path": "../validation/tsconfig.build.json" } + { "path": "../validation/tsconfig.build.json" }, + { "path": "../wcp/tsconfig.build.json" } ], "compilerOptions": { "rootDir": "./src", @@ -40,6 +42,10 @@ "@webiny/app-security": ["../app-security/src"], "@webiny/app-utils/*": ["../app-utils/src/*"], "@webiny/app-utils": ["../app-utils/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/form/*": ["../form/src/*"], "@webiny/form": ["../form/src"], "@webiny/plugins/*": ["../plugins/src/*"], @@ -51,7 +57,9 @@ "@webiny/utils/*": ["../utils/src/*"], "@webiny/utils": ["../utils/src"], "@webiny/validation/*": ["../validation/src/*"], - "@webiny/validation": ["../validation/src"] + "@webiny/validation": ["../validation/src"], + "@webiny/wcp/*": ["../wcp/src/*"], + "@webiny/wcp": ["../wcp/src"] }, "baseUrl": "." } diff --git a/packages/app-aco/tsconfig.json b/packages/app-aco/tsconfig.json index 8ff59f1d699..28d0ac317c5 100644 --- a/packages/app-aco/tsconfig.json +++ b/packages/app-aco/tsconfig.json @@ -8,12 +8,14 @@ { "path": "../app-headless-cms-common" }, { "path": "../app-security" }, { "path": "../app-utils" }, + { "path": "../feature" }, { "path": "../form" }, { "path": "../plugins" }, { "path": "../react-properties" }, { "path": "../shared-aco" }, { "path": "../utils" }, - { "path": "../validation" } + { "path": "../validation" }, + { "path": "../wcp" } ], "compilerOptions": { "rootDirs": ["./src", "./__tests__"], @@ -40,6 +42,10 @@ "@webiny/app-security": ["../app-security/src"], "@webiny/app-utils/*": ["../app-utils/src/*"], "@webiny/app-utils": ["../app-utils/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/form/*": ["../form/src/*"], "@webiny/form": ["../form/src"], "@webiny/plugins/*": ["../plugins/src/*"], @@ -51,7 +57,9 @@ "@webiny/utils/*": ["../utils/src/*"], "@webiny/utils": ["../utils/src"], "@webiny/validation/*": ["../validation/src/*"], - "@webiny/validation": ["../validation/src"] + "@webiny/validation": ["../validation/src"], + "@webiny/wcp/*": ["../wcp/src/*"], + "@webiny/wcp": ["../wcp/src"] }, "baseUrl": "." } diff --git a/packages/app-aco/vitest.config.ts b/packages/app-aco/vitest.config.ts index f12752ea827..163f5898977 100644 --- a/packages/app-aco/vitest.config.ts +++ b/packages/app-aco/vitest.config.ts @@ -1,5 +1,8 @@ import { createTestConfig } from "../../testing"; export default async () => { - return createTestConfig({ path: import.meta.dirname, vitestConfig: { fileParallelism: true } }); + return createTestConfig({ + path: import.meta.dirname, + vitestConfig: { fileParallelism: true } + }); }; diff --git a/packages/app-admin/package.json b/packages/app-admin/package.json index 26a458e0653..9bbffe28359 100644 --- a/packages/app-admin/package.json +++ b/packages/app-admin/package.json @@ -39,7 +39,7 @@ "@webiny/admin-ui": "0.0.0", "@webiny/app": "0.0.0", "@webiny/app-security": "0.0.0", - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/feature": "0.0.0", "@webiny/form": "0.0.0", "@webiny/icons": "0.0.0", diff --git a/packages/app-admin/src/base/Admin.tsx b/packages/app-admin/src/base/Admin.tsx index 08873086d09..0e32d62c2d4 100644 --- a/packages/app-admin/src/base/Admin.tsx +++ b/packages/app-admin/src/base/Admin.tsx @@ -12,6 +12,7 @@ import { createRootContainer } from "~/base/createRootContainer.js"; import { WcpProvider } from "~/presentation/wcp/WcpProvider.js"; import { createTenancyProvider } from "~/presentation/tenancy/createTenancyProvider.js"; import { TelemetryAdminAppStart } from "./TelemetryAdminAppStart.js"; +import { ApolloClientFeature } from "~/features/apolloClient/feature.js"; export interface AdminProps { createApolloClient: ApolloClientFactory; @@ -21,7 +22,12 @@ export interface AdminProps { const container = createRootContainer(); export const Admin = ({ children, createApolloClient }: AdminProps) => { - const ApolloProvider = createApolloProvider(createApolloClient); + const uri = process.env.REACT_APP_GRAPHQL_API_URL as string; + const apolloClient = createApolloClient({ uri }); + + ApolloClientFeature.register(container, apolloClient); + + const ApolloProvider = createApolloProvider(apolloClient); const UIProviders = createUiProviders(); const UiStateProvider = createUiStateProvider(); const AdminUiStateProvider = createAdminUiStateProvider(); diff --git a/packages/app-admin/src/base/providers/ApolloProvider.tsx b/packages/app-admin/src/base/providers/ApolloProvider.tsx index 6c49bf8a8cf..3c4d6c8d421 100644 --- a/packages/app-admin/src/base/providers/ApolloProvider.tsx +++ b/packages/app-admin/src/base/providers/ApolloProvider.tsx @@ -14,9 +14,8 @@ interface ApolloProviderProps { children: React.ReactNode; } -export const createApolloProvider = (clientFactory: ApolloClientFactory) => { +export const createApolloProvider = (apolloClient: ApolloClient) => { return function ApolloProvider({ children }: ApolloProviderProps) { - const uri = process.env.REACT_APP_GRAPHQL_API_URL as string; - return {children}; + return {children}; }; }; diff --git a/packages/app-admin/src/components/RegisterFeature.tsx b/packages/app-admin/src/components/RegisterFeature.tsx new file mode 100644 index 00000000000..f5272f63b29 --- /dev/null +++ b/packages/app-admin/src/components/RegisterFeature.tsx @@ -0,0 +1,12 @@ +import { useContainer } from "@webiny/app"; +import { createFeature } from "@webiny/feature/admin"; + +interface RegisterFeatureProps { + feature: ReturnType; +} + +export const RegisterFeature = ({ feature }: RegisterFeatureProps) => { + const container = useContainer(); + feature.register(container); + return null; +}; diff --git a/packages/app-admin/src/components/index.ts b/packages/app-admin/src/components/index.ts index f4b335517ab..8414ee935e2 100644 --- a/packages/app-admin/src/components/index.ts +++ b/packages/app-admin/src/components/index.ts @@ -28,3 +28,4 @@ export * from "~/components/NavigationPrompt.js"; export * from "~/components/SplitView/index.js"; export * from "~/components/Permissions/index.js"; export * from "~/components/SearchUI.js"; +export * from "~/components/RegisterFeature.js"; diff --git a/packages/app-admin/src/features/apolloClient/abstraction.ts b/packages/app-admin/src/features/apolloClient/abstraction.ts new file mode 100644 index 00000000000..35e7ec3ac54 --- /dev/null +++ b/packages/app-admin/src/features/apolloClient/abstraction.ts @@ -0,0 +1,8 @@ +import type { ApolloClient as IApolloClient } from "apollo-client"; +import { createAbstraction } from "@webiny/feature/admin"; + +export const ApolloClient = createAbstraction>("ApolloClient"); + +export namespace ApolloClient { + export type Interface = IApolloClient; +} diff --git a/packages/app-admin/src/features/apolloClient/feature.ts b/packages/app-admin/src/features/apolloClient/feature.ts new file mode 100644 index 00000000000..efa902cd79b --- /dev/null +++ b/packages/app-admin/src/features/apolloClient/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/admin"; +import { ApolloClient } from "./abstraction.js"; + +export const ApolloClientFeature = createFeature({ + name: "ApolloClient", + register(container, apolloClient: ApolloClient.Interface) { + container.registerInstance(ApolloClient, apolloClient); + } +}); diff --git a/packages/app-admin/src/features/tenancy/feature.ts b/packages/app-admin/src/features/tenancy/feature.ts index 174cd09a59a..eeb564b40ff 100644 --- a/packages/app-admin/src/features/tenancy/feature.ts +++ b/packages/app-admin/src/features/tenancy/feature.ts @@ -1,5 +1,5 @@ import { Container } from "@webiny/di"; -import { createFeature } from "@webiny/app"; +import { createFeature } from "@webiny/feature/admin"; import { TenancyService as TenancyServiceAbstraction } from "./abstractions.js"; import { TenancyService } from "./TenancyService.js"; import { LocalStorageFeature } from "@webiny/app/features/localStorage"; diff --git a/packages/app-admin/src/features/wcp/feature.ts b/packages/app-admin/src/features/wcp/feature.ts index 4023c3a3506..43ec0868a5e 100644 --- a/packages/app-admin/src/features/wcp/feature.ts +++ b/packages/app-admin/src/features/wcp/feature.ts @@ -1,4 +1,4 @@ -import { createFeature } from "@webiny/app"; +import { createFeature } from "@webiny/feature/admin"; import { Container } from "@webiny/di"; import { WcpService as WcpServiceAbstraction } from "./abstractions.js"; import { WcpService } from "./WcpService.js"; diff --git a/packages/app-admin/src/presentation/installation/presenters/SystemInstaller/feature.ts b/packages/app-admin/src/presentation/installation/presenters/SystemInstaller/feature.ts index 45b288f0531..78cb3ad4184 100644 --- a/packages/app-admin/src/presentation/installation/presenters/SystemInstaller/feature.ts +++ b/packages/app-admin/src/presentation/installation/presenters/SystemInstaller/feature.ts @@ -1,4 +1,4 @@ -import { createFeature } from "@webiny/app"; +import { createFeature } from "@webiny/feature/admin"; import { Container } from "@webiny/di"; import { SystemInstallerPresenter as SystemInstallerPresenterAbstraction } from "./abstractions.js"; import { SystemInstallerGateway } from "./SystemInstallerGateway.js"; diff --git a/packages/app-file-manager/src/components/Grid/Grid.tsx b/packages/app-file-manager/src/components/Grid/Grid.tsx index 841ee8be740..1718628c68c 100644 --- a/packages/app-file-manager/src/components/Grid/Grid.tsx +++ b/packages/app-file-manager/src/components/Grid/Grid.tsx @@ -1,8 +1,8 @@ import React, { useMemo } from "react"; import { cn, OverlayLoader } from "@webiny/admin-ui"; -import { FolderGridItem, FolderProvider } from "@webiny/app-aco"; +import { FolderActionConfig, FolderGridItem, FolderProvider } from "@webiny/app-aco"; import { i18n } from "@webiny/app/i18n/index.js"; -import type { FolderItem } from "@webiny/app-aco/types.js"; +import type { FolderDto } from "@webiny/app-aco"; import type { FileItem } from "@webiny/app-admin/types.js"; import { FileProvider } from "~/contexts/FileProvider.js"; import { Thumbnail } from "../Thumbnail/index.js"; @@ -12,7 +12,8 @@ const t = i18n.ns("app-admin/file-manager/components/grid"); interface GridProps { records: FileItem[]; - folders: FolderItem[]; + folders: FolderDto[]; + folderActions: FolderActionConfig[]; loading?: boolean; onFolderClick: (id: string) => void; selected: FileItem[]; @@ -26,6 +27,7 @@ interface GridProps { } export const Grid = ({ + folderActions, folders, records, loading, @@ -79,7 +81,7 @@ export const Grid = ({ {displaySubFolders && folders.map(folder => ( - + ))} {records.map(record => ( diff --git a/packages/app-file-manager/src/components/Header/Title.tsx b/packages/app-file-manager/src/components/Header/Title.tsx index b889da7a04e..da62de84e39 100644 --- a/packages/app-file-manager/src/components/Header/Title.tsx +++ b/packages/app-file-manager/src/components/Header/Title.tsx @@ -4,8 +4,9 @@ import { ReactComponent as HomeIcon } from "@webiny/icons/home.svg"; import { ReactComponent as FolderIcon } from "@webiny/icons/folder.svg"; import { ReactComponent as MoreVerticalIcon } from "@webiny/icons/more_vert.svg"; import { useFileManagerView } from "~/modules/FileManagerRenderer/FileManagerViewProvider/index.js"; -import { FolderProvider, useAcoConfig } from "@webiny/app-aco"; +import { FolderProvider } from "@webiny/app-aco"; import { OptionsMenu } from "@webiny/app-admin"; +import { useFileManagerViewConfig } from "~/modules/FileManagerRenderer/FileManagerView/FileManagerViewConfig.js"; export const Title = () => { const { @@ -16,7 +17,7 @@ export const Title = () => { currentFolder, folders } = useFileManagerView(); - const { folder: folderConfig } = useAcoConfig(); + const { browser } = useFileManagerViewConfig(); const icon = useMemo(() => { return isRootFolder ? : ; @@ -35,7 +36,7 @@ export const Title = () => { {currentFolder && ( { const { file } = useFile(); const { copyFileUrl } = useCopyFile({ file }); - const { OptionsMenuItem } = FileManagerViewConfig.Browser.FileAction; + const { OptionsMenuItem } = FileManagerViewConfig.Browser.File.Action; return ( { const { openDialogDeleteFile } = useDeleteFile({ file }); - const { OptionsMenuItem } = FileManagerViewConfig.Browser.FileAction; + const { OptionsMenuItem } = FileManagerViewConfig.Browser.File.Action; if (!canDelete(file)) { return null; diff --git a/packages/app-file-manager/src/components/Table/Actions/EditFile.tsx b/packages/app-file-manager/src/components/Table/Actions/EditFile.tsx index 85ac4932229..b38682a715b 100644 --- a/packages/app-file-manager/src/components/Table/Actions/EditFile.tsx +++ b/packages/app-file-manager/src/components/Table/Actions/EditFile.tsx @@ -7,7 +7,7 @@ import { useFile } from "~/hooks/useFile.js"; export const EditFile = () => { const { file } = useFile(); const { showFileDetails } = useFileManagerView(); - const { OptionsMenuItem } = FileManagerViewConfig.Browser.FileAction; + const { OptionsMenuItem } = FileManagerViewConfig.Browser.File.Action; return ( { const { file } = useFile(); const moveFileToFolder = useMoveFileToFolder(file); - const { OptionsMenuItem } = FileManagerViewConfig.Browser.FileAction; + const { OptionsMenuItem } = FileManagerViewConfig.Browser.File.Action; return ( { const { useTableRow, isFolderRow } = FileManagerViewConfig.Browser.Table.Column; const { row } = useTableRow(); - const { folder: folderConfig, record: recordConfig } = useAcoConfig(); + const { browser } = useFileManagerViewConfig(); if (isFolderRow(row)) { // If the user cannot manage folder structure, no need to show the menu. @@ -18,7 +21,7 @@ export const CellActions = () => { return ( @@ -28,7 +31,7 @@ export const CellActions = () => { return ( diff --git a/packages/app-file-manager/src/components/Table/Cells/CellName.tsx b/packages/app-file-manager/src/components/Table/Cells/CellName.tsx index 7937d3a1167..b51ba0b158e 100644 --- a/packages/app-file-manager/src/components/Table/Cells/CellName.tsx +++ b/packages/app-file-manager/src/components/Table/Cells/CellName.tsx @@ -3,18 +3,18 @@ import React from "react"; import { FolderIcon, FolderSharedIcon } from "@webiny/app-aco"; import { useFileManagerView } from "~/modules/FileManagerRenderer/FileManagerViewProvider/index.js"; import { FileManagerViewConfig } from "~/modules/FileManagerRenderer/FileManagerView/FileManagerViewConfig.js"; -import type { FolderItem } from "@webiny/app-aco/types.js"; import { cn, Text } from "@webiny/admin-ui"; import { CellThumbnail } from "./CellThumbnail.js"; import { FileProvider } from "~/contexts/FileProvider.js"; import type { FileItem } from "@webiny/app-admin/types.js"; +import type { FolderDto } from "@webiny/app-aco"; interface DefaultProps { onClick: (id: string) => void; } interface FolderCellNameProps extends DefaultProps { - folder: FolderItem; + folder: FolderDto; } export const FolderCellName = ({ folder, onClick }: FolderCellNameProps) => { diff --git a/packages/app-file-manager/src/components/Table/Table.tsx b/packages/app-file-manager/src/components/Table/Table.tsx index 31278066261..b9ae475b1dd 100644 --- a/packages/app-file-manager/src/components/Table/Table.tsx +++ b/packages/app-file-manager/src/components/Table/Table.tsx @@ -3,6 +3,7 @@ import type { DataTableSorting, OnDataTableSortingChange } from "@webiny/admin-u import { createFoldersData, createRecordsData, Table as AcoTable } from "@webiny/app-aco"; import { useFileManagerView } from "~/modules/FileManagerRenderer/FileManagerViewProvider/index.js"; import type { TableItem } from "~/types.js"; +import { useFileManagerViewConfig } from "~/modules/FileManagerRenderer/FileManagerView/FileManagerViewConfig.js"; export interface TableProps { onSelectRow: ((rows: TableItem[] | []) => void) | undefined; @@ -13,6 +14,7 @@ export interface TableProps { export const Table = forwardRef((props, ref) => { const view = useFileManagerView(); + const { browser } = useFileManagerViewConfig(); const data = useMemo(() => { if (!view.displaySubFolders) { @@ -28,6 +30,7 @@ export const Table = forwardRef((props, ref) => { return (
+ columns={browser.table.columns} data={data} loading={view.isListLoading} onSelectRow={props.onSelectRow} diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/FileManagerView.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/FileManagerView.tsx index d9646812c67..d8ac70da96e 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/FileManagerView.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/FileManagerView.tsx @@ -223,6 +223,7 @@ const FileManagerView = () => { return ( ("FileManagerView"); @@ -29,13 +30,14 @@ interface FileManagerViewConfigData { export function useFileManagerViewConfig() { const config = base.useConfig(); + const acoConfig = useAcoConfig(config); const browser = config.browser || {}; const fileDetailsActions = [...(config.fileDetails?.actions || [])]; const fileDetailsThumbnails = [...(config.fileDetails?.thumbnails || [])]; - return useMemo( + const finalConfig = useMemo( () => ({ getThumbnailRenderer, browser: { @@ -52,7 +54,9 @@ export function useFileManagerViewConfig() { bulkEditFields: [...(browser.bulkEditFields || [])], filterByTags: browser.filterByTags ?? false, filters: [...(browser.filters || [])], - filtersToWhere: [...(browser.filtersToWhere || [])] + filtersToWhere: [...(browser.filtersToWhere || [])], + folder: acoConfig.folder, + file: acoConfig.record }, fileDetails: { actions: fileDetailsActions, @@ -64,4 +68,6 @@ export function useFileManagerViewConfig() { }), [config] ); + + return finalConfig; } diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/LeftSidebar.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/LeftSidebar.tsx index 1c40a534189..4d4dbeb138f 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/LeftSidebar.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/LeftSidebar.tsx @@ -1,6 +1,7 @@ import React, { useEffect } from "react"; import { Separator } from "@webiny/admin-ui"; -import { FolderTree, useGetFolderHierarchy, useListFoldersByParentIds } from "@webiny/app-aco"; +import { FolderTree, useLoadFolderHierarchy, useListFoldersByParentIds } from "@webiny/app-aco"; +import { useFileManagerViewConfig } from "~/modules/FileManagerRenderer/FileManagerView/FileManagerViewConfig.js"; interface LeftSidebarProps { currentFolder: string; @@ -9,12 +10,13 @@ interface LeftSidebarProps { } export const LeftSidebar = ({ currentFolder, onFolderClick, children }: LeftSidebarProps) => { - const { folders, getFolderHierarchy } = useGetFolderHierarchy(); + const { browser } = useFileManagerViewConfig(); + const { folders, loadFolderHierarchy } = useLoadFolderHierarchy(); const { listFoldersByParentIds } = useListFoldersByParentIds(); useEffect(() => { if (folders.length === 0) { - getFolderHierarchy(currentFolder); + loadFolderHierarchy(currentFolder); } else { // Otherwise let's load only the current folder sub-tree listFoldersByParentIds([currentFolder]); @@ -24,6 +26,8 @@ export const LeftSidebar = ({ currentFolder, onFolderClick, children }: LeftSide return (
onFolderClick(data.id)} enableActions={true} diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FileAction.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FileAction.tsx index 463df6bbdd9..9f0ef20f434 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FileAction.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FileAction.tsx @@ -8,11 +8,7 @@ export type { RecordActionConfig as FileActionConfig }; type FileActionProps = React.ComponentProps; const BaseFileAction = (props: FileActionProps) => { - return ( - - - - ); + return ; }; export const FileAction = Object.assign(BaseFileAction, { diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderAction.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderAction.tsx index 043881f95a8..e8cba14d570 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderAction.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderAction.tsx @@ -8,11 +8,7 @@ export type { FolderActionConfig }; type FolderActionProps = React.ComponentProps; const BaseFolderAction = (props: FolderActionProps) => { - return ( - - - - ); + return ; }; export const FolderAction = Object.assign(BaseFolderAction, { diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderDropConfirmation.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderDropConfirmation.tsx index 69ebde68b94..b6878cc9d72 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderDropConfirmation.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/FolderDropConfirmation.tsx @@ -6,9 +6,5 @@ const { Folder } = AcoConfig; type FolderDropConfirmationProps = React.ComponentProps; export const FolderDropConfirmation = (props: FolderDropConfirmationProps) => { - return ( - - - - ); + return ; }; diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/Table/Column.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/Table/Column.tsx index 63f722d8e5d..a6a3fadefc5 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/Table/Column.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/Table/Column.tsx @@ -10,11 +10,7 @@ export type { ColumnConfig }; type ColumnProps = React.ComponentProps; const BaseColumnComponent = (props: ColumnProps) => { - return ( - - - - ); + return ; }; const BaseColumn = makeDecoratable("Column", BaseColumnComponent); diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/index.ts b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/index.ts index 3219aa89811..4e28f748f51 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/index.ts +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/configComponents/Browser/index.ts @@ -6,9 +6,7 @@ import { Filter } from "./Filter.js"; import type { FiltersToWhereConverter } from "./FiltersToWhere.js"; import { FiltersToWhere } from "./FiltersToWhere.js"; import { FilterByTags } from "./FilterByTags.js"; -import type { FolderActionConfig } from "./FolderAction.js"; import { FolderAction } from "./FolderAction.js"; -import type { FileActionConfig } from "./FileAction.js"; import { FileAction } from "./FileAction.js"; import type { TableConfig } from "./Table/index.js"; import { Table } from "./Table/index.js"; @@ -21,6 +19,7 @@ import { ActionButton } from "~/components/Grid/ActionButton.js"; import { File } from "~/components/Grid/File.js"; import { shouldDecorateFolderField } from "./FolderFieldDecorator.js"; import { FolderDropConfirmation } from "./FolderDropConfirmation.js"; +import { RecordConfig } from "@webiny/app-aco/config/record"; export interface BrowserConfig { bulkActions: BulkActionConfig[]; @@ -28,10 +27,9 @@ export interface BrowserConfig { filters: FilterConfig[]; filtersToWhere: FiltersToWhereConverter[]; filterByTags: boolean; - folderActions: FolderActionConfig[]; - fileActions: FileActionConfig[]; table: TableConfig; grid: GridConfig; + file: RecordConfig; } export const Browser = { @@ -56,11 +54,8 @@ export const Browser = { Action: FolderAction, DropConfirmation: FolderDropConfirmation }, - /** - * @deprecated - * Use `Browser.Folder.Action` instead - */ - FolderAction, - FileAction, + File: { + Action: FileAction + }, Table }; diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/index.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/index.tsx index f1c2fa8d967..99c5b7f02f2 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/index.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerView/index.tsx @@ -4,8 +4,7 @@ import type { FileManagerFileItem, FileManagerOnChange } from "@webiny/app-admin import { DialogsProvider, FileManagerRenderer as BaseFileManagerRenderer } from "@webiny/app-admin"; import type { FileItem } from "@webiny/app-admin/types.js"; import { FoldersProvider } from "@webiny/app-aco/contexts/folders.js"; -import { AcoWithConfig, NavigateFolderProvider } from "@webiny/app-aco"; -import { CompositionScope } from "@webiny/react-composition"; +import { NavigateFolderProvider } from "@webiny/app-aco"; import FileManagerView from "./FileManagerView.js"; import type { FileManagerViewProviderProps } from "~/modules/FileManagerRenderer/FileManagerViewProvider/index.js"; import { FileManagerViewProvider } from "~/modules/FileManagerRenderer/FileManagerViewProvider/index.js"; @@ -58,7 +57,6 @@ export function FileManagerProvider({ ...props }: FileManagerProviderProps) { const mimeTypes = images ? accept || imagesAccept : accept || []; - const [folderId, setFolderId] = useState(undefined); const navigateToFolder = useCallback((folderId: string) => { @@ -66,23 +64,19 @@ export function FileManagerProvider({ }, []); return ( - - - - - - - {children} - - - - - - + + + + + {children} + + + + ); } diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerViewProvider/FileManagerViewContext.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerViewProvider/FileManagerViewContext.tsx index 507b39b3d10..65431e4e6fc 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerViewProvider/FileManagerViewContext.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/FileManagerViewProvider/FileManagerViewContext.tsx @@ -6,7 +6,7 @@ import type { FileTag } from "~/types.js"; import { useFileManagerApi } from "~/index.js"; import type { State } from "./state.js"; import { initializeState } from "./state.js"; -import type { FolderItem, ListMeta, ListSearchRecordsSort } from "@webiny/app-aco/types.js"; +import type { ListMeta, ListSearchRecordsSort } from "@webiny/app-aco/types.js"; import type { UploadOptions } from "@webiny/app/types.js"; import { sortTableItems } from "@webiny/app-aco/sorting.js"; import { useListFolders, useNavigateFolder } from "@webiny/app-aco"; @@ -15,6 +15,7 @@ import { useListFiles } from "./useListFiles.js"; import { useTags } from "./useTags.js"; import { setSelection } from "./setSelection.js"; import { ROOT_FOLDER } from "~/constants.js"; +import type { FolderDto } from "@webiny/app-aco"; type PublicState = Omit; @@ -28,12 +29,12 @@ export interface DeleteFileOptions { export interface FileManagerViewContext extends PublicState { accept: string[]; - currentFolder: FolderItem | null; + currentFolder: FolderDto | null; createFile: (data: TFileItem) => Promise; deleteFile: (id: string, options?: DeleteFileOptions) => Promise; files: FileItem[]; folderId: string; - folders: FolderItem[]; + folders: FolderDto[]; displaySubFolders: boolean; getFile: (id: string) => Promise; hideFileDetails: () => void; @@ -87,9 +88,9 @@ export const FileManagerViewContext = React.createContext { +): FolderDto[] | [] => { if (!folders) { return []; } diff --git a/packages/app-file-manager/src/modules/FileManagerRenderer/index.tsx b/packages/app-file-manager/src/modules/FileManagerRenderer/index.tsx index 0753cffe3a5..f7ef4e5077d 100644 --- a/packages/app-file-manager/src/modules/FileManagerRenderer/index.tsx +++ b/packages/app-file-manager/src/modules/FileManagerRenderer/index.tsx @@ -42,14 +42,14 @@ export const FileManagerRendererModule = () => { } /> } /> {/* Folder Actions */} - } /> - } /> - } /> + } /> + } /> + } /> {/* File Actions */} - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> {/* Table Columns */} & { onClose: () => void; }; export const SchedulerRenderer = ({ title = "Scheduler", ...props }: SchedulerRendererProps) => { - const { table } = useAcoConfig(); + const { browser } = useSchedulerListConfig(); - if (!table.sorting?.length) { + if (!browser.table.sorting?.length) { return null; } @@ -19,7 +19,7 @@ export const SchedulerRenderer = ({ title = "Scheduler", ...props }: SchedulerRe Sorting.create(sort))} + sorting={browser.table.sorting.map(sort => Sorting.create(sort))} /> ); }; diff --git a/packages/app-headless-cms-scheduler/src/Presentation/components/Cells/CellActions/CellActions.tsx b/packages/app-headless-cms-scheduler/src/Presentation/components/Cells/CellActions/CellActions.tsx index ae2f2ec67a3..fcecf55e5df 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/components/Cells/CellActions/CellActions.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/components/Cells/CellActions/CellActions.tsx @@ -1,18 +1,17 @@ import React from "react"; -import { useAcoConfig } from "@webiny/app-aco"; import { OptionsMenu } from "@webiny/app-admin"; -import { SchedulerListConfig } from "~/Presentation/configs/index.js"; +import { SchedulerListConfig, useSchedulerListConfig } from "~/Presentation/configs/index.js"; import { SchedulerItemProvider } from "~/Presentation/hooks/index.js"; export const CellActions = () => { const { useTableRow } = SchedulerListConfig.Browser.Table.Column; const { row } = useTableRow(); - const { record: recordConfig } = useAcoConfig(); + const { browser } = useSchedulerListConfig(); return ( diff --git a/packages/app-headless-cms-scheduler/src/Presentation/components/Table/Table.tsx b/packages/app-headless-cms-scheduler/src/Presentation/components/Table/Table.tsx index 8b05d0149ec..093e87d8568 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/components/Table/Table.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/components/Table/Table.tsx @@ -2,9 +2,11 @@ import React, { useMemo } from "react"; import { createRecordsData, Table as AcoTable } from "@webiny/app-aco"; import { useScheduler } from "~/Presentation/hooks/index.js"; import { LoadingActions, type SchedulerEntryTableRow } from "~/types.js"; +import { useSchedulerListConfig } from "~/Presentation/configs/index.js"; export const Table = () => { const { vm, selectItems, sortItems } = useScheduler(); + const { browser } = useSchedulerListConfig(); const data = useMemo(() => { return createRecordsData(vm.items); @@ -16,6 +18,7 @@ export const Table = () => { return ( + columns={browser.table.columns} data={data} loading={vm.loading[LoadingActions.list]} onSelectRow={entries => selectItems(entries.map(entry => entry.data))} diff --git a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/EntryAction.tsx b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/EntryAction.tsx index 41908fe1dae..f2d61c052d8 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/EntryAction.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/EntryAction.tsx @@ -11,9 +11,7 @@ type EntryActionProps = React.ComponentProps; const BaseEntryAction = (props: EntryActionProps) => { return ( - - - + ); }; diff --git a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Column.tsx b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Column.tsx index c97f05b34b0..7b959b08fca 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Column.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Column.tsx @@ -9,11 +9,7 @@ export type { ColumnConfig }; type ColumnProps = React.ComponentProps; const BaseColumn = (props: ColumnProps) => { - return ( - - - - ); + return ; }; export const Column = Object.assign(BaseColumn, { diff --git a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Sorting.tsx b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Sorting.tsx index e153d63dbcc..2f22e19dd74 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Sorting.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/Browser/Table/Sorting.tsx @@ -8,9 +8,5 @@ export type { SortingConfig }; type SortingProps = React.ComponentProps; export const Sorting = (props: SortingProps) => { - return ( - - - - ); + return ; }; diff --git a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/SchedulerListConfig.tsx b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/SchedulerListConfig.tsx index bb60d16cb87..43c7cdd696e 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/configs/list/SchedulerListConfig.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/configs/list/SchedulerListConfig.tsx @@ -3,6 +3,7 @@ import { createConfigurableComponent } from "@webiny/react-properties"; import type { BrowserConfig } from "./Browser/index.js"; import { Browser } from "./Browser/index.js"; import { CompositionScope } from "@webiny/react-composition"; +import { useAcoConfig } from "@webiny/app-aco"; const base = createConfigurableComponent("SchedulerListConfig"); @@ -25,13 +26,13 @@ interface SchedulerListConfig { export function useSchedulerListConfig() { const config = base.useConfig(); - - const browser = config.browser || {}; + const acoConfig = useAcoConfig(config); return useMemo( () => ({ browser: { - ...browser, + table: acoConfig.table, + entryActions: acoConfig.record.actions, bulkActions: [] } }), diff --git a/packages/app-headless-cms-scheduler/src/Presentation/index.tsx b/packages/app-headless-cms-scheduler/src/Presentation/index.tsx index 8f0972ffc4c..b959a58d4e9 100644 --- a/packages/app-headless-cms-scheduler/src/Presentation/index.tsx +++ b/packages/app-headless-cms-scheduler/src/Presentation/index.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useState } from "react"; -import { AcoWithConfig } from "@webiny/app-aco"; import { SchedulerRenderer } from "~/Presentation/SchedulerRenderer/index.js"; import { CompositionScope } from "@webiny/react-composition"; import { SchedulerListWithConfig } from "~/Presentation/configs/index.js"; @@ -60,11 +59,9 @@ export const Scheduler = ({ render, ...rest }: SchedulerProps) => { <> {show && ( - - - - - + + + )} {render ? render({ showScheduler }) : null} diff --git a/packages/app-headless-cms-workflows/src/Components/OptionItem/OpenInNewWindow.tsx b/packages/app-headless-cms-workflows/src/Components/OptionItem/OpenInNewWindow.tsx index 87f5da1a593..f2745ba2635 100644 --- a/packages/app-headless-cms-workflows/src/Components/OptionItem/OpenInNewWindow.tsx +++ b/packages/app-headless-cms-workflows/src/Components/OptionItem/OpenInNewWindow.tsx @@ -4,11 +4,11 @@ import { Components } from "@webiny/app-workflows"; import { ReactComponent as OpenInNewIcon } from "@webiny/icons/open_in_new.svg"; import { useRouter } from "@webiny/app"; import { Routes } from "@webiny/app-headless-cms/routes.js"; -import { parseAppName } from "~/utils/appName.js"; +import { isCmsAppName, parseAppName } from "~/utils/appName.js"; const { OpenInNewWindow } = Components.List.Options; -export const ListOpenInNewWindow = OpenInNewWindow.createDecorator(() => { +export const ListOpenInNewWindow = OpenInNewWindow.createDecorator(Original => { return function ListOpenInNewWindow(props) { const { state } = props; @@ -27,6 +27,10 @@ export const ListOpenInNewWindow = OpenInNewWindow.createDecorator(() => { window.open(goTo, "_blank"); }, [state.id]); + if (isCmsAppName(state.app) === false) { + return ; + } + return ( } label={"Open In New Window"} />} diff --git a/packages/app-headless-cms-workflows/src/utils/appName.ts b/packages/app-headless-cms-workflows/src/utils/appName.ts index dbf088869fa..7e582632a13 100644 --- a/packages/app-headless-cms-workflows/src/utils/appName.ts +++ b/packages/app-headless-cms-workflows/src/utils/appName.ts @@ -11,3 +11,8 @@ export const parseAppName = (appName: string): string => { } return parts[1]; }; + +export const isCmsAppName = (appName: string): boolean => { + const parts = appName.split("."); + return parts.length === 2 && parts[0] === "cms"; +}; diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/BulkActions/ActionMove.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/BulkActions/ActionMove.tsx index 10f2eee5595..19fba43a89c 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/BulkActions/ActionMove.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/BulkActions/ActionMove.tsx @@ -23,7 +23,6 @@ export const ActionMove = observer(() => { const openWorkerDialog = useCallback( (folder: NodeDto) => { - console.log("folder", folder); showConfirmationDialog({ title: "Move entries", message: `You are about to move ${entriesLabel} to ${folder.label}. Are you sure you want to continue?`, diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Filters/Filters.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Filters/Filters.tsx index 18cbf3b0d02..8e80729c06c 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Filters/Filters.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Filters/Filters.tsx @@ -55,6 +55,7 @@ export const Filters = () => { diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/SidebarContent/SidebarContent.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/SidebarContent/SidebarContent.tsx index 297b4d7defe..3481f08e3cc 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/SidebarContent/SidebarContent.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/SidebarContent/SidebarContent.tsx @@ -1,12 +1,15 @@ import React from "react"; import { FolderTree, useNavigateFolder } from "@webiny/app-aco"; +import { useContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; export const SidebarContent = () => { + const { browser } = useContentEntryListConfig(); const { navigateToFolder, currentFolderId } = useNavigateFolder(); return (
navigateToFolder(data.id)} enableActions={true} diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/ChangeEntryStatus.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/ChangeEntryStatus.tsx index fd796f4a7df..57e9e385ebc 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/ChangeEntryStatus.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/ChangeEntryStatus.tsx @@ -8,7 +8,7 @@ export const ChangeEntryStatus = () => { const { entry } = useEntry(); const { canPublish, canUnpublish } = usePermission(); const { publishEntryRevision, unpublishEntryRevision } = useContentEntry(); - const { OptionsMenuItem } = ContentEntryListConfig.Browser.EntryAction; + const { OptionsMenuItem } = ContentEntryListConfig.Browser.Entry.Action; if (entry.meta.status === "published" && canUnpublish("cms.contentEntry")) { return ( diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/DeleteEntry.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/DeleteEntry.tsx index 6c3759f09d2..5e06b5157fa 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/DeleteEntry.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/DeleteEntry.tsx @@ -7,7 +7,7 @@ export const DeleteEntry = () => { const { entry } = useEntry(); const contentEntry = useContentEntry(); const { canDelete } = usePermission(); - const { OptionsMenuItem } = ContentEntryListConfig.Browser.EntryAction; + const { OptionsMenuItem } = ContentEntryListConfig.Browser.Entry.Action; const deleteEntry = async () => { await contentEntry.deleteEntry({ id: entry.entryId }); diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/EditEntry.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/EditEntry.tsx index c5f6c9e2f73..68d19a2d71c 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/EditEntry.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/EditEntry.tsx @@ -7,7 +7,7 @@ export const EditEntry = () => { const { entry } = useEntry(); const { canEdit } = usePermission(); const { getEntryEditUrl } = useContentEntriesList(); - const { OptionsMenuLink } = ContentEntryListConfig.Browser.EntryAction; + const { OptionsMenuLink } = ContentEntryListConfig.Browser.Entry.Action; if (!canEdit(entry, "cms.contentEntry")) { return null; diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/MoveEntry.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/MoveEntry.tsx index fff18f2c824..1dc1aa9c8cc 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/MoveEntry.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Actions/MoveEntry.tsx @@ -6,7 +6,7 @@ import { useEntry, useMoveContentEntryToFolder } from "~/admin/hooks/index.js"; export const MoveEntry = () => { const { entry: record } = useEntry(); const moveContentEntry = useMoveContentEntryToFolder({ record }); - const { OptionsMenuItem } = ContentEntryListConfig.Browser.EntryAction; + const { OptionsMenuItem } = ContentEntryListConfig.Browser.Entry.Action; return ( { const { useTableRow, isFolderRow } = ContentEntryListConfig.Browser.Table.Column; const { row } = useTableRow(); - const { folder: folderConfig, record: recordConfig } = useAcoConfig(); + const { browser } = useContentEntryListConfig(); if (isFolderRow(row)) { // If the user cannot manage folder structure, no need to show the menu. @@ -18,7 +21,7 @@ const DefaultCellActions = () => { return ( @@ -28,7 +31,7 @@ const DefaultCellActions = () => { return ( diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Cells/CellName.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Cells/CellName.tsx index 13ded9ca5a5..fc65de1d9be 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Cells/CellName.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/Cells/CellName.tsx @@ -1,4 +1,5 @@ import React from "react"; +import type { FolderDto } from "@webiny/app-aco"; import { Icon, Link, Text } from "@webiny/admin-ui"; import { ReactComponent as Folder } from "@webiny/icons/folder.svg"; @@ -10,11 +11,10 @@ import { ContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; import { useContentEntriesList } from "~/admin/views/contentEntries/hooks/index.js"; import { usePermission } from "~/admin/hooks/index.js"; -import type { FolderItem } from "@webiny/app-aco/types.js"; import type { CmsContentEntry } from "~/types.js"; interface FolderCellNameProps { - folder: FolderItem; + folder: FolderDto; } export const FolderCellName = ({ folder }: FolderCellNameProps) => { diff --git a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/index.tsx b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/index.tsx index 9b0924b2f5c..69b173477f4 100644 --- a/packages/app-headless-cms/src/admin/components/ContentEntries/Table/index.tsx +++ b/packages/app-headless-cms/src/admin/components/ContentEntries/Table/index.tsx @@ -3,9 +3,11 @@ import React, { useMemo } from "react"; import { Table as AcoTable, createRecordsData, createFoldersData } from "@webiny/app-aco"; import { useContentEntriesList, useModel } from "~/admin/hooks/index.js"; import type { TableItem } from "~/types.js"; +import { useContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; const BaseTable: ForwardRefRenderFunction = (_, ref) => { const { model } = useModel(); + const { browser } = useContentEntryListConfig(); const list = useContentEntriesList(); const data = useMemo(() => { @@ -19,6 +21,7 @@ const BaseTable: ForwardRefRenderFunction = (_, ref) => { return (
+ columns={browser.table.columns} data={data} nameColumnId={model.titleFieldId || "id"} namespace={`cms.${model.modelId}`} diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/AdvancedSearch/FieldRenderer.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/AdvancedSearch/FieldRenderer.tsx index 161bd346b96..80e4cfc02d0 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/AdvancedSearch/FieldRenderer.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/AdvancedSearch/FieldRenderer.tsx @@ -21,11 +21,7 @@ const BaseFieldRenderer = ({ modelIds = [], ...props }: FieldRendererProps) => { return null; } - return ( - - - - ); + return ; }; export const FieldRenderer = Object.assign(BaseFieldRenderer, { diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/EntryAction.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/EntryAction.tsx index 303a1f5473d..3840b562193 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/EntryAction.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/EntryAction.tsx @@ -15,11 +15,9 @@ const BaseEntryAction = makeDecoratable( "EntryAction", ({ modelIds = [], ...props }: EntryActionProps) => { return ( - - - - - + + + ); } ); diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderAction.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderAction.tsx index 77027777d5d..8c53428468b 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderAction.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderAction.tsx @@ -17,11 +17,7 @@ const BaseFolderAction = ({ modelIds = [], ...props }: FolderActionProps) => { return null; } - return ( - - - - ); + return ; }; export const FolderAction = Object.assign(BaseFolderAction, { diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderDropConfirmation.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderDropConfirmation.tsx index 5b8da0d25bd..1fb379abfaf 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderDropConfirmation.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/FolderDropConfirmation.tsx @@ -19,9 +19,5 @@ export const FolderDropConfirmation = ({ return null; } - return ( - - - - ); + return ; }; diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/Table/Column.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/Table/Column.tsx index 55392da9326..eeee2ef8d79 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/Table/Column.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/Table/Column.tsx @@ -14,11 +14,9 @@ export interface ColumnProps extends React.ComponentProps { return ( - - - - - + + + ); }; diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/index.ts b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/index.ts index 1ed8eeb663b..304ea4f28bc 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/index.ts +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/Browser/index.ts @@ -3,35 +3,35 @@ import type { AdvancedSearchConfig } from "./AdvancedSearch/index.js"; import { AdvancedSearch } from "./AdvancedSearch/index.js"; import type { BulkActionConfig } from "./BulkAction.js"; import { BulkAction } from "./BulkAction.js"; -import type { EntryActionConfig } from "./EntryAction.js"; import { EntryAction } from "./EntryAction.js"; import type { FilterConfig } from "./Filter.js"; import { Filter } from "./Filter.js"; import type { FiltersToWhereConverter } from "./FiltersToWhere.js"; import { FiltersToWhere } from "./FiltersToWhere.js"; -import type { FolderActionConfig } from "./FolderAction.js"; import { FolderAction } from "./FolderAction.js"; import type { TableConfig } from "./Table/index.js"; import { Table } from "./Table/index.js"; import { shouldDecorateFolderField } from "./FolderFieldDecorator.js"; import { FolderDropConfirmation } from "./FolderDropConfirmation.js"; +import { FolderConfig } from "@webiny/app-aco/config/folder"; +import { RecordConfig } from "@webiny/app-aco/config/record"; export interface BrowserConfig { advancedSearch: AdvancedSearchConfig; + table: TableConfig; + folder: FolderConfig[]; + entry: RecordConfig[]; bulkActions: BulkActionConfig[]; - entryActions: EntryActionConfig[]; filters: FilterConfig[]; filtersToWhere: FiltersToWhereConverter[]; - folderActions: FolderActionConfig[]; - table: TableConfig; } export const Browser = { AdvancedSearch, BulkAction, - EntryAction, Filter, FiltersToWhere, + Table, Folder: { ExtensionField: { createDecorator: createFolderFieldDecoratorFactory({ @@ -42,10 +42,7 @@ export const Browser = { Action: FolderAction, DropConfirmation: FolderDropConfirmation }, - Table, - /** - * @deprecated - * Use `Browser.Folder.Action` instead - */ - FolderAction + Entry: { + Action: EntryAction + } }; diff --git a/packages/app-headless-cms/src/admin/config/contentEntries/list/ContentEntryListConfig.tsx b/packages/app-headless-cms/src/admin/config/contentEntries/list/ContentEntryListConfig.tsx index 57b8660c8f4..c50fe4853e9 100644 --- a/packages/app-headless-cms/src/admin/config/contentEntries/list/ContentEntryListConfig.tsx +++ b/packages/app-headless-cms/src/admin/config/contentEntries/list/ContentEntryListConfig.tsx @@ -3,20 +3,34 @@ import { createConfigurableComponent } from "@webiny/react-properties"; import type { BrowserConfig } from "./Browser/index.js"; import { Browser } from "./Browser/index.js"; import { CompositionScope } from "@webiny/react-composition"; +import { useAcoConfig } from "@webiny/app-aco"; const base = createConfigurableComponent("ContentEntryListConfig"); -const ScopedContentEntryListConfig = ({ children }: { children: React.ReactNode }) => { +const InternalEntryListConfig = ({ children }: { children: React.ReactNode }) => { return ( - {children} + {children} ); }; -ScopedContentEntryListConfig.displayName = "ContentEntryListConfig"; +const PublicContentEntryListConfig = ({ children }: { children: React.ReactNode }) => { + return ( + + {children} + + ); +}; + +PublicContentEntryListConfig.displayName = "ContentEntryListConfig"; + +/* This one is a public API for other apps and third party developers. */ +export const ContentEntryListConfig = Object.assign(PublicContentEntryListConfig, { Browser }); + +/* This one is an internal API for the base app. It ensures this config is always applied first. */ +export const InternalContentEntryListConfig = Object.assign(InternalEntryListConfig, { Browser }); -export const ContentEntryListConfig = Object.assign(ScopedContentEntryListConfig, { Browser }); export const ContentEntryListWithConfig = base.WithConfig; interface ContentEntryListConfig { @@ -25,13 +39,17 @@ interface ContentEntryListConfig { export function useContentEntryListConfig() { const config = base.useConfig(); + const acoConfig = useAcoConfig(config); const browser = config.browser || {}; return useMemo( () => ({ browser: { - ...browser, + advancedSearch: acoConfig.advancedSearch, + table: acoConfig.table, + folder: acoConfig.folder, + entry: acoConfig.record, bulkActions: [...(browser.bulkActions || [])], filters: [...(browser.filters || [])], filtersToWhere: [...(browser.filtersToWhere || [])] diff --git a/packages/app-headless-cms/src/admin/plugins/fieldRenderers/ref/components/NewReferencedEntryDialog.tsx b/packages/app-headless-cms/src/admin/plugins/fieldRenderers/ref/components/NewReferencedEntryDialog.tsx index 15f30875195..8e68a34388e 100644 --- a/packages/app-headless-cms/src/admin/plugins/fieldRenderers/ref/components/NewReferencedEntryDialog.tsx +++ b/packages/app-headless-cms/src/admin/plugins/fieldRenderers/ref/components/NewReferencedEntryDialog.tsx @@ -15,15 +15,16 @@ import { GET_CONTENT_MODEL } from "~/admin/graphql/contentModels.js"; import { useCms } from "~/admin/hooks/index.js"; import { NavigateFolderProvider as AbstractNavigateFolderProvider, - SearchRecordsProvider + useLoadFolderHierarchy } from "@webiny/app-aco"; import { FolderTree, useNavigateFolder } from "@webiny/app-aco"; -import { SplitView, LeftPanel, RightPanel } from "@webiny/app-admin"; +import { SplitView, LeftPanel, RightPanel, DialogsProvider } from "@webiny/app-admin"; import { usePersistEntry } from "~/admin/hooks/usePersistEntry.js"; import type { AcoAppProviderContext } from "@webiny/app-aco/contexts/app.js"; import { AcoAppContext, createAppFromModel } from "@webiny/app-aco/contexts/app.js"; import { Drawer, OverlayLoader } from "@webiny/admin-ui"; import { ROOT_FOLDER } from "~/admin/constants.js"; +import { useContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; const t = i18n.ns("app-headless-cms/admin/fields/ref"); @@ -40,6 +41,15 @@ const EntryForm = ({ onCreate, setSaveEntry }: EntryFormProps) => { const { contentModel, loading } = useContentEntry(); const { persistEntry } = usePersistEntry({ addItemToListCache: false }); const { currentFolderId, navigateToFolder } = useNavigateFolder(); + const { browser } = useContentEntryListConfig(); + const { folders, loadFolderHierarchy } = useLoadFolderHierarchy(); + + useEffect(() => { + // The folders collection is empty, it must be the first render, let's load the full hierarchy. + if (folders.length === 0) { + loadFolderHierarchy(currentFolderId); + } + }, []); return ( @@ -47,6 +57,7 @@ const EntryForm = ({ onCreate, setSaveEntry }: EntryFormProps) => {
navigateToFolder(data.id)} enableActions={true} @@ -138,7 +149,9 @@ export const NewReferencedEntryDialog = ({ return ( - + {/* We need to mount to render dialog content in the right context. */} + {/* Otherwise, you'll see odd behavior with FolderTree, loading folders of the wrong type. */} + - + ); diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntries.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntries.tsx index df86ee7bf0f..a65a6a89fd5 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntries.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntries.tsx @@ -1,6 +1,5 @@ import React from "react"; import { DialogsProvider, makeDecoratable } from "@webiny/app-admin"; -import { AcoWithConfig } from "@webiny/app-aco"; import { Table as CmsAcoTable } from "./Table/index.js"; import { useModel } from "~/admin/components/ModelProvider/index.js"; import { @@ -22,13 +21,11 @@ export const ContentEntries = makeDecoratable("ContentEntries", () => { - - - - - - - + + + + + diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesDebounceRender.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesDebounceRender.tsx index 96c63925272..3d137a38dfd 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesDebounceRender.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesDebounceRender.tsx @@ -1,7 +1,6 @@ import React from "react"; import { useEffect, useMemo, useState } from "react"; import debounce from "lodash/debounce.js"; -import { useAcoConfig } from "@webiny/app-aco"; import { useContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; interface Props { @@ -18,7 +17,6 @@ interface Props { */ export const ContentEntriesDebounceRenderer = ({ children }: Props) => { const [render, setRender] = useState(false); - const acoConfigs = useAcoConfig(); const entryListConfigs = useContentEntryListConfig(); const entryEditorConfigs = useContentEntryListConfig(); @@ -38,7 +36,7 @@ export const ContentEntriesDebounceRenderer = ({ children }: Props) => { return () => { debouncedRender.cancel(); }; - }, [acoConfigs, entryListConfigs, entryEditorConfigs]); + }, [entryListConfigs, entryEditorConfigs]); return <>{render ? children : null}; }; diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesModule.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesModule.tsx index 6506abaf614..6de6d78d1eb 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesModule.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/ContentEntriesModule.tsx @@ -1,7 +1,7 @@ import React from "react"; import { ContentEntryEditorConfig, - ContentEntryListConfig + InternalContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; import { @@ -40,26 +40,28 @@ import { FullScreenContentEntry } from "~/admin/views/contentEntries/ContentEntr import { ShowRevisionList } from "~/admin/components/ContentEntryForm/Header/ShowRevisionsList/index.js"; import { cmsLegacyEntryEditor } from "~/utils/cmsLegacyEntryEditor.js"; import { ScheduleEntryMenuItem } from "~/admin/components/ContentEntries/Scheduler/actions/ScheduleEntryAction.js"; +import { AdvancedSearchConfigs } from "@webiny/app-aco/components/AdvancedSearch/AdvancedSearchConfigs"; -const { Browser } = ContentEntryListConfig; +const { Browser } = InternalContentEntryListConfig; const { Actions } = ContentEntryEditorConfig; export const ContentEntriesModule = () => { return ( <> - + + } /> } /> } /> } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> { element={} type={Browser.AdvancedSearch.FieldRenderer.FieldType.REF} /> - + diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/Table/index.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/Table/index.tsx index 791c7151dfd..66318d16e8c 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/Table/index.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/Table/index.tsx @@ -9,6 +9,7 @@ import { useApolloClient, useModel } from "~/admin/hooks/index.js"; import { ContentEntriesListProvider } from "~/admin/views/contentEntries/hooks/index.js"; import { LOCAL_STORAGE_LATEST_VISITED_FOLDER } from "~/admin/constants.js"; import { Routes } from "~/routes.js"; +import { useContentEntryListConfig } from "~/admin/config/contentEntries/index.js"; /** * Generates a `layoutId` to be used with the `` component. @@ -51,6 +52,7 @@ export const Table = () => { const { model } = useModel(); const client = useApolloClient(); const { goToRoute } = useRouter(); + const { browser } = useContentEntryListConfig(); const { route } = useRoute(Routes.ContentEntries.List); const navigateToFolder = useCallback( @@ -77,6 +79,7 @@ export const Table = () => { model={model} navigateToFolder={navigateToFolder} createNavigateFolderStorageKey={createNavigateFolderStorageKey} + columns={browser.table.columns ?? []} > diff --git a/packages/app-headless-cms/src/admin/views/contentEntries/hooks/useContentEntriesList.tsx b/packages/app-headless-cms/src/admin/views/contentEntries/hooks/useContentEntriesList.tsx index 78817e67a0b..43d2aaf9198 100644 --- a/packages/app-headless-cms/src/admin/views/contentEntries/hooks/useContentEntriesList.tsx +++ b/packages/app-headless-cms/src/admin/views/contentEntries/hooks/useContentEntriesList.tsx @@ -4,12 +4,12 @@ import { useRoute, useRouter } from "@webiny/app-admin"; import { makeDecoratable } from "@webiny/react-composition"; import { createSort, useAcoList, useNavigateFolder } from "@webiny/app-aco"; import type { ListMeta } from "@webiny/app-aco"; -import type { FolderItem } from "@webiny/app-aco/types.js"; import { useContentEntries } from "./useContentEntries.js"; import type { CmsContentEntry, TableItem } from "~/types.js"; import type { OnSortingChange, Sorting } from "@webiny/ui/DataTable/index.js"; import { ROOT_FOLDER } from "~/admin/constants.js"; import { Routes } from "~/routes.js"; +import type { FolderDto } from "@webiny/app-aco"; interface UpdateSearchCallableParams { search: string; @@ -22,7 +22,7 @@ export interface ContentEntriesListProviderContext { modelId: string; folderId: string; navigateTo: (folderId?: string) => void; - folders: FolderItem[]; + folders: FolderDto[]; getEntryEditUrl: (item: CmsContentEntry) => string; hideFilters: () => void; isListLoading: boolean; diff --git a/packages/app-serverless-cms/package.json b/packages/app-serverless-cms/package.json index b14a78bd2a5..938af51ac7e 100644 --- a/packages/app-serverless-cms/package.json +++ b/packages/app-serverless-cms/package.json @@ -28,6 +28,7 @@ "@webiny/app-security-access-management": "0.0.0", "@webiny/app-trash-bin": "0.0.0", "@webiny/app-website-builder": "0.0.0", + "@webiny/app-website-builder-workflows": "0.0.0", "@webiny/app-websockets": "0.0.0", "@webiny/app-workflows": "0.0.0", "@webiny/lexical-editor-actions": "0.0.0", diff --git a/packages/app-serverless-cms/src/Admin.tsx b/packages/app-serverless-cms/src/Admin.tsx index e0087e5fa78..c166b267cf0 100644 --- a/packages/app-serverless-cms/src/Admin.tsx +++ b/packages/app-serverless-cms/src/Admin.tsx @@ -23,6 +23,7 @@ import { Extension as WebsiteBuilder } from "@webiny/app-website-builder/Extensi import { SchedulerConfigs } from "@webiny/app-headless-cms-scheduler"; import { Components as WorkflowComponents } from "@webiny/app-workflows"; import { CmsWorkflows } from "@webiny/app-headless-cms-workflows"; +import { WebsiteBuilderWorkflows } from "@webiny/app-website-builder-workflows"; export interface AdminProps extends Omit { createApolloClient?: BaseAdminProps["createApolloClient"]; @@ -54,6 +55,7 @@ const App = (props: AdminProps) => { + {props.children} ); diff --git a/packages/app-serverless-cms/tsconfig.build.json b/packages/app-serverless-cms/tsconfig.build.json index 756cdb96719..a4a1a85ebcf 100644 --- a/packages/app-serverless-cms/tsconfig.build.json +++ b/packages/app-serverless-cms/tsconfig.build.json @@ -19,6 +19,7 @@ { "path": "../app-security-access-management/tsconfig.build.json" }, { "path": "../app-trash-bin/tsconfig.build.json" }, { "path": "../app-website-builder/tsconfig.build.json" }, + { "path": "../app-website-builder-workflows/tsconfig.build.json" }, { "path": "../app-websockets/tsconfig.build.json" }, { "path": "../app-workflows/tsconfig.build.json" }, { "path": "../lexical-editor-actions/tsconfig.build.json" }, @@ -72,6 +73,8 @@ "@webiny/app-trash-bin": ["../app-trash-bin/src"], "@webiny/app-website-builder/*": ["../app-website-builder/src/*"], "@webiny/app-website-builder": ["../app-website-builder/src"], + "@webiny/app-website-builder-workflows/*": ["../app-website-builder-workflows/src/*"], + "@webiny/app-website-builder-workflows": ["../app-website-builder-workflows/src"], "@webiny/app-websockets/*": ["../app-websockets/src/*"], "@webiny/app-websockets": ["../app-websockets/src"], "@webiny/app-workflows/*": ["../app-workflows/src/*"], diff --git a/packages/app-serverless-cms/tsconfig.json b/packages/app-serverless-cms/tsconfig.json index be7d471f63a..c77c3bd1746 100644 --- a/packages/app-serverless-cms/tsconfig.json +++ b/packages/app-serverless-cms/tsconfig.json @@ -19,6 +19,7 @@ { "path": "../app-security-access-management" }, { "path": "../app-trash-bin" }, { "path": "../app-website-builder" }, + { "path": "../app-website-builder-workflows" }, { "path": "../app-websockets" }, { "path": "../app-workflows" }, { "path": "../lexical-editor-actions" }, @@ -72,6 +73,8 @@ "@webiny/app-trash-bin": ["../app-trash-bin/src"], "@webiny/app-website-builder/*": ["../app-website-builder/src/*"], "@webiny/app-website-builder": ["../app-website-builder/src"], + "@webiny/app-website-builder-workflows/*": ["../app-website-builder-workflows/src/*"], + "@webiny/app-website-builder-workflows": ["../app-website-builder-workflows/src"], "@webiny/app-websockets/*": ["../app-websockets/src/*"], "@webiny/app-websockets": ["../app-websockets/src"], "@webiny/app-workflows/*": ["../app-workflows/src/*"], diff --git a/packages/app-trash-bin/src/Presentation/TrashBinRenderer/TrashBinRenderer.tsx b/packages/app-trash-bin/src/Presentation/TrashBinRenderer/TrashBinRenderer.tsx index 710356f44e0..338fbeae016 100644 --- a/packages/app-trash-bin/src/Presentation/TrashBinRenderer/TrashBinRenderer.tsx +++ b/packages/app-trash-bin/src/Presentation/TrashBinRenderer/TrashBinRenderer.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { useAcoConfig } from "@webiny/app-aco"; import { Sorting } from "@webiny/app-utils"; import type { TrashBinItemDTO } from "~/Domain/index.js"; import type { TrashBinProps } from "~/Presentation/index.js"; import { TrashBin } from "../TrashBin/index.js"; +import { useTrashBinListConfig } from "~/Presentation/configs/index.js"; export type TrashBinRendererProps = Omit & { onClose: () => void; @@ -12,9 +12,9 @@ export type TrashBinRendererProps = Omit & { }; export const TrashBinRenderer = ({ title = "Trash Bin", ...props }: TrashBinRendererProps) => { - const { table } = useAcoConfig(); + const { browser } = useTrashBinListConfig(); - if (!table.sorting.length) { + if (!browser.table.sorting.length) { return null; } @@ -22,7 +22,7 @@ export const TrashBinRenderer = ({ title = "Trash Bin", ...props }: TrashBinRend Sorting.create(sort))} + sorting={browser.table.sorting.map(sort => Sorting.create(sort))} /> ); }; diff --git a/packages/app-trash-bin/src/Presentation/components/Cells/CellActions/CellActions.tsx b/packages/app-trash-bin/src/Presentation/components/Cells/CellActions/CellActions.tsx index 8ed97b10895..bf41cbe9671 100644 --- a/packages/app-trash-bin/src/Presentation/components/Cells/CellActions/CellActions.tsx +++ b/packages/app-trash-bin/src/Presentation/components/Cells/CellActions/CellActions.tsx @@ -1,18 +1,17 @@ import React from "react"; -import { useAcoConfig } from "@webiny/app-aco"; import { OptionsMenu } from "@webiny/app-admin"; -import { TrashBinListConfig } from "~/Presentation/configs/index.js"; +import { TrashBinListConfig, useTrashBinListConfig } from "~/Presentation/configs/index.js"; import { TrashBinItemProvider } from "~/Presentation/hooks/index.js"; export const CellActions = () => { const { useTableRow } = TrashBinListConfig.Browser.Table.Column; const { row } = useTableRow(); - const { record: recordConfig } = useAcoConfig(); + const { browser } = useTrashBinListConfig(); return ( diff --git a/packages/app-trash-bin/src/Presentation/components/Table/Table.tsx b/packages/app-trash-bin/src/Presentation/components/Table/Table.tsx index d91ccedf77e..3dbb22daa66 100644 --- a/packages/app-trash-bin/src/Presentation/components/Table/Table.tsx +++ b/packages/app-trash-bin/src/Presentation/components/Table/Table.tsx @@ -3,9 +3,11 @@ import { createRecordsData, Table as AcoTable } from "@webiny/app-aco"; import { useTrashBin } from "~/Presentation/hooks/index.js"; import type { TrashBinTableRow } from "~/Domain/index.js"; import { LoadingActions } from "~/types.js"; +import { useTrashBinListConfig } from "~/Presentation/configs/index.js"; export const Table = () => { const { vm, selectItems, sortItems } = useTrashBin(); + const { browser } = useTrashBinListConfig(); const data = useMemo(() => { return createRecordsData(vm.items); @@ -17,6 +19,7 @@ export const Table = () => { return ( + columns={browser.table.columns} data={data} loading={vm.loading[LoadingActions.list]} onSelectRow={entries => selectItems(entries)} diff --git a/packages/app-trash-bin/src/Presentation/configs/list/Browser/EntryAction.tsx b/packages/app-trash-bin/src/Presentation/configs/list/Browser/EntryAction.tsx index 2e1079cdb8b..829ba0312ed 100644 --- a/packages/app-trash-bin/src/Presentation/configs/list/Browser/EntryAction.tsx +++ b/packages/app-trash-bin/src/Presentation/configs/list/Browser/EntryAction.tsx @@ -11,9 +11,7 @@ type EntryActionProps = React.ComponentProps; const BaseEntryAction = (props: EntryActionProps) => { return ( - - - + ); }; diff --git a/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Column.tsx b/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Column.tsx index 0ab99f52455..51a50d3eb6f 100644 --- a/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Column.tsx +++ b/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Column.tsx @@ -9,11 +9,7 @@ export type { ColumnConfig }; type ColumnProps = React.ComponentProps; const BaseColumn = (props: ColumnProps) => { - return ( - - - - ); + return ; }; export const Column = Object.assign(BaseColumn, { diff --git a/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Sorting.tsx b/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Sorting.tsx index e153d63dbcc..2f22e19dd74 100644 --- a/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Sorting.tsx +++ b/packages/app-trash-bin/src/Presentation/configs/list/Browser/Table/Sorting.tsx @@ -8,9 +8,5 @@ export type { SortingConfig }; type SortingProps = React.ComponentProps; export const Sorting = (props: SortingProps) => { - return ( - - - - ); + return ; }; diff --git a/packages/app-trash-bin/src/Presentation/configs/list/TrashBinListConfig.tsx b/packages/app-trash-bin/src/Presentation/configs/list/TrashBinListConfig.tsx index 4839db0f8bf..42a9e5cdf84 100644 --- a/packages/app-trash-bin/src/Presentation/configs/list/TrashBinListConfig.tsx +++ b/packages/app-trash-bin/src/Presentation/configs/list/TrashBinListConfig.tsx @@ -3,6 +3,7 @@ import { createConfigurableComponent } from "@webiny/react-properties"; import type { BrowserConfig } from "./Browser/index.js"; import { Browser } from "./Browser/index.js"; import { CompositionScope } from "@webiny/react-composition"; +import { useAcoConfig } from "@webiny/app-aco"; const base = createConfigurableComponent("TrashBinListConfig"); @@ -25,6 +26,7 @@ interface TrashBinListConfig { export function useTrashBinListConfig() { const config = base.useConfig(); + const acoConfig = useAcoConfig(config); const browser = config.browser || {}; @@ -32,6 +34,8 @@ export function useTrashBinListConfig() { () => ({ browser: { ...browser, + table: acoConfig.table, + entryActions: acoConfig.record.actions, bulkActions: [...(browser.bulkActions || [])] } }), diff --git a/packages/app-trash-bin/src/Presentation/index.tsx b/packages/app-trash-bin/src/Presentation/index.tsx index a70da2e1696..20a09f8d5be 100644 --- a/packages/app-trash-bin/src/Presentation/index.tsx +++ b/packages/app-trash-bin/src/Presentation/index.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useMemo, useState } from "react"; -import { AcoWithConfig } from "@webiny/app-aco"; import type { ITrashBinDeleteItemGateway, ITrashBinListGateway, @@ -85,16 +84,14 @@ export const TrashBin = ({ render, ...rest }: TrashBinProps) => { <> {show && ( - - - - - + + + )} {render ? render({ showTrashBin }) : null} diff --git a/packages/app-utils/package.json b/packages/app-utils/package.json index 90ccc25e167..380012d6fb6 100644 --- a/packages/app-utils/package.json +++ b/packages/app-utils/package.json @@ -11,6 +11,7 @@ "author": "Webiny Ltd.", "license": "MIT", "dependencies": { + "@webiny/feature": "0.0.0", "@webiny/utils": "0.0.0", "mobx": "^6.15.0" }, diff --git a/packages/app-utils/src/features/List/FilterFeature.ts b/packages/app-utils/src/features/List/FilterFeature.ts new file mode 100644 index 00000000000..a3b770fdfea --- /dev/null +++ b/packages/app-utils/src/features/List/FilterFeature.ts @@ -0,0 +1,30 @@ +import { + FilterFeature as Abstraction, + type IFilterFeature, + ListQueryParamsRepository, + type BaseListParams +} from "./abstractions.js"; + +class FilterFeatureImpl implements IFilterFeature { + constructor(private queryParams: ListQueryParamsRepository.Interface) {} + + async setFilter(key: string, value: unknown): Promise { + await this.queryParams.set(params => { + if (!params.filters) { + params.filters = {}; + } + params.filters[key] = value; + }); + } + + async clearAllFilters(): Promise { + await this.queryParams.set(params => { + params.filters = {}; + }); + } +} + +export const FilterFeature = Abstraction.createImplementation({ + implementation: FilterFeatureImpl, + dependencies: [ListQueryParamsRepository] +}); diff --git a/packages/app-utils/src/features/List/ListQueryParamsRepository.ts b/packages/app-utils/src/features/List/ListQueryParamsRepository.ts new file mode 100644 index 00000000000..832e9badac2 --- /dev/null +++ b/packages/app-utils/src/features/List/ListQueryParamsRepository.ts @@ -0,0 +1,85 @@ +import { makeAutoObservable, runInAction } from "mobx"; +import { + ListQueryParamsRepository as Abstraction, + type BaseListParams, + type IListQueryParamsRepository +} from "./abstractions.js"; + +export class ListQueryParamsRepositoryImpl + implements IListQueryParamsRepository +{ + private params: TParams; + private readonly initial: TParams; + private readonly listeners = new Set<(next: TParams) => void>(); + + constructor() { + this.initial = { search: "", sort: [], filters: {}, limit: 50 } as unknown as TParams; + this.params = this.clone(this.initial) as unknown as TParams; + + makeAutoObservable( + this, + { + subscribe: false, + dispose: false + }, + { autoBind: true } + ); + } + + get(): TParams { + return this.clone(this.params); + } + + async set(updater: (params: TParams) => void): Promise { + runInAction(() => { + updater(this.params); + }); + this.notify(); + } + + async replace(next: TParams): Promise { + runInAction(() => { + this.params = this.clone(next); + }); + this.notify(); + } + + reset(): void { + runInAction(() => { + this.params = this.clone(this.initial); + }); + this.notify(); + } + + subscribe(listener: (next: TParams) => void): () => void { + this.listeners.add(listener); + return () => { + this.listeners.delete(listener); + }; + } + + dispose(): void { + this.listeners.clear(); + } + + private notify(): void { + const snap = this.clone(this.params); + for (const listener of this.listeners) { + try { + listener(snap); + } catch (error) { + // Swallow listener errors to prevent cascading failures + console.error("Error in ListQueryParams listener:", error); + } + } + } + + private clone(value: TParams): TParams { + return structuredClone ? structuredClone(value) : JSON.parse(JSON.stringify(value)); + } +} + +export const ListQueryParamsRepository = Abstraction.createImplementation({ + implementation: ListQueryParamsRepositoryImpl, + dependencies: [] +}); diff --git a/packages/app-utils/src/features/List/LoadMoreFeature.ts b/packages/app-utils/src/features/List/LoadMoreFeature.ts new file mode 100644 index 00000000000..04895b36822 --- /dev/null +++ b/packages/app-utils/src/features/List/LoadMoreFeature.ts @@ -0,0 +1,41 @@ +import { + LoadMoreFeature as Abstraction, + type ILoadMoreFeature, + ListDataRepository, + ListQueryParamsRepository, + LoadingRepository, + type BaseListParams +} from "./abstractions.js"; + +class LoadMoreFeatureImpl implements ILoadMoreFeature { + constructor( + private repository: ListDataRepository.Interface, + private queryParams: ListQueryParamsRepository.Interface, + private loading: LoadingRepository.Interface + ) {} + + async execute(): Promise { + if (!this.canLoadMore()) { + return; + } + + const params = this.queryParams.get(); + + await this.loading.runCallback(this.repository.append(params), "loadMore"); + } + + private canLoadMore(): boolean { + // Don't allow load more if already loading + if (this.loading.isLoading("loadMore")) { + return false; + } + + // Check if repository has more items + return this.repository.hasMore(); + } +} + +export const LoadMoreFeature = Abstraction.createImplementation({ + implementation: LoadMoreFeatureImpl, + dependencies: [ListDataRepository, ListQueryParamsRepository, LoadingRepository] +}); diff --git a/packages/app-utils/src/features/List/LoadingRepository.ts b/packages/app-utils/src/features/List/LoadingRepository.ts new file mode 100644 index 00000000000..174f4aef2a3 --- /dev/null +++ b/packages/app-utils/src/features/List/LoadingRepository.ts @@ -0,0 +1,53 @@ +import { makeAutoObservable, runInAction } from "mobx"; +import { LoadingRepository as Abstraction, type ILoadingRepository } from "./abstractions.js"; + +class LoadingRepositoryImpl implements ILoadingRepository { + private loadingStates: Record = {}; + + constructor() { + makeAutoObservable(this, {}, { autoBind: true }); + } + + get(): Record { + return { ...this.loadingStates }; + } + + async set(action: string, isLoading: boolean = true): Promise { + runInAction(() => { + if (isLoading) { + this.loadingStates[action] = true; + } else { + delete this.loadingStates[action]; + } + }); + } + + async runCallback(callback: Promise, action: string): Promise { + await this.set(action, true); + try { + const result = await callback; + await this.set(action, false); + return result; + } catch (error) { + await this.set(action, false); + throw error; + } + } + + isLoading(action: string): boolean { + return this.loadingStates[action] === true; + } + + hasLoading(): boolean { + return Object.keys(this.loadingStates).length > 0; + } + + isEmpty(): boolean { + return Object.keys(this.loadingStates).length === 0; + } +} + +export const LoadingRepository = Abstraction.createImplementation({ + implementation: LoadingRepositoryImpl, + dependencies: [] +}); diff --git a/packages/app-utils/src/features/List/SearchFeature.ts b/packages/app-utils/src/features/List/SearchFeature.ts new file mode 100644 index 00000000000..86028cfa549 --- /dev/null +++ b/packages/app-utils/src/features/List/SearchFeature.ts @@ -0,0 +1,21 @@ +import { + SearchFeature as Abstraction, + type ISearchFeature, + ListQueryParamsRepository, + type BaseListParams +} from "./abstractions.js"; + +class SearchFeatureImpl implements ISearchFeature { + constructor(private queryParams: ListQueryParamsRepository.Interface) {} + + async setSearch(query: string): Promise { + await this.queryParams.set(params => { + params.search = query; + }); + } +} + +export const SearchFeature = Abstraction.createImplementation({ + implementation: SearchFeatureImpl, + dependencies: [ListQueryParamsRepository] +}); diff --git a/packages/app-utils/src/features/List/SortFeature.ts b/packages/app-utils/src/features/List/SortFeature.ts new file mode 100644 index 00000000000..b0a1aa311cb --- /dev/null +++ b/packages/app-utils/src/features/List/SortFeature.ts @@ -0,0 +1,21 @@ +import { + SortFeature as Abstraction, + type ISortFeature, + ListQueryParamsRepository, + type BaseListParams +} from "./abstractions.js"; + +class SortFeatureImpl implements ISortFeature { + constructor(private queryParams: ListQueryParamsRepository.Interface) {} + + async setSort(by: string, dir: "asc" | "desc"): Promise { + await this.queryParams.set(params => { + params.sort = { by, dir }; + }); + } +} + +export const SortFeature = Abstraction.createImplementation({ + implementation: SortFeatureImpl, + dependencies: [ListQueryParamsRepository] +}); diff --git a/packages/app-utils/src/features/List/abstractions.ts b/packages/app-utils/src/features/List/abstractions.ts new file mode 100644 index 00000000000..7a53c624674 --- /dev/null +++ b/packages/app-utils/src/features/List/abstractions.ts @@ -0,0 +1,120 @@ +import { createAbstraction } from "@webiny/feature/admin"; + +// ============================================================================ +// Base Types +// ============================================================================ + +export interface BaseListParams { + search?: string; + sort?: { by: string; dir: "asc" | "desc" }; + filters?: Record; + limit?: number; +} + +// ============================================================================ +// Query Params Repository +// ============================================================================ + +export interface IListQueryParamsRepository { + get(): TParams; + set(updater: (params: TParams) => void): Promise; + replace(next: TParams): Promise; + reset(): void; + subscribe(listener: (next: TParams) => void): () => void; + dispose(): void; +} + +export const ListQueryParamsRepository = createAbstraction>( + "ListQueryParamsRepository" +); + +export namespace ListQueryParamsRepository { + export type Interface = + IListQueryParamsRepository; +} + +// ============================================================================ +// Loading Repository +// ============================================================================ + +export interface ILoadingRepository { + get(): Record; + set(action: string, isLoading?: boolean): Promise; + runCallback(callback: Promise, action: string): Promise; + isLoading(action: string): boolean; + hasLoading(): boolean; + isEmpty(): boolean; +} + +export const LoadingRepository = createAbstraction("LoadingRepository"); + +export namespace LoadingRepository { + export type Interface = ILoadingRepository; +} + +// ============================================================================ +// List Data Repository +// ============================================================================ + +export interface IListDataRepository { + load(params: TParams): Promise; + append(params: TParams): Promise; + getAll(): TItem[]; + hasMore(): boolean; + clear(): void; +} + +export const ListDataRepository = + createAbstraction>("ListDataRepository"); + +export namespace ListDataRepository { + export type Interface< + TItem = any, + TParams extends BaseListParams = BaseListParams + > = IListDataRepository; +} + +// ============================================================================ +// Features +// ============================================================================ + +export interface ISearchFeature { + setSearch(query: string): Promise; +} + +export const SearchFeature = createAbstraction("SearchFeature"); + +export namespace SearchFeature { + export type Interface = ISearchFeature; +} + +export interface IFilterFeature { + setFilter(key: string, value: unknown): Promise; + clearAllFilters(): Promise; +} + +export const FilterFeature = createAbstraction("FilterFeature"); + +export namespace FilterFeature { + export type Interface = IFilterFeature; +} + +export interface ISortFeature { + setSort(by: string, dir: "asc" | "desc"): Promise; +} + +export const SortFeature = createAbstraction("SortFeature"); + +export namespace SortFeature { + export type Interface = ISortFeature; +} + +export interface ILoadMoreFeature { + execute(): Promise; +} + +export const LoadMoreFeature = createAbstraction("LoadMoreFeature"); + +export namespace LoadMoreFeature { + export type Interface = ILoadMoreFeature; +} diff --git a/packages/app-utils/src/features/List/feature.ts b/packages/app-utils/src/features/List/feature.ts new file mode 100644 index 00000000000..2a800bbce33 --- /dev/null +++ b/packages/app-utils/src/features/List/feature.ts @@ -0,0 +1,40 @@ +import { createFeature } from "@webiny/feature/admin/index.js"; +import { + ListQueryParamsRepository as QueryParamsAbstraction, + LoadingRepository as LoadingAbstraction, + SearchFeature as SearchAbstraction, + FilterFeature as FilterAbstraction, + SortFeature as SortAbstraction, + LoadMoreFeature as LoadMoreAbstraction +} from "./abstractions.js"; +import { ListQueryParamsRepository } from "./ListQueryParamsRepository.js"; +import { LoadingRepository } from "./LoadingRepository.js"; +import { SearchFeature } from "./SearchFeature.js"; +import { FilterFeature } from "./FilterFeature.js"; +import { SortFeature } from "./SortFeature.js"; +import { LoadMoreFeature } from "./LoadMoreFeature.js"; + +export const ListFeature = createFeature({ + name: "ListFeature", + register: container => { + // Register infrastructure + container.register(ListQueryParamsRepository); + container.register(LoadingRepository); + + // Register features + container.register(SearchFeature); + container.register(FilterFeature); + container.register(SortFeature); + container.register(LoadMoreFeature); + }, + resolve: container => { + return { + queryParams: container.resolve(QueryParamsAbstraction), + loading: container.resolve(LoadingAbstraction), + search: container.resolve(SearchAbstraction), + filter: container.resolve(FilterAbstraction), + sort: container.resolve(SortAbstraction), + loadMore: container.resolve(LoadMoreAbstraction) + }; + } +}); diff --git a/packages/app-utils/src/fta/Domain/Repositories/Loading/ILoadingRepository.ts b/packages/app-utils/src/fta/Domain/Repositories/Loading/ILoadingRepository.ts index 99ad773ee3c..a491a7c5938 100644 --- a/packages/app-utils/src/fta/Domain/Repositories/Loading/ILoadingRepository.ts +++ b/packages/app-utils/src/fta/Domain/Repositories/Loading/ILoadingRepository.ts @@ -1,6 +1,7 @@ export interface ILoadingRepository { get: () => Record; set: (action: string, isLoading?: boolean) => Promise; + getActiveLoadings(): string[]; runCallBack: (callback: Promise, action: string) => Promise; isLoading: (action: string) => boolean; hasLoading: () => boolean; diff --git a/packages/app-utils/tsconfig.build.json b/packages/app-utils/tsconfig.build.json index 09d2faad4f9..c1bc84d2d60 100644 --- a/packages/app-utils/tsconfig.build.json +++ b/packages/app-utils/tsconfig.build.json @@ -1,7 +1,10 @@ { "extends": "../../tsconfig.build.json", "include": ["src"], - "references": [{ "path": "../utils/tsconfig.build.json" }], + "references": [ + { "path": "../feature/tsconfig.build.json" }, + { "path": "../utils/tsconfig.build.json" } + ], "compilerOptions": { "rootDir": "./src", "outDir": "./dist", @@ -9,6 +12,10 @@ "paths": { "~/*": ["./src/*"], "~tests/*": ["./__tests__/*"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/utils/*": ["../utils/src/*"], "@webiny/utils": ["../utils/src"] }, diff --git a/packages/app-utils/tsconfig.json b/packages/app-utils/tsconfig.json index 198b286cf66..24a0f575903 100644 --- a/packages/app-utils/tsconfig.json +++ b/packages/app-utils/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "include": ["src", "__tests__"], - "references": [{ "path": "../utils" }], + "references": [{ "path": "../feature" }, { "path": "../utils" }], "compilerOptions": { "rootDirs": ["./src", "./__tests__"], "outDir": "./dist", @@ -9,6 +9,10 @@ "paths": { "~/*": ["./src/*"], "~tests/*": ["./__tests__/*"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/utils/*": ["../utils/src/*"], "@webiny/utils": ["../utils/src"] }, diff --git a/packages/app-website-builder-workflows/.babelrc.js b/packages/app-website-builder-workflows/.babelrc.js new file mode 100644 index 00000000000..b8b7769c0fc --- /dev/null +++ b/packages/app-website-builder-workflows/.babelrc.js @@ -0,0 +1 @@ +module.exports = require("@webiny/build-tools").createBabelConfigForReact({ path: __dirname }); diff --git a/packages/app-website-builder-workflows/package.json b/packages/app-website-builder-workflows/package.json new file mode 100644 index 00000000000..fe2d0be6358 --- /dev/null +++ b/packages/app-website-builder-workflows/package.json @@ -0,0 +1,44 @@ +{ + "name": "@webiny/app-website-builder-workflows", + "version": "0.0.0", + "type": "module", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git" + }, + "author": "Webiny Ltd", + "license": "MIT", + "dependencies": { + "@apollo/react-hooks": "^3.1.5", + "@webiny/admin-ui": "0.0.0", + "@webiny/app": "0.0.0", + "@webiny/app-admin": "0.0.0", + "@webiny/app-security": "0.0.0", + "@webiny/app-website-builder": "0.0.0", + "@webiny/app-workflows": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/icons": "0.0.0", + "mobx": "^6.15.0", + "mobx-react-lite": "^3.4.3", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-helmet": "^6.1.0" + }, + "devDependencies": { + "@types/react": "18.2.79", + "@webiny/build-tools": "0.0.0", + "rimraf": "^6.0.1", + "typescript": "5.9.3" + }, + "publishConfig": { + "access": "public", + "directory": "dist" + }, + "svgo": { + "plugins": { + "removeViewBox": false + } + }, + "gitHead": "b8aec8a1be3f25c3b428b357fe1e352c7cbff9ae" +} diff --git a/packages/app-website-builder-workflows/src/Components/OptionItem/OpenInNewWindow.tsx b/packages/app-website-builder-workflows/src/Components/OptionItem/OpenInNewWindow.tsx new file mode 100644 index 00000000000..1734888a459 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/OptionItem/OpenInNewWindow.tsx @@ -0,0 +1,39 @@ +import React, { useCallback } from "react"; +import { DropdownMenu, Icon } from "@webiny/admin-ui"; +import { Components } from "@webiny/app-workflows"; +import { ReactComponent as OpenInNewIcon } from "@webiny/icons/open_in_new.svg"; +import { useRouter } from "@webiny/app"; +import { Routes } from "@webiny/app-website-builder/routes.js"; +import { WB_PAGE_APP } from "~/constants.js"; + +const { OpenInNewWindow } = Components.List.Options; + +export const ListOpenInNewWindow = OpenInNewWindow.createDecorator(Original => { + return function ListOpenInNewWindow(props) { + const { state } = props; + + const { getLink } = useRouter(); + const onClick = useCallback(() => { + const url = getLink(Routes.Pages.Editor, { + id: state.targetRevisionId, + folderId: "root" + }); + + const goTo = `${window.location.origin}${url}`; + + window.open(goTo, "_blank"); + }, [state.id]); + + if (state.app !== WB_PAGE_APP) { + return ; + } + + return ( + } label={"Open In New Window"} />} + text={"Open in New Window"} + onClick={onClick} + /> + ); + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/OptionItem/index.ts b/packages/app-website-builder-workflows/src/Components/OptionItem/index.ts new file mode 100644 index 00000000000..f754bb19a34 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/OptionItem/index.ts @@ -0,0 +1 @@ +export { ListOpenInNewWindow } from "./OpenInNewWindow.js"; diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorAutoSave.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorAutoSave.tsx new file mode 100644 index 00000000000..171923a0ce6 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorAutoSave.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { PageEditorConfig } from "@webiny/app-website-builder"; +import { useWorkflowState } from "@webiny/app-workflows"; +import { observer } from "mobx-react-lite"; + +const { Ui } = PageEditorConfig; + +interface IWrappedAutoSave { + element?: React.ReactElement | null; +} +const WrappedAutoSave = observer((props: IWrappedAutoSave) => { + const { presenter } = useWorkflowState(); + /** + * Autosave should work only when the page does not have a workflow state assigned. + */ + if (!presenter.vm.state?.state) { + return props.element; + } + + return null; +}); + +export const PageEditorAutoSave = Ui.TopBar.Element.createDecorator(Original => { + return function PageEditorAutoSaveDecorated(props) { + if (props.name === "autoSave") { + return } />; + } + return ; + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorConfig.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorConfig.tsx new file mode 100644 index 00000000000..3f086389ecb --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorConfig.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import { PageEditorConfig as BaseConfig } from "@webiny/app-website-builder"; +import { PageEditorAutoSave } from "./PageEditorAutoSave.js"; +import { PageEditorSettings } from "./PageEditorSettings.js"; +import { PageFormWorkflowStateTooltip } from "./PageFormWorkflowStateTooltip.js"; +import { PageFormWorkflowStatePublishButton } from "./PageFormWorkflowStatePublishButton.js"; +import { PageEditorLayout } from "./PageEditorLayout.js"; + +export const PageEditorConfig = () => { + return ( + <> + + + {/* Should remove autosave feature */} + + {/* Should remove settings button */} + + {/* Should add a button with list of steps and their states + comment button in each row */} + + {/* should remove publish button from the form */} + + + + ); +}; diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorLayout.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorLayout.tsx new file mode 100644 index 00000000000..75d2b81ccd8 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorLayout.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { useSelectFromDocument } from "@webiny/app-website-builder/BaseEditor/hooks/useSelectFromDocument.js"; +import { useApolloClient } from "@apollo/react-hooks"; +import { useSecurity } from "@webiny/app-security"; +import { Components } from "@webiny/app-workflows"; +import { WB_PAGE_APP } from "~/constants.js"; +import { PageFormWorkflowState } from "./PageFormWorkflowState.js"; +import { PageEditorConfig } from "@webiny/app-website-builder"; + +const { Ui } = PageEditorConfig; + +const { + ContentReview: { WorkflowStateProvider } +} = Components; + +export const PageEditorLayout = Ui.TopBar.Layout.createDecorator(Original => { + return function PageEditorTopBarWorkflowsState() { + const page = useSelectFromDocument(doc => { + return { + id: doc.id, + title: doc.properties.title || "unknown page" + }; + }); + + const client = useApolloClient(); + const { identity } = useSecurity(); + + return ( + + {/* Original top bar*/} + + {/* Should render workflow state bar and the alert for storing changes */} + + + ); + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorSettings.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorSettings.tsx new file mode 100644 index 00000000000..2c219039028 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageEditorSettings.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { PageEditorConfig } from "@webiny/app-website-builder"; +import { useWorkflowState } from "@webiny/app-workflows"; +import { observer } from "mobx-react-lite"; + +const { Ui } = PageEditorConfig; + +interface IWrappedSettings { + element?: React.ReactElement | null; +} + +const WrappedSettings = observer((props: IWrappedSettings) => { + const { presenter } = useWorkflowState(); + if (!presenter.vm.state?.state) { + return props.element; + } + + return null; +}); + +export const PageEditorSettings = Ui.TopBar.Action.createDecorator(Original => { + return function PageEditorSettingsDecorated(props) { + if (props.name === "buttonSettings") { + return } />; + } + return ; + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowState.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowState.tsx new file mode 100644 index 00000000000..ceade55da48 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowState.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { Components } from "@webiny/app-workflows"; +import { Alert, Grid } from "@webiny/admin-ui"; +import type { IWorkflowState } from "@webiny/app-workflows/types.js"; + +const { + ContentReview: { WorkflowStateBar } +} = Components; + +interface IStoreAlertProps { + state: IWorkflowState | undefined | null; +} + +const StoreAlert = ({ state }: IStoreAlertProps) => { + if (!state) { + return null; + } + return ( + + Any changes you do on the page will not be stored! + + ); +}; + +export const PageFormWorkflowState = () => { + return ( +
+ + + + {({ state }) => { + return ; + }} + + + +
+ ); +}; diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStatePublishButton.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStatePublishButton.tsx new file mode 100644 index 00000000000..339d12cf151 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStatePublishButton.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { PageEditorConfig } from "@webiny/app-website-builder"; +import { useWorkflowState, WorkflowStateValue } from "@webiny/app-workflows"; +import { observer } from "mobx-react-lite"; + +const { Ui } = PageEditorConfig; + +interface IWrappedPublishButtonProps { + element?: React.ReactElement | null; +} + +const WrappedPublishButton = observer((props: IWrappedPublishButtonProps) => { + const { presenter } = useWorkflowState(); + /** + * Publish button should be visible only when there is no workflow available and when workflow state is approved. + */ + if (!presenter.vm.workflow || presenter.vm.state?.state === WorkflowStateValue.approved) { + return props.element; + } + + return null; +}); + +export const PageFormWorkflowStatePublishButton = Ui.TopBar.Action.createDecorator(Original => { + return function AutoSaveDecorator(props) { + if (props.name === "buttonPublish") { + return ( + } /> + ); + } + return ; + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStateTooltip.tsx b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStateTooltip.tsx new file mode 100644 index 00000000000..2b3d0b9f704 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageEditor/PageFormWorkflowStateTooltip.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Components } from "@webiny/app-workflows"; +import { PageEditorConfig } from "@webiny/app-website-builder"; + +const { + ContentReview: { WorkflowStateTooltip } +} = Components; + +const { Ui } = PageEditorConfig; + +const TooltipButton = () => { + return ; +}; + +export const PageFormWorkflowStateTooltip = () => { + return ( + } + /> + ); +}; diff --git a/packages/app-website-builder-workflows/src/Components/PageWorkflows/PageWorkflowsEditorView.tsx b/packages/app-website-builder-workflows/src/Components/PageWorkflows/PageWorkflowsEditorView.tsx new file mode 100644 index 00000000000..2f5933df30d --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageWorkflows/PageWorkflowsEditorView.tsx @@ -0,0 +1,57 @@ +import React, { useCallback, useMemo } from "react"; +import { AdminConfig, useRoute, useRouter } from "@webiny/app-admin"; +import { Routes } from "~/routes.js"; +import type { IWorkflowApplication } from "@webiny/app-workflows"; +import { Components } from "@webiny/app-workflows"; +import { Icon } from "@webiny/admin-ui"; +import { ReactComponent as WorkflowsIcon } from "@webiny/icons/workspaces.svg"; +import { WB_PAGE_APP } from "~/constants.js"; + +const { + Admin: { WorkflowsEditor } +} = Components; + +const { Menu } = AdminConfig; + +export const PageWorkflowsEditorMenu = () => { + const router = useRouter(); + + return ( + } + /> + ); +}; + +export const PageWorkflowsEditorView = () => { + const { route } = useRoute(Routes.Pages.Workflows); + const { goToRoute } = useRouter(); + + const apps = useMemo(() => { + return [ + { + id: WB_PAGE_APP, + name: "Pages", + icon: } label={"Pages Workflows"} /> + } + ]; + }, []); + + const onAppClick = useCallback( + (id: string) => { + goToRoute(Routes.Pages.Workflows, { + app: id + }); + }, + [apps] + ); + + const app = useMemo(() => { + return apps.find(a => a.id === route.params.app) || apps[0]; + }, [route.params.app, apps]); + + return ; +}; diff --git a/packages/app-website-builder-workflows/src/Components/PageWorkflows/index.ts b/packages/app-website-builder-workflows/src/Components/PageWorkflows/index.ts new file mode 100644 index 00000000000..ea1b4999f08 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PageWorkflows/index.ts @@ -0,0 +1 @@ +export * from "./PageWorkflowsEditorView.js"; diff --git a/packages/app-website-builder-workflows/src/Components/PagesList/PageListChangeStatus.tsx b/packages/app-website-builder-workflows/src/Components/PagesList/PageListChangeStatus.tsx new file mode 100644 index 00000000000..4961159f4e6 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PagesList/PageListChangeStatus.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { PageListConfig } from "@webiny/app-website-builder"; +import { WorkflowStateValue } from "@webiny/app-workflows/types.js"; +import type { WithWorkflowState } from "~/types.js"; +import type { PageDto } from "@webiny/app-website-builder/domain/Page/index.js"; +import { usePage } from "@webiny/app-website-builder/modules/pages/PagesList/hooks/usePage.js"; + +const { Browser } = PageListConfig; + +const decoratePage = (page: PageDto): WithWorkflowState => { + return { + ...page, + // @ts-expect-error + state: page.state, + $selectable: true + }; +}; + +interface IWrappedElementProps { + element: React.ReactElement | undefined; +} + +const WrappedElement = (props: IWrappedElementProps) => { + const { page } = usePage(); + const { state } = decoratePage(page); + if (!state?.state || state.state === WorkflowStateValue.approved) { + return props.element; + } + return null; +}; + +export const PageListChangeStatus = Browser.Page.Action.createDecorator(Original => { + return function PageListChangeStatusAction(props) { + if (props.name === "changeStatus") { + return } />; + } + return ; + }; +}); diff --git a/packages/app-website-builder-workflows/src/Components/PagesList/PagesList.tsx b/packages/app-website-builder-workflows/src/Components/PagesList/PagesList.tsx new file mode 100644 index 00000000000..ed7f26e75c5 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PagesList/PagesList.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import { PageListConfig } from "@webiny/app-website-builder"; +import { PagesListContentReviews } from "./PagesListContentReviews.js"; +import { PageListChangeStatus } from "./PageListChangeStatus.js"; + +export const PagesList = () => { + return ( + + {/* Add a Content Reviews button to footer in sidebar */} + + {/* Decorate the Change Status action in row options to hide it for pages in certain workflow states */} + + + ); +}; diff --git a/packages/app-website-builder-workflows/src/Components/PagesList/PagesListContentReviews.tsx b/packages/app-website-builder-workflows/src/Components/PagesList/PagesListContentReviews.tsx new file mode 100644 index 00000000000..894118d7100 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PagesList/PagesListContentReviews.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { Sidebar } from "@webiny/admin-ui"; +import { PageListConfig } from "@webiny/app-website-builder"; +import { Components } from "@webiny/app-workflows"; +import { useApolloClient } from "@apollo/react-hooks"; +import { WB_PAGE_APP } from "~/constants.js"; +import { ReactComponent as WorkflowStateListIcon } from "@webiny/icons/work.svg"; + +const { + Overlay: { WorkflowStateListAppOverlay } +} = Components; + +const { Browser } = PageListConfig; + +const PageListContentReviewsButton = () => { + const client = useApolloClient(); + + return ( + + {({ showOverlay }) => { + return ( + } + label={"Content Reviews"} + /> + } + /> + ); + }} + + ); +}; + +export const PagesListContentReviews = () => { + return ( + } + /> + ); +}; diff --git a/packages/app-website-builder-workflows/src/Components/PagesList/index.ts b/packages/app-website-builder-workflows/src/Components/PagesList/index.ts new file mode 100644 index 00000000000..d7cc603c1d5 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/PagesList/index.ts @@ -0,0 +1 @@ +export { PagesList } from "./PagesList.js"; diff --git a/packages/app-website-builder-workflows/src/Components/index.ts b/packages/app-website-builder-workflows/src/Components/index.ts new file mode 100644 index 00000000000..0b88ddb9502 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Components/index.ts @@ -0,0 +1,4 @@ +export * from "./OptionItem/index.js"; +export * from "./PageEditor/PageEditorConfig.js"; +export * from "./PagesList/index.js"; +export * from "./PageWorkflows/index.js"; diff --git a/packages/app-website-builder-workflows/src/Routes/WebsiteBuilderWorkflowsMenu.tsx b/packages/app-website-builder-workflows/src/Routes/WebsiteBuilderWorkflowsMenu.tsx new file mode 100644 index 00000000000..9fdfbfe7348 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Routes/WebsiteBuilderWorkflowsMenu.tsx @@ -0,0 +1,53 @@ +import React, { Suspense } from "react"; +import { Routes } from "~/routes.js"; +import Helmet from "react-helmet"; +import { + PageWorkflowsEditorMenu, + PageWorkflowsEditorView +} from "~/Components/PageWorkflows/PageWorkflowsEditorView.js"; +import { i18n } from "@webiny/app/i18n/index.js"; +import { SecureRoute } from "@webiny/app-security/components/index.js"; +import { OverlayLoader } from "@webiny/admin-ui"; +import { AdminConfig, AdminLayout } from "@webiny/app-admin"; +import { useCanUseWorkflows } from "@webiny/app-workflows"; + +const t = i18n.namespace("WebsiteBuilder.Page.Workflows.Editor"); + +interface LoaderProps { + children: React.ReactNode; +} + +const Loader = ({ children, ...props }: LoaderProps) => ( + }> + {React.cloneElement(children as unknown as React.ReactElement, props)} + +); + +const { Route } = AdminConfig; + +export const WebsiteBuilderWorkflowsMenu = () => { + const { canUseWorkflows } = useCanUseWorkflows(); + if (!canUseWorkflows) { + return null; + } + return ( + + + + + {t`Workflows`} + + + + + + + } + /> + + + ); +}; diff --git a/packages/app-website-builder-workflows/src/Routes/index.ts b/packages/app-website-builder-workflows/src/Routes/index.ts new file mode 100644 index 00000000000..039fc29b4d8 --- /dev/null +++ b/packages/app-website-builder-workflows/src/Routes/index.ts @@ -0,0 +1 @@ +export { WebsiteBuilderWorkflowsMenu } from "./WebsiteBuilderWorkflowsMenu.js"; diff --git a/packages/app-website-builder-workflows/src/constants.ts b/packages/app-website-builder-workflows/src/constants.ts new file mode 100644 index 00000000000..4dce9a5e723 --- /dev/null +++ b/packages/app-website-builder-workflows/src/constants.ts @@ -0,0 +1 @@ +export const WB_PAGE_APP = "wb.page"; diff --git a/packages/app-website-builder-workflows/src/index.tsx b/packages/app-website-builder-workflows/src/index.tsx new file mode 100644 index 00000000000..870ac9498d8 --- /dev/null +++ b/packages/app-website-builder-workflows/src/index.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { RegisterFeature, Wcp } from "@webiny/app-admin"; +import { WebsiteBuilderWorkflowsMenu } from "~/Routes/index.js"; +import { ListOpenInNewWindow, PageEditorConfig, PagesList } from "~/Components/index.js"; +import { PageListWorkflowsFeature } from "~/presentation/page/PageList/feature.js"; + +export const WebsiteBuilderWorkflows = () => { + return ( + + + + + + + + ); +}; diff --git a/packages/app-website-builder-workflows/src/presentation/page/PageList/PageListPresenterDecorator.ts b/packages/app-website-builder-workflows/src/presentation/page/PageList/PageListPresenterDecorator.ts new file mode 100644 index 00000000000..61ef0fd3577 --- /dev/null +++ b/packages/app-website-builder-workflows/src/presentation/page/PageList/PageListPresenterDecorator.ts @@ -0,0 +1,50 @@ +import { makeAutoObservable } from "mobx"; +import { + IDocumentListPresenterInit, + IDocumentListVm, + PageListPresenter +} from "@webiny/app-website-builder/presentation/pages/PageList/index.js"; +import type { WithWorkflowState } from "~/types.js"; +import type { PageDto } from "@webiny/app-website-builder/domain/Page/index.js"; + +class PageListPresenterWithWorkflows implements PageListPresenter.Interface { + private readonly decoratee; + + public constructor(decoratee: PageListPresenter.Interface) { + this.decoratee = decoratee; + makeAutoObservable(this); + } + + public get vm(): IDocumentListVm { + const vm = this.decoratee.vm; + return { + ...vm, + data: vm.data.map(page => { + return this.extendPage(page); + }) + }; + } + + public init(params: IDocumentListPresenterInit): void { + this.decoratee.init(params); + } + + public showFilters(show: boolean): void { + this.decoratee.showFilters(show); + } + + private extendPage(page: PageDto): WithWorkflowState { + return { + ...page, + // @ts-expect-error + state: page.state, + // @ts-expect-error + $selectable: !page.state + }; + } +} + +export const PageListPresenterDecorator = PageListPresenter.createDecorator({ + decorator: PageListPresenterWithWorkflows, + dependencies: [] +}); diff --git a/packages/app-website-builder-workflows/src/presentation/page/PageList/WorkflowStateListPagesFieldSelection.ts b/packages/app-website-builder-workflows/src/presentation/page/PageList/WorkflowStateListPagesFieldSelection.ts new file mode 100644 index 00000000000..d8db4bb787e --- /dev/null +++ b/packages/app-website-builder-workflows/src/presentation/page/PageList/WorkflowStateListPagesFieldSelection.ts @@ -0,0 +1,22 @@ +import { ListPagesGraphQLFieldSelection } from "@webiny/app-website-builder/features/pages/loadPages/abstractions.js"; + +class WorkflowStatesListPagesGraphQLFieldSelection + implements ListPagesGraphQLFieldSelection.Interface +{ + getSelection(): string[] { + return [ + `state { + workflowId + stepId + stepName + state + }` + ]; + } +} + +export const WorkflowStateListPagesFieldSelection = + ListPagesGraphQLFieldSelection.createImplementation({ + dependencies: [], + implementation: WorkflowStatesListPagesGraphQLFieldSelection + }); diff --git a/packages/app-website-builder-workflows/src/presentation/page/PageList/feature.ts b/packages/app-website-builder-workflows/src/presentation/page/PageList/feature.ts new file mode 100644 index 00000000000..80c0d95d95e --- /dev/null +++ b/packages/app-website-builder-workflows/src/presentation/page/PageList/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/admin"; +import { PageListPresenterDecorator } from "./PageListPresenterDecorator.js"; +import { WorkflowStateListPagesFieldSelection } from "./WorkflowStateListPagesFieldSelection.js"; + +export const PageListWorkflowsFeature = createFeature({ + name: "PageListWorkflows", + register(container) { + container.registerDecorator(PageListPresenterDecorator); + container.register(WorkflowStateListPagesFieldSelection); + } +}); diff --git a/packages/app-website-builder-workflows/src/routes.ts b/packages/app-website-builder-workflows/src/routes.ts new file mode 100644 index 00000000000..a01452ceecb --- /dev/null +++ b/packages/app-website-builder-workflows/src/routes.ts @@ -0,0 +1,19 @@ +import { Route } from "@webiny/app-admin"; + +export const Routes = { + Pages: { + Workflows: new Route({ + name: "WebsiteBuilder/PageWorkflow", + path: "/website-builder/workflows", + params: zod => { + return { + app: zod.string().optional() + }; + } + }), + WorkflowStateList: new Route({ + name: "WebsiteBuilder/Pages/WorkflowStateList", + path: "/website-builder/pages/workflow-states" + }) + } +}; diff --git a/packages/app-website-builder-workflows/src/types.ts b/packages/app-website-builder-workflows/src/types.ts new file mode 100644 index 00000000000..5ddf3751789 --- /dev/null +++ b/packages/app-website-builder-workflows/src/types.ts @@ -0,0 +1,19 @@ +import type { WorkflowStateValue } from "@webiny/app-workflows/types.js"; + +export interface IRecordWorkflowStateIdentity { + id: string; + displayName: string; + type: string; +} + +export interface IRecordWorkflowState { + workflowId: string; + stepId: string; + state: WorkflowStateValue; + savedBy?: IRecordWorkflowStateIdentity | null; +} + +export type WithWorkflowState = T & { + state?: IRecordWorkflowState | null; + $selectable: boolean; +}; diff --git a/packages/app-website-builder-workflows/tsconfig.build.json b/packages/app-website-builder-workflows/tsconfig.build.json new file mode 100644 index 00000000000..bcdf67d3126 --- /dev/null +++ b/packages/app-website-builder-workflows/tsconfig.build.json @@ -0,0 +1,45 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src"], + "references": [ + { "path": "../admin-ui/tsconfig.build.json" }, + { "path": "../app/tsconfig.build.json" }, + { "path": "../app-admin/tsconfig.build.json" }, + { "path": "../app-security/tsconfig.build.json" }, + { "path": "../app-website-builder/tsconfig.build.json" }, + { "path": "../app-workflows/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" } + ], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/admin-ui/*": ["../admin-ui/src/*"], + "@webiny/admin-ui": ["../admin-ui/src"], + "@webiny/app/features/envConfig": ["../app/src/features/envConfig/index.js"], + "@webiny/app/features/localStorage": ["../app/src/features/localStorage/index.js"], + "@webiny/app/features/router": ["../app/src/features/router/index.js"], + "@webiny/app/features/graphqlClient": ["../app/src/features/graphqlClient/index.js"], + "@webiny/app/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/features/wcp": ["../app-admin/src/features/wcp/index.js"], + "@webiny/app-admin/features/tenancy": ["../app-admin/src/features/tenancy/index.js"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/src"], + "@webiny/app-security/*": ["../app-security/src/*"], + "@webiny/app-security": ["../app-security/src"], + "@webiny/app-website-builder/*": ["../app-website-builder/src/*"], + "@webiny/app-website-builder": ["../app-website-builder/src"], + "@webiny/app-workflows/*": ["../app-workflows/src/*"], + "@webiny/app-workflows": ["../app-workflows/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"] + }, + "baseUrl": "." + } +} diff --git a/packages/app-website-builder-workflows/tsconfig.json b/packages/app-website-builder-workflows/tsconfig.json new file mode 100644 index 00000000000..1f1a32de184 --- /dev/null +++ b/packages/app-website-builder-workflows/tsconfig.json @@ -0,0 +1,45 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../admin-ui" }, + { "path": "../app" }, + { "path": "../app-admin" }, + { "path": "../app-security" }, + { "path": "../app-website-builder" }, + { "path": "../app-workflows" }, + { "path": "../feature" } + ], + "compilerOptions": { + "rootDirs": ["./src", "./__tests__"], + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/admin-ui/*": ["../admin-ui/src/*"], + "@webiny/admin-ui": ["../admin-ui/src"], + "@webiny/app/features/envConfig": ["../app/src/features/envConfig/index.js"], + "@webiny/app/features/localStorage": ["../app/src/features/localStorage/index.js"], + "@webiny/app/features/router": ["../app/src/features/router/index.js"], + "@webiny/app/features/graphqlClient": ["../app/src/features/graphqlClient/index.js"], + "@webiny/app/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/features/wcp": ["../app-admin/src/features/wcp/index.js"], + "@webiny/app-admin/features/tenancy": ["../app-admin/src/features/tenancy/index.js"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/src"], + "@webiny/app-security/*": ["../app-security/src/*"], + "@webiny/app-security": ["../app-security/src"], + "@webiny/app-website-builder/*": ["../app-website-builder/src/*"], + "@webiny/app-website-builder": ["../app-website-builder/src"], + "@webiny/app-workflows/*": ["../app-workflows/src/*"], + "@webiny/app-workflows": ["../app-workflows/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"] + }, + "baseUrl": "." + } +} diff --git a/packages/app-website-builder-workflows/webiny.config.js b/packages/app-website-builder-workflows/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/app-website-builder-workflows/webiny.config.js @@ -0,0 +1,8 @@ +import { createWatchPackage, createBuildPackage } from "@webiny/build-tools"; + +export default { + commands: { + build: createBuildPackage({ cwd: import.meta.dirname }), + watch: createWatchPackage({ cwd: import.meta.dirname }) + } +}; diff --git a/packages/app-website-builder/package.json b/packages/app-website-builder/package.json index de70d7b375a..ebb8752ee07 100644 --- a/packages/app-website-builder/package.json +++ b/packages/app-website-builder/package.json @@ -25,6 +25,7 @@ "@webiny/app-security": "0.0.0", "@webiny/app-utils": "0.0.0", "@webiny/error": "0.0.0", + "@webiny/feature": "0.0.0", "@webiny/form": "0.0.0", "@webiny/icons": "0.0.0", "@webiny/lexical-converter": "0.0.0", diff --git a/packages/app-website-builder/src/BaseEditor/config/EditorConfig.tsx b/packages/app-website-builder/src/BaseEditor/config/EditorConfig.tsx index d1d3d142c11..c5e46a58cce 100644 --- a/packages/app-website-builder/src/BaseEditor/config/EditorConfig.tsx +++ b/packages/app-website-builder/src/BaseEditor/config/EditorConfig.tsx @@ -19,7 +19,7 @@ interface EditorConfig { inputRenderers: ElementInputConfig[]; } -const base = createConfigurableComponent("DocumentEditorConfig"); +const base = createConfigurableComponent("DocumentEditor"); export const EditorConfig = Object.assign(base.Config, { /** diff --git a/packages/app-website-builder/src/BaseEditor/defaultConfig/Toolbar/Navigator/index.ts b/packages/app-website-builder/src/BaseEditor/defaultConfig/Toolbar/Navigator/index.ts index e69de29bb2d..21470dcfa78 100644 --- a/packages/app-website-builder/src/BaseEditor/defaultConfig/Toolbar/Navigator/index.ts +++ b/packages/app-website-builder/src/BaseEditor/defaultConfig/Toolbar/Navigator/index.ts @@ -0,0 +1,2 @@ +export { Navigator } from "./Navigator.js"; +export { NavigatorTab } from "./NavigatorTab.js"; diff --git a/packages/app-website-builder/src/Extension.tsx b/packages/app-website-builder/src/Extension.tsx index 864d97a6cf1..91a4fb494c7 100644 --- a/packages/app-website-builder/src/Extension.tsx +++ b/packages/app-website-builder/src/Extension.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { AdminConfig } from "@webiny/app-admin"; +import { AdminConfig, useContainer } from "@webiny/app-admin"; import { HasPermission } from "@webiny/app-security"; import { ReactComponent as PagesIcon } from "@webiny/icons/table_chart.svg"; import { PageEditor } from "~/modules/pages/PageEditor.js"; @@ -12,11 +12,15 @@ import { RedirectsListConfig } from "~/modules/redirects/RedirectsListConfig.js" import { Routes } from "~/routes.js"; import { useRouter } from "@webiny/app-admin"; import { PagesWidget } from "~/modules/widgets/PagesWidget.js"; +import { PageListFeature } from "~/presentation/pages/PageList/feature.js"; const { Menu, Route, Dashboard } = AdminConfig; export const Extension = () => { const router = useRouter(); + const container = useContainer(); + + PageListFeature.register(container); return ( <> diff --git a/packages/app-website-builder/src/domain/Page/Page.ts b/packages/app-website-builder/src/domain/Page/Page.ts index 6f943e966a4..447cd82da15 100644 --- a/packages/app-website-builder/src/domain/Page/Page.ts +++ b/packages/app-website-builder/src/domain/Page/Page.ts @@ -56,6 +56,9 @@ export class Page { this.savedOn = data.savedOn ?? ""; this.modifiedBy = this.createIdentity(data.modifiedBy); this.modifiedOn = data.modifiedOn ?? ""; + // TODO remove when implemented in a proper way + // @ts-expect-error + this.state = data.state; } static create(data: PageData) { diff --git a/packages/app-website-builder/src/domain/Page/PageDto.ts b/packages/app-website-builder/src/domain/Page/PageDto.ts index ec6c337fb94..2d23b68a7d3 100644 --- a/packages/app-website-builder/src/domain/Page/PageDto.ts +++ b/packages/app-website-builder/src/domain/Page/PageDto.ts @@ -24,6 +24,7 @@ export interface PageDto { export class PageDtoMapper { static toDTO(page: Page): PageDto { return { + ...page, id: page.id, entryId: page.entryId, status: page.status, diff --git a/packages/app-website-builder/src/features/pages/createPage/CreatePageGqlGateway.ts b/packages/app-website-builder/src/features/pages/createPage/CreatePageGqlGateway.ts index 495e754f73d..bd910e4b1ff 100644 --- a/packages/app-website-builder/src/features/pages/createPage/CreatePageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/createPage/CreatePageGqlGateway.ts @@ -18,11 +18,13 @@ export interface CreatePageVariables { data: PageDto; } -export const CREATE_PAGE = (PAGE_FIELDS: string) => gql` +export const CREATE_PAGE = (fields: string[]) => gql` mutation CreatePage($data: WbPageCreateInput!) { websiteBuilder { createPage(data: $data) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -34,10 +36,10 @@ export const CREATE_PAGE = (PAGE_FIELDS: string) => gql` `; export class CreatePageGqlGateway implements ICreatePageGateway { - private client: ApolloClient; - private modelFields: string; + private client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/createPageRevisionFrom/CreatePageRevisionFromGqlGateway.ts b/packages/app-website-builder/src/features/pages/createPageRevisionFrom/CreatePageRevisionFromGqlGateway.ts index d2731fc7b63..4a9317368fb 100644 --- a/packages/app-website-builder/src/features/pages/createPageRevisionFrom/CreatePageRevisionFromGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/createPageRevisionFrom/CreatePageRevisionFromGqlGateway.ts @@ -17,11 +17,13 @@ export interface CreatePageRevisionFromPageResponse { }; } -export const CREATE_PAGE_REVISION_FROM = (PAGE_FIELDS: string) => gql` +export const CREATE_PAGE_REVISION_FROM = (fields: string[]) => gql` mutation CreatePageRevisionFrom($id: ID!) { websiteBuilder { createPageRevisionFrom(id: $id) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -33,10 +35,10 @@ export const CREATE_PAGE_REVISION_FROM = (PAGE_FIELDS: string) => gql` `; export class CreatePageRevisionFromGqlGateway implements ICreatePageRevisionFromGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/duplicatePage/DuplicatePageGqlGateway.ts b/packages/app-website-builder/src/features/pages/duplicatePage/DuplicatePageGqlGateway.ts index c126053256c..0e6cba66bfb 100644 --- a/packages/app-website-builder/src/features/pages/duplicatePage/DuplicatePageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/duplicatePage/DuplicatePageGqlGateway.ts @@ -17,11 +17,13 @@ export interface DuplicatePageResponse { }; } -export const DUPLICATE_PAGE = (PAGE_FIELDS: string) => gql` +export const DUPLICATE_PAGE = (fields: string[]) => gql` mutation DuplicatePage($id: ID!) { websiteBuilder { duplicatePage(id: $id) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -33,10 +35,10 @@ export const DUPLICATE_PAGE = (PAGE_FIELDS: string) => gql` `; export class DuplicatePageGqlGateway implements IDuplicatePageGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/getPage/GetPageGqlGateway.ts b/packages/app-website-builder/src/features/pages/getPage/GetPageGqlGateway.ts index d6032fbb8d6..baf5a1773ba 100644 --- a/packages/app-website-builder/src/features/pages/getPage/GetPageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/getPage/GetPageGqlGateway.ts @@ -17,11 +17,13 @@ export interface GetPageQueryVariables { id: string; } -export const GET_PAGE = (PAGE_FIELDS: string) => gql` +export const GET_PAGE = (fields: string[]) => gql` query GetPage($id: ID!) { websiteBuilder { getPageById(id: $id) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -33,10 +35,10 @@ export const GET_PAGE = (PAGE_FIELDS: string) => gql` `; export class GetPageGqlGateway implements IGetPageGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/loadPages/ListPagesGqlGateway.ts b/packages/app-website-builder/src/features/pages/loadPages/ListPagesGqlGateway.ts index f4b27f8e805..ec86140f3b5 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/ListPagesGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/ListPagesGqlGateway.ts @@ -4,6 +4,7 @@ import type { PageGatewayDto } from "~/features/pages/loadPages/PageGatewayDto.j import type { IListPagesGateway } from "./IListPagesGateway.js"; import { type ListPagesGatewayParams } from "./IListPagesGateway.js"; import { type WbError, type WbListMeta } from "~/types.js"; +import type { IListPagesGraphQLFieldSelection } from "~/features/pages/loadPages/abstractions.js"; const LIST_META_FIELD = /* GraphQL */ ` meta { @@ -41,11 +42,13 @@ export interface ListPagesQueryVariables { search?: string; } -export const LIST_PAGES = (PAGES_FIELDS: string) => gql` +export const LIST_PAGES = (fields: string[]) => gql` query ListPages($where: WbPagesListWhereInput, $limit: Int, $after: String, $sort: [WbPageListSorter], $search: String) { websiteBuilder { listPages(where: $where, limit: $limit, after: $after, sort: $sort, search: $search) { - data ${PAGES_FIELDS} + data { + ${fields.join("\n")} + } ${LIST_META_FIELD} ${ERROR_FIELD} } @@ -53,21 +56,34 @@ export const LIST_PAGES = (PAGES_FIELDS: string) => gql` } `; +export interface IListPagesGatewayParams { + client: ApolloClient; + modelFields: string[]; + fieldSelection: IListPagesGraphQLFieldSelection[]; +} + export class ListPagesGqlGateway implements IListPagesGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; + private readonly fieldSelection; - constructor(client: ApolloClient, modelFields: string) { - this.client = client; - this.modelFields = modelFields; + public constructor(params: IListPagesGatewayParams) { + this.client = params.client; + this.modelFields = params.modelFields; + this.fieldSelection = params.fieldSelection; } - async execute(params: ListPagesGatewayParams) { + public async execute(params: ListPagesGatewayParams) { + const fields = [...this.modelFields]; + for (const extraFields of this.fieldSelection) { + fields.push(...extraFields.getSelection()); + } + const { data: response } = await this.client.query< ListPagesResponse, ListPagesQueryVariables >({ - query: LIST_PAGES(this.modelFields), + query: LIST_PAGES(fields), variables: { ...params, where: { diff --git a/packages/app-website-builder/src/features/pages/loadPages/abstractions.ts b/packages/app-website-builder/src/features/pages/loadPages/abstractions.ts new file mode 100644 index 00000000000..527ee417656 --- /dev/null +++ b/packages/app-website-builder/src/features/pages/loadPages/abstractions.ts @@ -0,0 +1,13 @@ +import { createAbstraction } from "@webiny/feature/admin"; + +export interface IListPagesGraphQLFieldSelection { + getSelection(): string[]; +} + +export const ListPagesGraphQLFieldSelection = createAbstraction( + "ListPagesGraphQLFieldSelection" +); + +export namespace ListPagesGraphQLFieldSelection { + export type Interface = IListPagesGraphQLFieldSelection; +} diff --git a/packages/app-website-builder/src/features/pages/loadPages/useFilterPages.ts b/packages/app-website-builder/src/features/pages/loadPages/useFilterPages.ts index 82451963804..cd48019488c 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/useFilterPages.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/useFilterPages.ts @@ -1,14 +1,10 @@ import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { useGetPageGraphQLFields } from "~/features/pages/index.js"; -import { ListPagesGqlGateway } from "~/features/pages/loadPages/ListPagesGqlGateway.js"; -import { FilterPages } from "~/features/pages/loadPages/FilterPages.js"; import { useGetDescendantFolders } from "@webiny/app-aco"; +import { FilterPages } from "~/features/pages/loadPages/FilterPages.js"; +import { useListPagesGateway } from "~/features/pages/loadPages/useListPagesGateway.js"; export const useFilterPages = () => { - const client = useApolloClient(); - const fields = useGetPageGraphQLFields(["properties", "metadata"]); - const gateway = new ListPagesGqlGateway(client, fields); + const gateway = useListPagesGateway(["properties", "metadata"]); const { getDescendantFolders } = useGetDescendantFolders(); const filterPages = useCallback( diff --git a/packages/app-website-builder/src/features/pages/loadPages/useListPagesGateway.ts b/packages/app-website-builder/src/features/pages/loadPages/useListPagesGateway.ts new file mode 100644 index 00000000000..57b7bec005e --- /dev/null +++ b/packages/app-website-builder/src/features/pages/loadPages/useListPagesGateway.ts @@ -0,0 +1,20 @@ +import { useApolloClient } from "@apollo/react-hooks"; +import { useGetPageGraphQLFields } from "~/features/pages/index.js"; +import { ListPagesGqlGateway } from "./ListPagesGqlGateway.js"; +import { useContainer } from "@webiny/app"; +import type { IListPagesGateway } from "./IListPagesGateway.js"; +import { ListPagesGraphQLFieldSelection } from "./abstractions.js"; + +export const useListPagesGateway = (inputFields: string[]): IListPagesGateway => { + const container = useContainer(); + const client = useApolloClient(); + const fields = useGetPageGraphQLFields(inputFields); + + const selections = container.resolveAll(ListPagesGraphQLFieldSelection); + + return new ListPagesGqlGateway({ + client, + modelFields: fields, + fieldSelection: selections + }); +}; diff --git a/packages/app-website-builder/src/features/pages/loadPages/useLoadMorePages.ts b/packages/app-website-builder/src/features/pages/loadPages/useLoadMorePages.ts index 4685e4bedf3..7fd5ffb0d0b 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/useLoadMorePages.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/useLoadMorePages.ts @@ -1,13 +1,9 @@ import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { useGetPageGraphQLFields } from "~/features/pages/index.js"; -import { ListPagesGqlGateway } from "~/features/pages/loadPages/ListPagesGqlGateway.js"; import { LoadMorePages } from "~/features/pages/loadPages/LoadMorePages.js"; +import { useListPagesGateway } from "./useListPagesGateway.js"; export const useLoadMorePages = () => { - const client = useApolloClient(); - const fields = useGetPageGraphQLFields(["properties", "metadata"]); - const gateway = new ListPagesGqlGateway(client, fields); + const gateway = useListPagesGateway(["properties", "metadata"]); const loadMorePages = useCallback(() => { const instance = LoadMorePages.getInstance(gateway); diff --git a/packages/app-website-builder/src/features/pages/loadPages/useLoadPages.ts b/packages/app-website-builder/src/features/pages/loadPages/useLoadPages.ts index a4c14a7d415..489648bf82d 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/useLoadPages.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/useLoadPages.ts @@ -1,14 +1,10 @@ import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { useGetPageGraphQLFields } from "~/features/pages/index.js"; -import { ListPagesGqlGateway } from "~/features/pages/loadPages/ListPagesGqlGateway.js"; import { LoadPages } from "~/features/pages/loadPages/LoadPages.js"; import type { LoadPagesUseCaseParams } from "~/features/pages/loadPages/ILoadPagesUseCase.js"; +import { useListPagesGateway } from "~/features/pages/loadPages/useListPagesGateway.js"; export const useLoadPages = () => { - const client = useApolloClient(); - const fields = useGetPageGraphQLFields(["properties", "metadata"]); - const gateway = new ListPagesGqlGateway(client, fields); + const gateway = useListPagesGateway(["properties", "metadata"]); const loadPages = useCallback( (params: LoadPagesUseCaseParams) => { diff --git a/packages/app-website-builder/src/features/pages/loadPages/useSearchPages.ts b/packages/app-website-builder/src/features/pages/loadPages/useSearchPages.ts index 81e4323e0d6..56ae9e56d64 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/useSearchPages.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/useSearchPages.ts @@ -1,14 +1,10 @@ import { useCallback } from "react"; -import { useApolloClient } from "@apollo/react-hooks"; -import { useGetPageGraphQLFields } from "~/features/pages/index.js"; -import { ListPagesGqlGateway } from "~/features/pages/loadPages/ListPagesGqlGateway.js"; import { SearchPages } from "~/features/pages/loadPages/SearchPages.js"; import { useGetDescendantFolders } from "@webiny/app-aco"; +import { useListPagesGateway } from "~/features/pages/loadPages/useListPagesGateway.js"; export const useSearchPages = () => { - const client = useApolloClient(); - const fields = useGetPageGraphQLFields(["properties", "metadata"]); - const gateway = new ListPagesGqlGateway(client, fields); + const gateway = useListPagesGateway(["properties", "metadata"]); const { getDescendantFolders } = useGetDescendantFolders(); const searchPages = useCallback( diff --git a/packages/app-website-builder/src/features/pages/loadPages/useSortPages.ts b/packages/app-website-builder/src/features/pages/loadPages/useSortPages.ts index 629744a742b..52e12e0633a 100644 --- a/packages/app-website-builder/src/features/pages/loadPages/useSortPages.ts +++ b/packages/app-website-builder/src/features/pages/loadPages/useSortPages.ts @@ -1,15 +1,11 @@ -import { useApolloClient } from "@apollo/react-hooks"; -import { ListPagesGqlGateway } from "~/features/pages/loadPages/ListPagesGqlGateway.js"; -import { useGetPageGraphQLFields } from "~/features/pages/index.js"; import { SortPages } from "~/features/pages/loadPages/SortPages.js"; import type { OnDataTableSortingChange } from "@webiny/admin-ui"; import type { ColumnSorting } from "@webiny/app-utils"; import { SortingMapper } from "@webiny/app-utils"; +import { useListPagesGateway } from "~/features/pages/loadPages/useListPagesGateway.js"; export const useSortPages = () => { - const client = useApolloClient(); - const fields = useGetPageGraphQLFields(["properties", "metadata"]); - const gateway = new ListPagesGqlGateway(client, fields); + const gateway = useListPagesGateway(["properties", "metadata"]); const sortPages: OnDataTableSortingChange = async updaterOrValue => { let newSorts: ColumnSorting[] = []; diff --git a/packages/app-website-builder/src/features/pages/publishPage/PublishPageGqlGateway.ts b/packages/app-website-builder/src/features/pages/publishPage/PublishPageGqlGateway.ts index bb4d6d78823..8b174f6e647 100644 --- a/packages/app-website-builder/src/features/pages/publishPage/PublishPageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/publishPage/PublishPageGqlGateway.ts @@ -17,11 +17,13 @@ export interface PublishPageResponse { }; } -export const PUBLISH_PAGE = (PAGE_FIELDS: string) => gql` +export const PUBLISH_PAGE = (fields: string[]) => gql` mutation PublishPage($id: ID!) { websiteBuilder { publishPage(id: $id) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -33,10 +35,10 @@ export const PUBLISH_PAGE = (PAGE_FIELDS: string) => gql` `; export class PublishPageGqlGateway implements IPublishPageGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/selectPages/index.ts b/packages/app-website-builder/src/features/pages/selectPages/index.ts index e69de29bb2d..4ceb7ff7bd2 100644 --- a/packages/app-website-builder/src/features/pages/selectPages/index.ts +++ b/packages/app-website-builder/src/features/pages/selectPages/index.ts @@ -0,0 +1,4 @@ +export type { ISelectPagesUseCase } from "./ISelectPagesUseCases.js"; +export { SelectPagesUseCase } from "./SelectPagesUseCase.js"; +export { SelectPages } from "./SelectPages.js"; +export { useSelectPages } from "./useSelectPages.js"; diff --git a/packages/app-website-builder/src/features/pages/unpublishPage/UnpublishPageGqlGateway.ts b/packages/app-website-builder/src/features/pages/unpublishPage/UnpublishPageGqlGateway.ts index 2d5dd9d50bc..f9071e803b4 100644 --- a/packages/app-website-builder/src/features/pages/unpublishPage/UnpublishPageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/unpublishPage/UnpublishPageGqlGateway.ts @@ -17,11 +17,13 @@ export interface UnpublishPageResponse { }; } -export const UNPUBLISH_PAGE = (PAGE_FIELDS: string) => gql` +export const UNPUBLISH_PAGE = (fields: string[]) => gql` mutation UnpublishPage($id: ID!) { websiteBuilder { unpublishPage(id: $id) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -33,10 +35,10 @@ export const UNPUBLISH_PAGE = (PAGE_FIELDS: string) => gql` `; export class UnpublishPageGqlGateway implements IUnpublishPageGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/updatePage/UpdatePageGqlGateway.ts b/packages/app-website-builder/src/features/pages/updatePage/UpdatePageGqlGateway.ts index ca3cf677021..de42fb58e77 100644 --- a/packages/app-website-builder/src/features/pages/updatePage/UpdatePageGqlGateway.ts +++ b/packages/app-website-builder/src/features/pages/updatePage/UpdatePageGqlGateway.ts @@ -33,11 +33,13 @@ export interface UpdatePageVariables { >; } -export const UPDATE_PAGE = (PAGE_FIELDS: string) => gql` +export const UPDATE_PAGE = (fields: string[]) => gql` mutation UpdatePage($id: ID!, $data: WbPageUpdateInput!) { websiteBuilder { updatePage(id: $id, data: $data) { - data ${PAGE_FIELDS} + data { + ${fields.join("\n")} + } error { code data @@ -49,10 +51,10 @@ export const UPDATE_PAGE = (PAGE_FIELDS: string) => gql` `; export class UpdatePageGqlGateway implements IUpdatePageGateway { - private client: ApolloClient; - private modelFields: string; + private readonly client; + private readonly modelFields; - constructor(client: ApolloClient, modelFields: string) { + constructor(client: ApolloClient, modelFields: string[]) { this.client = client; this.modelFields = modelFields; } diff --git a/packages/app-website-builder/src/features/pages/useGetPageGraphQLFields.ts b/packages/app-website-builder/src/features/pages/useGetPageGraphQLFields.ts index 756163c3adb..34354f1f548 100644 --- a/packages/app-website-builder/src/features/pages/useGetPageGraphQLFields.ts +++ b/packages/app-website-builder/src/features/pages/useGetPageGraphQLFields.ts @@ -1,33 +1,27 @@ -export const useGetPageGraphQLFields = (fields: string[]) => { - return /* GraphQL */ `{ - id - entryId - status - version - wbyAco_location { +export const useGetPageGraphQLFields = (fields: string[]): string[] => { + return [ + `id`, + `entryId`, + `status`, + `version`, + `wbyAco_location { folderId - } - createdOn - createdBy { + }`, + `createdOn`, + `createdBy { id displayName - } - savedOn - savedBy { + }`, + `savedOn`, + `savedBy { id displayName - } - modifiedOn - modifiedBy { + }`, + `modifiedOn`, + `modifiedBy { id displayName - } - ${fields - .map( - field => ` - ${field} - ` - ) - .join("\n")} - }`; + }`, + ...fields + ]; }; diff --git a/packages/app-website-builder/src/features/redirects/selectRedirects/index.ts b/packages/app-website-builder/src/features/redirects/selectRedirects/index.ts index e69de29bb2d..e2ff23fdaa3 100644 --- a/packages/app-website-builder/src/features/redirects/selectRedirects/index.ts +++ b/packages/app-website-builder/src/features/redirects/selectRedirects/index.ts @@ -0,0 +1,4 @@ +export type { ISelectRedirectsUseCase } from "./ISelectRedirectsUseCases.js"; +export { SelectRedirects } from "./SelectRedirects.js"; +export { SelectRedirectsUseCase } from "./SelectRedirectsUseCase.js"; +export { useSelectRedirects } from "./useSelectRedirects.js"; diff --git a/packages/app-website-builder/src/modules/pages/PageEditor/DefaultPageEditorConfig.tsx b/packages/app-website-builder/src/modules/pages/PageEditor/DefaultPageEditorConfig.tsx index 9155ef5d7db..20c28877f21 100644 --- a/packages/app-website-builder/src/modules/pages/PageEditor/DefaultPageEditorConfig.tsx +++ b/packages/app-website-builder/src/modules/pages/PageEditor/DefaultPageEditorConfig.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { PageEditorConfig } from "~/modules/pages/PageEditor/PageEditorConfig.js"; -import { AutoSaveIndicator, PageAutoSave } from "~/modules/pages/PageEditor/PageAutoSave.js"; +import { PageEditorConfig } from "./PageEditorConfig.js"; +import { PageAutoSave } from "./PageAutoSave.js"; import { BackButton } from "./TopBar/BackButton.js"; import { Title } from "./TopBar/Title.js"; import { PublishButton } from "./TopBar/PublishButton.js"; -import { RevisionsMenu } from "~/modules/pages/PageEditor/TopBar/RevisionsMenu.js"; +import { RevisionsMenu } from "./TopBar/RevisionsMenu.js"; import { SettingsButton } from "./TopBar/SettingsButton.js"; const { Ui } = PageEditorConfig; @@ -17,13 +17,7 @@ export const DefaultPageEditorConfig = () => { } /> } /> } /> - - } - /> + } /> ); }; diff --git a/packages/app-website-builder/src/modules/pages/PageEditor/PageAutoSave.tsx b/packages/app-website-builder/src/modules/pages/PageEditor/PageAutoSave.tsx index 8e98c85380d..76545f90c6f 100644 --- a/packages/app-website-builder/src/modules/pages/PageEditor/PageAutoSave.tsx +++ b/packages/app-website-builder/src/modules/pages/PageEditor/PageAutoSave.tsx @@ -4,7 +4,7 @@ import { useDocumentEditor } from "~/DocumentEditor/index.js"; import { useUpdatePage } from "~/features/pages/index.js"; import { useSelectFromEditor } from "~/BaseEditor/hooks/useSelectFromEditor.js"; -export const PageAutoSave = () => { +const PageAutoSaveAction = () => { const editor = useDocumentEditor(); const { updatePage } = useUpdatePage(); @@ -32,8 +32,15 @@ export const PageAutoSave = () => { return null; }; -export const AutoSaveIndicator = () => { +export const PageAutoSave = () => { const isSaving = useSelectFromEditor(state => state.autoSaving ?? false); - return isSaving ?
Autosaving...
: null; + return ( + <> + + {isSaving ? ( +
Autosaving...
+ ) : null} + + ); }; diff --git a/packages/app-website-builder/src/modules/pages/PageList.tsx b/packages/app-website-builder/src/modules/pages/PageList.tsx index 2b3e9967fca..992816ff8fb 100644 --- a/packages/app-website-builder/src/modules/pages/PageList.tsx +++ b/packages/app-website-builder/src/modules/pages/PageList.tsx @@ -1,12 +1,6 @@ import React from "react"; -import { - useRoute, - useRouter, - AdminLayout, - CompositionScope, - DialogsProvider -} from "@webiny/app-admin"; -import { AcoWithConfig, NavigateFolderProvider } from "@webiny/app-aco"; +import { useRoute, useRouter, AdminLayout, DialogsProvider } from "@webiny/app-admin"; +import { NavigateFolderProvider } from "@webiny/app-aco"; import { FoldersProvider } from "@webiny/app-aco/contexts/folders.js"; import { PagesList } from "./PagesList/PagesList.js"; import { WB_PAGE_APP } from "~/constants.js"; @@ -27,24 +21,20 @@ export const PageList = () => { }; return ( - - - - - - - - - - - - - - - + + + + + + + + + + + ); }; diff --git a/packages/app-website-builder/src/modules/pages/PagesList/PagesList.tsx b/packages/app-website-builder/src/modules/pages/PagesList/PagesList.tsx index 896bca9fa84..984e40699e9 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/PagesList.tsx +++ b/packages/app-website-builder/src/modules/pages/PagesList/PagesList.tsx @@ -2,12 +2,7 @@ import React from "react"; import { Layout } from "~/modules/pages/PagesList/components/Layout/index.js"; import { Main } from "~/modules/pages/PagesList/components/Main/index.js"; import { Sidebar } from "~/modules/pages/PagesList/components/Sidebar/index.js"; -import { DocumentListPresenterProvider } from "~/modules/pages/PagesList/presenters/DocumentListPresenterContext.js"; export const PagesList = () => { - return ( - - } sidebar={} /> - - ); + return } sidebar={} />; }; diff --git a/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/Sidebar.tsx b/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/Sidebar.tsx index 51fcbe0adf3..9f8bb77e665 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/Sidebar.tsx +++ b/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/Sidebar.tsx @@ -1,17 +1,25 @@ import React from "react"; import { FolderTree, useNavigateFolder } from "@webiny/app-aco"; +import { SidebarFooter } from "./SidebarFooter.js"; +import { usePageListConfig } from "~/modules/pages/configs/index.js"; const Sidebar = () => { const { currentFolderId, navigateToFolder } = useNavigateFolder(); + const { browser } = usePageListConfig(); return ( -
+
navigateToFolder(data.id)} enableActions={true} enableCreate={true} /> +
+ +
); }; diff --git a/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/SidebarFooter.tsx b/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/SidebarFooter.tsx new file mode 100644 index 00000000000..56400262f01 --- /dev/null +++ b/packages/app-website-builder/src/modules/pages/PagesList/components/Sidebar/SidebarFooter.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { usePageListConfig } from "~/modules/pages/configs/index.js"; + +export const SidebarFooter = () => { + const { browser } = usePageListConfig(); + + return ( + <> + {browser.sidebarFooter.map(footer => { + return React.cloneElement(footer.element, { key: footer.name }); + })} + + ); +}; diff --git a/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Cells/CellActions.tsx b/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Cells/CellActions.tsx index b0be8cf8d57..db18297047a 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Cells/CellActions.tsx +++ b/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Cells/CellActions.tsx @@ -1,12 +1,14 @@ import React from "react"; -import { FolderProvider, useAcoConfig } from "@webiny/app-aco"; +import { FolderProvider } from "@webiny/app-aco"; import { makeDecoratable, OptionsMenu } from "@webiny/app-admin"; -import { PageListConfig } from "~/modules/pages/configs/index.js"; +import { PageListConfig, usePageListConfig } from "~/modules/pages/configs/index.js"; import { PageProvider } from "~/modules/pages/PagesList/hooks/usePage.js"; + const DefaultCellActions = () => { const { useTableRow, isFolderRow } = PageListConfig.Browser.Table.Column; const { row } = useTableRow(); - const { folder: folderConfig, record: documentConfig } = useAcoConfig(); + const { browser } = usePageListConfig(); + const { folder: folderConfig, record: documentConfig } = browser; if (isFolderRow(row)) { // If the user cannot manage folder structure, no need to show the menu. diff --git a/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Table.tsx b/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Table.tsx index 8a22f1937dc..36a81907d62 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Table.tsx +++ b/packages/app-website-builder/src/modules/pages/PagesList/components/Table/Table.tsx @@ -4,11 +4,13 @@ import { useDocumentList } from "~/modules/pages/PagesList/useDocumentList.js"; import { useSortPages } from "~/features/pages/index.js"; import { useSelectPages } from "~/features/pages/selectPages/useSelectPages.js"; import type { TableRow } from "~/modules/pages/types.js"; +import { usePageListConfig } from "~/modules/pages/configs/index.js"; export const Table = () => { const { vm } = useDocumentList(); const { sortPages } = useSortPages(); const { selectPages } = useSelectPages(); + const { browser } = usePageListConfig(); const data = useMemo(() => { return [...createFoldersData(vm.folders), ...createRecordsData(vm.data)]; @@ -20,6 +22,7 @@ export const Table = () => { return ( + columns={browser.table.columns} data={data} loading={vm.isLoading} sorting={vm.sorting} diff --git a/packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenterContext.tsx b/packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenterContext.tsx deleted file mode 100644 index fb05cc88974..00000000000 --- a/packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenterContext.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { createContext, useContext, useMemo } from "react"; -import { DocumentListPresenter } from "./DocumentListPresenter.js"; - -const DocumentListPresenterContext = createContext(null); - -export const DocumentListPresenterProvider: React.FC<{ children: React.ReactNode }> = ({ - children -}) => { - const presenter = useMemo(() => new DocumentListPresenter(), []); - return ( - - {children} - - ); -}; - -export function useDocumentListPresenter() { - const presenter = useContext(DocumentListPresenterContext); - if (!presenter) { - throw new Error( - "useDocumentListPresenter must be used within a DocumentListPresenterProvider" - ); - } - return presenter; -} diff --git a/packages/app-website-builder/src/modules/pages/PagesList/presenters/index.ts b/packages/app-website-builder/src/modules/pages/PagesList/presenters/index.ts deleted file mode 100644 index 084273cb255..00000000000 --- a/packages/app-website-builder/src/modules/pages/PagesList/presenters/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./DocumentListPresenter.js"; diff --git a/packages/app-website-builder/src/modules/pages/PagesList/useDocumentList.ts b/packages/app-website-builder/src/modules/pages/PagesList/useDocumentList.ts index ce3b8eb0ffc..cd344969a04 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/useDocumentList.ts +++ b/packages/app-website-builder/src/modules/pages/PagesList/useDocumentList.ts @@ -1,23 +1,25 @@ -import { useCallback, useState, useMemo, useEffect, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { autorun } from "mobx"; import { - useGetFolderHierarchy, + useLoadFolderHierarchy, useListFoldersByParentIds, useNavigateFolder } from "@webiny/app-aco"; -import { useDocumentListPresenter } from "./presenters/DocumentListPresenterContext.js"; import { useFilterPages, useLoadPages } from "~/features/pages/index.js"; import { useSelectPages } from "~/features/pages/selectPages/useSelectPages.js"; +import { makeDecoratableHook } from "@webiny/react-composition"; +import { useFeature } from "@webiny/app"; +import { PageListFeature } from "~/presentation/pages/PageList/feature.js"; -export const useDocumentList = () => { +export const useDocumentList = makeDecoratableHook(() => { const isFirstLoad = useRef(true); - const { folders, getFolderHierarchy } = useGetFolderHierarchy(); + const { folders, loadFolderHierarchy } = useLoadFolderHierarchy(); const { listFoldersByParentIds } = useListFoldersByParentIds(); const { currentFolderId } = useNavigateFolder(); const { loadPages: listDocuments } = useLoadPages(); const { filterPages: filterDocuments } = useFilterPages(); const { selectPages: selectDocuments } = useSelectPages(); - const presenter = useDocumentListPresenter(); + const { presenter } = useFeature(PageListFeature); const params = useMemo( () => ({ @@ -53,7 +55,7 @@ export const useDocumentList = () => { useEffect(() => { // The folders collection is empty, it must be the first render, let's load the full hierarchy. if (folders.length === 0) { - getFolderHierarchy(vm.folderId); + loadFolderHierarchy(vm.folderId); } else { // Otherwise let's load only the current folder sub-tree listFoldersByParentIds([vm.folderId]); @@ -77,4 +79,4 @@ export const useDocumentList = () => { vm, showFilters }; -}; +}); diff --git a/packages/app-website-builder/src/modules/pages/PagesListConfig.tsx b/packages/app-website-builder/src/modules/pages/PagesListConfig.tsx index ea902984c63..695b1efdd3e 100644 --- a/packages/app-website-builder/src/modules/pages/PagesListConfig.tsx +++ b/packages/app-website-builder/src/modules/pages/PagesListConfig.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { PageListConfig } from "./configs/list/index.js"; +import { InternalPageListConfig } from "./configs/list/index.js"; import { DeleteFolder, EditFolder, SetFolderPermissions } from "@webiny/app-aco"; import { CellActions, @@ -24,13 +24,13 @@ import { import { FilterByStatus } from "~/modules/pages/PagesList/components/Filters/index.js"; import { StaticPageForm } from "~/modules/pages/PagesList/components/Main/CreatePage/StaticPageForm.js"; -const { Browser } = PageListConfig; +const { Browser } = InternalPageListConfig; export const PagesListConfig = () => { return ( <> - - + } @@ -80,7 +80,7 @@ export const PagesListConfig = () => { hideable={false} className={"text-right"} /> - + ); }; diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/BulkAction.tsx b/packages/app-website-builder/src/modules/pages/configs/list/Browser/BulkAction.tsx index 22bdb4d4f86..a4a2eb4162d 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/Browser/BulkAction.tsx +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/BulkAction.tsx @@ -6,6 +6,7 @@ import { useDocumentList } from "~/modules/pages/PagesList/useDocumentList.js"; import { useSelectPages } from "~/features/pages/selectPages/useSelectPages.js"; import { Page, type PageDto, PageDtoMapper } from "~/domain/Page/index.js"; import type { TableRow } from "~/modules/pages/types.js"; +import { makeDecoratable } from "@webiny/react-composition"; export interface BulkActionConfig { name: string; @@ -20,36 +21,33 @@ export interface BulkActionProps { element?: React.ReactElement; } -export const BaseBulkAction = ({ - name, - after = undefined, - before = undefined, - remove = false, - element -}: BulkActionProps) => { - const getId = useIdGenerator("bulkAction"); +export const BaseBulkAction = makeDecoratable( + "BulkAction", + ({ name, after = undefined, before = undefined, remove = false, element }: BulkActionProps) => { + const getId = useIdGenerator("bulkAction"); - const placeAfter = after !== undefined ? getId(after) : undefined; - const placeBefore = before !== undefined ? getId(before) : undefined; + const placeAfter = after !== undefined ? getId(after) : undefined; + const placeBefore = before !== undefined ? getId(before) : undefined; - return ( - - - - {element ? ( - - ) : null} + return ( + + + + {element ? ( + + ) : null} + - - ); -}; + ); + } +); const useWorker = () => { const { vm } = useDocumentList(); diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/FolderAction.tsx b/packages/app-website-builder/src/modules/pages/configs/list/Browser/FolderAction.tsx index 043881f95a8..e8cba14d570 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/Browser/FolderAction.tsx +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/FolderAction.tsx @@ -8,11 +8,7 @@ export type { FolderActionConfig }; type FolderActionProps = React.ComponentProps; const BaseFolderAction = (props: FolderActionProps) => { - return ( - - - - ); + return ; }; export const FolderAction = Object.assign(BaseFolderAction, { diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/PageAction.tsx b/packages/app-website-builder/src/modules/pages/configs/list/Browser/PageAction.tsx index 2175238e7d4..816c2e1342b 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/Browser/PageAction.tsx +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/PageAction.tsx @@ -1,5 +1,6 @@ import React from "react"; import { AcoConfig, type RecordActionConfig } from "@webiny/app-aco"; +import { makeDecoratable } from "@webiny/react-composition"; const { Record } = AcoConfig; @@ -7,13 +8,9 @@ export type { RecordActionConfig as PageActionConfig }; type PageActionProps = React.ComponentProps; -const BasePageAction = (props: PageActionProps) => { - return ( - - - - ); -}; +const BasePageAction = makeDecoratable("PageAction", (props: PageActionProps) => { + return ; +}); export const PageAction = Object.assign(BasePageAction, { OptionsMenuItem: Record.Action.OptionsMenuItem, diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/SidebarFooter.tsx b/packages/app-website-builder/src/modules/pages/configs/list/Browser/SidebarFooter.tsx new file mode 100644 index 00000000000..b7423a57663 --- /dev/null +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/SidebarFooter.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { Property, useIdGenerator } from "@webiny/react-properties"; + +export interface SidebarFooterConfig { + name: string; + element: React.ReactElement; +} + +export interface SidebarFooterProps { + name: string; + element?: React.ReactElement; + after?: string; + before?: string; + remove?: boolean; +} + +export const SidebarFooter = ({ + name, + element, + after = undefined, + before = undefined, + remove = false +}: SidebarFooterProps) => { + const getId = useIdGenerator("sidebarFooter"); + + const placeAfter = after !== undefined ? getId(after) : undefined; + const placeBefore = before !== undefined ? getId(before) : undefined; + + return ( + + + + {element ? ( + + ) : null} + + + ); +}; diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/Table/Column.tsx b/packages/app-website-builder/src/modules/pages/configs/list/Browser/Table/Column.tsx index 266bc82dc78..7ce8216855f 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/Browser/Table/Column.tsx +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/Table/Column.tsx @@ -14,11 +14,7 @@ export type { ColumnConfig }; type ColumnProps = React.ComponentProps; const BaseColumnComponent = (props: ColumnProps) => { - return ( - - - - ); + return ; }; const BaseColumn = makeDecoratable("Column", BaseColumnComponent); diff --git a/packages/app-website-builder/src/modules/pages/configs/list/Browser/index.ts b/packages/app-website-builder/src/modules/pages/configs/list/Browser/index.ts index c07605f9d15..24dc249aa2a 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/Browser/index.ts +++ b/packages/app-website-builder/src/modules/pages/configs/list/Browser/index.ts @@ -4,6 +4,7 @@ import { FiltersToWhere, type FiltersToWhereConverter } from "./FiltersToWhere.j import { Filter, type FilterConfig } from "./Filter.js"; import { FolderAction, type FolderActionConfig } from "./FolderAction.js"; import { BulkAction, type BulkActionConfig } from "./BulkAction.js"; +import { SidebarFooter, type SidebarFooterConfig } from "./SidebarFooter.js"; export interface BrowserConfig { bulkActions: BulkActionConfig[]; @@ -12,6 +13,7 @@ export interface BrowserConfig { folderActions: FolderActionConfig[]; pageActions: PageActionConfig[]; table: TableConfig; + sidebarFooter: SidebarFooterConfig[]; } export const Browser = { @@ -24,5 +26,8 @@ export const Browser = { Page: { Action: PageAction }, - Table + Table, + Sidebar: { + Footer: SidebarFooter + } }; diff --git a/packages/app-website-builder/src/modules/pages/configs/list/PageListConfig.tsx b/packages/app-website-builder/src/modules/pages/configs/list/PageListConfig.tsx index 07bad39bebd..26b94dae299 100644 --- a/packages/app-website-builder/src/modules/pages/configs/list/PageListConfig.tsx +++ b/packages/app-website-builder/src/modules/pages/configs/list/PageListConfig.tsx @@ -1,40 +1,49 @@ import React, { useMemo } from "react"; import { createConfigurableComponent } from "@webiny/react-properties"; -import { CompositionScope } from "@webiny/react-composition"; import { Browser, type BrowserConfig } from "./Browser/index.js"; import { PageType } from "./PageType.js"; +import { type AcoConfig, useAcoConfig } from "@webiny/app-aco"; -const base = createConfigurableComponent("WbPageListConfig"); +const base = createConfigurableComponent("WbPageList"); -const ScopedPageListConfig = ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ); +const ScopedPublicPageListConfig = ({ children }: { children: React.ReactNode }) => { + return {children}; +}; + +ScopedPublicPageListConfig.displayName = "WbPageListConfig"; + +const ScopedInternalPageListConfig = ({ children }: { children: React.ReactNode }) => { + return {children}; }; -ScopedPageListConfig.displayName = "WbPageListConfig"; +ScopedInternalPageListConfig.displayName = "WbPageListConfig"; -export const PageListConfig = Object.assign(ScopedPageListConfig, { Browser, PageType }); +export const PageListConfig = Object.assign(ScopedPublicPageListConfig, { Browser, PageType }); +export const InternalPageListConfig = Object.assign(ScopedInternalPageListConfig, { + Browser, + PageType +}); export const PageListWithConfig = base.WithConfig; -interface PageListConfig { +interface PageListConfig extends AcoConfig { browser: BrowserConfig; } export function usePageListConfig() { const config = base.useConfig(); + const acoConfig = useAcoConfig(config ?? {}); const browser = config.browser || {}; return useMemo( () => ({ browser: { + ...acoConfig, ...browser, bulkActions: [...(browser.bulkActions || [])], filters: [...(browser.filters || [])], - filtersToWhere: [...(browser.filtersToWhere || [])] + filtersToWhere: [...(browser.filtersToWhere || [])], + sidebarFooter: [...(browser.sidebarFooter || [])] } }), [config] diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList.tsx index e0ab91ca1a0..b8f1a85db32 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList.tsx @@ -1,12 +1,6 @@ import React from "react"; -import { - useRoute, - useRouter, - AdminLayout, - CompositionScope, - DialogsProvider -} from "@webiny/app-admin"; -import { AcoWithConfig, NavigateFolderProvider } from "@webiny/app-aco"; +import { useRoute, useRouter, AdminLayout, DialogsProvider } from "@webiny/app-admin"; +import { NavigateFolderProvider } from "@webiny/app-aco"; import { FoldersProvider } from "@webiny/app-aco/contexts/folders.js"; import { DocumentList } from "./RedirectsList/DocumentList.js"; import { WB_REDIRECT_LATEST_VISITED_FOLDER, WB_REDIRECTS_APP } from "~/constants.js"; @@ -24,25 +18,22 @@ export const RedirectsList = () => { const navigateToFolder = (folderId: string) => { router.goToRoute(Routes.Redirects.List, { folderId }); }; + return ( - - - - - - - - - - - - - - - + + + + + + + + + + + ); }; diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Sidebar/Sidebar.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Sidebar/Sidebar.tsx index 51fcbe0adf3..90a7c2501b5 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Sidebar/Sidebar.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Sidebar/Sidebar.tsx @@ -1,12 +1,15 @@ import React from "react"; import { FolderTree, useNavigateFolder } from "@webiny/app-aco"; +import { useRedirectListConfig } from "~/modules/redirects/configs/index.js"; const Sidebar = () => { const { currentFolderId, navigateToFolder } = useNavigateFolder(); + const { browser } = useRedirectListConfig(); return (
navigateToFolder(data.id)} enableActions={true} diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Delete.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Delete.tsx index 9f4c767488c..80d39e1d57a 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Delete.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Delete.tsx @@ -10,7 +10,7 @@ export const Delete = () => { redirect }); - const { OptionsMenuItem } = RedirectListConfig.Browser.Record.Action; + const { OptionsMenuItem } = RedirectListConfig.Browser.Redirect.Action; return ( { const { redirect } = useRedirect(); diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Move.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Move.tsx index 49d1f6f1b46..c2280adcb81 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Move.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Actions/Move.tsx @@ -4,7 +4,7 @@ import { useMoveRedirectToFolderDialog } from "~/modules/redirects/RedirectsList import { useRedirect } from "~/modules/redirects/RedirectsList/hooks/useRedirect.js"; import { RedirectListConfig } from "~/modules/redirects/configs/index.js"; -const { OptionsMenuItem } = RedirectListConfig.Browser.Record.Action; +const { OptionsMenuItem } = RedirectListConfig.Browser.Redirect.Action; export const Move = () => { const { redirect } = useRedirect(); diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellActions.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellActions.tsx index ed4228e5d7c..edf5b353f7c 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellActions.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellActions.tsx @@ -1,15 +1,15 @@ import React from "react"; -import { FolderProvider, useAcoConfig } from "@webiny/app-aco"; +import { FolderProvider } from "@webiny/app-aco"; import { makeDecoratable, OptionsMenu } from "@webiny/app-admin"; import { RedirectProvider } from "~/modules/redirects/RedirectsList/hooks/useRedirect.js"; -import { RedirectListConfig } from "~/modules/redirects/configs/index.js"; +import { RedirectListConfig, useRedirectListConfig } from "~/modules/redirects/configs/index.js"; import type { RedirectDto } from "~/domain/Redirect/index.js"; const { useTableRow, isFolderRow } = RedirectListConfig.Browser.Table.Column; const DefaultCellActions = () => { const { row } = useTableRow(); - const { folder: folderConfig, record: documentConfig } = useAcoConfig(); + const { browser } = useRedirectListConfig(); if (isFolderRow(row)) { // If the user cannot manage folder structure, no need to show the menu. @@ -19,14 +19,14 @@ const DefaultCellActions = () => { return ( - + ); } return ( - + ); }; diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellRedirectType.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellRedirectType.tsx new file mode 100644 index 00000000000..536ed02e9bc --- /dev/null +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Cells/CellRedirectType.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { RedirectListConfig } from "~/modules/redirects/configs/index.js"; + +const { useTableRow, isFolderRow } = RedirectListConfig.Browser.Table.Column; + +export const CellRedirectType = () => { + const { row } = useTableRow(); + + if (isFolderRow(row)) { + return <>{"-"}; + } + + const { redirectType } = row.data; + + return <>{redirectType}; +}; diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Table.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Table.tsx index 79a403ca4ae..5bb0c58c4da 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Table.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/components/Table/Table.tsx @@ -4,14 +4,17 @@ import type { TableRow } from "~/modules/redirects/RedirectsList/presenters/inde import { useDocumentList } from "~/modules/redirects/RedirectsList/useDocumentList.js"; import { useSortRedirects } from "~/features/redirects/index.js"; import { useSelectRedirects } from "~/features/redirects/selectRedirects/useSelectRedirects.js"; +import { useRedirectListConfig } from "~/modules/redirects/configs/index.js"; export const Table = () => { const { vm } = useDocumentList(); const { sortRedirects } = useSortRedirects(); const { selectRedirects } = useSelectRedirects(); + const { browser } = useRedirectListConfig(); return ( + columns={browser.table.columns} data={vm.data} loading={vm.isLoading} sorting={vm.sorting} diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/DocumentListPresenter.ts b/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/DocumentListPresenter.ts index 19f41c9c020..8227eeef1df 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/DocumentListPresenter.ts +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/DocumentListPresenter.ts @@ -1,3 +1,5 @@ +import type { Folder } from "@webiny/app-aco/domain/folder/Folder.js"; +import { folderCacheFactory } from "@webiny/app-aco/features/folders/cache/index.js"; import { makeAutoObservable } from "mobx"; import orderBy from "lodash/orderBy.js"; import { loadingActions, ROOT_FOLDER, WB_REDIRECTS_APP } from "~/constants.js"; @@ -11,8 +13,6 @@ import { sortRepositoryFactory } from "@webiny/app-utils"; import { type IListCache, redirectListCache } from "~/domain/Redirect/index.js"; -import type { Folder } from "@webiny/app-aco"; -import { folderCacheFactory } from "@webiny/app-aco"; import { TableRowMapper } from "~/modules/redirects/RedirectsList/presenters/TableRowMapper.js"; import { type ISearchRepository, searchRepositoryFactory } from "~/domain/Search/index.js"; import { diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/TableRowMapper.ts b/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/TableRowMapper.ts index ff964ab2a0a..74c9a069a23 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/TableRowMapper.ts +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/presenters/TableRowMapper.ts @@ -1,5 +1,6 @@ -import type { Folder, FolderTableRow, RecordTableRow } from "@webiny/app-aco"; -import { FolderDtoMapper } from "@webiny/app-aco"; +import type { FolderTableRow, RecordTableRow } from "@webiny/app-aco"; +import type { Folder } from "@webiny/app-aco/domain/folder/Folder.js"; +import { FolderDtoMapper } from "@webiny/app-aco/domain/folder/FolderDtoMapper.js"; import { type Redirect, type RedirectDto, RedirectDtoMapper } from "~/domain/Redirect/index.js"; export type RedirectTableRow = RecordTableRow; diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsList/useDocumentList.ts b/packages/app-website-builder/src/modules/redirects/RedirectsList/useDocumentList.ts index 17b4400ac85..4ed7fd9112f 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsList/useDocumentList.ts +++ b/packages/app-website-builder/src/modules/redirects/RedirectsList/useDocumentList.ts @@ -1,17 +1,15 @@ import { useCallback, useState, useMemo, useEffect, useRef } from "react"; import { autorun } from "mobx"; -import { - useGetFolderHierarchy, - useListFoldersByParentIds, - useNavigateFolder -} from "@webiny/app-aco"; +import { useListFoldersByParentIds } from "@webiny/app-aco"; +import { useLoadFolderHierarchy } from "@webiny/app-aco"; +import { useNavigateFolder } from "@webiny/app-aco"; import { useDocumentListPresenter } from "./presenters/DocumentListPresenterContext.js"; import { useFilterRedirects, useLoadRedirects } from "~/features/redirects/index.js"; import { useSelectRedirects } from "~/features/redirects/selectRedirects/useSelectRedirects.js"; export const useDocumentList = () => { const isFirstLoad = useRef(true); - const { folders, getFolderHierarchy } = useGetFolderHierarchy(); + const { folders, loadFolderHierarchy } = useLoadFolderHierarchy(); const { listFoldersByParentIds } = useListFoldersByParentIds(); const { currentFolderId } = useNavigateFolder(); const { loadRedirects: listDocuments } = useLoadRedirects(); @@ -53,7 +51,7 @@ export const useDocumentList = () => { useEffect(() => { // The folders collection is empty, it must be the first render, let's load the full hierarchy. if (folders.length === 0) { - getFolderHierarchy(vm.folderId); + loadFolderHierarchy(vm.folderId); } else { // Otherwise let's load only the current folder sub-tree listFoldersByParentIds([vm.folderId]); diff --git a/packages/app-website-builder/src/modules/redirects/RedirectsListConfig.tsx b/packages/app-website-builder/src/modules/redirects/RedirectsListConfig.tsx index 1265572a811..1c6d00b7f8b 100644 --- a/packages/app-website-builder/src/modules/redirects/RedirectsListConfig.tsx +++ b/packages/app-website-builder/src/modules/redirects/RedirectsListConfig.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { CellRedirectType } from "~/modules/redirects/RedirectsList/components/Table/Cells/CellRedirectType.js"; import { RedirectListConfig } from "./configs/index.js"; import { DeleteFolder, EditFolder, SetFolderPermissions } from "@webiny/app-aco"; import { @@ -28,9 +29,9 @@ export const RedirectsListConfig = () => { } /> } /> } /> - } /> - } /> - } /> + } /> + } /> + } /> } /> } /> { hideable={false} size={200} /> - + } + /> } /> ; const BaseFolderAction = (props: FolderActionProps) => { - return ( - - - - ); + return ; }; export const FolderAction = Object.assign(BaseFolderAction, { diff --git a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/RedirectAction.tsx b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/RedirectAction.tsx index 79c2910a174..1f079b58d51 100644 --- a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/RedirectAction.tsx +++ b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/RedirectAction.tsx @@ -8,11 +8,7 @@ export type { RecordActionConfig }; type RedirectActionProps = React.ComponentProps; const BaseRedirectAction = (props: RedirectActionProps) => { - return ( - - - - ); + return ; }; export const RedirectAction = Object.assign(BaseRedirectAction, { diff --git a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/Table/Column.tsx b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/Table/Column.tsx index c4e9d64fbb6..99cccf7c3ba 100644 --- a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/Table/Column.tsx +++ b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/Table/Column.tsx @@ -16,11 +16,7 @@ export type { ColumnConfig }; type ColumnProps = React.ComponentProps; const BaseColumnComponent = (props: ColumnProps) => { - return ( - - - - ); + return ; }; const BaseColumn = makeDecoratable("Column", BaseColumnComponent); diff --git a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/index.ts b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/index.ts index 713f883965f..8170c975308 100644 --- a/packages/app-website-builder/src/modules/redirects/configs/list/Browser/index.ts +++ b/packages/app-website-builder/src/modules/redirects/configs/list/Browser/index.ts @@ -1,19 +1,18 @@ -import { - RedirectAction, - type RecordActionConfig as RedirectActionConfig -} from "./RedirectAction.js"; +import { RedirectAction } from "./RedirectAction.js"; import { Table, type TableConfig } from "./Table/index.js"; import { FiltersToWhere, type FiltersToWhereConverter } from "./FiltersToWhere.js"; import { Filter, type FilterConfig } from "./Filter.js"; -import { FolderAction, type FolderActionConfig } from "./FolderAction.js"; +import { FolderAction } from "./FolderAction.js"; import { BulkAction, type BulkActionConfig } from "./BulkAction.js"; +import type { FolderConfig } from "@webiny/app-aco/config/folder/index.js"; +import type { RecordConfig } from "@webiny/app-aco/config/record/index.js"; export interface BrowserConfig { bulkActions: BulkActionConfig[]; filters: FilterConfig[]; filtersToWhere: FiltersToWhereConverter[]; - folderActions: FolderActionConfig[]; - redirectActions: RedirectActionConfig[]; + folder: FolderConfig; + redirect: RecordConfig; table: TableConfig; } @@ -24,7 +23,7 @@ export const Browser = { Folder: { Action: FolderAction }, - Record: { + Redirect: { Action: RedirectAction }, Table diff --git a/packages/app-website-builder/src/modules/redirects/configs/list/RedirectListConfig.tsx b/packages/app-website-builder/src/modules/redirects/configs/list/RedirectListConfig.tsx index 025514daf1b..f974bbe6c61 100644 --- a/packages/app-website-builder/src/modules/redirects/configs/list/RedirectListConfig.tsx +++ b/packages/app-website-builder/src/modules/redirects/configs/list/RedirectListConfig.tsx @@ -1,16 +1,12 @@ import React, { useMemo } from "react"; import { createConfigurableComponent } from "@webiny/react-properties"; -import { CompositionScope } from "@webiny/react-composition"; import { Browser, type BrowserConfig } from "./Browser/index.js"; +import { useAcoConfig } from "@webiny/app-aco"; -const base = createConfigurableComponent("WbRedirectListConfig"); +const base = createConfigurableComponent("WbRedirectList"); const ScopedRedirectListConfig = ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ); + return {children}; }; ScopedRedirectListConfig.displayName = "WbRedirectListConfig"; @@ -24,6 +20,7 @@ interface RedirectListConfig { export function useRedirectListConfig() { const config = base.useConfig(); + const acoConfig = useAcoConfig(config); const browser = config.browser || {}; @@ -33,7 +30,10 @@ export function useRedirectListConfig() { ...browser, bulkActions: [...(browser.bulkActions || [])], filters: [...(browser.filters || [])], - filtersToWhere: [...(browser.filtersToWhere || [])] + filtersToWhere: [...(browser.filtersToWhere || [])], + table: acoConfig.table, + folder: acoConfig.folder, + redirect: acoConfig.record } }), [config] diff --git a/packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenter.ts b/packages/app-website-builder/src/presentation/pages/PageList/DocumentListPresenter.ts similarity index 91% rename from packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenter.ts rename to packages/app-website-builder/src/presentation/pages/PageList/DocumentListPresenter.ts index 882af7641a2..5b28c03ccde 100644 --- a/packages/app-website-builder/src/modules/pages/PagesList/presenters/DocumentListPresenter.ts +++ b/packages/app-website-builder/src/presentation/pages/PageList/DocumentListPresenter.ts @@ -1,3 +1,6 @@ +import type { Folder } from "@webiny/app-aco/domain/folder/Folder.js"; +import { FolderDtoMapper } from "@webiny/app-aco/domain/folder/FolderDtoMapper.js"; +import { folderCacheFactory } from "@webiny/app-aco/features/folders/cache/index.js"; import { makeAutoObservable } from "mobx"; import orderBy from "lodash/orderBy.js"; import { loadingActions, ROOT_FOLDER, WB_PAGE_APP } from "~/constants.js"; @@ -11,20 +14,15 @@ import { sortRepositoryFactory } from "@webiny/app-utils"; import { type IListCache, type Page, PageDtoMapper, pageListCache } from "~/domain/Page/index.js"; -import { type Folder, FolderDtoMapper } from "@webiny/app-aco"; -import { folderCacheFactory } from "@webiny/app-aco"; import { type ISearchRepository, searchRepositoryFactory } from "~/domain/Search/index.js"; import { type ISelectedItemsRepository, selectedItemsRepositoryFactory } from "~/domain/SelectedItem/index.js"; import { filterRepositoryFactory, type IFilterRepository } from "~/domain/Filter/index.js"; +import { PageListPresenter } from "./abstractions.js"; -interface DocumentListPresenterParams { - folderId: string; -} - -class DocumentListPresenter { +class DocumentListPresenter implements PageListPresenter.Interface { private folderId: string = ROOT_FOLDER; private foldersCache: IListCache; private documentsCache: IListCache; @@ -50,11 +48,11 @@ class DocumentListPresenter { makeAutoObservable(this); } - public init(params: DocumentListPresenterParams) { + public init(params: PageListPresenter.Init) { this.folderId = params.folderId; } - public get vm() { + public get vm(): PageListPresenter.ViewModel { const data = this.getVmDocuments(); const folders = this.getVmFolders(); const isEmpty = !this.getIsLoading() && data.length === 0 && folders.length === 0; @@ -163,4 +161,4 @@ class DocumentListPresenter { }; } -export { DocumentListPresenter, type DocumentListPresenterParams }; +export { DocumentListPresenter }; diff --git a/packages/app-website-builder/src/presentation/pages/PageList/abstractions.ts b/packages/app-website-builder/src/presentation/pages/PageList/abstractions.ts new file mode 100644 index 00000000000..5d5fee28a5d --- /dev/null +++ b/packages/app-website-builder/src/presentation/pages/PageList/abstractions.ts @@ -0,0 +1,40 @@ +import type { FolderDto } from "@webiny/app-aco/domain/folder/FolderDto.js"; +import { createAbstraction } from "@webiny/feature/admin"; +import type { ColumnSorting } from "@webiny/app-utils"; +import type { PageDto } from "~/domain/Page/index.js"; + +export interface IDocumentListPresenterInit { + folderId: string; +} + +export interface IDocumentListVm { + folderId: string; + title: string | undefined; + data: PageDto[]; + folders: FolderDto[]; + selected: any[]; + meta: { totalCount: number; currentCount: number }; + sorting: ColumnSorting[]; + searchQuery: string; + searchLabel: string; + isSearch: boolean; + isEmpty: boolean; + isRoot: boolean; + isLoading: boolean; + isLoadingMore: boolean; + isFilterVisible: boolean; +} + +export interface IDocumentListPresenter { + init(params: IDocumentListPresenterInit): void; + showFilters(show: boolean): void; + vm: IDocumentListVm; +} + +export const PageListPresenter = createAbstraction("PageListPresenter"); + +export namespace PageListPresenter { + export type Interface = IDocumentListPresenter; + export type Init = IDocumentListPresenterInit; + export type ViewModel = IDocumentListVm; +} diff --git a/packages/app-website-builder/src/presentation/pages/PageList/feature.ts b/packages/app-website-builder/src/presentation/pages/PageList/feature.ts new file mode 100644 index 00000000000..eaec808558d --- /dev/null +++ b/packages/app-website-builder/src/presentation/pages/PageList/feature.ts @@ -0,0 +1,13 @@ +import { createFeature } from "@webiny/feature/admin"; +import { PageListPresenter } from "./abstractions.js"; +import { DocumentListPresenter } from "./DocumentListPresenter.js"; + +export const PageListFeature = createFeature({ + name: "PageListFeature", + register(container) { + container.registerInstance(PageListPresenter, new DocumentListPresenter()); + }, + resolve(container) { + return { presenter: container.resolve(PageListPresenter) }; + } +}); diff --git a/packages/app-website-builder/src/presentation/pages/PageList/index.ts b/packages/app-website-builder/src/presentation/pages/PageList/index.ts new file mode 100644 index 00000000000..ea9ebfb8417 --- /dev/null +++ b/packages/app-website-builder/src/presentation/pages/PageList/index.ts @@ -0,0 +1 @@ +export * from "./abstractions.js"; diff --git a/packages/app-website-builder/tsconfig.build.json b/packages/app-website-builder/tsconfig.build.json index ab7ca3dc831..bcaea35cae4 100644 --- a/packages/app-website-builder/tsconfig.build.json +++ b/packages/app-website-builder/tsconfig.build.json @@ -10,6 +10,7 @@ { "path": "../app-security/tsconfig.build.json" }, { "path": "../app-utils/tsconfig.build.json" }, { "path": "../error/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" }, { "path": "../form/tsconfig.build.json" }, { "path": "../lexical-converter/tsconfig.build.json" }, { "path": "../lexical-editor/tsconfig.build.json" }, @@ -51,6 +52,10 @@ "@webiny/app-utils": ["../app-utils/src"], "@webiny/error/*": ["../error/src/*"], "@webiny/error": ["../error/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/form/*": ["../form/src/*"], "@webiny/form": ["../form/src"], "@webiny/lexical-converter/*": ["../lexical-converter/src/*"], diff --git a/packages/app-website-builder/tsconfig.json b/packages/app-website-builder/tsconfig.json index bac60848c42..01f36a424ed 100644 --- a/packages/app-website-builder/tsconfig.json +++ b/packages/app-website-builder/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../app-security" }, { "path": "../app-utils" }, { "path": "../error" }, + { "path": "../feature" }, { "path": "../form" }, { "path": "../lexical-converter" }, { "path": "../lexical-editor" }, @@ -51,6 +52,10 @@ "@webiny/app-utils": ["../app-utils/src"], "@webiny/error/*": ["../error/src/*"], "@webiny/error": ["../error/src"], + "@webiny/feature/api": ["../feature/src/api/index.js"], + "@webiny/feature/admin": ["../feature/src/admin/index.js"], + "@webiny/feature/*": ["../feature/src/*"], + "@webiny/feature": ["../feature/src"], "@webiny/form/*": ["../form/src/*"], "@webiny/form": ["../form/src"], "@webiny/lexical-converter/*": ["../lexical-converter/src/*"], diff --git a/packages/app-workflows/src/Components/WorkflowState/Bar/Bars/WorkflowStateBarReview.tsx b/packages/app-workflows/src/Components/WorkflowState/Bar/Bars/WorkflowStateBarReview.tsx index d6a66c0b675..12a9f496288 100644 --- a/packages/app-workflows/src/Components/WorkflowState/Bar/Bars/WorkflowStateBarReview.tsx +++ b/packages/app-workflows/src/Components/WorkflowState/Bar/Bars/WorkflowStateBarReview.tsx @@ -12,6 +12,7 @@ export const WorkflowStateBarReview = WorkflowStateBarComponent.createDecorator( const { presenter } = props; const { step } = presenter.vm; + /** * If current user cannot review the step, continue. */ diff --git a/packages/app-workflows/src/Components/WorkflowState/Bar/Dialogs/CommentDialog.tsx b/packages/app-workflows/src/Components/WorkflowState/Bar/Dialogs/CommentDialog.tsx index 1b6fddc88f9..cf398dddf65 100644 --- a/packages/app-workflows/src/Components/WorkflowState/Bar/Dialogs/CommentDialog.tsx +++ b/packages/app-workflows/src/Components/WorkflowState/Bar/Dialogs/CommentDialog.tsx @@ -9,7 +9,7 @@ interface ICommentDialogProps { export const CommentDialog = (props: ICommentDialogProps) => { const { presenter } = props; - const step = presenter.vm.step; + const step = presenter.vm.dialog?.step; if (!step) { return null; diff --git a/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBar.tsx b/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBar.tsx index da9cc204f28..1c938b0c58c 100644 --- a/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBar.tsx +++ b/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBar.tsx @@ -13,8 +13,28 @@ import { WorkflowStateBarComponent } from "./WorkflowStateBarComponent.js"; import { WorkflowStateBarStartReview } from "./Bars/WorkflowStateBarStartReview.js"; import { useWorkflowState } from "../useWorkflowState.js"; import { WorkflowStateBarDialogs } from "~/Components/WorkflowState/Bar/WorkflowStateBarDialogs.js"; +import type { IWorkflow, IWorkflowState } from "~/types.js"; -export const WorkflowStateBar = observer(() => { +interface IWorkflowStateBarPropsChildrenParams { + workflow: IWorkflow; + state?: IWorkflowState | null; +} +interface IWorkflowStateBarPropsChildren { + ( + params: IWorkflowStateBarPropsChildrenParams + ): React.ReactElement | React.ReactElement[] | null; +} +export interface IWorkflowStateBarProps { + /** + * To have access to a state and workflow, render function is meant to be used. + * It will rerender on every state change, and it will provide the latest state and workflow. + * + * Also, this way we avoid using MobX in the consumers of this component. + */ + children?: IWorkflowStateBarPropsChildren | React.ReactElement | React.ReactElement[] | null; +} + +export const WorkflowStateBar = observer((props: IWorkflowStateBarProps) => { const { presenter } = useWorkflowState(); /** * If no workflow, do not show anything - there might not be a workflow assigned. @@ -39,6 +59,9 @@ export const WorkflowStateBar = observer(() => { + {typeof props.children === "function" + ? props.children({ state: presenter.vm.state, workflow: presenter.vm.workflow }) + : props.children} ); }); diff --git a/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBarDialogs.tsx b/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBarDialogs.tsx index be31c6de630..8b8425e607f 100644 --- a/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBarDialogs.tsx +++ b/packages/app-workflows/src/Components/WorkflowState/Bar/WorkflowStateBarDialogs.tsx @@ -18,7 +18,7 @@ import { export const WorkflowStateBarDialogs = observer(() => { const { presenter } = useWorkflowState(); - switch (presenter.vm.dialog) { + switch (presenter.vm.dialog?.type) { case "cancelReview": return ; case "requestReview": diff --git a/packages/app-workflows/src/Components/WorkflowState/WorkflowStateProvider.tsx b/packages/app-workflows/src/Components/WorkflowState/WorkflowStateProvider.tsx index 4c15c894269..7bcf5c2050f 100644 --- a/packages/app-workflows/src/Components/WorkflowState/WorkflowStateProvider.tsx +++ b/packages/app-workflows/src/Components/WorkflowState/WorkflowStateProvider.tsx @@ -44,7 +44,7 @@ export const WorkflowStateProvider = (props: IWorkflowStateProps) => { workflowsRepository, title }); - }, [app, id, identity, client, title]); + }, [app, id, identity?.id, title]); return ( diff --git a/packages/app-workflows/src/Components/WorkflowState/index.ts b/packages/app-workflows/src/Components/WorkflowState/index.ts index c89c6af85fe..486810c9262 100644 --- a/packages/app-workflows/src/Components/WorkflowState/index.ts +++ b/packages/app-workflows/src/Components/WorkflowState/index.ts @@ -1,3 +1,4 @@ +export * from "./useWorkflowState.js"; export * from "./WorkflowStateProvider.js"; export * from "./Bar/WorkflowStateBar.js"; export * from "./Overlay/WorkflowStateOverlay.js"; diff --git a/packages/app-workflows/src/Components/WorkflowStateList/List/WorkflowStateList.tsx b/packages/app-workflows/src/Components/WorkflowStateList/List/WorkflowStateList.tsx index e501791961b..afb9713427d 100644 --- a/packages/app-workflows/src/Components/WorkflowStateList/List/WorkflowStateList.tsx +++ b/packages/app-workflows/src/Components/WorkflowStateList/List/WorkflowStateList.tsx @@ -13,13 +13,6 @@ const columns: DataTableColumns = { header: "Title", enableSorting: true }, - savedOn: { - header: "Last Modified", - enableSorting: true, - cell(state) { - return ; - } - }, createdBy: { header: "Submitted By", enableSorting: false, @@ -27,6 +20,24 @@ const columns: DataTableColumns = { return <>{state.createdBy.displayName}; } }, + savedBy: { + header: "Modified By", + enableSorting: false, + cell(state) { + if (state.savedBy.id === state.createdBy.id) { + return <>-; + } + return <>{state.savedBy.displayName}; + } + }, + savedOn: { + header: "Last Modified", + enableSorting: true, + cell(state) { + return ; + } + }, + currentStep: { header: "Workflow", enableSorting: false, diff --git a/packages/app-workflows/src/Components/WorkflowsEditor/Editor/Step/Step.tsx b/packages/app-workflows/src/Components/WorkflowsEditor/Editor/Step/Step.tsx index cda06790e20..a63a2459e27 100644 --- a/packages/app-workflows/src/Components/WorkflowsEditor/Editor/Step/Step.tsx +++ b/packages/app-workflows/src/Components/WorkflowsEditor/Editor/Step/Step.tsx @@ -145,7 +145,6 @@ export const Step = observer( -
) : ( { const item = await this.repository.requestReview({ app: this.app, @@ -133,7 +130,7 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { }); runInAction(() => { this.state = item ? WorkflowStateModel.create(item) : null; - this.dialog = null; + this.clearDialog(); }); }; @@ -146,7 +143,9 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { if (!item) { return; } - this.dialog = "start:success"; + this.setDialog({ + type: "start:success" + }); }); }; @@ -160,7 +159,9 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { if (!item) { return; } - this.dialog = "approve:success"; + this.setDialog({ + type: "approve:success" + }); }); }; @@ -174,7 +175,9 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { if (!item) { return; } - this.dialog = "reject:success"; + this.setDialog({ + type: "reject:success" + }); }); }; @@ -182,7 +185,7 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { await this.repository.cancel(this.state!.id); runInAction(() => { this.state = null; - this.dialog = null; + this.clearDialog(); }); }; @@ -195,44 +198,56 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { if (!item) { return; } - this.dialog = "takeOver:success"; + this.setDialog({ + type: "takeOver:success", + step: null + }); }); }; showCancelReviewDialog = () => { runInAction(() => { - this.dialog = "cancelReview"; + this.setDialog({ + type: "cancelReview" + }); }); }; showRequestReviewDialog = () => { runInAction(() => { - this.dialog = "requestReview"; + this.setDialog({ + type: "requestReview" + }); }); }; showStartDialog = () => { runInAction(() => { - this.dialog = "start"; + this.setDialog({ + type: "start" + }); }); }; showApproveDialog = () => { runInAction(() => { - this.dialog = "approve"; + this.setDialog({ + type: "approve" + }); }); }; hideDialog = () => { runInAction(() => { - this.dialog = null; - this.step = null; + this.clearDialog(); }); }; showRejectDialog = () => { runInAction(() => { - this.dialog = "reject"; + this.setDialog({ + type: "reject" + }); }); }; @@ -242,14 +257,18 @@ export class WorkflowStatePresenter implements IWorkflowStatePresenter { return; } runInAction(() => { - this.step = step; - this.dialog = "comment"; + this.setDialog({ + type: "comment", + step + }); }); }; showTakeOverDialog = () => { runInAction(() => { - this.dialog = "takeOver"; + this.setDialog({ + type: "takeOver" + }); }); }; } diff --git a/packages/app-workflows/src/Presenters/abstractions/WorkflowStatePresenter.ts b/packages/app-workflows/src/Presenters/abstractions/WorkflowStatePresenter.ts index 48ac7f6140c..60ee616c804 100644 --- a/packages/app-workflows/src/Presenters/abstractions/WorkflowStatePresenter.ts +++ b/packages/app-workflows/src/Presenters/abstractions/WorkflowStatePresenter.ts @@ -1,20 +1,8 @@ -import type { IWorkflow, IWorkflowState } from "~/types.js"; +import type { IWorkflow, IWorkflowState, IWorkflowStateStep } from "~/types.js"; import type { IWorkflowStateError } from "~/Gateways/abstraction/WorkflowStateGateway.js"; -import type { IWorkflowStateStepModel } from "~/Models/index.js"; -export interface IWorkflowStatePresenterViewModel { - id: string; - app: string; - loading: boolean; - workflow: IWorkflow | null; - error: IWorkflowStateError | null; - state: IWorkflowState | null | undefined; - step: IWorkflowStateStepModel | null; - lastApprovedStep: IWorkflowStateStepModel | null; - lastRejectedStep: IWorkflowStateStepModel | null; - nextStep: IWorkflowStateStepModel | null; - canCancel: boolean; - dialog: +export interface IWorkflowStatePresenterViewModelDialog { + type: | "cancelReview" | "requestReview" | "start" @@ -25,8 +13,23 @@ export interface IWorkflowStatePresenterViewModel { | "reject:success" | "comment" | "takeOver" - | "takeOver:success" - | null; + | "takeOver:success"; + step?: IWorkflowStateStep | null; +} + +export interface IWorkflowStatePresenterViewModel { + id: string; + app: string; + loading: boolean; + workflow: IWorkflow | null; + error: IWorkflowStateError | null; + state: IWorkflowState | null | undefined; + step: IWorkflowStateStep | null | undefined; + lastApprovedStep: IWorkflowStateStep | null; + lastRejectedStep: IWorkflowStateStep | null; + nextStep: IWorkflowStateStep | null; + canCancel: boolean; + dialog: IWorkflowStatePresenterViewModelDialog | null; } export interface IWorkflowStatePresenter { diff --git a/packages/app-workflows/src/index.tsx b/packages/app-workflows/src/index.tsx index df371be3008..4f005eea992 100644 --- a/packages/app-workflows/src/index.tsx +++ b/packages/app-workflows/src/index.tsx @@ -16,7 +16,7 @@ import { WorkflowsEditor } from "./Components/WorkflowsEditor/index.js"; import { HasWorkflowsEditorPermission } from "./Components/WorkflowsPermissions/index.js"; export { useCanUseWorkflows } from "./hooks/canUseWorkflows.js"; -export { useWorkflowState } from "./Components/WorkflowState/useWorkflowState.js"; +export { useWorkflowState } from "./Components/WorkflowState/index.js"; export type { IWorkflowApplication, IWorkflowState } from "~/types.js"; export { WorkflowStateValue } from "~/types.js"; export { useWorkflowsPermission } from "./Components/WorkflowsPermissions/index.js"; diff --git a/packages/app/package.json b/packages/app/package.json index 4c9c128022b..9746bb4b3d8 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -26,7 +26,7 @@ "@apollo/react-hooks": "^3.1.5", "@emotion/styled": "11.10.6", "@types/react": "18.2.79", - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/i18n": "0.0.0", "@webiny/i18n-react": "0.0.0", "@webiny/plugins": "0.0.0", diff --git a/packages/cli-core/package.json b/packages/cli-core/package.json index b38c0cf4123..b39bb6820d0 100644 --- a/packages/cli-core/package.json +++ b/packages/cli-core/package.json @@ -10,7 +10,7 @@ "description": "A command line interface (CLI) for managing Webiny projects.", "license": "MIT", "dependencies": { - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/project": "0.0.0", "@webiny/pulumi-sdk": "0.0.0", "@webiny/telemetry": "0.0.0", diff --git a/packages/cli/files/references.json b/packages/cli/files/references.json index 219f738ba6b..f5123681f07 100644 --- a/packages/cli/files/references.json +++ b/packages/cli/files/references.json @@ -1 +1 @@ -{"dependencies":[{"name":"@apollo/react-common","version":"3.1.4","files":["/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"@apollo/react-components","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-mailer/package.json"]},{"name":"@apollo/react-hooks","version":"3.1.5","files":["/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-record-locking/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json"]},{"name":"@auth0/auth0-react","version":"2.5.0","files":["/packages/app-admin-auth0/package.json"]},{"name":"@aws-amplify/auth","version":"5.6.15","files":["/packages/app-admin-cognito/package.json","/packages/app-cognito-authenticator/package.json"]},{"name":"@aws-sdk/client-apigatewaymanagementapi","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudfront","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-events","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-logs","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cognito-identity-provider","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb-streams","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-eventbridge","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iam","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iot","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-lambda","version":"3.942.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-s3","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-scheduler","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sfn","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sqs","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sts","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/credential-providers","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-storage","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-presigned-post","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-request-presigner","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/util-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@babel/code-frame","version":"7.27.1","files":["/packages/api-headless-cms/package.json"]},{"name":"@babel/core","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-env","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-react","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-typescript","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/runtime","version":"7.28.4","files":["/packages/build-tools/package.json"]},{"name":"@elastic/elasticsearch","version":"7.12.0","files":["/packages/api-elasticsearch/package.json","/packages/data-migration/package.json","/packages/migrations/package.json"]},{"name":"@emotion/css","version":"11.10.6","files":["/packages/app-headless-cms/package.json"]},{"name":"@emotion/react","version":"11.10.8","files":["/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-theme/package.json","/packages/theme/package.json"]},{"name":"@emotion/styled","version":"11.10.6","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@fastify/aws-lambda","version":"4.1.0","files":["/packages/handler-aws/package.json"]},{"name":"@fastify/compress","version":"7.0.3","files":["/packages/handler/package.json"]},{"name":"@fastify/cookie","version":"9.4.0","files":["/packages/handler/package.json"]},{"name":"@fortawesome/fontawesome-common-types","version":"0.3.0","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/fontawesome-svg-core","version":"1.3.0","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@fortawesome/free-brands-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-regular-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/react-fontawesome","version":"0.1.19","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@graphql-tools/merge","version":"9.1.6","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/resolvers-composition","version":"7.0.25","files":["/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/schema","version":"10.0.30","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/utils","version":"10.11.0","files":["/packages/handler-graphql/package.json"]},{"name":"@iconify/json","version":"2.2.386","files":["/packages/app-admin/package.json"]},{"name":"@lexical/code","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/hashtag","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/headless","version":"0.35.0","files":["/packages/lexical-converter/package.json"]},{"name":"@lexical/history","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/html","version":"0.35.0","files":["/packages/lexical-converter/package.json"]},{"name":"@lexical/list","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/mark","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/overflow","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/react","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/rich-text","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/selection","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/text","version":"0.35.0","files":["/packages/lexical-editor/package.json"]},{"name":"@lexical/utils","version":"0.35.0","files":["/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/app-headless-cms-scheduler/package.json"]},{"name":"@minoru/react-dnd-treeview","version":"3.5.3","files":["/packages/admin-ui/package.json"]},{"name":"@monaco-editor/react","version":"4.7.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@noble/hashes","version":"2.0.0","files":["/packages/utils/package.json"]},{"name":"@okta/okta-auth-js","version":"5.11.0","files":["/packages/app-admin-okta/package.json"]},{"name":"@okta/okta-react","version":"6.10.0","files":["/packages/app-admin-okta/package.json"]},{"name":"@okta/okta-signin-widget","version":"5.16.1","files":["/packages/app-admin-okta/package.json"]},{"name":"@pulumi/aws","version":"7.12.0","files":["/packages/project-aws/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/pulumi","version":"3.210.0","files":["/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/random","version":"4.18.4","files":["/packages/project-aws/package.json"]},{"name":"@rsbuild/core","version":"1.6.0","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-react","version":"1.4.1","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-sass","version":"1.4.0","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"1.2.2","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-type-check","version":"1.3.0","files":["/packages/build-tools/package.json"]},{"name":"@rspack/core","version":"1.5.5","files":["/packages/build-tools/package.json"]},{"name":"@smithy/node-http-handler","version":"2.5.0","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@svgr/webpack","version":"6.5.1","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/build-tools/package.json","/packages/ui/package.json"]},{"name":"@swc/plugin-emotion","version":"11.1.0","files":["/packages/build-tools/package.json"]},{"name":"@tailwindcss/postcss","version":"4.1.16","files":["/packages/build-tools/package.json"]},{"name":"@tanstack/react-table","version":"8.21.3","files":["/packages/admin-ui/package.json"]},{"name":"@types/aws-lambda","version":"8.10.152","files":["/packages/aws-helpers/package.json","/packages/aws-sdk/package.json"]},{"name":"@types/hoist-non-react-statics","version":"3.3.7","files":["/package.json"]},{"name":"@types/mime","version":"2.0.3","files":["/packages/app-admin/package.json"]},{"name":"@types/prismjs","version":"1.26.5","files":["/packages/lexical-nodes/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@types/webpack-env","version":"1.18.8","files":["/packages/build-tools/package.json"]},{"name":"accounting","version":"0.4.1","files":["/packages/i18n/package.json"]},{"name":"apollo-cache","version":"1.3.5","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"apollo-cache-inmemory","version":"1.6.6","files":["/packages/app/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"apollo-link-batch-http","version":"1.2.14","files":["/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-context","version":"1.0.20","files":["/packages/app/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-graphql-playground/package.json"]},{"name":"apollo-link-error","version":"1.1.13","files":["/packages/app/package.json"]},{"name":"apollo-link-http-common","version":"0.2.16","files":["/packages/app/package.json"]},{"name":"apollo-utilities","version":"1.3.4","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"archiver","version":"7.0.1","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"boolean","version":"3.2.0","files":["/packages/app/package.json","/packages/handler-graphql/package.json"]},{"name":"bson-objectid","version":"2.0.4","files":["/packages/utils/package.json"]},{"name":"bytes","version":"3.1.2","files":["/packages/admin-ui/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-sync-system/package.json","/packages/app/package.json","/packages/app-file-manager/package.json"]},{"name":"cache-control-parser","version":"2.0.6","files":["/packages/api-file-manager/package.json"]},{"name":"case","version":"1.6.3","files":["/packages/app-admin/package.json","/packages/project/package.json"]},{"name":"center-align","version":"1.0.1","files":["/packages/data-migration/package.json"]},{"name":"chalk","version":"4.1.2","files":["/packages/aws-layers/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/data-migration/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json"]},{"name":"cheerio","version":"1.1.2","files":["/packages/aws-helpers/package.json","/packages/lexical-converter/package.json"]},{"name":"chokidar","version":"4.0.3","files":["/packages/build-tools/package.json","/packages/project/package.json"]},{"name":"ci-info","version":"4.3.0","files":["/packages/global-config/package.json","/packages/project/package.json","/packages/telemetry/package.json"]},{"name":"class-variance-authority","version":"0.7.1","files":["/packages/admin-ui/package.json"]},{"name":"classnames","version":"2.5.1","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/ui/package.json"]},{"name":"cli-progress","version":"3.12.0","files":["/scripts/cjsToEsm/package.json"]},{"name":"cli-table3","version":"0.6.5","files":["/packages/system-requirements/package.json"]},{"name":"clsx","version":"2.1.1","files":["/packages/admin-ui/package.json"]},{"name":"cmdk","version":"1.1.1","files":["/packages/admin-ui/package.json"]},{"name":"core-js","version":"3.45.1","files":["/packages/project-aws/package.json"]},{"name":"cropperjs","version":"1.6.2","files":["/packages/app-file-manager/package.json"]},{"name":"cross-fetch","version":"3.2.0","files":["/packages/project-aws/package.json"]},{"name":"crypto-hash","version":"3.1.0","files":["/packages/app-record-locking/package.json"]},{"name":"crypto-js","version":"4.2.0","files":["/packages/api-mailer/package.json"]},{"name":"css-loader","version":"7.1.2","files":["/packages/build-tools/package.json"]},{"name":"csstype","version":"3.1.3","files":["/packages/website-builder-sdk/package.json"]},{"name":"dataloader","version":"2.2.3","files":["/packages/api-core/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"dataurl-to-blob","version":"0.0.1","files":["/packages/app-file-manager/package.json"]},{"name":"date-fns","version":"2.30.0","files":["/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json"]},{"name":"dayjs","version":"1.11.18","files":["/packages/app-file-manager/package.json"]},{"name":"debounce","version":"1.2.1","files":["/packages/project/package.json"]},{"name":"decompress","version":"4.2.1","files":["/packages/pulumi-sdk/package.json"]},{"name":"deep-equal","version":"2.2.3","files":["/packages/api-core/package.json","/packages/api-security-cognito/package.json","/packages/app-website-builder/package.json","/packages/tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/packages/website-builder-sdk/package.json"]},{"name":"dnd-core","version":"16.0.1","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"dot-object","version":"2.1.5","files":["/packages/api-headless-cms-ddb/package.json"]},{"name":"dot-prop","version":"6.0.1","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb/package.json","/packages/db-dynamodb/package.json"]},{"name":"dot-prop-immutable","version":"2.1.1","files":["/packages/app-aco/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json"]},{"name":"dotenv","version":"8.6.0","files":["/packages/project/package.json"]},{"name":"dynamodb-toolbox","version":"0.9.5","files":["/packages/db-dynamodb/package.json"]},{"name":"elastic-ts","version":"0.12.0","files":["/packages/api-elasticsearch/package.json"]},{"name":"emotion","version":"10.0.27","files":["/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-theme/package.json"]},{"name":"eslint","version":"9.39.1","files":["/packages/build-tools/package.json"]},{"name":"execa","version":"5.1.1","files":["/packages/cli/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/buildPackages/package.json"]},{"name":"exit-hook","version":"4.0.0","files":["/packages/project/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/packages/build-tools/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"fast-json-patch","version":"3.1.1","files":["/packages/website-builder-sdk/package.json"]},{"name":"fast-json-stable-stringify","version":"2.1.0","files":["/packages/website-builder-sdk/package.json"]},{"name":"fastify","version":"4.29.1","files":["/packages/handler/package.json","/packages/handler-aws/package.json"]},{"name":"fecha","version":"2.3.3","files":["/packages/i18n/package.json"]},{"name":"find-up","version":"5.0.0","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json"]},{"name":"folder-hash","version":"4.1.1","files":["/scripts/buildPackages/package.json"]},{"name":"fs-extra","version":"11.3.2","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json","/packages/pulumi-sdk/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"fuse.js","version":"7.1.0","files":["/packages/db-dynamodb/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/project-utils/package.json","/scripts/prepublishOnly/package.json"]},{"name":"glob","version":"7.2.3","files":["/packages/cli-core/package.json","/packages/i18n/package.json"]},{"name":"graphlib","version":"2.1.8","files":["/packages/app-admin/package.json"]},{"name":"graphql","version":"16.12.0","files":["/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-mailer/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-request","version":"7.3.5","files":["/packages/project/package.json"]},{"name":"graphql-scalars","version":"1.25.0","files":["/packages/handler-graphql/package.json"]},{"name":"graphql-tag","version":"2.12.6","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/handler-graphql/package.json"]},{"name":"history","version":"5.3.0","files":["/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"humanize-duration","version":"3.33.1","files":["/packages/cli-core/package.json","/packages/project/package.json"]},{"name":"inquirer","version":"12.9.6","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json"]},{"name":"invariant","version":"2.2.4","files":["/packages/app/package.json","/packages/project-aws/package.json"]},{"name":"inversify","version":"6.2.2","files":["/packages/ioc/package.json"]},{"name":"is-hotkey","version":"0.2.0","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"isnumeric","version":"0.3.3","files":["/packages/validation/package.json"]},{"name":"jose","version":"5.10.0","files":["/packages/api-core/package.json"]},{"name":"js-yaml","version":"4.1.1","files":["/packages/create-webiny-project/package.json"]},{"name":"jsdom","version":"25.0.1","files":["/packages/api-headless-cms/package.json"]},{"name":"jsesc","version":"3.1.0","files":["/packages/telemetry/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/utils/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-security-auth0/package.json","/packages/api-security-okta/package.json"]},{"name":"lexical","version":"0.35.0","files":["/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json","/packages/website-builder-sdk/package.json"]},{"name":"listr","version":"0.14.3","files":["/packages/create-webiny-project/package.json"]},{"name":"listr2","version":"5.0.8","files":["/scripts/buildPackages/package.json"]},{"name":"load-json-file","version":"6.2.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"load-script","version":"1.0.0","files":["/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json"]},{"name":"lodash","version":"4.17.21","files":["/packages/admin-ui/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security-access-management/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/build-tools/package.json","/packages/db-dynamodb/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/lexical-editor/package.json","/packages/migrations/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tasks/package.json","/packages/ui/package.json","/packages/validation/package.json","/packages/website-builder-sdk/package.json"]},{"name":"matcher","version":"5.0.0","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"md5","version":"2.3.0","files":["/packages/api-core/package.json"]},{"name":"mime","version":"3.0.0","files":["/packages/api-file-manager-s3/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json"]},{"name":"minimatch","version":"5.1.6","files":["/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app/package.json","/packages/app-file-manager/package.json","/packages/app-security/package.json","/packages/data-migration/package.json","/packages/project/package.json"]},{"name":"mobx","version":"6.15.0","files":["/packages/admin-ui/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-trash-bin/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"mobx-react-lite","version":"3.4.3","files":["/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/website-builder-react/package.json"]},{"name":"monaco-editor","version":"0.53.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"mqtt","version":"5.14.1","files":["/packages/project/package.json"]},{"name":"nanoid","version":"3.3.11","files":["/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"nanoid-dictionary","version":"4.3.0","files":["/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"neverthrow","version":"8.2.0","files":["/packages/project/package.json"]},{"name":"nodemailer","version":"7.0.10","files":["/packages/api-mailer/package.json"]},{"name":"object-hash","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json"]},{"name":"object-merge-advanced","version":"12.1.0","files":["/packages/tasks/package.json"]},{"name":"object-sizeof","version":"2.6.5","files":["/packages/tasks/package.json"]},{"name":"open","version":"10.2.0","files":["/packages/cli-core/package.json"]},{"name":"ora","version":"4.1.1","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project-aws/package.json"]},{"name":"os","version":"0.1.2","files":["/packages/create-webiny-project/package.json"]},{"name":"p-limit","version":"7.1.1","files":["/scripts/cjsToEsm/package.json"]},{"name":"p-map","version":"7.0.3","files":["/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-reduce","version":"3.0.0","files":["/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-retry","version":"7.0.0","files":["/packages/api-dynamodb-to-elasticsearch/package.json","/packages/app-file-manager-s3/package.json","/packages/project/package.json","/packages/utils/package.json"]},{"name":"pako","version":"2.1.0","files":["/packages/app-aco/package.json"]},{"name":"pino","version":"9.13.1","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json"]},{"name":"pino-pretty","version":"9.4.1","files":["/packages/cli-core/package.json","/packages/data-migration/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json"]},{"name":"pluralize","version":"8.0.0","files":["/packages/api-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"postcss","version":"8.5.6","files":["/packages/website-builder-nextjs/package.json"]},{"name":"postcss-import","version":"16.1.1","files":["/packages/website-builder-nextjs/package.json"]},{"name":"postcss-loader","version":"8.2.0","files":["/packages/build-tools/package.json"]},{"name":"process","version":"0.11.10","files":["/packages/build-tools/package.json"]},{"name":"prop-types","version":"15.8.1","files":["/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-mailer/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json"]},{"name":"radix-ui","version":"1.4.3","files":["/packages/admin-ui/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/build-tools/package.json"]},{"name":"raw.macro","version":"0.4.2","files":["/packages/app-headless-cms/package.json"]},{"name":"react","version":"18.2.0","files":["/packages/admin-ui/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/website-builder-react/package.json"]},{"name":"react-butterfiles","version":"1.3.3","files":["/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-color","version":"2.19.3","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"react-custom-scrollbars","version":"4.2.1","files":["/packages/admin-ui/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-dnd","version":"16.0.1","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dnd-html5-backend","version":"16.0.1","files":["/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/build-tools/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/react-composition/package.json","/packages/website-builder-react/package.json"]},{"name":"react-draggable","version":"4.5.0","files":["/packages/app-admin/package.json"]},{"name":"react-helmet","version":"6.1.0","files":["/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-security-access-management/package.json","/packages/app-workflows/package.json"]},{"name":"react-hotkeyz","version":"1.0.4","files":["/packages/app-aco/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-lazy-load","version":"3.1.14","files":["/packages/app-file-manager/package.json"]},{"name":"react-refresh","version":"0.11.0","files":["/packages/build-tools/package.json"]},{"name":"react-resizable","version":"3.0.5","files":["/packages/app-admin/package.json"]},{"name":"react-resizable-panels","version":"2.1.9","files":["/packages/app-admin/package.json"]},{"name":"react-style-object-to-css","version":"1.1.2","files":["/packages/lexical-theme/package.json"]},{"name":"react-test-renderer","version":"18.3.1","files":["/packages/project/package.json"]},{"name":"react-transition-group","version":"4.4.5","files":["/packages/app-admin/package.json"]},{"name":"react-virtualized","version":"9.22.6","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"read-json-sync","version":"2.0.1","files":["/packages/build-tools/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/cjsToEsm/package.json"]},{"name":"reflect-metadata","version":"0.2.2","files":["/packages/ioc/package.json"]},{"name":"regenerator-runtime","version":"0.14.1","files":["/packages/project-aws/package.json"]},{"name":"replace-in-path","version":"1.1.0","files":["/packages/project/package.json"]},{"name":"reset-css","version":"5.0.2","files":["/packages/app-admin/package.json"]},{"name":"rimraf","version":"6.0.1","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json"]},{"name":"sanitize-filename","version":"1.6.3","files":["/packages/api-file-manager-s3/package.json"]},{"name":"sass","version":"1.93.0","files":["/packages/build-tools/package.json"]},{"name":"sass-loader","version":"16.0.5","files":["/packages/build-tools/package.json"]},{"name":"semver","version":"7.7.3","files":["/packages/api-headless-cms/package.json","/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/data-migration/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json"]},{"name":"serialize-error","version":"12.0.0","files":["/packages/project/package.json","/scripts/buildPackages/package.json"]},{"name":"sharp","version":"0.34.5","files":["/packages/api-file-manager-s3/package.json"]},{"name":"short-hash","version":"1.0.0","files":["/packages/i18n/package.json"]},{"name":"slugify","version":"1.6.6","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-website-builder/package.json"]},{"name":"sonner","version":"2.0.7","files":["/packages/admin-ui/package.json"]},{"name":"srcset","version":"4.0.0","files":["/packages/aws-helpers/package.json"]},{"name":"store","version":"2.0.12","files":["/packages/app-aco/package.json","/packages/app-admin/package.json"]},{"name":"strip-ansi","version":"6.0.1","files":["/packages/telemetry/package.json"]},{"name":"style-loader","version":"3.3.4","files":["/packages/build-tools/package.json"]},{"name":"tailwind-merge","version":"2.6.0","files":["/packages/admin-ui/package.json"]},{"name":"tailwindcss","version":"4.1.16","files":["/packages/admin-ui/package.json"]},{"name":"tar","version":"6.2.1","files":["/packages/pulumi-sdk/package.json"]},{"name":"timeago-react","version":"3.0.7","files":["/packages/admin-ui/package.json"]},{"name":"tinycolor2","version":"1.6.0","files":["/packages/app-admin/package.json"]},{"name":"ts-invariant","version":"0.10.3","files":["/packages/app/package.json"]},{"name":"ts-morph","version":"24.0.0","files":["/packages/app-admin/package.json","/packages/build-tools/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"tsx","version":"4.20.5","files":["/packages/build-tools/package.json","/packages/cli/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/cjsToEsm/package.json"]},{"name":"tw-animate-css","version":"1.4.0","files":["/packages/admin-ui/package.json"]},{"name":"type-fest","version":"5.2.0","files":["/packages/admin-ui/package.json","/packages/api-websockets/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json"]},{"name":"typescript","version":"5.9.3","files":["/packages/build-tools/package.json"]},{"name":"unicode-emoji-json","version":"0.8.0","files":["/packages/app-admin/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/packages/api-headless-cms-import-export/package.json","/packages/plugins/package.json"]},{"name":"universal-router","version":"9.2.1","files":["/packages/app/package.json"]},{"name":"unzipper","version":"0.12.3","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"url-loader","version":"4.1.1","files":["/packages/build-tools/package.json"]},{"name":"use-deep-compare-effect","version":"1.8.1","files":["/packages/app-headless-cms/package.json"]},{"name":"utf-8-validate","version":"6.0.5","files":["/packages/build-tools/package.json"]},{"name":"uuid","version":"13.0.0","files":["/packages/create-webiny-project/package.json","/packages/global-config/package.json"]},{"name":"validate-npm-package-name","version":"6.0.2","files":["/packages/create-webiny-project/package.json"]},{"name":"vitest","version":"3.2.4","files":["/packages/api-headless-cms-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"warning","version":"4.0.3","files":["/packages/app/package.json"]},{"name":"write-json-file","version":"4.3.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"wts-client","version":"2.0.0","files":["/packages/telemetry/package.json"]},{"name":"yargs","version":"17.7.2","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json"]},{"name":"yesno","version":"0.4.0","files":["/packages/create-webiny-project/package.json"]},{"name":"zod","version":"3.25.76","files":["/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/cli-core/package.json","/packages/handler-graphql/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/tasks/package.json","/packages/utils/package.json"]}],"devDependencies":[{"name":"@4tw/cypress-drag-drop","version":"1.8.1","files":["/cypress-tests/package.json"]},{"name":"@babel/cli","version":"7.28.3","files":["/package.json"]},{"name":"@babel/code-frame","version":"7.27.1","files":["/package.json"]},{"name":"@babel/compat-data","version":"7.28.5","files":["/package.json"]},{"name":"@babel/core","version":"7.28.5","files":["/package.json"]},{"name":"@babel/helper-define-polyfill-provider","version":"0.6.5","files":["/package.json"]},{"name":"@babel/helper-environment-visitor","version":"7.24.7","files":["/package.json"]},{"name":"@babel/parser","version":"7.28.5","files":["/package.json"]},{"name":"@babel/plugin-proposal-class-properties","version":"7.18.6","files":["/package.json"]},{"name":"@babel/plugin-proposal-object-rest-spread","version":"7.20.7","files":["/package.json"]},{"name":"@babel/plugin-proposal-throw-expressions","version":"7.27.1","files":["/package.json"]},{"name":"@babel/plugin-syntax-object-rest-spread","version":"7.8.3","files":["/package.json"]},{"name":"@babel/plugin-transform-modules-commonjs","version":"7.27.1","files":["/package.json"]},{"name":"@babel/plugin-transform-runtime","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-env","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-react","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-typescript","version":"7.28.5","files":["/package.json"]},{"name":"@babel/register","version":"7.28.3","files":["/package.json","/packages/i18n/package.json"]},{"name":"@babel/runtime","version":"7.28.4","files":["/package.json"]},{"name":"@babel/template","version":"7.27.2","files":["/package.json"]},{"name":"@babel/traverse","version":"7.28.5","files":["/package.json"]},{"name":"@babel/types","version":"7.28.5","files":["/package.json"]},{"name":"@commitlint/cli","version":"11.0.0","files":["/package.json"]},{"name":"@commitlint/config-conventional","version":"11.0.0","files":["/package.json"]},{"name":"@elastic/elasticsearch","version":"7.12.0","files":["/packages/api-headless-cms-ddb-es/package.json","/packages/project-utils/package.json"]},{"name":"@emotion/babel-plugin","version":"11.13.5","files":["/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"@emotion/react","version":"11.10.8","files":["/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@eslint/eslintrc","version":"3.3.1","files":["/package.json"]},{"name":"@eslint/js","version":"9.39.1","files":["/package.json"]},{"name":"@faker-js/faker","version":"9.9.0","files":["/packages/api-headless-cms-es-tasks/package.json","/packages/api-sync-system/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"6.7.2","files":["/packages/admin-ui/package.json"]},{"name":"@grpc/grpc-js","version":"1.14.0","files":["/package.json"]},{"name":"@lexical/code","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/hashtag","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/headless","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/history","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/html","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/list","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/mark","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/overflow","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/react","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/rich-text","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/selection","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/text","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/utils","version":"0.35.0","files":["/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/icons/package.json"]},{"name":"@octokit/rest","version":"20.1.2","files":["/package.json"]},{"name":"@storybook/addon-a11y","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/addon-docs","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/addon-webpack5-compiler-babel","version":"3.0.6","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/react-webpack5","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@svgr/webpack","version":"6.5.1","files":["/packages/admin-ui/package.json","/packages/app-file-manager/package.json"]},{"name":"@tailwindcss/postcss","version":"4.1.16","files":["/packages/admin-ui/package.json"]},{"name":"@testing-library/cypress","version":"10.1.0","files":["/cypress-tests/package.json"]},{"name":"@testing-library/react","version":"15.0.7","files":["/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/ui/package.json"]},{"name":"@testing-library/user-event","version":"14.6.1","files":["/packages/form/package.json"]},{"name":"@types/accounting","version":"0.4.5","files":["/packages/i18n/package.json"]},{"name":"@types/adm-zip","version":"0.5.7","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/archiver","version":"6.0.3","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/babel__code-frame","version":"7.0.6","files":["/packages/api-headless-cms/package.json"]},{"name":"@types/bytes","version":"3.1.5","files":["/packages/app-admin/package.json"]},{"name":"@types/center-align","version":"1.0.2","files":["/packages/data-migration/package.json"]},{"name":"@types/cli-progress","version":"3.11.6","files":["/scripts/cjsToEsm/package.json"]},{"name":"@types/crypto-js","version":"4.2.2","files":["/packages/api-mailer/package.json"]},{"name":"@types/debounce","version":"1.2.4","files":["/packages/project/package.json"]},{"name":"@types/deep-equal","version":"1.0.4","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/dot-object","version":"2.1.6","files":["/packages/api-headless-cms-ddb/package.json"]},{"name":"@types/folder-hash","version":"4.0.4","files":["/scripts/buildPackages/package.json"]},{"name":"@types/fs-extra","version":"11.0.4","files":["/package.json"]},{"name":"@types/glob","version":"7.2.0","files":["/packages/i18n/package.json"]},{"name":"@types/graphlib","version":"2.1.12","files":["/packages/app-admin/package.json"]},{"name":"@types/humanize-duration","version":"3.27.4","files":["/packages/project/package.json"]},{"name":"@types/inquirer","version":"8.2.12","files":["/package.json"]},{"name":"@types/invariant","version":"2.2.37","files":["/packages/form/package.json"]},{"name":"@types/is-hotkey","version":"0.1.10","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/is-number","version":"7.0.5","files":["/packages/db-dynamodb/package.json"]},{"name":"@types/js-yaml","version":"4.0.9","files":["/packages/create-webiny-project/package.json"]},{"name":"@types/jsdom","version":"21.1.7","files":["/packages/lexical-converter/package.json","/packages/project/package.json"]},{"name":"@types/jsonpack","version":"1.1.6","files":["/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"@types/jsonwebtoken","version":"9.0.10","files":["/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json"]},{"name":"@types/jwk-to-pem","version":"2.0.3","files":["/packages/api-cognito-authenticator/package.json","/packages/api-security-auth0/package.json"]},{"name":"@types/listr","version":"0.14.9","files":["/packages/cli-core/package.json"]},{"name":"@types/lodash","version":"4.17.20","files":["/packages/api-sync-system/package.json","/packages/app/package.json","/packages/app-cognito-authenticator/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/validation/package.json"]},{"name":"@types/md5","version":"2.3.5","files":["/packages/api-core/package.json"]},{"name":"@types/ncp","version":"2.0.8","files":["/packages/project-aws/package.json"]},{"name":"@types/node","version":"22.18.6","files":["/package.json"]},{"name":"@types/nodemailer","version":"6.4.19","files":["/packages/api-mailer/package.json"]},{"name":"@types/object-hash","version":"3.0.6","files":["/packages/api-file-manager/package.json"]},{"name":"@types/pako","version":"2.0.4","files":["/packages/app-website-builder/package.json"]},{"name":"@types/platform","version":"1.3.6","files":["/packages/app-website-builder/package.json"]},{"name":"@types/pluralize","version":"0.0.33","files":["/packages/api-headless-cms/package.json"]},{"name":"@types/postcss-import","version":"14.0.3","files":["/packages/website-builder-nextjs/package.json"]},{"name":"@types/randomcolor","version":"0.5.9","files":["/packages/app-website-builder/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/package.json","/packages/admin-ui/package.json","/packages/app-aco/package.json","/packages/app-audit-logs/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-trash-bin/package.json","/packages/app-workflows/package.json","/packages/theme/package.json","/packages/website-builder-react/package.json"]},{"name":"@types/react-color","version":"2.17.12","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@types/react-custom-scrollbars","version":"4.0.13","files":["/packages/admin-ui/package.json"]},{"name":"@types/react-dom","version":"18.2.25","files":["/package.json"]},{"name":"@types/react-helmet","version":"6.1.11","files":["/packages/app-admin-ui/package.json","/packages/app-security-access-management/package.json"]},{"name":"@types/react-images","version":"0.5.3","files":["/packages/app-website-builder/package.json"]},{"name":"@types/react-resizable","version":"3.0.8","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/react-test-renderer","version":"18.3.1","files":["/packages/project/package.json"]},{"name":"@types/react-transition-group","version":"4.4.12","files":["/packages/app-admin/package.json"]},{"name":"@types/react-virtualized","version":"9.22.3","files":["/packages/admin-ui/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/read-json-sync","version":"2.0.3","files":["/packages/project/package.json"]},{"name":"@types/resize-observer-browser","version":"0.1.11","files":["/packages/app-website-builder/package.json"]},{"name":"@types/semver","version":"7.7.1","files":["/packages/data-migration/package.json"]},{"name":"@types/store","version":"2.0.5","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/tinycolor2","version":"1.4.6","files":["/packages/app-admin/package.json"]},{"name":"@types/uniqid","version":"5.3.4","files":["/packages/feature-flags/package.json","/packages/plugins/package.json"]},{"name":"@types/universal-router","version":"8.0.0","files":["/packages/app/package.json"]},{"name":"@types/unzipper","version":"0.10.11","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/validate-npm-package-name","version":"3.0.3","files":["/packages/create-webiny-project/package.json"]},{"name":"@types/warning","version":"3.0.3","files":["/packages/app/package.json"]},{"name":"@types/yargs","version":"17.0.33","files":["/scripts/buildPackages/package.json"]},{"name":"@typescript-eslint/eslint-plugin","version":"8.48.0","files":["/package.json"]},{"name":"@typescript-eslint/parser","version":"8.48.0","files":["/package.json"]},{"name":"@vitest/coverage-v8","version":"3.2.4","files":["/package.json"]},{"name":"@vitest/eslint-plugin","version":"1.4.2","files":["/package.json"]},{"name":"adio","version":"2.0.1","files":["/package.json"]},{"name":"adm-zip","version":"0.5.16","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"amazon-cognito-identity-js","version":"4.6.3","files":["/cypress-tests/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"apollo-graphql","version":"0.9.7","files":["/packages/api-headless-cms/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"aws-sdk-client-mock","version":"4.1.0","files":["/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-sync-system/package.json"]},{"name":"axios","version":"1.12.2","files":["/package.json"]},{"name":"babel-loader","version":"10.0.0","files":["/package.json","/packages/ui/package.json"]},{"name":"babel-plugin-dynamic-import-node","version":"2.3.3","files":["/package.json"]},{"name":"babel-plugin-macros","version":"3.1.0","files":["/package.json"]},{"name":"babel-plugin-module-resolver","version":"5.0.2","files":["/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"babel-plugin-named-asset-import","version":"1.0.0-next.fb6e6f70","files":["/packages/app-admin-ui/package.json"]},{"name":"chalk","version":"4.1.2","files":["/package.json","/packages/admin-ui/package.json"]},{"name":"cross-env","version":"5.2.1","files":["/package.json"]},{"name":"cross-spawn","version":"6.0.6","files":["/package.json"]},{"name":"css-loader","version":"7.1.2","files":["/packages/admin-ui/package.json"]},{"name":"cypress","version":"13.17.0","files":["/cypress-tests/package.json"]},{"name":"cypress-image-snapshot","version":"4.0.1","files":["/cypress-tests/package.json"]},{"name":"cypress-mailosaur","version":"2.17.0","files":["/cypress-tests/package.json"]},{"name":"cypress-wait-until","version":"1.7.2","files":["/cypress-tests/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/package.json"]},{"name":"del","version":"6.1.1","files":["/cypress-tests/package.json"]},{"name":"elastic-ts","version":"0.12.0","files":["/packages/migrations/package.json"]},{"name":"env-ci","version":"2.6.0","files":["/package.json"]},{"name":"eslint","version":"9.39.1","files":["/package.json"]},{"name":"eslint-config-standard","version":"17.1.0","files":["/package.json"]},{"name":"eslint-import-resolver-babel-module","version":"5.3.2","files":["/package.json"]},{"name":"eslint-plugin-import","version":"2.32.0","files":["/package.json"]},{"name":"eslint-plugin-lodash","version":"8.0.0","files":["/package.json"]},{"name":"eslint-plugin-node","version":"11.1.0","files":["/package.json"]},{"name":"eslint-plugin-promise","version":"7.2.1","files":["/package.json"]},{"name":"eslint-plugin-react","version":"7.37.5","files":["/package.json"]},{"name":"eslint-plugin-standard","version":"5.0.0","files":["/package.json"]},{"name":"eslint-plugin-storybook","version":"9.1.16","files":["/packages/admin-ui/package.json"]},{"name":"execa","version":"5.1.1","files":["/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/common-audit-logs/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"file-loader","version":"6.2.0","files":["/packages/admin-ui/package.json"]},{"name":"folder-hash","version":"4.1.1","files":["/package.json"]},{"name":"fs-extra","version":"11.3.2","files":["/package.json"]},{"name":"get-stream","version":"3.0.0","files":["/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/package.json"]},{"name":"git-cz","version":"1.8.4","files":["/package.json"]},{"name":"github-actions-wac","version":"2.0.0","files":["/package.json"]},{"name":"glob","version":"7.2.3","files":["/package.json"]},{"name":"graphql","version":"16.12.0","files":["/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-file-manager-aco/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/testing/package.json"]},{"name":"graphql-request","version":"7.3.5","files":["/cypress-tests/package.json"]},{"name":"husky","version":"4.3.8","files":["/package.json"]},{"name":"identity-obj-proxy","version":"3.0.0","files":["/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"inquirer","version":"12.9.6","files":["/package.json"]},{"name":"jest-dynalite","version":"3.6.1","files":["/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/data-migration/package.json","/packages/db-dynamodb/package.json","/packages/migrations/package.json","/packages/project-utils/package.json"]},{"name":"jest-extended","version":"6.0.0","files":["/package.json"]},{"name":"jsdom","version":"25.0.1","files":["/packages/lexical-converter/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/api-file-manager-ddb/package.json"]},{"name":"lerna","version":"8.1.2","files":["/package.json"]},{"name":"lexical","version":"0.35.0","files":["/package.json"]},{"name":"lint-staged","version":"16.1.6","files":["/package.json"]},{"name":"listr","version":"0.14.3","files":["/package.json"]},{"name":"listr2","version":"5.0.8","files":["/packages/project-utils/package.json"]},{"name":"load-json-file","version":"6.2.0","files":["/package.json","/packages/project-utils/package.json"]},{"name":"lodash","version":"4.17.21","files":["/package.json","/cypress-tests/package.json"]},{"name":"longest","version":"2.0.1","files":["/package.json"]},{"name":"md5","version":"2.3.0","files":["/packages/api-security-cognito/package.json"]},{"name":"minimatch","version":"5.1.6","files":["/package.json"]},{"name":"mobx","version":"6.15.0","files":["/packages/form/package.json"]},{"name":"mobx-react-lite","version":"3.4.3","files":["/packages/form/package.json"]},{"name":"nanoid","version":"3.3.11","files":["/package.json","/cypress-tests/package.json"]},{"name":"ncp","version":"2.0.0","files":["/packages/ui/package.json"]},{"name":"next","version":"15.5.7","files":["/packages/website-builder-nextjs/package.json"]},{"name":"pino","version":"9.13.1","files":["/packages/logger/package.json","/packages/project-utils/package.json"]},{"name":"pino-pretty","version":"9.4.1","files":["/packages/project-utils/package.json"]},{"name":"postcss-loader","version":"8.2.0","files":["/packages/admin-ui/package.json"]},{"name":"prettier","version":"3.6.2","files":["/package.json","/packages/admin-ui/package.json","/packages/api-aco/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-website-builder/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/ui/package.json"]},{"name":"react","version":"18.2.0","files":["/package.json","/packages/lexical-nodes/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/package.json"]},{"name":"react-router","version":"7.9.1","files":["/packages/admin-ui/package.json"]},{"name":"react-router-dom","version":"7.9.1","files":["/packages/admin-ui/package.json"]},{"name":"rimraf","version":"6.0.1","files":["/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json","/packages/api-security-okta/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/aws-sdk/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/package.json","/packages/data-migration/package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/error/package.json","/packages/feature-flags/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-client/package.json","/packages/handler-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/logger/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pubsub/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tasks/package.json","/packages/testing/package.json","/packages/theme/package.json","/packages/ui/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/scripts/cli/package.json"]},{"name":"sass","version":"1.93.0","files":["/packages/admin-ui/package.json"]},{"name":"semver","version":"7.7.3","files":["/package.json"]},{"name":"storybook","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"ts-expect","version":"1.3.0","files":["/package.json"]},{"name":"tsx","version":"4.20.5","files":["/package.json"]},{"name":"ttypescript","version":"1.5.15","files":["/packages/api-file-manager-aco/package.json"]},{"name":"type-fest","version":"5.2.0","files":["/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-record-locking/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/project/package.json","/packages/tasks/package.json","/scripts/prepublishOnly/package.json"]},{"name":"typescript","version":"5.9.3","files":["/package.json","/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-dynamodb-to-elasticsearch/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-es-tasks/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-tasks-ddb-es/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json","/packages/api-security-okta/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/aws-sdk/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/package.json","/packages/data-migration/package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/error/package.json","/packages/feature/package.json","/packages/feature-flags/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-client/package.json","/packages/handler-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/ioc/package.json","/packages/logger/package.json","/packages/migrations/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pubsub/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/shared-aco/package.json","/packages/tasks/package.json","/packages/testing/package.json","/packages/theme/package.json","/packages/ui/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/cypress-tests/package.json","/scripts/cli/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/cypress-tests/package.json"]},{"name":"validator","version":"13.15.23","files":["/package.json"]},{"name":"verdaccio","version":"6.2.4","files":["/package.json"]},{"name":"vite-tsconfig-paths","version":"5.1.4","files":["/package.json"]},{"name":"vitest","version":"3.2.4","files":["/package.json","/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-es-tasks/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-cognito/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/data-migration/package.json","/packages/db-dynamodb/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/ioc/package.json","/packages/lexical-converter/package.json","/packages/migrations/package.json","/packages/plugins/package.json","/packages/project-utils/package.json","/packages/pubsub/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tasks/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"webpack","version":"5.101.3","files":["/packages/website-builder-nextjs/package.json"]},{"name":"write-json-file","version":"4.3.0","files":["/package.json","/packages/api-headless-cms/package.json"]},{"name":"yargs","version":"17.7.2","files":["/package.json","/packages/project-utils/package.json"]},{"name":"zod","version":"3.25.76","files":["/packages/ioc/package.json"]}],"peerDependencies":[{"name":"minimatch","version":"5.1.6","files":["/packages/ui/package.json"]},{"name":"react","version":"18.2.0","files":["/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/packages/ui/package.json"]}],"resolutions":[{"name":"@emotion/react","version":"11.10.8","files":["/package.json"]},{"name":"@octokit/rest","version":"20.1.2","files":["/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/package.json"]},{"name":"@types/react-dom","version":"18.2.25","files":["/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/package.json"]},{"name":"react","version":"18.2.0","files":["/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/package.json"]},{"name":"systeminformation","version":"5.23.18","files":["/package.json"]}],"references":[{"name":"@types/hoist-non-react-statics","versions":[{"version":"3.3.7","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"@babel/cli","versions":[{"version":"7.28.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/code-frame","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@babel/compat-data","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/core","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/helper-define-polyfill-provider","versions":[{"version":"0.6.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/helper-environment-visitor","versions":[{"version":"7.24.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/parser","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-class-properties","versions":[{"version":"7.18.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-object-rest-spread","versions":[{"version":"7.20.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-throw-expressions","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-syntax-object-rest-spread","versions":[{"version":"7.8.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-transform-modules-commonjs","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-transform-runtime","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/preset-env","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/preset-react","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/preset-typescript","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/register","versions":[{"version":"7.28.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"@babel/runtime","versions":[{"version":"7.28.4","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/template","versions":[{"version":"7.27.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/traverse","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/types","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/cli","versions":[{"version":"11.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/config-conventional","versions":[{"version":"11.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@eslint/eslintrc","versions":[{"version":"3.3.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@eslint/js","versions":[{"version":"9.39.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@grpc/grpc-js","versions":[{"version":"1.14.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@lexical/code","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/hashtag","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/headless","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/history","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/html","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/list","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/mark","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/overflow","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/react","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/rich-text","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/selection","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/text","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/utils","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@octokit/rest","versions":[{"version":"20.1.2","files":[{"file":"/package.json","types":["devDependencies","resolutions"]}]}]},{"name":"@types/fs-extra","versions":[{"version":"11.0.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/inquirer","versions":[{"version":"8.2.12","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/node","versions":[{"version":"22.18.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/react","versions":[{"version":"18.2.79","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-dom","versions":[{"version":"18.2.25","files":[{"file":"/package.json","types":["devDependencies","resolutions"]}]}]},{"name":"@typescript-eslint/eslint-plugin","versions":[{"version":"8.48.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@typescript-eslint/parser","versions":[{"version":"8.48.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@vitest/coverage-v8","versions":[{"version":"3.2.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@vitest/eslint-plugin","versions":[{"version":"1.4.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"adio","versions":[{"version":"2.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"axios","versions":[{"version":"1.12.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-loader","versions":[{"version":"10.0.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-dynamic-import-node","versions":[{"version":"2.3.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-macros","versions":[{"version":"3.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-module-resolver","versions":[{"version":"5.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]}]}]},{"name":"chalk","versions":[{"version":"4.1.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/aws-layers/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"cross-env","versions":[{"version":"5.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"cross-spawn","versions":[{"version":"6.0.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"deepmerge","versions":[{"version":"4.3.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"env-ci","versions":[{"version":"2.6.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint","versions":[{"version":"9.39.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"eslint-config-standard","versions":[{"version":"17.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-import-resolver-babel-module","versions":[{"version":"5.3.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-import","versions":[{"version":"2.32.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-lodash","versions":[{"version":"8.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-node","versions":[{"version":"11.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-promise","versions":[{"version":"7.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-react","versions":[{"version":"7.37.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-standard","versions":[{"version":"5.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"execa","versions":[{"version":"5.1.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"folder-hash","versions":[{"version":"4.1.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"fs-extra","versions":[{"version":"11.3.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"get-stream","versions":[{"version":"3.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"get-yarn-workspaces","versions":[{"version":"1.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"git-cz","versions":[{"version":"1.8.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"github-actions-wac","versions":[{"version":"2.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"glob","versions":[{"version":"7.2.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"graphql","versions":[{"version":"16.12.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies","devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]}]}]},{"name":"husky","versions":[{"version":"4.3.8","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"inquirer","versions":[{"version":"12.9.6","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"jest-extended","versions":[{"version":"6.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lerna","versions":[{"version":"8.1.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lexical","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"lint-staged","versions":[{"version":"16.1.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"listr","versions":[{"version":"0.14.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"load-json-file","versions":[{"version":"6.2.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"lodash","versions":[{"version":"4.17.21","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-aco/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/i18n-react/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]},{"file":"/packages/validation/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"longest","versions":[{"version":"2.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"minimatch","versions":[{"version":"5.1.6","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]}]}]},{"name":"nanoid","versions":[{"version":"3.3.11","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"prettier","versions":[{"version":"3.6.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"react","versions":[{"version":"18.2.0","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["peerDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-websockets/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["peerDependencies"]},{"file":"/packages/i18n/package.json","types":["peerDependencies"]},{"file":"/packages/i18n-react/package.json","types":["peerDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["peerDependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"react-dom","versions":[{"version":"18.2.0","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-websockets/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"semver","versions":[{"version":"7.7.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"ts-expect","versions":[{"version":"1.3.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"tsx","versions":[{"version":"4.20.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"type-fest","versions":[{"version":"5.2.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["dependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/db/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"typescript","versions":[{"version":"5.9.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-os/package.json","types":["devDependencies"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-security-okta/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-utils/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/error/package.json","types":["devDependencies"]},{"file":"/packages/feature/package.json","types":["devDependencies"]},{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-client/package.json","types":["devDependencies"]},{"file":"/packages/handler-db/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/i18n-react/package.json","types":["devDependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/shared-aco/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/wcp/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"validator","versions":[{"version":"13.15.23","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"verdaccio","versions":[{"version":"6.2.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"vite-tsconfig-paths","versions":[{"version":"5.1.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"vitest","versions":[{"version":"3.2.4","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"write-json-file","versions":[{"version":"4.3.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"yargs","versions":[{"version":"17.7.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"systeminformation","versions":[{"version":"5.23.18","files":[{"file":"/package.json","types":["resolutions"]}]}]},{"name":"@emotion/react","versions":[{"version":"11.10.8","files":[{"file":"/package.json","types":["resolutions"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["dependencies"]}]}]},{"name":"jsonwebtoken","versions":[{"version":"9.0.3","files":[{"file":"/package.json","types":["resolutions"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["dependencies"]},{"file":"/packages/api-security-okta/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-svg-core","versions":[{"version":"1.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/react-fontawesome","versions":[{"version":"0.1.19","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]}]}]},{"name":"@minoru/react-dnd-treeview","versions":[{"version":"3.5.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"@monaco-editor/react","versions":[{"version":"4.7.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@tanstack/react-table","versions":[{"version":"8.21.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"bytes","versions":[{"version":"3.1.2","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"class-variance-authority","versions":[{"version":"0.7.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"clsx","versions":[{"version":"2.1.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"cmdk","versions":[{"version":"1.1.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"mobx","versions":[{"version":"6.15.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-utils/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"monaco-editor","versions":[{"version":"0.53.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"radix-ui","versions":[{"version":"1.4.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"react-color","versions":[{"version":"2.19.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"react-custom-scrollbars","versions":[{"version":"4.2.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-dnd","versions":[{"version":"16.0.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"react-virtualized","versions":[{"version":"9.22.6","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"sonner","versions":[{"version":"2.0.7","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwind-merge","versions":[{"version":"2.6.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwindcss","versions":[{"version":"4.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"timeago-react","versions":[{"version":"3.0.7","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tw-animate-css","versions":[{"version":"1.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-solid-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@storybook/addon-a11y","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-docs","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-webpack5-compiler-babel","versions":[{"version":"3.0.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/react-webpack5","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@svgr/webpack","versions":[{"version":"6.5.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]}]}]},{"name":"@tailwindcss/postcss","versions":[{"version":"4.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/react-color","versions":[{"version":"2.17.12","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-custom-scrollbars","versions":[{"version":"4.0.13","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-virtualized","versions":[{"version":"9.22.3","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"css-loader","versions":[{"version":"7.1.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"eslint-plugin-storybook","versions":[{"version":"9.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"file-loader","versions":[{"version":"6.2.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"postcss-loader","versions":[{"version":"8.2.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"react-router","versions":[{"version":"7.9.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"react-router-dom","versions":[{"version":"7.9.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"rimraf","versions":[{"version":"6.0.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-os/package.json","types":["devDependencies"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-security-okta/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/error/package.json","types":["devDependencies"]},{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-client/package.json","types":["devDependencies"]},{"file":"/packages/handler-db/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/i18n-react/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/wcp/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"sass","versions":[{"version":"1.93.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"storybook","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"zod","versions":[{"version":"3.25.76","files":[{"file":"/packages/api-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["dependencies"]},{"file":"/packages/api-log/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-websockets/package.json","types":["dependencies"]},{"file":"/packages/api-workflows/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"@types/jsonwebtoken","versions":[{"version":"9.0.10","files":[{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]}]}]},{"name":"@types/jwk-to-pem","versions":[{"version":"2.0.3","files":[{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]}]}]},{"name":"dataloader","versions":[{"version":"2.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]}]}]},{"name":"deep-equal","versions":[{"version":"2.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"jose","versions":[{"version":"5.10.0","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"md5","versions":[{"version":"2.3.0","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]}]}]},{"name":"@types/md5","versions":[{"version":"2.3.5","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]}]}]},{"name":"jest-dynalite","versions":[{"version":"3.6.1","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"p-retry","versions":[{"version":"7.0.0","files":[{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"@elastic/elasticsearch","versions":[{"version":"7.12.0","files":[{"file":"/packages/api-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"elastic-ts","versions":[{"version":"0.12.0","files":[{"file":"/packages/api-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]}]}]},{"name":"cache-control-parser","versions":[{"version":"2.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"object-hash","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"@types/object-hash","versions":[{"version":"3.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]}]}]},{"name":"ttypescript","versions":[{"version":"1.5.15","files":[{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]}]}]},{"name":"jsonpack","versions":[{"version":"1.1.5","files":[{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"mime","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"p-map","versions":[{"version":"7.0.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"p-reduce","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"sanitize-filename","versions":[{"version":"1.6.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"sharp","versions":[{"version":"0.34.5","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/merge","versions":[{"version":"9.1.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/schema","versions":[{"version":"10.0.30","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"dot-prop","versions":[{"version":"6.0.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"graphql-tag","versions":[{"version":"2.12.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"jsdom","versions":[{"version":"25.0.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]}]}]},{"name":"pluralize","versions":[{"version":"8.0.0","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"slugify","versions":[{"version":"1.6.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@types/babel__code-frame","versions":[{"version":"7.0.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"@types/pluralize","versions":[{"version":"0.0.33","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"apollo-graphql","versions":[{"version":"0.9.7","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"dot-object","versions":[{"version":"2.1.5","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]}]}]},{"name":"@types/dot-object","versions":[{"version":"2.1.6","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]}]}]},{"name":"@types/jsonpack","versions":[{"version":"1.1.6","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]}]}]},{"name":"@faker-js/faker","versions":[{"version":"9.9.0","files":[{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"@smithy/node-http-handler","versions":[{"version":"2.5.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"archiver","versions":[{"version":"7.0.1","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"uniqid","versions":[{"version":"5.4.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/plugins/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"unzipper","versions":[{"version":"0.12.3","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"@types/adm-zip","versions":[{"version":"0.5.7","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"@types/archiver","versions":[{"version":"6.0.3","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"@types/unzipper","versions":[{"version":"0.10.11","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"adm-zip","versions":[{"version":"0.5.16","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"aws-sdk-client-mock","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"crypto-js","versions":[{"version":"4.2.0","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"nodemailer","versions":[{"version":"7.0.10","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"@types/crypto-js","versions":[{"version":"4.2.2","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@types/nodemailer","versions":[{"version":"6.4.19","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@types/lodash","versions":[{"version":"4.17.20","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/cli/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]}]}]},{"name":"@apollo/react-hooks","versions":[{"version":"3.1.5","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@emotion/styled","versions":[{"version":"11.10.6","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"apollo-cache","versions":[{"version":"1.3.5","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"apollo-cache-inmemory","versions":[{"version":"1.6.6","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-client","versions":[{"version":"2.6.10","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"apollo-link","versions":[{"version":"1.2.14","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-context","versions":[{"version":"1.0.20","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-error","versions":[{"version":"1.1.13","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-http-common","versions":[{"version":"0.2.16","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-utilities","versions":[{"version":"1.3.4","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"boolean","versions":[{"version":"3.2.0","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"history","versions":[{"version":"5.3.0","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"invariant","versions":[{"version":"2.2.4","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"ts-invariant","versions":[{"version":"0.10.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"universal-router","versions":[{"version":"9.2.1","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"warning","versions":[{"version":"4.0.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"@types/universal-router","versions":[{"version":"8.0.0","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"@types/warning","versions":[{"version":"3.0.3","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"dot-prop-immutable","versions":[{"version":"2.1.1","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]}]}]},{"name":"mobx-react-lite","versions":[{"version":"3.4.3","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"pako","versions":[{"version":"2.1.0","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]}]}]},{"name":"react-hotkeyz","versions":[{"version":"1.0.4","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"store","versions":[{"version":"2.0.12","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-components","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]}]}]},{"name":"@iconify/json","versions":[{"version":"2.2.386","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@types/mime","versions":[{"version":"2.0.3","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"case","versions":[{"version":"1.6.3","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"classnames","versions":[{"version":"2.5.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]}]}]},{"name":"emotion","versions":[{"version":"10.0.27","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]}]}]},{"name":"graphlib","versions":[{"version":"2.1.8","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"is-hotkey","versions":[{"version":"0.2.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"prop-types","versions":[{"version":"15.8.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"react-draggable","versions":[{"version":"4.5.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-resizable","versions":[{"version":"3.0.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-resizable-panels","versions":[{"version":"2.1.9","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-transition-group","versions":[{"version":"4.4.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"reset-css","versions":[{"version":"5.0.2","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"tinycolor2","versions":[{"version":"1.6.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"ts-morph","versions":[{"version":"24.0.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"unicode-emoji-json","versions":[{"version":"0.8.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@emotion/babel-plugin","versions":[{"version":"11.13.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@types/bytes","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/graphlib","versions":[{"version":"2.1.12","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/is-hotkey","versions":[{"version":"0.1.10","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-resizable","versions":[{"version":"3.0.8","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-transition-group","versions":[{"version":"4.4.12","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/store","versions":[{"version":"2.0.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/tinycolor2","versions":[{"version":"1.4.6","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@auth0/auth0-react","versions":[{"version":"2.5.0","files":[{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]}]}]},{"name":"react-helmet","versions":[{"version":"6.1.0","files":[{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@aws-amplify/auth","versions":[{"version":"5.6.15","files":[{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-auth-js","versions":[{"version":"5.11.0","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-react","versions":[{"version":"6.10.0","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-signin-widget","versions":[{"version":"5.16.1","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@types/react-helmet","versions":[{"version":"6.1.11","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-named-asset-import","versions":[{"version":"1.0.0-next.fb6e6f70","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"date-fns","versions":[{"version":"2.30.0","files":[{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-common","versions":[{"version":"3.1.4","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"cropperjs","versions":[{"version":"1.6.2","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"dataurl-to-blob","versions":[{"version":"0.0.1","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"dayjs","versions":[{"version":"1.11.18","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"load-script","versions":[{"version":"1.0.0","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"react-butterfiles","versions":[{"version":"1.3.3","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-lazy-load","versions":[{"version":"3.1.14","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"@emotion/css","versions":[{"version":"11.10.6","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-common-types","versions":[{"version":"0.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-brands-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-regular-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"dnd-core","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"raw.macro","versions":[{"version":"0.4.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-dnd-html5-backend","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"use-deep-compare-effect","versions":[{"version":"1.8.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@material-design-icons/svg","versions":[{"version":"0.14.15","files":[{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/icons/package.json","types":["devDependencies"]}]}]},{"name":"crypto-hash","versions":[{"version":"3.1.0","files":[{"file":"/packages/app-record-locking/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-batch-http","versions":[{"version":"1.2.14","files":[{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"matcher","versions":[{"version":"5.0.0","files":[{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/deep-equal","versions":[{"version":"1.0.4","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"@types/pako","versions":[{"version":"2.0.4","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/platform","versions":[{"version":"1.3.6","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/randomcolor","versions":[{"version":"0.5.9","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-images","versions":[{"version":"0.5.3","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/resize-observer-browser","versions":[{"version":"0.1.11","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/aws-lambda","versions":[{"version":"8.10.152","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]},{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"cheerio","versions":[{"version":"1.1.2","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"srcset","versions":[{"version":"4.0.0","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-apigatewaymanagementapi","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudfront","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-events","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-logs","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cognito-identity-provider","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb-streams","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-eventbridge","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iam","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iot","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-lambda","versions":[{"version":"3.942.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-s3","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-scheduler","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sfn","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sqs","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sts","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/credential-providers","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-storage","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-presigned-post","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-request-presigner","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/util-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/core","versions":[{"version":"1.6.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-react","versions":[{"version":"1.4.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-sass","versions":[{"version":"1.4.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-svgr","versions":[{"version":"1.2.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-type-check","versions":[{"version":"1.3.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rspack/core","versions":[{"version":"1.5.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@swc/plugin-emotion","versions":[{"version":"11.1.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/webpack-env","versions":[{"version":"1.18.8","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"chokidar","versions":[{"version":"4.0.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"fast-glob","versions":[{"version":"3.3.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"find-up","versions":[{"version":"5.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"process","versions":[{"version":"0.11.10","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"raw-loader","versions":[{"version":"4.0.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"react-refresh","versions":[{"version":"0.11.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"read-json-sync","versions":[{"version":"2.0.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"sass-loader","versions":[{"version":"16.0.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"style-loader","versions":[{"version":"3.3.4","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"url-loader","versions":[{"version":"4.1.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"utf-8-validate","versions":[{"version":"6.0.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"humanize-duration","versions":[{"version":"3.33.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"open","versions":[{"version":"10.2.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]}]}]},{"name":"ora","versions":[{"version":"4.1.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"pino","versions":[{"version":"9.13.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"pino-pretty","versions":[{"version":"9.4.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/listr","versions":[{"version":"0.14.9","files":[{"file":"/packages/cli-core/package.json","types":["devDependencies"]}]}]},{"name":"js-yaml","versions":[{"version":"4.1.1","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"os","versions":[{"version":"0.1.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"uuid","versions":[{"version":"13.0.0","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]}]}]},{"name":"validate-npm-package-name","versions":[{"version":"6.0.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"yesno","versions":[{"version":"0.4.0","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"@types/js-yaml","versions":[{"version":"4.0.9","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"@types/validate-npm-package-name","versions":[{"version":"3.0.3","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"center-align","versions":[{"version":"1.0.1","files":[{"file":"/packages/data-migration/package.json","types":["dependencies"]}]}]},{"name":"@types/center-align","versions":[{"version":"1.0.2","files":[{"file":"/packages/data-migration/package.json","types":["devDependencies"]}]}]},{"name":"@types/semver","versions":[{"version":"7.7.1","files":[{"file":"/packages/data-migration/package.json","types":["devDependencies"]}]}]},{"name":"dynamodb-toolbox","versions":[{"version":"0.9.5","files":[{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"fuse.js","versions":[{"version":"7.1.0","files":[{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"@types/is-number","versions":[{"version":"7.0.5","files":[{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]}]}]},{"name":"@types/uniqid","versions":[{"version":"5.3.4","files":[{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/react","versions":[{"version":"15.0.7","files":[{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/user-event","versions":[{"version":"14.6.1","files":[{"file":"/packages/form/package.json","types":["devDependencies"]}]}]},{"name":"@types/invariant","versions":[{"version":"2.2.37","files":[{"file":"/packages/form/package.json","types":["devDependencies"]}]}]},{"name":"ci-info","versions":[{"version":"4.3.0","files":[{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"@fastify/compress","versions":[{"version":"7.0.3","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/cookie","versions":[{"version":"9.4.0","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"fastify","versions":[{"version":"4.29.1","files":[{"file":"/packages/handler/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@fastify/aws-lambda","versions":[{"version":"4.1.0","files":[{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/resolvers-composition","versions":[{"version":"7.0.25","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/utils","versions":[{"version":"10.11.0","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"graphql-scalars","versions":[{"version":"1.25.0","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"accounting","versions":[{"version":"0.4.1","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"fecha","versions":[{"version":"2.3.3","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"short-hash","versions":[{"version":"1.0.0","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"@types/accounting","versions":[{"version":"0.4.5","files":[{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"@types/glob","versions":[{"version":"7.2.0","files":[{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"inversify","versions":[{"version":"6.2.2","files":[{"file":"/packages/ioc/package.json","types":["dependencies"]}]}]},{"name":"reflect-metadata","versions":[{"version":"0.2.2","files":[{"file":"/packages/ioc/package.json","types":["dependencies"]}]}]},{"name":"@types/jsdom","versions":[{"version":"21.1.7","files":[{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/prismjs","versions":[{"version":"1.26.5","files":[{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"react-style-object-to-css","versions":[{"version":"1.1.2","files":[{"file":"/packages/lexical-theme/package.json","types":["dependencies"]}]}]},{"name":"debounce","versions":[{"version":"1.2.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"dotenv","versions":[{"version":"8.6.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"exit-hook","versions":[{"version":"4.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"graphql-request","versions":[{"version":"7.3.5","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"mqtt","versions":[{"version":"5.14.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"neverthrow","versions":[{"version":"8.2.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"react-test-renderer","versions":[{"version":"18.3.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"replace-in-path","versions":[{"version":"1.1.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"serialize-error","versions":[{"version":"12.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"@types/debounce","versions":[{"version":"1.2.4","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/humanize-duration","versions":[{"version":"3.27.4","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-test-renderer","versions":[{"version":"18.3.1","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/read-json-sync","versions":[{"version":"2.0.3","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@pulumi/aws","versions":[{"version":"7.12.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/pulumi","versions":[{"version":"3.210.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/random","versions":[{"version":"4.18.4","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"core-js","versions":[{"version":"3.45.1","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"cross-fetch","versions":[{"version":"3.2.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"regenerator-runtime","versions":[{"version":"0.14.1","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"@types/ncp","versions":[{"version":"2.0.8","files":[{"file":"/packages/project-aws/package.json","types":["devDependencies"]}]}]},{"name":"listr2","versions":[{"version":"5.0.8","files":[{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"decompress","versions":[{"version":"4.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"tar","versions":[{"version":"6.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"identity-obj-proxy","versions":[{"version":"3.0.0","files":[{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"cli-table3","versions":[{"version":"0.6.5","files":[{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"object-merge-advanced","versions":[{"version":"12.1.0","files":[{"file":"/packages/tasks/package.json","types":["dependencies"]}]}]},{"name":"object-sizeof","versions":[{"version":"2.6.5","files":[{"file":"/packages/tasks/package.json","types":["dependencies"]}]}]},{"name":"jsesc","versions":[{"version":"3.1.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"strip-ansi","versions":[{"version":"6.0.1","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"wts-client","versions":[{"version":"2.0.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"ncp","versions":[{"version":"2.0.0","files":[{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@noble/hashes","versions":[{"version":"2.0.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"bson-objectid","versions":[{"version":"2.0.4","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"nanoid-dictionary","versions":[{"version":"4.3.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"isnumeric","versions":[{"version":"0.3.3","files":[{"file":"/packages/validation/package.json","types":["dependencies"]}]}]},{"name":"postcss","versions":[{"version":"8.5.6","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]}]}]},{"name":"postcss-import","versions":[{"version":"16.1.1","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]}]}]},{"name":"@types/postcss-import","versions":[{"version":"14.0.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"next","versions":[{"version":"15.5.7","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"webpack","versions":[{"version":"5.101.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"csstype","versions":[{"version":"3.1.3","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"fast-json-patch","versions":[{"version":"3.1.1","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"fast-json-stable-stringify","versions":[{"version":"2.1.0","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@4tw/cypress-drag-drop","versions":[{"version":"1.8.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/cypress","versions":[{"version":"10.1.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"amazon-cognito-identity-js","versions":[{"version":"4.6.3","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress","versions":[{"version":"13.17.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-image-snapshot","versions":[{"version":"4.0.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-mailosaur","versions":[{"version":"2.17.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-wait-until","versions":[{"version":"1.7.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"del","versions":[{"version":"6.1.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@types/folder-hash","versions":[{"version":"4.0.4","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]}]}]},{"name":"@types/yargs","versions":[{"version":"17.0.33","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]}]}]},{"name":"cli-progress","versions":[{"version":"3.12.0","files":[{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"p-limit","versions":[{"version":"7.1.1","files":[{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"@types/cli-progress","versions":[{"version":"3.11.6","files":[{"file":"/scripts/cjsToEsm/package.json","types":["devDependencies"]}]}]}]} +{"dependencies":[{"name":"@apollo/react-common","version":"3.1.4","files":["/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"@apollo/react-components","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-mailer/package.json"]},{"name":"@apollo/react-hooks","version":"3.1.5","files":["/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-record-locking/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json"]},{"name":"@auth0/auth0-react","version":"2.5.0","files":["/packages/app-admin-auth0/package.json"]},{"name":"@aws-amplify/auth","version":"5.6.15","files":["/packages/app-admin-cognito/package.json","/packages/app-cognito-authenticator/package.json"]},{"name":"@aws-sdk/client-apigatewaymanagementapi","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudfront","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-events","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-logs","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cognito-identity-provider","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb-streams","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-eventbridge","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iam","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iot","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-lambda","version":"3.942.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-s3","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-scheduler","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sfn","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sqs","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sts","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/credential-providers","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-storage","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-presigned-post","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-request-presigner","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/util-dynamodb","version":"3.940.0","files":["/packages/aws-sdk/package.json"]},{"name":"@babel/code-frame","version":"7.27.1","files":["/packages/api-headless-cms/package.json"]},{"name":"@babel/core","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-env","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-react","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/preset-typescript","version":"7.28.5","files":["/packages/build-tools/package.json"]},{"name":"@babel/runtime","version":"7.28.4","files":["/packages/build-tools/package.json"]},{"name":"@elastic/elasticsearch","version":"7.12.0","files":["/packages/api-elasticsearch/package.json","/packages/data-migration/package.json","/packages/migrations/package.json"]},{"name":"@emotion/css","version":"11.10.6","files":["/packages/app-headless-cms/package.json"]},{"name":"@emotion/react","version":"11.10.8","files":["/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-theme/package.json","/packages/theme/package.json"]},{"name":"@emotion/styled","version":"11.10.6","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@fastify/aws-lambda","version":"4.1.0","files":["/packages/handler-aws/package.json"]},{"name":"@fastify/compress","version":"7.0.3","files":["/packages/handler/package.json"]},{"name":"@fastify/cookie","version":"9.4.0","files":["/packages/handler/package.json"]},{"name":"@fortawesome/fontawesome-common-types","version":"0.3.0","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/fontawesome-svg-core","version":"1.3.0","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@fortawesome/free-brands-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-regular-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"6.7.2","files":["/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/react-fontawesome","version":"0.1.19","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@graphql-tools/merge","version":"9.1.6","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/resolvers-composition","version":"7.0.25","files":["/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/schema","version":"10.0.30","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/utils","version":"10.11.0","files":["/packages/handler-graphql/package.json"]},{"name":"@iconify/json","version":"2.2.386","files":["/packages/app-admin/package.json"]},{"name":"@lexical/code","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/hashtag","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/headless","version":"0.35.0","files":["/packages/lexical-converter/package.json"]},{"name":"@lexical/history","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/html","version":"0.35.0","files":["/packages/lexical-converter/package.json"]},{"name":"@lexical/list","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/mark","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/overflow","version":"0.35.0","files":["/packages/lexical-nodes/package.json"]},{"name":"@lexical/react","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/rich-text","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/selection","version":"0.35.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/text","version":"0.35.0","files":["/packages/lexical-editor/package.json"]},{"name":"@lexical/utils","version":"0.35.0","files":["/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/app-headless-cms-scheduler/package.json"]},{"name":"@minoru/react-dnd-treeview","version":"3.5.3","files":["/packages/admin-ui/package.json"]},{"name":"@monaco-editor/react","version":"4.7.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@noble/hashes","version":"2.0.0","files":["/packages/utils/package.json"]},{"name":"@okta/okta-auth-js","version":"5.11.0","files":["/packages/app-admin-okta/package.json"]},{"name":"@okta/okta-react","version":"6.10.0","files":["/packages/app-admin-okta/package.json"]},{"name":"@okta/okta-signin-widget","version":"5.16.1","files":["/packages/app-admin-okta/package.json"]},{"name":"@pulumi/aws","version":"7.12.0","files":["/packages/project-aws/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/pulumi","version":"3.210.0","files":["/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/random","version":"4.18.4","files":["/packages/project-aws/package.json"]},{"name":"@rsbuild/core","version":"1.6.0","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-react","version":"1.4.1","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-sass","version":"1.4.0","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"1.2.2","files":["/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-type-check","version":"1.3.0","files":["/packages/build-tools/package.json"]},{"name":"@rspack/core","version":"1.5.5","files":["/packages/build-tools/package.json"]},{"name":"@smithy/node-http-handler","version":"2.5.0","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@svgr/webpack","version":"6.5.1","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/build-tools/package.json","/packages/ui/package.json"]},{"name":"@swc/plugin-emotion","version":"11.1.0","files":["/packages/build-tools/package.json"]},{"name":"@tailwindcss/postcss","version":"4.1.16","files":["/packages/build-tools/package.json"]},{"name":"@tanstack/react-table","version":"8.21.3","files":["/packages/admin-ui/package.json"]},{"name":"@types/aws-lambda","version":"8.10.152","files":["/packages/aws-helpers/package.json","/packages/aws-sdk/package.json"]},{"name":"@types/hoist-non-react-statics","version":"3.3.7","files":["/package.json"]},{"name":"@types/mime","version":"2.0.3","files":["/packages/app-admin/package.json"]},{"name":"@types/prismjs","version":"1.26.5","files":["/packages/lexical-nodes/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@types/webpack-env","version":"1.18.8","files":["/packages/build-tools/package.json"]},{"name":"accounting","version":"0.4.1","files":["/packages/i18n/package.json"]},{"name":"apollo-cache","version":"1.3.5","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"apollo-cache-inmemory","version":"1.6.6","files":["/packages/app/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"apollo-link-batch-http","version":"1.2.14","files":["/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-context","version":"1.0.20","files":["/packages/app/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-graphql-playground/package.json"]},{"name":"apollo-link-error","version":"1.1.13","files":["/packages/app/package.json"]},{"name":"apollo-link-http-common","version":"0.2.16","files":["/packages/app/package.json"]},{"name":"apollo-utilities","version":"1.3.4","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"archiver","version":"7.0.1","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"boolean","version":"3.2.0","files":["/packages/app/package.json","/packages/handler-graphql/package.json"]},{"name":"bson-objectid","version":"2.0.4","files":["/packages/utils/package.json"]},{"name":"bytes","version":"3.1.2","files":["/packages/admin-ui/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-sync-system/package.json","/packages/app/package.json","/packages/app-file-manager/package.json"]},{"name":"cache-control-parser","version":"2.0.6","files":["/packages/api-file-manager/package.json"]},{"name":"case","version":"1.6.3","files":["/packages/app-admin/package.json","/packages/project/package.json"]},{"name":"center-align","version":"1.0.1","files":["/packages/data-migration/package.json"]},{"name":"chalk","version":"4.1.2","files":["/packages/aws-layers/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/data-migration/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json"]},{"name":"cheerio","version":"1.1.2","files":["/packages/aws-helpers/package.json","/packages/lexical-converter/package.json"]},{"name":"chokidar","version":"4.0.3","files":["/packages/build-tools/package.json","/packages/project/package.json"]},{"name":"ci-info","version":"4.3.0","files":["/packages/global-config/package.json","/packages/project/package.json","/packages/telemetry/package.json"]},{"name":"class-variance-authority","version":"0.7.1","files":["/packages/admin-ui/package.json"]},{"name":"classnames","version":"2.5.1","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/ui/package.json"]},{"name":"cli-progress","version":"3.12.0","files":["/scripts/cjsToEsm/package.json"]},{"name":"cli-table3","version":"0.6.5","files":["/packages/system-requirements/package.json"]},{"name":"clsx","version":"2.1.1","files":["/packages/admin-ui/package.json"]},{"name":"cmdk","version":"1.1.1","files":["/packages/admin-ui/package.json"]},{"name":"core-js","version":"3.45.1","files":["/packages/project-aws/package.json"]},{"name":"cropperjs","version":"1.6.2","files":["/packages/app-file-manager/package.json"]},{"name":"cross-fetch","version":"3.2.0","files":["/packages/project-aws/package.json"]},{"name":"crypto-hash","version":"3.1.0","files":["/packages/app-record-locking/package.json"]},{"name":"crypto-js","version":"4.2.0","files":["/packages/api-mailer/package.json"]},{"name":"css-loader","version":"7.1.2","files":["/packages/build-tools/package.json"]},{"name":"csstype","version":"3.1.3","files":["/packages/website-builder-sdk/package.json"]},{"name":"dataloader","version":"2.2.3","files":["/packages/api-core/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"dataurl-to-blob","version":"0.0.1","files":["/packages/app-file-manager/package.json"]},{"name":"date-fns","version":"2.30.0","files":["/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json"]},{"name":"dayjs","version":"1.11.18","files":["/packages/app-file-manager/package.json"]},{"name":"debounce","version":"1.2.1","files":["/packages/project/package.json"]},{"name":"decompress","version":"4.2.1","files":["/packages/pulumi-sdk/package.json"]},{"name":"deep-equal","version":"2.2.3","files":["/packages/api-core/package.json","/packages/api-security-cognito/package.json","/packages/app-website-builder/package.json","/packages/tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/packages/website-builder-sdk/package.json"]},{"name":"dnd-core","version":"16.0.1","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"dot-object","version":"2.1.5","files":["/packages/api-headless-cms-ddb/package.json"]},{"name":"dot-prop","version":"6.0.1","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb/package.json","/packages/db-dynamodb/package.json"]},{"name":"dot-prop-immutable","version":"2.1.1","files":["/packages/app-aco/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-mailer/package.json"]},{"name":"dotenv","version":"8.6.0","files":["/packages/project/package.json"]},{"name":"dynamodb-toolbox","version":"0.9.5","files":["/packages/db-dynamodb/package.json"]},{"name":"elastic-ts","version":"0.12.0","files":["/packages/api-elasticsearch/package.json"]},{"name":"emotion","version":"10.0.27","files":["/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-theme/package.json"]},{"name":"eslint","version":"9.39.1","files":["/packages/build-tools/package.json"]},{"name":"execa","version":"5.1.1","files":["/packages/cli/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/buildPackages/package.json"]},{"name":"exit-hook","version":"4.0.0","files":["/packages/project/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/packages/build-tools/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"fast-json-patch","version":"3.1.1","files":["/packages/website-builder-sdk/package.json"]},{"name":"fast-json-stable-stringify","version":"2.1.0","files":["/packages/website-builder-sdk/package.json"]},{"name":"fastify","version":"4.29.1","files":["/packages/handler/package.json","/packages/handler-aws/package.json"]},{"name":"fecha","version":"2.3.3","files":["/packages/i18n/package.json"]},{"name":"find-up","version":"5.0.0","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json"]},{"name":"folder-hash","version":"4.1.1","files":["/scripts/buildPackages/package.json"]},{"name":"fs-extra","version":"11.3.2","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json","/packages/pulumi-sdk/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"fuse.js","version":"7.1.0","files":["/packages/db-dynamodb/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/project-utils/package.json","/scripts/prepublishOnly/package.json"]},{"name":"glob","version":"7.2.3","files":["/packages/cli-core/package.json","/packages/i18n/package.json"]},{"name":"graphlib","version":"2.1.8","files":["/packages/app-admin/package.json"]},{"name":"graphql","version":"16.12.0","files":["/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-mailer/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-request","version":"7.3.5","files":["/packages/project/package.json"]},{"name":"graphql-scalars","version":"1.25.0","files":["/packages/handler-graphql/package.json"]},{"name":"graphql-tag","version":"2.12.6","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/handler-graphql/package.json"]},{"name":"history","version":"5.3.0","files":["/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"humanize-duration","version":"3.33.1","files":["/packages/cli-core/package.json","/packages/project/package.json"]},{"name":"inquirer","version":"12.9.6","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json"]},{"name":"invariant","version":"2.2.4","files":["/packages/app/package.json","/packages/project-aws/package.json"]},{"name":"inversify","version":"6.2.2","files":["/packages/ioc/package.json"]},{"name":"is-hotkey","version":"0.2.0","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"isnumeric","version":"0.3.3","files":["/packages/validation/package.json"]},{"name":"jose","version":"5.10.0","files":["/packages/api-core/package.json"]},{"name":"js-yaml","version":"4.1.1","files":["/packages/create-webiny-project/package.json"]},{"name":"jsdom","version":"25.0.1","files":["/packages/api-headless-cms/package.json"]},{"name":"jsesc","version":"3.1.0","files":["/packages/telemetry/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/utils/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-security-auth0/package.json","/packages/api-security-okta/package.json"]},{"name":"lexical","version":"0.35.0","files":["/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json","/packages/website-builder-sdk/package.json"]},{"name":"listr","version":"0.14.3","files":["/packages/create-webiny-project/package.json"]},{"name":"listr2","version":"5.0.8","files":["/scripts/buildPackages/package.json"]},{"name":"load-json-file","version":"6.2.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"load-script","version":"1.0.0","files":["/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json"]},{"name":"lodash","version":"4.17.21","files":["/packages/admin-ui/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security-access-management/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/build-tools/package.json","/packages/db-dynamodb/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/lexical-editor/package.json","/packages/migrations/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tasks/package.json","/packages/ui/package.json","/packages/validation/package.json","/packages/website-builder-sdk/package.json"]},{"name":"matcher","version":"5.0.0","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"md5","version":"2.3.0","files":["/packages/api-core/package.json"]},{"name":"mime","version":"3.0.0","files":["/packages/api-file-manager-s3/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json"]},{"name":"minimatch","version":"5.1.6","files":["/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app/package.json","/packages/app-file-manager/package.json","/packages/app-security/package.json","/packages/data-migration/package.json","/packages/project/package.json"]},{"name":"mobx","version":"6.15.0","files":["/packages/admin-ui/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-trash-bin/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"mobx-react-lite","version":"3.4.3","files":["/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/website-builder-react/package.json"]},{"name":"monaco-editor","version":"0.53.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"mqtt","version":"5.14.1","files":["/packages/project/package.json"]},{"name":"nanoid","version":"3.3.11","files":["/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"nanoid-dictionary","version":"4.3.0","files":["/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"neverthrow","version":"8.2.0","files":["/packages/project/package.json"]},{"name":"nodemailer","version":"7.0.11","files":["/packages/api-mailer/package.json"]},{"name":"object-hash","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json"]},{"name":"object-merge-advanced","version":"12.1.0","files":["/packages/tasks/package.json"]},{"name":"object-sizeof","version":"2.6.5","files":["/packages/tasks/package.json"]},{"name":"open","version":"10.2.0","files":["/packages/cli-core/package.json"]},{"name":"ora","version":"4.1.1","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/project-aws/package.json"]},{"name":"os","version":"0.1.2","files":["/packages/create-webiny-project/package.json"]},{"name":"p-limit","version":"7.1.1","files":["/scripts/cjsToEsm/package.json"]},{"name":"p-map","version":"7.0.3","files":["/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-reduce","version":"3.0.0","files":["/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-retry","version":"7.0.0","files":["/packages/api-dynamodb-to-elasticsearch/package.json","/packages/app-file-manager-s3/package.json","/packages/project/package.json","/packages/utils/package.json"]},{"name":"pino","version":"9.13.1","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json"]},{"name":"pino-pretty","version":"9.4.1","files":["/packages/cli-core/package.json","/packages/data-migration/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json"]},{"name":"pluralize","version":"8.0.0","files":["/packages/api-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"postcss","version":"8.5.6","files":["/packages/website-builder-nextjs/package.json"]},{"name":"postcss-import","version":"16.1.1","files":["/packages/website-builder-nextjs/package.json"]},{"name":"postcss-loader","version":"8.2.0","files":["/packages/build-tools/package.json"]},{"name":"process","version":"0.11.10","files":["/packages/build-tools/package.json"]},{"name":"prop-types","version":"15.8.1","files":["/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-mailer/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json"]},{"name":"radix-ui","version":"1.4.3","files":["/packages/admin-ui/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/build-tools/package.json"]},{"name":"raw.macro","version":"0.4.2","files":["/packages/app-headless-cms/package.json"]},{"name":"react","version":"18.2.0","files":["/packages/admin-ui/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/website-builder-react/package.json"]},{"name":"react-butterfiles","version":"1.3.3","files":["/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-color","version":"2.19.3","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"react-custom-scrollbars","version":"4.2.1","files":["/packages/admin-ui/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-dnd","version":"16.0.1","files":["/packages/admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dnd-html5-backend","version":"16.0.1","files":["/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/build-tools/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/react-composition/package.json","/packages/website-builder-react/package.json"]},{"name":"react-draggable","version":"4.5.0","files":["/packages/app-admin/package.json"]},{"name":"react-helmet","version":"6.1.0","files":["/packages/app-admin-auth0/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-security-access-management/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json"]},{"name":"react-hotkeyz","version":"1.0.4","files":["/packages/app-aco/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json"]},{"name":"react-lazy-load","version":"3.1.14","files":["/packages/app-file-manager/package.json"]},{"name":"react-refresh","version":"0.11.0","files":["/packages/build-tools/package.json"]},{"name":"react-resizable","version":"3.0.5","files":["/packages/app-admin/package.json"]},{"name":"react-resizable-panels","version":"2.1.9","files":["/packages/app-admin/package.json"]},{"name":"react-style-object-to-css","version":"1.1.2","files":["/packages/lexical-theme/package.json"]},{"name":"react-test-renderer","version":"18.3.1","files":["/packages/project/package.json"]},{"name":"react-transition-group","version":"4.4.5","files":["/packages/app-admin/package.json"]},{"name":"react-virtualized","version":"9.22.6","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"read-json-sync","version":"2.0.1","files":["/packages/build-tools/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/cjsToEsm/package.json"]},{"name":"reflect-metadata","version":"0.2.2","files":["/packages/ioc/package.json"]},{"name":"regenerator-runtime","version":"0.14.1","files":["/packages/project-aws/package.json"]},{"name":"replace-in-path","version":"1.1.0","files":["/packages/project/package.json"]},{"name":"reset-css","version":"5.0.2","files":["/packages/app-admin/package.json"]},{"name":"rimraf","version":"6.0.1","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json"]},{"name":"sanitize-filename","version":"1.6.3","files":["/packages/api-file-manager-s3/package.json"]},{"name":"sass","version":"1.93.0","files":["/packages/build-tools/package.json"]},{"name":"sass-loader","version":"16.0.5","files":["/packages/build-tools/package.json"]},{"name":"semver","version":"7.7.3","files":["/packages/api-headless-cms/package.json","/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/data-migration/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json"]},{"name":"serialize-error","version":"12.0.0","files":["/packages/project/package.json","/scripts/buildPackages/package.json"]},{"name":"sharp","version":"0.34.5","files":["/packages/api-file-manager-s3/package.json"]},{"name":"short-hash","version":"1.0.0","files":["/packages/i18n/package.json"]},{"name":"slugify","version":"1.6.6","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-website-builder/package.json"]},{"name":"sonner","version":"2.0.7","files":["/packages/admin-ui/package.json"]},{"name":"srcset","version":"4.0.0","files":["/packages/aws-helpers/package.json"]},{"name":"store","version":"2.0.12","files":["/packages/app-aco/package.json","/packages/app-admin/package.json"]},{"name":"strip-ansi","version":"6.0.1","files":["/packages/telemetry/package.json"]},{"name":"style-loader","version":"3.3.4","files":["/packages/build-tools/package.json"]},{"name":"tailwind-merge","version":"2.6.0","files":["/packages/admin-ui/package.json"]},{"name":"tailwindcss","version":"4.1.16","files":["/packages/admin-ui/package.json"]},{"name":"tar","version":"6.2.1","files":["/packages/pulumi-sdk/package.json"]},{"name":"timeago-react","version":"3.0.7","files":["/packages/admin-ui/package.json"]},{"name":"tinycolor2","version":"1.6.0","files":["/packages/app-admin/package.json"]},{"name":"ts-invariant","version":"0.10.3","files":["/packages/app/package.json"]},{"name":"ts-morph","version":"24.0.0","files":["/packages/app-admin/package.json","/packages/build-tools/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"tsx","version":"4.20.5","files":["/packages/build-tools/package.json","/packages/cli/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/cjsToEsm/package.json"]},{"name":"tw-animate-css","version":"1.4.0","files":["/packages/admin-ui/package.json"]},{"name":"type-fest","version":"5.2.0","files":["/packages/admin-ui/package.json","/packages/api-websockets/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json"]},{"name":"typescript","version":"5.9.3","files":["/packages/build-tools/package.json"]},{"name":"unicode-emoji-json","version":"0.8.0","files":["/packages/app-admin/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/packages/api-headless-cms-import-export/package.json","/packages/plugins/package.json"]},{"name":"universal-router","version":"9.2.1","files":["/packages/app/package.json"]},{"name":"unzipper","version":"0.12.3","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"url-loader","version":"4.1.1","files":["/packages/build-tools/package.json"]},{"name":"use-deep-compare-effect","version":"1.8.1","files":["/packages/app-headless-cms/package.json"]},{"name":"utf-8-validate","version":"6.0.5","files":["/packages/build-tools/package.json"]},{"name":"uuid","version":"13.0.0","files":["/packages/create-webiny-project/package.json","/packages/global-config/package.json"]},{"name":"validate-npm-package-name","version":"6.0.2","files":["/packages/create-webiny-project/package.json"]},{"name":"vitest","version":"3.2.4","files":["/packages/api-headless-cms-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"warning","version":"4.0.3","files":["/packages/app/package.json"]},{"name":"write-json-file","version":"4.3.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json"]},{"name":"wts-client","version":"2.0.0","files":["/packages/telemetry/package.json"]},{"name":"yargs","version":"17.7.2","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json"]},{"name":"yesno","version":"0.4.0","files":["/packages/create-webiny-project/package.json"]},{"name":"zod","version":"3.25.76","files":["/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/cli-core/package.json","/packages/handler-graphql/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/tasks/package.json","/packages/utils/package.json"]}],"devDependencies":[{"name":"@4tw/cypress-drag-drop","version":"1.8.1","files":["/cypress-tests/package.json"]},{"name":"@babel/cli","version":"7.28.3","files":["/package.json"]},{"name":"@babel/code-frame","version":"7.27.1","files":["/package.json"]},{"name":"@babel/compat-data","version":"7.28.5","files":["/package.json"]},{"name":"@babel/core","version":"7.28.5","files":["/package.json"]},{"name":"@babel/helper-define-polyfill-provider","version":"0.6.5","files":["/package.json"]},{"name":"@babel/helper-environment-visitor","version":"7.24.7","files":["/package.json"]},{"name":"@babel/parser","version":"7.28.5","files":["/package.json"]},{"name":"@babel/plugin-proposal-class-properties","version":"7.18.6","files":["/package.json"]},{"name":"@babel/plugin-proposal-object-rest-spread","version":"7.20.7","files":["/package.json"]},{"name":"@babel/plugin-proposal-throw-expressions","version":"7.27.1","files":["/package.json"]},{"name":"@babel/plugin-syntax-object-rest-spread","version":"7.8.3","files":["/package.json"]},{"name":"@babel/plugin-transform-modules-commonjs","version":"7.27.1","files":["/package.json"]},{"name":"@babel/plugin-transform-runtime","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-env","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-react","version":"7.28.5","files":["/package.json"]},{"name":"@babel/preset-typescript","version":"7.28.5","files":["/package.json"]},{"name":"@babel/register","version":"7.28.3","files":["/package.json","/packages/i18n/package.json"]},{"name":"@babel/runtime","version":"7.28.4","files":["/package.json"]},{"name":"@babel/template","version":"7.27.2","files":["/package.json"]},{"name":"@babel/traverse","version":"7.28.5","files":["/package.json"]},{"name":"@babel/types","version":"7.28.5","files":["/package.json"]},{"name":"@commitlint/cli","version":"11.0.0","files":["/package.json"]},{"name":"@commitlint/config-conventional","version":"11.0.0","files":["/package.json"]},{"name":"@elastic/elasticsearch","version":"7.12.0","files":["/packages/api-headless-cms-ddb-es/package.json","/packages/project-utils/package.json"]},{"name":"@emotion/babel-plugin","version":"11.13.5","files":["/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"@emotion/react","version":"11.10.8","files":["/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@eslint/eslintrc","version":"3.3.1","files":["/package.json"]},{"name":"@eslint/js","version":"9.39.1","files":["/package.json"]},{"name":"@faker-js/faker","version":"9.9.0","files":["/packages/api-headless-cms-es-tasks/package.json","/packages/api-sync-system/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"6.7.2","files":["/packages/admin-ui/package.json"]},{"name":"@grpc/grpc-js","version":"1.14.0","files":["/package.json"]},{"name":"@lexical/code","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/hashtag","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/headless","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/history","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/html","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/list","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/mark","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/overflow","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/react","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/rich-text","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/selection","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/text","version":"0.35.0","files":["/package.json"]},{"name":"@lexical/utils","version":"0.35.0","files":["/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/icons/package.json"]},{"name":"@octokit/rest","version":"20.1.2","files":["/package.json"]},{"name":"@storybook/addon-a11y","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/addon-docs","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/addon-webpack5-compiler-babel","version":"3.0.6","files":["/packages/admin-ui/package.json"]},{"name":"@storybook/react-webpack5","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"@svgr/webpack","version":"6.5.1","files":["/packages/admin-ui/package.json","/packages/app-file-manager/package.json"]},{"name":"@tailwindcss/postcss","version":"4.1.16","files":["/packages/admin-ui/package.json"]},{"name":"@testing-library/cypress","version":"10.1.0","files":["/cypress-tests/package.json"]},{"name":"@testing-library/react","version":"15.0.7","files":["/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/ui/package.json"]},{"name":"@testing-library/user-event","version":"14.6.1","files":["/packages/form/package.json"]},{"name":"@types/accounting","version":"0.4.5","files":["/packages/i18n/package.json"]},{"name":"@types/adm-zip","version":"0.5.7","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/archiver","version":"6.0.3","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/babel__code-frame","version":"7.0.6","files":["/packages/api-headless-cms/package.json"]},{"name":"@types/bytes","version":"3.1.5","files":["/packages/app-admin/package.json"]},{"name":"@types/center-align","version":"1.0.2","files":["/packages/data-migration/package.json"]},{"name":"@types/cli-progress","version":"3.11.6","files":["/scripts/cjsToEsm/package.json"]},{"name":"@types/crypto-js","version":"4.2.2","files":["/packages/api-mailer/package.json"]},{"name":"@types/debounce","version":"1.2.4","files":["/packages/project/package.json"]},{"name":"@types/deep-equal","version":"1.0.4","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/dot-object","version":"2.1.6","files":["/packages/api-headless-cms-ddb/package.json"]},{"name":"@types/folder-hash","version":"4.0.4","files":["/scripts/buildPackages/package.json"]},{"name":"@types/fs-extra","version":"11.0.4","files":["/package.json"]},{"name":"@types/glob","version":"7.2.0","files":["/packages/i18n/package.json"]},{"name":"@types/graphlib","version":"2.1.12","files":["/packages/app-admin/package.json"]},{"name":"@types/humanize-duration","version":"3.27.4","files":["/packages/project/package.json"]},{"name":"@types/inquirer","version":"8.2.12","files":["/package.json"]},{"name":"@types/invariant","version":"2.2.37","files":["/packages/form/package.json"]},{"name":"@types/is-hotkey","version":"0.1.10","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/is-number","version":"7.0.5","files":["/packages/db-dynamodb/package.json"]},{"name":"@types/js-yaml","version":"4.0.9","files":["/packages/create-webiny-project/package.json"]},{"name":"@types/jsdom","version":"21.1.7","files":["/packages/lexical-converter/package.json","/packages/project/package.json"]},{"name":"@types/jsonpack","version":"1.1.6","files":["/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"@types/jsonwebtoken","version":"9.0.10","files":["/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json"]},{"name":"@types/jwk-to-pem","version":"2.0.3","files":["/packages/api-cognito-authenticator/package.json","/packages/api-security-auth0/package.json"]},{"name":"@types/listr","version":"0.14.9","files":["/packages/cli-core/package.json"]},{"name":"@types/lodash","version":"4.17.20","files":["/packages/api-sync-system/package.json","/packages/app/package.json","/packages/app-cognito-authenticator/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/validation/package.json"]},{"name":"@types/md5","version":"2.3.5","files":["/packages/api-core/package.json"]},{"name":"@types/ncp","version":"2.0.8","files":["/packages/project-aws/package.json"]},{"name":"@types/node","version":"22.18.6","files":["/package.json"]},{"name":"@types/nodemailer","version":"6.4.19","files":["/packages/api-mailer/package.json"]},{"name":"@types/object-hash","version":"3.0.6","files":["/packages/api-file-manager/package.json"]},{"name":"@types/pako","version":"2.0.4","files":["/packages/app-website-builder/package.json"]},{"name":"@types/platform","version":"1.3.6","files":["/packages/app-website-builder/package.json"]},{"name":"@types/pluralize","version":"0.0.33","files":["/packages/api-headless-cms/package.json"]},{"name":"@types/postcss-import","version":"14.0.3","files":["/packages/website-builder-nextjs/package.json"]},{"name":"@types/randomcolor","version":"0.5.9","files":["/packages/app-website-builder/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/package.json","/packages/admin-ui/package.json","/packages/app-aco/package.json","/packages/app-audit-logs/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/theme/package.json","/packages/website-builder-react/package.json"]},{"name":"@types/react-color","version":"2.17.12","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@types/react-custom-scrollbars","version":"4.0.13","files":["/packages/admin-ui/package.json"]},{"name":"@types/react-dom","version":"18.2.25","files":["/package.json"]},{"name":"@types/react-helmet","version":"6.1.11","files":["/packages/app-admin-ui/package.json","/packages/app-security-access-management/package.json"]},{"name":"@types/react-images","version":"0.5.3","files":["/packages/app-website-builder/package.json"]},{"name":"@types/react-resizable","version":"3.0.8","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/react-test-renderer","version":"18.3.1","files":["/packages/project/package.json"]},{"name":"@types/react-transition-group","version":"4.4.12","files":["/packages/app-admin/package.json"]},{"name":"@types/react-virtualized","version":"9.22.3","files":["/packages/admin-ui/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/read-json-sync","version":"2.0.3","files":["/packages/project/package.json"]},{"name":"@types/resize-observer-browser","version":"0.1.11","files":["/packages/app-website-builder/package.json"]},{"name":"@types/semver","version":"7.7.1","files":["/packages/data-migration/package.json"]},{"name":"@types/store","version":"2.0.5","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"@types/tinycolor2","version":"1.4.6","files":["/packages/app-admin/package.json"]},{"name":"@types/uniqid","version":"5.3.4","files":["/packages/feature-flags/package.json","/packages/plugins/package.json"]},{"name":"@types/universal-router","version":"8.0.0","files":["/packages/app/package.json"]},{"name":"@types/unzipper","version":"0.10.11","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"@types/validate-npm-package-name","version":"3.0.3","files":["/packages/create-webiny-project/package.json"]},{"name":"@types/warning","version":"3.0.3","files":["/packages/app/package.json"]},{"name":"@types/yargs","version":"17.0.33","files":["/scripts/buildPackages/package.json"]},{"name":"@typescript-eslint/eslint-plugin","version":"8.48.0","files":["/package.json"]},{"name":"@typescript-eslint/parser","version":"8.48.0","files":["/package.json"]},{"name":"@vitest/coverage-v8","version":"3.2.4","files":["/package.json"]},{"name":"@vitest/eslint-plugin","version":"1.4.2","files":["/package.json"]},{"name":"adio","version":"2.0.1","files":["/package.json"]},{"name":"adm-zip","version":"0.5.16","files":["/packages/api-headless-cms-import-export/package.json"]},{"name":"amazon-cognito-identity-js","version":"4.6.3","files":["/cypress-tests/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"apollo-graphql","version":"0.9.7","files":["/packages/api-headless-cms/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app-aco/package.json","/packages/app-trash-bin/package.json"]},{"name":"aws-sdk-client-mock","version":"4.1.0","files":["/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-sync-system/package.json"]},{"name":"axios","version":"1.12.2","files":["/package.json"]},{"name":"babel-loader","version":"10.0.0","files":["/package.json","/packages/ui/package.json"]},{"name":"babel-plugin-dynamic-import-node","version":"2.3.3","files":["/package.json"]},{"name":"babel-plugin-macros","version":"3.1.0","files":["/package.json"]},{"name":"babel-plugin-module-resolver","version":"5.0.2","files":["/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"babel-plugin-named-asset-import","version":"1.0.0-next.fb6e6f70","files":["/packages/app-admin-ui/package.json"]},{"name":"chalk","version":"4.1.2","files":["/package.json","/packages/admin-ui/package.json"]},{"name":"cross-env","version":"5.2.1","files":["/package.json"]},{"name":"cross-spawn","version":"6.0.6","files":["/package.json"]},{"name":"css-loader","version":"7.1.2","files":["/packages/admin-ui/package.json"]},{"name":"cypress","version":"13.17.0","files":["/cypress-tests/package.json"]},{"name":"cypress-image-snapshot","version":"4.0.1","files":["/cypress-tests/package.json"]},{"name":"cypress-mailosaur","version":"2.17.0","files":["/cypress-tests/package.json"]},{"name":"cypress-wait-until","version":"1.7.2","files":["/cypress-tests/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/package.json"]},{"name":"del","version":"6.1.1","files":["/cypress-tests/package.json"]},{"name":"elastic-ts","version":"0.12.0","files":["/packages/migrations/package.json"]},{"name":"env-ci","version":"2.6.0","files":["/package.json"]},{"name":"eslint","version":"9.39.1","files":["/package.json"]},{"name":"eslint-config-standard","version":"17.1.0","files":["/package.json"]},{"name":"eslint-import-resolver-babel-module","version":"5.3.2","files":["/package.json"]},{"name":"eslint-plugin-import","version":"2.32.0","files":["/package.json"]},{"name":"eslint-plugin-lodash","version":"8.0.0","files":["/package.json"]},{"name":"eslint-plugin-node","version":"11.1.0","files":["/package.json"]},{"name":"eslint-plugin-promise","version":"7.2.1","files":["/package.json"]},{"name":"eslint-plugin-react","version":"7.37.5","files":["/package.json"]},{"name":"eslint-plugin-standard","version":"5.0.0","files":["/package.json"]},{"name":"eslint-plugin-storybook","version":"9.1.16","files":["/packages/admin-ui/package.json"]},{"name":"execa","version":"5.1.1","files":["/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/common-audit-logs/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"file-loader","version":"6.2.0","files":["/packages/admin-ui/package.json"]},{"name":"folder-hash","version":"4.1.1","files":["/package.json"]},{"name":"fs-extra","version":"11.3.2","files":["/package.json"]},{"name":"get-stream","version":"3.0.0","files":["/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/package.json"]},{"name":"git-cz","version":"1.8.4","files":["/package.json"]},{"name":"github-actions-wac","version":"2.0.0","files":["/package.json"]},{"name":"glob","version":"7.2.3","files":["/package.json"]},{"name":"graphql","version":"16.12.0","files":["/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-file-manager-aco/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/testing/package.json"]},{"name":"graphql-request","version":"7.3.5","files":["/cypress-tests/package.json"]},{"name":"husky","version":"4.3.8","files":["/package.json"]},{"name":"identity-obj-proxy","version":"3.0.0","files":["/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"inquirer","version":"12.9.6","files":["/package.json"]},{"name":"jest-dynalite","version":"3.6.1","files":["/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-sync-system/package.json","/packages/data-migration/package.json","/packages/db-dynamodb/package.json","/packages/migrations/package.json","/packages/project-utils/package.json"]},{"name":"jest-extended","version":"6.0.0","files":["/package.json"]},{"name":"jsdom","version":"25.0.1","files":["/packages/lexical-converter/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/api-file-manager-ddb/package.json"]},{"name":"lerna","version":"8.1.2","files":["/package.json"]},{"name":"lexical","version":"0.35.0","files":["/package.json"]},{"name":"lint-staged","version":"16.1.6","files":["/package.json"]},{"name":"listr","version":"0.14.3","files":["/package.json"]},{"name":"listr2","version":"5.0.8","files":["/packages/project-utils/package.json"]},{"name":"load-json-file","version":"6.2.0","files":["/package.json","/packages/project-utils/package.json"]},{"name":"lodash","version":"4.17.21","files":["/package.json","/cypress-tests/package.json"]},{"name":"longest","version":"2.0.1","files":["/package.json"]},{"name":"md5","version":"2.3.0","files":["/packages/api-security-cognito/package.json"]},{"name":"minimatch","version":"5.1.6","files":["/package.json"]},{"name":"mobx","version":"6.15.0","files":["/packages/form/package.json"]},{"name":"mobx-react-lite","version":"3.4.3","files":["/packages/form/package.json"]},{"name":"nanoid","version":"3.3.11","files":["/package.json","/cypress-tests/package.json"]},{"name":"ncp","version":"2.0.0","files":["/packages/ui/package.json"]},{"name":"next","version":"15.5.7","files":["/packages/website-builder-nextjs/package.json"]},{"name":"pino","version":"9.13.1","files":["/packages/logger/package.json","/packages/project-utils/package.json"]},{"name":"pino-pretty","version":"9.4.1","files":["/packages/project-utils/package.json"]},{"name":"postcss-loader","version":"8.2.0","files":["/packages/admin-ui/package.json"]},{"name":"prettier","version":"3.6.2","files":["/package.json","/packages/admin-ui/package.json","/packages/api-aco/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-website-builder/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/ui/package.json"]},{"name":"react","version":"18.2.0","files":["/package.json","/packages/lexical-nodes/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/package.json"]},{"name":"react-router","version":"7.9.1","files":["/packages/admin-ui/package.json"]},{"name":"react-router-dom","version":"7.9.1","files":["/packages/admin-ui/package.json"]},{"name":"rimraf","version":"6.0.1","files":["/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json","/packages/api-security-okta/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/aws-sdk/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/package.json","/packages/data-migration/package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/error/package.json","/packages/feature-flags/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-client/package.json","/packages/handler-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/logger/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pubsub/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tasks/package.json","/packages/testing/package.json","/packages/theme/package.json","/packages/ui/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/scripts/cli/package.json"]},{"name":"sass","version":"1.93.0","files":["/packages/admin-ui/package.json"]},{"name":"semver","version":"7.7.3","files":["/package.json"]},{"name":"storybook","version":"9.1.8","files":["/packages/admin-ui/package.json"]},{"name":"ts-expect","version":"1.3.0","files":["/package.json"]},{"name":"tsx","version":"4.20.5","files":["/package.json"]},{"name":"ttypescript","version":"1.5.15","files":["/packages/api-file-manager-aco/package.json"]},{"name":"type-fest","version":"5.2.0","files":["/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-record-locking/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/project/package.json","/packages/tasks/package.json","/scripts/prepublishOnly/package.json"]},{"name":"typescript","version":"5.9.3","files":["/package.json","/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-cognito-authenticator/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-dynamodb-to-elasticsearch/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-ddb/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-es-tasks/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-tasks-ddb-es/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-auth0/package.json","/packages/api-security-cognito/package.json","/packages/api-security-okta/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-auth0/package.json","/packages/app-admin-cognito/package.json","/packages/app-admin-okta/package.json","/packages/app-admin-ui/package.json","/packages/app-admin-users-cognito/package.json","/packages/app-audit-logs/package.json","/packages/app-cognito-authenticator/package.json","/packages/app-file-manager/package.json","/packages/app-file-manager-s3/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-scheduler/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-mailer/package.json","/packages/app-record-locking/package.json","/packages/app-security/package.json","/packages/app-security-access-management/package.json","/packages/app-serverless-cms/package.json","/packages/app-trash-bin/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/aws-sdk/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/package.json","/packages/data-migration/package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/error/package.json","/packages/feature/package.json","/packages/feature-flags/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-client/package.json","/packages/handler-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/ioc/package.json","/packages/logger/package.json","/packages/migrations/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pubsub/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/shared-aco/package.json","/packages/tasks/package.json","/packages/testing/package.json","/packages/theme/package.json","/packages/ui/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/cypress-tests/package.json","/scripts/cli/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/cypress-tests/package.json"]},{"name":"validator","version":"13.15.23","files":["/package.json"]},{"name":"verdaccio","version":"6.2.4","files":["/package.json"]},{"name":"vite-tsconfig-paths","version":"5.1.4","files":["/package.json","/packages/app-aco/package.json"]},{"name":"vitest","version":"3.2.4","files":["/package.json","/packages/admin-ui/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-elasticsearch/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-bulk-actions/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-es-tasks/package.json","/packages/api-headless-cms-import-export/package.json","/packages/api-headless-cms-scheduler/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-log/package.json","/packages/api-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-security-cognito/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/data-migration/package.json","/packages/db-dynamodb/package.json","/packages/form/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/ioc/package.json","/packages/lexical-converter/package.json","/packages/migrations/package.json","/packages/plugins/package.json","/packages/project-utils/package.json","/packages/pubsub/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tasks/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json"]},{"name":"webpack","version":"5.101.3","files":["/packages/website-builder-nextjs/package.json"]},{"name":"write-json-file","version":"4.3.0","files":["/package.json","/packages/api-headless-cms/package.json"]},{"name":"yargs","version":"17.7.2","files":["/package.json","/packages/project-utils/package.json"]},{"name":"zod","version":"3.25.76","files":["/packages/ioc/package.json"]}],"peerDependencies":[{"name":"minimatch","version":"5.1.6","files":["/packages/ui/package.json"]},{"name":"react","version":"18.2.0","files":["/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/theme/package.json","/packages/ui/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/packages/ui/package.json"]}],"resolutions":[{"name":"@emotion/react","version":"11.10.8","files":["/package.json"]},{"name":"@octokit/rest","version":"20.1.2","files":["/package.json"]},{"name":"@types/react","version":"18.2.79","files":["/package.json"]},{"name":"@types/react-dom","version":"18.2.25","files":["/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/package.json"]},{"name":"react","version":"18.2.0","files":["/package.json"]},{"name":"react-dom","version":"18.2.0","files":["/package.json"]},{"name":"systeminformation","version":"5.23.18","files":["/package.json"]}],"references":[{"name":"@types/hoist-non-react-statics","versions":[{"version":"3.3.7","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"@babel/cli","versions":[{"version":"7.28.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/code-frame","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@babel/compat-data","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/core","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/helper-define-polyfill-provider","versions":[{"version":"0.6.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/helper-environment-visitor","versions":[{"version":"7.24.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/parser","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-class-properties","versions":[{"version":"7.18.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-object-rest-spread","versions":[{"version":"7.20.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-proposal-throw-expressions","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-syntax-object-rest-spread","versions":[{"version":"7.8.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-transform-modules-commonjs","versions":[{"version":"7.27.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/plugin-transform-runtime","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/preset-env","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/preset-react","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/preset-typescript","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/register","versions":[{"version":"7.28.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"@babel/runtime","versions":[{"version":"7.28.4","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@babel/template","versions":[{"version":"7.27.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/traverse","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@babel/types","versions":[{"version":"7.28.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/cli","versions":[{"version":"11.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/config-conventional","versions":[{"version":"11.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@eslint/eslintrc","versions":[{"version":"3.3.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@eslint/js","versions":[{"version":"9.39.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@grpc/grpc-js","versions":[{"version":"1.14.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@lexical/code","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/hashtag","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/headless","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/history","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/html","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/list","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/mark","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/overflow","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/react","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/rich-text","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/selection","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/text","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/utils","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@octokit/rest","versions":[{"version":"20.1.2","files":[{"file":"/package.json","types":["devDependencies","resolutions"]}]}]},{"name":"@types/fs-extra","versions":[{"version":"11.0.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/inquirer","versions":[{"version":"8.2.12","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/node","versions":[{"version":"22.18.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/react","versions":[{"version":"18.2.79","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-dom","versions":[{"version":"18.2.25","files":[{"file":"/package.json","types":["devDependencies","resolutions"]}]}]},{"name":"@typescript-eslint/eslint-plugin","versions":[{"version":"8.48.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@typescript-eslint/parser","versions":[{"version":"8.48.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@vitest/coverage-v8","versions":[{"version":"3.2.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@vitest/eslint-plugin","versions":[{"version":"1.4.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"adio","versions":[{"version":"2.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"axios","versions":[{"version":"1.12.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-loader","versions":[{"version":"10.0.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-dynamic-import-node","versions":[{"version":"2.3.3","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-macros","versions":[{"version":"3.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-module-resolver","versions":[{"version":"5.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]}]}]},{"name":"chalk","versions":[{"version":"4.1.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/aws-layers/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"cross-env","versions":[{"version":"5.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"cross-spawn","versions":[{"version":"6.0.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"deepmerge","versions":[{"version":"4.3.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"env-ci","versions":[{"version":"2.6.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint","versions":[{"version":"9.39.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"eslint-config-standard","versions":[{"version":"17.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-import-resolver-babel-module","versions":[{"version":"5.3.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-import","versions":[{"version":"2.32.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-lodash","versions":[{"version":"8.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-node","versions":[{"version":"11.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-promise","versions":[{"version":"7.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-react","versions":[{"version":"7.37.5","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"eslint-plugin-standard","versions":[{"version":"5.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"execa","versions":[{"version":"5.1.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"folder-hash","versions":[{"version":"4.1.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"fs-extra","versions":[{"version":"11.3.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"get-stream","versions":[{"version":"3.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"get-yarn-workspaces","versions":[{"version":"1.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"git-cz","versions":[{"version":"1.8.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"github-actions-wac","versions":[{"version":"2.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"glob","versions":[{"version":"7.2.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"graphql","versions":[{"version":"16.12.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies","devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]}]}]},{"name":"husky","versions":[{"version":"4.3.8","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"inquirer","versions":[{"version":"12.9.6","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"jest-extended","versions":[{"version":"6.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lerna","versions":[{"version":"8.1.2","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lexical","versions":[{"version":"0.35.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"lint-staged","versions":[{"version":"16.1.6","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"listr","versions":[{"version":"0.14.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"load-json-file","versions":[{"version":"6.2.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"lodash","versions":[{"version":"4.17.21","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-aco/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/i18n-react/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]},{"file":"/packages/validation/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"longest","versions":[{"version":"2.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"minimatch","versions":[{"version":"5.1.6","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]}]}]},{"name":"nanoid","versions":[{"version":"3.3.11","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"prettier","versions":[{"version":"3.6.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"react","versions":[{"version":"18.2.0","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["peerDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-websockets/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["peerDependencies"]},{"file":"/packages/i18n/package.json","types":["peerDependencies"]},{"file":"/packages/i18n-react/package.json","types":["peerDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/react-properties/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["dependencies"]},{"file":"/packages/theme/package.json","types":["peerDependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"react-dom","versions":[{"version":"18.2.0","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-websockets/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["peerDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"semver","versions":[{"version":"7.7.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"ts-expect","versions":[{"version":"1.3.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"tsx","versions":[{"version":"4.20.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"type-fest","versions":[{"version":"5.2.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["dependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/db/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"typescript","versions":[{"version":"5.9.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-os/package.json","types":["devDependencies"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-security-okta/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-utils/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/error/package.json","types":["devDependencies"]},{"file":"/packages/feature/package.json","types":["devDependencies"]},{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-client/package.json","types":["devDependencies"]},{"file":"/packages/handler-db/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/i18n-react/package.json","types":["devDependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/shared-aco/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/wcp/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"validator","versions":[{"version":"13.15.23","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"verdaccio","versions":[{"version":"6.2.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"vite-tsconfig-paths","versions":[{"version":"5.1.4","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]}]}]},{"name":"vitest","versions":[{"version":"3.2.4","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-aco/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-bulk-actions/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"write-json-file","versions":[{"version":"4.3.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]}]}]},{"name":"yargs","versions":[{"version":"17.7.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"systeminformation","versions":[{"version":"5.23.18","files":[{"file":"/package.json","types":["resolutions"]}]}]},{"name":"@emotion/react","versions":[{"version":"11.10.8","files":[{"file":"/package.json","types":["resolutions"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["dependencies"]}]}]},{"name":"jsonwebtoken","versions":[{"version":"9.0.3","files":[{"file":"/package.json","types":["resolutions"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["dependencies"]},{"file":"/packages/api-security-okta/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-svg-core","versions":[{"version":"1.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/react-fontawesome","versions":[{"version":"0.1.19","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]}]}]},{"name":"@minoru/react-dnd-treeview","versions":[{"version":"3.5.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"@monaco-editor/react","versions":[{"version":"4.7.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@tanstack/react-table","versions":[{"version":"8.21.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"bytes","versions":[{"version":"3.1.2","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"class-variance-authority","versions":[{"version":"0.7.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"clsx","versions":[{"version":"2.1.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"cmdk","versions":[{"version":"1.1.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"mobx","versions":[{"version":"6.15.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-utils/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"monaco-editor","versions":[{"version":"0.53.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"radix-ui","versions":[{"version":"1.4.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"react-color","versions":[{"version":"2.19.3","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"react-custom-scrollbars","versions":[{"version":"4.2.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-dnd","versions":[{"version":"16.0.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"react-virtualized","versions":[{"version":"9.22.6","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"sonner","versions":[{"version":"2.0.7","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwind-merge","versions":[{"version":"2.6.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwindcss","versions":[{"version":"4.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"timeago-react","versions":[{"version":"3.0.7","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tw-animate-css","versions":[{"version":"1.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-solid-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@storybook/addon-a11y","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-docs","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-webpack5-compiler-babel","versions":[{"version":"3.0.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/react-webpack5","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@svgr/webpack","versions":[{"version":"6.5.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]}]}]},{"name":"@tailwindcss/postcss","versions":[{"version":"4.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/react-color","versions":[{"version":"2.17.12","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-custom-scrollbars","versions":[{"version":"4.0.13","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-virtualized","versions":[{"version":"9.22.3","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"css-loader","versions":[{"version":"7.1.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"eslint-plugin-storybook","versions":[{"version":"9.1.16","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"file-loader","versions":[{"version":"6.2.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"postcss-loader","versions":[{"version":"8.2.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"react-router","versions":[{"version":"7.9.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"react-router-dom","versions":[{"version":"7.9.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"rimraf","versions":[{"version":"6.0.1","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-background-tasks-os/package.json","types":["devDependencies"]},{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]},{"file":"/packages/api-security-okta/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/api-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-mailer/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-websockets/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/error/package.json","types":["devDependencies"]},{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/handler/package.json","types":["devDependencies"]},{"file":"/packages/handler-aws/package.json","types":["devDependencies"]},{"file":"/packages/handler-client/package.json","types":["devDependencies"]},{"file":"/packages/handler-db/package.json","types":["devDependencies"]},{"file":"/packages/handler-graphql/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/i18n-react/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/pubsub/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/tasks/package.json","types":["devDependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/wcp/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"sass","versions":[{"version":"1.93.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"storybook","versions":[{"version":"9.1.8","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"zod","versions":[{"version":"3.25.76","files":[{"file":"/packages/api-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-tasks/package.json","types":["dependencies"]},{"file":"/packages/api-log/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-websockets/package.json","types":["dependencies"]},{"file":"/packages/api-workflows/package.json","types":["dependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/ioc/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"@types/jsonwebtoken","versions":[{"version":"9.0.10","files":[{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]}]}]},{"name":"@types/jwk-to-pem","versions":[{"version":"2.0.3","files":[{"file":"/packages/api-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/api-security-auth0/package.json","types":["devDependencies"]}]}]},{"name":"dataloader","versions":[{"version":"2.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]}]}]},{"name":"deep-equal","versions":[{"version":"2.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/tasks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"jose","versions":[{"version":"5.10.0","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"md5","versions":[{"version":"2.3.0","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-security-cognito/package.json","types":["devDependencies"]}]}]},{"name":"@types/md5","versions":[{"version":"2.3.5","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]}]}]},{"name":"jest-dynalite","versions":[{"version":"3.6.1","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/api-log/package.json","types":["devDependencies"]},{"file":"/packages/api-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"p-retry","versions":[{"version":"7.0.0","files":[{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"@elastic/elasticsearch","versions":[{"version":"7.12.0","files":[{"file":"/packages/api-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"elastic-ts","versions":[{"version":"0.12.0","files":[{"file":"/packages/api-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/migrations/package.json","types":["devDependencies"]}]}]},{"name":"cache-control-parser","versions":[{"version":"2.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"object-hash","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"@types/object-hash","versions":[{"version":"3.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]}]}]},{"name":"ttypescript","versions":[{"version":"1.5.15","files":[{"file":"/packages/api-file-manager-aco/package.json","types":["devDependencies"]}]}]},{"name":"jsonpack","versions":[{"version":"1.1.5","files":[{"file":"/packages/api-file-manager-ddb/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"mime","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"p-map","versions":[{"version":"7.0.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"p-reduce","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"sanitize-filename","versions":[{"version":"1.6.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"sharp","versions":[{"version":"0.34.5","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/merge","versions":[{"version":"9.1.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/schema","versions":[{"version":"10.0.30","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"dot-prop","versions":[{"version":"6.0.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"graphql-tag","versions":[{"version":"2.12.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"jsdom","versions":[{"version":"25.0.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]}]}]},{"name":"pluralize","versions":[{"version":"8.0.0","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"slugify","versions":[{"version":"1.6.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@types/babel__code-frame","versions":[{"version":"7.0.6","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"@types/pluralize","versions":[{"version":"0.0.33","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"apollo-graphql","versions":[{"version":"0.9.7","files":[{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]}]}]},{"name":"dot-object","versions":[{"version":"2.1.5","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["dependencies"]}]}]},{"name":"@types/dot-object","versions":[{"version":"2.1.6","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]}]}]},{"name":"@types/jsonpack","versions":[{"version":"1.1.6","files":[{"file":"/packages/api-headless-cms-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["devDependencies"]}]}]},{"name":"@faker-js/faker","versions":[{"version":"9.9.0","files":[{"file":"/packages/api-headless-cms-es-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"@smithy/node-http-handler","versions":[{"version":"2.5.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"archiver","versions":[{"version":"7.0.1","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"uniqid","versions":[{"version":"5.4.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]},{"file":"/packages/plugins/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"unzipper","versions":[{"version":"0.12.3","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["dependencies"]}]}]},{"name":"@types/adm-zip","versions":[{"version":"0.5.7","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"@types/archiver","versions":[{"version":"6.0.3","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"@types/unzipper","versions":[{"version":"0.10.11","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"adm-zip","versions":[{"version":"0.5.16","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]}]}]},{"name":"aws-sdk-client-mock","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-headless-cms-import-export/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"crypto-js","versions":[{"version":"4.2.0","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"nodemailer","versions":[{"version":"7.0.11","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"@types/crypto-js","versions":[{"version":"4.2.2","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@types/nodemailer","versions":[{"version":"6.4.19","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@types/lodash","versions":[{"version":"4.17.20","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["devDependencies"]},{"file":"/packages/cli/package.json","types":["devDependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/i18n/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]}]}]},{"name":"@apollo/react-hooks","versions":[{"version":"3.1.5","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@emotion/styled","versions":[{"version":"11.10.6","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"apollo-cache","versions":[{"version":"1.3.5","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"apollo-cache-inmemory","versions":[{"version":"1.6.6","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-client","versions":[{"version":"2.6.10","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"apollo-link","versions":[{"version":"1.2.14","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-context","versions":[{"version":"1.0.20","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-error","versions":[{"version":"1.1.13","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-http-common","versions":[{"version":"0.2.16","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"apollo-utilities","versions":[{"version":"1.3.4","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"boolean","versions":[{"version":"3.2.0","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"history","versions":[{"version":"5.3.0","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"invariant","versions":[{"version":"2.2.4","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"ts-invariant","versions":[{"version":"0.10.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"universal-router","versions":[{"version":"9.2.1","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"warning","versions":[{"version":"4.0.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"@types/universal-router","versions":[{"version":"8.0.0","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"@types/warning","versions":[{"version":"3.0.3","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"dot-prop-immutable","versions":[{"version":"2.1.1","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]}]}]},{"name":"mobx-react-lite","versions":[{"version":"3.4.3","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-trash-bin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"react-hotkeyz","versions":[{"version":"1.0.4","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"store","versions":[{"version":"2.0.12","files":[{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-components","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]}]}]},{"name":"@iconify/json","versions":[{"version":"2.2.386","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@types/mime","versions":[{"version":"2.0.3","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"case","versions":[{"version":"1.6.3","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"classnames","versions":[{"version":"2.5.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["dependencies"]}]}]},{"name":"emotion","versions":[{"version":"10.0.27","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/lexical-theme/package.json","types":["dependencies"]}]}]},{"name":"graphlib","versions":[{"version":"2.1.8","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"is-hotkey","versions":[{"version":"0.2.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"prop-types","versions":[{"version":"15.8.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-mailer/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"react-draggable","versions":[{"version":"4.5.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-resizable","versions":[{"version":"3.0.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-resizable-panels","versions":[{"version":"2.1.9","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"react-transition-group","versions":[{"version":"4.4.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"reset-css","versions":[{"version":"5.0.2","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"tinycolor2","versions":[{"version":"1.6.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"ts-morph","versions":[{"version":"24.0.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"unicode-emoji-json","versions":[{"version":"0.8.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@emotion/babel-plugin","versions":[{"version":"11.13.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-security/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/theme/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@types/bytes","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/graphlib","versions":[{"version":"2.1.12","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/is-hotkey","versions":[{"version":"0.1.10","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-resizable","versions":[{"version":"3.0.8","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-transition-group","versions":[{"version":"4.4.12","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@types/store","versions":[{"version":"2.0.5","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/tinycolor2","versions":[{"version":"1.4.6","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"@auth0/auth0-react","versions":[{"version":"2.5.0","files":[{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]}]}]},{"name":"react-helmet","versions":[{"version":"6.1.0","files":[{"file":"/packages/app-admin-auth0/package.json","types":["dependencies"]},{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin-users-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@aws-amplify/auth","versions":[{"version":"5.6.15","files":[{"file":"/packages/app-admin-cognito/package.json","types":["dependencies"]},{"file":"/packages/app-cognito-authenticator/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-auth-js","versions":[{"version":"5.11.0","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-react","versions":[{"version":"6.10.0","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@okta/okta-signin-widget","versions":[{"version":"5.16.1","files":[{"file":"/packages/app-admin-okta/package.json","types":["dependencies"]}]}]},{"name":"@types/react-helmet","versions":[{"version":"6.1.11","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-security-access-management/package.json","types":["devDependencies"]}]}]},{"name":"babel-plugin-named-asset-import","versions":[{"version":"1.0.0-next.fb6e6f70","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"date-fns","versions":[{"version":"2.30.0","files":[{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-common","versions":[{"version":"3.1.4","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"cropperjs","versions":[{"version":"1.6.2","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"dataurl-to-blob","versions":[{"version":"0.0.1","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"dayjs","versions":[{"version":"1.11.18","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"load-script","versions":[{"version":"1.0.0","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"react-butterfiles","versions":[{"version":"1.3.3","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-lazy-load","versions":[{"version":"3.1.14","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"@emotion/css","versions":[{"version":"11.10.6","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-common-types","versions":[{"version":"0.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-brands-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-regular-svg-icons","versions":[{"version":"6.7.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"dnd-core","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"raw.macro","versions":[{"version":"0.4.2","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"react-dnd-html5-backend","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"use-deep-compare-effect","versions":[{"version":"1.8.1","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@material-design-icons/svg","versions":[{"version":"0.14.15","files":[{"file":"/packages/app-headless-cms-scheduler/package.json","types":["dependencies"]},{"file":"/packages/icons/package.json","types":["devDependencies"]}]}]},{"name":"crypto-hash","versions":[{"version":"3.1.0","files":[{"file":"/packages/app-record-locking/package.json","types":["dependencies"]}]}]},{"name":"apollo-link-batch-http","versions":[{"version":"1.2.14","files":[{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"matcher","versions":[{"version":"5.0.0","files":[{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/deep-equal","versions":[{"version":"1.0.4","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]}]}]},{"name":"@types/pako","versions":[{"version":"2.0.4","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/platform","versions":[{"version":"1.3.6","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/randomcolor","versions":[{"version":"0.5.9","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-images","versions":[{"version":"0.5.3","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/resize-observer-browser","versions":[{"version":"0.1.11","files":[{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]}]}]},{"name":"@types/aws-lambda","versions":[{"version":"8.10.152","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]},{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"cheerio","versions":[{"version":"1.1.2","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"srcset","versions":[{"version":"4.0.0","files":[{"file":"/packages/aws-helpers/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-apigatewaymanagementapi","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudfront","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-events","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-logs","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cognito-identity-provider","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb-streams","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-eventbridge","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iam","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iot","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-lambda","versions":[{"version":"3.942.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-s3","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-scheduler","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sfn","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sqs","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sts","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/credential-providers","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-storage","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-presigned-post","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-request-presigner","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/util-dynamodb","versions":[{"version":"3.940.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/core","versions":[{"version":"1.6.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-react","versions":[{"version":"1.4.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-sass","versions":[{"version":"1.4.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-svgr","versions":[{"version":"1.2.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-type-check","versions":[{"version":"1.3.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rspack/core","versions":[{"version":"1.5.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@swc/plugin-emotion","versions":[{"version":"11.1.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/webpack-env","versions":[{"version":"1.18.8","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"chokidar","versions":[{"version":"4.0.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"fast-glob","versions":[{"version":"3.3.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"find-up","versions":[{"version":"5.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"process","versions":[{"version":"0.11.10","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"raw-loader","versions":[{"version":"4.0.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"react-refresh","versions":[{"version":"0.11.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"read-json-sync","versions":[{"version":"2.0.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"sass-loader","versions":[{"version":"16.0.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"style-loader","versions":[{"version":"3.3.4","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"url-loader","versions":[{"version":"4.1.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"utf-8-validate","versions":[{"version":"6.0.5","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"humanize-duration","versions":[{"version":"3.33.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"open","versions":[{"version":"10.2.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]}]}]},{"name":"ora","versions":[{"version":"4.1.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"pino","versions":[{"version":"9.13.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]}]}]},{"name":"pino-pretty","versions":[{"version":"9.4.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/data-migration/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/listr","versions":[{"version":"0.14.9","files":[{"file":"/packages/cli-core/package.json","types":["devDependencies"]}]}]},{"name":"js-yaml","versions":[{"version":"4.1.1","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"os","versions":[{"version":"0.1.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"uuid","versions":[{"version":"13.0.0","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]}]}]},{"name":"validate-npm-package-name","versions":[{"version":"6.0.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"yesno","versions":[{"version":"0.4.0","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"@types/js-yaml","versions":[{"version":"4.0.9","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"@types/validate-npm-package-name","versions":[{"version":"3.0.3","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"center-align","versions":[{"version":"1.0.1","files":[{"file":"/packages/data-migration/package.json","types":["dependencies"]}]}]},{"name":"@types/center-align","versions":[{"version":"1.0.2","files":[{"file":"/packages/data-migration/package.json","types":["devDependencies"]}]}]},{"name":"@types/semver","versions":[{"version":"7.7.1","files":[{"file":"/packages/data-migration/package.json","types":["devDependencies"]}]}]},{"name":"dynamodb-toolbox","versions":[{"version":"0.9.5","files":[{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"fuse.js","versions":[{"version":"7.1.0","files":[{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"@types/is-number","versions":[{"version":"7.0.5","files":[{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]}]}]},{"name":"@types/uniqid","versions":[{"version":"5.3.4","files":[{"file":"/packages/feature-flags/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/react","versions":[{"version":"15.0.7","files":[{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]},{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]},{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/user-event","versions":[{"version":"14.6.1","files":[{"file":"/packages/form/package.json","types":["devDependencies"]}]}]},{"name":"@types/invariant","versions":[{"version":"2.2.37","files":[{"file":"/packages/form/package.json","types":["devDependencies"]}]}]},{"name":"ci-info","versions":[{"version":"4.3.0","files":[{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"@fastify/compress","versions":[{"version":"7.0.3","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/cookie","versions":[{"version":"9.4.0","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"fastify","versions":[{"version":"4.29.1","files":[{"file":"/packages/handler/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@fastify/aws-lambda","versions":[{"version":"4.1.0","files":[{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/resolvers-composition","versions":[{"version":"7.0.25","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/utils","versions":[{"version":"10.11.0","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"graphql-scalars","versions":[{"version":"1.25.0","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"accounting","versions":[{"version":"0.4.1","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"fecha","versions":[{"version":"2.3.3","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"short-hash","versions":[{"version":"1.0.0","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"@types/accounting","versions":[{"version":"0.4.5","files":[{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"@types/glob","versions":[{"version":"7.2.0","files":[{"file":"/packages/i18n/package.json","types":["devDependencies"]}]}]},{"name":"inversify","versions":[{"version":"6.2.2","files":[{"file":"/packages/ioc/package.json","types":["dependencies"]}]}]},{"name":"reflect-metadata","versions":[{"version":"0.2.2","files":[{"file":"/packages/ioc/package.json","types":["dependencies"]}]}]},{"name":"@types/jsdom","versions":[{"version":"21.1.7","files":[{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/prismjs","versions":[{"version":"1.26.5","files":[{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"react-style-object-to-css","versions":[{"version":"1.1.2","files":[{"file":"/packages/lexical-theme/package.json","types":["dependencies"]}]}]},{"name":"debounce","versions":[{"version":"1.2.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"dotenv","versions":[{"version":"8.6.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"exit-hook","versions":[{"version":"4.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"graphql-request","versions":[{"version":"7.3.5","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"mqtt","versions":[{"version":"5.14.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"neverthrow","versions":[{"version":"8.2.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"react-test-renderer","versions":[{"version":"18.3.1","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"replace-in-path","versions":[{"version":"1.1.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"serialize-error","versions":[{"version":"12.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"@types/debounce","versions":[{"version":"1.2.4","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/humanize-duration","versions":[{"version":"3.27.4","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-test-renderer","versions":[{"version":"18.3.1","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@types/read-json-sync","versions":[{"version":"2.0.3","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@pulumi/aws","versions":[{"version":"7.12.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/pulumi","versions":[{"version":"3.210.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/random","versions":[{"version":"4.18.4","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"core-js","versions":[{"version":"3.45.1","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"cross-fetch","versions":[{"version":"3.2.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"regenerator-runtime","versions":[{"version":"0.14.1","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"@types/ncp","versions":[{"version":"2.0.8","files":[{"file":"/packages/project-aws/package.json","types":["devDependencies"]}]}]},{"name":"listr2","versions":[{"version":"5.0.8","files":[{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"decompress","versions":[{"version":"4.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"tar","versions":[{"version":"6.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"identity-obj-proxy","versions":[{"version":"3.0.0","files":[{"file":"/packages/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"cli-table3","versions":[{"version":"0.6.5","files":[{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"object-merge-advanced","versions":[{"version":"12.1.0","files":[{"file":"/packages/tasks/package.json","types":["dependencies"]}]}]},{"name":"object-sizeof","versions":[{"version":"2.6.5","files":[{"file":"/packages/tasks/package.json","types":["dependencies"]}]}]},{"name":"jsesc","versions":[{"version":"3.1.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"strip-ansi","versions":[{"version":"6.0.1","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"wts-client","versions":[{"version":"2.0.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"ncp","versions":[{"version":"2.0.0","files":[{"file":"/packages/ui/package.json","types":["devDependencies"]}]}]},{"name":"@noble/hashes","versions":[{"version":"2.0.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"bson-objectid","versions":[{"version":"2.0.4","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"nanoid-dictionary","versions":[{"version":"4.3.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"isnumeric","versions":[{"version":"0.3.3","files":[{"file":"/packages/validation/package.json","types":["dependencies"]}]}]},{"name":"postcss","versions":[{"version":"8.5.6","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]}]}]},{"name":"postcss-import","versions":[{"version":"16.1.1","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]}]}]},{"name":"@types/postcss-import","versions":[{"version":"14.0.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"next","versions":[{"version":"15.5.7","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"webpack","versions":[{"version":"5.101.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"csstype","versions":[{"version":"3.1.3","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"fast-json-patch","versions":[{"version":"3.1.1","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"fast-json-stable-stringify","versions":[{"version":"2.1.0","files":[{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"@4tw/cypress-drag-drop","versions":[{"version":"1.8.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/cypress","versions":[{"version":"10.1.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"amazon-cognito-identity-js","versions":[{"version":"4.6.3","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress","versions":[{"version":"13.17.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-image-snapshot","versions":[{"version":"4.0.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-mailosaur","versions":[{"version":"2.17.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-wait-until","versions":[{"version":"1.7.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"del","versions":[{"version":"6.1.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@types/folder-hash","versions":[{"version":"4.0.4","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]}]}]},{"name":"@types/yargs","versions":[{"version":"17.0.33","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]}]}]},{"name":"cli-progress","versions":[{"version":"3.12.0","files":[{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"p-limit","versions":[{"version":"7.1.1","files":[{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"@types/cli-progress","versions":[{"version":"3.11.6","files":[{"file":"/scripts/cjsToEsm/package.json","types":["devDependencies"]}]}]}]} diff --git a/packages/feature/package.json b/packages/feature/package.json index ae8b7ec7777..1b6e99c00fe 100644 --- a/packages/feature/package.json +++ b/packages/feature/package.json @@ -14,7 +14,7 @@ "./admin": "./admin/index.js" }, "dependencies": { - "@webiny/di": "^0.2.1" + "@webiny/di": "^0.2.3" }, "devDependencies": { "@webiny/build-tools": "0.0.0", diff --git a/packages/feature/src/admin/createFeature.ts b/packages/feature/src/admin/createFeature.ts index 8273e7512a6..3a510425056 100644 --- a/packages/feature/src/admin/createFeature.ts +++ b/packages/feature/src/admin/createFeature.ts @@ -12,13 +12,13 @@ export function createFeature< >(def: { name: string; register: (container: Container, ...args: TParams) => void; - resolve: (container: Container) => TExports; + resolve?: (container: Container) => TExports; }): FeatureDefinition { return { name: def.name, register: def.register, resolve: (container: Container): TExports => { - return def.resolve(container); + return (def.resolve ? def.resolve(container) : undefined) as TExports; } }; } diff --git a/packages/project/package.json b/packages/project/package.json index 96086e297d2..cc87b64334d 100644 --- a/packages/project/package.json +++ b/packages/project/package.json @@ -12,7 +12,7 @@ "dependencies": { "@webiny/aws-sdk": "0.0.0", "@webiny/build-tools": "0.0.0", - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "@webiny/global-config": "0.0.0", "@webiny/pulumi-sdk": "0.0.0", "@webiny/react-properties": "0.0.0", diff --git a/packages/project/src/defineExtension/createExtensionDefinition.ts b/packages/project/src/defineExtension/createExtensionDefinition.ts index 51d08976e0c..ff5fb42e1eb 100644 --- a/packages/project/src/defineExtension/createExtensionDefinition.ts +++ b/packages/project/src/defineExtension/createExtensionDefinition.ts @@ -1,6 +1,6 @@ import { ExtensionDefinitionModel } from "./models/index.js"; -import { type DefineExtensionParams } from "./types.js"; -import { type z } from "zod"; +import type { DefineExtensionParams } from "./types.js"; +import type { z } from "zod"; export function createExtensionDefinition( extensionParams: DefineExtensionParams diff --git a/packages/react-properties/src/createConfigurableComponent.tsx b/packages/react-properties/src/createConfigurableComponent.tsx index de62093bcd5..cb04ecbbb82 100644 --- a/packages/react-properties/src/createConfigurableComponent.tsx +++ b/packages/react-properties/src/createConfigurableComponent.tsx @@ -12,8 +12,8 @@ const createHOC = return function ConfigHOC({ children }) { return ( - {newChildren} {children} + {newChildren} ); }; diff --git a/packages/testing/src/context/helpers.ts b/packages/testing/src/context/helpers.ts index 39f5d23e9ab..a8b0fb7d8f7 100644 --- a/packages/testing/src/context/helpers.ts +++ b/packages/testing/src/context/helpers.ts @@ -26,6 +26,9 @@ export const createPermissions = (permissions?: PermissionsArg[]): PermissionsAr return permissions; } return [ + { + name: "*" + }, { name: "cms.settings" }, diff --git a/packages/wcp/src/License.ts b/packages/wcp/src/License.ts index 2cc5ef526de..e0d0e1645e6 100644 --- a/packages/wcp/src/License.ts +++ b/packages/wcp/src/License.ts @@ -1,8 +1,8 @@ -import { getWcpProjectEnvironment } from "~/getWcpProjectEnvironment.js"; -import type { DecryptedWcpProjectLicense, ILicense, WcpProject } from "~/types.js"; -import { getWcpProjectLicense } from "~/licenses.js"; -import { NullLicense } from "~/NullLicense.js"; -import type { WCP_FEATURE_LABEL } from "~/index.js"; +import { getWcpProjectEnvironment } from "./getWcpProjectEnvironment.js"; +import type { DecryptedWcpProjectLicense, ILicense, WcpProject } from "./types.js"; +import { getWcpProjectLicense } from "./licenses.js"; +import { NullLicense } from "./NullLicense.js"; +import type { WCP_FEATURE_LABEL } from "./index.js"; export class License implements ILicense { private readonly license: DecryptedWcpProjectLicense; diff --git a/packages/wcp/src/NullLicense.ts b/packages/wcp/src/NullLicense.ts index f4b5dd959b6..ebf7e09a870 100644 --- a/packages/wcp/src/NullLicense.ts +++ b/packages/wcp/src/NullLicense.ts @@ -1,5 +1,5 @@ -import type { WCP_FEATURE_LABEL } from "~/index.js"; -import type { ILicense, WcpProject } from "~/types.js"; +import type { WCP_FEATURE_LABEL } from "./index.js"; +import type { ILicense, WcpProject } from "./types.js"; export class NullLicense implements ILicense { getRawLicense() { diff --git a/packages/wcp/src/getWcpProjectEnvironment.ts b/packages/wcp/src/getWcpProjectEnvironment.ts index 7e77a3e6f48..6d5e48091fb 100644 --- a/packages/wcp/src/getWcpProjectEnvironment.ts +++ b/packages/wcp/src/getWcpProjectEnvironment.ts @@ -1,5 +1,5 @@ -import type { WcpProjectEnvironment } from "~/types.js"; -import { decrypt } from "~/encryption.js"; +import type { WcpProjectEnvironment } from "./types.js"; +import { decrypt } from "./encryption.js"; export function getWcpProjectEnvironment(): WcpProjectEnvironment | null { if (process.env.WCP_PROJECT_ENVIRONMENT) { diff --git a/packages/wcp/src/testing/createTestWcpLicense.ts b/packages/wcp/src/testing/createTestWcpLicense.ts index 5605b5c26ab..0f3e2ca41b9 100644 --- a/packages/wcp/src/testing/createTestWcpLicense.ts +++ b/packages/wcp/src/testing/createTestWcpLicense.ts @@ -1,7 +1,12 @@ import type { DecryptedWcpProjectLicense } from "~/types.js"; import { MT_OPTIONS_MAX_COUNT_TYPE, PROJECT_PACKAGE_FEATURE_NAME } from "~/types.js"; -export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { +interface LicenseOptions { + recordLocking?: boolean; + folderLevelPermissions?: boolean; +} + +export const createTestWcpLicense = (options?: LicenseOptions): DecryptedWcpProjectLicense => { return { orgId: "org-id", projectId: "project-id", @@ -11,7 +16,7 @@ export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { enabled: true, options: { teams: true, - folderLevelPermissions: true, + folderLevelPermissions: options?.folderLevelPermissions ?? true, privateFiles: true } }, @@ -30,7 +35,7 @@ export const createTestWcpLicense = (): DecryptedWcpProjectLicense => { enabled: false }, [PROJECT_PACKAGE_FEATURE_NAME.RECORD_LOCKING]: { - enabled: false + enabled: options?.recordLocking ?? false }, [PROJECT_PACKAGE_FEATURE_NAME.SEATS]: { enabled: true, diff --git a/packages/wcp/src/types.ts b/packages/wcp/src/types.ts index 1fdc8dfbcc5..091d9c48cf1 100644 --- a/packages/wcp/src/types.ts +++ b/packages/wcp/src/types.ts @@ -1,4 +1,4 @@ -import type { WCP_FEATURE_LABEL } from "~/index.js"; +import type { WCP_FEATURE_LABEL } from "./index.js"; export interface WcpProject { orgId: string; diff --git a/scripts/cli/package.json b/scripts/cli/package.json index 455efe0e0b1..fd45518cf3b 100644 --- a/scripts/cli/package.json +++ b/scripts/cli/package.json @@ -11,7 +11,7 @@ "description": "An iternal command line interface (CLI), used for development of Webiny itself.", "license": "MIT", "dependencies": { - "@webiny/di": "^0.2.1", + "@webiny/di": "^0.2.3", "chalk": "^4.1.2", "find-up": "^5.0.0", "pino": "^9.13.1", diff --git a/testing/createTestConfig.ts b/testing/createTestConfig.ts index e172feba2b1..eb866f9722f 100644 --- a/testing/createTestConfig.ts +++ b/testing/createTestConfig.ts @@ -2,6 +2,7 @@ import { basename, join } from "path"; import fs from "fs"; import type { ViteUserConfig } from "vitest/config"; import { testPattern } from "./vitest.project.js"; +import tsconfigPaths from "vite-tsconfig-paths"; type TestPreset = { setupFiles?: string[]; @@ -52,13 +53,15 @@ export const createTestConfig = async ({ process.env.JEST_DYNALITE_CONFIG_DIRECTORY = path; - const project: ViteUserConfig["test"] = { + const project: NonNullable = { name: name, include: [`${path}${testPattern}`], dir: path, fileParallelism: false, ...vitestConfig, - css: false + css: false, + // @ts-expect-error This _does_ actually work! + plugins: [tsconfigPaths({ root: path })] }; const setupAfterEnv = join(path, "__tests__", "setup", "setupAfterEnv.js"); diff --git a/yarn.lock b/yarn.lock index 8d1c540ab6e..ed82ea58654 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13817,7 +13817,7 @@ __metadata: "@webiny/aws-sdk": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" "@webiny/db-dynamodb": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/error": "npm:0.0.0" "@webiny/feature": "npm:0.0.0" "@webiny/handler": "npm:0.0.0" @@ -13938,7 +13938,7 @@ __metadata: "@webiny/api": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" "@webiny/db-dynamodb": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/error": "npm:0.0.0" "@webiny/feature": "npm:0.0.0" "@webiny/handler": "npm:0.0.0" @@ -14391,7 +14391,7 @@ __metadata: "@webiny/aws-sdk": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" "@webiny/db-dynamodb": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/error": "npm:0.0.0" "@webiny/feature": "npm:0.0.0" "@webiny/handler": "npm:0.0.0" @@ -14475,7 +14475,7 @@ __metadata: graphql: "npm:^16.12.0" jest-dynalite: "npm:^3.6.1" lodash: "npm:^4.17.21" - nodemailer: "npm:^7.0.10" + nodemailer: "npm:^7.0.11" rimraf: "npm:^6.0.1" typescript: "npm:5.9.3" vitest: "npm:^3.2.4" @@ -14696,7 +14696,7 @@ __metadata: dependencies: "@webiny/aws-sdk": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/plugins": "npm:0.0.0" "@webiny/project-utils": "npm:0.0.0" "@webiny/utils": "npm:0.0.0" @@ -14719,6 +14719,8 @@ __metadata: "@webiny/app-security": "npm:0.0.0" "@webiny/app-utils": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" + "@webiny/di": "npm:^0.2.3" + "@webiny/feature": "npm:0.0.0" "@webiny/form": "npm:0.0.0" "@webiny/icons": "npm:0.0.0" "@webiny/plugins": "npm:0.0.0" @@ -14726,6 +14728,7 @@ __metadata: "@webiny/shared-aco": "npm:0.0.0" "@webiny/utils": "npm:0.0.0" "@webiny/validation": "npm:0.0.0" + "@webiny/wcp": "npm:0.0.0" apollo-client: "npm:^2.6.10" apollo-link: "npm:^1.2.14" dot-prop-immutable: "npm:^2.1.1" @@ -14734,7 +14737,6 @@ __metadata: lodash: "npm:^4.17.21" mobx: "npm:^6.15.0" mobx-react-lite: "npm:^3.4.3" - pako: "npm:^2.1.0" react: "npm:18.2.0" react-dom: "npm:18.2.0" react-hotkeyz: "npm:^1.0.4" @@ -14742,6 +14744,7 @@ __metadata: slugify: "npm:^1.6.6" store: "npm:^2.0.12" typescript: "npm:5.9.3" + vite-tsconfig-paths: "npm:^5.1.4" vitest: "npm:^3.2.4" zod: "npm:^3.25.76" languageName: unknown @@ -14912,7 +14915,7 @@ __metadata: "@webiny/app": "npm:0.0.0" "@webiny/app-security": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/feature": "npm:0.0.0" "@webiny/form": "npm:0.0.0" "@webiny/icons": "npm:0.0.0" @@ -15397,6 +15400,7 @@ __metadata: "@webiny/app-security-access-management": "npm:0.0.0" "@webiny/app-trash-bin": "npm:0.0.0" "@webiny/app-website-builder": "npm:0.0.0" + "@webiny/app-website-builder-workflows": "npm:0.0.0" "@webiny/app-websockets": "npm:0.0.0" "@webiny/app-workflows": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" @@ -15448,12 +15452,38 @@ __metadata: resolution: "@webiny/app-utils@workspace:packages/app-utils" dependencies: "@webiny/build-tools": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" "@webiny/utils": "npm:0.0.0" mobx: "npm:^6.15.0" typescript: "npm:5.9.3" languageName: unknown linkType: soft +"@webiny/app-website-builder-workflows@npm:0.0.0, @webiny/app-website-builder-workflows@workspace:packages/app-website-builder-workflows": + version: 0.0.0-use.local + resolution: "@webiny/app-website-builder-workflows@workspace:packages/app-website-builder-workflows" + dependencies: + "@apollo/react-hooks": "npm:^3.1.5" + "@types/react": "npm:18.2.79" + "@webiny/admin-ui": "npm:0.0.0" + "@webiny/app": "npm:0.0.0" + "@webiny/app-admin": "npm:0.0.0" + "@webiny/app-security": "npm:0.0.0" + "@webiny/app-website-builder": "npm:0.0.0" + "@webiny/app-workflows": "npm:0.0.0" + "@webiny/build-tools": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" + "@webiny/icons": "npm:0.0.0" + mobx: "npm:^6.15.0" + mobx-react-lite: "npm:^3.4.3" + react: "npm:18.2.0" + react-dom: "npm:18.2.0" + react-helmet: "npm:^6.1.0" + rimraf: "npm:^6.0.1" + typescript: "npm:5.9.3" + languageName: unknown + linkType: soft + "@webiny/app-website-builder@npm:0.0.0, @webiny/app-website-builder@workspace:packages/app-website-builder": version: 0.0.0-use.local resolution: "@webiny/app-website-builder@workspace:packages/app-website-builder" @@ -15485,6 +15515,7 @@ __metadata: "@webiny/app-utils": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" "@webiny/error": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" "@webiny/form": "npm:0.0.0" "@webiny/icons": "npm:0.0.0" "@webiny/lexical-converter": "npm:0.0.0" @@ -15585,7 +15616,7 @@ __metadata: "@types/universal-router": "npm:^8.0.0" "@types/warning": "npm:^3.0.3" "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/i18n": "npm:0.0.0" "@webiny/i18n-react": "npm:0.0.0" "@webiny/plugins": "npm:0.0.0" @@ -15726,7 +15757,7 @@ __metadata: "@types/listr": "npm:^0.14.9" "@types/lodash": "npm:4.17.20" "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/project": "npm:0.0.0" "@webiny/pulumi-sdk": "npm:0.0.0" "@webiny/telemetry": "npm:0.0.0" @@ -15843,12 +15874,12 @@ __metadata: languageName: unknown linkType: soft -"@webiny/di@npm:^0.2.1": - version: 0.2.1 - resolution: "@webiny/di@npm:0.2.1" +"@webiny/di@npm:^0.2.3": + version: 0.2.3 + resolution: "@webiny/di@npm:0.2.3" dependencies: reflect-metadata: "npm:^0.2.2" - checksum: 10/0fe0435d8f626ee250e05e7304284ee9e304940bb1a734adda44eb58d22bec6799a450d2a225751296afe1485bfaba02733b1f2f33c85f7ffb33b87e20ad0717 + checksum: 10/44f1c573775b1aa4c77d4234e8678362898b070185b9c0d5ee172ff0fed237302015fe98db9eab309d1a2b448004c05d503eabf6a39f797b113f16906991ea42 languageName: node linkType: hard @@ -15878,7 +15909,7 @@ __metadata: resolution: "@webiny/feature@workspace:packages/feature" dependencies: "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" typescript: "npm:5.9.3" languageName: unknown linkType: soft @@ -16313,7 +16344,7 @@ __metadata: "@types/read-json-sync": "npm:^2.0.3" "@webiny/aws-sdk": "npm:0.0.0" "@webiny/build-tools": "npm:0.0.0" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" "@webiny/global-config": "npm:0.0.0" "@webiny/pulumi-sdk": "npm:0.0.0" "@webiny/react-properties": "npm:0.0.0" @@ -16452,7 +16483,7 @@ __metadata: version: 0.0.0-use.local resolution: "@webiny/scripts@workspace:scripts/cli" dependencies: - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" chalk: "npm:^4.1.2" find-up: "npm:^5.0.0" pino: "npm:^9.13.1" @@ -27861,10 +27892,10 @@ __metadata: languageName: node linkType: hard -"nodemailer@npm:^7.0.10": - version: 7.0.10 - resolution: "nodemailer@npm:7.0.10" - checksum: 10/b9b8794ffc6c0d84440a9dd422664908e9c2003a15cb0e6bdd240a3625121de3978323c2ba4af78080fd735ef514d6caed376bd5f5dd6c17cf0d2c399d0dc354 +"nodemailer@npm:^7.0.11": + version: 7.0.11 + resolution: "nodemailer@npm:7.0.11" + checksum: 10/2ad4dd56a4caf84a83aa6f4378ded26d5ef8a644ca3be09c3b4fb2255d861369e620f29be6c3c97148ac4a50aa5fdff6240b9d60805362bd99ca15f2ea62e8a2 languageName: node linkType: hard @@ -28859,13 +28890,6 @@ __metadata: languageName: node linkType: hard -"pako@npm:^2.1.0": - version: 2.1.0 - resolution: "pako@npm:2.1.0" - checksum: 10/38a04991d0ec4f4b92794a68b8c92bf7340692c5d980255c92148da96eb3e550df7a86a7128b5ac0c65ecddfe5ef3bbe9c6dab13e1bc315086e759b18f7c1401 - languageName: node - linkType: hard - "pako@npm:~0.2.0": version: 0.2.9 resolution: "pako@npm:0.2.9" @@ -31345,7 +31369,7 @@ __metadata: "@typescript-eslint/parser": "npm:8.48.0" "@vitest/coverage-v8": "npm:^3.2.4" "@vitest/eslint-plugin": "npm:^1.4.2" - "@webiny/di": "npm:^0.2.1" + "@webiny/di": "npm:^0.2.3" adio: "npm:^2.0.1" axios: "npm:^1.12.2" babel-loader: "npm:^10.0.0"