diff --git a/packages/api-collaboration/__tests__/Collaboration.test.ts b/packages/api-collaboration/__tests__/Collaboration.test.ts new file mode 100644 index 00000000000..cbde57b3161 --- /dev/null +++ b/packages/api-collaboration/__tests__/Collaboration.test.ts @@ -0,0 +1,135 @@ +import { describe, expect, it } from "vitest"; +import { createGraphQLHandler, TEST_CONTENT_TYPE } from "~tests/__helpers/handler.js"; + +const CONTENT_ID = "articleModel:entry-123"; + +describe("collaboration threads (graphql)", () => { + const handler = createGraphQLHandler(); + + it("runs the full thread lifecycle: create, list, get, reply, resolve, reopen, edit, delete", async () => { + // 1. Create a thread with its first message. + const [createResponse] = await handler.createCollabThread({ + input: { + contentType: TEST_CONTENT_TYPE, + contentId: CONTENT_ID, + locator: "title", + type: "note", + body: "Should this be the SEO title?", + mentions: ["user-marko"] + } + }); + + const created = createResponse.data.collaboration.createCollabThread; + expect(created.error).toBeNull(); + expect(created.data).toMatchObject({ + contentType: TEST_CONTENT_TYPE, + contentId: CONTENT_ID, + locator: "title", + type: "note", + resolved: false, + anchor: { exists: true, authorized: true, label: "Test Field", path: ["Tab 1"] } + }); + expect(created.data.messages).toHaveLength(1); + expect(created.data.messages[0]).toMatchObject({ + body: "Should this be the SEO title?", + mentions: ["user-marko"] + }); + expect(created.data.createdBy.id).toBeTruthy(); + + const threadId = created.data.id; + const firstMessageId = created.data.messages[0].id; + + // 2. List threads for the content target. + const [listResponse] = await handler.listCollabThreads({ + where: { contentType: TEST_CONTENT_TYPE, contentId: CONTENT_ID } + }); + const list = listResponse.data.collaboration.listCollabThreads; + expect(list.error).toBeNull(); + expect(list.data).toHaveLength(1); + expect(list.data[0].id).toBe(threadId); + expect(list.meta.totalCount).toBe(1); + + // 3. Get the single thread. + const [getResponse] = await handler.getCollabThread({ id: threadId }); + expect(getResponse.data.collaboration.getCollabThread.data.id).toBe(threadId); + + // 4. Reply, then confirm two messages. + const [replyResponse] = await handler.replyToCollabThread({ + threadId, + body: "Display title. SEO title lives in Tab 2." + }); + expect(replyResponse.data.collaboration.replyToCollabThread.error).toBeNull(); + const replyId = replyResponse.data.collaboration.replyToCollabThread.data.id; + expect(replyId).toBeTruthy(); + + const [afterReply] = await handler.getCollabThread({ id: threadId }); + expect(afterReply.data.collaboration.getCollabThread.data.messages).toHaveLength(2); + + // 5. Resolve — records resolvedBy. + const [resolveResponse] = await handler.resolveCollabThread({ id: threadId }); + const resolved = resolveResponse.data.collaboration.resolveCollabThread.data; + expect(resolved.resolved).toBe(true); + expect(resolved.resolvedBy.id).toBeTruthy(); + expect(resolved.resolvedOn).toBeTruthy(); + + // 6. Reopen — clears resolution. + const [reopenResponse] = await handler.reopenCollabThread({ id: threadId }); + const reopened = reopenResponse.data.collaboration.reopenCollabThread.data; + expect(reopened.resolved).toBe(false); + expect(reopened.resolvedBy).toBeNull(); + + // 7. Edit the first message. + const [editResponse] = await handler.updateCollabMessage({ + threadId, + messageId: firstMessageId, + body: "Edited: which title convention are we using?" + }); + expect(editResponse.data.collaboration.updateCollabMessage.data.body).toBe( + "Edited: which title convention are we using?" + ); + + // 8. Soft-delete the reply. + const [deleteMessageResponse] = await handler.deleteCollabMessage({ + threadId, + messageId: replyId + }); + expect(deleteMessageResponse.data.collaboration.deleteCollabMessage.data).toBe(true); + + const [afterMessageDelete] = await handler.getCollabThread({ id: threadId }); + const deletedMessage = + afterMessageDelete.data.collaboration.getCollabThread.data.messages.find( + (message: { id: string }) => message.id === replyId + ); + expect(deletedMessage.deleted).toBe(true); + + // 9. Soft-delete the thread; it disappears from reads. + const [deleteThreadResponse] = await handler.deleteCollabThread({ id: threadId }); + expect(deleteThreadResponse.data.collaboration.deleteCollabThread.data).toBe(true); + + const [afterThreadDelete] = await handler.getCollabThread({ id: threadId }); + expect(afterThreadDelete.data.collaboration.getCollabThread.data).toBeNull(); + expect(afterThreadDelete.data.collaboration.getCollabThread.error).not.toBeNull(); + + const [listAfterDelete] = await handler.listCollabThreads({ + where: { contentType: TEST_CONTENT_TYPE, contentId: CONTENT_ID } + }); + expect(listAfterDelete.data.collaboration.listCollabThreads.data).toHaveLength(0); + }); + + it("denies creating a thread when no resolver owns the content type", async () => { + const [response] = await handler.createCollabThread({ + input: { + contentType: "unknown.type", + contentId: "x:y", + locator: "title", + type: "note", + body: "Nobody can resolve this anchor." + } + }); + + const result = response.data.collaboration.createCollabThread; + expect(result.data).toBeNull(); + expect(result.error).not.toBeNull(); + expect(result.error.code).toBe("Collaboration/Thread/NotAuthorized"); + }); +}); diff --git a/packages/api-collaboration/__tests__/__helpers/graphql.ts b/packages/api-collaboration/__tests__/__helpers/graphql.ts new file mode 100644 index 00000000000..55195712d80 --- /dev/null +++ b/packages/api-collaboration/__tests__/__helpers/graphql.ts @@ -0,0 +1,161 @@ +import type { GenericRecord } from "@webiny/api/types.js"; + +export interface ICollabError { + code: string; + message: string; + data?: GenericRecord; +} + +const ERROR = /* GraphQL */ ` + error { + code + message + } +`; + +const IDENTITY = /* GraphQL */ ` + { + id + displayName + type + } +`; + +const MESSAGE = /* GraphQL */ ` + { + id + body + mentions + createdBy ${IDENTITY} + createdOn + deleted + } +`; + +const THREAD = /* GraphQL */ ` + { + id + contentType + contentId + locator + type + resolved + resolvedBy ${IDENTITY} + resolvedOn + assigneeId + dueDate + createdBy ${IDENTITY} + createdOn + messages ${MESSAGE} + anchor { + exists + authorized + label + path + } + } +`; + +export const CREATE_COLLAB_THREAD_MUTATION = /* GraphQL */ ` + mutation CreateCollabThread($input: CreateCollabThreadInput!) { + collaboration { + createCollabThread(input: $input) { + data ${THREAD} + ${ERROR} + } + } + } +`; + +export const LIST_COLLAB_THREADS_QUERY = /* GraphQL */ ` + query ListCollabThreads($where: ListCollabThreadsWhereInput!, $limit: Int, $after: String) { + collaboration { + listCollabThreads(where: $where, limit: $limit, after: $after) { + data ${THREAD} + meta { + totalCount + hasMoreItems + cursor + } + ${ERROR} + } + } + } +`; + +export const GET_COLLAB_THREAD_QUERY = /* GraphQL */ ` + query GetCollabThread($id: ID!) { + collaboration { + getCollabThread(id: $id) { + data ${THREAD} + ${ERROR} + } + } + } +`; + +export const REPLY_TO_COLLAB_THREAD_MUTATION = /* GraphQL */ ` + mutation ReplyToCollabThread($threadId: ID!, $body: String!, $mentions: [String!]) { + collaboration { + replyToCollabThread(threadId: $threadId, body: $body, mentions: $mentions) { + data ${MESSAGE} + ${ERROR} + } + } + } +`; + +export const RESOLVE_COLLAB_THREAD_MUTATION = /* GraphQL */ ` + mutation ResolveCollabThread($id: ID!) { + collaboration { + resolveCollabThread(id: $id) { + data ${THREAD} + ${ERROR} + } + } + } +`; + +export const REOPEN_COLLAB_THREAD_MUTATION = /* GraphQL */ ` + mutation ReopenCollabThread($id: ID!) { + collaboration { + reopenCollabThread(id: $id) { + data ${THREAD} + ${ERROR} + } + } + } +`; + +export const UPDATE_COLLAB_MESSAGE_MUTATION = /* GraphQL */ ` + mutation UpdateCollabMessage($threadId: ID!, $messageId: ID!, $body: String!) { + collaboration { + updateCollabMessage(threadId: $threadId, messageId: $messageId, body: $body) { + data ${MESSAGE} + ${ERROR} + } + } + } +`; + +export const DELETE_COLLAB_MESSAGE_MUTATION = /* GraphQL */ ` + mutation DeleteCollabMessage($threadId: ID!, $messageId: ID!) { + collaboration { + deleteCollabMessage(threadId: $threadId, messageId: $messageId) { + data + ${ERROR} + } + } + } +`; + +export const DELETE_COLLAB_THREAD_MUTATION = /* GraphQL */ ` + mutation DeleteCollabThread($id: ID!) { + collaboration { + deleteCollabThread(id: $id) { + data + ${ERROR} + } + } + } +`; diff --git a/packages/api-collaboration/__tests__/__helpers/handler.ts b/packages/api-collaboration/__tests__/__helpers/handler.ts new file mode 100644 index 00000000000..f8dad135231 --- /dev/null +++ b/packages/api-collaboration/__tests__/__helpers/handler.ts @@ -0,0 +1,72 @@ +import { useGraphQLHandler, type UseGraphQLHandlerParams } from "@webiny/testing"; +import { PluginsContainer } from "@webiny/plugins"; +import { ContextPlugin } from "@webiny/api"; +import { createCollaboration } from "~/index.js"; +import { CollabLocatorResolver } from "~/domain/locator/abstractions.js"; +import { + CREATE_COLLAB_THREAD_MUTATION, + DELETE_COLLAB_MESSAGE_MUTATION, + DELETE_COLLAB_THREAD_MUTATION, + GET_COLLAB_THREAD_QUERY, + LIST_COLLAB_THREADS_QUERY, + REOPEN_COLLAB_THREAD_MUTATION, + REPLY_TO_COLLAB_THREAD_MUTATION, + RESOLVE_COLLAB_THREAD_MUTATION, + UPDATE_COLLAB_MESSAGE_MUTATION +} from "./graphql.js"; + +/** + * A content type used only by tests. It stands in for a real content app (CMS/WB) so the + * collaboration core can be exercised without seeding a full CMS model + entry. + */ +export const TEST_CONTENT_TYPE = "test.entry"; + +/** + * Stub resolver: any anchor exists and is readable. The `label`/`path` mirror the shape a real + * resolver returns so assertions cover the anchor projection too. + */ +class TestLocatorResolverImpl implements CollabLocatorResolver.Interface { + public readonly contentType = TEST_CONTENT_TYPE; + + async resolve(): Promise { + return { exists: true, authorized: true, label: "Test Field", path: ["Tab 1"] }; + } +} + +const TestLocatorResolver = CollabLocatorResolver.createImplementation({ + implementation: TestLocatorResolverImpl, + dependencies: [] +}); + +export const createGraphQLHandler = (params: UseGraphQLHandlerParams = {}) => { + const plugins = new PluginsContainer(params.plugins || []); + + plugins.register(createCollaboration()); + + // Register the test stub resolver into the same container. + plugins.register( + new ContextPlugin(async context => { + context.container.register(TestLocatorResolver); + }) + ); + + const handler = useGraphQLHandler({ + ...params, + permissions: [{ name: "*" }], + debug: params.debug === undefined ? true : params.debug, + plugins: plugins.all() + }); + + return { + handler, + createCollabThread: handler.createMutation(CREATE_COLLAB_THREAD_MUTATION), + listCollabThreads: handler.createQuery(LIST_COLLAB_THREADS_QUERY), + getCollabThread: handler.createQuery(GET_COLLAB_THREAD_QUERY), + replyToCollabThread: handler.createMutation(REPLY_TO_COLLAB_THREAD_MUTATION), + resolveCollabThread: handler.createMutation(RESOLVE_COLLAB_THREAD_MUTATION), + reopenCollabThread: handler.createMutation(REOPEN_COLLAB_THREAD_MUTATION), + updateCollabMessage: handler.createMutation(UPDATE_COLLAB_MESSAGE_MUTATION), + deleteCollabMessage: handler.createMutation(DELETE_COLLAB_MESSAGE_MUTATION), + deleteCollabThread: handler.createMutation(DELETE_COLLAB_THREAD_MUTATION) + }; +}; diff --git a/packages/api-collaboration/__tests__/modelLocator.test.ts b/packages/api-collaboration/__tests__/modelLocator.test.ts new file mode 100644 index 00000000000..2b0b66d8600 --- /dev/null +++ b/packages/api-collaboration/__tests__/modelLocator.test.ts @@ -0,0 +1,104 @@ +import { describe, expect, it } from "vitest"; +import type { CmsModel } from "@webiny/api-headless-cms/types"; +import { walkModelLocator } from "~/features/cms/CmsLocatorResolver/modelLocator.js"; +import { formatCmsContentId, parseCmsContentId } from "~/utils/cmsContentId.js"; + +// A minimal model exercising top-level, nested object, list, and dynamic-zone fields. +const model = { + modelId: "article", + fields: [ + { fieldId: "title", label: "Title", type: "text" }, + { + fieldId: "author", + label: "Author", + type: "object", + settings: { + fields: [{ fieldId: "name", label: "Name", type: "text" }] + } + }, + { + fieldId: "gallery", + label: "Gallery", + type: "object", + settings: { + fields: [{ fieldId: "caption", label: "Caption", type: "text" }] + } + }, + { + fieldId: "content", + label: "Content", + type: "dynamicZone", + settings: { + templates: [ + { + id: "hero", + fields: [{ fieldId: "heading", label: "Heading", type: "text" }] + } + ] + } + } + ] +} as unknown as CmsModel; + +describe("walkModelLocator", () => { + it("resolves a top-level field with no breadcrumb", () => { + expect(walkModelLocator(model, "title")).toEqual({ + exists: true, + label: "Title", + path: undefined + }); + }); + + it("resolves a nested object field with a breadcrumb", () => { + expect(walkModelLocator(model, "author.name")).toEqual({ + exists: true, + label: "Name", + path: ["Author"] + }); + }); + + it("skips numeric list indices", () => { + expect(walkModelLocator(model, "gallery.2.caption")).toEqual({ + exists: true, + label: "Caption", + path: ["Gallery"] + }); + }); + + it("descends dynamic-zone templates", () => { + expect(walkModelLocator(model, "content.heading")).toEqual({ + exists: true, + label: "Heading", + path: ["Content"] + }); + }); + + it("reports a removed top-level field as non-existent", () => { + expect(walkModelLocator(model, "subtitle")).toEqual({ exists: false }); + }); + + it("reports a removed nested field as non-existent", () => { + expect(walkModelLocator(model, "author.unknown")).toEqual({ exists: false }); + }); +}); + +describe("cmsContentId", () => { + it("round-trips modelId and entryId", () => { + expect(parseCmsContentId(formatCmsContentId("article", "entry-1"))).toEqual({ + modelId: "article", + entryId: "entry-1" + }); + }); + + it("splits on the first separator only", () => { + expect(parseCmsContentId("article:a:b")).toEqual({ + modelId: "article", + entryId: "a:b" + }); + }); + + it("returns nulls when malformed", () => { + expect(parseCmsContentId("noseparator")).toEqual({ modelId: null, entryId: null }); + expect(parseCmsContentId("article:")).toEqual({ modelId: null, entryId: null }); + }); +}); diff --git a/packages/api-collaboration/ci.config.json b/packages/api-collaboration/ci.config.json new file mode 100644 index 00000000000..5a6d5348b40 --- /dev/null +++ b/packages/api-collaboration/ci.config.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../.github/workflows/ci.config.schema.json", + "vitest": { + "storageOps": ["ddb", "ddb-os,ddb"] + } +} diff --git a/packages/api-collaboration/package.json b/packages/api-collaboration/package.json new file mode 100644 index 00000000000..99aaf06b4be --- /dev/null +++ b/packages/api-collaboration/package.json @@ -0,0 +1,44 @@ +{ + "name": "@webiny/api-collaboration", + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./index.js", + "./*": "./*" + }, + "description": "Collaboration API (threaded comments and tasks on content)", + "keywords": [ + "api-collaboration:base" + ], + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git", + "directory": "packages/api-collaboration" + }, + "license": "MIT", + "dependencies": { + "@webiny/api-core": "0.0.0", + "@webiny/api-headless-cms": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/handler": "0.0.0", + "@webiny/handler-graphql": "0.0.0", + "@webiny/utils": "0.0.0", + "zod": "4.4.3" + }, + "devDependencies": { + "@webiny/api": "0.0.0", + "@webiny/build-tools": "0.0.0", + "@webiny/plugins": "0.0.0", + "@webiny/project-utils": "0.0.0", + "@webiny/testing": "0.0.0", + "type-fest": "^5.8.0", + "typescript": "7.0.2", + "vitest": "^4.1.10" + }, + "publishConfig": { + "access": "public" + }, + "webiny": { + "publishFrom": "dist" + } +} diff --git a/packages/api-collaboration/src/constants.ts b/packages/api-collaboration/src/constants.ts new file mode 100644 index 00000000000..4b9e7491c4a --- /dev/null +++ b/packages/api-collaboration/src/constants.ts @@ -0,0 +1,8 @@ +export const COLLAB_THREAD_MODEL_ID = "wbyCollabThread"; + +/** + * Known content types. The collaboration core is content-agnostic — each content app + * registers a locator resolver for its own content type. These constants exist only so + * the built-in integrations agree on a value. + */ +export const CONTENT_TYPE_CMS_ENTRY = "cms.entry"; diff --git a/packages/api-collaboration/src/domain/locator/abstractions.ts b/packages/api-collaboration/src/domain/locator/abstractions.ts new file mode 100644 index 00000000000..7506378b394 --- /dev/null +++ b/packages/api-collaboration/src/domain/locator/abstractions.ts @@ -0,0 +1,52 @@ +import { createAbstraction } from "@webiny/feature/api"; + +export interface ICollabLocatorResolveParams { + contentType: string; + contentId: string; + locator: string; +} + +export interface ICollabLocatorResolution { + /** + * Whether the anchor still exists in the current revision of the target content. + */ + exists: boolean; + /** + * Whether the current identity is allowed to read the target content. Thread access + * equals read access to the target, so resolvers double as the permission gate. + */ + authorized: boolean; + /** + * Human label for the anchor (field display name, block type/name). + */ + label?: string; + /** + * Ancestor labels for display as a breadcrumb (e.g. ["Tab 1", "Field 1"]). + */ + path?: string[]; + /** + * Human title of the target content itself (e.g. the CMS entry title). Used by consumers + * (like notifications) that want to reference the entry rather than the anchored field. + */ + contentTitle?: string; +} + +/** + * Registered (as a multi-injection) by each content app. The collaboration core treats the + * `locator` as an opaque string and delegates resolution + read-access checks to the resolver + * that owns a given `contentType`. + */ +export interface ICollabLocatorResolver { + // e.g. "cms.entry", "pb.page" + contentType: string; + resolve(params: ICollabLocatorResolveParams): Promise; +} + +export const CollabLocatorResolver = + createAbstraction("CollabLocatorResolver"); + +export namespace CollabLocatorResolver { + export type Interface = ICollabLocatorResolver; + export type Params = ICollabLocatorResolveParams; + export type Resolution = ICollabLocatorResolution; +} diff --git a/packages/api-collaboration/src/domain/locator/errors.ts b/packages/api-collaboration/src/domain/locator/errors.ts new file mode 100644 index 00000000000..3fdaaf0bb46 --- /dev/null +++ b/packages/api-collaboration/src/domain/locator/errors.ts @@ -0,0 +1,12 @@ +import { BaseError } from "@webiny/feature/api"; + +export class CollabResolverNotFoundError extends BaseError<{ contentType: string }> { + override readonly code = "Collaboration/Resolver/NotFound" as const; + + constructor(contentType: string) { + super({ + message: `No collaboration locator resolver is registered for content type "${contentType}".`, + data: { contentType } + }); + } +} diff --git a/packages/api-collaboration/src/domain/thread/CollabThreadMapper.ts b/packages/api-collaboration/src/domain/thread/CollabThreadMapper.ts new file mode 100644 index 00000000000..602be8cc3e5 --- /dev/null +++ b/packages/api-collaboration/src/domain/thread/CollabThreadMapper.ts @@ -0,0 +1,44 @@ +import { parseIdentifier } from "@webiny/utils/parseIdentifier.js"; +import type { CmsEntry } from "@webiny/api-headless-cms/types/index.js"; +import { + CollabThreadMapper as MapperAbstraction, + CollabThreadType, + type ICollabThread, + type ICollabThreadValues +} from "./abstractions.js"; + +class CollabThreadMapperImpl implements MapperAbstraction.Interface { + fromCmsEntry(entry: CmsEntry): ICollabThread { + const { id } = parseIdentifier(entry.id); + const values = entry.values; + + return { + id, + contentType: values.contentType, + contentId: values.contentId, + // CMS stores an empty text field as null; entry-level comments use an empty locator. + locator: values.locator ?? "", + type: values.type ?? CollabThreadType.note, + resolved: values.resolved ?? false, + resolvedBy: values.resolvedBy ?? null, + resolvedOn: values.resolvedOn ?? null, + assigneeId: values.assigneeId ?? null, + dueDate: values.dueDate ?? null, + messages: values.messages ?? [], + deleted: values.deleted ?? false, + deletedBy: values.deletedBy ?? null, + deletedOn: values.deletedOn ?? null, + createdBy: { + id: entry.createdBy.id, + displayName: entry.createdBy.displayName, + type: entry.createdBy.type + }, + createdOn: entry.createdOn + }; + } +} + +export const CollabThreadMapper = MapperAbstraction.createImplementation({ + implementation: CollabThreadMapperImpl, + dependencies: [] +}); diff --git a/packages/api-collaboration/src/domain/thread/abstractions.ts b/packages/api-collaboration/src/domain/thread/abstractions.ts new file mode 100644 index 00000000000..8cc6b561602 --- /dev/null +++ b/packages/api-collaboration/src/domain/thread/abstractions.ts @@ -0,0 +1,74 @@ +import { createAbstraction } from "@webiny/feature/api"; +import type { CmsEntry, CmsModel } from "@webiny/api-headless-cms/types"; + +export enum CollabThreadType { + note = "note", + task = "task" +} + +/** + * The canonical Webiny identity projection stored on records and messages. + */ +export interface ICollabIdentity { + id: string; + displayName: string; + type: string; +} + +export interface ICollabMessage { + id: string; + body: string; + mentions: string[]; + createdBy: ICollabIdentity; + createdOn: string; + deleted?: boolean; + deletedBy?: ICollabIdentity | null; + deletedOn?: string | null; +} + +/** + * The values persisted on the `wbyCollabThread` CMS entry. `createdBy`/`createdOn` for the + * thread itself are the CMS entry system fields and are NOT part of the stored values. + */ +export interface ICollabThreadValues { + contentType: string; + contentId: string; + locator: string; + type: CollabThreadType; + resolved: boolean; + resolvedBy?: ICollabIdentity | null; + resolvedOn?: string | null; + // task variant only + assigneeId?: string | null; + dueDate?: string | null; + messages: ICollabMessage[]; + // soft-delete of the whole thread + deleted?: boolean; + deletedBy?: ICollabIdentity | null; + deletedOn?: string | null; +} + +export interface ICollabThread extends ICollabThreadValues { + id: string; + createdBy: ICollabIdentity; + createdOn: string; +} + +/** + * The `wbyCollabThread` private CMS model, resolved to a `CmsModel` instance at boot. + */ +export const CollabThreadModel = createAbstraction("CollabThreadModel"); + +export namespace CollabThreadModel { + export type Interface = CmsModel; +} + +export interface ICollabThreadMapper { + fromCmsEntry(entry: CmsEntry): ICollabThread; +} + +export const CollabThreadMapper = createAbstraction("CollabThreadMapper"); + +export namespace CollabThreadMapper { + export type Interface = ICollabThreadMapper; +} diff --git a/packages/api-collaboration/src/domain/thread/errors.ts b/packages/api-collaboration/src/domain/thread/errors.ts new file mode 100644 index 00000000000..48540e9b7d0 --- /dev/null +++ b/packages/api-collaboration/src/domain/thread/errors.ts @@ -0,0 +1,68 @@ +import { BaseError } from "@webiny/feature/api"; + +export class CollabThreadNotFoundError extends BaseError<{ id: string }> { + override readonly code = "Collaboration/Thread/NotFound" as const; + + constructor(data: { id: string }) { + super({ + message: `Collaboration thread with id "${data.id}" was not found!`, + data + }); + } +} + +export class CollabMessageNotFoundError extends BaseError<{ threadId: string; messageId: string }> { + override readonly code = "Collaboration/Message/NotFound" as const; + + constructor(data: { threadId: string; messageId: string }) { + super({ + message: `Message "${data.messageId}" was not found in thread "${data.threadId}"!`, + data + }); + } +} + +export class CollabThreadNotAuthorizedError extends BaseError { + override readonly code = "Collaboration/Thread/NotAuthorized" as const; + + constructor(message?: string) { + super({ + message: message || "Not authorized to access this collaboration thread." + }); + } +} + +export class CollabThreadPersistenceError extends BaseError { + override readonly code = "Collaboration/Thread/Persistence" as const; + + constructor(error: Error) { + super({ + message: error.message + }); + } +} + +export class CollabThreadValidationError extends BaseError { + override readonly code = "Collaboration/Thread/Validation" as const; + + constructor(message: string) { + super({ + message + }); + } +} + +/** + * The anchor (content + locator) could not be resolved — either no resolver is registered + * for the content type, or the resolver reported the anchor does not exist. + */ +export class CollabAnchorNotFoundError extends BaseError<{ contentType: string; locator: string }> { + override readonly code = "Collaboration/Anchor/NotFound" as const; + + constructor(data: { contentType: string; locator: string }) { + super({ + message: `Could not resolve anchor "${data.locator}" for content type "${data.contentType}".`, + data + }); + } +} diff --git a/packages/api-collaboration/src/domain/thread/events.ts b/packages/api-collaboration/src/domain/thread/events.ts new file mode 100644 index 00000000000..6d98f8e1abd --- /dev/null +++ b/packages/api-collaboration/src/domain/thread/events.ts @@ -0,0 +1,59 @@ +import { createAbstraction } from "@webiny/feature/api"; +import { DomainEvent } from "@webiny/api-core/features/eventPublisher/index.js"; +import type { IEventHandler } from "@webiny/api-core/features/eventPublisher/index.js"; +import type { ICollabMessage, ICollabThread } from "./abstractions.js"; +import type { ICollabLocatorResolution } from "~/domain/locator/abstractions.js"; + +/** + * Emitted after a thread (and its first message) is created. Consumers (e.g. notifications) + * react to mentions on the first message. `anchor` carries the resolved label/breadcrumb for + * display. + */ +export interface CollabThreadCreatedPayload { + thread: ICollabThread; + message: ICollabMessage; + anchor: ICollabLocatorResolution; +} + +export class CollabThreadCreatedEvent extends DomainEvent { + eventType = "collaboration.threadCreated" as const; + + getHandlerAbstraction() { + return CollabThreadCreatedHandler; + } +} + +export const CollabThreadCreatedHandler = createAbstraction< + IEventHandler +>("CollabThreadCreatedHandler"); + +export namespace CollabThreadCreatedHandler { + export type Interface = IEventHandler; + export type Event = CollabThreadCreatedEvent; +} + +/** + * Emitted after a reply is added to a thread. Consumers react to thread participants and any + * mentions on the reply. + */ +export interface CollabReplyAddedPayload { + thread: ICollabThread; + message: ICollabMessage; + anchor: ICollabLocatorResolution; +} + +export class CollabReplyAddedEvent extends DomainEvent { + eventType = "collaboration.replyAdded" as const; + + getHandlerAbstraction() { + return CollabReplyAddedHandler; + } +} + +export const CollabReplyAddedHandler = + createAbstraction>("CollabReplyAddedHandler"); + +export namespace CollabReplyAddedHandler { + export type Interface = IEventHandler; + export type Event = CollabReplyAddedEvent; +} diff --git a/packages/api-collaboration/src/domain/thread/threadModel.ts b/packages/api-collaboration/src/domain/thread/threadModel.ts new file mode 100644 index 00000000000..d8a1006fa4d --- /dev/null +++ b/packages/api-collaboration/src/domain/thread/threadModel.ts @@ -0,0 +1,110 @@ +import { ModelFactory } from "@webiny/api-headless-cms/features/modelBuilder/index.js"; +import { CollabThreadType } from "~/domain/thread/abstractions.js"; +import { COLLAB_THREAD_MODEL_ID } from "~/constants.js"; + +const types = [ + { + label: "Note", + value: CollabThreadType.note + }, + { + label: "Task", + value: CollabThreadType.task + } +]; + +class CollabThreadModelImpl implements ModelFactory.Interface { + public async execute(builder: ModelFactory.Builder) { + return [ + builder + .private({ + modelId: COLLAB_THREAD_MODEL_ID, + name: "Collaboration Thread" + }) + .fields(fields => ({ + contentType: fields + .text() + .label("Content Type") + .required("Content type is required."), + contentId: fields + .text() + .label("Content ID") + .required("Content ID is required."), + // Empty locator = an entry-level (unanchored) comment. + locator: fields.text().label("Locator"), + type: fields + .text() + .label("Type") + .required("Type is required.") + .predefinedValues(types), + resolved: fields.boolean().label("Resolved"), + resolvedBy: fields + .object() + .label("Resolved By") + .fields(identityFields => ({ + id: identityFields.text().label("ID"), + displayName: identityFields.text().label("Display Name"), + type: identityFields.text().label("Type") + })), + resolvedOn: fields.text().label("Resolved On"), + assigneeId: fields.text().label("Assignee ID"), + dueDate: fields.text().label("Due Date"), + deleted: fields.boolean().label("Deleted"), + deletedBy: fields + .object() + .label("Deleted By") + .fields(identityFields => ({ + id: identityFields.text().label("ID"), + displayName: identityFields.text().label("Display Name"), + type: identityFields.text().label("Type") + })), + deletedOn: fields.text().label("Deleted On"), + messages: fields + .object() + .label("Messages") + .list() + .fields(messageFields => ({ + id: messageFields.text().label("ID").required("ID is required."), + body: messageFields.text().label("Body").required("Body is required."), + mentions: messageFields.text().label("Mentions").list(), + createdBy: messageFields + .object() + .label("Created By") + .fields(identityFields => ({ + id: identityFields + .text() + .label("ID") + .required("ID is required."), + displayName: identityFields + .text() + .label("Display Name") + .required("Display name is required."), + type: identityFields + .text() + .label("Type") + .required("Type is required.") + })), + createdOn: messageFields + .text() + .label("Created On") + .required("Created on is required."), + deleted: messageFields.boolean().label("Deleted"), + deletedBy: messageFields + .object() + .label("Deleted By") + .fields(identityFields => ({ + id: identityFields.text().label("ID"), + displayName: identityFields.text().label("Display Name"), + type: identityFields.text().label("Type") + })), + deletedOn: messageFields.text().label("Deleted On") + })) + })) + ]; + } +} + +export const CollabThreadModel = ModelFactory.createImplementation({ + implementation: CollabThreadModelImpl, + dependencies: [] +}); diff --git a/packages/api-collaboration/src/features/cms/CmsLocatorResolver/CmsLocatorResolver.ts b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/CmsLocatorResolver.ts new file mode 100644 index 00000000000..17cafa10eee --- /dev/null +++ b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/CmsLocatorResolver.ts @@ -0,0 +1,65 @@ +import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js"; +import { GetLatestRevisionByEntryIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetLatestRevisionByEntryId/index.js"; +import { getEntryTitle } from "@webiny/api-headless-cms/utils/getEntryTitle.js"; +import { CollabLocatorResolver } from "~/domain/locator/abstractions.js"; +import { CONTENT_TYPE_CMS_ENTRY } from "~/constants.js"; +import { parseCmsContentId } from "~/utils/cmsContentId.js"; +import { walkModelLocator } from "./modelLocator.js"; + +class CmsLocatorResolverImpl implements CollabLocatorResolver.Interface { + public readonly contentType = CONTENT_TYPE_CMS_ENTRY; + + constructor( + private getModel: GetModelUseCase.Interface, + private getLatestRevision: GetLatestRevisionByEntryIdUseCase.Interface + ) {} + + async resolve(params: CollabLocatorResolver.Params): Promise { + const { modelId, entryId } = parseCmsContentId(params.contentId); + if (!modelId || !entryId) { + return { exists: false, authorized: false }; + } + + const modelResult = await this.getModel.execute(modelId); + if (modelResult.isFail()) { + // Model missing or not readable — treat as no access. + return { exists: false, authorized: false }; + } + + const model = modelResult.value; + + // Loading the current revision is the read-access gate: it fails with + // "Cms/Entry/NotAuthorized" when the caller may not read the entry. + const entryResult = await this.getLatestRevision.execute(model, { id: entryId }); + if (entryResult.isFail()) { + if (entryResult.error.code === "Cms/Entry/NotAuthorized") { + return { exists: false, authorized: false }; + } + // Entry itself is gone (deleted / never existed) — authorized but orphaned. + return { exists: false, authorized: true }; + } + + const contentTitle = getEntryTitle(model, entryResult.value); + + // Empty locator = an entry-level (unanchored) comment. The entry read above is the + // access gate; the anchor is the entry itself. + if (!params.locator || params.locator.trim().length === 0) { + return { exists: true, authorized: true, label: "Entry", path: [], contentTitle }; + } + + const walk = walkModelLocator(model, params.locator); + + return { + exists: walk.exists, + authorized: true, + label: walk.label, + path: walk.path, + contentTitle + }; + } +} + +export const CmsLocatorResolver = CollabLocatorResolver.createImplementation({ + implementation: CmsLocatorResolverImpl, + dependencies: [GetModelUseCase, GetLatestRevisionByEntryIdUseCase] +}); diff --git a/packages/api-collaboration/src/features/cms/CmsLocatorResolver/feature.ts b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/feature.ts new file mode 100644 index 00000000000..30e9af193ab --- /dev/null +++ b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { CmsLocatorResolver } from "./CmsLocatorResolver.js"; + +export const CmsLocatorResolverFeature = createFeature({ + name: "Collaboration/CmsLocatorResolver", + register(container) { + container.register(CmsLocatorResolver).inSingletonScope(); + } +}); diff --git a/packages/api-collaboration/src/features/cms/CmsLocatorResolver/modelLocator.ts b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/modelLocator.ts new file mode 100644 index 00000000000..01a2be64503 --- /dev/null +++ b/packages/api-collaboration/src/features/cms/CmsLocatorResolver/modelLocator.ts @@ -0,0 +1,81 @@ +import type { CmsModel, CmsModelField } from "@webiny/api-headless-cms/types"; + +export interface ModelLocatorResult { + exists: boolean; + label?: string; + /** + * Ancestor field labels for a breadcrumb display (e.g. ["Address"] for an `address.street` + * locator). Reflects object / dynamic-zone nesting; excludes the target field itself. + */ + path?: string[]; +} + +const isListIndex = (segment: string): boolean => { + return /^\d+$/.test(segment); +}; + +/** + * Object fields expose children via `settings.fields`; dynamic-zone fields via + * `settings.templates[].fields`. We flatten both so a `fieldId` segment can be matched + * regardless of which template it belongs to. + */ +const childFieldsOf = (field: CmsModelField): CmsModelField[] => { + const children: CmsModelField[] = []; + const settings = field.settings; + if (settings?.fields) { + children.push(...settings.fields); + } + if (settings?.templates) { + for (const template of settings.templates) { + if (template.fields) { + children.push(...template.fields); + } + } + } + return children; +}; + +const findByFieldId = (fields: CmsModelField[], fieldId: string): CmsModelField | undefined => { + return fields.find(field => field.fieldId === fieldId); +}; + +/** + * Walks a `fieldId`-dotted locator (e.g. `author.address.2.street`) against a model definition. + * Numeric segments are list indices and are skipped for structural matching. Returns whether the + * field still exists in the model, its label, and the ancestor breadcrumb. + */ +export const walkModelLocator = (model: CmsModel, locator: string): ModelLocatorResult => { + const segments = locator + .split(".") + .filter(segment => segment.length > 0 && !isListIndex(segment)); + + if (segments.length === 0) { + return { exists: false }; + } + + let currentFields: CmsModelField[] = model.fields; + let field: CmsModelField | undefined; + const labels: string[] = []; + + for (const segment of segments) { + field = findByFieldId(currentFields, segment); + if (!field) { + return { exists: false }; + } + labels.push(field.label || field.fieldId); + currentFields = childFieldsOf(field); + } + + if (!field) { + return { exists: false }; + } + + const label = field.label || field.fieldId; + const ancestors = labels.slice(0, -1); + + return { + exists: true, + label, + path: ancestors.length > 0 ? ancestors : undefined + }; +}; diff --git a/packages/api-collaboration/src/features/locator/ResolveLocator/ResolveLocatorUseCase.ts b/packages/api-collaboration/src/features/locator/ResolveLocator/ResolveLocatorUseCase.ts new file mode 100644 index 00000000000..c29a78627e2 --- /dev/null +++ b/packages/api-collaboration/src/features/locator/ResolveLocator/ResolveLocatorUseCase.ts @@ -0,0 +1,27 @@ +import { Result } from "@webiny/feature/api"; +import { CollabLocatorResolver } from "~/domain/locator/abstractions.js"; +import { CollabResolverNotFoundError } from "~/domain/locator/errors.js"; +import { ResolveLocatorUseCase as UseCase } from "./abstractions.js"; + +class ResolveLocatorUseCaseImpl implements UseCase.Interface { + private readonly resolvers; + + constructor(resolvers: CollabLocatorResolver.Interface[]) { + this.resolvers = resolvers; + } + + async execute(params: UseCase.Params): UseCase.Return { + const resolver = this.resolvers.find(item => item.contentType === params.contentType); + if (!resolver) { + return Result.fail(new CollabResolverNotFoundError(params.contentType)); + } + + const resolution = await resolver.resolve(params); + return Result.ok(resolution); + } +} + +export const ResolveLocatorUseCase = UseCase.createImplementation({ + implementation: ResolveLocatorUseCaseImpl, + dependencies: [[CollabLocatorResolver, { multiple: true }]] +}); diff --git a/packages/api-collaboration/src/features/locator/ResolveLocator/abstractions.ts b/packages/api-collaboration/src/features/locator/ResolveLocator/abstractions.ts new file mode 100644 index 00000000000..ccf50c6b3fd --- /dev/null +++ b/packages/api-collaboration/src/features/locator/ResolveLocator/abstractions.ts @@ -0,0 +1,30 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { + ICollabLocatorResolution, + ICollabLocatorResolveParams +} from "~/domain/locator/abstractions.js"; +import type { CollabResolverNotFoundError } from "~/domain/locator/errors.js"; + +export interface IResolveLocatorUseCaseErrors { + resolverNotFound: CollabResolverNotFoundError; +} + +type UseCaseError = IResolveLocatorUseCaseErrors[keyof IResolveLocatorUseCaseErrors]; + +export interface IResolveLocatorUseCase { + execute( + params: ICollabLocatorResolveParams + ): Promise>; +} + +export const ResolveLocatorUseCase = + createAbstraction("ResolveLocatorUseCase"); + +export namespace ResolveLocatorUseCase { + export type Interface = IResolveLocatorUseCase; + export type Params = ICollabLocatorResolveParams; + export type Resolution = ICollabLocatorResolution; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/locator/ResolveLocator/feature.ts b/packages/api-collaboration/src/features/locator/ResolveLocator/feature.ts new file mode 100644 index 00000000000..6a69c3c52cd --- /dev/null +++ b/packages/api-collaboration/src/features/locator/ResolveLocator/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { ResolveLocatorUseCase } from "./ResolveLocatorUseCase.js"; + +export const ResolveLocatorFeature = createFeature({ + name: "Collaboration/ResolveLocator", + register(container) { + container.register(ResolveLocatorUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/locator/ResolveLocator/index.ts b/packages/api-collaboration/src/features/locator/ResolveLocator/index.ts new file mode 100644 index 00000000000..e85d2cd9e0d --- /dev/null +++ b/packages/api-collaboration/src/features/locator/ResolveLocator/index.ts @@ -0,0 +1 @@ +export { ResolveLocatorUseCase } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadRepository.ts b/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadRepository.ts new file mode 100644 index 00000000000..312ac0041eb --- /dev/null +++ b/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadRepository.ts @@ -0,0 +1,39 @@ +import { Result } from "@webiny/feature/api"; +import { CreateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/CreateEntry/index.js"; +import { + CollabThreadMapper, + CollabThreadModel, + type ICollabThreadValues +} from "~/domain/thread/abstractions.js"; +import { CollabThreadPersistenceError } from "~/domain/thread/errors.js"; +import { CreateThreadRepository as Repository } from "./abstractions.js"; + +class CreateThreadRepositoryImpl implements Repository.Interface { + constructor( + private createEntry: CreateEntryUseCase.Interface, + private model: CollabThreadModel.Interface, + private mapper: CollabThreadMapper.Interface + ) {} + + async execute(params: Repository.Params): Repository.Return { + try { + const createResult = await this.createEntry.execute(this.model, { + id: params.id, + values: params.values + }); + + if (createResult.isFail()) { + return Result.fail(new CollabThreadPersistenceError(createResult.error)); + } + + return Result.ok(this.mapper.fromCmsEntry(createResult.value)); + } catch (error) { + return Result.fail(new CollabThreadPersistenceError(error as Error)); + } + } +} + +export const CreateThreadRepository = Repository.createImplementation({ + implementation: CreateThreadRepositoryImpl, + dependencies: [CreateEntryUseCase, CollabThreadModel, CollabThreadMapper] +}); diff --git a/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadUseCase.ts b/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadUseCase.ts new file mode 100644 index 00000000000..94119fd8d1c --- /dev/null +++ b/packages/api-collaboration/src/features/thread/CreateThread/CreateThreadUseCase.ts @@ -0,0 +1,106 @@ +import { Result } from "@webiny/feature/api"; +import { mdbid } from "@webiny/utils"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { EventPublisher } from "@webiny/api-core/features/eventPublisher/index.js"; +import { CreateThreadRepository, CreateThreadUseCase as UseCase } from "./abstractions.js"; +import { ResolveLocatorUseCase } from "~/features/locator/ResolveLocator/index.js"; +import { type ICollabMessage, type ICollabThreadValues } from "~/domain/thread/abstractions.js"; +import { CollabThreadCreatedEvent } from "~/domain/thread/events.js"; +import { + CollabAnchorNotFoundError, + CollabThreadNotAuthorizedError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; +import { toCollabIdentity } from "~/utils/identity.js"; + +class CreateThreadUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private resolveLocator: ResolveLocatorUseCase.Interface, + private repository: CreateThreadRepository.Interface, + private eventPublisher: EventPublisher.Interface + ) {} + + async execute(input: UseCase.Input): UseCase.Return { + const identity = this.identityContext.getIdentity(); + if (identity.isAnonymous()) { + return Result.fail( + new CollabThreadNotAuthorizedError("You must be signed in to comment.") + ); + } + + if (!input.body || input.body.trim().length === 0) { + return Result.fail(new CollabThreadValidationError("Message body cannot be empty.")); + } + + const resolution = await this.resolveLocator.execute({ + contentType: input.contentType, + contentId: input.contentId, + locator: input.locator + }); + + if (resolution.isFail() || !resolution.value.authorized) { + return Result.fail( + new CollabThreadNotAuthorizedError( + "You do not have access to comment on this content." + ) + ); + } + + if (!resolution.value.exists) { + return Result.fail( + new CollabAnchorNotFoundError({ + contentType: input.contentType, + locator: input.locator + }) + ); + } + + const now = new Date().toISOString(); + const author = toCollabIdentity(identity); + + const message: ICollabMessage = { + id: mdbid(), + body: input.body, + mentions: input.mentions ?? [], + createdBy: author, + createdOn: now + }; + + const values: ICollabThreadValues = { + contentType: input.contentType, + contentId: input.contentId, + locator: input.locator, + type: input.type, + resolved: false, + resolvedBy: null, + resolvedOn: null, + assigneeId: input.assigneeId ?? null, + dueDate: input.dueDate ?? null, + messages: [message], + deleted: false, + deletedBy: null, + deletedOn: null + }; + + const createResult = await this.repository.execute({ id: mdbid(), values }); + if (createResult.isFail()) { + return Result.fail(createResult.error); + } + + await this.eventPublisher.publish( + new CollabThreadCreatedEvent({ + thread: createResult.value, + message, + anchor: resolution.value + }) + ); + + return Result.ok({ thread: createResult.value, anchor: resolution.value }); + } +} + +export const CreateThreadUseCase = UseCase.createImplementation({ + implementation: CreateThreadUseCaseImpl, + dependencies: [IdentityContext, ResolveLocatorUseCase, CreateThreadRepository, EventPublisher] +}); diff --git a/packages/api-collaboration/src/features/thread/CreateThread/abstractions.ts b/packages/api-collaboration/src/features/thread/CreateThread/abstractions.ts new file mode 100644 index 00000000000..a0b79809c86 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/CreateThread/abstractions.ts @@ -0,0 +1,80 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import { + type CollabThreadType, + type ICollabThread, + type ICollabThreadValues +} from "~/domain/thread/abstractions.js"; +import type { + CollabAnchorNotFoundError, + CollabThreadNotAuthorizedError, + CollabThreadPersistenceError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; + +export interface ICreateThreadInput { + contentType: string; + contentId: string; + locator: string; + type: CollabThreadType; + // first message + body: string; + mentions?: string[]; + // task variant only + assigneeId?: string | null; + dueDate?: string | null; +} + +/** + * CreateThread use case interface. + */ +export interface ICreateThreadUseCase { + execute(input: ICreateThreadInput): Promise>; +} + +export interface ICreateThreadUseCaseErrors { + notAuthorized: CollabThreadNotAuthorizedError; + anchorNotFound: CollabAnchorNotFoundError; + validation: CollabThreadValidationError; + persistence: CollabThreadPersistenceError; +} + +type UseCaseError = ICreateThreadUseCaseErrors[keyof ICreateThreadUseCaseErrors]; + +export const CreateThreadUseCase = createAbstraction("CreateThreadUseCase"); + +export namespace CreateThreadUseCase { + export type Interface = ICreateThreadUseCase; + export type Input = ICreateThreadInput; + export type Return = Promise>; + export type Error = UseCaseError; +} + +/** + * CreateThread repository interface. + */ +export interface ICreateThreadRepositoryParams { + id: string; + values: ICollabThreadValues; +} + +export interface ICreateThreadRepository { + execute(params: ICreateThreadRepositoryParams): Promise>; +} + +export interface ICreateThreadRepositoryErrors { + persistence: CollabThreadPersistenceError; +} + +type RepositoryError = ICreateThreadRepositoryErrors[keyof ICreateThreadRepositoryErrors]; + +export const CreateThreadRepository = + createAbstraction("CreateThreadRepository"); + +export namespace CreateThreadRepository { + export type Interface = ICreateThreadRepository; + export type Params = ICreateThreadRepositoryParams; + export type Return = Promise>; + export type Error = RepositoryError; +} diff --git a/packages/api-collaboration/src/features/thread/CreateThread/feature.ts b/packages/api-collaboration/src/features/thread/CreateThread/feature.ts new file mode 100644 index 00000000000..58993b5a654 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/CreateThread/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { CreateThreadUseCase } from "./CreateThreadUseCase.js"; +import { CreateThreadRepository } from "./CreateThreadRepository.js"; + +export const CreateThreadFeature = createFeature({ + name: "Collaboration/CreateThread", + register(container) { + container.register(CreateThreadRepository).inSingletonScope(); + container.register(CreateThreadUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/CreateThread/index.ts b/packages/api-collaboration/src/features/thread/CreateThread/index.ts new file mode 100644 index 00000000000..61d0fc19043 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/CreateThread/index.ts @@ -0,0 +1 @@ +export { CreateThreadUseCase, CreateThreadRepository } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/DeleteThread/DeleteThreadUseCase.ts b/packages/api-collaboration/src/features/thread/DeleteThread/DeleteThreadUseCase.ts new file mode 100644 index 00000000000..c4c5a9112d0 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/DeleteThread/DeleteThreadUseCase.ts @@ -0,0 +1,47 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { DeleteThreadUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; +import { CollabThreadNotAuthorizedError } from "~/domain/thread/errors.js"; +import { toCollabIdentity } from "~/utils/identity.js"; + +class DeleteThreadUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface + ) {} + + async execute(id: string): UseCase.Return { + const loaded = await this.getThread.execute(id); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread } = loaded.value; + + const identity = this.identityContext.getIdentity(); + if (!identity.isAdmin() && identity.id !== thread.createdBy.id) { + return Result.fail( + new CollabThreadNotAuthorizedError("You can only delete your own threads.") + ); + } + + thread.deleted = true; + thread.deletedBy = toCollabIdentity(identity); + thread.deletedOn = new Date().toISOString(); + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + return Result.ok(true); + } +} + +export const DeleteThreadUseCase = UseCase.createImplementation({ + implementation: DeleteThreadUseCaseImpl, + dependencies: [IdentityContext, GetThreadUseCase, UpdateThreadRepository] +}); diff --git a/packages/api-collaboration/src/features/thread/DeleteThread/abstractions.ts b/packages/api-collaboration/src/features/thread/DeleteThread/abstractions.ts new file mode 100644 index 00000000000..8f7555e82ae --- /dev/null +++ b/packages/api-collaboration/src/features/thread/DeleteThread/abstractions.ts @@ -0,0 +1,27 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError, + CollabThreadPersistenceError +} from "~/domain/thread/errors.js"; + +export interface IDeleteThreadUseCase { + execute(id: string): Promise>; +} + +export interface IDeleteThreadUseCaseErrors { + notFound: CollabThreadNotFoundError; + notAuthorized: CollabThreadNotAuthorizedError; + persistence: CollabThreadPersistenceError; +} + +type UseCaseError = IDeleteThreadUseCaseErrors[keyof IDeleteThreadUseCaseErrors]; + +export const DeleteThreadUseCase = createAbstraction("DeleteThreadUseCase"); + +export namespace DeleteThreadUseCase { + export type Interface = IDeleteThreadUseCase; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/thread/DeleteThread/feature.ts b/packages/api-collaboration/src/features/thread/DeleteThread/feature.ts new file mode 100644 index 00000000000..32839f014a9 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/DeleteThread/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { DeleteThreadUseCase } from "./DeleteThreadUseCase.js"; + +export const DeleteThreadFeature = createFeature({ + name: "Collaboration/DeleteThread", + register(container) { + container.register(DeleteThreadUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/DeleteThread/index.ts b/packages/api-collaboration/src/features/thread/DeleteThread/index.ts new file mode 100644 index 00000000000..823b62efb2e --- /dev/null +++ b/packages/api-collaboration/src/features/thread/DeleteThread/index.ts @@ -0,0 +1 @@ +export { DeleteThreadUseCase } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/GetThread/GetThreadRepository.ts b/packages/api-collaboration/src/features/thread/GetThread/GetThreadRepository.ts new file mode 100644 index 00000000000..30bdf383274 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/GetThread/GetThreadRepository.ts @@ -0,0 +1,41 @@ +import { Result } from "@webiny/feature/api"; +import { createIdentifier } from "@webiny/utils"; +import { GetEntryByIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetEntryById/index.js"; +import { + CollabThreadMapper, + CollabThreadModel, + type ICollabThreadValues +} from "~/domain/thread/abstractions.js"; +import { CollabThreadNotFoundError, CollabThreadPersistenceError } from "~/domain/thread/errors.js"; +import { GetThreadRepository as Repository } from "./abstractions.js"; + +class GetThreadRepositoryImpl implements Repository.Interface { + constructor( + private getEntryById: GetEntryByIdUseCase.Interface, + private model: CollabThreadModel.Interface, + private mapper: CollabThreadMapper.Interface + ) {} + + async execute(id: string): Repository.Return { + const revisionId = createIdentifier({ id, version: 1 }); + + const entryResult = await this.getEntryById.execute( + this.model, + revisionId + ); + + if (entryResult.isFail()) { + if (entryResult.error.code === "Cms/Entry/NotFound") { + return Result.fail(new CollabThreadNotFoundError({ id })); + } + return Result.fail(new CollabThreadPersistenceError(entryResult.error)); + } + + return Result.ok(this.mapper.fromCmsEntry(entryResult.value)); + } +} + +export const GetThreadRepository = Repository.createImplementation({ + implementation: GetThreadRepositoryImpl, + dependencies: [GetEntryByIdUseCase, CollabThreadModel, CollabThreadMapper] +}); diff --git a/packages/api-collaboration/src/features/thread/GetThread/GetThreadUseCase.ts b/packages/api-collaboration/src/features/thread/GetThread/GetThreadUseCase.ts new file mode 100644 index 00000000000..f885e4bdcf5 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/GetThread/GetThreadUseCase.ts @@ -0,0 +1,49 @@ +import { Result } from "@webiny/feature/api"; +import { GetThreadRepository, GetThreadUseCase as UseCase } from "./abstractions.js"; +import { ResolveLocatorUseCase } from "~/features/locator/ResolveLocator/index.js"; +import { + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError +} from "~/domain/thread/errors.js"; + +class GetThreadUseCaseImpl implements UseCase.Interface { + constructor( + private repository: GetThreadRepository.Interface, + private resolveLocator: ResolveLocatorUseCase.Interface + ) {} + + async execute(id: string): UseCase.Return { + const threadResult = await this.repository.execute(id); + if (threadResult.isFail()) { + return Result.fail(threadResult.error); + } + + const thread = threadResult.value; + if (thread.deleted) { + return Result.fail(new CollabThreadNotFoundError({ id })); + } + + const resolution = await this.resolveLocator.execute({ + contentType: thread.contentType, + contentId: thread.contentId, + locator: thread.locator + }); + + // Access to a thread equals read access to its target content. Without a resolver we + // cannot verify access, so we deny. + if (resolution.isFail() || !resolution.value.authorized) { + return Result.fail( + new CollabThreadNotAuthorizedError( + "You do not have access to this collaboration thread." + ) + ); + } + + return Result.ok({ thread, anchor: resolution.value }); + } +} + +export const GetThreadUseCase = UseCase.createImplementation({ + implementation: GetThreadUseCaseImpl, + dependencies: [GetThreadRepository, ResolveLocatorUseCase] +}); diff --git a/packages/api-collaboration/src/features/thread/GetThread/abstractions.ts b/packages/api-collaboration/src/features/thread/GetThread/abstractions.ts new file mode 100644 index 00000000000..841c99ab470 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/GetThread/abstractions.ts @@ -0,0 +1,54 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { ICollabThread } from "~/domain/thread/abstractions.js"; +import type { + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError, + CollabThreadPersistenceError +} from "~/domain/thread/errors.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; + +/** + * GetThread repository — pure load by id. + */ +export interface IGetThreadRepository { + execute(id: string): Promise>; +} + +export interface IGetThreadRepositoryErrors { + notFound: CollabThreadNotFoundError; + persistence: CollabThreadPersistenceError; +} + +type RepositoryError = IGetThreadRepositoryErrors[keyof IGetThreadRepositoryErrors]; + +export const GetThreadRepository = createAbstraction("GetThreadRepository"); + +export namespace GetThreadRepository { + export type Interface = IGetThreadRepository; + export type Return = Promise>; + export type Error = RepositoryError; +} + +/** + * GetThread use case — loads a thread and ensures the caller may read the target content. + */ +export interface IGetThreadUseCase { + execute(id: string): Promise>; +} + +export interface IGetThreadUseCaseErrors { + notFound: CollabThreadNotFoundError; + persistence: CollabThreadPersistenceError; + notAuthorized: CollabThreadNotAuthorizedError; +} + +type UseCaseError = IGetThreadUseCaseErrors[keyof IGetThreadUseCaseErrors]; + +export const GetThreadUseCase = createAbstraction("GetThreadUseCase"); + +export namespace GetThreadUseCase { + export type Interface = IGetThreadUseCase; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/thread/GetThread/feature.ts b/packages/api-collaboration/src/features/thread/GetThread/feature.ts new file mode 100644 index 00000000000..a9096fa9bc2 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/GetThread/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { GetThreadRepository } from "./GetThreadRepository.js"; +import { GetThreadUseCase } from "./GetThreadUseCase.js"; + +export const GetThreadFeature = createFeature({ + name: "Collaboration/GetThread", + register(container) { + container.register(GetThreadRepository).inSingletonScope(); + container.register(GetThreadUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/GetThread/index.ts b/packages/api-collaboration/src/features/thread/GetThread/index.ts new file mode 100644 index 00000000000..1501b6a130a --- /dev/null +++ b/packages/api-collaboration/src/features/thread/GetThread/index.ts @@ -0,0 +1 @@ +export { GetThreadUseCase, GetThreadRepository } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsRepository.ts b/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsRepository.ts new file mode 100644 index 00000000000..9b952d70da3 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsRepository.ts @@ -0,0 +1,54 @@ +import { Result } from "@webiny/feature/api"; +import { CmsWhereMapper } from "@webiny/api-headless-cms"; +import { ListLatestEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries/index.js"; +import { + CollabThreadMapper, + CollabThreadModel, + type ICollabThreadValues +} from "~/domain/thread/abstractions.js"; +import { CollabThreadPersistenceError } from "~/domain/thread/errors.js"; +import { ListThreadsRepository as Repository } from "./abstractions.js"; + +class ListThreadsRepositoryImpl implements Repository.Interface { + constructor( + private listLatestEntries: ListLatestEntriesUseCase.Interface, + private model: CollabThreadModel.Interface, + private mapper: CollabThreadMapper.Interface, + private cmsWhereMapper: CmsWhereMapper.Interface + ) {} + + async execute(params: Repository.Params): Repository.Return { + // Exclude soft-deleted threads at the query level (not in JS afterwards), so `limit` and + // the returned `meta` describe the same set the caller sees — otherwise a page made up of + // soft-deleted rows would return 0 items while `meta` still reports more to load. + const where = this.cmsWhereMapper.map({ + input: { ...params.where, deleted: false }, + fields: this.model.fields + }); + + const sort = (params.sort ?? ["createdOn_DESC"]) as (`${string}_ASC` | `${string}_DESC`)[]; + + const listResult = await this.listLatestEntries.execute(this.model, { + sort, + limit: params.limit ?? 100, + after: params.after ?? undefined, + where + }); + + if (listResult.isFail()) { + return Result.fail(new CollabThreadPersistenceError(listResult.error)); + } + + const { entries, meta } = listResult.value; + + return Result.ok({ + items: entries.map(entry => this.mapper.fromCmsEntry(entry)), + meta + }); + } +} + +export const ListThreadsRepository = Repository.createImplementation({ + implementation: ListThreadsRepositoryImpl, + dependencies: [ListLatestEntriesUseCase, CollabThreadModel, CollabThreadMapper, CmsWhereMapper] +}); diff --git a/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsUseCase.ts b/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsUseCase.ts new file mode 100644 index 00000000000..d6bd9202f9b --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ListThreads/ListThreadsUseCase.ts @@ -0,0 +1,43 @@ +import { Result } from "@webiny/feature/api"; +import { ListThreadsRepository, ListThreadsUseCase as UseCase } from "./abstractions.js"; +import { ResolveLocatorUseCase } from "~/features/locator/ResolveLocator/index.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; + +class ListThreadsUseCaseImpl implements UseCase.Interface { + constructor( + private repository: ListThreadsRepository.Interface, + private resolveLocator: ResolveLocatorUseCase.Interface + ) {} + + async execute(params: UseCase.Params): UseCase.Return { + const listResult = await this.repository.execute(params); + if (listResult.isFail()) { + return Result.fail(listResult.error); + } + + const { items, meta } = listResult.value; + const views: ICollabThreadView[] = []; + + for (const thread of items) { + const resolution = await this.resolveLocator.execute({ + contentType: thread.contentType, + contentId: thread.contentId, + locator: thread.locator + }); + + // Without read access to the target we omit the thread entirely. + if (resolution.isFail() || !resolution.value.authorized) { + continue; + } + + views.push({ thread, anchor: resolution.value }); + } + + return Result.ok({ items: views, meta }); + } +} + +export const ListThreadsUseCase = UseCase.createImplementation({ + implementation: ListThreadsUseCaseImpl, + dependencies: [ListThreadsRepository, ResolveLocatorUseCase] +}); diff --git a/packages/api-collaboration/src/features/thread/ListThreads/abstractions.ts b/packages/api-collaboration/src/features/thread/ListThreads/abstractions.ts new file mode 100644 index 00000000000..b010a10cc4c --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ListThreads/abstractions.ts @@ -0,0 +1,69 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { CollabThreadType, ICollabThread } from "~/domain/thread/abstractions.js"; +import type { CollabThreadPersistenceError } from "~/domain/thread/errors.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; +import type { IMeta } from "~/types.js"; + +export interface IListThreadsWhere { + contentType: string; + contentId: string; + type?: CollabThreadType; + resolved?: boolean; +} + +export interface IListThreadsParams { + where: IListThreadsWhere; + limit?: number; + after?: string | null; + sort?: string[]; +} + +/** + * ListThreads repository — lists raw threads for a content target. + */ +export interface IListThreadsRepository { + execute(params: IListThreadsParams): Promise>; +} + +export interface IListThreadsResult { + items: ICollabThread[]; + meta: IMeta; +} + +export interface IListThreadsRepositoryErrors { + persistence: CollabThreadPersistenceError; +} + +type RepositoryError = IListThreadsRepositoryErrors[keyof IListThreadsRepositoryErrors]; + +export const ListThreadsRepository = + createAbstraction("ListThreadsRepository"); + +export namespace ListThreadsRepository { + export type Interface = IListThreadsRepository; + export type Params = IListThreadsParams; + export type Return = Promise>; + export type Error = RepositoryError; +} + +/** + * ListThreads use case — returns each thread paired with its resolved anchor. + */ +export interface IListThreadsViewResult { + items: ICollabThreadView[]; + meta: IMeta; +} + +export interface IListThreadsUseCase { + execute(params: IListThreadsParams): Promise>; +} + +export const ListThreadsUseCase = createAbstraction("ListThreadsUseCase"); + +export namespace ListThreadsUseCase { + export type Interface = IListThreadsUseCase; + export type Params = IListThreadsParams; + export type Return = Promise>; + export type Error = RepositoryError; +} diff --git a/packages/api-collaboration/src/features/thread/ListThreads/feature.ts b/packages/api-collaboration/src/features/thread/ListThreads/feature.ts new file mode 100644 index 00000000000..cd310d8b27d --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ListThreads/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { ListThreadsUseCase } from "./ListThreadsUseCase.js"; +import { ListThreadsRepository } from "./ListThreadsRepository.js"; + +export const ListThreadsFeature = createFeature({ + name: "Collaboration/ListThreads", + register(container) { + container.register(ListThreadsRepository).inSingletonScope(); + container.register(ListThreadsUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/ListThreads/index.ts b/packages/api-collaboration/src/features/thread/ListThreads/index.ts new file mode 100644 index 00000000000..a6db621d0ab --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ListThreads/index.ts @@ -0,0 +1 @@ +export { ListThreadsUseCase, ListThreadsRepository } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/MessageOperations/DeleteMessageUseCase.ts b/packages/api-collaboration/src/features/thread/MessageOperations/DeleteMessageUseCase.ts new file mode 100644 index 00000000000..0c0b1bc56ee --- /dev/null +++ b/packages/api-collaboration/src/features/thread/MessageOperations/DeleteMessageUseCase.ts @@ -0,0 +1,59 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { DeleteMessageUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; +import { + CollabMessageNotFoundError, + CollabThreadNotAuthorizedError +} from "~/domain/thread/errors.js"; +import { toCollabIdentity } from "~/utils/identity.js"; + +class DeleteMessageUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface + ) {} + + async execute(input: UseCase.Input): UseCase.Return { + const loaded = await this.getThread.execute(input.threadId); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread } = loaded.value; + const message = thread.messages.find(item => item.id === input.messageId); + if (!message || message.deleted) { + return Result.fail( + new CollabMessageNotFoundError({ + threadId: input.threadId, + messageId: input.messageId + }) + ); + } + + const identity = this.identityContext.getIdentity(); + if (!identity.isAdmin() && identity.id !== message.createdBy.id) { + return Result.fail( + new CollabThreadNotAuthorizedError("You can only delete your own messages.") + ); + } + + message.deleted = true; + message.deletedBy = toCollabIdentity(identity); + message.deletedOn = new Date().toISOString(); + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + return Result.ok(true); + } +} + +export const DeleteMessageUseCase = UseCase.createImplementation({ + implementation: DeleteMessageUseCaseImpl, + dependencies: [IdentityContext, GetThreadUseCase, UpdateThreadRepository] +}); diff --git a/packages/api-collaboration/src/features/thread/MessageOperations/UpdateMessageUseCase.ts b/packages/api-collaboration/src/features/thread/MessageOperations/UpdateMessageUseCase.ts new file mode 100644 index 00000000000..eb41bf22d91 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/MessageOperations/UpdateMessageUseCase.ts @@ -0,0 +1,61 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { UpdateMessageUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; +import { + CollabMessageNotFoundError, + CollabThreadNotAuthorizedError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; + +class UpdateMessageUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface + ) {} + + async execute(input: UseCase.Input): UseCase.Return { + if (!input.body || input.body.trim().length === 0) { + return Result.fail(new CollabThreadValidationError("Message body cannot be empty.")); + } + + const loaded = await this.getThread.execute(input.threadId); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread } = loaded.value; + const message = thread.messages.find(item => item.id === input.messageId); + if (!message || message.deleted) { + return Result.fail( + new CollabMessageNotFoundError({ + threadId: input.threadId, + messageId: input.messageId + }) + ); + } + + const identity = this.identityContext.getIdentity(); + if (!identity.isAdmin() && identity.id !== message.createdBy.id) { + return Result.fail( + new CollabThreadNotAuthorizedError("You can only edit your own messages.") + ); + } + + message.body = input.body; + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + return Result.ok(message); + } +} + +export const UpdateMessageUseCase = UseCase.createImplementation({ + implementation: UpdateMessageUseCaseImpl, + dependencies: [IdentityContext, GetThreadUseCase, UpdateThreadRepository] +}); diff --git a/packages/api-collaboration/src/features/thread/MessageOperations/abstractions.ts b/packages/api-collaboration/src/features/thread/MessageOperations/abstractions.ts new file mode 100644 index 00000000000..5bffb23521b --- /dev/null +++ b/packages/api-collaboration/src/features/thread/MessageOperations/abstractions.ts @@ -0,0 +1,65 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { ICollabMessage } from "~/domain/thread/abstractions.js"; +import type { + CollabMessageNotFoundError, + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError, + CollabThreadPersistenceError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; + +export interface IMessageOperationErrors { + threadNotFound: CollabThreadNotFoundError; + messageNotFound: CollabMessageNotFoundError; + notAuthorized: CollabThreadNotAuthorizedError; + validation: CollabThreadValidationError; + persistence: CollabThreadPersistenceError; +} + +type UseCaseError = IMessageOperationErrors[keyof IMessageOperationErrors]; + +/** + * Edit a message body — author or admin only. + */ +export interface IUpdateMessageInput { + threadId: string; + messageId: string; + body: string; +} + +export interface IUpdateMessageUseCase { + execute(input: IUpdateMessageInput): Promise>; +} + +export const UpdateMessageUseCase = + createAbstraction("UpdateMessageUseCase"); + +export namespace UpdateMessageUseCase { + export type Interface = IUpdateMessageUseCase; + export type Input = IUpdateMessageInput; + export type Return = Promise>; + export type Error = UseCaseError; +} + +/** + * Soft-delete a message — author or admin only. + */ +export interface IDeleteMessageInput { + threadId: string; + messageId: string; +} + +export interface IDeleteMessageUseCase { + execute(input: IDeleteMessageInput): Promise>; +} + +export const DeleteMessageUseCase = + createAbstraction("DeleteMessageUseCase"); + +export namespace DeleteMessageUseCase { + export type Interface = IDeleteMessageUseCase; + export type Input = IDeleteMessageInput; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/thread/MessageOperations/feature.ts b/packages/api-collaboration/src/features/thread/MessageOperations/feature.ts new file mode 100644 index 00000000000..8e290eba0d0 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/MessageOperations/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { UpdateMessageUseCase } from "./UpdateMessageUseCase.js"; +import { DeleteMessageUseCase } from "./DeleteMessageUseCase.js"; + +export const MessageOperationsFeature = createFeature({ + name: "Collaboration/MessageOperations", + register(container) { + container.register(UpdateMessageUseCase); + container.register(DeleteMessageUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/MessageOperations/index.ts b/packages/api-collaboration/src/features/thread/MessageOperations/index.ts new file mode 100644 index 00000000000..27ab7835c27 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/MessageOperations/index.ts @@ -0,0 +1 @@ +export { UpdateMessageUseCase, DeleteMessageUseCase } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/ReplyToThread/ReplyToThreadUseCase.ts b/packages/api-collaboration/src/features/thread/ReplyToThread/ReplyToThreadUseCase.ts new file mode 100644 index 00000000000..a6a1a35f442 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ReplyToThread/ReplyToThreadUseCase.ts @@ -0,0 +1,69 @@ +import { Result } from "@webiny/feature/api"; +import { mdbid } from "@webiny/utils"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { EventPublisher } from "@webiny/api-core/features/eventPublisher/index.js"; +import { ReplyToThreadUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; +import type { ICollabMessage } from "~/domain/thread/abstractions.js"; +import { CollabReplyAddedEvent } from "~/domain/thread/events.js"; +import { + CollabThreadNotAuthorizedError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; +import { toCollabIdentity } from "~/utils/identity.js"; + +class ReplyToThreadUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface, + private eventPublisher: EventPublisher.Interface + ) {} + + async execute(input: UseCase.Input): UseCase.Return { + const identity = this.identityContext.getIdentity(); + if (identity.isAnonymous()) { + return Result.fail( + new CollabThreadNotAuthorizedError("You must be signed in to reply.") + ); + } + + if (!input.body || input.body.trim().length === 0) { + return Result.fail(new CollabThreadValidationError("Reply body cannot be empty.")); + } + + const loaded = await this.getThread.execute(input.threadId); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread, anchor } = loaded.value; + + const message: ICollabMessage = { + id: mdbid(), + body: input.body, + mentions: input.mentions ?? [], + createdBy: toCollabIdentity(identity), + createdOn: new Date().toISOString() + }; + + thread.messages = [...thread.messages, message]; + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + await this.eventPublisher.publish( + new CollabReplyAddedEvent({ thread: updateResult.value, message, anchor }) + ); + + return Result.ok(message); + } +} + +export const ReplyToThreadUseCase = UseCase.createImplementation({ + implementation: ReplyToThreadUseCaseImpl, + dependencies: [IdentityContext, GetThreadUseCase, UpdateThreadRepository, EventPublisher] +}); diff --git a/packages/api-collaboration/src/features/thread/ReplyToThread/abstractions.ts b/packages/api-collaboration/src/features/thread/ReplyToThread/abstractions.ts new file mode 100644 index 00000000000..baa5087876c --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ReplyToThread/abstractions.ts @@ -0,0 +1,38 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { ICollabMessage } from "~/domain/thread/abstractions.js"; +import type { + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError, + CollabThreadPersistenceError, + CollabThreadValidationError +} from "~/domain/thread/errors.js"; + +export interface IReplyToThreadInput { + threadId: string; + body: string; + mentions?: string[]; +} + +export interface IReplyToThreadUseCase { + execute(input: IReplyToThreadInput): Promise>; +} + +export interface IReplyToThreadUseCaseErrors { + notFound: CollabThreadNotFoundError; + notAuthorized: CollabThreadNotAuthorizedError; + validation: CollabThreadValidationError; + persistence: CollabThreadPersistenceError; +} + +type UseCaseError = IReplyToThreadUseCaseErrors[keyof IReplyToThreadUseCaseErrors]; + +export const ReplyToThreadUseCase = + createAbstraction("ReplyToThreadUseCase"); + +export namespace ReplyToThreadUseCase { + export type Interface = IReplyToThreadUseCase; + export type Input = IReplyToThreadInput; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/thread/ReplyToThread/feature.ts b/packages/api-collaboration/src/features/thread/ReplyToThread/feature.ts new file mode 100644 index 00000000000..2c1b2f218e9 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ReplyToThread/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { ReplyToThreadUseCase } from "./ReplyToThreadUseCase.js"; + +export const ReplyToThreadFeature = createFeature({ + name: "Collaboration/ReplyToThread", + register(container) { + container.register(ReplyToThreadUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/ReplyToThread/index.ts b/packages/api-collaboration/src/features/thread/ReplyToThread/index.ts new file mode 100644 index 00000000000..d776743578d --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ReplyToThread/index.ts @@ -0,0 +1 @@ +export { ReplyToThreadUseCase } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/ThreadResolution/ReopenThreadUseCase.ts b/packages/api-collaboration/src/features/thread/ThreadResolution/ReopenThreadUseCase.ts new file mode 100644 index 00000000000..a44d11db173 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ThreadResolution/ReopenThreadUseCase.ts @@ -0,0 +1,36 @@ +import { Result } from "@webiny/feature/api"; +import { ReopenThreadUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; + +class ReopenThreadUseCaseImpl implements UseCase.Interface { + constructor( + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface + ) {} + + async execute(id: string): UseCase.Return { + const loaded = await this.getThread.execute(id); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread, anchor } = loaded.value; + + thread.resolved = false; + thread.resolvedBy = null; + thread.resolvedOn = null; + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + return Result.ok({ thread: updateResult.value, anchor }); + } +} + +export const ReopenThreadUseCase = UseCase.createImplementation({ + implementation: ReopenThreadUseCaseImpl, + dependencies: [GetThreadUseCase, UpdateThreadRepository] +}); diff --git a/packages/api-collaboration/src/features/thread/ThreadResolution/ResolveThreadUseCase.ts b/packages/api-collaboration/src/features/thread/ThreadResolution/ResolveThreadUseCase.ts new file mode 100644 index 00000000000..d719ac12534 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ThreadResolution/ResolveThreadUseCase.ts @@ -0,0 +1,39 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { ResolveThreadUseCase as UseCase } from "./abstractions.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { UpdateThreadRepository } from "~/features/thread/UpdateThread/index.js"; +import { toCollabIdentity } from "~/utils/identity.js"; + +class ResolveThreadUseCaseImpl implements UseCase.Interface { + constructor( + private identityContext: IdentityContext.Interface, + private getThread: GetThreadUseCase.Interface, + private updateThread: UpdateThreadRepository.Interface + ) {} + + async execute(id: string): UseCase.Return { + const loaded = await this.getThread.execute(id); + if (loaded.isFail()) { + return Result.fail(loaded.error); + } + + const { thread, anchor } = loaded.value; + + thread.resolved = true; + thread.resolvedBy = toCollabIdentity(this.identityContext.getIdentity()); + thread.resolvedOn = new Date().toISOString(); + + const updateResult = await this.updateThread.execute(thread); + if (updateResult.isFail()) { + return Result.fail(updateResult.error); + } + + return Result.ok({ thread: updateResult.value, anchor }); + } +} + +export const ResolveThreadUseCase = UseCase.createImplementation({ + implementation: ResolveThreadUseCaseImpl, + dependencies: [IdentityContext, GetThreadUseCase, UpdateThreadRepository] +}); diff --git a/packages/api-collaboration/src/features/thread/ThreadResolution/abstractions.ts b/packages/api-collaboration/src/features/thread/ThreadResolution/abstractions.ts new file mode 100644 index 00000000000..72542f4ed4e --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ThreadResolution/abstractions.ts @@ -0,0 +1,47 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { + CollabThreadNotAuthorizedError, + CollabThreadNotFoundError, + CollabThreadPersistenceError +} from "~/domain/thread/errors.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; + +export interface IThreadResolutionErrors { + notFound: CollabThreadNotFoundError; + notAuthorized: CollabThreadNotAuthorizedError; + persistence: CollabThreadPersistenceError; +} + +type UseCaseError = IThreadResolutionErrors[keyof IThreadResolutionErrors]; + +/** + * Resolve a thread — open to anyone with access. Records `resolvedBy`/`resolvedOn`. + */ +export interface IResolveThreadUseCase { + execute(id: string): Promise>; +} + +export const ResolveThreadUseCase = + createAbstraction("ResolveThreadUseCase"); + +export namespace ResolveThreadUseCase { + export type Interface = IResolveThreadUseCase; + export type Return = Promise>; + export type Error = UseCaseError; +} + +/** + * Reopen a resolved thread — open to anyone with access. Clears the resolution. + */ +export interface IReopenThreadUseCase { + execute(id: string): Promise>; +} + +export const ReopenThreadUseCase = createAbstraction("ReopenThreadUseCase"); + +export namespace ReopenThreadUseCase { + export type Interface = IReopenThreadUseCase; + export type Return = Promise>; + export type Error = UseCaseError; +} diff --git a/packages/api-collaboration/src/features/thread/ThreadResolution/feature.ts b/packages/api-collaboration/src/features/thread/ThreadResolution/feature.ts new file mode 100644 index 00000000000..120668dcc9b --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ThreadResolution/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { ResolveThreadUseCase } from "./ResolveThreadUseCase.js"; +import { ReopenThreadUseCase } from "./ReopenThreadUseCase.js"; + +export const ThreadResolutionFeature = createFeature({ + name: "Collaboration/ThreadResolution", + register(container) { + container.register(ResolveThreadUseCase); + container.register(ReopenThreadUseCase); + } +}); diff --git a/packages/api-collaboration/src/features/thread/ThreadResolution/index.ts b/packages/api-collaboration/src/features/thread/ThreadResolution/index.ts new file mode 100644 index 00000000000..69072c075d4 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/ThreadResolution/index.ts @@ -0,0 +1 @@ +export { ResolveThreadUseCase, ReopenThreadUseCase } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/UpdateThread/UpdateThreadRepository.ts b/packages/api-collaboration/src/features/thread/UpdateThread/UpdateThreadRepository.ts new file mode 100644 index 00000000000..0871d907cfe --- /dev/null +++ b/packages/api-collaboration/src/features/thread/UpdateThread/UpdateThreadRepository.ts @@ -0,0 +1,61 @@ +import { Result } from "@webiny/feature/api"; +import { createIdentifier } from "@webiny/utils"; +import { UpdateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/UpdateEntry/index.js"; +import { + CollabThreadModel, + type ICollabThread, + type ICollabThreadValues +} from "~/domain/thread/abstractions.js"; +import { CollabThreadNotFoundError, CollabThreadPersistenceError } from "~/domain/thread/errors.js"; +import { UpdateThreadRepository as Repository } from "./abstractions.js"; + +class UpdateThreadRepositoryImpl implements Repository.Interface { + constructor( + private updateEntry: UpdateEntryUseCase.Interface, + private model: CollabThreadModel.Interface + ) {} + + async execute(thread: ICollabThread): Repository.Return { + const revisionId = createIdentifier({ id: thread.id, version: 1 }); + + const values: ICollabThreadValues = { + contentType: thread.contentType, + contentId: thread.contentId, + locator: thread.locator, + type: thread.type, + resolved: thread.resolved, + resolvedBy: thread.resolvedBy ?? null, + resolvedOn: thread.resolvedOn ?? null, + assigneeId: thread.assigneeId ?? null, + dueDate: thread.dueDate ?? null, + messages: thread.messages, + deleted: thread.deleted ?? false, + deletedBy: thread.deletedBy ?? null, + deletedOn: thread.deletedOn ?? null + }; + + try { + const updateResult = await this.updateEntry.execute( + this.model, + revisionId, + { values } + ); + + if (updateResult.isFail()) { + if (updateResult.error.code === "Cms/Entry/NotFound") { + return Result.fail(new CollabThreadNotFoundError({ id: thread.id })); + } + return Result.fail(new CollabThreadPersistenceError(updateResult.error)); + } + + return Result.ok(thread); + } catch (error) { + return Result.fail(new CollabThreadPersistenceError(error as Error)); + } + } +} + +export const UpdateThreadRepository = Repository.createImplementation({ + implementation: UpdateThreadRepositoryImpl, + dependencies: [UpdateEntryUseCase, CollabThreadModel] +}); diff --git a/packages/api-collaboration/src/features/thread/UpdateThread/abstractions.ts b/packages/api-collaboration/src/features/thread/UpdateThread/abstractions.ts new file mode 100644 index 00000000000..12b734c9229 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/UpdateThread/abstractions.ts @@ -0,0 +1,31 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { ICollabThread } from "~/domain/thread/abstractions.js"; +import type { + CollabThreadNotFoundError, + CollabThreadPersistenceError +} from "~/domain/thread/errors.js"; + +/** + * Shared repository that persists the full thread entry (read-modify-write). Used by every + * thread mutation (reply, resolve, reopen, edit/delete message, delete thread). + */ +export interface IUpdateThreadRepository { + execute(thread: ICollabThread): Promise>; +} + +export interface IUpdateThreadRepositoryErrors { + notFound: CollabThreadNotFoundError; + persistence: CollabThreadPersistenceError; +} + +type RepositoryError = IUpdateThreadRepositoryErrors[keyof IUpdateThreadRepositoryErrors]; + +export const UpdateThreadRepository = + createAbstraction("UpdateThreadRepository"); + +export namespace UpdateThreadRepository { + export type Interface = IUpdateThreadRepository; + export type Return = Promise>; + export type Error = RepositoryError; +} diff --git a/packages/api-collaboration/src/features/thread/UpdateThread/feature.ts b/packages/api-collaboration/src/features/thread/UpdateThread/feature.ts new file mode 100644 index 00000000000..45d33ca1e24 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/UpdateThread/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { UpdateThreadRepository } from "./UpdateThreadRepository.js"; + +export const UpdateThreadFeature = createFeature({ + name: "Collaboration/UpdateThread", + register(container) { + container.register(UpdateThreadRepository).inSingletonScope(); + } +}); diff --git a/packages/api-collaboration/src/features/thread/UpdateThread/index.ts b/packages/api-collaboration/src/features/thread/UpdateThread/index.ts new file mode 100644 index 00000000000..e428385dbf6 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/UpdateThread/index.ts @@ -0,0 +1 @@ +export { UpdateThreadRepository } from "./abstractions.js"; diff --git a/packages/api-collaboration/src/features/thread/shared/abstractions.ts b/packages/api-collaboration/src/features/thread/shared/abstractions.ts new file mode 100644 index 00000000000..935e62ee0c9 --- /dev/null +++ b/packages/api-collaboration/src/features/thread/shared/abstractions.ts @@ -0,0 +1,11 @@ +import type { ICollabThread } from "~/domain/thread/abstractions.js"; +import type { ICollabLocatorResolution } from "~/domain/locator/abstractions.js"; + +/** + * A thread paired with the current resolution of its anchor (existence, label, breadcrumb). + * The anchor is computed at read time and never persisted. + */ +export interface ICollabThreadView { + thread: ICollabThread; + anchor: ICollabLocatorResolution; +} diff --git a/packages/api-collaboration/src/graphql/collaboration.ts b/packages/api-collaboration/src/graphql/collaboration.ts new file mode 100644 index 00000000000..6430dd9e4a7 --- /dev/null +++ b/packages/api-collaboration/src/graphql/collaboration.ts @@ -0,0 +1,480 @@ +import { GraphQLSchemaFactory } from "@webiny/handler-graphql/graphql/abstractions.js"; +import { NotFoundError, resolve, resolveList } from "@webiny/handler-graphql"; +import { createZodError } from "@webiny/utils"; +import { CollabThreadType } from "~/domain/thread/abstractions.js"; +import type { ICollabThreadView } from "~/features/thread/shared/abstractions.js"; +import { CreateThreadUseCase } from "~/features/thread/CreateThread/index.js"; +import { GetThreadUseCase } from "~/features/thread/GetThread/index.js"; +import { ListThreadsUseCase } from "~/features/thread/ListThreads/index.js"; +import { ReplyToThreadUseCase } from "~/features/thread/ReplyToThread/index.js"; +import { + ReopenThreadUseCase, + ResolveThreadUseCase +} from "~/features/thread/ThreadResolution/index.js"; +import { + DeleteMessageUseCase, + UpdateMessageUseCase +} from "~/features/thread/MessageOperations/index.js"; +import { DeleteThreadUseCase } from "~/features/thread/DeleteThread/index.js"; +import { + createCollabThreadValidation, + deleteCollabMessageValidation, + getCollabThreadValidation, + idOnlyValidation, + listCollabThreadsValidation, + replyToCollabThreadValidation, + updateCollabMessageValidation +} from "./validation.js"; + +const toGqlThread = (view: ICollabThreadView) => { + return { ...view.thread, anchor: view.anchor }; +}; + +interface IIdArgs { + id: string; +} + +interface IListCollabThreadsArgs { + where: { + contentType: string; + contentId: string; + type?: string; + resolved?: boolean; + }; + limit?: number; + after?: string; +} + +interface ICreateCollabThreadArgs { + input: { + contentType: string; + contentId: string; + locator: string; + type: string; + body: string; + mentions?: string[]; + assigneeId?: string; + dueDate?: string; + }; +} + +interface IReplyToCollabThreadArgs { + threadId: string; + body: string; + mentions?: string[]; +} + +interface IUpdateCollabMessageArgs { + threadId: string; + messageId: string; + body: string; +} + +interface IDeleteCollabMessageArgs { + threadId: string; + messageId: string; +} + +class CollaborationSchema_ implements GraphQLSchemaFactory.Interface { + async execute( + builder: GraphQLSchemaFactory.SchemaBuilder + ): Promise { + builder.addTypeDefs(/* GraphQL */ ` + type CollabError { + code: String + message: String + data: JSON + stack: String + } + + enum CollabThreadType { + note + task + } + + type CollabIdentity { + id: String! + displayName: String + type: String + } + + type CollabMessage { + id: String! + body: String! + mentions: [String!]! + createdBy: CollabIdentity! + createdOn: String! + deleted: Boolean + deletedBy: CollabIdentity + deletedOn: String + } + + type CollabAnchor { + exists: Boolean! + authorized: Boolean! + label: String + path: [String!] + } + + type CollabThread { + id: ID! + contentType: String! + contentId: String! + locator: String! + type: CollabThreadType! + resolved: Boolean! + resolvedBy: CollabIdentity + resolvedOn: String + assigneeId: String + dueDate: String + messages: [CollabMessage!]! + createdBy: CollabIdentity! + createdOn: String! + anchor: CollabAnchor! + } + + type CollabThreadListMeta { + cursor: String + hasMoreItems: Boolean! + totalCount: Int! + } + + type GetCollabThreadResponse { + data: CollabThread + error: CollabError + } + + type ListCollabThreadsResponse { + data: [CollabThread!] + meta: CollabThreadListMeta + error: CollabError + } + + type CollabThreadResponse { + data: CollabThread + error: CollabError + } + + type CollabMessageResponse { + data: CollabMessage + error: CollabError + } + + type CollabBooleanResponse { + data: Boolean + error: CollabError + } + + input ListCollabThreadsWhereInput { + contentType: String! + contentId: String! + type: CollabThreadType + resolved: Boolean + } + + input CreateCollabThreadInput { + contentType: String! + contentId: String! + locator: String! + type: CollabThreadType! + body: String! + mentions: [String!] + assigneeId: String + dueDate: String + } + + type CollaborationQuery { + listCollabThreads( + where: ListCollabThreadsWhereInput! + limit: Int + after: String + ): ListCollabThreadsResponse! + getCollabThread(id: ID!): GetCollabThreadResponse! + } + + type CollaborationMutation { + createCollabThread(input: CreateCollabThreadInput!): CollabThreadResponse! + replyToCollabThread( + threadId: ID! + body: String! + mentions: [String!] + ): CollabMessageResponse! + resolveCollabThread(id: ID!): CollabThreadResponse! + reopenCollabThread(id: ID!): CollabThreadResponse! + updateCollabMessage( + threadId: ID! + messageId: ID! + body: String! + ): CollabMessageResponse! + deleteCollabMessage(threadId: ID!, messageId: ID!): CollabBooleanResponse! + deleteCollabThread(id: ID!): CollabBooleanResponse! + } + + extend type Query { + collaboration: CollaborationQuery + } + + extend type Mutation { + collaboration: CollaborationMutation + } + `); + + builder.addResolver({ + path: "Query.collaboration", + dependencies: [], + resolver: () => () => ({}) + }); + + builder.addResolver({ + path: "Mutation.collaboration", + dependencies: [], + resolver: () => () => ({}) + }); + + builder.addResolver({ + path: "CollaborationQuery.getCollabThread", + dependencies: [GetThreadUseCase], + resolver: (getThread: GetThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await getCollabThreadValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const threadResult = await getThread.execute(result.data.id); + + if (threadResult.isFail()) { + throw new NotFoundError(threadResult.error.message); + } + + return toGqlThread(threadResult.value); + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationQuery.listCollabThreads", + dependencies: [ListThreadsUseCase], + resolver: (listThreads: ListThreadsUseCase.Interface) => { + return async ({ args }) => { + return resolveList(async () => { + const result = await listCollabThreadsValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const listResult = await listThreads.execute({ + where: { + contentType: result.data.where.contentType, + contentId: result.data.where.contentId, + type: result.data.where.type as CollabThreadType | undefined, + resolved: result.data.where.resolved + }, + limit: result.data.limit, + after: result.data.after + }); + + if (listResult.isFail()) { + throw listResult.error; + } + + return { + items: listResult.value.items.map(toGqlThread), + meta: listResult.value.meta + }; + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.createCollabThread", + dependencies: [CreateThreadUseCase], + resolver: (createThread: CreateThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await createCollabThreadValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const createResult = await createThread.execute({ + contentType: result.data.input.contentType, + contentId: result.data.input.contentId, + locator: result.data.input.locator, + type: result.data.input.type as CollabThreadType, + body: result.data.input.body, + mentions: result.data.input.mentions, + assigneeId: result.data.input.assigneeId, + dueDate: result.data.input.dueDate + }); + + if (createResult.isFail()) { + throw createResult.error; + } + + return toGqlThread(createResult.value); + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.replyToCollabThread", + dependencies: [ReplyToThreadUseCase], + resolver: (reply: ReplyToThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await replyToCollabThreadValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const replyResult = await reply.execute({ + threadId: result.data.threadId, + body: result.data.body, + mentions: result.data.mentions + }); + + if (replyResult.isFail()) { + throw replyResult.error; + } + + return replyResult.value; + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.resolveCollabThread", + dependencies: [ResolveThreadUseCase], + resolver: (resolveThread: ResolveThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idOnlyValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const threadResult = await resolveThread.execute(result.data.id); + + if (threadResult.isFail()) { + throw threadResult.error; + } + + return toGqlThread(threadResult.value); + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.reopenCollabThread", + dependencies: [ReopenThreadUseCase], + resolver: (reopenThread: ReopenThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idOnlyValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const threadResult = await reopenThread.execute(result.data.id); + + if (threadResult.isFail()) { + throw threadResult.error; + } + + return toGqlThread(threadResult.value); + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.updateCollabMessage", + dependencies: [UpdateMessageUseCase], + resolver: (updateMessage: UpdateMessageUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await updateCollabMessageValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const messageResult = await updateMessage.execute({ + threadId: result.data.threadId, + messageId: result.data.messageId, + body: result.data.body + }); + + if (messageResult.isFail()) { + throw messageResult.error; + } + + return messageResult.value; + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.deleteCollabMessage", + dependencies: [DeleteMessageUseCase], + resolver: (deleteMessage: DeleteMessageUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await deleteCollabMessageValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const deleteResult = await deleteMessage.execute({ + threadId: result.data.threadId, + messageId: result.data.messageId + }); + + if (deleteResult.isFail()) { + throw deleteResult.error; + } + + return true; + }); + }; + } + }); + + builder.addResolver({ + path: "CollaborationMutation.deleteCollabThread", + dependencies: [DeleteThreadUseCase], + resolver: (deleteThread: DeleteThreadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idOnlyValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const deleteResult = await deleteThread.execute(result.data.id); + + if (deleteResult.isFail()) { + throw deleteResult.error; + } + + return true; + }); + }; + } + }); + + return builder; + } +} + +export const CollaborationSchema = GraphQLSchemaFactory.createImplementation({ + implementation: CollaborationSchema_, + dependencies: [] +}); diff --git a/packages/api-collaboration/src/graphql/validation.ts b/packages/api-collaboration/src/graphql/validation.ts new file mode 100644 index 00000000000..1bc4db2d29f --- /dev/null +++ b/packages/api-collaboration/src/graphql/validation.ts @@ -0,0 +1,54 @@ +import zod from "zod"; + +const threadType = zod.enum(["note", "task"]); + +export const createCollabThreadValidation = zod.object({ + input: zod.object({ + contentType: zod.string().min(1, "Content type is required."), + contentId: zod.string().min(1, "Content ID is required."), + // Empty string = entry-level (unanchored) comment. + locator: zod.string(), + type: threadType, + body: zod.string().min(1, "Message body is required."), + mentions: zod.array(zod.string()).optional(), + assigneeId: zod.string().nullish(), + dueDate: zod.string().nullish() + }) +}); + +export const listCollabThreadsValidation = zod.object({ + where: zod.object({ + contentType: zod.string().min(1, "Content type is required."), + contentId: zod.string().min(1, "Content ID is required."), + type: threadType.optional(), + resolved: zod.boolean().optional() + }), + limit: zod.number().optional(), + after: zod.string().nullish(), + sort: zod.array(zod.string()).optional() +}); + +export const getCollabThreadValidation = zod.object({ + id: zod.string().min(1, "ID is required.") +}); + +export const idOnlyValidation = zod.object({ + id: zod.string().min(1, "ID is required.") +}); + +export const replyToCollabThreadValidation = zod.object({ + threadId: zod.string().min(1, "Thread ID is required."), + body: zod.string().min(1, "Reply body is required."), + mentions: zod.array(zod.string()).optional() +}); + +export const updateCollabMessageValidation = zod.object({ + threadId: zod.string().min(1, "Thread ID is required."), + messageId: zod.string().min(1, "Message ID is required."), + body: zod.string().min(1, "Message body is required.") +}); + +export const deleteCollabMessageValidation = zod.object({ + threadId: zod.string().min(1, "Thread ID is required."), + messageId: zod.string().min(1, "Message ID is required.") +}); diff --git a/packages/api-collaboration/src/index.ts b/packages/api-collaboration/src/index.ts new file mode 100644 index 00000000000..6e3ea327c3a --- /dev/null +++ b/packages/api-collaboration/src/index.ts @@ -0,0 +1,70 @@ +import { ContextPlugin, createRegisterExtensionPlugin } from "@webiny/handler"; +import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js"; +import { COLLAB_THREAD_MODEL_ID } from "./constants.js"; +import { CollabThreadModel as CollabThreadPrivateModel } from "./domain/thread/threadModel.js"; +import { CollabThreadModel } from "./domain/thread/abstractions.js"; +import { CollabThreadMapper } from "./domain/thread/CollabThreadMapper.js"; +import { ResolveLocatorFeature } from "./features/locator/ResolveLocator/feature.js"; +import { CmsLocatorResolverFeature } from "./features/cms/CmsLocatorResolver/feature.js"; +import { GetThreadFeature } from "./features/thread/GetThread/feature.js"; +import { UpdateThreadFeature } from "./features/thread/UpdateThread/feature.js"; +import { CreateThreadFeature } from "./features/thread/CreateThread/feature.js"; +import { ListThreadsFeature } from "./features/thread/ListThreads/feature.js"; +import { ReplyToThreadFeature } from "./features/thread/ReplyToThread/feature.js"; +import { ThreadResolutionFeature } from "./features/thread/ThreadResolution/feature.js"; +import { MessageOperationsFeature } from "./features/thread/MessageOperations/feature.js"; +import { DeleteThreadFeature } from "./features/thread/DeleteThread/feature.js"; +import { CollaborationSchema } from "./graphql/collaboration.js"; + +export const createCollaboration = () => { + const modelsPlugin = createRegisterExtensionPlugin(context => { + context.container.register(CollabThreadPrivateModel); + }); + + const collaborationContextPlugin = new ContextPlugin(async context => { + const tenantContext = context.container.resolve(TenantContext); + const identityContext = context.container.resolve(IdentityContext); + + // TODO(before release): collaboration ships in the APW tier — re-gate on the + // appropriate WCP capability (e.g. wcpContext.canUseWorkflows()). Ungated for now so it + // works without a workflows license during development. + if (!tenantContext.getTenant()) { + return; + } + + // Register private model, then resolve it to a CmsModel instance. + context.container.register(CollabThreadPrivateModel); + + const getModel = context.container.resolve(GetModelUseCase); + + await identityContext.withoutAuthorization(async () => { + const threadModel = await getModel.execute(COLLAB_THREAD_MODEL_ID); + context.container.registerInstance(CollabThreadModel, threadModel.value); + }); + + // Register the mapper. + context.container.register(CollabThreadMapper); + + // Register features. + ResolveLocatorFeature.register(context.container); + // Built-in CMS locator resolver (contentType "cms.entry"). + CmsLocatorResolverFeature.register(context.container); + UpdateThreadFeature.register(context.container); + GetThreadFeature.register(context.container); + CreateThreadFeature.register(context.container); + ListThreadsFeature.register(context.container); + ReplyToThreadFeature.register(context.container); + ThreadResolutionFeature.register(context.container); + MessageOperationsFeature.register(context.container); + DeleteThreadFeature.register(context.container); + + // Register the GraphQL schema. + context.container.register(CollaborationSchema); + }); + + collaborationContextPlugin.name = "collaboration.context"; + + return [collaborationContextPlugin, modelsPlugin]; +}; diff --git a/packages/api-collaboration/src/types.ts b/packages/api-collaboration/src/types.ts new file mode 100644 index 00000000000..8e17912c707 --- /dev/null +++ b/packages/api-collaboration/src/types.ts @@ -0,0 +1,5 @@ +export interface IMeta { + totalCount: number; + hasMoreItems: boolean; + cursor: string | null; +} diff --git a/packages/api-collaboration/src/utils/cmsContentId.ts b/packages/api-collaboration/src/utils/cmsContentId.ts new file mode 100644 index 00000000000..2a4fc3d4f47 --- /dev/null +++ b/packages/api-collaboration/src/utils/cmsContentId.ts @@ -0,0 +1,23 @@ +/** + * For `cms.entry` content, the collaboration core's opaque `contentId` encodes both the model + * and the (revision-independent) entry id as `":"`. The admin client always + * knows both when creating a thread, and the CMS resolver parses it here. This keeps the core + * content-agnostic (it never learns what a "model" is). + */ +export const formatCmsContentId = (modelId: string, entryId: string): string => { + return `${modelId}:${entryId}`; +}; + +export interface ParsedCmsContentId { + modelId: string | null; + entryId: string | null; +} + +export const parseCmsContentId = (contentId: string): ParsedCmsContentId => { + const [modelId, entryId] = contentId.split(":"); + if (!modelId || !entryId) { + return { modelId: null, entryId: null }; + } + + return { modelId, entryId }; +}; diff --git a/packages/api-collaboration/src/utils/identity.ts b/packages/api-collaboration/src/utils/identity.ts new file mode 100644 index 00000000000..215d421b72f --- /dev/null +++ b/packages/api-collaboration/src/utils/identity.ts @@ -0,0 +1,21 @@ +import type { ICollabIdentity } from "~/domain/thread/abstractions.js"; + +/** + * The subset of a Webiny identity (request identity or CMS identity) we project from. + */ +export interface WebinyIdentityLike { + id: string; + displayName: string; + type: string; +} + +/** + * Projects any Webiny identity (request identity or CMS identity) to the stored shape. + */ +export const toCollabIdentity = (identity: WebinyIdentityLike): ICollabIdentity => { + return { + id: identity.id, + displayName: identity.displayName, + type: identity.type + }; +}; diff --git a/packages/api-collaboration/tsconfig.build.json b/packages/api-collaboration/tsconfig.build.json new file mode 100644 index 00000000000..090ea5f578a --- /dev/null +++ b/packages/api-collaboration/tsconfig.build.json @@ -0,0 +1,44 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src"], + "references": [ + { "path": "../api-core/tsconfig.build.json" }, + { "path": "../api-headless-cms/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" }, + { "path": "../handler/tsconfig.build.json" }, + { "path": "../handler-graphql/tsconfig.build.json" }, + { "path": "../utils/tsconfig.build.json" }, + { "path": "../api/tsconfig.build.json" }, + { "path": "../plugins/tsconfig.build.json" }, + { "path": "../testing/tsconfig.build.json" } + ], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], + "@webiny/api-headless-cms": ["../api-headless-cms/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/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"], + "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], + "@webiny/handler-graphql": ["../handler-graphql/src"], + "@webiny/utils/*": ["../utils/src/*"], + "@webiny/utils": ["../utils/src"], + "@webiny/api/*": ["../api/src/*"], + "@webiny/api": ["../api/src"], + "@webiny/plugins/*": ["../plugins/src/*"], + "@webiny/plugins": ["../plugins/src"], + "@webiny/testing/*": ["../testing/src/*"], + "@webiny/testing": ["../testing/src"] + } + } +} diff --git a/packages/api-collaboration/tsconfig.json b/packages/api-collaboration/tsconfig.json new file mode 100644 index 00000000000..e167c2a43ed --- /dev/null +++ b/packages/api-collaboration/tsconfig.json @@ -0,0 +1,44 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../api-core" }, + { "path": "../api-headless-cms" }, + { "path": "../feature" }, + { "path": "../handler" }, + { "path": "../handler-graphql" }, + { "path": "../utils" }, + { "path": "../api" }, + { "path": "../plugins" }, + { "path": "../testing" } + ], + "compilerOptions": { + "rootDirs": ["./src", "./__tests__"], + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], + "@webiny/api-headless-cms": ["../api-headless-cms/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/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"], + "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], + "@webiny/handler-graphql": ["../handler-graphql/src"], + "@webiny/utils/*": ["../utils/src/*"], + "@webiny/utils": ["../utils/src"], + "@webiny/api/*": ["../api/src/*"], + "@webiny/api": ["../api/src"], + "@webiny/plugins/*": ["../plugins/src/*"], + "@webiny/plugins": ["../plugins/src"], + "@webiny/testing/*": ["../testing/src/*"], + "@webiny/testing": ["../testing/src"] + } + } +} diff --git a/packages/api-collaboration/vitest.config.ts b/packages/api-collaboration/vitest.config.ts new file mode 100644 index 00000000000..abbb26649c9 --- /dev/null +++ b/packages/api-collaboration/vitest.config.ts @@ -0,0 +1,34 @@ +import { resolve } from "path"; +import { createTestConfig } from "../../testing"; + +/** + * Loads storage-operations presets straight from the given packages, bypassing the workspace + * scan. Used as a fallback when the scan fails (e.g. a stray `packages/*` dir without a + * package.json breaks `get-yarn-workspaces`). + */ +const loadPresetsFromPackages = async (...pkgs: string[]) => { + const all: unknown[] = []; + for (const pkg of pkgs) { + const presetsPath = resolve(process.cwd(), "packages", pkg, "__tests__/__api__/presets.js"); + const mod = await import(presetsPath); + all.push(...(mod.default ?? mod)); + } + return all; +}; + +export default async () => { + let presets; + try { + const { getPresets } = await import("@webiny/project-utils/testing/presets/index.js"); + presets = await getPresets( + ["@webiny/api-headless-cms", "storage-operations"], + ["@webiny/api-core", "storage-operations"] + ); + } catch { + // Fallback for local environments where the workspace scan can't enumerate all packages. + process.env.WEBINY_STORAGE_OPS = process.env.WEBINY_STORAGE_OPS || "ddb"; + presets = await loadPresetsFromPackages("api-core-ddb", "api-headless-cms-ddb"); + } + + return createTestConfig({ path: import.meta.dirname, presets }); +}; diff --git a/packages/api-collaboration/webiny.config.js b/packages/api-collaboration/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/api-collaboration/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/api-notifications-integrations/package.json b/packages/api-notifications-integrations/package.json new file mode 100644 index 00000000000..fd78fcff3cd --- /dev/null +++ b/packages/api-notifications-integrations/package.json @@ -0,0 +1,38 @@ +{ + "name": "@webiny/api-notifications-integrations", + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./index.js", + "./*": "./*" + }, + "description": "Notification integrations: feeds the notifications inbox from Workflows and Collaboration", + "keywords": [ + "api-notifications-integrations:base" + ], + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git", + "directory": "packages/api-notifications-integrations" + }, + "license": "MIT", + "dependencies": { + "@webiny/api-collaboration": "0.0.0", + "@webiny/api-core": "0.0.0", + "@webiny/api-notifications": "0.0.0", + "@webiny/api-workflows": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/handler": "0.0.0" + }, + "devDependencies": { + "@webiny/build-tools": "0.0.0", + "@webiny/project-utils": "0.0.0", + "typescript": "7.0.2" + }, + "publishConfig": { + "access": "public" + }, + "webiny": { + "publishFrom": "dist" + } +} diff --git a/packages/api-notifications-integrations/src/collaboration/ReplyAddedNotificationHandler.ts b/packages/api-notifications-integrations/src/collaboration/ReplyAddedNotificationHandler.ts new file mode 100644 index 00000000000..afc87c64531 --- /dev/null +++ b/packages/api-notifications-integrations/src/collaboration/ReplyAddedNotificationHandler.ts @@ -0,0 +1,68 @@ +import { CollabReplyAddedHandler } from "@webiny/api-collaboration/domain/thread/events.js"; +import { CreateNotificationUseCase } from "@webiny/api-notifications/features/CreateNotification/index.js"; +import { NotificationType } from "@webiny/api-notifications/domain/notification/abstractions.js"; +import { APP_HEADLESS_CMS } from "~/constants.js"; + +/** + * A reply -> notify the thread participants (authors of earlier messages) and anyone mentioned + * in the reply. Mentioned recipients get a `mention` notification; other participants get a + * `reply` notification. The reply's author is never notified. + */ +class ReplyAddedNotificationHandlerImpl implements CollabReplyAddedHandler.Interface { + constructor(private createNotification: CreateNotificationUseCase.Interface) {} + + async handle(event: CollabReplyAddedHandler.Event) { + const { thread, message, anchor } = event.payload; + const actor = message.createdBy; + // Reference the entry itself (e.g. "replied to your comment on "), falling + // back to the anchored field label only when the entry title is unavailable. + const title = anchor.contentTitle || anchor.label || "a comment"; + const link = { + app: APP_HEADLESS_CMS, + contentType: thread.contentType, + contentId: thread.contentId, + locator: thread.locator, + threadId: thread.id + }; + + const mentioned = new Set((message.mentions || []).filter(id => id && id !== actor.id)); + const participants = new Set(); + + for (const existing of thread.messages) { + if (existing.id === message.id || existing.deleted) { + continue; + } + const authorId = existing.createdBy?.id; + if (authorId && authorId !== actor.id && !mentioned.has(authorId)) { + participants.add(authorId); + } + } + + for (const recipientId of mentioned) { + await this.createNotification.execute({ + recipientId, + type: NotificationType.mention, + actor, + title, + snippet: message.body, + link + }); + } + + for (const recipientId of participants) { + await this.createNotification.execute({ + recipientId, + type: NotificationType.reply, + actor, + title, + snippet: message.body, + link + }); + } + } +} + +export const ReplyAddedNotificationHandler = CollabReplyAddedHandler.createImplementation({ + implementation: ReplyAddedNotificationHandlerImpl, + dependencies: [CreateNotificationUseCase] +}); diff --git a/packages/api-notifications-integrations/src/collaboration/ThreadCreatedNotificationHandler.ts b/packages/api-notifications-integrations/src/collaboration/ThreadCreatedNotificationHandler.ts new file mode 100644 index 00000000000..ba9c48aa57a --- /dev/null +++ b/packages/api-notifications-integrations/src/collaboration/ThreadCreatedNotificationHandler.ts @@ -0,0 +1,44 @@ +import { CollabThreadCreatedHandler } from "@webiny/api-collaboration/domain/thread/events.js"; +import { CreateNotificationUseCase } from "@webiny/api-notifications/features/CreateNotification/index.js"; +import { NotificationType } from "@webiny/api-notifications/domain/notification/abstractions.js"; +import { APP_HEADLESS_CMS } from "~/constants.js"; + +/** + * A new comment thread -> notify everyone mentioned in the first message. + */ +class ThreadCreatedNotificationHandlerImpl implements CollabThreadCreatedHandler.Interface { + constructor(private createNotification: CreateNotificationUseCase.Interface) {} + + async handle(event: CollabThreadCreatedHandler.Event) { + const { thread, message, anchor } = event.payload; + const actor = message.createdBy; + // Reference the entry itself (e.g. "mentioned you on "), falling back to the + // anchored field label only when the entry title is unavailable. + const title = anchor.contentTitle || anchor.label || "a comment"; + const link = { + app: APP_HEADLESS_CMS, + contentType: thread.contentType, + contentId: thread.contentId, + locator: thread.locator, + threadId: thread.id + }; + + const recipients = new Set((message.mentions || []).filter(id => id && id !== actor.id)); + + for (const recipientId of recipients) { + await this.createNotification.execute({ + recipientId, + type: NotificationType.mention, + actor, + title, + snippet: message.body, + link + }); + } + } +} + +export const ThreadCreatedNotificationHandler = CollabThreadCreatedHandler.createImplementation({ + implementation: ThreadCreatedNotificationHandlerImpl, + dependencies: [CreateNotificationUseCase] +}); diff --git a/packages/api-notifications-integrations/src/collaboration/feature.ts b/packages/api-notifications-integrations/src/collaboration/feature.ts new file mode 100644 index 00000000000..3b14620f85c --- /dev/null +++ b/packages/api-notifications-integrations/src/collaboration/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { ThreadCreatedNotificationHandler } from "./ThreadCreatedNotificationHandler.js"; +import { ReplyAddedNotificationHandler } from "./ReplyAddedNotificationHandler.js"; + +export const CollaborationNotificationsFeature = createFeature({ + name: "NotificationsIntegrations/Collaboration", + register(container) { + container.register(ThreadCreatedNotificationHandler); + container.register(ReplyAddedNotificationHandler); + } +}); diff --git a/packages/api-notifications-integrations/src/constants.ts b/packages/api-notifications-integrations/src/constants.ts new file mode 100644 index 00000000000..be016089d8b --- /dev/null +++ b/packages/api-notifications-integrations/src/constants.ts @@ -0,0 +1,2 @@ +/** Routing hints stored on a notification's link, used by the admin to navigate on click. */ +export const APP_HEADLESS_CMS = "cms"; diff --git a/packages/api-notifications-integrations/src/index.ts b/packages/api-notifications-integrations/src/index.ts new file mode 100644 index 00000000000..d0ec77dae12 --- /dev/null +++ b/packages/api-notifications-integrations/src/index.ts @@ -0,0 +1,25 @@ +import { ContextPlugin } from "@webiny/handler"; +import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js"; +import { CollaborationNotificationsFeature } from "./collaboration/feature.js"; +import { WorkflowNotificationsFeature } from "./workflows/feature.js"; + +/** + * Wires the notification inbox to its sources: subscribes to Collaboration events (mentions, + * replies) and Workflow events (approved, rejected) and creates notifications for the relevant + * recipients. Register alongside createNotifications() in the API. + */ +export const createNotificationsIntegrations = () => { + const plugin = new ContextPlugin(async context => { + const tenantContext = context.container.resolve(TenantContext); + if (!tenantContext.getTenant()) { + return; + } + + CollaborationNotificationsFeature.register(context.container); + WorkflowNotificationsFeature.register(context.container); + }); + + plugin.name = "notifications.integrations.context"; + + return [plugin]; +}; diff --git a/packages/api-notifications-integrations/src/workflows/WorkflowApprovedNotificationHandler.ts b/packages/api-notifications-integrations/src/workflows/WorkflowApprovedNotificationHandler.ts new file mode 100644 index 00000000000..6efe76bdbad --- /dev/null +++ b/packages/api-notifications-integrations/src/workflows/WorkflowApprovedNotificationHandler.ts @@ -0,0 +1,43 @@ +import { WorkflowStateApproveStepHandler } from "@webiny/api-workflows/features/workflowState/ApproveWorkflowStateStep/events.js"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { CreateNotificationUseCase } from "@webiny/api-notifications/features/CreateNotification/index.js"; +import { NotificationType } from "@webiny/api-notifications/domain/notification/abstractions.js"; + +/** + * A workflow step was approved -> notify the person who submitted the content for review. + */ +class WorkflowApprovedNotificationHandlerImpl implements WorkflowStateApproveStepHandler.Interface { + constructor( + private createNotification: CreateNotificationUseCase.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async handle(event: WorkflowStateApproveStepHandler.Event) { + const state = event.payload.state; + const recipientId = state.createdBy?.id; + const identity = this.identityContext.getIdentity(); + + if (!recipientId || recipientId === identity.id) { + return; + } + + await this.createNotification.execute({ + recipientId, + type: NotificationType.approved, + actor: { + id: identity.id, + displayName: identity.displayName, + type: identity.type + }, + title: state.title, + snippet: null, + link: { app: state.app, contentId: state.targetId } + }); + } +} + +export const WorkflowApprovedNotificationHandler = + WorkflowStateApproveStepHandler.createImplementation({ + implementation: WorkflowApprovedNotificationHandlerImpl, + dependencies: [CreateNotificationUseCase, IdentityContext] + }); diff --git a/packages/api-notifications-integrations/src/workflows/WorkflowRejectedNotificationHandler.ts b/packages/api-notifications-integrations/src/workflows/WorkflowRejectedNotificationHandler.ts new file mode 100644 index 00000000000..efe019f0f63 --- /dev/null +++ b/packages/api-notifications-integrations/src/workflows/WorkflowRejectedNotificationHandler.ts @@ -0,0 +1,42 @@ +import { WorkflowStateRejectHandler } from "@webiny/api-workflows/features/workflowState/RejectWorkflowStateStep/events.js"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { CreateNotificationUseCase } from "@webiny/api-notifications/features/CreateNotification/index.js"; +import { NotificationType } from "@webiny/api-notifications/domain/notification/abstractions.js"; + +/** + * A workflow step was rejected -> notify the person who submitted the content for review. + */ +class WorkflowRejectedNotificationHandlerImpl implements WorkflowStateRejectHandler.Interface { + constructor( + private createNotification: CreateNotificationUseCase.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async handle(event: WorkflowStateRejectHandler.Event) { + const state = event.payload.state; + const recipientId = state.createdBy?.id; + const identity = this.identityContext.getIdentity(); + + if (!recipientId || recipientId === identity.id) { + return; + } + + await this.createNotification.execute({ + recipientId, + type: NotificationType.rejected, + actor: { + id: identity.id, + displayName: identity.displayName, + type: identity.type + }, + title: state.title, + snippet: null, + link: { app: state.app, contentId: state.targetId } + }); + } +} + +export const WorkflowRejectedNotificationHandler = WorkflowStateRejectHandler.createImplementation({ + implementation: WorkflowRejectedNotificationHandlerImpl, + dependencies: [CreateNotificationUseCase, IdentityContext] +}); diff --git a/packages/api-notifications-integrations/src/workflows/feature.ts b/packages/api-notifications-integrations/src/workflows/feature.ts new file mode 100644 index 00000000000..a2be1de7087 --- /dev/null +++ b/packages/api-notifications-integrations/src/workflows/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { WorkflowApprovedNotificationHandler } from "./WorkflowApprovedNotificationHandler.js"; +import { WorkflowRejectedNotificationHandler } from "./WorkflowRejectedNotificationHandler.js"; + +export const WorkflowNotificationsFeature = createFeature({ + name: "NotificationsIntegrations/Workflows", + register(container) { + container.register(WorkflowApprovedNotificationHandler); + container.register(WorkflowRejectedNotificationHandler); + } +}); diff --git a/packages/api-notifications-integrations/tsconfig.build.json b/packages/api-notifications-integrations/tsconfig.build.json new file mode 100644 index 00000000000..af25303fb5c --- /dev/null +++ b/packages/api-notifications-integrations/tsconfig.build.json @@ -0,0 +1,35 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src"], + "references": [ + { "path": "../api-collaboration/tsconfig.build.json" }, + { "path": "../api-core/tsconfig.build.json" }, + { "path": "../api-notifications/tsconfig.build.json" }, + { "path": "../api-workflows/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" }, + { "path": "../handler/tsconfig.build.json" } + ], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-collaboration/*": ["../api-collaboration/src/*"], + "@webiny/api-collaboration": ["../api-collaboration/src"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-notifications/*": ["../api-notifications/src/*"], + "@webiny/api-notifications": ["../api-notifications/src"], + "@webiny/api-workflows/*": ["../api-workflows/src/*"], + "@webiny/api-workflows": ["../api-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"], + "@webiny/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"] + } + } +} diff --git a/packages/api-notifications-integrations/tsconfig.json b/packages/api-notifications-integrations/tsconfig.json new file mode 100644 index 00000000000..60f20b9d81f --- /dev/null +++ b/packages/api-notifications-integrations/tsconfig.json @@ -0,0 +1,35 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../api-collaboration" }, + { "path": "../api-core" }, + { "path": "../api-notifications" }, + { "path": "../api-workflows" }, + { "path": "../feature" }, + { "path": "../handler" } + ], + "compilerOptions": { + "rootDirs": ["./src", "./__tests__"], + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-collaboration/*": ["../api-collaboration/src/*"], + "@webiny/api-collaboration": ["../api-collaboration/src"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-notifications/*": ["../api-notifications/src/*"], + "@webiny/api-notifications": ["../api-notifications/src"], + "@webiny/api-workflows/*": ["../api-workflows/src/*"], + "@webiny/api-workflows": ["../api-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"], + "@webiny/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"] + } + } +} diff --git a/packages/api-notifications-integrations/webiny.config.js b/packages/api-notifications-integrations/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/api-notifications-integrations/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/api-notifications/ci.config.json b/packages/api-notifications/ci.config.json new file mode 100644 index 00000000000..5a6d5348b40 --- /dev/null +++ b/packages/api-notifications/ci.config.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../.github/workflows/ci.config.schema.json", + "vitest": { + "storageOps": ["ddb", "ddb-os,ddb"] + } +} diff --git a/packages/api-notifications/package.json b/packages/api-notifications/package.json new file mode 100644 index 00000000000..919eca506b9 --- /dev/null +++ b/packages/api-notifications/package.json @@ -0,0 +1,43 @@ +{ + "name": "@webiny/api-notifications", + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./index.js", + "./*": "./*" + }, + "description": "Notifications API (in-app notification inbox)", + "keywords": [ + "api-notifications:base" + ], + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git", + "directory": "packages/api-notifications" + }, + "license": "MIT", + "dependencies": { + "@webiny/api-core": "0.0.0", + "@webiny/api-headless-cms": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/handler": "0.0.0", + "@webiny/handler-graphql": "0.0.0", + "@webiny/utils": "0.0.0", + "zod": "4.4.3" + }, + "devDependencies": { + "@webiny/build-tools": "0.0.0", + "@webiny/plugins": "0.0.0", + "@webiny/project-utils": "0.0.0", + "@webiny/testing": "0.0.0", + "type-fest": "^5.8.0", + "typescript": "7.0.2", + "vitest": "^4.1.10" + }, + "publishConfig": { + "access": "public" + }, + "webiny": { + "publishFrom": "dist" + } +} diff --git a/packages/api-notifications/src/constants.ts b/packages/api-notifications/src/constants.ts new file mode 100644 index 00000000000..86227a8aa2a --- /dev/null +++ b/packages/api-notifications/src/constants.ts @@ -0,0 +1 @@ +export const NOTIFICATION_MODEL_ID = "wbyNotification"; diff --git a/packages/api-notifications/src/domain/notification/NotificationMapper.ts b/packages/api-notifications/src/domain/notification/NotificationMapper.ts new file mode 100644 index 00000000000..15375d61939 --- /dev/null +++ b/packages/api-notifications/src/domain/notification/NotificationMapper.ts @@ -0,0 +1,35 @@ +import { parseIdentifier } from "@webiny/utils/parseIdentifier.js"; +import type { CmsEntry } from "@webiny/api-headless-cms/types/index.js"; +import { + NotificationMapper as MapperAbstraction, + NotificationType, + type INotification, + type INotificationValues +} from "./abstractions.js"; + +class NotificationMapperImpl implements MapperAbstraction.Interface { + fromCmsEntry(entry: CmsEntry): INotification { + const { id } = parseIdentifier(entry.id); + const values = entry.values; + + return { + id, + recipientId: values.recipientId, + type: values.type ?? NotificationType.mention, + actor: values.actor, + title: values.title, + snippet: values.snippet ?? null, + link: values.link ?? null, + read: values.read ?? false, + readOn: values.readOn ?? null, + archived: values.archived ?? false, + archivedOn: values.archivedOn ?? null, + createdOn: entry.createdOn + }; + } +} + +export const NotificationMapper = MapperAbstraction.createImplementation({ + implementation: NotificationMapperImpl, + dependencies: [] +}); diff --git a/packages/api-notifications/src/domain/notification/NotificationRepository.ts b/packages/api-notifications/src/domain/notification/NotificationRepository.ts new file mode 100644 index 00000000000..770c693a251 --- /dev/null +++ b/packages/api-notifications/src/domain/notification/NotificationRepository.ts @@ -0,0 +1,167 @@ +import { Result } from "@webiny/feature/api"; +import { createIdentifier, mdbid } from "@webiny/utils"; +import { CmsWhereMapper } from "@webiny/api-headless-cms"; +import { CreateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/CreateEntry/index.js"; +import { ListLatestEntriesUseCase } from "@webiny/api-headless-cms/features/contentEntry/ListEntries/index.js"; +import { GetEntryByIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetEntryById/index.js"; +import { UpdateEntryUseCase } from "@webiny/api-headless-cms/features/contentEntry/UpdateEntry/index.js"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { + NotificationMapper, + NotificationModel, + NotificationRepository as RepositoryAbstraction, + type INotification, + type INotificationValues, + type INotificationWhere +} from "./abstractions.js"; +import { NotificationNotFoundError, NotificationPersistenceError } from "./errors.js"; + +const SORT = ["createdOn_DESC"] as (`${string}_ASC` | `${string}_DESC`)[]; + +class NotificationRepositoryImpl implements RepositoryAbstraction.Interface { + constructor( + private createEntry: CreateEntryUseCase.Interface, + private listLatestEntries: ListLatestEntriesUseCase.Interface, + private getEntryById: GetEntryByIdUseCase.Interface, + private updateEntry: UpdateEntryUseCase.Interface, + private model: NotificationModel.Interface, + private mapper: NotificationMapper.Interface, + private cmsWhereMapper: CmsWhereMapper.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async create(values: INotificationValues) { + try { + const created = await this.identityContext.withoutAuthorization(() => + this.createEntry.execute(this.model, { + id: mdbid(), + values + }) + ); + if (created.isFail()) { + return Result.fail(new NotificationPersistenceError(created.error)); + } + return Result.ok(this.mapper.fromCmsEntry(created.value)); + } catch (error) { + return Result.fail(new NotificationPersistenceError(error as Error)); + } + } + + async list(params: RepositoryAbstraction.ListParams) { + const where = this.cmsWhereMapper.map({ + input: this.buildWhere(params.where), + fields: this.model.fields + }); + + const result = await this.identityContext.withoutAuthorization(() => + this.listLatestEntries.execute(this.model, { + sort: SORT, + limit: params.limit ?? 50, + after: params.after ?? undefined, + where + }) + ); + + if (result.isFail()) { + return Result.fail(new NotificationPersistenceError(result.error)); + } + + return Result.ok({ + items: result.value.entries.map(entry => this.mapper.fromCmsEntry(entry)), + meta: result.value.meta + }); + } + + async count(where: INotificationWhere) { + const mapped = this.cmsWhereMapper.map({ + input: this.buildWhere(where), + fields: this.model.fields + }); + + const result = await this.identityContext.withoutAuthorization(() => + this.listLatestEntries.execute(this.model, { + limit: 1, + where: mapped + }) + ); + + if (result.isFail()) { + return Result.fail(new NotificationPersistenceError(result.error)); + } + + return Result.ok(result.value.meta.totalCount); + } + + async getById(id: string) { + const revisionId = createIdentifier({ id, version: 1 }); + + const result = await this.identityContext.withoutAuthorization(() => + this.getEntryById.execute(this.model, revisionId) + ); + + if (result.isFail()) { + if (result.error.code === "Cms/Entry/NotFound") { + return Result.fail(new NotificationNotFoundError({ id })); + } + return Result.fail(new NotificationPersistenceError(result.error)); + } + + return Result.ok(this.mapper.fromCmsEntry(result.value)); + } + + async save(notification: INotification) { + const revisionId = createIdentifier({ id: notification.id, version: 1 }); + const values: INotificationValues = { + recipientId: notification.recipientId, + type: notification.type, + actor: notification.actor, + title: notification.title, + snippet: notification.snippet ?? null, + link: notification.link ?? null, + read: notification.read, + readOn: notification.readOn ?? null, + archived: notification.archived, + archivedOn: notification.archivedOn ?? null + }; + + try { + const result = await this.identityContext.withoutAuthorization(() => + this.updateEntry.execute(this.model, revisionId, { values }) + ); + if (result.isFail()) { + if (result.error.code === "Cms/Entry/NotFound") { + return Result.fail(new NotificationNotFoundError({ id: notification.id })); + } + return Result.fail(new NotificationPersistenceError(result.error)); + } + return Result.ok(notification); + } catch (error) { + return Result.fail(new NotificationPersistenceError(error as Error)); + } + } + + private buildWhere(where: INotificationWhere): Record { + const input: Record = { recipientId: where.recipientId }; + if (typeof where.archived === "boolean") { + input.archived = where.archived; + } + if (typeof where.read === "boolean") { + input.read = where.read; + } + return input; + } +} + +export const NotificationRepository = RepositoryAbstraction.createImplementation({ + implementation: NotificationRepositoryImpl, + dependencies: [ + CreateEntryUseCase, + ListLatestEntriesUseCase, + GetEntryByIdUseCase, + UpdateEntryUseCase, + NotificationModel, + NotificationMapper, + CmsWhereMapper, + IdentityContext + ] +}); diff --git a/packages/api-notifications/src/domain/notification/abstractions.ts b/packages/api-notifications/src/domain/notification/abstractions.ts new file mode 100644 index 00000000000..c45d218b42f --- /dev/null +++ b/packages/api-notifications/src/domain/notification/abstractions.ts @@ -0,0 +1,107 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { CmsEntry, CmsModel } from "@webiny/api-headless-cms/types"; +import type { IMeta } from "~/types.js"; +import type { NotificationNotFoundError, NotificationPersistenceError } from "./errors.js"; + +export enum NotificationType { + mention = "mention", + reply = "reply", + reviewRequested = "reviewRequested", + approved = "approved", + rejected = "rejected" +} + +export interface NotificationIdentity { + id: string; + displayName: string; + type: string; +} + +/** + * Where the notification points — enough for the admin to deep-link. + */ +export interface NotificationLink { + app?: string | null; + contentType?: string | null; + contentId?: string | null; + locator?: string | null; + threadId?: string | null; +} + +export interface INotificationValues { + recipientId: string; + type: NotificationType; + actor: NotificationIdentity; + title: string; + snippet?: string | null; + link?: NotificationLink | null; + read: boolean; + readOn?: string | null; + archived: boolean; + archivedOn?: string | null; +} + +export interface INotification extends INotificationValues { + id: string; + createdOn: string; +} + +export interface INotificationWhere { + recipientId: string; + archived?: boolean; + read?: boolean; +} + +export interface INotificationListParams { + where: INotificationWhere; + limit?: number; + after?: string | null; +} + +export interface INotificationListResult { + items: INotification[]; + meta: IMeta; +} + +/** + * Persistence for notifications. All operations run without CMS authorization (notifications are + * system-managed rows); access control is enforced by the use cases via `recipientId`. + */ +export interface INotificationRepository { + create( + values: INotificationValues + ): Promise>; + list( + params: INotificationListParams + ): Promise>; + count(where: INotificationWhere): Promise>; + getById( + id: string + ): Promise>; + save( + notification: INotification + ): Promise>; +} + +export const NotificationModel = createAbstraction("NotificationModel"); +export namespace NotificationModel { + export type Interface = CmsModel; +} + +export interface INotificationMapper { + fromCmsEntry(entry: CmsEntry): INotification; +} +export const NotificationMapper = createAbstraction("NotificationMapper"); +export namespace NotificationMapper { + export type Interface = INotificationMapper; +} + +export const NotificationRepository = + createAbstraction("NotificationRepository"); +export namespace NotificationRepository { + export type Interface = INotificationRepository; + export type Where = INotificationWhere; + export type ListParams = INotificationListParams; + export type ListResult = INotificationListResult; +} diff --git a/packages/api-notifications/src/domain/notification/errors.ts b/packages/api-notifications/src/domain/notification/errors.ts new file mode 100644 index 00000000000..fe06b8d6594 --- /dev/null +++ b/packages/api-notifications/src/domain/notification/errors.ts @@ -0,0 +1,32 @@ +import { BaseError } from "@webiny/feature/api"; + +export class NotificationNotFoundError extends BaseError<{ id: string }> { + override readonly code = "Notifications/NotFound" as const; + + constructor(data: { id: string }) { + super({ + message: `Notification with id "${data.id}" was not found!`, + data + }); + } +} + +export class NotificationNotAuthorizedError extends BaseError { + override readonly code = "Notifications/NotAuthorized" as const; + + constructor(message?: string) { + super({ + message: message || "Not authorized to access this notification." + }); + } +} + +export class NotificationPersistenceError extends BaseError { + override readonly code = "Notifications/Persistence" as const; + + constructor(error: Error) { + super({ + message: error.message + }); + } +} diff --git a/packages/api-notifications/src/domain/notification/notificationModel.ts b/packages/api-notifications/src/domain/notification/notificationModel.ts new file mode 100644 index 00000000000..cb4c5395665 --- /dev/null +++ b/packages/api-notifications/src/domain/notification/notificationModel.ts @@ -0,0 +1,54 @@ +import { ModelFactory } from "@webiny/api-headless-cms/features/modelBuilder/index.js"; +import { NotificationType } from "./abstractions.js"; +import { NOTIFICATION_MODEL_ID } from "~/constants.js"; + +const types = [ + { label: "Mention", value: NotificationType.mention }, + { label: "Reply", value: NotificationType.reply }, + { label: "Review requested", value: NotificationType.reviewRequested }, + { label: "Approved", value: NotificationType.approved }, + { label: "Rejected", value: NotificationType.rejected } +]; + +class NotificationModelImpl implements ModelFactory.Interface { + public async execute(builder: ModelFactory.Builder) { + return [ + builder + .private({ + modelId: NOTIFICATION_MODEL_ID, + name: "Notification" + }) + .fields(fields => ({ + recipientId: fields + .text() + .label("Recipient ID") + .required("Recipient ID is required."), + type: fields + .text() + .label("Type") + .required("Type is required.") + .predefinedValues(types), + actor: fields + .object() + .label("Actor") + .fields(actorFields => ({ + id: actorFields.text().label("ID").required("ID is required."), + displayName: actorFields.text().label("Display Name"), + type: actorFields.text().label("Type") + })), + title: fields.text().label("Title"), + snippet: fields.text().label("Snippet"), + link: fields.json().label("Link"), + read: fields.boolean().label("Read"), + readOn: fields.text().label("Read On"), + archived: fields.boolean().label("Archived"), + archivedOn: fields.text().label("Archived On") + })) + ]; + } +} + +export const NotificationModel = ModelFactory.createImplementation({ + implementation: NotificationModelImpl, + dependencies: [] +}); diff --git a/packages/api-notifications/src/features/ArchiveNotifications/ArchiveNotificationUseCase.ts b/packages/api-notifications/src/features/ArchiveNotifications/ArchiveNotificationUseCase.ts new file mode 100644 index 00000000000..23f3554515c --- /dev/null +++ b/packages/api-notifications/src/features/ArchiveNotifications/ArchiveNotificationUseCase.ts @@ -0,0 +1,34 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { ArchiveNotificationUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; +import { NotificationNotAuthorizedError } from "~/domain/notification/errors.js"; + +class ArchiveNotificationUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute(id: string) { + const result = await this.repository.getById(id); + if (result.isFail()) { + return Result.fail(result.error); + } + + const notification = result.value; + if (notification.recipientId !== this.identityContext.getIdentity().id) { + return Result.fail(new NotificationNotAuthorizedError()); + } + + notification.archived = true; + notification.archivedOn = new Date().toISOString(); + + return this.repository.save(notification); + } +} + +export const ArchiveNotificationUseCase = UseCase.createImplementation({ + implementation: ArchiveNotificationUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/ArchiveNotifications/UnarchiveNotificationUseCase.ts b/packages/api-notifications/src/features/ArchiveNotifications/UnarchiveNotificationUseCase.ts new file mode 100644 index 00000000000..dbf1ad3e1f3 --- /dev/null +++ b/packages/api-notifications/src/features/ArchiveNotifications/UnarchiveNotificationUseCase.ts @@ -0,0 +1,34 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { UnarchiveNotificationUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; +import { NotificationNotAuthorizedError } from "~/domain/notification/errors.js"; + +class UnarchiveNotificationUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute(id: string) { + const result = await this.repository.getById(id); + if (result.isFail()) { + return Result.fail(result.error); + } + + const notification = result.value; + if (notification.recipientId !== this.identityContext.getIdentity().id) { + return Result.fail(new NotificationNotAuthorizedError()); + } + + notification.archived = false; + notification.archivedOn = null; + + return this.repository.save(notification); + } +} + +export const UnarchiveNotificationUseCase = UseCase.createImplementation({ + implementation: UnarchiveNotificationUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/ArchiveNotifications/abstractions.ts b/packages/api-notifications/src/features/ArchiveNotifications/abstractions.ts new file mode 100644 index 00000000000..3644a1191a0 --- /dev/null +++ b/packages/api-notifications/src/features/ArchiveNotifications/abstractions.ts @@ -0,0 +1,39 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { INotification } from "~/domain/notification/abstractions.js"; +import type { + NotificationNotAuthorizedError, + NotificationNotFoundError, + NotificationPersistenceError +} from "~/domain/notification/errors.js"; + +export type ArchiveNotificationError = + | NotificationNotFoundError + | NotificationNotAuthorizedError + | NotificationPersistenceError; + +export interface IArchiveNotificationUseCase { + execute(id: string): Promise>; +} + +export const ArchiveNotificationUseCase = createAbstraction( + "ArchiveNotificationUseCase" +); + +export namespace ArchiveNotificationUseCase { + export type Interface = IArchiveNotificationUseCase; + export type Error = ArchiveNotificationError; +} + +export interface IUnarchiveNotificationUseCase { + execute(id: string): Promise>; +} + +export const UnarchiveNotificationUseCase = createAbstraction( + "UnarchiveNotificationUseCase" +); + +export namespace UnarchiveNotificationUseCase { + export type Interface = IUnarchiveNotificationUseCase; + export type Error = ArchiveNotificationError; +} diff --git a/packages/api-notifications/src/features/ArchiveNotifications/feature.ts b/packages/api-notifications/src/features/ArchiveNotifications/feature.ts new file mode 100644 index 00000000000..d974a3e94a7 --- /dev/null +++ b/packages/api-notifications/src/features/ArchiveNotifications/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { ArchiveNotificationUseCase } from "./ArchiveNotificationUseCase.js"; +import { UnarchiveNotificationUseCase } from "./UnarchiveNotificationUseCase.js"; + +export const ArchiveNotificationsFeature = createFeature({ + name: "Notifications/ArchiveNotifications", + register(container) { + container.register(ArchiveNotificationUseCase); + container.register(UnarchiveNotificationUseCase); + } +}); diff --git a/packages/api-notifications/src/features/ArchiveNotifications/index.ts b/packages/api-notifications/src/features/ArchiveNotifications/index.ts new file mode 100644 index 00000000000..354125f8460 --- /dev/null +++ b/packages/api-notifications/src/features/ArchiveNotifications/index.ts @@ -0,0 +1 @@ +export { ArchiveNotificationUseCase, UnarchiveNotificationUseCase } from "./abstractions.js"; diff --git a/packages/api-notifications/src/features/CreateNotification/CreateNotificationUseCase.ts b/packages/api-notifications/src/features/CreateNotification/CreateNotificationUseCase.ts new file mode 100644 index 00000000000..2b7e60b3881 --- /dev/null +++ b/packages/api-notifications/src/features/CreateNotification/CreateNotificationUseCase.ts @@ -0,0 +1,24 @@ +import { CreateNotificationUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; + +class CreateNotificationUseCaseImpl implements UseCase.Interface { + constructor(private repository: NotificationRepository.Interface) {} + + async execute(input: UseCase.Input) { + return this.repository.create({ + recipientId: input.recipientId, + type: input.type, + actor: input.actor, + title: input.title, + snippet: input.snippet ?? null, + link: input.link ?? null, + read: false, + archived: false + }); + } +} + +export const CreateNotificationUseCase = UseCase.createImplementation({ + implementation: CreateNotificationUseCaseImpl, + dependencies: [NotificationRepository] +}); diff --git a/packages/api-notifications/src/features/CreateNotification/abstractions.ts b/packages/api-notifications/src/features/CreateNotification/abstractions.ts new file mode 100644 index 00000000000..119e2d5e2be --- /dev/null +++ b/packages/api-notifications/src/features/CreateNotification/abstractions.ts @@ -0,0 +1,33 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { + INotification, + NotificationIdentity, + NotificationLink, + NotificationType +} from "~/domain/notification/abstractions.js"; +import type { NotificationPersistenceError } from "~/domain/notification/errors.js"; + +export interface ICreateNotificationInput { + recipientId: string; + type: NotificationType; + actor: NotificationIdentity; + title: string; + snippet?: string | null; + link?: NotificationLink | null; +} + +export interface ICreateNotificationUseCase { + execute( + input: ICreateNotificationInput + ): Promise>; +} + +export const CreateNotificationUseCase = createAbstraction( + "CreateNotificationUseCase" +); + +export namespace CreateNotificationUseCase { + export type Interface = ICreateNotificationUseCase; + export type Input = ICreateNotificationInput; +} diff --git a/packages/api-notifications/src/features/CreateNotification/feature.ts b/packages/api-notifications/src/features/CreateNotification/feature.ts new file mode 100644 index 00000000000..a05e060e1bc --- /dev/null +++ b/packages/api-notifications/src/features/CreateNotification/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { CreateNotificationUseCase } from "./CreateNotificationUseCase.js"; + +export const CreateNotificationFeature = createFeature({ + name: "Notifications/CreateNotification", + register(container) { + container.register(CreateNotificationUseCase); + } +}); diff --git a/packages/api-notifications/src/features/CreateNotification/index.ts b/packages/api-notifications/src/features/CreateNotification/index.ts new file mode 100644 index 00000000000..f56ff93426a --- /dev/null +++ b/packages/api-notifications/src/features/CreateNotification/index.ts @@ -0,0 +1 @@ +export { CreateNotificationUseCase } from "./abstractions.js"; diff --git a/packages/api-notifications/src/features/ListNotifications/ListNotificationsUseCase.ts b/packages/api-notifications/src/features/ListNotifications/ListNotificationsUseCase.ts new file mode 100644 index 00000000000..a6b805dea15 --- /dev/null +++ b/packages/api-notifications/src/features/ListNotifications/ListNotificationsUseCase.ts @@ -0,0 +1,29 @@ +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { ListNotificationsUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; + +class ListNotificationsUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute(params: UseCase.Params) { + const recipientId = this.identityContext.getIdentity().id; + + return this.repository.list({ + where: { + recipientId, + archived: params.archived, + read: params.read + }, + limit: params.limit, + after: params.after + }); + } +} + +export const ListNotificationsUseCase = UseCase.createImplementation({ + implementation: ListNotificationsUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/ListNotifications/abstractions.ts b/packages/api-notifications/src/features/ListNotifications/abstractions.ts new file mode 100644 index 00000000000..1b6b0511092 --- /dev/null +++ b/packages/api-notifications/src/features/ListNotifications/abstractions.ts @@ -0,0 +1,26 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { INotificationListResult } from "~/domain/notification/abstractions.js"; +import type { NotificationPersistenceError } from "~/domain/notification/errors.js"; + +export interface IListNotificationsParams { + archived?: boolean; + read?: boolean; + limit?: number; + after?: string | null; +} + +export interface IListNotificationsUseCase { + execute( + params: IListNotificationsParams + ): Promise>; +} + +export const ListNotificationsUseCase = createAbstraction( + "ListNotificationsUseCase" +); + +export namespace ListNotificationsUseCase { + export type Interface = IListNotificationsUseCase; + export type Params = IListNotificationsParams; +} diff --git a/packages/api-notifications/src/features/ListNotifications/feature.ts b/packages/api-notifications/src/features/ListNotifications/feature.ts new file mode 100644 index 00000000000..3550c428617 --- /dev/null +++ b/packages/api-notifications/src/features/ListNotifications/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { ListNotificationsUseCase } from "./ListNotificationsUseCase.js"; + +export const ListNotificationsFeature = createFeature({ + name: "Notifications/ListNotifications", + register(container) { + container.register(ListNotificationsUseCase); + } +}); diff --git a/packages/api-notifications/src/features/ListNotifications/index.ts b/packages/api-notifications/src/features/ListNotifications/index.ts new file mode 100644 index 00000000000..065fa0577ce --- /dev/null +++ b/packages/api-notifications/src/features/ListNotifications/index.ts @@ -0,0 +1 @@ +export { ListNotificationsUseCase } from "./abstractions.js"; diff --git a/packages/api-notifications/src/features/MarkNotifications/MarkAllNotificationsReadUseCase.ts b/packages/api-notifications/src/features/MarkNotifications/MarkAllNotificationsReadUseCase.ts new file mode 100644 index 00000000000..b972c18dae7 --- /dev/null +++ b/packages/api-notifications/src/features/MarkNotifications/MarkAllNotificationsReadUseCase.ts @@ -0,0 +1,43 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { MarkAllNotificationsReadUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; + +class MarkAllNotificationsReadUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute() { + const recipientId = this.identityContext.getIdentity().id; + + const listResult = await this.repository.list({ + where: { recipientId, archived: false, read: false }, + limit: 200 + }); + if (listResult.isFail()) { + return Result.fail(listResult.error); + } + + const now = new Date().toISOString(); + let updated = 0; + + for (const notification of listResult.value.items) { + notification.read = true; + notification.readOn = now; + const saveResult = await this.repository.save(notification); + if (saveResult.isFail()) { + return Result.fail(saveResult.error); + } + updated++; + } + + return Result.ok(updated); + } +} + +export const MarkAllNotificationsReadUseCase = UseCase.createImplementation({ + implementation: MarkAllNotificationsReadUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/MarkNotifications/MarkNotificationReadUseCase.ts b/packages/api-notifications/src/features/MarkNotifications/MarkNotificationReadUseCase.ts new file mode 100644 index 00000000000..6c475d9d73e --- /dev/null +++ b/packages/api-notifications/src/features/MarkNotifications/MarkNotificationReadUseCase.ts @@ -0,0 +1,38 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { MarkNotificationReadUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; +import { NotificationNotAuthorizedError } from "~/domain/notification/errors.js"; + +class MarkNotificationReadUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute(id: string) { + const result = await this.repository.getById(id); + if (result.isFail()) { + return Result.fail(result.error); + } + + const notification = result.value; + if (notification.recipientId !== this.identityContext.getIdentity().id) { + return Result.fail(new NotificationNotAuthorizedError()); + } + + if (notification.read) { + return Result.ok(notification); + } + + notification.read = true; + notification.readOn = new Date().toISOString(); + + return this.repository.save(notification); + } +} + +export const MarkNotificationReadUseCase = UseCase.createImplementation({ + implementation: MarkNotificationReadUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/MarkNotifications/abstractions.ts b/packages/api-notifications/src/features/MarkNotifications/abstractions.ts new file mode 100644 index 00000000000..da065ee4dbf --- /dev/null +++ b/packages/api-notifications/src/features/MarkNotifications/abstractions.ts @@ -0,0 +1,38 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { INotification } from "~/domain/notification/abstractions.js"; +import type { + NotificationNotAuthorizedError, + NotificationNotFoundError, + NotificationPersistenceError +} from "~/domain/notification/errors.js"; + +export type MarkNotificationError = + | NotificationNotFoundError + | NotificationNotAuthorizedError + | NotificationPersistenceError; + +export interface IMarkNotificationReadUseCase { + execute(id: string): Promise>; +} + +export const MarkNotificationReadUseCase = createAbstraction( + "MarkNotificationReadUseCase" +); + +export namespace MarkNotificationReadUseCase { + export type Interface = IMarkNotificationReadUseCase; + export type Error = MarkNotificationError; +} + +export interface IMarkAllNotificationsReadUseCase { + execute(): Promise>; +} + +export const MarkAllNotificationsReadUseCase = createAbstraction( + "MarkAllNotificationsReadUseCase" +); + +export namespace MarkAllNotificationsReadUseCase { + export type Interface = IMarkAllNotificationsReadUseCase; +} diff --git a/packages/api-notifications/src/features/MarkNotifications/feature.ts b/packages/api-notifications/src/features/MarkNotifications/feature.ts new file mode 100644 index 00000000000..c8f1c6b01a5 --- /dev/null +++ b/packages/api-notifications/src/features/MarkNotifications/feature.ts @@ -0,0 +1,11 @@ +import { createFeature } from "@webiny/feature/api"; +import { MarkNotificationReadUseCase } from "./MarkNotificationReadUseCase.js"; +import { MarkAllNotificationsReadUseCase } from "./MarkAllNotificationsReadUseCase.js"; + +export const MarkNotificationsFeature = createFeature({ + name: "Notifications/MarkNotifications", + register(container) { + container.register(MarkNotificationReadUseCase); + container.register(MarkAllNotificationsReadUseCase); + } +}); diff --git a/packages/api-notifications/src/features/MarkNotifications/index.ts b/packages/api-notifications/src/features/MarkNotifications/index.ts new file mode 100644 index 00000000000..f1190c0108c --- /dev/null +++ b/packages/api-notifications/src/features/MarkNotifications/index.ts @@ -0,0 +1 @@ +export { MarkNotificationReadUseCase, MarkAllNotificationsReadUseCase } from "./abstractions.js"; diff --git a/packages/api-notifications/src/features/NotificationCounts/NotificationCountsUseCase.ts b/packages/api-notifications/src/features/NotificationCounts/NotificationCountsUseCase.ts new file mode 100644 index 00000000000..ffdffaa0ef8 --- /dev/null +++ b/packages/api-notifications/src/features/NotificationCounts/NotificationCountsUseCase.ts @@ -0,0 +1,42 @@ +import { Result } from "@webiny/feature/api"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { NotificationCountsUseCase as UseCase } from "./abstractions.js"; +import { NotificationRepository } from "~/domain/notification/abstractions.js"; + +class NotificationCountsUseCaseImpl implements UseCase.Interface { + constructor( + private repository: NotificationRepository.Interface, + private identityContext: IdentityContext.Interface + ) {} + + async execute() { + const recipientId = this.identityContext.getIdentity().id; + + const [inbox, archive, unread] = await Promise.all([ + this.repository.count({ recipientId, archived: false }), + this.repository.count({ recipientId, archived: true }), + this.repository.count({ recipientId, archived: false, read: false }) + ]); + + if (inbox.isFail()) { + return Result.fail(inbox.error); + } + if (archive.isFail()) { + return Result.fail(archive.error); + } + if (unread.isFail()) { + return Result.fail(unread.error); + } + + return Result.ok({ + inbox: inbox.value, + archive: archive.value, + unread: unread.value + }); + } +} + +export const NotificationCountsUseCase = UseCase.createImplementation({ + implementation: NotificationCountsUseCaseImpl, + dependencies: [NotificationRepository, IdentityContext] +}); diff --git a/packages/api-notifications/src/features/NotificationCounts/abstractions.ts b/packages/api-notifications/src/features/NotificationCounts/abstractions.ts new file mode 100644 index 00000000000..039cc4b2666 --- /dev/null +++ b/packages/api-notifications/src/features/NotificationCounts/abstractions.ts @@ -0,0 +1,22 @@ +import type { Result } from "@webiny/feature/api"; +import { createAbstraction } from "@webiny/feature/api"; +import type { NotificationPersistenceError } from "~/domain/notification/errors.js"; + +export interface INotificationCounts { + inbox: number; + archive: number; + unread: number; +} + +export interface INotificationCountsUseCase { + execute(): Promise>; +} + +export const NotificationCountsUseCase = createAbstraction( + "NotificationCountsUseCase" +); + +export namespace NotificationCountsUseCase { + export type Interface = INotificationCountsUseCase; + export type Counts = INotificationCounts; +} diff --git a/packages/api-notifications/src/features/NotificationCounts/feature.ts b/packages/api-notifications/src/features/NotificationCounts/feature.ts new file mode 100644 index 00000000000..93403b42b5f --- /dev/null +++ b/packages/api-notifications/src/features/NotificationCounts/feature.ts @@ -0,0 +1,9 @@ +import { createFeature } from "@webiny/feature/api"; +import { NotificationCountsUseCase } from "./NotificationCountsUseCase.js"; + +export const NotificationCountsFeature = createFeature({ + name: "Notifications/NotificationCounts", + register(container) { + container.register(NotificationCountsUseCase); + } +}); diff --git a/packages/api-notifications/src/features/NotificationCounts/index.ts b/packages/api-notifications/src/features/NotificationCounts/index.ts new file mode 100644 index 00000000000..d24a48813fd --- /dev/null +++ b/packages/api-notifications/src/features/NotificationCounts/index.ts @@ -0,0 +1 @@ +export { NotificationCountsUseCase } from "./abstractions.js"; diff --git a/packages/api-notifications/src/graphql/notifications.ts b/packages/api-notifications/src/graphql/notifications.ts new file mode 100644 index 00000000000..6e734cba0de --- /dev/null +++ b/packages/api-notifications/src/graphql/notifications.ts @@ -0,0 +1,282 @@ +import { GraphQLSchemaFactory } from "@webiny/handler-graphql/graphql/abstractions.js"; +import { NotFoundError, resolve, resolveList } from "@webiny/handler-graphql"; +import { createZodError } from "@webiny/utils"; +import { ListNotificationsUseCase } from "~/features/ListNotifications/index.js"; +import { NotificationCountsUseCase } from "~/features/NotificationCounts/index.js"; +import { + MarkAllNotificationsReadUseCase, + MarkNotificationReadUseCase +} from "~/features/MarkNotifications/index.js"; +import { + ArchiveNotificationUseCase, + UnarchiveNotificationUseCase +} from "~/features/ArchiveNotifications/index.js"; +import { listNotificationsValidation, idValidation } from "./validation.js"; + +interface IIdArgs { + id: string; +} + +interface IListNotificationsArgs { + where?: { + archived?: boolean; + read?: boolean; + }; + limit?: number; + after?: string; +} + +class NotificationsSchema_ implements GraphQLSchemaFactory.Interface { + async execute( + builder: GraphQLSchemaFactory.SchemaBuilder + ): Promise { + builder.addTypeDefs(/* GraphQL */ ` + type NotificationError { + code: String + message: String + data: JSON + stack: String + } + + enum NotificationTypeValue { + mention + reply + reviewRequested + approved + rejected + } + + type NotificationActor { + id: String! + displayName: String + type: String + } + + type NotificationLink { + app: String + contentType: String + contentId: String + locator: String + threadId: String + } + + type Notification { + id: ID! + type: NotificationTypeValue! + actor: NotificationActor! + title: String + snippet: String + link: NotificationLink + read: Boolean! + readOn: String + archived: Boolean! + archivedOn: String + createdOn: String! + } + + type NotificationCounts { + inbox: Int! + archive: Int! + unread: Int! + } + + type NotificationListMeta { + cursor: String + hasMoreItems: Boolean! + totalCount: Int! + } + + type ListNotificationsResponse { + data: [Notification!] + meta: NotificationListMeta + error: NotificationError + } + + type NotificationResponse { + data: Notification + error: NotificationError + } + + type NotificationCountsResponse { + data: NotificationCounts + error: NotificationError + } + + type NotificationCountResponse { + data: Int + error: NotificationError + } + + input ListNotificationsWhereInput { + archived: Boolean + read: Boolean + } + + type NotificationsQuery { + listNotifications( + where: ListNotificationsWhereInput + limit: Int + after: String + ): ListNotificationsResponse! + notificationCounts: NotificationCountsResponse! + } + + type NotificationsMutation { + markNotificationRead(id: ID!): NotificationResponse! + markAllNotificationsRead: NotificationCountResponse! + archiveNotification(id: ID!): NotificationResponse! + unarchiveNotification(id: ID!): NotificationResponse! + } + + extend type Query { + notifications: NotificationsQuery + } + + extend type Mutation { + notifications: NotificationsMutation + } + `); + + builder.addResolver({ + path: "Query.notifications", + dependencies: [], + resolver: () => () => ({}) + }); + + builder.addResolver({ + path: "Mutation.notifications", + dependencies: [], + resolver: () => () => ({}) + }); + + builder.addResolver({ + path: "NotificationsQuery.listNotifications", + dependencies: [ListNotificationsUseCase], + resolver: (listNotifications: ListNotificationsUseCase.Interface) => { + return async ({ args }) => { + return resolveList(async () => { + const result = await listNotificationsValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + + const listResult = await listNotifications.execute({ + archived: result.data.where?.archived, + read: result.data.where?.read, + limit: result.data.limit, + after: result.data.after + }); + + if (listResult.isFail()) { + throw listResult.error; + } + + return listResult.value; + }); + }; + } + }); + + builder.addResolver({ + path: "NotificationsQuery.notificationCounts", + dependencies: [NotificationCountsUseCase], + resolver: (counts: NotificationCountsUseCase.Interface) => { + return async () => { + return resolve(async () => { + const result = await counts.execute(); + if (result.isFail()) { + throw result.error; + } + return result.value; + }); + }; + } + }); + + builder.addResolver({ + path: "NotificationsMutation.markNotificationRead", + dependencies: [MarkNotificationReadUseCase], + resolver: (markRead: MarkNotificationReadUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + const markResult = await markRead.execute(result.data.id); + if (markResult.isFail()) { + if (markResult.error.code === "Notifications/NotFound") { + throw new NotFoundError(markResult.error.message); + } + throw markResult.error; + } + return markResult.value; + }); + }; + } + }); + + builder.addResolver({ + path: "NotificationsMutation.markAllNotificationsRead", + dependencies: [MarkAllNotificationsReadUseCase], + resolver: (markAll: MarkAllNotificationsReadUseCase.Interface) => { + return async () => { + return resolve(async () => { + const result = await markAll.execute(); + if (result.isFail()) { + throw result.error; + } + return result.value; + }); + }; + } + }); + + builder.addResolver({ + path: "NotificationsMutation.archiveNotification", + dependencies: [ArchiveNotificationUseCase], + resolver: (archive: ArchiveNotificationUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + const archiveResult = await archive.execute(result.data.id); + if (archiveResult.isFail()) { + throw archiveResult.error; + } + return archiveResult.value; + }); + }; + } + }); + + builder.addResolver({ + path: "NotificationsMutation.unarchiveNotification", + dependencies: [UnarchiveNotificationUseCase], + resolver: (unarchive: UnarchiveNotificationUseCase.Interface) => { + return async ({ args }) => { + return resolve(async () => { + const result = await idValidation.safeParseAsync(args); + if (!result.success) { + throw createZodError(result.error); + } + const unarchiveResult = await unarchive.execute(result.data.id); + if (unarchiveResult.isFail()) { + throw unarchiveResult.error; + } + return unarchiveResult.value; + }); + }; + } + }); + + return builder; + } +} + +export const NotificationsSchema = GraphQLSchemaFactory.createImplementation({ + implementation: NotificationsSchema_, + dependencies: [] +}); diff --git a/packages/api-notifications/src/graphql/validation.ts b/packages/api-notifications/src/graphql/validation.ts new file mode 100644 index 00000000000..59211c7f95d --- /dev/null +++ b/packages/api-notifications/src/graphql/validation.ts @@ -0,0 +1,16 @@ +import zod from "zod"; + +export const listNotificationsValidation = zod.object({ + where: zod + .object({ + archived: zod.boolean().optional(), + read: zod.boolean().optional() + }) + .optional(), + limit: zod.number().optional(), + after: zod.string().nullish() +}); + +export const idValidation = zod.object({ + id: zod.string().min(1, "ID is required.") +}); diff --git a/packages/api-notifications/src/index.ts b/packages/api-notifications/src/index.ts new file mode 100644 index 00000000000..6077a3ff8f0 --- /dev/null +++ b/packages/api-notifications/src/index.ts @@ -0,0 +1,54 @@ +import { ContextPlugin, createRegisterExtensionPlugin } from "@webiny/handler"; +import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js"; +import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js"; +import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js"; +import { NOTIFICATION_MODEL_ID } from "./constants.js"; +import { NotificationModel as NotificationPrivateModel } from "./domain/notification/notificationModel.js"; +import { NotificationModel } from "./domain/notification/abstractions.js"; +import { NotificationMapper } from "./domain/notification/NotificationMapper.js"; +import { NotificationRepository } from "./domain/notification/NotificationRepository.js"; +import { CreateNotificationFeature } from "./features/CreateNotification/feature.js"; +import { ListNotificationsFeature } from "./features/ListNotifications/feature.js"; +import { NotificationCountsFeature } from "./features/NotificationCounts/feature.js"; +import { MarkNotificationsFeature } from "./features/MarkNotifications/feature.js"; +import { ArchiveNotificationsFeature } from "./features/ArchiveNotifications/feature.js"; +import { NotificationsSchema } from "./graphql/notifications.js"; + +export const createNotifications = () => { + const modelsPlugin = createRegisterExtensionPlugin(context => { + context.container.register(NotificationPrivateModel); + }); + + const notificationsContextPlugin = new ContextPlugin(async context => { + const tenantContext = context.container.resolve(TenantContext); + const identityContext = context.container.resolve(IdentityContext); + + if (!tenantContext.getTenant()) { + return; + } + + context.container.register(NotificationPrivateModel); + + const getModel = context.container.resolve(GetModelUseCase); + + await identityContext.withoutAuthorization(async () => { + const model = await getModel.execute(NOTIFICATION_MODEL_ID); + context.container.registerInstance(NotificationModel, model.value); + }); + + context.container.register(NotificationMapper); + context.container.register(NotificationRepository).inSingletonScope(); + + CreateNotificationFeature.register(context.container); + ListNotificationsFeature.register(context.container); + NotificationCountsFeature.register(context.container); + MarkNotificationsFeature.register(context.container); + ArchiveNotificationsFeature.register(context.container); + + context.container.register(NotificationsSchema); + }); + + notificationsContextPlugin.name = "notifications.context"; + + return [notificationsContextPlugin, modelsPlugin]; +}; diff --git a/packages/api-notifications/src/types.ts b/packages/api-notifications/src/types.ts new file mode 100644 index 00000000000..8e17912c707 --- /dev/null +++ b/packages/api-notifications/src/types.ts @@ -0,0 +1,5 @@ +export interface IMeta { + totalCount: number; + hasMoreItems: boolean; + cursor: string | null; +} diff --git a/packages/api-notifications/tsconfig.build.json b/packages/api-notifications/tsconfig.build.json new file mode 100644 index 00000000000..db6398483f9 --- /dev/null +++ b/packages/api-notifications/tsconfig.build.json @@ -0,0 +1,41 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src"], + "references": [ + { "path": "../api-core/tsconfig.build.json" }, + { "path": "../api-headless-cms/tsconfig.build.json" }, + { "path": "../feature/tsconfig.build.json" }, + { "path": "../handler/tsconfig.build.json" }, + { "path": "../handler-graphql/tsconfig.build.json" }, + { "path": "../utils/tsconfig.build.json" }, + { "path": "../plugins/tsconfig.build.json" }, + { "path": "../testing/tsconfig.build.json" } + ], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], + "@webiny/api-headless-cms": ["../api-headless-cms/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/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"], + "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], + "@webiny/handler-graphql": ["../handler-graphql/src"], + "@webiny/utils/*": ["../utils/src/*"], + "@webiny/utils": ["../utils/src"], + "@webiny/plugins/*": ["../plugins/src/*"], + "@webiny/plugins": ["../plugins/src"], + "@webiny/testing/*": ["../testing/src/*"], + "@webiny/testing": ["../testing/src"] + } + } +} diff --git a/packages/api-notifications/tsconfig.json b/packages/api-notifications/tsconfig.json new file mode 100644 index 00000000000..c9e87513e0a --- /dev/null +++ b/packages/api-notifications/tsconfig.json @@ -0,0 +1,41 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../api-core" }, + { "path": "../api-headless-cms" }, + { "path": "../feature" }, + { "path": "../handler" }, + { "path": "../handler-graphql" }, + { "path": "../utils" }, + { "path": "../plugins" }, + { "path": "../testing" } + ], + "compilerOptions": { + "rootDirs": ["./src", "./__tests__"], + "outDir": "./dist", + "declarationDir": "./dist", + "paths": { + "~/*": ["./src/*"], + "~tests/*": ["./__tests__/*"], + "@webiny/api-core/*": ["../api-core/src/*"], + "@webiny/api-core": ["../api-core/src"], + "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], + "@webiny/api-headless-cms": ["../api-headless-cms/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/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"], + "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], + "@webiny/handler-graphql": ["../handler-graphql/src"], + "@webiny/utils/*": ["../utils/src/*"], + "@webiny/utils": ["../utils/src"], + "@webiny/plugins/*": ["../plugins/src/*"], + "@webiny/plugins": ["../plugins/src"], + "@webiny/testing/*": ["../testing/src/*"], + "@webiny/testing": ["../testing/src"] + } + } +} diff --git a/packages/api-notifications/vitest.config.ts b/packages/api-notifications/vitest.config.ts new file mode 100644 index 00000000000..7bd5483d8d4 --- /dev/null +++ b/packages/api-notifications/vitest.config.ts @@ -0,0 +1,32 @@ +import { resolve } from "path"; +import { createTestConfig } from "../../testing"; + +/** + * Loads storage-operations presets directly, bypassing the workspace scan (which can fail on + * stray local package dirs without a package.json). + */ +const loadPresetsFromPackages = async (...pkgs: string[]) => { + const all: unknown[] = []; + for (const pkg of pkgs) { + const presetsPath = resolve(process.cwd(), "packages", pkg, "__tests__/__api__/presets.js"); + const mod = await import(presetsPath); + all.push(...(mod.default ?? mod)); + } + return all; +}; + +export default async () => { + let presets; + try { + const { getPresets } = await import("@webiny/project-utils/testing/presets/index.js"); + presets = await getPresets( + ["@webiny/api-headless-cms", "storage-operations"], + ["@webiny/api-core", "storage-operations"] + ); + } catch { + process.env.WEBINY_STORAGE_OPS = process.env.WEBINY_STORAGE_OPS || "ddb"; + presets = await loadPresetsFromPackages("api-core-ddb", "api-headless-cms-ddb"); + } + + return createTestConfig({ path: import.meta.dirname, presets }); +}; diff --git a/packages/api-notifications/webiny.config.js b/packages/api-notifications/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/api-notifications/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-admin/src/features/formModel/Field.ts b/packages/app-admin/src/features/formModel/Field.ts index 08484bd290e..296855c58e6 100644 --- a/packages/app-admin/src/features/formModel/Field.ts +++ b/packages/app-admin/src/features/formModel/Field.ts @@ -402,6 +402,7 @@ export class Field implements IField { return { name: this.config.name, + qualifiedName: this._qualifiedName, type: this.config.type, label: this.config.label, help: this.config.help, diff --git a/packages/app-admin/src/features/formModel/ObjectField.ts b/packages/app-admin/src/features/formModel/ObjectField.ts index 1bf23479df3..d51257e0093 100644 --- a/packages/app-admin/src/features/formModel/ObjectField.ts +++ b/packages/app-admin/src/features/formModel/ObjectField.ts @@ -764,6 +764,7 @@ export class ObjectField implements IObjectField { const baseVm = this._base.vm; return { name: baseVm.name, + qualifiedName: baseVm.qualifiedName, type: "object", label: baseVm.label, help: baseVm.help, diff --git a/packages/app-admin/src/features/formModel/abstractions.ts b/packages/app-admin/src/features/formModel/abstractions.ts index 25db8793d10..6bd9586a3ab 100644 --- a/packages/app-admin/src/features/formModel/abstractions.ts +++ b/packages/app-admin/src/features/formModel/abstractions.ts @@ -142,6 +142,8 @@ export interface IFieldValidation { export interface IFieldVM { name: string; + /** Full dotted path of the field within the form (e.g. "author.address.street"). */ + qualifiedName: string; type: string; label?: string; help?: string; diff --git a/packages/app-admin/src/features/formModel/createFieldRenderer.tsx b/packages/app-admin/src/features/formModel/createFieldRenderer.tsx index df962d7e8ec..5d0ed1b9c68 100644 --- a/packages/app-admin/src/features/formModel/createFieldRenderer.tsx +++ b/packages/app-admin/src/features/formModel/createFieldRenderer.tsx @@ -1,7 +1,20 @@ import React, { useEffect, useRef } from "react"; import { observer } from "mobx-react-lite"; +import { makeDecoratable } from "@webiny/react-composition"; import type { IFieldVM, IObjectFieldVM, FieldRendererSettings } from "./abstractions.js"; +/** + * Decoratable wrapper rendered around every leaf field's content. Features can decorate it to + * inject per-field UI (e.g. a comment marker) keyed on `field.qualifiedName`, without forking + * the form. By default it renders the field content unchanged. + */ +export const FormFieldWrapper = makeDecoratable( + "FormFieldWrapper", + ({ children }: { field: IFieldVM; children?: React.ReactNode }) => { + return <>{children}; + } +); + type RendererField = IFieldVM & { rendererSettings: FieldRendererSettings; }; @@ -27,7 +40,11 @@ const ScrollOnFocus = observer( field.clearFocusRequest(); }, [field.focusRequested]); - return
{children}
; + return ( +
+ {children} +
+ ); } ); diff --git a/packages/app-collaboration/package.json b/packages/app-collaboration/package.json new file mode 100644 index 00000000000..667cd81c736 --- /dev/null +++ b/packages/app-collaboration/package.json @@ -0,0 +1,41 @@ +{ + "name": "@webiny/app-collaboration", + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./index.js", + "./*": "./*" + }, + "description": "Collaboration Admin (threaded comments and tasks on content)", + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git", + "directory": "packages/app-collaboration" + }, + "author": "Webiny Ltd", + "license": "MIT", + "dependencies": { + "@webiny/admin-ui": "0.0.0", + "@webiny/app": "0.0.0", + "@webiny/app-admin": "0.0.0", + "@webiny/app-headless-cms": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/icons": "0.0.0", + "mobx": "^6.16.1", + "mobx-react-lite": "^4.1.1", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@types/react": "18.3.31", + "@webiny/build-tools": "0.0.0", + "rimraf": "^6.1.3", + "typescript": "7.0.2" + }, + "publishConfig": { + "access": "public" + }, + "webiny": { + "publishFrom": "dist" + } +} diff --git a/packages/app-collaboration/src/app.tsx b/packages/app-collaboration/src/app.tsx new file mode 100644 index 00000000000..55cf24ace2b --- /dev/null +++ b/packages/app-collaboration/src/app.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { RegisterFeature } from "@webiny/app-admin"; +import { CollaborationApiFeature } from "~/features/api/feature.js"; +import { CommentsPresenterFeature } from "~/presentation/comments/feature.js"; +import { CommentsHeaderButton } from "~/cms/CommentsHeaderButton.js"; +import { CommentsSidePanelDecorator } from "~/cms/CommentsSidePanelDecorator.js"; +import { FieldMarkerDecorator } from "~/cms/FieldMarkerDecorator.js"; + +/** + * Mount once in the admin app. Registers the collaboration data-access + presenter features and + * injects the Comments header toggle + side panel into the Headless CMS entry editor. + * + * TODO(before release): collaboration ships in the APW tier — re-wrap in + * (or the appropriate capability gate). Ungated for now so it works + * without a workflows license during development. + */ +export const CollaborationAdminApp = () => { + return ( + <> + + + + + + + ); +}; diff --git a/packages/app-collaboration/src/cms/CommentFieldMarker.tsx b/packages/app-collaboration/src/cms/CommentFieldMarker.tsx new file mode 100644 index 00000000000..a62d275d550 --- /dev/null +++ b/packages/app-collaboration/src/cms/CommentFieldMarker.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { ReactComponent as AddCommentIcon } from "@webiny/icons/add_comment.svg"; +import { ReactComponent as ChatBubbleIcon } from "@webiny/icons/chat_bubble.svg"; +import type { IFieldVM } from "@webiny/app-admin/features/formModel/abstractions.js"; +import { useCommentsPresenter } from "~/presentation/comments/useComments.js"; +import "~/presentation/comments/styles.js"; + +interface CommentFieldMarkerProps { + field: IFieldVM; +} + +/** + * Per-field affordance in the CMS entry form: a comment-count badge when the field has open + * threads, otherwise a hover-revealed "Comment" pill. Clicking opens the panel focused on this + * field. Renders nothing outside the entry editor (no contentId loaded). + */ +export const CommentFieldMarker = observer(({ field }: CommentFieldMarkerProps) => { + const presenter = useCommentsPresenter(); + const { vm } = presenter; + + if (!vm.contentId) { + return null; + } + + const locator = field.qualifiedName; + const count = vm.threads.filter(thread => thread.locator === locator).length; + + if (count > 0) { + return ( + presenter.openForField(locator)} + > + + {count} + + ); + } + + return ( + presenter.openPanel(locator)} + > + + Comment + + ); +}); diff --git a/packages/app-collaboration/src/cms/CommentMarkersContext.ts b/packages/app-collaboration/src/cms/CommentMarkersContext.ts new file mode 100644 index 00000000000..68e92b41568 --- /dev/null +++ b/packages/app-collaboration/src/cms/CommentMarkersContext.ts @@ -0,0 +1,35 @@ +import { createContext, useContext } from "react"; + +/** + * Scopes the per-field comment markers to the entry form they belong to. + * + * The markers decorate the shared, app-wide FormFieldWrapper, so without scoping they would also + * render on unrelated forms (access management, file settings, …) and — the case this guards — + * on the nested entry form shown when creating/editing a *referenced* entry in a drawer. That + * drawer renders its form in a child DI container, so we tag the context with the container the + * markers are active in and compare against the container each field actually renders in: a + * mismatch (drawer/child container) means "not this form", so no marker. + */ +export interface CommentMarkersContextValue { + /** contentId of the entry comments are active for, or null when markers are disabled. */ + contentId: string | null; + /** DI container the main entry form renders in. Typed loosely to avoid a @webiny/di dep. */ + container: unknown; + /** + * Locators of fields nested inside an array/list field. Markers are hidden for these because + * their locator can't uniquely anchor a thread to one array element yet. + */ + listLocators: Set; +} + +const CommentMarkersContext = createContext({ + contentId: null, + container: null, + listLocators: new Set() +}); + +export const CommentMarkersProvider = CommentMarkersContext.Provider; + +export const useCommentMarkersContext = (): CommentMarkersContextValue => { + return useContext(CommentMarkersContext); +}; diff --git a/packages/app-collaboration/src/cms/CommentsHeaderButton.tsx b/packages/app-collaboration/src/cms/CommentsHeaderButton.tsx new file mode 100644 index 00000000000..f87dbc9e61c --- /dev/null +++ b/packages/app-collaboration/src/cms/CommentsHeaderButton.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { InternalContentEntryEditorConfig } from "@webiny/app-headless-cms"; +import { CommentsToggle } from "~/presentation/comments/components/CommentsToggle.js"; + +const { Actions } = InternalContentEntryEditorConfig; + +export const CommentsHeaderButton = () => { + return ( + + } + /> + + ); +}; diff --git a/packages/app-collaboration/src/cms/CommentsSidePanelDecorator.tsx b/packages/app-collaboration/src/cms/CommentsSidePanelDecorator.tsx new file mode 100644 index 00000000000..81958e1aa0d --- /dev/null +++ b/packages/app-collaboration/src/cms/CommentsSidePanelDecorator.tsx @@ -0,0 +1,128 @@ +import React, { useEffect, useRef } from "react"; +import { observer } from "mobx-react-lite"; +import { useContainer, useRoute } from "@webiny/app"; +import { ContentEntryFormContent } from "@webiny/app-headless-cms/presentation/contentEntries/views/layout/index.js"; +import { useContentEntryFormPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/form/useContentEntryFormPresenter.js"; +import { useCommentsPresenter } from "~/presentation/comments/useComments.js"; +import { CommentsPanel } from "~/presentation/comments/components/CommentsPanel.js"; +import { CommentMarkersProvider } from "~/cms/CommentMarkersContext.js"; +import { collectListFieldLocators } from "~/cms/listFieldLocators.js"; +import { buildLocatorLabel } from "~/cms/fieldLabels.js"; +import { cmsContentId, COLLAB_THREAD_PARAM, COLLAB_FIELD_PARAM } from "~/constants.js"; + +export const CommentsSidePanelDecorator = ContentEntryFormContent.createDecorator(Original => { + return observer(function CommentsSidePanelDecoratorInner(props) { + const formPresenter = useContentEntryFormPresenter(); + const presenter = useCommentsPresenter(); + const { route: currentRoute, replaceRouteParams } = useRoute(); + const container = useContainer(); + + const vm = formPresenter.vm; + const entryId = vm.entry?.entryId; + const modelId = vm.model?.modelId; + const contentId = + !vm.isNewEntry && entryId && modelId ? cmsContentId(modelId, entryId) : null; + + // Scope the per-field markers to this form's container so they don't leak into a nested + // referenced-entry drawer (which renders in a child container). See CommentMarkersContext. + // `listLocators` marks fields nested in array/list fields, whose markers are suppressed + // (their locator can't uniquely anchor to a single array element yet). Recomputed each + // render so it stays in sync as list items are added/removed. + const markersContext = { + contentId, + container, + listLocators: collectListFieldLocators(vm.form) + }; + + // Deep-link params (from a notification click or a copied thread link). `params` merges + // path + query, so these arrive as query-string values on the entry URL. + const params = currentRoute?.params as Record | undefined; + const threadParam = params?.[COLLAB_THREAD_PARAM]; + const fieldParam = params?.[COLLAB_FIELD_PARAM]; + const consumedRef = useRef(null); + + useEffect(() => { + if (vm.isNewEntry || !entryId || !modelId) { + return; + } + presenter.init(cmsContentId(modelId, entryId)); + + // Consume a deep-link once: open + highlight the thread, scroll the form to the field, + // then strip the params from the URL so a refresh/back doesn't re-trigger it. + if (threadParam && consumedRef.current !== threadParam) { + consumedRef.current = threadParam; + presenter.openAndHighlight(threadParam); + if (fieldParam) { + formPresenter.vm.form?.focusField(fieldParam); + } + replaceRouteParams((current: Record) => { + const next = { ...current }; + delete next[COLLAB_THREAD_PARAM]; + delete next[COLLAB_FIELD_PARAM]; + return next; + }); + } + }, [ + entryId, + modelId, + vm.isNewEntry, + threadParam, + fieldParam, + presenter, + formPresenter, + replaceRouteParams + ]); + + if (vm.isNewEntry || !entryId || !modelId) { + return ; + } + + const open = presenter.vm.isOpen; + + const modelFields = vm.model.fields || []; + const resolveLabel = (locator: string) => buildLocatorLabel(modelFields, locator); + + const jumpToField = (locator: string) => { + vm.form?.focusField(locator); + }; + + // The panel stays mounted so it can animate both in and out: the container width + // transitions 0 <-> 384px (content column reflows smoothly) while the panel itself + // slides + fades in. + return ( + +
+
+ +
+
+
+ +
+
+
+
+ ); + }); +}); diff --git a/packages/app-collaboration/src/cms/FieldMarkerDecorator.tsx b/packages/app-collaboration/src/cms/FieldMarkerDecorator.tsx new file mode 100644 index 00000000000..0fed7ddff6b --- /dev/null +++ b/packages/app-collaboration/src/cms/FieldMarkerDecorator.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { useContainer } from "@webiny/app"; +import { FormFieldWrapper } from "@webiny/app-admin/features/formModel/createFieldRenderer.js"; +import type { IFieldVM } from "@webiny/app-admin/features/formModel/abstractions.js"; +import { useCommentMarkersContext } from "./CommentMarkersContext.js"; +import { CommentFieldMarker } from "./CommentFieldMarker.js"; + +interface WrapperProps { + field: IFieldVM; + children?: React.ReactNode; +} + +/** + * Decorates the shared per-field wrapper to mount a comment marker on each field. Renders the + * marker only for fields belonging to the entry form comments are active for: the context carries + * that form's DI container, and a field rendered in a different container (e.g. the child + * container of a referenced-entry create/edit drawer) is left untouched. Passes through unchanged + * on every other form (no context provider) with zero layout impact. + */ +export const FieldMarkerDecorator = FormFieldWrapper.createDecorator(Original => { + return observer(function FieldMarkerWrapper(props: WrapperProps) { + const { contentId, container, listLocators } = useCommentMarkersContext(); + const currentContainer = useContainer(); + + // Active only inside the entry form that owns the comments panel — not in a nested + // referenced-entry drawer (rendered in a child container) or any unrelated form. + const active = !!contentId && container === currentContainer; + + // Fields nested inside an array/list field can't uniquely anchor a thread yet (their + // locator is shared across all array items), so no marker there. + if (!active || listLocators.has(props.field.qualifiedName)) { + return ; + } + + return ( +
+ + +
+ ); + }); +}); diff --git a/packages/app-collaboration/src/cms/fieldLabels.ts b/packages/app-collaboration/src/cms/fieldLabels.ts new file mode 100644 index 00000000000..7045b184f7c --- /dev/null +++ b/packages/app-collaboration/src/cms/fieldLabels.ts @@ -0,0 +1,84 @@ +/** + * Client-side mirror of the server CmsLocatorResolver's model walk. Produces the same + * "Parent › Field" breadcrumb the resolved thread anchor shows, so the composer's location chip + * matches the created comment's location. + */ +export interface FieldLike { + fieldId: string; + label?: string; + settings?: { + fields?: FieldLike[]; + templates?: Array<{ fields?: FieldLike[] }>; + }; +} + +export interface FieldOption { + locator: string; + label: string; +} + +const isListIndex = (segment: string): boolean => /^\d+$/.test(segment); + +const childrenOf = (field: FieldLike): FieldLike[] => { + const children: FieldLike[] = []; + if (field.settings?.fields) { + children.push(...field.settings.fields); + } + if (field.settings?.templates) { + for (const template of field.settings.templates) { + if (template.fields) { + children.push(...template.fields); + } + } + } + return children; +}; + +/** + * Resolves a fieldId-dotted locator (numeric list indices skipped) to a breadcrumb label. + * Falls back to the raw locator if a segment can't be resolved. + */ +export const buildLocatorLabel = (fields: FieldLike[], locator: string): string => { + const segments = locator.split(".").filter(part => part.length > 0 && !isListIndex(part)); + + let current = fields; + let field: FieldLike | undefined; + const labels: string[] = []; + + for (const segment of segments) { + field = current.find(item => item.fieldId === segment); + if (!field) { + return locator; + } + labels.push(field.label || field.fieldId); + current = childrenOf(field); + } + + return labels.length > 0 ? labels.join(" › ") : locator; +}; + +/** + * Flattens all leaf fields (top-level + nested object/dynamic-zone) into selectable options with + * dotted locators and breadcrumb labels — used to populate the composer's field picker. + */ +export const flattenFields = ( + fields: FieldLike[], + parentPath = "", + parentLabels: string[] = [] +): FieldOption[] => { + const options: FieldOption[] = []; + + for (const field of fields) { + const locator = parentPath ? `${parentPath}.${field.fieldId}` : field.fieldId; + const labels = [...parentLabels, field.label || field.fieldId]; + const children = childrenOf(field); + + if (children.length === 0) { + options.push({ locator, label: labels.join(" › ") }); + } else { + options.push(...flattenFields(children, locator, labels)); + } + } + + return options; +}; diff --git a/packages/app-collaboration/src/cms/listFieldLocators.ts b/packages/app-collaboration/src/cms/listFieldLocators.ts new file mode 100644 index 00000000000..36e8de08e4b --- /dev/null +++ b/packages/app-collaboration/src/cms/listFieldLocators.ts @@ -0,0 +1,57 @@ +import type { + IFieldVM, + IFormVM, + IObjectFieldVM, + LayoutNodeVM +} from "@webiny/app-admin/features/formModel/abstractions.js"; + +/** + * Collects the locators (qualifiedNames) of every field that lives *inside* an array/list field. + * + * A field nested in a list field shares one qualifiedName across all array items (the form model + * doesn't embed the item index), so it can't uniquely anchor a comment thread to a single element. + * Comment markers are therefore hidden for these fields until per-element anchoring is supported + * (tracked as a follow-up). Entry-level, top-level, and single (non-list) nested fields are + * unaffected. + */ +export const collectListFieldLocators = (form?: IFormVM | null): Set => { + const out = new Set(); + if (form) { + walkLayout(form.layout, out); + } + return out; +}; + +const walkLayout = (nodes: LayoutNodeVM[], out: Set): void => { + for (const node of nodes) { + if (node.type === "row") { + walkFields(node.fields, false, out); + } else if (node.type === "tabs") { + for (const tab of node.tabs) { + walkLayout(tab.layout, out); + } + } + } +}; + +const walkFields = (fields: IFieldVM[], insideList: boolean, out: Set): void => { + for (const field of fields) { + if (insideList) { + out.add(field.qualifiedName); + } + if (field.type !== "object") { + continue; + } + const object = field as IObjectFieldVM; + if (object.isList) { + // Every item's children share the parent path (no index), so any item's fields yield + // the same locators — walking all items just guarantees we see them even if item 0 is + // structurally different (templated lists). + for (const item of object.items ?? []) { + walkFields(item.fields, true, out); + } + } else { + walkFields(object.fields ?? [], insideList, out); + } + } +}; diff --git a/packages/app-collaboration/src/constants.ts b/packages/app-collaboration/src/constants.ts new file mode 100644 index 00000000000..669e9585baf --- /dev/null +++ b/packages/app-collaboration/src/constants.ts @@ -0,0 +1,22 @@ +/** + * Content type for CMS entries. Must match the server-side CmsLocatorResolver. + */ +export const CONTENT_TYPE_CMS_ENTRY = "cms.entry"; + +/** + * Composes the opaque collaboration contentId for a CMS entry (`:`), matching + * how the server-side CmsLocatorResolver parses it. + */ +export const cmsContentId = (modelId: string, entryId: string): string => { + return `${modelId}:${entryId}`; +}; + +/** + * URL query params carrying a deep-link to a specific comment thread on an entry: "when this + * editor loads, open the comments panel and highlight `commentThread` (optionally scrolling to + * `commentField`)". Set by any feature that links to a comment (notifications, a copied thread + * link) and consumed once by the CMS side, which then strips them from the URL. Living on the URL + * (instead of sessionStorage) makes such links shareable and reproducible. + */ +export const COLLAB_THREAD_PARAM = "commentThread"; +export const COLLAB_FIELD_PARAM = "commentField"; diff --git a/packages/app-collaboration/src/features/api/CollaborationApi.ts b/packages/app-collaboration/src/features/api/CollaborationApi.ts new file mode 100644 index 00000000000..d694b05411a --- /dev/null +++ b/packages/app-collaboration/src/features/api/CollaborationApi.ts @@ -0,0 +1,48 @@ +import { CollaborationApi as ApiAbstraction, CollaborationGateway } from "./abstractions.js"; +import type { IListThreadsOptions } from "./abstractions.js"; +import type { CreateThreadInput, ListThreadsWhere } from "~/types.js"; + +class CollaborationApiImpl implements ApiAbstraction.Interface { + constructor(private gateway: CollaborationGateway.Interface) {} + + listThreads(where: ListThreadsWhere, options?: IListThreadsOptions) { + return this.gateway.listThreads(where, options); + } + + createThread(input: CreateThreadInput) { + return this.gateway.createThread(input); + } + + replyToThread(threadId: string, body: string, mentions?: string[]) { + return this.gateway.replyToThread(threadId, body, mentions); + } + + resolveThread(id: string) { + return this.gateway.resolveThread(id); + } + + reopenThread(id: string) { + return this.gateway.reopenThread(id); + } + + deleteThread(id: string) { + return this.gateway.deleteThread(id); + } + + updateMessage(threadId: string, messageId: string, body: string) { + return this.gateway.updateMessage(threadId, messageId, body); + } + + deleteMessage(threadId: string, messageId: string) { + return this.gateway.deleteMessage(threadId, messageId); + } + + listMentionableUsers() { + return this.gateway.listMentionableUsers(); + } +} + +export const CollaborationApi = ApiAbstraction.createImplementation({ + implementation: CollaborationApiImpl, + dependencies: [CollaborationGateway] +}); diff --git a/packages/app-collaboration/src/features/api/CollaborationGateway.ts b/packages/app-collaboration/src/features/api/CollaborationGateway.ts new file mode 100644 index 00000000000..f8dfe58b340 --- /dev/null +++ b/packages/app-collaboration/src/features/api/CollaborationGateway.ts @@ -0,0 +1,227 @@ +import { MainGraphQLClient } from "@webiny/app/features/mainGraphQLClient/abstractions.js"; +import { CollaborationGateway as GatewayAbstraction } from "./abstractions.js"; +import type { IListThreadsOptions, IListThreadsResult } from "./abstractions.js"; +import type { + CollabMessage, + CollabThread, + CollabThreadsMeta, + CollabUser, + CreateThreadInput, + ListThreadsWhere +} from "~/types.js"; +import { ERROR_FIELDS, MESSAGE_FIELDS, THREAD_FIELDS } from "./graphqlFields.js"; + +interface GqlError { + code: string; + message: string; + data?: unknown; +} + +const unwrap = (envelope: { data: T | null; error: GqlError | null }): T => { + if (envelope.error) { + throw new Error(envelope.error.message); + } + return envelope.data as T; +}; + +const LIST_QUERY = /* GraphQL */ ` + query ListCollabThreads($where: ListCollabThreadsWhereInput!, $limit: Int, $after: String) { + collaboration { + listCollabThreads(where: $where, limit: $limit, after: $after) { + data ${THREAD_FIELDS} + meta { totalCount hasMoreItems cursor } + ${ERROR_FIELDS} + } + } + } +`; + +const CREATE_MUTATION = /* GraphQL */ ` + mutation CreateCollabThread($input: CreateCollabThreadInput!) { + collaboration { createCollabThread(input: $input) { data ${THREAD_FIELDS} ${ERROR_FIELDS} } } + } +`; + +const REPLY_MUTATION = /* GraphQL */ ` + mutation ReplyToCollabThread($threadId: ID!, $body: String!, $mentions: [String!]) { + collaboration { + replyToCollabThread(threadId: $threadId, body: $body, mentions: $mentions) { + data ${MESSAGE_FIELDS} + ${ERROR_FIELDS} + } + } + } +`; + +const RESOLVE_MUTATION = /* GraphQL */ ` + mutation ResolveCollabThread($id: ID!) { + collaboration { resolveCollabThread(id: $id) { data ${THREAD_FIELDS} ${ERROR_FIELDS} } } + } +`; + +const REOPEN_MUTATION = /* GraphQL */ ` + mutation ReopenCollabThread($id: ID!) { + collaboration { reopenCollabThread(id: $id) { data ${THREAD_FIELDS} ${ERROR_FIELDS} } } + } +`; + +const DELETE_MUTATION = /* GraphQL */ ` + mutation DeleteCollabThread($id: ID!) { + collaboration { deleteCollabThread(id: $id) { data ${ERROR_FIELDS} } } + } +`; + +const UPDATE_MESSAGE_MUTATION = /* GraphQL */ ` + mutation UpdateCollabMessage($threadId: ID!, $messageId: ID!, $body: String!) { + collaboration { + updateCollabMessage(threadId: $threadId, messageId: $messageId, body: $body) { + data ${MESSAGE_FIELDS} + ${ERROR_FIELDS} + } + } + } +`; + +const DELETE_MESSAGE_MUTATION = /* GraphQL */ ` + mutation DeleteCollabMessage($threadId: ID!, $messageId: ID!) { + collaboration { + deleteCollabMessage(threadId: $threadId, messageId: $messageId) { + data ${ERROR_FIELDS} + } + } + } +`; + +const LIST_USERS_QUERY = /* GraphQL */ ` + query CollabListMentionableUsers { + adminUsers { + listUsers { + data { + id + displayName + email + avatar + } + error { + code + message + } + } + } + } +`; + +interface ListResponse { + collaboration: { + listCollabThreads: { + data: CollabThread[] | null; + meta: CollabThreadsMeta | null; + error: GqlError | null; + }; + }; +} + +class CollaborationGatewayImpl implements GatewayAbstraction.Interface { + constructor(private client: MainGraphQLClient.Interface) {} + + async listThreads( + where: ListThreadsWhere, + options: IListThreadsOptions = {} + ): Promise { + const response = await this.client.execute({ + query: LIST_QUERY, + variables: { where, limit: options.limit, after: options.after } + }); + const envelope = response.collaboration.listCollabThreads; + if (envelope.error) { + throw new Error(envelope.error.message); + } + return { + items: envelope.data || [], + meta: envelope.meta || { totalCount: 0, hasMoreItems: false, cursor: null } + }; + } + + async createThread(input: CreateThreadInput): Promise { + const response = await this.client.execute<{ + collaboration: { + createCollabThread: { data: CollabThread | null; error: GqlError | null }; + }; + }>({ query: CREATE_MUTATION, variables: { input } }); + return unwrap(response.collaboration.createCollabThread); + } + + async replyToThread( + threadId: string, + body: string, + mentions?: string[] + ): Promise { + const response = await this.client.execute<{ + collaboration: { + replyToCollabThread: { data: CollabMessage | null; error: GqlError | null }; + }; + }>({ query: REPLY_MUTATION, variables: { threadId, body, mentions } }); + return unwrap(response.collaboration.replyToCollabThread); + } + + async resolveThread(id: string): Promise { + const response = await this.client.execute<{ + collaboration: { + resolveCollabThread: { data: CollabThread | null; error: GqlError | null }; + }; + }>({ query: RESOLVE_MUTATION, variables: { id } }); + return unwrap(response.collaboration.resolveCollabThread); + } + + async reopenThread(id: string): Promise { + const response = await this.client.execute<{ + collaboration: { + reopenCollabThread: { data: CollabThread | null; error: GqlError | null }; + }; + }>({ query: REOPEN_MUTATION, variables: { id } }); + return unwrap(response.collaboration.reopenCollabThread); + } + + async deleteThread(id: string): Promise { + const response = await this.client.execute<{ + collaboration: { deleteCollabThread: { data: boolean | null; error: GqlError | null } }; + }>({ query: DELETE_MUTATION, variables: { id } }); + return unwrap(response.collaboration.deleteCollabThread) === true; + } + + async updateMessage(threadId: string, messageId: string, body: string): Promise { + const response = await this.client.execute<{ + collaboration: { + updateCollabMessage: { data: CollabMessage | null; error: GqlError | null }; + }; + }>({ query: UPDATE_MESSAGE_MUTATION, variables: { threadId, messageId, body } }); + return unwrap(response.collaboration.updateCollabMessage); + } + + async deleteMessage(threadId: string, messageId: string): Promise { + const response = await this.client.execute<{ + collaboration: { + deleteCollabMessage: { data: boolean | null; error: GqlError | null }; + }; + }>({ query: DELETE_MESSAGE_MUTATION, variables: { threadId, messageId } }); + return unwrap(response.collaboration.deleteCollabMessage) === true; + } + + async listMentionableUsers(): Promise { + try { + const response = await this.client.execute<{ + adminUsers: { listUsers: { data: CollabUser[] | null; error: GqlError | null } }; + }>({ query: LIST_USERS_QUERY }); + const envelope = response.adminUsers.listUsers; + return envelope.error ? [] : envelope.data || []; + } catch { + // Best-effort: mentions degrade gracefully if the caller can't list users. + return []; + } + } +} + +export const CollaborationGateway = GatewayAbstraction.createImplementation({ + implementation: CollaborationGatewayImpl, + dependencies: [MainGraphQLClient] +}); diff --git a/packages/app-collaboration/src/features/api/abstractions.ts b/packages/app-collaboration/src/features/api/abstractions.ts new file mode 100644 index 00000000000..a88fbe4e8f3 --- /dev/null +++ b/packages/app-collaboration/src/features/api/abstractions.ts @@ -0,0 +1,53 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { + CollabMessage, + CollabThread, + CollabThreadsMeta, + CollabUser, + CreateThreadInput, + ListThreadsWhere +} from "~/types.js"; + +export interface IListThreadsResult { + items: CollabThread[]; + meta: CollabThreadsMeta; +} + +export interface IListThreadsOptions { + limit?: number; + after?: string | null; +} + +/** + * Multi-method data-access surface for the collaboration GraphQL API. The Gateway talks to the + * API; the Api use case is a thin, injectable pass-through the presenter depends on. + */ +export interface ICollaborationApi { + listThreads( + where: ListThreadsWhere, + options?: IListThreadsOptions + ): Promise; + createThread(input: CreateThreadInput): Promise; + replyToThread(threadId: string, body: string, mentions?: string[]): Promise; + resolveThread(id: string): Promise; + reopenThread(id: string): Promise; + deleteThread(id: string): Promise; + updateMessage(threadId: string, messageId: string, body: string): Promise; + deleteMessage(threadId: string, messageId: string): Promise; + /** Tenant members that can be @mentioned. Best-effort: returns [] if unavailable. */ + listMentionableUsers(): Promise; +} + +export const CollaborationGateway = createAbstraction("Collaboration/Gateway"); + +export namespace CollaborationGateway { + export type Interface = ICollaborationApi; +} + +export const CollaborationApi = createAbstraction("Collaboration/Api"); + +export namespace CollaborationApi { + export type Interface = ICollaborationApi; + export type ListResult = IListThreadsResult; + export type ListOptions = IListThreadsOptions; +} diff --git a/packages/app-collaboration/src/features/api/feature.ts b/packages/app-collaboration/src/features/api/feature.ts new file mode 100644 index 00000000000..53d74532dde --- /dev/null +++ b/packages/app-collaboration/src/features/api/feature.ts @@ -0,0 +1,17 @@ +import { createFeature } from "@webiny/feature/admin"; +import { CollaborationApi as ApiAbstraction } from "./abstractions.js"; +import { CollaborationGateway } from "./CollaborationGateway.js"; +import { CollaborationApi } from "./CollaborationApi.js"; + +export const CollaborationApiFeature = createFeature({ + name: "Collaboration/Api", + register(container) { + container.register(CollaborationGateway).inSingletonScope(); + container.register(CollaborationApi).inSingletonScope(); + }, + resolve(container) { + return { + api: container.resolve(ApiAbstraction) + }; + } +}); diff --git a/packages/app-collaboration/src/features/api/graphqlFields.ts b/packages/app-collaboration/src/features/api/graphqlFields.ts new file mode 100644 index 00000000000..1622dda4cda --- /dev/null +++ b/packages/app-collaboration/src/features/api/graphqlFields.ts @@ -0,0 +1,52 @@ +export const ERROR_FIELDS = /* GraphQL */ ` + error { + code + message + data + } +`; + +export const IDENTITY_FIELDS = /* GraphQL */ ` + { + id + displayName + type + } +`; + +export const MESSAGE_FIELDS = /* GraphQL */ ` + { + id + body + mentions + createdBy ${IDENTITY_FIELDS} + createdOn + deleted + deletedBy ${IDENTITY_FIELDS} + deletedOn + } +`; + +export const THREAD_FIELDS = /* GraphQL */ ` + { + id + contentType + contentId + locator + type + resolved + resolvedBy ${IDENTITY_FIELDS} + resolvedOn + assigneeId + dueDate + createdBy ${IDENTITY_FIELDS} + createdOn + messages ${MESSAGE_FIELDS} + anchor { + exists + authorized + label + path + } + } +`; diff --git a/packages/app-collaboration/src/index.tsx b/packages/app-collaboration/src/index.tsx new file mode 100644 index 00000000000..1facc1aa263 --- /dev/null +++ b/packages/app-collaboration/src/index.tsx @@ -0,0 +1,12 @@ +export { CollaborationAdminApp } from "./app.js"; +export { CollaborationApiFeature } from "./features/api/feature.js"; +export { CommentsPresenterFeature } from "./presentation/comments/feature.js"; +export { useCommentsPresenter } from "./presentation/comments/useComments.js"; +export { CONTENT_TYPE_CMS_ENTRY, cmsContentId } from "./constants.js"; +export type { + CollabThread, + CollabMessage, + CollabIdentity, + CollabAnchor, + CollabThreadType +} from "./types.js"; diff --git a/packages/app-collaboration/src/presentation/comments/CommentsPresenter.ts b/packages/app-collaboration/src/presentation/comments/CommentsPresenter.ts new file mode 100644 index 00000000000..81e8cf8a52c --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/CommentsPresenter.ts @@ -0,0 +1,288 @@ +import { makeAutoObservable, runInAction, toJS } from "mobx"; +import { CommentsPresenter as PresenterAbstraction } from "./abstractions.js"; +import type { ICreateThreadParams } from "./abstractions.js"; +import { CollaborationApi } from "~/features/api/abstractions.js"; +import { CONTENT_TYPE_CMS_ENTRY } from "~/constants.js"; +import type { CollabThread, CollabUser } from "~/types.js"; + +/** + * Extracts a human-readable message from an Apollo-style error (which otherwise surfaces the + * generic "GraphQL errors"). + */ +const readableError = (err: unknown): string => { + const error = err as { + message?: string; + graphQLErrors?: Array<{ message?: string }>; + networkError?: { message?: string }; + }; + return ( + error?.graphQLErrors?.[0]?.message || + error?.networkError?.message || + error?.message || + "Failed to load comments." + ); +}; + +class CommentsPresenterImpl implements PresenterAbstraction.Interface { + private contentId: string | null = null; + private threads: CollabThread[] = []; + private loading = false; + private error: string | null = null; + private isOpen = false; + private activeLocator: string | null = null; + private filterLocator: string | null = null; + private highlightThreadId: string | null = null; + private users: CollabUser[] = []; + // Composer draft state lives on the presenter so the React composer stays a thin view. + private composerBody = ""; + private composerMentions: string[] = []; + private composerSubmitting = false; + + constructor(private api: CollaborationApi.Interface) { + makeAutoObservable(this, { api: false }); + } + + openPanel(locator?: string) { + this.isOpen = true; + // No locator => entry-level (unanchored) comment. + this.activeLocator = locator ?? null; + // A general open (or "add comment" on a field) shows all threads. + this.filterLocator = null; + } + + closePanel() { + this.isOpen = false; + this.filterLocator = null; + } + + togglePanel() { + this.isOpen = !this.isOpen; + this.filterLocator = null; + } + + openForField(locator: string) { + this.isOpen = true; + this.filterLocator = locator; + this.activeLocator = locator; + } + + clearFieldFilter() { + this.filterLocator = null; + } + + setActiveLocator(locator: string | null) { + this.activeLocator = locator; + } + + openAndHighlight(threadId: string) { + this.openPanel(); + this.highlightThreadId = threadId; + } + + clearHighlight() { + this.highlightThreadId = null; + } + + get vm(): PresenterAbstraction.ViewModel { + // Soft-deleted threads are filtered out server-side, so everything here is live. + const active = this.threads; + const open = active.filter(thread => !thread.resolved && thread.anchor.exists); + const outdated = active.filter(thread => !thread.resolved && !thread.anchor.exists); + const resolved = active.filter(thread => thread.resolved); + // Only field-anchored threads count toward "across N fields". + const fields = new Set(open.filter(thread => thread.locator).map(thread => thread.locator)); + + return { + loading: this.loading, + error: this.error, + contentId: this.contentId, + isOpen: this.isOpen, + activeLocator: this.activeLocator, + filterLocator: this.filterLocator, + highlightThreadId: this.highlightThreadId, + threads: toJS(open), + outdatedThreads: toJS(outdated), + resolvedThreads: toJS(resolved), + unresolvedCount: open.length + outdated.length, + fieldCount: fields.size, + mentionableUsers: toJS(this.users), + composer: { + body: this.composerBody, + mentions: toJS(this.composerMentions), + submitting: this.composerSubmitting + } + }; + } + + setComposerBody(body: string) { + this.composerBody = body; + } + + addComposerMention(userId: string) { + if (!this.composerMentions.includes(userId)) { + this.composerMentions.push(userId); + } + } + + resetComposer() { + this.composerBody = ""; + this.composerMentions = []; + } + + async submitComposer() { + if (!this.composerBody.trim() || this.composerSubmitting) { + return; + } + runInAction(() => { + this.composerSubmitting = true; + }); + try { + // Empty locator => entry-level (unanchored) comment. + await this.createThread({ + locator: this.activeLocator ?? "", + body: this.composerBody, + mentions: toJS(this.composerMentions) + }); + this.resetComposer(); + } finally { + runInAction(() => { + this.composerSubmitting = false; + }); + } + } + + async init(contentId: string) { + if (this.contentId === contentId && this.threads.length > 0) { + return; + } + runInAction(() => { + this.contentId = contentId; + }); + void this.loadUsers(); + await this.reload(); + } + + private async loadUsers() { + if (this.users.length > 0) { + return; + } + try { + const users = await this.api.listMentionableUsers(); + runInAction(() => { + this.users = users; + }); + } catch { + // Best-effort — mentions simply won't autocomplete. + } + } + + async reload() { + if (!this.contentId) { + return; + } + runInAction(() => { + this.loading = true; + this.error = null; + }); + try { + const result = await this.api.listThreads({ + contentType: CONTENT_TYPE_CMS_ENTRY, + contentId: this.contentId + }); + runInAction(() => { + this.threads = result.items; + this.loading = false; + }); + } catch (err) { + console.error("[collaboration] failed to load comment threads", err); + runInAction(() => { + this.error = readableError(err); + this.loading = false; + }); + } + } + + async createThread(params: ICreateThreadParams) { + if (!this.contentId) { + return; + } + const thread = await this.api.createThread({ + contentType: CONTENT_TYPE_CMS_ENTRY, + contentId: this.contentId, + locator: params.locator, + type: params.type ?? "note", + body: params.body, + mentions: params.mentions + }); + runInAction(() => { + this.threads = [thread, ...this.threads]; + }); + } + + async reply(threadId: string, body: string, mentions?: string[]) { + await this.api.replyToThread(threadId, body, mentions); + await this.reload(); + } + + /** + * Owns reply validation + the reply use-case call. The reply input state stays local to the + * thread card (a view concern), but the submit orchestration lives here. + */ + async submitReply(threadId: string, body: string, mentions?: string[]) { + if (!body.trim()) { + return; + } + await this.reply(threadId, body, mentions); + } + + async resolve(threadId: string) { + const updated = await this.api.resolveThread(threadId); + this.replaceThread(updated); + } + + async reopen(threadId: string) { + const updated = await this.api.reopenThread(threadId); + this.replaceThread(updated); + } + + async remove(threadId: string) { + await this.api.deleteThread(threadId); + runInAction(() => { + this.threads = this.threads.filter(thread => thread.id !== threadId); + }); + } + + async editMessage(threadId: string, messageId: string, body: string) { + await this.api.updateMessage(threadId, messageId, body); + await this.reload(); + } + + /** + * Owns message-edit validation + the edit use-case call. The edit draft state stays local to + * the message component (a view concern), but the submit orchestration lives here. + */ + async submitMessageEdit(threadId: string, messageId: string, body: string) { + if (!body.trim()) { + return; + } + await this.editMessage(threadId, messageId, body); + } + + async deleteMessage(threadId: string, messageId: string) { + await this.api.deleteMessage(threadId, messageId); + await this.reload(); + } + + private replaceThread(updated: CollabThread) { + runInAction(() => { + this.threads = this.threads.map(thread => + thread.id === updated.id ? updated : thread + ); + }); + } +} + +export const CommentsPresenter = PresenterAbstraction.createImplementation({ + implementation: CommentsPresenterImpl, + dependencies: [CollaborationApi] +}); diff --git a/packages/app-collaboration/src/presentation/comments/abstractions.ts b/packages/app-collaboration/src/presentation/comments/abstractions.ts new file mode 100644 index 00000000000..866a5487682 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/abstractions.ts @@ -0,0 +1,84 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { CollabThread, CollabThreadType, CollabUser } from "~/types.js"; + +export interface ICreateThreadParams { + locator: string; + body: string; + type?: CollabThreadType; + mentions?: string[]; +} + +export interface ICommentsViewModel { + loading: boolean; + error: string | null; + contentId: string | null; + /** Whether the comments side panel is open (shared UI state). */ + isOpen: boolean; + /** Locator the composer should default to (set by a field's "add comment" action). */ + activeLocator: string | null; + /** When set, the panel shows only threads anchored to this field's locator. */ + filterLocator: string | null; + /** Thread to visually highlight + scroll to (e.g. arriving from a notification deep-link). */ + highlightThreadId: string | null; + /** Open (unresolved) threads whose anchor still exists. */ + threads: CollabThread[]; + /** Threads whose anchor no longer exists in the current revision. */ + outdatedThreads: CollabThread[]; + /** Resolved threads. */ + resolvedThreads: CollabThread[]; + unresolvedCount: number; + fieldCount: number; + /** Tenant members available to @mention. */ + mentionableUsers: CollabUser[]; + /** Composer draft state (owned by the presenter, rendered by the thin composer view). */ + composer: { + body: string; + mentions: string[]; + submitting: boolean; + }; +} + +export interface ICommentsPresenter { + readonly vm: ICommentsViewModel; + init(contentId: string): Promise; + reload(): Promise; + openPanel(locator?: string): void; + closePanel(): void; + togglePanel(): void; + /** Open the panel filtered to a single field's threads (from that field's comment marker). */ + openForField(locator: string): void; + /** Remove the field filter and show all threads again. */ + clearFieldFilter(): void; + setActiveLocator(locator: string | null): void; + openAndHighlight(threadId: string): void; + clearHighlight(): void; + createThread(params: ICreateThreadParams): Promise; + /** Update the composer draft body. */ + setComposerBody(body: string): void; + /** Add a mentioned user id to the composer draft (deduped). */ + addComposerMention(userId: string): void; + /** Clear the composer draft (body + mentions). */ + resetComposer(): void; + /** Validate + submit the composer draft as a new thread, then reset it. */ + submitComposer(): Promise; + reply(threadId: string, body: string, mentions?: string[]): Promise; + /** Validate + submit a reply to a thread. */ + submitReply(threadId: string, body: string, mentions?: string[]): Promise; + resolve(threadId: string): Promise; + reopen(threadId: string): Promise; + remove(threadId: string): Promise; + editMessage(threadId: string, messageId: string, body: string): Promise; + /** Validate + submit an edit to an existing message. */ + submitMessageEdit(threadId: string, messageId: string, body: string): Promise; + deleteMessage(threadId: string, messageId: string): Promise; +} + +export const CommentsPresenter = createAbstraction( + "Collaboration/CommentsPresenter" +); + +export namespace CommentsPresenter { + export type Interface = ICommentsPresenter; + export type ViewModel = ICommentsViewModel; + export type CreateParams = ICreateThreadParams; +} diff --git a/packages/app-collaboration/src/presentation/comments/components/AutoTextarea.tsx b/packages/app-collaboration/src/presentation/comments/components/AutoTextarea.tsx new file mode 100644 index 00000000000..6b0f125874e --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/AutoTextarea.tsx @@ -0,0 +1,61 @@ +import React, { useLayoutEffect, useRef } from "react"; +import { TextareaPrimitive } from "@webiny/admin-ui"; + +interface Props { + value: string; + onChange: (value: string) => void; + placeholder?: string; + className?: string; + autoFocus?: boolean; + /** Height (px) at which the textarea stops growing and starts scrolling. */ + maxHeight?: number; + onKeyDown?: (event: React.KeyboardEvent) => void; +} + +/** + * A controlled textarea that grows with its content (starting at one line) up to `maxHeight`, + * then scrolls. Keeps long comments and replies readable while typing. + */ +export const AutoTextarea = ({ + value, + onChange, + placeholder, + className, + autoFocus, + maxHeight = 220, + onKeyDown +}: Props) => { + const ref = useRef(null); + + const resize = () => { + const el = ref.current; + if (!el) { + return; + } + el.style.height = "auto"; + const next = Math.min(el.scrollHeight, maxHeight); + el.style.height = `${next}px`; + el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden"; + }; + + useLayoutEffect(() => { + resize(); + }, [value]); + + return ( + } + onKeyDown={onKeyDown} + /> + ); +}; diff --git a/packages/app-collaboration/src/presentation/comments/components/CommentsPanel.tsx b/packages/app-collaboration/src/presentation/comments/components/CommentsPanel.tsx new file mode 100644 index 00000000000..83899814451 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/CommentsPanel.tsx @@ -0,0 +1,182 @@ +import React, { useState } from "react"; +import { observer } from "mobx-react-lite"; +import { ReactComponent as CloseIcon } from "@webiny/icons/close.svg"; +import { ReactComponent as RefreshIcon } from "@webiny/icons/refresh.svg"; +import { ReactComponent as ExpandMoreIcon } from "@webiny/icons/expand_more.svg"; +import { ReactComponent as ExpandLessIcon } from "@webiny/icons/expand_less.svg"; +import type { CommentsPresenter } from "../abstractions.js"; +import { ThreadCard } from "./ThreadCard.js"; +import { Composer } from "./Composer.js"; +import type { CollabThread } from "~/types.js"; +import "../styles.js"; + +interface Props { + presenter: CommentsPresenter.Interface; + onJumpToField: (locator: string) => void; + resolveLabel: (locator: string) => string; +} + +interface ThreadListProps { + threads: CollabThread[]; + presenter: CommentsPresenter.Interface; + onJumpToField: (locator: string) => void; +} + +/** + * Renders a list of thread cards, or nothing when there are none. Keeps the "is there anything to + * show?" decision inside the list component rather than in every caller. + */ +const ThreadList = ({ threads, presenter, onJumpToField }: ThreadListProps) => { + if (threads.length === 0) { + return null; + } + return ( + <> + {threads.map(thread => ( + + ))} + + ); +}; + +interface SectionProps { + label: string; + threads: CollabThread[]; + presenter: CommentsPresenter.Interface; + onJumpToField: (locator: string) => void; + defaultOpen?: boolean; +} + +const CollapsibleSection = ({ + label, + threads, + presenter, + onJumpToField, + defaultOpen = true +}: SectionProps) => { + const [open, setOpen] = useState(defaultOpen); + if (threads.length === 0) { + return null; + } + return ( +
+ + {open ? ( + + ) : null} +
+ ); +}; + +export const CommentsPanel = observer((props: Props) => { + const { presenter, onJumpToField, resolveLabel } = props; + const { vm } = presenter; + + const filter = vm.filterLocator; + const matchesFilter = (thread: CollabThread) => !filter || thread.locator === filter; + const openThreads = vm.threads.filter(matchesFilter); + const outdatedThreads = vm.outdatedThreads.filter(matchesFilter); + const resolvedThreads = vm.resolvedThreads.filter(matchesFilter); + const total = openThreads.length + outdatedThreads.length + resolvedThreads.length; + + return ( + + ); +}); diff --git a/packages/app-collaboration/src/presentation/comments/components/CommentsToggle.tsx b/packages/app-collaboration/src/presentation/comments/components/CommentsToggle.tsx new file mode 100644 index 00000000000..81f1f9fd4db --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/CommentsToggle.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { Button } from "@webiny/admin-ui"; +import { ReactComponent as CommentIcon } from "@webiny/icons/comment.svg"; +import { useContentEntryFormPresenter } from "@webiny/app-headless-cms/presentation/contentEntries/form/useContentEntryFormPresenter.js"; +import { useCommentsPresenter } from "../useComments.js"; + +export const CommentsToggle = observer(() => { + const presenter = useCommentsPresenter(); + const { vm: formVm } = useContentEntryFormPresenter(); + const { vm } = presenter; + + // Comments anchor to a persisted entry, so there's nothing to open until the entry is saved + // (a new/unsaved entry has no id). Hide the toggle until then. + if (formVm.isNewEntry || !formVm.entry) { + return null; + } + + const count = vm.unresolvedCount; + + return ( + + ); +}); diff --git a/packages/app-collaboration/src/presentation/comments/components/Composer.tsx b/packages/app-collaboration/src/presentation/comments/components/Composer.tsx new file mode 100644 index 00000000000..30b49017455 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/Composer.tsx @@ -0,0 +1,92 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { useSecurity } from "@webiny/app-admin"; +import { ReactComponent as CloseIcon } from "@webiny/icons/close.svg"; +import type { CommentsPresenter } from "../abstractions.js"; +import { avatarColor, initials } from "../styles.js"; +import { MentionTextarea } from "./MentionTextarea.js"; + +interface Props { + presenter: CommentsPresenter.Interface; + activeLocator: string | null; + resolveLabel: (locator: string) => string; +} + +export const Composer = observer((props: Props) => { + const { presenter, activeLocator, resolveLabel } = props; + const { identity } = useSecurity(); + const authorName = identity?.displayName || "You"; + const { body, submitting } = presenter.vm.composer; + + return ( +
+ {activeLocator ? ( + + + {resolveLabel(activeLocator)} + + + ) : ( + + Whole entry + + )} + +
+
+ + {initials(authorName)} + + presenter.setComposerBody(value)} + onMention={userId => presenter.addComposerMention(userId)} + onKeyDown={event => { + event.stopPropagation(); + if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) { + void presenter.submitComposer(); + } + }} + /> +
+
+ + +
+
+
+ ); +}); diff --git a/packages/app-collaboration/src/presentation/comments/components/MentionTextarea.tsx b/packages/app-collaboration/src/presentation/comments/components/MentionTextarea.tsx new file mode 100644 index 00000000000..b7333a2f125 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/MentionTextarea.tsx @@ -0,0 +1,218 @@ +import React, { useLayoutEffect, useRef, useState } from "react"; +import { TextareaPrimitive } from "@webiny/admin-ui"; +import { avatarColor, initials } from "../styles.js"; +import type { CollabUser } from "~/types.js"; + +interface Props { + value: string; + onChange: (value: string) => void; + onMention?: (userId: string) => void; + users: CollabUser[]; + /** User id to omit from suggestions (e.g. the current user). */ + excludeUserId?: string; + placeholder?: string; + className?: string; + autoFocus?: boolean; + maxHeight?: number; + onKeyDown?: (event: React.KeyboardEvent) => void; +} + +// The active @token immediately before the caret (start of line or after whitespace). +const MENTION_RE = /(?:^|\s)@([\w.\-]*)$/; + +/** + * An auto-growing textarea with @mention autocomplete. Detects an `@query` before the caret, + * shows a filtered user list, and on select inserts `@Display Name ` and reports the user id + * (mentions are tracked by the parent — best-effort, not re-parsed from text). + */ +export const MentionTextarea = ({ + value, + onChange, + onMention, + users, + excludeUserId, + placeholder, + className, + autoFocus, + maxHeight = 220, + onKeyDown +}: Props) => { + const ref = useRef(null); + // When the user dismisses the menu with Esc, keep it closed until they type again + // (otherwise the following keyup/click re-detects the same @token and reopens it). + const dismissedRef = useRef(false); + const [query, setQuery] = useState(null); + const [atIndex, setAtIndex] = useState(0); + const [caret, setCaret] = useState(0); + const [highlight, setHighlight] = useState(0); + const [pendingCaret, setPendingCaret] = useState(null); + + const resize = () => { + const el = ref.current; + if (!el) { + return; + } + el.style.height = "auto"; + const next = Math.min(el.scrollHeight, maxHeight); + el.style.height = `${next}px`; + el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden"; + }; + + useLayoutEffect(() => { + resize(); + }, [value]); + + useLayoutEffect(() => { + if (pendingCaret !== null && ref.current) { + ref.current.selectionStart = pendingCaret; + ref.current.selectionEnd = pendingCaret; + ref.current.focus(); + setPendingCaret(null); + } + }); + + const matches = + query !== null + ? users + .filter(user => { + if (excludeUserId && user.id === excludeUserId) { + return false; + } + const q = query.toLowerCase(); + return ( + user.displayName.toLowerCase().includes(q) || + (user.email || "").toLowerCase().includes(q) + ); + }) + .slice(0, 6) + : []; + + const detect = (text: string, position: number) => { + if (dismissedRef.current) { + setQuery(null); + return; + } + const match = text.slice(0, position).match(MENTION_RE); + if (match) { + setQuery(match[1]); + setAtIndex(position - match[1].length - 1); + setHighlight(0); + } else { + setQuery(null); + } + }; + + const accept = (user: CollabUser) => { + const insert = `@${user.displayName} `; + const next = value.slice(0, atIndex) + insert + value.slice(caret); + onChange(next); + onMention?.(user.id); + setQuery(null); + setPendingCaret(atIndex + insert.length); + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + // Always isolate keys from the CMS editor's global shortcuts. + event.stopPropagation(); + + if (query !== null && matches.length > 0) { + if (event.key === "ArrowDown") { + event.preventDefault(); + setHighlight(h => (h + 1) % matches.length); + return; + } + if (event.key === "ArrowUp") { + event.preventDefault(); + setHighlight(h => (h - 1 + matches.length) % matches.length); + return; + } + if (event.key === "Enter" || event.key === "Tab") { + event.preventDefault(); + accept(matches[highlight]); + return; + } + if (event.key === "Escape") { + event.preventDefault(); + dismissedRef.current = true; + setQuery(null); + return; + } + } + + onKeyDown?.(event); + }; + + return ( +
+ { + // Typing re-enables suggestions after an Esc dismissal. + dismissedRef.current = false; + onChange(event.target.value); + const position = event.target.selectionStart ?? event.target.value.length; + setCaret(position); + detect(event.target.value, position); + }} + onClick={event => { + const el = event.currentTarget; + const position = el.selectionStart ?? 0; + setCaret(position); + detect(el.value, position); + }} + onKeyUp={event => { + const el = event.currentTarget; + const position = el.selectionStart ?? 0; + setCaret(position); + detect(el.value, position); + }} + onKeyDown={handleKeyDown} + onBlur={() => window.setTimeout(() => setQuery(null), 120)} + /> + {query !== null && matches.length > 0 ? ( +
+ {matches.map((user, index) => ( + + ))} +
+ ) : null} +
+ ); +}; diff --git a/packages/app-collaboration/src/presentation/comments/components/ThreadCard.tsx b/packages/app-collaboration/src/presentation/comments/components/ThreadCard.tsx new file mode 100644 index 00000000000..0ed7f2f0054 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/components/ThreadCard.tsx @@ -0,0 +1,427 @@ +import React, { useEffect, useRef, useState } from "react"; +import { observer } from "mobx-react-lite"; +import { useSecurity } from "@webiny/app-admin"; +import { ReactComponent as ArrowOutwardIcon } from "@webiny/icons/arrow_outward.svg"; +import { ReactComponent as CheckCircleIcon } from "@webiny/icons/check_circle.svg"; +import { ReactComponent as MoreHorizIcon } from "@webiny/icons/more_horiz.svg"; +import { ReactComponent as LinkIcon } from "@webiny/icons/link.svg"; +import { ReactComponent as DeleteIcon } from "@webiny/icons/delete.svg"; +import { ReactComponent as EditIcon } from "@webiny/icons/edit.svg"; +import type { CommentsPresenter } from "../abstractions.js"; +import { avatarColor, formatTimestamp, initials } from "../styles.js"; +import { AutoTextarea } from "./AutoTextarea.js"; +import { MentionTextarea } from "./MentionTextarea.js"; +import { COLLAB_THREAD_PARAM, COLLAB_FIELD_PARAM } from "~/constants.js"; +import type { CollabMessage, CollabThread } from "~/types.js"; + +interface Props { + presenter: CommentsPresenter.Interface; + thread: CollabThread; + onJumpToField: (locator: string) => void; +} + +const fieldLabel = (thread: CollabThread): string => { + const path = thread.anchor.path || []; + const label = thread.anchor.label || thread.locator; + return [...path, label].join(" › "); +}; + +// Highlights @mentions. Known display names (matched longest-first) are highlighted in full, +// e.g. "@John Doe"; anything else falls back to the single `@token`. +const renderBody = (body: string, names: string[]) => { + const known = [...names].filter(Boolean).sort((a, b) => b.length - a.length); + const parts: React.ReactNode[] = []; + let buffer = ""; + let key = 0; + let i = 0; + + const flush = () => { + if (buffer) { + parts.push({buffer}); + buffer = ""; + } + }; + + while (i < body.length) { + if (body[i] === "@") { + const rest = body.slice(i + 1); + const name = known.find(candidate => rest.startsWith(candidate)); + const token = name ?? rest.match(/^[^\s]+/)?.[0]; + if (token) { + flush(); + parts.push( + + @{token} + + ); + i += 1 + token.length; + continue; + } + } + buffer += body[i]; + i++; + } + + flush(); + return parts; +}; + +interface MessageProps { + presenter: CommentsPresenter.Interface; + threadId: string; + message: CollabMessage; + nested: boolean; + canManage: boolean; + mentionNames: string[]; +} + +const Message = ({ + presenter, + threadId, + message, + nested, + canManage, + mentionNames +}: MessageProps) => { + const [editing, setEditing] = useState(false); + const [draft, setDraft] = useState(message.body); + const [busy, setBusy] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + + const rootClassName = [ + "wby-collab-msg", + nested ? "wby-collab-reply" : "", + canManage ? "wby-collab-msg--manageable" : "", + menuOpen ? "is-menu-open" : "" + ] + .filter(Boolean) + .join(" "); + + const startEdit = () => { + setDraft(message.body); + setEditing(true); + }; + + const saveEdit = async () => { + if (!draft.trim() || busy) { + return; + } + setBusy(true); + try { + await presenter.submitMessageEdit(threadId, message.id, draft); + setEditing(false); + } finally { + setBusy(false); + } + }; + + return ( +
+ + {initials(message.createdBy.displayName)} + +
+
+ {message.createdBy.displayName} + + + {formatTimestamp(message.createdOn)} + + {canManage && !editing ? ( + + + {menuOpen ? ( + <> +
setMenuOpen(false)} + /> +
+ + +
+ + ) : null} + + ) : null} + +
+ {editing ? ( +
+ { + event.stopPropagation(); + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + void saveEdit(); + } + if (event.key === "Escape") { + setEditing(false); + } + }} + /> +
+ + +
+
+ ) : ( +
+ {renderBody(message.body, mentionNames)} +
+ )} +
+
+ ); +}; + +export const ThreadCard = observer((props: Props) => { + const { presenter, thread, onJumpToField } = props; + const { identity } = useSecurity(); + const currentUserId = identity?.id; + const [reply, setReply] = useState(""); + const [replyMentions, setReplyMentions] = useState([]); + const [busy, setBusy] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [copied, setCopied] = useState(false); + + const messages = thread.messages.filter(message => !message.deleted); + const mentionNames = presenter.vm.mentionableUsers.map(user => user.displayName); + + const highlighted = presenter.vm.highlightThreadId === thread.id; + const rootRef = useRef(null); + + useEffect(() => { + if (!highlighted || !rootRef.current) { + return; + } + rootRef.current.scrollIntoView({ behavior: "smooth", block: "center" }); + const timer = window.setTimeout(() => presenter.clearHighlight(), 2600); + return () => window.clearTimeout(timer); + }, [highlighted, presenter]); + + const rootClassName = [ + "wby-collab-thread", + thread.resolved ? "wby-collab-thread--resolved" : "", + highlighted ? "wby-collab-thread--highlight" : "" + ] + .filter(Boolean) + .join(" "); + + // Builds a shareable link to this thread by augmenting the current entry URL with the + // deep-link query params. Opening it re-runs the panel-open + highlight + field-scroll flow. + const copyThreadLink = () => { + const url = new URL(window.location.href); + url.searchParams.set(COLLAB_THREAD_PARAM, thread.id); + if (thread.locator) { + url.searchParams.set(COLLAB_FIELD_PARAM, thread.locator); + } else { + url.searchParams.delete(COLLAB_FIELD_PARAM); + } + const done = () => { + setCopied(true); + window.setTimeout(() => { + setCopied(false); + setMenuOpen(false); + }, 1200); + }; + if (typeof navigator !== "undefined" && navigator.clipboard) { + navigator.clipboard.writeText(url.toString()).then(done, done); + } else { + done(); + } + }; + + const submitReply = async () => { + if (!reply.trim() || busy) { + return; + } + setBusy(true); + try { + await presenter.submitReply(thread.id, reply, replyMentions); + setReply(""); + setReplyMentions([]); + } finally { + setBusy(false); + } + }; + + return ( +
+
+ {thread.locator ? ( + + ) : ( + + {fieldLabel(thread)} + + )} + +
+ + + + {menuOpen ? ( + <> +
setMenuOpen(false)} + /> +
+ + +
+ +
+ + ) : null} +
+
+ + {thread.anchor.exists ? null : ( +
+ This field no longer exists in the current revision. +
+ )} + + {messages.map((message, index) => ( + 0} + canManage={!!currentUserId && currentUserId === message.createdBy.id} + mentionNames={mentionNames} + /> + ))} + + {thread.resolved ? ( +
+ Resolved by {thread.resolvedBy?.displayName || "someone"} +
+ ) : ( +
+ + setReplyMentions(current => + current.includes(userId) ? current : [...current, userId] + ) + } + onKeyDown={event => { + event.stopPropagation(); + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + void submitReply(); + } + }} + /> +
+ )} +
+ ); +}); diff --git a/packages/app-collaboration/src/presentation/comments/feature.ts b/packages/app-collaboration/src/presentation/comments/feature.ts new file mode 100644 index 00000000000..b0c1ad7cb11 --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/feature.ts @@ -0,0 +1,15 @@ +import { createFeature } from "@webiny/feature/admin"; +import { CommentsPresenter as PresenterAbstraction } from "./abstractions.js"; +import { CommentsPresenter } from "./CommentsPresenter.js"; + +export const CommentsPresenterFeature = createFeature({ + name: "Collaboration/CommentsPresenter", + register(container) { + container.register(CommentsPresenter).inSingletonScope(); + }, + resolve(container) { + return { + presenter: container.resolve(PresenterAbstraction) + }; + } +}); diff --git a/packages/app-collaboration/src/presentation/comments/styles.ts b/packages/app-collaboration/src/presentation/comments/styles.ts new file mode 100644 index 00000000000..269e610d82a --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/styles.ts @@ -0,0 +1,225 @@ +/** + * Collaboration panel styles, injected once. Uses the admin theme CSS variables (same tokens the + * design mockup uses) so hover states, focus rings, transitions and keyframes are possible — + * which inline styles cannot express. + */ +const CSS = ` +.wby-collab-panel { display:flex; flex-direction:column; height:100%; background:var(--color-neutral-0); border-left:1px solid var(--color-neutral-200); font-family:var(--font-sans); color:var(--text-color-neutral-primary); } +.wby-collab-panel__header { flex:0 0 auto; display:flex; align-items:center; justify-content:space-between; padding:14px 16px 12px; border-bottom:1px solid var(--color-neutral-200); } +.wby-collab-panel__title { display:flex; align-items:center; gap:8px; } +.wby-collab-panel__title-text { font-size:15px; font-weight:600; } +.wby-collab-count { display:inline-flex; align-items:center; justify-content:center; min-width:20px; height:20px; padding:0 6px; background:var(--color-neutral-100); border-radius:10px; font-size:12px; font-weight:600; color:var(--color-neutral-600); } +.wby-collab-actions { display:flex; align-items:center; gap:2px; } + +.wby-collab-iconbtn { display:flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:6px; cursor:pointer; color:var(--color-neutral-500); background:transparent; border:none; padding:0; transition:background .12s ease, color .12s ease; } +.wby-collab-iconbtn:hover { background:var(--color-neutral-100); color:var(--color-neutral-800); } +.wby-collab-iconbtn svg { width:18px; height:18px; fill:currentColor; } + +.wby-collab-overview { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:10px 16px; background:var(--color-neutral-50); border-bottom:1px solid var(--color-neutral-200); font-size:13px; } +.wby-collab-overview__count { display:inline-flex; align-items:center; gap:6px; font-weight:600; color:var(--color-neutral-800); } +.wby-collab-dot { width:8px; height:8px; border-radius:50%; background:var(--color-primary); display:inline-block; } +.wby-collab-overview__sep { color:var(--color-neutral-400); } +.wby-collab-overview__fields { color:var(--color-neutral-500); } +.wby-collab-iconbtn:disabled { cursor:default; opacity:.6; } +.wby-collab-spin { animation:wby-collab-spin .8s linear infinite; } +@keyframes wby-collab-spin { to { transform:rotate(360deg); } } +.wby-collab-filter { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:10px 16px; background:var(--color-primary-100); border-bottom:1px solid var(--color-primary-200); font-size:13px; } +.wby-collab-filter__label { flex:1; min-width:0; color:var(--color-neutral-800); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.wby-collab-filter__label strong { font-weight:600; } +.wby-collab-filter__clear { flex:0 0 auto; background:none; border:none; padding:0; cursor:pointer; font-family:inherit; font-size:13px; font-weight:600; color:var(--color-primary); } +.wby-collab-filter__clear:hover { text-decoration:underline; } +.wby-collab-sort { margin-left:auto; display:flex; align-items:center; gap:4px; font-weight:600; color:var(--color-neutral-600); background:none; border:none; cursor:pointer; font-size:13px; font-family:inherit; padding:0; } +.wby-collab-sort svg { width:16px; height:16px; fill:currentColor; } + +.wby-collab-list { flex:1 1 auto; overflow-y:auto; padding:16px; display:flex; flex-direction:column; gap:18px; } + +.wby-collab-chip { display:inline-flex; align-items:center; gap:6px; padding:3px 10px; border-radius:6px; font-size:12px; font-weight:600; cursor:pointer; border:none; background:var(--color-neutral-100); color:var(--color-neutral-700); transition:background .12s ease; max-width:100%; font-family:inherit; } +.wby-collab-chip:hover { background:var(--color-neutral-200); } +.wby-collab-chip--active { background:var(--color-primary-100); color:var(--color-primary-700); cursor:default; } +.wby-collab-chip--entry { background:var(--color-neutral-100); color:var(--color-neutral-600); cursor:default; } +.wby-collab-chip--entry:hover { background:var(--color-neutral-100); } +.wby-collab-chip__x { display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; margin-left:2px; padding:0; border:none; background:transparent; color:inherit; cursor:pointer; border-radius:4px; transition:background .12s ease; } +.wby-collab-chip__x:hover { background:var(--color-primary-200); } +.wby-collab-chip__x svg { width:12px; height:12px; fill:currentColor; } +.wby-collab-chip__label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.wby-collab-chip__jump { width:14px; height:14px; fill:var(--color-neutral-500); flex:0 0 auto; } + +.wby-collab-thread { border:1px solid var(--color-neutral-200); border-radius:10px; padding:12px 12px 8px; display:flex; flex-direction:column; gap:12px; transition:box-shadow .12s ease, border-color .12s ease; } +.wby-collab-thread:hover { box-shadow:var(--shadow-sm); border-color:var(--color-neutral-300); } +.wby-collab-thread--resolved { opacity:.72; } +.wby-collab-thread--highlight { border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-200); animation:wby-collab-pulse 1s ease-in-out 2; } +@keyframes wby-collab-pulse { 0%,100% { box-shadow:0 0 0 3px var(--color-primary-200); } 50% { box-shadow:0 0 0 6px var(--color-primary-200); } } +.wby-collab-thread--resolved:hover { opacity:1; } +.wby-collab-thread__head { display:flex; align-items:center; justify-content:space-between; gap:8px; } +.wby-collab-thread__head-actions { display:flex; align-items:center; gap:6px; position:relative; } + +.wby-collab-pillbtn { display:flex; align-items:center; gap:5px; height:28px; padding:0 10px; border:1px solid var(--color-neutral-300); background:var(--color-neutral-0); border-radius:14px; font-size:12px; font-weight:600; color:var(--color-neutral-700); cursor:pointer; transition:background .12s ease, border-color .12s ease; font-family:inherit; } +.wby-collab-pillbtn:hover { background:var(--color-neutral-100); } +.wby-collab-pillbtn svg { width:16px; height:16px; } +.wby-collab-pillbtn svg.wby-collab-ok { fill:var(--color-success-600); } + +.wby-collab-menu { position:absolute; top:34px; right:0; width:190px; background:var(--color-neutral-0); border:1px solid var(--color-neutral-200); border-radius:8px; box-shadow:var(--shadow-lg); padding:4px; z-index:10; transform-origin:top right; animation:wby-collab-menu-in .12s ease; } +@keyframes wby-collab-menu-in { from { opacity:0; transform:scale(.97) translateY(-4px); } to { opacity:1; transform:scale(1) translateY(0); } } +.wby-collab-menuitem { display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:6px; font-size:13px; font-weight:500; color:var(--color-neutral-800); cursor:pointer; background:none; border:none; width:100%; text-align:left; font-family:inherit; } +.wby-collab-menuitem:hover { background:var(--color-neutral-100); } +.wby-collab-menuitem svg { width:18px; height:18px; fill:var(--color-neutral-600); flex:0 0 auto; } +.wby-collab-menuitem svg.wby-collab-ok { fill:var(--color-success-600); } +.wby-collab-menuitem--danger { color:var(--color-destructive); } +.wby-collab-menuitem--danger svg { fill:var(--color-destructive); } +.wby-collab-menuitem--danger:hover { background:var(--color-destructive-100); } +.wby-collab-menu--compact { top:28px; width:152px; } +.wby-collab-menu__divider { height:1px; background:var(--color-neutral-200); margin:4px 6px; } +.wby-collab-backdrop { position:fixed; inset:0; z-index:9; } + +.wby-collab-msg { display:flex; gap:10px; } +.wby-collab-avatar { flex:0 0 auto; width:28px; height:28px; border-radius:50%; color:#fff; font-size:11px; font-weight:600; display:flex; align-items:center; justify-content:center; } +.wby-collab-avatar--sm { width:24px; height:24px; font-size:10px; } +.wby-collab-msg__main { flex:1; min-width:0; } +.wby-collab-msg__meta { display:flex; align-items:center; gap:6px; min-height:24px; } +.wby-collab-msg__name { font-size:13px; font-weight:600; } +.wby-collab-msg__right { margin-left:auto; display:flex; align-items:center; gap:4px; padding-left:8px; } +.wby-collab-msg__time { font-size:12px; color:var(--color-neutral-500); white-space:nowrap; } +.wby-collab-msg__menu-anchor { display:none; position:relative; align-items:center; } +.wby-collab-msg--manageable:hover .wby-collab-msg__time { display:none; } +.wby-collab-msg--manageable:hover .wby-collab-msg__menu-anchor { display:flex; } +.wby-collab-msg.is-menu-open .wby-collab-msg__time { display:none; } +.wby-collab-msg.is-menu-open .wby-collab-msg__menu-anchor { display:flex; } +.wby-collab-msgbtn { display:flex; align-items:center; justify-content:center; width:24px; height:24px; border:none; background:transparent; color:var(--color-neutral-500); border-radius:4px; cursor:pointer; padding:0; transition:background .12s ease, color .12s ease; } +.wby-collab-msgbtn:hover { background:var(--color-neutral-100); color:var(--color-neutral-800); } +.wby-collab-msgbtn svg { width:14px; height:14px; fill:currentColor; } +.wby-collab-msg__edit { display:flex; flex-direction:column; gap:6px; margin-top:4px; } +.wby-collab-msg__edit-actions { display:flex; gap:6px; justify-content:flex-end; } +.wby-collab-msg__body { font-size:14px; line-height:20px; color:var(--color-neutral-800); margin-top:2px; overflow-wrap:anywhere; } +.wby-collab-msg__body--resolved { text-decoration:line-through; text-decoration-color:var(--color-neutral-400); } +.wby-collab-reply { padding-left:10px; border-left:2px solid var(--color-neutral-200); margin-left:14px; } +.wby-collab-resolved-by { font-size:12px; color:var(--color-neutral-500); } + +.wby-collab-replybar { display:flex; align-items:center; gap:8px; padding:2px 0 4px; } +.wby-collab-input { flex:1; height:32px; padding:0 12px; font-size:13px; border:1px solid var(--color-neutral-300); border-radius:16px; outline:none; font-family:inherit; transition:border-color .12s ease, box-shadow .12s ease; background:var(--color-neutral-0); } +.wby-collab-input:focus { border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-200); } + +.wby-collab-composer { display:flex; flex-direction:column; gap:10px; } +.wby-collab-composer__box { border:2px solid var(--color-neutral-300); border-radius:10px; padding:12px; box-shadow:none; display:flex; flex-direction:column; gap:10px; transition:border-color .15s ease, box-shadow .15s ease; } +.wby-collab-composer__box:hover, .wby-collab-composer__box:focus-within { border-color:var(--color-primary); box-shadow:0 0 0 4px var(--color-primary-200); } +.wby-collab-composer__row { display:flex; gap:10px; } +.wby-collab-textarea { flex:1; min-height:44px; border:none; outline:none; resize:none; overflow-y:hidden; font-size:14px; line-height:20px; font-family:inherit; color:var(--color-neutral-900); background:transparent; padding:4px 0; box-sizing:border-box; } +.wby-collab-reply-input { width:100%; min-height:32px; box-sizing:border-box; padding:6px 12px; font-size:13px; line-height:18px; border:1px solid var(--color-neutral-300); border-radius:16px; outline:none; font-family:inherit; resize:none; overflow-y:hidden; background:var(--color-neutral-0); transition:border-color .12s ease, box-shadow .12s ease; } +.wby-collab-reply-input:focus { border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-200); } +.wby-collab-composer__actions { display:flex; justify-content:flex-end; gap:8px; align-items:center; } +.wby-collab-btn { height:30px; padding:0 14px; border:none; border-radius:6px; font-size:13px; font-weight:600; cursor:pointer; transition:background .12s ease; font-family:inherit; } +.wby-collab-btn--primary { background:var(--color-primary); color:#fff; } +.wby-collab-btn--primary:hover { background:var(--color-primary-700, var(--color-primary)); } +.wby-collab-btn--primary:disabled { opacity:.55; cursor:default; } +.wby-collab-btn--ghost { background:transparent; color:var(--color-neutral-600); } +.wby-collab-btn--ghost:hover { background:var(--color-neutral-100); } +.wby-collab-select { height:30px; border-radius:6px; border:1px solid var(--color-neutral-300); padding:0 8px; font-size:13px; font-family:inherit; background:var(--color-neutral-0); color:var(--color-neutral-800); max-width:100%; } + +.wby-collab-section { display:flex; align-items:center; gap:6px; cursor:pointer; color:var(--color-neutral-600); user-select:none; background:none; border:none; padding:0; font-family:inherit; } +.wby-collab-section svg { width:18px; height:18px; fill:currentColor; transition:transform .15s ease; } +.wby-collab-section__label { font-size:12px; font-weight:600; letter-spacing:.03em; text-transform:uppercase; } +.wby-collab-group { display:flex; flex-direction:column; gap:12px; } + +.wby-collab-banner { display:flex; align-items:center; gap:6px; padding:6px 8px; background:var(--color-warning-100); border-radius:6px; font-size:12px; color:var(--color-warning-700); } +.wby-collab-banner svg { width:15px; height:15px; fill:currentColor; flex:0 0 auto; } + +.wby-collab-error { color:var(--color-destructive); font-size:13px; background:var(--color-destructive-100); padding:8px 10px; border-radius:6px; } +.wby-collab-empty { color:var(--color-neutral-500); font-size:13px; text-align:center; padding:24px 0; } + +.wby-collab-mention { color:var(--color-primary); font-weight:600; } + +.wby-collab-mention-wrap { position:relative; flex:1; min-width:0; } +.wby-collab-mention-wrap textarea { width:100%; box-sizing:border-box; } +.wby-collab-mention-menu { position:absolute; left:0; top:calc(100% + 4px); width:100%; max-height:220px; overflow-y:auto; background:var(--color-neutral-0); border:1px solid var(--color-neutral-200); border-radius:8px; box-shadow:var(--shadow-lg); padding:4px; z-index:20; } +.wby-collab-mention-item { display:flex; align-items:center; gap:8px; width:100%; padding:6px 8px; border:none; background:none; cursor:pointer; border-radius:6px; text-align:left; font-family:inherit; } +.wby-collab-mention-item:hover, .wby-collab-mention-item.is-active { background:var(--color-neutral-100); } +.wby-collab-mention-item__main { display:flex; flex-direction:column; min-width:0; } +.wby-collab-mention-item__name { font-size:13px; font-weight:600; color:var(--color-neutral-900); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.wby-collab-mention-item__email { font-size:11px; color:var(--color-neutral-500); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } + +/* In-form per-field markers */ +.wby-collab-field { position:relative; } +.wby-collab-marker { position:absolute; top:-7px; right:8px; display:inline-flex; align-items:center; gap:5px; height:26px; padding:0 10px; border-radius:13px; cursor:pointer; font-size:12px; font-weight:600; box-shadow:var(--shadow-md); background:var(--color-neutral-0); border:1px solid var(--color-neutral-300); z-index:3; } +.wby-collab-marker svg { width:15px; height:15px; } +.wby-collab-marker--add { color:var(--color-primary); opacity:0; transform:translateY(3px); transition:opacity .12s ease, transform .12s ease, background .12s ease; } +.wby-collab-field:hover .wby-collab-marker--add { opacity:1; transform:translateY(0); } +.wby-collab-marker--add:hover { background:var(--color-neutral-50); } +.wby-collab-marker--add svg { fill:var(--color-primary); } +.wby-collab-marker--count { color:var(--color-neutral-800); } +.wby-collab-marker--count svg { fill:var(--color-neutral-600); } +.wby-collab-marker__badge { display:inline-flex; align-items:center; justify-content:center; min-width:16px; height:16px; padding:0 4px; background:var(--color-primary); color:#fff; border-radius:8px; font-size:10px; font-weight:700; } +`; + +let injected = false; + +export const ensureCollabStyles = (): void => { + if (injected || typeof document === "undefined") { + return; + } + if (document.getElementById("wby-collab-styles")) { + injected = true; + return; + } + const style = document.createElement("style"); + style.id = "wby-collab-styles"; + style.textContent = CSS; + document.head.appendChild(style); + injected = true; +}; + +// Inject on import. +ensureCollabStyles(); + +/** Deterministic avatar color from a display name. */ +export const avatarColor = (name: string): string => { + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = (hash * 31 + name.charCodeAt(i)) % 360; + } + return `hsl(${hash} 42% 50%)`; +}; + +const DAY_MS = 86_400_000; +const YEAR_MS = 365 * DAY_MS; + +/** + * Formats a comment timestamp: + * - < 24h ago -> relative ("just now", "5m ago", "3h ago") + * - < 12 months -> short date ("Jun 25") + * - otherwise -> short date with year ("Jun 25, 2026") + */ +export const formatTimestamp = (iso: string): string => { + const then = new Date(iso).getTime(); + if (Number.isNaN(then)) { + return ""; + } + const diff = Date.now() - then; + + if (diff < DAY_MS) { + const minutes = Math.floor(diff / 60_000); + if (minutes < 1) { + return "just now"; + } + if (minutes < 60) { + return `${minutes}m ago`; + } + return `${Math.floor(minutes / 60)}h ago`; + } + + const date = new Date(then); + const options: Intl.DateTimeFormatOptions = + diff < YEAR_MS + ? { month: "short", day: "numeric" } + : { month: "short", day: "numeric", year: "numeric" }; + return date.toLocaleDateString(undefined, options); +}; + +export const initials = (name: string): string => { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) { + return "?"; + } + if (parts.length === 1) { + return parts[0].slice(0, 2).toUpperCase(); + } + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); +}; diff --git a/packages/app-collaboration/src/presentation/comments/useComments.ts b/packages/app-collaboration/src/presentation/comments/useComments.ts new file mode 100644 index 00000000000..727e9a60c1b --- /dev/null +++ b/packages/app-collaboration/src/presentation/comments/useComments.ts @@ -0,0 +1,6 @@ +import { useFeature } from "@webiny/app"; +import { CommentsPresenterFeature } from "./feature.js"; + +export const useCommentsPresenter = () => { + return useFeature(CommentsPresenterFeature).presenter; +}; diff --git a/packages/app-collaboration/src/types.ts b/packages/app-collaboration/src/types.ts new file mode 100644 index 00000000000..5f879a6b6f9 --- /dev/null +++ b/packages/app-collaboration/src/types.ts @@ -0,0 +1,73 @@ +export type CollabThreadType = "note" | "task"; + +export interface CollabIdentity { + id: string; + displayName: string; + type: string; +} + +export interface CollabMessage { + id: string; + body: string; + mentions: string[]; + createdBy: CollabIdentity; + createdOn: string; + deleted?: boolean | null; + deletedBy?: CollabIdentity | null; + deletedOn?: string | null; +} + +export interface CollabAnchor { + exists: boolean; + authorized: boolean; + label?: string | null; + path?: string[] | null; +} + +export interface CollabThread { + id: string; + contentType: string; + contentId: string; + locator: string; + type: CollabThreadType; + resolved: boolean; + resolvedBy?: CollabIdentity | null; + resolvedOn?: string | null; + assigneeId?: string | null; + dueDate?: string | null; + messages: CollabMessage[]; + createdBy: CollabIdentity; + createdOn: string; + anchor: CollabAnchor; +} + +export interface CollabUser { + id: string; + displayName: string; + email?: string | null; + avatar?: { src?: string } | null; +} + +export interface CollabThreadsMeta { + totalCount: number; + hasMoreItems: boolean; + cursor: string | null; +} + +export interface ListThreadsWhere { + contentType: string; + contentId: string; + type?: CollabThreadType; + resolved?: boolean; +} + +export interface CreateThreadInput { + contentType: string; + contentId: string; + locator: string; + type: CollabThreadType; + body: string; + mentions?: string[]; + assigneeId?: string | null; + dueDate?: string | null; +} diff --git a/packages/app-collaboration/tsconfig.build.json b/packages/app-collaboration/tsconfig.build.json new file mode 100644 index 00000000000..84d3470594e --- /dev/null +++ b/packages/app-collaboration/tsconfig.build.json @@ -0,0 +1,32 @@ +{ + "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-headless-cms/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/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/src"], + "@webiny/app-headless-cms/*": ["../app-headless-cms/src/*"], + "@webiny/app-headless-cms": ["../app-headless-cms/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"] + } + } +} diff --git a/packages/app-collaboration/tsconfig.json b/packages/app-collaboration/tsconfig.json new file mode 100644 index 00000000000..e18e00ae906 --- /dev/null +++ b/packages/app-collaboration/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../admin-ui" }, + { "path": "../app" }, + { "path": "../app-admin" }, + { "path": "../app-headless-cms" }, + { "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/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/src"], + "@webiny/app-headless-cms/*": ["../app-headless-cms/src/*"], + "@webiny/app-headless-cms": ["../app-headless-cms/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"] + } + } +} diff --git a/packages/app-collaboration/webiny.config.js b/packages/app-collaboration/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/app-collaboration/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-notifications/package.json b/packages/app-notifications/package.json new file mode 100644 index 00000000000..5742d9b3efc --- /dev/null +++ b/packages/app-notifications/package.json @@ -0,0 +1,40 @@ +{ + "name": "@webiny/app-notifications", + "version": "0.0.0", + "type": "module", + "exports": { + ".": "./index.js", + "./*": "./*" + }, + "description": "Notifications Admin (in-app notification inbox)", + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git", + "directory": "packages/app-notifications" + }, + "author": "Webiny Ltd", + "license": "MIT", + "dependencies": { + "@webiny/admin-ui": "0.0.0", + "@webiny/app": "0.0.0", + "@webiny/app-admin": "0.0.0", + "@webiny/feature": "0.0.0", + "@webiny/icons": "0.0.0", + "mobx": "^6.16.1", + "mobx-react-lite": "^4.1.1", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@types/react": "18.3.31", + "@webiny/build-tools": "0.0.0", + "rimraf": "^6.1.3", + "typescript": "7.0.2" + }, + "publishConfig": { + "access": "public" + }, + "webiny": { + "publishFrom": "dist" + } +} diff --git a/packages/app-notifications/src/app.tsx b/packages/app-notifications/src/app.tsx new file mode 100644 index 00000000000..fd538112a1a --- /dev/null +++ b/packages/app-notifications/src/app.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { RegisterFeature } from "@webiny/app-admin"; +import { NotificationsApiFeature } from "~/features/api/feature.js"; +import { NotificationsPresenterFeature } from "~/presentation/notifications/feature.js"; +import { NotificationsHeaderDecorator } from "~/presentation/notifications/components/NotificationsHeaderDecorator.js"; + +/** + * Mount once in the admin app. Registers the notifications data-access + presenter features and + * injects the notifications bell + inbox panel into the admin top bar (via the user-menu decorator). + */ +export const NotificationsAdminApp = () => { + return ( + <> + + + + + ); +}; diff --git a/packages/app-notifications/src/features/api/NotificationsApi.ts b/packages/app-notifications/src/features/api/NotificationsApi.ts new file mode 100644 index 00000000000..dfe0ea1caac --- /dev/null +++ b/packages/app-notifications/src/features/api/NotificationsApi.ts @@ -0,0 +1,30 @@ +import { NotificationsApi as ApiAbstraction, NotificationsGateway } from "./abstractions.js"; +import type { IListNotificationsParams } from "./abstractions.js"; + +class NotificationsApiImpl implements ApiAbstraction.Interface { + constructor(private gateway: NotificationsGateway.Interface) {} + + list(params: IListNotificationsParams) { + return this.gateway.list(params); + } + counts() { + return this.gateway.counts(); + } + markRead(id: string) { + return this.gateway.markRead(id); + } + markAllRead() { + return this.gateway.markAllRead(); + } + archive(id: string) { + return this.gateway.archive(id); + } + unarchive(id: string) { + return this.gateway.unarchive(id); + } +} + +export const NotificationsApi = ApiAbstraction.createImplementation({ + implementation: NotificationsApiImpl, + dependencies: [NotificationsGateway] +}); diff --git a/packages/app-notifications/src/features/api/NotificationsGateway.ts b/packages/app-notifications/src/features/api/NotificationsGateway.ts new file mode 100644 index 00000000000..33a63571411 --- /dev/null +++ b/packages/app-notifications/src/features/api/NotificationsGateway.ts @@ -0,0 +1,186 @@ +import { MainGraphQLClient } from "@webiny/app/features/mainGraphQLClient/abstractions.js"; +import { NotificationsGateway as GatewayAbstraction } from "./abstractions.js"; +import type { IListNotificationsParams, IListNotificationsResult } from "./abstractions.js"; +import type { Notification, NotificationCounts, NotificationsMeta } from "~/types.js"; + +interface GqlError { + code: string; + message: string; + data?: unknown; +} + +const unwrap = (envelope: { data: T | null; error: GqlError | null }): T => { + if (envelope.error) { + throw new Error(envelope.error.message); + } + return envelope.data as T; +}; + +const NOTIFICATION_FIELDS = /* GraphQL */ ` + { + id + type + actor { + id + displayName + type + } + title + snippet + link { + app + contentType + contentId + locator + threadId + } + read + readOn + archived + archivedOn + createdOn + } +`; + +const LIST_QUERY = /* GraphQL */ ` + query ListNotifications($where: ListNotificationsWhereInput, $limit: Int, $after: String) { + notifications { + listNotifications(where: $where, limit: $limit, after: $after) { + data ${NOTIFICATION_FIELDS} + meta { totalCount hasMoreItems cursor } + error { code message } + } + } + } +`; + +const COUNTS_QUERY = /* GraphQL */ ` + query NotificationCounts { + notifications { + notificationCounts { + data { + inbox + archive + unread + } + error { + code + message + } + } + } + } +`; + +const MARK_READ = /* GraphQL */ ` + mutation MarkNotificationRead($id: ID!) { + notifications { markNotificationRead(id: $id) { data ${NOTIFICATION_FIELDS} error { code message } } } + } +`; + +const MARK_ALL_READ = /* GraphQL */ ` + mutation MarkAllNotificationsRead { + notifications { + markAllNotificationsRead { + data + error { + code + message + } + } + } + } +`; + +const ARCHIVE = /* GraphQL */ ` + mutation ArchiveNotification($id: ID!) { + notifications { archiveNotification(id: $id) { data ${NOTIFICATION_FIELDS} error { code message } } } + } +`; + +const UNARCHIVE = /* GraphQL */ ` + mutation UnarchiveNotification($id: ID!) { + notifications { unarchiveNotification(id: $id) { data ${NOTIFICATION_FIELDS} error { code message } } } + } +`; + +class NotificationsGatewayImpl implements GatewayAbstraction.Interface { + constructor(private client: MainGraphQLClient.Interface) {} + + async list(params: IListNotificationsParams): Promise { + const response = await this.client.execute<{ + notifications: { + listNotifications: { + data: Notification[] | null; + meta: NotificationsMeta | null; + error: GqlError | null; + }; + }; + }>({ + query: LIST_QUERY, + variables: { + where: { archived: params.archived, read: params.read }, + limit: params.limit, + after: params.after + } + }); + const envelope = response.notifications.listNotifications; + if (envelope.error) { + throw new Error(envelope.error.message); + } + return { + items: envelope.data || [], + meta: envelope.meta || { totalCount: 0, hasMoreItems: false, cursor: null } + }; + } + + async counts(): Promise { + const response = await this.client.execute<{ + notifications: { + notificationCounts: { data: NotificationCounts | null; error: GqlError | null }; + }; + }>({ query: COUNTS_QUERY }); + return unwrap(response.notifications.notificationCounts); + } + + async markRead(id: string): Promise { + const response = await this.client.execute<{ + notifications: { + markNotificationRead: { data: Notification | null; error: GqlError | null }; + }; + }>({ query: MARK_READ, variables: { id } }); + return unwrap(response.notifications.markNotificationRead); + } + + async markAllRead(): Promise { + const response = await this.client.execute<{ + notifications: { + markAllNotificationsRead: { data: number | null; error: GqlError | null }; + }; + }>({ query: MARK_ALL_READ }); + return unwrap(response.notifications.markAllNotificationsRead) ?? 0; + } + + async archive(id: string): Promise { + const response = await this.client.execute<{ + notifications: { + archiveNotification: { data: Notification | null; error: GqlError | null }; + }; + }>({ query: ARCHIVE, variables: { id } }); + return unwrap(response.notifications.archiveNotification); + } + + async unarchive(id: string): Promise { + const response = await this.client.execute<{ + notifications: { + unarchiveNotification: { data: Notification | null; error: GqlError | null }; + }; + }>({ query: UNARCHIVE, variables: { id } }); + return unwrap(response.notifications.unarchiveNotification); + } +} + +export const NotificationsGateway = GatewayAbstraction.createImplementation({ + implementation: NotificationsGatewayImpl, + dependencies: [MainGraphQLClient] +}); diff --git a/packages/app-notifications/src/features/api/abstractions.ts b/packages/app-notifications/src/features/api/abstractions.ts new file mode 100644 index 00000000000..97401983fdc --- /dev/null +++ b/packages/app-notifications/src/features/api/abstractions.ts @@ -0,0 +1,35 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { Notification, NotificationCounts, NotificationsMeta } from "~/types.js"; + +export interface IListNotificationsParams { + archived?: boolean; + read?: boolean; + limit?: number; + after?: string | null; +} + +export interface IListNotificationsResult { + items: Notification[]; + meta: NotificationsMeta; +} + +export interface INotificationsApi { + list(params: IListNotificationsParams): Promise; + counts(): Promise; + markRead(id: string): Promise; + markAllRead(): Promise; + archive(id: string): Promise; + unarchive(id: string): Promise; +} + +export const NotificationsGateway = createAbstraction("Notifications/Gateway"); +export namespace NotificationsGateway { + export type Interface = INotificationsApi; +} + +export const NotificationsApi = createAbstraction("Notifications/Api"); +export namespace NotificationsApi { + export type Interface = INotificationsApi; + export type ListParams = IListNotificationsParams; + export type ListResult = IListNotificationsResult; +} diff --git a/packages/app-notifications/src/features/api/feature.ts b/packages/app-notifications/src/features/api/feature.ts new file mode 100644 index 00000000000..e74107c0f75 --- /dev/null +++ b/packages/app-notifications/src/features/api/feature.ts @@ -0,0 +1,17 @@ +import { createFeature } from "@webiny/feature/admin"; +import { NotificationsApi as ApiAbstraction } from "./abstractions.js"; +import { NotificationsGateway } from "./NotificationsGateway.js"; +import { NotificationsApi } from "./NotificationsApi.js"; + +export const NotificationsApiFeature = createFeature({ + name: "Notifications/Api", + register(container) { + container.register(NotificationsGateway).inSingletonScope(); + container.register(NotificationsApi).inSingletonScope(); + }, + resolve(container) { + return { + api: container.resolve(ApiAbstraction) + }; + } +}); diff --git a/packages/app-notifications/src/index.tsx b/packages/app-notifications/src/index.tsx new file mode 100644 index 00000000000..d4cf850b43d --- /dev/null +++ b/packages/app-notifications/src/index.tsx @@ -0,0 +1,5 @@ +export { NotificationsAdminApp } from "./app.js"; +export { NotificationsApiFeature } from "./features/api/feature.js"; +export { NotificationsPresenterFeature } from "./presentation/notifications/feature.js"; +export { useNotificationsPresenter } from "./presentation/notifications/useNotifications.js"; +export type { Notification, NotificationType, NotificationCounts } from "./types.js"; diff --git a/packages/app-notifications/src/presentation/notifications/NotificationsPresenter.ts b/packages/app-notifications/src/presentation/notifications/NotificationsPresenter.ts new file mode 100644 index 00000000000..fdf202047f6 --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/NotificationsPresenter.ts @@ -0,0 +1,128 @@ +import { makeAutoObservable, runInAction, toJS } from "mobx"; +import { NotificationsPresenter as PresenterAbstraction } from "./abstractions.js"; +import type { NotificationsTab } from "./abstractions.js"; +import { NotificationsApi } from "~/features/api/abstractions.js"; +import type { Notification, NotificationCounts } from "~/types.js"; + +const EMPTY_COUNTS: NotificationCounts = { inbox: 0, archive: 0, unread: 0 }; + +class NotificationsPresenterImpl implements PresenterAbstraction.Interface { + private open = false; + private loading = false; + private error: string | null = null; + private tab: NotificationsTab = "inbox"; + private unreadOnly = false; + private counts: NotificationCounts = EMPTY_COUNTS; + private items: Notification[] = []; + private loaded = false; + + constructor(private api: NotificationsApi.Interface) { + makeAutoObservable(this, { api: false }); + } + + get vm(): PresenterAbstraction.ViewModel { + return { + open: this.open, + loading: this.loading, + error: this.error, + tab: this.tab, + unreadOnly: this.unreadOnly, + counts: toJS(this.counts), + items: toJS(this.items) + }; + } + + async init() { + if (this.loaded) { + return; + } + this.loaded = true; + await this.refreshCounts(); + } + + openPanel() { + this.open = true; + void this.reload(); + } + + closePanel() { + this.open = false; + } + + togglePanel() { + this.open ? this.closePanel() : this.openPanel(); + } + + setTab(tab: NotificationsTab) { + this.tab = tab; + void this.reload(); + } + + setUnreadOnly(value: boolean) { + this.unreadOnly = value; + void this.reload(); + } + + async reload() { + runInAction(() => { + this.loading = true; + this.error = null; + }); + try { + const result = await this.api.list({ + archived: this.tab === "archive", + read: this.unreadOnly ? false : undefined, + limit: 50 + }); + runInAction(() => { + this.items = result.items; + this.loading = false; + }); + } catch (err) { + runInAction(() => { + this.error = (err as Error).message; + this.loading = false; + }); + } + } + + async refresh() { + await Promise.all([this.reload(), this.refreshCounts()]); + } + + async markRead(id: string) { + await this.api.markRead(id); + await Promise.all([this.reload(), this.refreshCounts()]); + } + + async markAllRead() { + await this.api.markAllRead(); + await Promise.all([this.reload(), this.refreshCounts()]); + } + + async archive(id: string) { + const notification = this.items.find(item => item.id === id); + if (notification?.archived) { + await this.api.unarchive(id); + } else { + await this.api.archive(id); + } + await Promise.all([this.reload(), this.refreshCounts()]); + } + + private async refreshCounts() { + try { + const counts = await this.api.counts(); + runInAction(() => { + this.counts = counts; + }); + } catch { + // best-effort + } + } +} + +export const NotificationsPresenter = PresenterAbstraction.createImplementation({ + implementation: NotificationsPresenterImpl, + dependencies: [NotificationsApi] +}); diff --git a/packages/app-notifications/src/presentation/notifications/abstractions.ts b/packages/app-notifications/src/presentation/notifications/abstractions.ts new file mode 100644 index 00000000000..b1a2d1f0138 --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/abstractions.ts @@ -0,0 +1,39 @@ +import { createAbstraction } from "@webiny/feature/admin"; +import type { Notification, NotificationCounts } from "~/types.js"; + +export type NotificationsTab = "inbox" | "archive"; + +export interface INotificationsViewModel { + open: boolean; + loading: boolean; + error: string | null; + tab: NotificationsTab; + unreadOnly: boolean; + counts: NotificationCounts; + items: Notification[]; +} + +export interface INotificationsPresenter { + readonly vm: INotificationsViewModel; + init(): Promise; + openPanel(): void; + closePanel(): void; + togglePanel(): void; + setTab(tab: NotificationsTab): void; + setUnreadOnly(value: boolean): void; + reload(): Promise; + /** Reload the current list and refresh the inbox/archive/unread counts. */ + refresh(): Promise; + markRead(id: string): Promise; + markAllRead(): Promise; + archive(id: string): Promise; +} + +export const NotificationsPresenter = + createAbstraction("Notifications/Presenter"); + +export namespace NotificationsPresenter { + export type Interface = INotificationsPresenter; + export type ViewModel = INotificationsViewModel; + export type Tab = NotificationsTab; +} diff --git a/packages/app-notifications/src/presentation/notifications/components/NotificationItem.tsx b/packages/app-notifications/src/presentation/notifications/components/NotificationItem.tsx new file mode 100644 index 00000000000..d294e0b72f4 --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/components/NotificationItem.tsx @@ -0,0 +1,153 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { Route, useRouter } from "@webiny/app-admin"; +import { ReactComponent as MentionIcon } from "@webiny/icons/alternate_email.svg"; +import { ReactComponent as ReplyIcon } from "@webiny/icons/reply.svg"; +import { ReactComponent as ReviewIcon } from "@webiny/icons/rate_review.svg"; +import { ReactComponent as ApprovedIcon } from "@webiny/icons/check_circle.svg"; +import { ReactComponent as RejectedIcon } from "@webiny/icons/cancel.svg"; +import { ReactComponent as ArchiveIcon } from "@webiny/icons/archive.svg"; +import { ReactComponent as UnarchiveIcon } from "@webiny/icons/unarchive.svg"; +import type { NotificationsPresenter } from "../abstractions.js"; +import { avatarColor, formatTimestamp, initials } from "../styles.js"; +import type { Notification, NotificationLink, NotificationType } from "~/types.js"; + +// Mirrors the Headless CMS "content entries" route so we can navigate without depending on +// @webiny/app-headless-cms. `goToRoute` resolves by the route name. +// Deep-link query params consumed by @webiny/app-collaboration to open + highlight the thread +// once the target entry loads. Duplicated (not imported) so notifications carries no dependency +// on collaboration. +const COLLAB_THREAD_PARAM = "commentThread"; +const COLLAB_FIELD_PARAM = "commentField"; + +const CMS_ENTRIES_ROUTE = new Route({ + name: "Cms/ContentEntries/List", + path: "/cms/content-entries/:modelId", + params: zod => ({ + modelId: zod.string(), + id: zod.string().optional(), + [COLLAB_THREAD_PARAM]: zod.string().optional(), + [COLLAB_FIELD_PARAM]: zod.string().optional() + }) +}); + +/** + * Resolves a Headless CMS entry route from a notification link. Collaboration links encode + * `contentId` as ":". Returns null for links we can't navigate to yet + * (e.g. workflow links that carry only a bare targetId without a model). + */ +const cmsEntryTarget = (link?: NotificationLink | null) => { + if (!link || link.app !== "cms" || !link.contentId) { + return null; + } + const [modelId, entryId] = link.contentId.split(":"); + if (!modelId || !entryId) { + return null; + } + return { modelId, entryId }; +}; + +interface Props { + presenter: NotificationsPresenter.Interface; + notification: Notification; +} + +const ACTION_TEXT: Record = { + mention: "mentioned you on", + reply: "replied to your comment on", + reviewRequested: "requested your review on", + approved: "approved", + rejected: "rejected" +}; + +const TYPE_ICON: Record = { + mention: , + reply: , + reviewRequested: , + approved: , + rejected: +}; + +const renderSnippet = (body: string) => { + return body.split(/(@[^\s]+)/g).map((part, index) => + part.startsWith("@") ? ( + + {part} + + ) : ( + {part} + ) + ); +}; + +export const NotificationItem = observer(({ presenter, notification }: Props) => { + const router = useRouter(); + const actorName = notification.actor.displayName || "Someone"; + const target = cmsEntryTarget(notification.link); + + const open = () => { + void presenter.markRead(notification.id); + presenter.closePanel(); + if (!target) { + return; + } + const params: { + modelId: string; + id: string; + commentThread?: string; + commentField?: string; + } = { modelId: target.modelId, id: target.entryId }; + const threadId = notification.link?.threadId; + if (threadId) { + // Non-path params become query string; collaboration reads them on entry load. + params.commentThread = threadId; + if (notification.link?.locator) { + params.commentField = notification.link.locator; + } + } + router.goToRoute(CMS_ENTRIES_ROUTE, params); + }; + + return ( +
+ + {initials(actorName)} + +
+
+ {actorName}{" "} + {ACTION_TEXT[notification.type]}{" "} + {notification.title ? ( + {notification.title} + ) : null} +
+ {notification.snippet ? ( +
+ {renderSnippet(notification.snippet)} +
+ ) : null} +
+ {TYPE_ICON[notification.type]} + {formatTimestamp(notification.createdOn)} +
+
+ + {notification.read ? null : } + +
+ ); +}); diff --git a/packages/app-notifications/src/presentation/notifications/components/NotificationsBell.tsx b/packages/app-notifications/src/presentation/notifications/components/NotificationsBell.tsx new file mode 100644 index 00000000000..9975cb7e3db --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/components/NotificationsBell.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { IconButton } from "@webiny/admin-ui"; +import { ReactComponent as NotificationsIcon } from "@webiny/icons/notifications.svg"; +import type { NotificationsPresenter } from "../abstractions.js"; +import "../styles.js"; + +interface Props { + presenter: NotificationsPresenter.Interface; +} + +/** + * Notifications toggle rendered inside the admin top bar (next to the user menu). Uses the + * design-system IconButton so it sits in the header flow — no manual positioning. The unread + * badge is layered on a relative wrapper around the button. + */ +export const NotificationsBell = observer(({ presenter }: Props) => { + const { unread } = presenter.vm.counts; + + return ( + + } + aria-label="Notifications" + title={unread > 0 ? `Notifications (${unread} unread)` : "Notifications"} + onClick={() => presenter.togglePanel()} + /> + {unread > 0 ? ( + {unread > 99 ? "99+" : unread} + ) : null} + + ); +}); diff --git a/packages/app-notifications/src/presentation/notifications/components/NotificationsHeaderDecorator.tsx b/packages/app-notifications/src/presentation/notifications/components/NotificationsHeaderDecorator.tsx new file mode 100644 index 00000000000..d2a1dfffa8f --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/components/NotificationsHeaderDecorator.tsx @@ -0,0 +1,30 @@ +import React, { useEffect } from "react"; +import { observer } from "mobx-react-lite"; +import { UserMenu } from "@webiny/app-admin"; +import { useNotificationsPresenter } from "~/presentation/notifications/useNotifications.js"; +import { NotificationsBell } from "~/presentation/notifications/components/NotificationsBell.js"; +import { NotificationsPanel } from "~/presentation/notifications/components/NotificationsPanel.js"; + +/** + * Registers the notifications bell into the admin top bar by decorating the user menu: the bell + * renders immediately before it, in the header's flex row, so placement is handled by the layout + * (no fixed positioning). The slide-in panel mounts here too (fixed overlay, toggled via the + * shared presenter). + */ +export const NotificationsHeaderDecorator = UserMenu.createDecorator(Original => { + return observer(function UserMenuWithNotifications() { + const presenter = useNotificationsPresenter(); + + useEffect(() => { + void presenter.init(); + }, [presenter]); + + return ( + <> + + {presenter.vm.open ? : null} + + + ); + }); +}); diff --git a/packages/app-notifications/src/presentation/notifications/components/NotificationsPanel.tsx b/packages/app-notifications/src/presentation/notifications/components/NotificationsPanel.tsx new file mode 100644 index 00000000000..38a2cae9b3b --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/components/NotificationsPanel.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { ReactComponent as DoneAllIcon } from "@webiny/icons/done_all.svg"; +import { ReactComponent as RefreshIcon } from "@webiny/icons/refresh.svg"; +import { ReactComponent as CloseIcon } from "@webiny/icons/close.svg"; +import { ReactComponent as InboxIcon } from "@webiny/icons/inbox.svg"; +import type { NotificationsPresenter } from "../abstractions.js"; +import { groupByTime } from "../styles.js"; +import { NotificationItem } from "./NotificationItem.js"; +import "../styles.js"; + +interface Props { + presenter: NotificationsPresenter.Interface; +} + +export const NotificationsPanel = observer(({ presenter }: Props) => { + const { vm } = presenter; + const groups = groupByTime(vm.items); + const isEmpty = !vm.loading && !vm.error && vm.items.length === 0; + + return ( + <> +
presenter.closePanel()} /> + + + ); +}); diff --git a/packages/app-notifications/src/presentation/notifications/feature.ts b/packages/app-notifications/src/presentation/notifications/feature.ts new file mode 100644 index 00000000000..25e61433d1c --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/feature.ts @@ -0,0 +1,15 @@ +import { createFeature } from "@webiny/feature/admin"; +import { NotificationsPresenter as PresenterAbstraction } from "./abstractions.js"; +import { NotificationsPresenter } from "./NotificationsPresenter.js"; + +export const NotificationsPresenterFeature = createFeature({ + name: "Notifications/Presenter", + register(container) { + container.register(NotificationsPresenter).inSingletonScope(); + }, + resolve(container) { + return { + presenter: container.resolve(PresenterAbstraction) + }; + } +}); diff --git a/packages/app-notifications/src/presentation/notifications/styles.ts b/packages/app-notifications/src/presentation/notifications/styles.ts new file mode 100644 index 00000000000..4bc1284cfc2 --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/styles.ts @@ -0,0 +1,154 @@ +const CSS = ` +.wby-notif-bell-wrap { position:relative; display:inline-flex; } +.wby-notif-bell__badge { position:absolute; top:-2px; right:-2px; min-width:16px; height:16px; padding:0 4px; background:var(--color-primary); color:#fff; border-radius:8px; font-size:10px; font-weight:700; line-height:1; display:flex; align-items:center; justify-content:center; box-sizing:border-box; box-shadow:0 0 0 2px var(--color-neutral-0); pointer-events:none; } + +.wby-notif-scrim { position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:50; opacity:0; animation:wby-notif-fade .18s ease forwards; } +@keyframes wby-notif-fade { to { opacity:1; } } +.wby-notif-panel { position:fixed; top:0; right:0; height:100vh; width:440px; max-width:100vw; background:var(--color-neutral-0); box-shadow:var(--shadow-xl); z-index:51; display:flex; flex-direction:column; font-family:var(--font-sans); color:var(--text-color-neutral-primary); transform:translateX(100%); animation:wby-notif-slide .22s ease forwards; } +@keyframes wby-notif-slide { to { transform:translateX(0); } } + +.wby-notif-header { flex:0 0 auto; display:flex; align-items:center; justify-content:space-between; padding:16px 16px 10px; } +.wby-notif-title { display:flex; align-items:center; gap:8px; font-size:20px; font-weight:600; } +.wby-notif-title__icon { flex:0 0 auto; width:22px; height:22px; fill:var(--color-primary); } +.wby-notif-newbadge { display:inline-flex; align-items:center; gap:4px; padding:2px 8px; background:var(--color-primary-100); color:var(--color-primary-700); border-radius:10px; font-size:12px; font-weight:700; } +.wby-notif-actions { display:flex; align-items:center; gap:2px; } +.wby-notif-iconbtn { display:flex; align-items:center; justify-content:center; width:30px; height:30px; border-radius:6px; cursor:pointer; color:var(--color-neutral-500); background:transparent; border:none; padding:0; transition:background .12s ease, color .12s ease; } +.wby-notif-iconbtn:hover { background:var(--color-neutral-100); color:var(--color-neutral-800); } +.wby-notif-iconbtn:not(:disabled) { cursor:pointer; } +.wby-notif-iconbtn:disabled { opacity:.4; cursor:default; } +.wby-notif-iconbtn svg { width:18px; height:18px; fill:currentColor; pointer-events:none; } +.wby-notif-iconbtn--primary { color:var(--color-primary); } +.wby-notif-spin { animation:wby-notif-spin .8s linear infinite; } +@keyframes wby-notif-spin { to { transform:rotate(360deg); } } + +.wby-notif-tabs { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:0 16px 12px; border-bottom:1px solid var(--color-neutral-200); } +.wby-notif-tab { display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border-radius:8px; border:none; background:transparent; cursor:pointer; font-size:14px; font-weight:600; color:var(--color-neutral-600); font-family:inherit; } +.wby-notif-tab.is-active { background:var(--color-neutral-100); color:var(--color-neutral-900); } +.wby-notif-tab__count { color:var(--color-neutral-500); font-weight:600; } +.wby-notif-unreadonly { margin-left:auto; display:flex; align-items:center; gap:6px; font-size:13px; color:var(--color-neutral-700); cursor:pointer; user-select:none; } +.wby-notif-unreadonly input { cursor:pointer; } + +.wby-notif-list { flex:1 1 auto; overflow-y:auto; } +.wby-notif-group__label { padding:14px 16px 4px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--color-neutral-500); } +.wby-notif-item { display:flex; gap:12px; padding:14px 16px; border-top:1px solid var(--color-neutral-100); cursor:pointer; position:relative; transition:background .12s ease; } +.wby-notif-item:first-child { border-top:none; } +.wby-notif-item:hover { background:var(--color-neutral-50); } +.wby-notif-item--unread { background:var(--color-primary-100); } +.wby-notif-item--unread:hover { background:var(--color-primary-100); } +.wby-notif-avatar { flex:0 0 auto; width:40px; height:40px; border-radius:50%; color:#fff; font-size:13px; font-weight:600; display:flex; align-items:center; justify-content:center; } +.wby-notif-item__body { flex:1; min-width:0; padding-right:16px; } +.wby-notif-item__text { font-size:14px; line-height:20px; color:var(--color-neutral-900); } +.wby-notif-item__strong { font-weight:700; } +.wby-notif-item__snippet { margin-top:6px; padding-left:10px; border-left:2px solid var(--color-neutral-300); font-size:13px; line-height:18px; color:var(--color-neutral-600); overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; } +.wby-notif-item__meta { margin-top:8px; display:flex; align-items:center; gap:8px; font-size:12px; color:var(--color-neutral-500); } +.wby-notif-item__typeicon { width:24px; height:24px; border-radius:50%; border:1px solid var(--color-neutral-200); display:flex; align-items:center; justify-content:center; flex:0 0 auto; } +.wby-notif-item__typeicon svg { width:13px; height:13px; fill:var(--color-neutral-500); } +.wby-notif-dot { position:absolute; top:16px; right:16px; width:9px; height:9px; border-radius:50%; background:var(--color-primary); } +.wby-notif-item:hover .wby-notif-dot { display:none; } +.wby-notif-archive { position:absolute; top:10px; right:12px; opacity:0; transition:opacity .12s ease; } +.wby-notif-item:hover .wby-notif-archive { opacity:1; } +.wby-notif-mention { color:var(--color-primary); font-weight:600; } +.wby-notif-empty { padding:48px 24px; text-align:center; color:var(--color-neutral-500); font-size:14px; } +`; + +let injected = false; + +export const ensureNotifStyles = (): void => { + if (injected || typeof document === "undefined") { + return; + } + if (document.getElementById("wby-notif-styles")) { + injected = true; + return; + } + const style = document.createElement("style"); + style.id = "wby-notif-styles"; + style.textContent = CSS; + document.head.appendChild(style); + injected = true; +}; + +ensureNotifStyles(); + +export const avatarColor = (name: string): string => { + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = (hash * 31 + name.charCodeAt(i)) % 360; + } + return `hsl(${hash} 42% 50%)`; +}; + +export const initials = (name: string): string => { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) { + return "?"; + } + if (parts.length === 1) { + return parts[0].slice(0, 2).toUpperCase(); + } + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); +}; + +const DAY_MS = 86_400_000; +const YEAR_MS = 365 * DAY_MS; + +export const formatTimestamp = (iso: string): string => { + const then = new Date(iso).getTime(); + if (Number.isNaN(then)) { + return ""; + } + const diff = Date.now() - then; + if (diff < DAY_MS) { + const minutes = Math.floor(diff / 60_000); + if (minutes < 1) { + return "just now"; + } + if (minutes < 60) { + return `${minutes}m ago`; + } + return `${Math.floor(minutes / 60)}h ago`; + } + if (diff < 2 * DAY_MS) { + return "Yesterday"; + } + const date = new Date(then); + const options: Intl.DateTimeFormatOptions = + diff < YEAR_MS + ? { month: "short", day: "numeric" } + : { month: "short", day: "numeric", year: "numeric" }; + return date.toLocaleDateString(undefined, options); +}; + +export interface TimeGroup { + label: string; + items: T[]; +} + +export const groupByTime = (items: T[]): TimeGroup[] => { + const now = Date.now(); + const startOfToday = new Date(); + startOfToday.setHours(0, 0, 0, 0); + const todayStart = startOfToday.getTime(); + const weekStart = now - 7 * DAY_MS; + + const today: T[] = []; + const week: T[] = []; + const older: T[] = []; + + for (const item of items) { + const time = new Date(item.createdOn).getTime(); + if (time >= todayStart) { + today.push(item); + } else if (time >= weekStart) { + week.push(item); + } else { + older.push(item); + } + } + + return [ + { label: "Today", items: today }, + { label: "Earlier this week", items: week }, + { label: "Older", items: older } + ].filter(group => group.items.length > 0); +}; diff --git a/packages/app-notifications/src/presentation/notifications/useNotifications.ts b/packages/app-notifications/src/presentation/notifications/useNotifications.ts new file mode 100644 index 00000000000..04854945ee0 --- /dev/null +++ b/packages/app-notifications/src/presentation/notifications/useNotifications.ts @@ -0,0 +1,6 @@ +import { useFeature } from "@webiny/app"; +import { NotificationsPresenterFeature } from "./feature.js"; + +export const useNotificationsPresenter = () => { + return useFeature(NotificationsPresenterFeature).presenter; +}; diff --git a/packages/app-notifications/src/types.ts b/packages/app-notifications/src/types.ts new file mode 100644 index 00000000000..5eeb8c6d010 --- /dev/null +++ b/packages/app-notifications/src/types.ts @@ -0,0 +1,41 @@ +export type NotificationType = "mention" | "reply" | "reviewRequested" | "approved" | "rejected"; + +export interface NotificationActor { + id: string; + displayName?: string | null; + type?: string | null; +} + +export interface NotificationLink { + app?: string | null; + contentType?: string | null; + contentId?: string | null; + locator?: string | null; + threadId?: string | null; +} + +export interface Notification { + id: string; + type: NotificationType; + actor: NotificationActor; + title?: string | null; + snippet?: string | null; + link?: NotificationLink | null; + read: boolean; + readOn?: string | null; + archived: boolean; + archivedOn?: string | null; + createdOn: string; +} + +export interface NotificationCounts { + inbox: number; + archive: number; + unread: number; +} + +export interface NotificationsMeta { + totalCount: number; + hasMoreItems: boolean; + cursor: string | null; +} diff --git a/packages/app-notifications/tsconfig.build.json b/packages/app-notifications/tsconfig.build.json new file mode 100644 index 00000000000..d084eb5e86f --- /dev/null +++ b/packages/app-notifications/tsconfig.build.json @@ -0,0 +1,29 @@ +{ + "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": "../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/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/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"] + } + } +} diff --git a/packages/app-notifications/tsconfig.json b/packages/app-notifications/tsconfig.json new file mode 100644 index 00000000000..e7965caa3c5 --- /dev/null +++ b/packages/app-notifications/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src", "__tests__"], + "references": [ + { "path": "../admin-ui" }, + { "path": "../app" }, + { "path": "../app-admin" }, + { "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/*": ["../app/src/*"], + "@webiny/app": ["../app/src"], + "@webiny/app-admin/*": ["../app-admin/src/*"], + "@webiny/app-admin": ["../app-admin/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"] + } + } +} diff --git a/packages/app-notifications/webiny.config.js b/packages/app-notifications/webiny.config.js new file mode 100644 index 00000000000..4f5e3db04b9 --- /dev/null +++ b/packages/app-notifications/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-serverless-cms/package.json b/packages/app-serverless-cms/package.json index 7a9c896bad3..96c0adae4fe 100644 --- a/packages/app-serverless-cms/package.json +++ b/packages/app-serverless-cms/package.json @@ -19,12 +19,14 @@ "@webiny/app-admin": "0.0.0", "@webiny/app-admin-ui": "0.0.0", "@webiny/app-audit-logs": "0.0.0", + "@webiny/app-collaboration": "0.0.0", "@webiny/app-file-manager": "0.0.0", "@webiny/app-graphql-playground": "0.0.0", "@webiny/app-headless-cms": "0.0.0", "@webiny/app-headless-cms-scheduler": "0.0.0", "@webiny/app-headless-cms-workflows": "0.0.0", "@webiny/app-mailer": "0.0.0", + "@webiny/app-notifications": "0.0.0", "@webiny/app-record-locking": "0.0.0", "@webiny/app-scheduler": "0.0.0", "@webiny/app-sdk-playground": "0.0.0", diff --git a/packages/app-serverless-cms/src/Admin.tsx b/packages/app-serverless-cms/src/Admin.tsx index 1da776bfc20..67c910e4931 100644 --- a/packages/app-serverless-cms/src/Admin.tsx +++ b/packages/app-serverless-cms/src/Admin.tsx @@ -21,6 +21,8 @@ import { SchedulerModule } from "@webiny/app-scheduler"; import { CmsScheduler } from "@webiny/app-headless-cms-scheduler"; import { WorkflowsAdminApp } from "@webiny/app-workflows"; import { CmsWorkflows } from "@webiny/app-headless-cms-workflows"; +import { CollaborationAdminApp } from "@webiny/app-collaboration"; +import { NotificationsAdminApp } from "@webiny/app-notifications"; import { WebsiteBuilderWorkflows } from "@webiny/app-website-builder-workflows"; import { Container } from "@webiny/di"; import type { PluginCollection } from "@webiny/plugins/types.js"; @@ -63,6 +65,8 @@ const App = (props: AdminProps) => { + + diff --git a/packages/app-serverless-cms/tsconfig.build.json b/packages/app-serverless-cms/tsconfig.build.json index 97899fd21c9..bce13313276 100644 --- a/packages/app-serverless-cms/tsconfig.build.json +++ b/packages/app-serverless-cms/tsconfig.build.json @@ -7,12 +7,14 @@ { "path": "../app-admin/tsconfig.build.json" }, { "path": "../app-admin-ui/tsconfig.build.json" }, { "path": "../app-audit-logs/tsconfig.build.json" }, + { "path": "../app-collaboration/tsconfig.build.json" }, { "path": "../app-file-manager/tsconfig.build.json" }, { "path": "../app-graphql-playground/tsconfig.build.json" }, { "path": "../app-headless-cms/tsconfig.build.json" }, { "path": "../app-headless-cms-scheduler/tsconfig.build.json" }, { "path": "../app-headless-cms-workflows/tsconfig.build.json" }, { "path": "../app-mailer/tsconfig.build.json" }, + { "path": "../app-notifications/tsconfig.build.json" }, { "path": "../app-record-locking/tsconfig.build.json" }, { "path": "../app-scheduler/tsconfig.build.json" }, { "path": "../app-sdk-playground/tsconfig.build.json" }, @@ -44,6 +46,8 @@ "@webiny/app-admin-ui": ["../app-admin-ui/src"], "@webiny/app-audit-logs/*": ["../app-audit-logs/src/*"], "@webiny/app-audit-logs": ["../app-audit-logs/src"], + "@webiny/app-collaboration/*": ["../app-collaboration/src/*"], + "@webiny/app-collaboration": ["../app-collaboration/src"], "@webiny/app-file-manager/*": ["../app-file-manager/src/*"], "@webiny/app-file-manager": ["../app-file-manager/src"], "@webiny/app-graphql-playground/*": ["../app-graphql-playground/src/*"], @@ -56,6 +60,8 @@ "@webiny/app-headless-cms-workflows": ["../app-headless-cms-workflows/src"], "@webiny/app-mailer/*": ["../app-mailer/src/*"], "@webiny/app-mailer": ["../app-mailer/src"], + "@webiny/app-notifications/*": ["../app-notifications/src/*"], + "@webiny/app-notifications": ["../app-notifications/src"], "@webiny/app-record-locking/*": ["../app-record-locking/src/*"], "@webiny/app-record-locking": ["../app-record-locking/src"], "@webiny/app-scheduler/*": ["../app-scheduler/src/*"], diff --git a/packages/app-serverless-cms/tsconfig.json b/packages/app-serverless-cms/tsconfig.json index 01f6188ea87..4974481ba87 100644 --- a/packages/app-serverless-cms/tsconfig.json +++ b/packages/app-serverless-cms/tsconfig.json @@ -7,12 +7,14 @@ { "path": "../app-admin" }, { "path": "../app-admin-ui" }, { "path": "../app-audit-logs" }, + { "path": "../app-collaboration" }, { "path": "../app-file-manager" }, { "path": "../app-graphql-playground" }, { "path": "../app-headless-cms" }, { "path": "../app-headless-cms-scheduler" }, { "path": "../app-headless-cms-workflows" }, { "path": "../app-mailer" }, + { "path": "../app-notifications" }, { "path": "../app-record-locking" }, { "path": "../app-scheduler" }, { "path": "../app-sdk-playground" }, @@ -44,6 +46,8 @@ "@webiny/app-admin-ui": ["../app-admin-ui/src"], "@webiny/app-audit-logs/*": ["../app-audit-logs/src/*"], "@webiny/app-audit-logs": ["../app-audit-logs/src"], + "@webiny/app-collaboration/*": ["../app-collaboration/src/*"], + "@webiny/app-collaboration": ["../app-collaboration/src"], "@webiny/app-file-manager/*": ["../app-file-manager/src/*"], "@webiny/app-file-manager": ["../app-file-manager/src"], "@webiny/app-graphql-playground/*": ["../app-graphql-playground/src/*"], @@ -56,6 +60,8 @@ "@webiny/app-headless-cms-workflows": ["../app-headless-cms-workflows/src"], "@webiny/app-mailer/*": ["../app-mailer/src/*"], "@webiny/app-mailer": ["../app-mailer/src"], + "@webiny/app-notifications/*": ["../app-notifications/src/*"], + "@webiny/app-notifications": ["../app-notifications/src"], "@webiny/app-record-locking/*": ["../app-record-locking/src/*"], "@webiny/app-record-locking": ["../app-record-locking/src"], "@webiny/app-scheduler/*": ["../app-scheduler/src/*"], diff --git a/packages/cli/files/references.json b/packages/cli/files/references.json index 8923554d0d9..fe4896a0b56 100644 --- a/packages/cli/files/references.json +++ b/packages/cli/files/references.json @@ -1 +1 @@ -{"dependencies":[{"name":"@ai-sdk/anthropic","version":"4.0.11","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"@ai-sdk/openai","version":"4.0.11","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"@apollo/react-components","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@apollo/react-hooks","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@atlaskit/pragmatic-drag-and-drop","version":"2.0.1","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@atlaskit/pragmatic-drag-and-drop-hitbox","version":"2.0.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@auth0/auth0-spa-js","version":"2.23.0","files":["/packages/auth0/package.json","/packages/auth0/package.json"]},{"name":"@aws-sdk/client-apigatewaymanagementapi","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudfront","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-events","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-logs","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cognito-identity-provider","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb-streams","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-eventbridge","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iam","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iot","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-lambda","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-s3","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-scheduler","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sfn","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sqs","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sts","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/credential-providers","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-dynamodb","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-storage","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-presigned-post","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-request-presigner","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/util-dynamodb","version":"3.996.6","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@babel/code-frame","version":"8.0.0","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"@emotion/css","version":"11.13.5","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@emotion/react","version":"11.14.0","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"@emotion/styled","version":"11.14.1","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@fastify/aws-lambda","version":"6.4.0","files":["/packages/handler-aws/package.json","/packages/handler-aws/package.json"]},{"name":"@fastify/compress","version":"9.0.0","files":["/packages/handler/package.json","/packages/handler/package.json"]},{"name":"@fastify/cookie","version":"11.1.1","files":["/packages/handler/package.json","/packages/handler/package.json"]},{"name":"@fastify/multipart","version":"10.1.0","files":["/packages/api-file-manager-server/package.json","/packages/api-file-manager-server/package.json"]},{"name":"@fortawesome/fontawesome-common-types","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/fontawesome-svg-core","version":"7.3.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"@fortawesome/free-brands-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-regular-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/react-fontawesome","version":"3.4.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@graphql-tools/merge","version":"9.2.0","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/resolvers-composition","version":"7.0.33","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/schema","version":"10.0.36","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/utils","version":"11.2.0","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"@iconify/json","version":"2.2.498","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@lexical/code","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/hashtag","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/headless","version":"0.47.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"@lexical/history","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor/package.json"]},{"name":"@lexical/html","version":"0.47.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"@lexical/list","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/mark","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/overflow","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/react","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/rich-text","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/selection","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/text","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor/package.json"]},{"name":"@lexical/utils","version":"0.47.0","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/app-admin/package.json","/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-scheduler/package.json","/packages/app-scheduler/package.json"]},{"name":"@minoru/react-dnd-treeview","version":"3.5.4","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@modelcontextprotocol/sdk","version":"1.29.0","files":["/packages/mcp/package.json","/packages/mcp/package.json"]},{"name":"@monaco-editor/react","version":"4.7.0","files":["/packages/admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/app-website-builder/package.json","/packages/admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/app-website-builder/package.json"]},{"name":"@noble/hashes","version":"2.2.0","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"@octokit/rest","version":"22.0.1","files":["/scripts/release/package.json","/scripts/release/package.json"]},{"name":"@okta/okta-auth-js","version":"8.0.1","files":["/packages/okta/package.json","/packages/okta/package.json"]},{"name":"@opensearch-project/opensearch","version":"3.6.0","files":["/packages/api-opensearch/package.json","/packages/api-opensearch/package.json"]},{"name":"@pulumi/aws","version":"7.36.0","files":["/packages/project-aws/package.json","/packages/pulumi-sdk/package.json","/packages/project-aws/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/pulumi","version":"3.251.0","files":["/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/random","version":"4.21.0","files":["/packages/project-aws/package.json","/packages/project-aws/package.json"]},{"name":"@radix-ui/react-scroll-area","version":"1.2.14","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@rsbuild/core","version":"2.1.5","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-react","version":"2.1.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-sass","version":"2.0.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"2.0.5","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-type-check","version":"1.5.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsdoctor/rspack-plugin","version":"1.5.18","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rslib/core","version":"0.23.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rspack/core","version":"2.1.3","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@swc/plugin-emotion","version":"14.15.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@tailwindcss/postcss","version":"4.3.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@tanstack/react-table","version":"8.21.3","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@types/aws-lambda","version":"8.10.162","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@types/hoist-non-react-statics","version":"3.3.7","files":["/package.json"]},{"name":"@types/prismjs","version":"1.26.6","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@types/react","version":"18.3.31","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","/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":"accounting","version":"0.4.1","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"ai","version":"7.0.19","files":["/packages/ai-powerups/package.json","/packages/api-core/package.json","/packages/ai-powerups/package.json","/packages/api-core/package.json"]},{"name":"apollo-cache","version":"1.3.5","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-cache-inmemory","version":"1.6.6","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-batch-http","version":"1.2.14","files":["/packages/app-serverless-cms/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-context","version":"1.0.20","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"apollo-link-error","version":"1.1.13","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-link-http-common","version":"0.2.16","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-utilities","version":"1.3.4","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"aws-amplify","version":"6.18.0","files":["/packages/cognito/package.json","/packages/cognito/package.json"]},{"name":"bree","version":"9.2.9","files":["/packages/api-scheduler-server/package.json","/packages/api-scheduler-server/package.json"]},{"name":"bson-objectid","version":"2.0.4","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"bytes","version":"3.1.2","files":["/packages/admin-ui/package.json","/packages/api-sync-system/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/admin-ui/package.json","/packages/api-sync-system/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json"]},{"name":"cache-control-parser","version":"2.2.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"chalk","version":"5.6.2","files":["/packages/aws-layers/package.json","/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/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/release/package.json","/packages/aws-layers/package.json","/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/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/release/package.json"]},{"name":"cheerio","version":"1.2.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"chokidar","version":"5.0.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"ci-info","version":"4.4.0","files":["/packages/cli-core/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/telemetry/package.json","/packages/cli-core/package.json","/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","/packages/admin-ui/package.json"]},{"name":"classnames","version":"2.5.1","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"cli-progress","version":"3.12.0","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"cli-table3","version":"0.6.5","files":["/packages/system-requirements/package.json","/packages/system-requirements/package.json"]},{"name":"clsx","version":"2.1.1","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"cmdk","version":"1.1.1","files":["/packages/admin-ui/package.json","/packages/app-admin-ui/package.json","/packages/admin-ui/package.json","/packages/app-admin-ui/package.json"]},{"name":"css-loader","version":"7.1.4","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"csstype","version":"3.2.3","files":["/packages/website-builder-sdk/package.json","/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","/packages/api-core/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"date-fns","version":"4.4.0","files":["/packages/admin-ui/package.json","/packages/api-headless-cms-storage/package.json","/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms-storage/package.json","/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json"]},{"name":"dayjs","version":"1.11.21","files":["/packages/app-file-manager/package.json","/packages/app-file-manager/package.json"]},{"name":"debounce","version":"3.0.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"decompress","version":"4.2.1","files":["/packages/pulumi-sdk/package.json","/packages/pulumi-sdk/package.json"]},{"name":"deep-equal","version":"2.2.3","files":["/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/packages/project/package.json","/packages/website-builder-sdk/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json"]},{"name":"dnd-core","version":"16.0.1","files":["/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"dot-prop","version":"10.1.0","files":["/packages/api-headless-cms-storage/package.json","/packages/db-dynamodb/package.json","/packages/api-headless-cms-storage/package.json","/packages/db-dynamodb/package.json"]},{"name":"dot-prop-immutable","version":"2.1.1","files":["/packages/api-headless-cms/package.json","/packages/app-headless-cms/package.json","/packages/api-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"dotenv","version":"17.4.2","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"dynamodb-toolbox","version":"0.9.5","files":["/packages/db-dynamodb/package.json","/packages/db-dynamodb/package.json"]},{"name":"email-addresses","version":"5.0.0","files":["/packages/api-mailer/package.json","/packages/project-aws/package.json","/packages/api-mailer/package.json","/packages/project-aws/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","/scripts/release/package.json","/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","/scripts/release/package.json"]},{"name":"exifreader","version":"4.41.0","files":["/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json"]},{"name":"exit-hook","version":"5.1.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/i18n/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/i18n/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"fast-json-patch","version":"3.1.1","files":["/packages/cms-sdk/package.json","/packages/website-builder-sdk/package.json","/packages/cms-sdk/package.json","/packages/website-builder-sdk/package.json"]},{"name":"fast-json-stable-stringify","version":"2.1.0","files":["/packages/website-builder-sdk/package.json","/packages/website-builder-sdk/package.json"]},{"name":"fastify","version":"5.10.0","files":["/packages/api-file-manager-server/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/api-file-manager-server/package.json","/packages/handler/package.json","/packages/handler-aws/package.json"]},{"name":"fecha","version":"4.2.3","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"find-up","version":"8.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","/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":"front-matter","version":"4.0.2","files":["/packages/ai-powerups/package.json","/packages/mcp/package.json","/packages/ai-powerups/package.json","/packages/mcp/package.json"]},{"name":"fs-extra","version":"11.3.6","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","/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.4.2","files":["/packages/db-utils/package.json","/packages/db-utils/package.json"]},{"name":"get-tsconfig","version":"4.14.0","files":["/packages/project/package.json","/packages/project/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","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/project-utils/package.json","/scripts/prepublishOnly/package.json"]},{"name":"graphql","version":"16.14.2","files":["/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-language-service","version":"5.5.2","files":["/packages/app-graphql-playground/package.json","/packages/app-graphql-playground/package.json"]},{"name":"graphql-request","version":"7.4.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"graphql-scalars","version":"1.25.0","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-tag","version":"2.12.7","files":["/packages/api-headless-cms/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"history","version":"5.3.0","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"humanize-duration","version":"3.34.0","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/cli-core/package.json","/packages/project/package.json"]},{"name":"inquirer","version":"14.0.2","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/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","/packages/app/package.json","/packages/project-aws/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","/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"jose","version":"6.2.3","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"js-yaml","version":"5.2.1","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"jsdom","version":"29.1.1","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"jsesc","version":"3.1.0","files":["/packages/telemetry/package.json","/packages/telemetry/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/packages/api-core/package.json","/packages/auth0/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/api-core/package.json","/packages/auth0/package.json","/packages/cognito/package.json","/packages/okta/package.json"]},{"name":"jwt-decode","version":"4.0.0","files":["/package.json"]},{"name":"knex","version":"3.3.0","files":["/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json"]},{"name":"lexical","version":"0.47.0","files":["/packages/app-admin/package.json","/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json","/packages/app-admin/package.json","/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json"]},{"name":"listr2","version":"10.2.2","files":["/packages/create-webiny-project/package.json","/scripts/buildPackages/package.json","/packages/create-webiny-project/package.json","/scripts/buildPackages/package.json"]},{"name":"load-json-file","version":"7.0.1","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cjsToEsm/package.json","/scripts/release/package.json","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cjsToEsm/package.json","/scripts/release/package.json"]},{"name":"lodash","version":"4.18.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-storage/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-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/cognito/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/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-properties/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-sdk/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-storage/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-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/cognito/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/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-properties/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-sdk/package.json"]},{"name":"markdown-to-jsx","version":"9.8.2","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"matcher","version":"6.0.0","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"mime","version":"4.1.0","files":["/packages/api-file-manager/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json","/packages/api-file-manager/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json"]},{"name":"minimatch","version":"10.2.5","files":["/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project/package.json","/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project/package.json"]},{"name":"mobx","version":"6.16.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/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-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-scheduler/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/languages/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/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-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-scheduler/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/languages/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json"]},{"name":"mobx-react-lite","version":"4.1.1","files":["/packages/ai-powerups/package.json","/packages/app-aco/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-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-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/ai-powerups/package.json","/packages/app-aco/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-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-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/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","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json"]},{"name":"mqtt","version":"5.15.2","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"nanoid","version":"5.1.16","files":["/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json","/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"nanoid-dictionary","version":"5.0.0","files":["/packages/utils/package.json","/packages/website-builder-sdk/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"neverthrow","version":"8.2.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"node-notifier","version":"10.0.1","files":["/scripts/buildPackages/package.json","/scripts/buildPackages/package.json"]},{"name":"nodemailer","version":"9.0.3","files":["/packages/api-mailer/package.json","/packages/api-mailer/package.json"]},{"name":"object-hash","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"object-merge-advanced","version":"14.1.3","files":["/packages/background-tasks/package.json","/packages/background-tasks/package.json"]},{"name":"object-sizeof","version":"2.6.5","files":["/packages/background-tasks/package.json","/packages/background-tasks/package.json"]},{"name":"ohash","version":"2.0.11","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"open","version":"11.0.0","files":["/packages/cli-core/package.json","/packages/cli-core/package.json"]},{"name":"ora","version":"9.4.1","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json"]},{"name":"os","version":"0.1.2","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"p-limit","version":"7.3.0","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"p-map","version":"7.0.5","files":["/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/sdk/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/sdk/package.json"]},{"name":"p-reduce","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-retry","version":"8.0.0","files":["/packages/api-dynamodb-to-elasticsearch/package.json","/packages/project/package.json","/packages/sdk/package.json","/packages/utils/package.json","/scripts/release/package.json","/packages/api-dynamodb-to-elasticsearch/package.json","/packages/project/package.json","/packages/sdk/package.json","/packages/utils/package.json","/scripts/release/package.json"]},{"name":"pino","version":"10.3.1","files":["/packages/api-core/package.json","/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json","/packages/api-core/package.json","/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json"]},{"name":"pino-lambda","version":"4.4.1","files":["/packages/api-core/package.json","/packages/handler-aws/package.json","/packages/api-core/package.json","/packages/handler-aws/package.json"]},{"name":"pino-pretty","version":"13.1.3","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/packages/cli-core/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","/packages/api-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"postcss","version":"8.5.16","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"postcss-import","version":"16.1.1","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"postcss-loader","version":"8.2.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"radix-ui","version":"1.6.2","files":["/packages/admin-ui/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/admin-ui/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"react","version":"18.3.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-aws-template/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-react/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-aws-template/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-react/package.json"]},{"name":"react-color","version":"2.19.3","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json","/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/admin-ui/package.json"]},{"name":"react-day-picker","version":"10.0.1","files":["/packages/admin-ui/package.json","/packages/admin-ui/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","/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","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/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-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/react-composition/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/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-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/react-composition/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json"]},{"name":"react-helmet","version":"6.1.0","files":["/packages/app-admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/app-admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json"]},{"name":"react-lazy-load","version":"4.0.1","files":["/packages/app-file-manager/package.json","/packages/app-file-manager/package.json"]},{"name":"react-refresh","version":"0.18.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"react-resizable-panels","version":"4.12.1","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"react-transition-group","version":"4.4.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"react-virtualized","version":"9.22.6","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"replace-in-path","version":"1.1.0","files":["/packages/project/package.json","/packages/project-aws/package.json","/packages/project/package.json","/packages/project-aws/package.json"]},{"name":"reset-css","version":"5.0.2","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"rimraf","version":"6.1.3","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/package.json"]},{"name":"sanitize-filename","version":"1.6.4","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"sass","version":"1.101.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"sass-loader","version":"17.0.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"semver","version":"7.8.5","files":["/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/release/package.json","/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/release/package.json"]},{"name":"serialize-error","version":"13.0.1","files":["/packages/project/package.json","/scripts/buildPackages/package.json","/packages/project/package.json","/scripts/buildPackages/package.json"]},{"name":"sharp","version":"0.35.3","files":["/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json"]},{"name":"slugify","version":"1.6.9","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/form/package.json","/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/form/package.json"]},{"name":"sonner","version":"2.0.7","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"standardwebhooks","version":"1.0.0","files":["/packages/sdk/package.json","/packages/webhooks/package.json","/packages/sdk/package.json","/packages/webhooks/package.json"]},{"name":"strip-ansi","version":"7.2.0","files":["/packages/telemetry/package.json","/packages/telemetry/package.json"]},{"name":"strip-json-comments","version":"5.0.3","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"style-loader","version":"4.0.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"tailwind-merge","version":"3.6.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"tailwindcss","version":"4.3.2","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"tar","version":"7.5.19","files":["/packages/pulumi-sdk/package.json","/packages/pulumi-sdk/package.json"]},{"name":"tinycolor2","version":"1.6.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"ts-invariant","version":"0.10.3","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"ts-morph","version":"28.0.0","files":["/packages/build-tools/package.json","/packages/project/package.json","/packages/project-aws/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json","/scripts/generateSkills/package.json","/packages/build-tools/package.json","/packages/project/package.json","/packages/project-aws/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json","/scripts/generateSkills/package.json"]},{"name":"tsx","version":"4.23.0","files":["/packages/build-tools/package.json","/packages/cli/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/cjsToEsm/package.json","/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","/packages/admin-ui/package.json"]},{"name":"type-fest","version":"5.8.0","files":["/packages/admin-ui/package.json","/packages/api-websockets-aws/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/api-websockets-aws/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json"]},{"name":"typescript","version":"7.0.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"unicode-emoji-json","version":"0.9.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/packages/plugins/package.json","/packages/plugins/package.json"]},{"name":"url-loader","version":"4.1.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"use-deep-compare-effect","version":"1.8.1","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"utf-8-validate","version":"6.0.6","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"validate-npm-package-name","version":"8.0.0","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"vitest","version":"4.1.10","files":["/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-aco/package.json"]},{"name":"vue","version":"3.5.39","files":["/packages/website-builder-vue/package.json","/packages/website-builder-vue/package.json"]},{"name":"warning","version":"4.0.3","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"write-json-file","version":"7.0.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json"]},{"name":"ws","version":"8.21.0","files":["/packages/api-websockets-server/package.json","/packages/api-websockets-server/package.json"]},{"name":"yargs","version":"18.0.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json","/scripts/release/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json","/scripts/release/package.json"]},{"name":"yesno","version":"0.4.0","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"zod","version":"4.4.3","files":["/packages/ai-powerups/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-mailer/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-server/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/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/webhooks/package.json","/packages/ai-powerups/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-mailer/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-server/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/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/webhooks/package.json"]}],"devDependencies":[{"name":"@4tw/cypress-drag-drop","version":"2.3.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"@babel/code-frame","version":"8.0.0","files":["/package.json"]},{"name":"@commitlint/cli","version":"21.2.1","files":["/package.json"]},{"name":"@commitlint/config-conventional","version":"21.2.0","files":["/package.json"]},{"name":"@emotion/react","version":"11.14.0","files":["/packages/react-rich-text-lexical-renderer/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@faker-js/faker","version":"10.5.0","files":["/packages/api-sync-system/package.json","/packages/api-sync-system/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"7.3.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@grpc/grpc-js","version":"1.14.4","files":["/package.json"]},{"name":"@lexical/code","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/hashtag","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/headless","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/history","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/html","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/list","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/mark","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/overflow","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/react","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/rich-text","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/selection","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/text","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/utils","version":"0.47.0","files":["/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/icons/package.json","/packages/icons/package.json"]},{"name":"@octokit/rest","version":"22.0.1","files":["/package.json"]},{"name":"@rsbuild/core","version":"2.1.5","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"2.0.5","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/addon-a11y","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/addon-docs","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/react","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@tailwindcss/postcss","version":"4.3.2","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@testing-library/cypress","version":"10.1.3","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"@testing-library/react","version":"16.3.2","files":["/packages/app-admin/package.json","/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/app-admin/package.json","/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@testing-library/user-event","version":"14.6.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"@types/accounting","version":"0.4.5","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"@types/bytes","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/cli-progress","version":"3.11.6","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"@types/debounce","version":"1.2.4","files":["/packages/project/package.json","/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","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/fs-extra","version":"11.0.4","files":["/package.json"]},{"name":"@types/graphlib","version":"2.1.12","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/humanize-duration","version":"3.27.4","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"@types/invariant","version":"2.2.37","files":["/packages/form/package.json","/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","/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","/packages/db-dynamodb/package.json"]},{"name":"@types/js-yaml","version":"4.0.9","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"@types/jsdom","version":"28.0.3","files":["/packages/project/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","/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-core/package.json","/packages/auth0/package.json","/packages/okta/package.json","/packages/api-core/package.json","/packages/auth0/package.json","/packages/okta/package.json"]},{"name":"@types/jwk-to-pem","version":"2.0.3","files":["/packages/auth0/package.json","/packages/okta/package.json","/packages/auth0/package.json","/packages/okta/package.json"]},{"name":"@types/lodash","version":"4.17.24","files":["/packages/api-sync-system/package.json","/packages/app/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/mcp/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/validation/package.json","/packages/api-sync-system/package.json","/packages/app/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/mcp/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/ncp","version":"2.0.8","files":["/packages/mcp/package.json","/packages/project-aws/package.json","/packages/mcp/package.json","/packages/project-aws/package.json"]},{"name":"@types/node","version":"24.13.3","files":["/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/create-webiny-project/_templates/base/template.package.json"]},{"name":"@types/nodemailer","version":"8.0.1","files":["/packages/api-mailer/package.json","/packages/api-mailer/package.json"]},{"name":"@types/object-hash","version":"3.0.6","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"@types/pluralize","version":"0.0.33","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"@types/postcss-import","version":"14.0.3","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"@types/react","version":"18.3.31","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-scheduler/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/website-builder-react/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-scheduler/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/website-builder-react/package.json"]},{"name":"@types/react-color","version":"3.0.13","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json","/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","/packages/admin-ui/package.json"]},{"name":"@types/react-dom","version":"18.3.7","files":["/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/create-webiny-project/_templates/base/template.package.json"]},{"name":"@types/react-helmet","version":"6.1.11","files":["/packages/app-admin-ui/package.json","/packages/app-admin-ui/package.json"]},{"name":"@types/react-resizable","version":"4.0.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/react-transition-group","version":"4.4.12","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/react-virtualized","version":"9.22.3","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@types/store","version":"2.0.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/tinycolor2","version":"1.4.6","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/uniqid","version":"5.3.4","files":["/packages/feature-flags/package.json","/packages/plugins/package.json","/packages/feature-flags/package.json","/packages/plugins/package.json"]},{"name":"@types/validate-npm-package-name","version":"4.0.2","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"@types/warning","version":"3.0.4","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"@types/ws","version":"8.18.1","files":["/packages/api-websockets-server/package.json","/packages/api-websockets-server/package.json"]},{"name":"@types/yargs","version":"17.0.35","files":["/scripts/buildPackages/package.json","/scripts/release/package.json","/scripts/buildPackages/package.json","/scripts/release/package.json"]},{"name":"@vitest/coverage-v8","version":"4.1.10","files":["/package.json"]},{"name":"adio","version":"3.0.1","files":["/package.json"]},{"name":"amazon-cognito-identity-js","version":"6.3.20","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"apollo-graphql","version":"0.9.7","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"aws-sdk-client-mock","version":"4.1.0","files":["/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json"]},{"name":"better-sqlite3","version":"12.11.1","files":["/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json"]},{"name":"chalk","version":"5.6.2","files":["/package.json","/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"cross-env","version":"10.1.0","files":["/package.json"]},{"name":"cypress","version":"15.18.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-image-snapshot","version":"4.0.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-mailosaur","version":"5.0.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-vite","version":"1.10.2","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-wait-until","version":"3.0.2","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/package.json"]},{"name":"del","version":"8.0.1","files":["/cypress-tests/package.json","/cypress-tests/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/mcp/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/common-audit-logs/package.json","/packages/mcp/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/package.json"]},{"name":"fs-extra","version":"11.3.6","files":["/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/package.json"]},{"name":"github-actions-wac","version":"2.0.0","files":["/package.json"]},{"name":"graphql","version":"16.14.2","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-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/testing/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-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/testing/package.json"]},{"name":"graphql-request","version":"7.4.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"husky","version":"9.1.7","files":["/package.json"]},{"name":"inquirer","version":"14.0.2","files":["/package.json"]},{"name":"jest-dynalite","version":"3.6.1","files":["/packages/api-core-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-sync-system/package.json","/packages/db-dynamodb/package.json","/packages/project-utils/package.json","/packages/api-core-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-sync-system/package.json","/packages/db-dynamodb/package.json","/packages/project-utils/package.json"]},{"name":"jest-extended","version":"7.0.0","files":["/package.json"]},{"name":"jsdom","version":"29.1.1","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"lexical","version":"0.47.0","files":["/package.json"]},{"name":"listr2","version":"10.2.2","files":["/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"load-json-file","version":"7.0.1","files":["/package.json","/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"lodash","version":"4.18.1","files":["/package.json","/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"minimatch","version":"10.2.5","files":["/package.json"]},{"name":"mobx","version":"6.16.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"mobx-react-lite","version":"4.1.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"nanoid","version":"5.1.16","files":["/package.json","/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"next","version":"16.2.10","files":["/package.json"]},{"name":"oxfmt","version":"0.58.0","files":["/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms/package.json","/packages/react-properties/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms/package.json","/packages/react-properties/package.json"]},{"name":"oxlint","version":"1.73.0","files":["/package.json"]},{"name":"pino","version":"10.3.1","files":["/packages/logger/package.json","/packages/project-utils/package.json","/packages/logger/package.json","/packages/project-utils/package.json"]},{"name":"pino-pretty","version":"13.1.3","files":["/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"react","version":"18.3.1","files":["/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/package.json"]},{"name":"rimraf","version":"6.1.3","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tenant-manager/package.json","/packages/testing/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.101.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"semver","version":"7.8.5","files":["/package.json"]},{"name":"storybook","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"storybook-react-rsbuild","version":"3.3.4","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"ts-expect","version":"1.3.0","files":["/package.json"]},{"name":"tsx","version":"4.23.0","files":["/package.json","/packages/mcp/package.json","/packages/mcp/package.json"]},{"name":"type-fest","version":"5.8.0","files":["/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-record-locking/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/background-tasks/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-record-locking/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/background-tasks/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json"]},{"name":"typescript","version":"7.0.2","files":["/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-core-sql/package.json","/packages/api-dynamodb-to-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-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/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-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/common-audit-logs/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/sdk/package.json","/packages/shared-aco/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/cypress-tests/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-core-sql/package.json","/packages/api-dynamodb-to-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-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/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-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/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-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/common-audit-logs/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/sdk/package.json","/packages/shared-aco/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/cypress-tests/package.json","/scripts/cli/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"validator","version":"13.15.35","files":["/package.json"]},{"name":"verdaccio","version":"6.7.4","files":["/package.json"]},{"name":"vite","version":"8.1.4","files":["/packages/website-builder-nuxt/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"vitest","version":"4.1.10","files":["/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-core-sql/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/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-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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/languages/package.json","/packages/lexical-converter/package.json","/packages/plugins/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/scripts/release/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-core/package.json","/packages/api-core-sql/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/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-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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/languages/package.json","/packages/lexical-converter/package.json","/packages/plugins/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/scripts/release/package.json"]},{"name":"vue","version":"3.5.39","files":["/packages/website-builder-nuxt/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"webpack","version":"5.108.4","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nextjs/package.json"]},{"name":"write-json-file","version":"7.0.0","files":["/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"yargs","version":"18.0.0","files":["/package.json","/packages/project-utils/package.json","/packages/project-utils/package.json"]}],"peerDependencies":[{"name":"aws-lambda","version":"1.0.7","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"react","version":"18.3.1","files":["/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json"]}],"resolutions":[{"name":"@types/react","version":"18.3.31","files":["/package.json"]},{"name":"@types/react-dom","version":"18.3.7","files":["/package.json"]},{"name":"react","version":"18.3.1","files":["/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/package.json"]}],"references":[{"name":"@types/hoist-non-react-statics","versions":[{"version":"3.3.7","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"jwt-decode","versions":[{"version":"4.0.0","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"@babel/code-frame","versions":[{"version":"8.0.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@commitlint/cli","versions":[{"version":"21.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/config-conventional","versions":[{"version":"21.2.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@grpc/grpc-js","versions":[{"version":"1.14.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@lexical/code","versions":[{"version":"0.47.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.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/headless","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/history","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/html","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/list","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/mark","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/overflow","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/react","versions":[{"version":"0.47.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.47.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.47.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.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/utils","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"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/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@octokit/rest","versions":[{"version":"22.0.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"@types/fs-extra","versions":[{"version":"11.0.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/node","versions":[{"version":"24.13.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]}]}]},{"name":"@types/react","versions":[{"version":"18.3.31","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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/cognito/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.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/website-builder-react/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-dom","versions":[{"version":"18.3.7","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]}]}]},{"name":"@vitest/coverage-v8","versions":[{"version":"4.1.10","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"adio","versions":[{"version":"3.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"chalk","versions":[{"version":"5.6.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/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/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"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"cross-env","versions":[{"version":"10.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"deepmerge","versions":[{"version":"4.3.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"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/mcp/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"fast-glob","versions":[{"version":"3.3.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"fs-extra","versions":[{"version":"11.3.6","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-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":"github-actions-wac","versions":[{"version":"2.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"graphql","versions":[{"version":"16.14.2","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-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/api-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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-website-builder/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]}]}]},{"name":"husky","versions":[{"version":"9.1.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"inquirer","versions":[{"version":"14.0.2","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":"7.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lexical","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"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"]}]}]},{"name":"load-json-file","versions":[{"version":"7.0.1","files":[{"file":"/package.json","types":["devDependencies"]},{"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/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"lodash","versions":[{"version":"4.18.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-aco/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-storage/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-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-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cognito/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/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/react-properties/package.json","types":["dependencies"]},{"file":"/packages/validation/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"minimatch","versions":[{"version":"10.2.5","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-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"nanoid","versions":[{"version":"5.1.16","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":"next","versions":[{"version":"16.2.10","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"oxfmt","versions":[{"version":"0.58.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]}]}]},{"name":"oxlint","versions":[{"version":"1.73.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"react","versions":[{"version":"18.3.1","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/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-ui/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["peerDependencies"]},{"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-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.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/languages/package.json","types":["dependencies"]},{"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"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/project-aws-template/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/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/webiny/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"react-dom","versions":[{"version":"18.3.1","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/ai-powerups/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-ui/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-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["dependencies"]},{"file":"/packages/languages/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"semver","versions":[{"version":"7.8.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"ts-expect","versions":[{"version":"1.3.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"tsx","versions":[{"version":"4.23.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"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.8.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":["devDependencies"]},{"file":"/packages/api-websockets-aws/package.json","types":["dependencies","devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/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/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["devDependencies"]}]}]},{"name":"typescript","versions":[{"version":"7.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/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-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-dynamodb-to-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-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-server/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-storage/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-server/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-scheduler/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-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/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-scheduler/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/auth0/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/cms-sdk/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/db-utils/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-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/languages/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"file":"/packages/okta/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/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/sdk/package.json","types":["devDependencies"]},{"file":"/packages/shared-aco/package.json","types":["devDependencies"]},{"file":"/packages/tenant-manager/package.json","types":["devDependencies"]},{"file":"/packages/testing/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/webhooks/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["devDependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"validator","versions":[{"version":"13.15.35","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"verdaccio","versions":[{"version":"6.7.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"vitest","versions":[{"version":"4.1.10","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/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-file-manager-server/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-storage/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-server/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-scheduler/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-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/package.json","types":["devDependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/cms-sdk/package.json","types":["devDependencies"]},{"file":"/packages/cognito/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/db-utils/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/languages/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/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/sdk/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/webhooks/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":"/scripts/release/package.json","types":["devDependencies"]}]}]},{"name":"write-json-file","versions":[{"version":"7.0.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":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"yargs","versions":[{"version":"18.0.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/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"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-svg-core","versions":[{"version":"7.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/react-fontawesome","versions":[{"version":"3.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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.4","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-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@radix-ui/react-scroll-area","versions":[{"version":"1.2.14","files":[{"file":"/packages/admin-ui/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-sync-system/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]}]}]},{"name":"date-fns","versions":[{"version":"4.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-storage/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"mobx","versions":[{"version":"6.16.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/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-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-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cms-sdk/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/languages/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-vue/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"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]}]}]},{"name":"radix-ui","versions":[{"version":"1.6.2","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/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"]}]}]},{"name":"react-day-picker","versions":[{"version":"10.0.1","files":[{"file":"/packages/admin-ui/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":"3.6.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwindcss","versions":[{"version":"4.3.2","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":"7.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/core","versions":[{"version":"2.1.5","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-svgr","versions":[{"version":"2.0.5","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@storybook/addon-a11y","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-docs","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/react","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@tailwindcss/postcss","versions":[{"version":"4.3.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/react-color","versions":[{"version":"3.0.13","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"]}]}]},{"name":"rimraf","versions":[{"version":"6.1.3","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/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-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/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-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-server/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/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/api-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/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/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-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/languages/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/tenant-manager/package.json","types":["devDependencies"]},{"file":"/packages/testing/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.101.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"storybook","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"storybook-react-rsbuild","versions":[{"version":"3.3.4","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"ai","versions":[{"version":"7.0.19","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"front-matter","versions":[{"version":"4.0.2","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["dependencies"]}]}]},{"name":"mobx-react-lite","versions":[{"version":"4.1.1","files":[{"file":"/packages/ai-powerups/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-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-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"zod","versions":[{"version":"4.4.3","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs-ddb/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-tasks/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-aws/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-server/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/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]}]}]},{"name":"knex","versions":[{"version":"3.3.0","files":[{"file":"/packages/api-aco-sql/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs-sql/package.json","types":["dependencies"]},{"file":"/packages/api-core-sql/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-sql/package.json","types":["dependencies"]}]}]},{"name":"better-sqlite3","versions":[{"version":"12.11.1","files":[{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/package.json","types":["devDependencies"]}]}]},{"name":"@ai-sdk/anthropic","versions":[{"version":"4.0.11","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"@ai-sdk/openai","versions":[{"version":"4.0.11","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"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":"jose","versions":[{"version":"6.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"jsonwebtoken","versions":[{"version":"9.0.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/auth0/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]}]}]},{"name":"pino","versions":[{"version":"10.3.1","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"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-lambda","versions":[{"version":"4.4.1","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@types/jsonwebtoken","versions":[{"version":"9.0.10","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/auth0/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]}]}]},{"name":"jest-dynalite","versions":[{"version":"3.6.1","files":[{"file":"/packages/api-core-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-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"p-retry","versions":[{"version":"8.0.0","files":[{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"cache-control-parser","versions":[{"version":"2.2.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"mime","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"object-hash","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"p-map","versions":[{"version":"7.0.5","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]}]}]},{"name":"p-reduce","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"sanitize-filename","versions":[{"version":"1.6.4","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"@types/object-hash","versions":[{"version":"3.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]}]}]},{"name":"exifreader","versions":[{"version":"4.41.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"sharp","versions":[{"version":"0.35.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"@fastify/multipart","versions":[{"version":"10.1.0","files":[{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"fastify","versions":[{"version":"5.10.0","files":[{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]},{"file":"/packages/handler/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/merge","versions":[{"version":"9.2.0","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.36","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"dot-prop-immutable","versions":[{"version":"2.1.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"graphql-tag","versions":[{"version":"2.12.7","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"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.9","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-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["dependencies"]}]}]},{"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":"@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":"dot-prop","versions":[{"version":"10.1.0","files":[{"file":"/packages/api-headless-cms-storage/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"email-addresses","versions":[{"version":"5.0.0","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"nodemailer","versions":[{"version":"9.0.3","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"@types/nodemailer","versions":[{"version":"8.0.1","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@opensearch-project/opensearch","versions":[{"version":"3.6.0","files":[{"file":"/packages/api-opensearch/package.json","types":["dependencies"]}]}]},{"name":"aws-sdk-client-mock","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"bree","versions":[{"version":"9.2.9","files":[{"file":"/packages/api-scheduler-server/package.json","types":["dependencies"]}]}]},{"name":"@faker-js/faker","versions":[{"version":"10.5.0","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"@types/lodash","versions":[{"version":"4.17.24","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/app/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/mcp/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":"ws","versions":[{"version":"8.21.0","files":[{"file":"/packages/api-websockets-server/package.json","types":["dependencies"]}]}]},{"name":"@types/ws","versions":[{"version":"8.18.1","files":[{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]}]}]},{"name":"@emotion/styled","versions":[{"version":"11.14.1","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/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-serverless-cms/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-admin/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"apollo-link","versions":[{"version":"1.2.14","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/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/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-serverless-cms/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":"warning","versions":[{"version":"4.0.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"@types/warning","versions":[{"version":"3.0.4","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"@apollo/react-components","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-hooks","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@atlaskit/pragmatic-drag-and-drop","versions":[{"version":"2.0.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@atlaskit/pragmatic-drag-and-drop-hitbox","versions":[{"version":"2.0.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@emotion/css","versions":[{"version":"11.13.5","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/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"@emotion/react","versions":[{"version":"11.14.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-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/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"@iconify/json","versions":[{"version":"2.2.498","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"classnames","versions":[{"version":"2.5.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/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":"markdown-to-jsx","versions":[{"version":"9.8.2","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"react-resizable-panels","versions":[{"version":"4.12.1","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":"unicode-emoji-json","versions":[{"version":"0.9.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@testing-library/react","versions":[{"version":"16.3.2","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"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"]}]}]},{"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":"4.0.0","files":[{"file":"/packages/app-admin/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"]}]}]},{"name":"@types/tinycolor2","versions":[{"version":"1.4.6","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"react-helmet","versions":[{"version":"6.1.0","files":[{"file":"/packages/app-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"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@types/react-helmet","versions":[{"version":"6.1.11","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"dayjs","versions":[{"version":"1.11.21","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"react-lazy-load","versions":[{"version":"4.0.1","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"graphql-language-service","versions":[{"version":"5.5.2","files":[{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-common-types","versions":[{"version":"7.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-brands-svg-icons","versions":[{"version":"7.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-regular-svg-icons","versions":[{"version":"7.3.0","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":"dnd-core","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"@material-design-icons/svg","versions":[{"version":"0.14.15","files":[{"file":"/packages/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/icons/package.json","types":["devDependencies"]}]}]},{"name":"apollo-link-batch-http","versions":[{"version":"1.2.14","files":[{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"deep-equal","versions":[{"version":"2.2.3","files":[{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["dependencies"]}]}]},{"name":"matcher","versions":[{"version":"6.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":"@auth0/auth0-spa-js","versions":[{"version":"2.23.0","files":[{"file":"/packages/auth0/package.json","types":["dependencies"]}]}]},{"name":"@types/jwk-to-pem","versions":[{"version":"2.0.3","files":[{"file":"/packages/auth0/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]}]}]},{"name":"@aws-sdk/client-apigatewaymanagementapi","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudfront","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-events","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-logs","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cognito-identity-provider","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb-streams","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-eventbridge","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iam","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iot","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-lambda","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-s3","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-scheduler","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sfn","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sqs","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sts","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/credential-providers","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-dynamodb","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-storage","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-presigned-post","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-request-presigner","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/util-dynamodb","versions":[{"version":"3.996.6","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/aws-lambda","versions":[{"version":"8.10.162","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"aws-lambda","versions":[{"version":"1.0.7","files":[{"file":"/packages/aws-sdk/package.json","types":["peerDependencies"]}]}]},{"name":"object-merge-advanced","versions":[{"version":"14.1.3","files":[{"file":"/packages/background-tasks/package.json","types":["dependencies"]}]}]},{"name":"object-sizeof","versions":[{"version":"2.6.5","files":[{"file":"/packages/background-tasks/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-react","versions":[{"version":"2.1.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-sass","versions":[{"version":"2.0.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-type-check","versions":[{"version":"1.5.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsdoctor/rspack-plugin","versions":[{"version":"1.5.18","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rslib/core","versions":[{"version":"0.23.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rspack/core","versions":[{"version":"2.1.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@swc/plugin-emotion","versions":[{"version":"14.15.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"css-loader","versions":[{"version":"7.1.4","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"find-up","versions":[{"version":"8.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":"postcss-loader","versions":[{"version":"8.2.1","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"]}]}]},{"name":"react-refresh","versions":[{"version":"0.18.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"sass-loader","versions":[{"version":"17.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"strip-json-comments","versions":[{"version":"5.0.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"style-loader","versions":[{"version":"4.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"ts-morph","versions":[{"version":"28.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]},{"file":"/scripts/generateSkills/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.6","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"ci-info","versions":[{"version":"4.4.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"humanize-duration","versions":[{"version":"3.34.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"open","versions":[{"version":"11.0.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]}]}]},{"name":"ora","versions":[{"version":"9.4.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"pino-pretty","versions":[{"version":"13.1.3","files":[{"file":"/packages/cli-core/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":"fast-json-patch","versions":[{"version":"3.1.1","files":[{"file":"/packages/cms-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"aws-amplify","versions":[{"version":"6.18.0","files":[{"file":"/packages/cognito/package.json","types":["dependencies"]}]}]},{"name":"js-yaml","versions":[{"version":"5.2.1","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"listr2","versions":[{"version":"10.2.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"os","versions":[{"version":"0.1.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"validate-npm-package-name","versions":[{"version":"8.0.0","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":"4.0.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"dynamodb-toolbox","versions":[{"version":"0.9.5","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":"fuse.js","versions":[{"version":"7.4.2","files":[{"file":"/packages/db-utils/package.json","types":["dependencies"]}]}]},{"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/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":"@fastify/compress","versions":[{"version":"9.0.0","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/cookie","versions":[{"version":"11.1.1","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/aws-lambda","versions":[{"version":"6.4.0","files":[{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/resolvers-composition","versions":[{"version":"7.0.33","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/utils","versions":[{"version":"11.2.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":"4.2.3","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"ohash","versions":[{"version":"2.0.11","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":"cheerio","versions":[{"version":"1.2.0","files":[{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"jsdom","versions":[{"version":"29.1.1","files":[{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"@types/prismjs","versions":[{"version":"1.26.6","files":[{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@modelcontextprotocol/sdk","versions":[{"version":"1.29.0","files":[{"file":"/packages/mcp/package.json","types":["dependencies"]}]}]},{"name":"@types/ncp","versions":[{"version":"2.0.8","files":[{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]}]}]},{"name":"@okta/okta-auth-js","versions":[{"version":"8.0.1","files":[{"file":"/packages/okta/package.json","types":["dependencies"]}]}]},{"name":"uniqid","versions":[{"version":"5.4.0","files":[{"file":"/packages/plugins/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"chokidar","versions":[{"version":"5.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"debounce","versions":[{"version":"3.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"dotenv","versions":[{"version":"17.4.2","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"exit-hook","versions":[{"version":"5.1.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"get-tsconfig","versions":[{"version":"4.14.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"graphql-request","versions":[{"version":"7.4.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"mqtt","versions":[{"version":"5.15.2","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"neverthrow","versions":[{"version":"8.2.0","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"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"serialize-error","versions":[{"version":"13.0.1","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/jsdom","versions":[{"version":"28.0.3","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@pulumi/aws","versions":[{"version":"7.36.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/pulumi","versions":[{"version":"3.251.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.21.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"decompress","versions":[{"version":"4.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"tar","versions":[{"version":"7.5.19","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"standardwebhooks","versions":[{"version":"1.0.0","files":[{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]}]}]},{"name":"cli-table3","versions":[{"version":"0.6.5","files":[{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"jsesc","versions":[{"version":"3.1.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"strip-ansi","versions":[{"version":"7.2.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"@noble/hashes","versions":[{"version":"2.2.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":"jsonpack","versions":[{"version":"1.1.5","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"nanoid-dictionary","versions":[{"version":"5.0.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"postcss","versions":[{"version":"8.5.16","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["dependencies"]}]}]},{"name":"postcss-import","versions":[{"version":"16.1.1","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["dependencies"]}]}]},{"name":"@types/postcss-import","versions":[{"version":"14.0.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]}]}]},{"name":"webpack","versions":[{"version":"5.108.4","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"vite","versions":[{"version":"8.1.4","files":[{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]}]}]},{"name":"vue","versions":[{"version":"3.5.39","files":[{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["dependencies"]}]}]},{"name":"csstype","versions":[{"version":"3.2.3","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":"2.3.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/cypress","versions":[{"version":"10.1.3","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"amazon-cognito-identity-js","versions":[{"version":"6.3.20","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress","versions":[{"version":"15.18.1","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":"5.0.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-vite","versions":[{"version":"1.10.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-wait-until","versions":[{"version":"3.0.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"del","versions":[{"version":"8.0.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"node-notifier","versions":[{"version":"10.0.1","files":[{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"@types/yargs","versions":[{"version":"17.0.35","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]},{"file":"/scripts/release/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.3.0","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":"@ai-sdk/anthropic","version":"4.0.11","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"@ai-sdk/openai","version":"4.0.11","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"@apollo/react-components","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@apollo/react-hooks","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@atlaskit/pragmatic-drag-and-drop","version":"2.0.1","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@atlaskit/pragmatic-drag-and-drop-hitbox","version":"2.0.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@auth0/auth0-spa-js","version":"2.23.0","files":["/packages/auth0/package.json","/packages/auth0/package.json"]},{"name":"@aws-sdk/client-apigatewaymanagementapi","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudfront","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-events","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cloudwatch-logs","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-cognito-identity-provider","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-dynamodb-streams","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-eventbridge","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iam","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-iot","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-lambda","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-s3","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-scheduler","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sfn","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sqs","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/client-sts","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/credential-providers","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-dynamodb","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/lib-storage","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-presigned-post","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/s3-request-presigner","version":"3.1084.0","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@aws-sdk/util-dynamodb","version":"3.996.6","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@babel/code-frame","version":"8.0.0","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"@emotion/css","version":"11.13.5","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@emotion/react","version":"11.14.0","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"@emotion/styled","version":"11.14.1","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor-actions/package.json"]},{"name":"@fastify/aws-lambda","version":"6.4.0","files":["/packages/handler-aws/package.json","/packages/handler-aws/package.json"]},{"name":"@fastify/compress","version":"9.0.0","files":["/packages/handler/package.json","/packages/handler/package.json"]},{"name":"@fastify/cookie","version":"11.1.1","files":["/packages/handler/package.json","/packages/handler/package.json"]},{"name":"@fastify/multipart","version":"10.1.0","files":["/packages/api-file-manager-server/package.json","/packages/api-file-manager-server/package.json"]},{"name":"@fortawesome/fontawesome-common-types","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/fontawesome-svg-core","version":"7.3.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"@fortawesome/free-brands-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-regular-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"7.3.0","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"@fortawesome/react-fontawesome","version":"3.4.0","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json"]},{"name":"@graphql-tools/merge","version":"9.2.0","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/resolvers-composition","version":"7.0.33","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/schema","version":"10.0.36","files":["/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"@graphql-tools/utils","version":"11.2.0","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"@iconify/json","version":"2.2.498","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@lexical/code","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/hashtag","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/headless","version":"0.47.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"@lexical/history","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor/package.json"]},{"name":"@lexical/html","version":"0.47.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"@lexical/list","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/mark","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/overflow","version":"0.47.0","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/react","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/rich-text","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/selection","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json"]},{"name":"@lexical/text","version":"0.47.0","files":["/packages/lexical-editor/package.json","/packages/lexical-editor/package.json"]},{"name":"@lexical/utils","version":"0.47.0","files":["/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/app-admin/package.json","/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-scheduler/package.json","/packages/app-scheduler/package.json"]},{"name":"@minoru/react-dnd-treeview","version":"3.5.4","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@modelcontextprotocol/sdk","version":"1.29.0","files":["/packages/mcp/package.json","/packages/mcp/package.json"]},{"name":"@monaco-editor/react","version":"4.7.0","files":["/packages/admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/app-website-builder/package.json","/packages/admin-ui/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/app-website-builder/package.json"]},{"name":"@noble/hashes","version":"2.2.0","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"@octokit/rest","version":"22.0.1","files":["/scripts/release/package.json","/scripts/release/package.json"]},{"name":"@okta/okta-auth-js","version":"8.0.1","files":["/packages/okta/package.json","/packages/okta/package.json"]},{"name":"@opensearch-project/opensearch","version":"3.6.0","files":["/packages/api-opensearch/package.json","/packages/api-opensearch/package.json"]},{"name":"@pulumi/aws","version":"7.36.0","files":["/packages/project-aws/package.json","/packages/pulumi-sdk/package.json","/packages/project-aws/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/pulumi","version":"3.251.0","files":["/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json"]},{"name":"@pulumi/random","version":"4.21.0","files":["/packages/project-aws/package.json","/packages/project-aws/package.json"]},{"name":"@radix-ui/react-scroll-area","version":"1.2.14","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@rsbuild/core","version":"2.1.5","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-react","version":"2.1.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-sass","version":"2.0.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"2.0.5","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsbuild/plugin-type-check","version":"1.5.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rsdoctor/rspack-plugin","version":"1.5.18","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rslib/core","version":"0.23.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@rspack/core","version":"2.1.3","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@swc/plugin-emotion","version":"14.15.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@tailwindcss/postcss","version":"4.3.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"@tanstack/react-table","version":"8.21.3","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@types/aws-lambda","version":"8.10.162","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"@types/hoist-non-react-statics","version":"3.3.7","files":["/package.json"]},{"name":"@types/prismjs","version":"1.26.6","files":["/packages/lexical-nodes/package.json","/packages/lexical-nodes/package.json"]},{"name":"@types/react","version":"18.3.31","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","/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":"accounting","version":"0.4.1","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"ai","version":"7.0.19","files":["/packages/ai-powerups/package.json","/packages/api-core/package.json","/packages/ai-powerups/package.json","/packages/api-core/package.json"]},{"name":"apollo-cache","version":"1.3.5","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-cache-inmemory","version":"1.6.6","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-client","version":"2.6.10","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link","version":"1.2.14","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-batch-http","version":"1.2.14","files":["/packages/app-serverless-cms/package.json","/packages/app-serverless-cms/package.json"]},{"name":"apollo-link-context","version":"1.0.20","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"apollo-link-error","version":"1.1.13","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-link-http-common","version":"0.2.16","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"apollo-utilities","version":"1.3.4","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-serverless-cms/package.json"]},{"name":"aws-amplify","version":"6.18.0","files":["/packages/cognito/package.json","/packages/cognito/package.json"]},{"name":"bree","version":"9.2.9","files":["/packages/api-scheduler-server/package.json","/packages/api-scheduler-server/package.json"]},{"name":"bson-objectid","version":"2.0.4","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"bytes","version":"3.1.2","files":["/packages/admin-ui/package.json","/packages/api-sync-system/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/admin-ui/package.json","/packages/api-sync-system/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json"]},{"name":"cache-control-parser","version":"2.2.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"chalk","version":"5.6.2","files":["/packages/aws-layers/package.json","/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/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/release/package.json","/packages/aws-layers/package.json","/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/system-requirements/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cli/package.json","/scripts/release/package.json"]},{"name":"cheerio","version":"1.2.0","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"chokidar","version":"5.0.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"ci-info","version":"4.4.0","files":["/packages/cli-core/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/telemetry/package.json","/packages/cli-core/package.json","/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","/packages/admin-ui/package.json"]},{"name":"classnames","version":"2.5.1","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"cli-progress","version":"3.12.0","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"cli-table3","version":"0.6.5","files":["/packages/system-requirements/package.json","/packages/system-requirements/package.json"]},{"name":"clsx","version":"2.1.1","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"cmdk","version":"1.1.1","files":["/packages/admin-ui/package.json","/packages/app-admin-ui/package.json","/packages/admin-ui/package.json","/packages/app-admin-ui/package.json"]},{"name":"css-loader","version":"7.1.4","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"csstype","version":"3.2.3","files":["/packages/website-builder-sdk/package.json","/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","/packages/api-core/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json"]},{"name":"date-fns","version":"4.4.0","files":["/packages/admin-ui/package.json","/packages/api-headless-cms-storage/package.json","/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms-storage/package.json","/packages/app-audit-logs/package.json","/packages/db-dynamodb/package.json"]},{"name":"dayjs","version":"1.11.21","files":["/packages/app-file-manager/package.json","/packages/app-file-manager/package.json"]},{"name":"debounce","version":"3.0.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"decompress","version":"4.2.1","files":["/packages/pulumi-sdk/package.json","/packages/pulumi-sdk/package.json"]},{"name":"deep-equal","version":"2.2.3","files":["/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/packages/project/package.json","/packages/website-builder-sdk/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json"]},{"name":"dnd-core","version":"16.0.1","files":["/packages/app-headless-cms-common/package.json","/packages/app-headless-cms-common/package.json"]},{"name":"dot-prop","version":"10.1.0","files":["/packages/api-headless-cms-storage/package.json","/packages/db-dynamodb/package.json","/packages/api-headless-cms-storage/package.json","/packages/db-dynamodb/package.json"]},{"name":"dot-prop-immutable","version":"2.1.1","files":["/packages/api-headless-cms/package.json","/packages/app-headless-cms/package.json","/packages/api-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"dotenv","version":"17.4.2","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"dynamodb-toolbox","version":"0.9.5","files":["/packages/db-dynamodb/package.json","/packages/db-dynamodb/package.json"]},{"name":"email-addresses","version":"5.0.0","files":["/packages/api-mailer/package.json","/packages/project-aws/package.json","/packages/api-mailer/package.json","/packages/project-aws/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","/scripts/release/package.json","/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","/scripts/release/package.json"]},{"name":"exifreader","version":"4.41.0","files":["/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json"]},{"name":"exit-hook","version":"5.1.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/i18n/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/i18n/package.json","/packages/project/package.json","/scripts/cjsToEsm/package.json"]},{"name":"fast-json-patch","version":"3.1.1","files":["/packages/cms-sdk/package.json","/packages/website-builder-sdk/package.json","/packages/cms-sdk/package.json","/packages/website-builder-sdk/package.json"]},{"name":"fast-json-stable-stringify","version":"2.1.0","files":["/packages/website-builder-sdk/package.json","/packages/website-builder-sdk/package.json"]},{"name":"fastify","version":"5.10.0","files":["/packages/api-file-manager-server/package.json","/packages/handler/package.json","/packages/handler-aws/package.json","/packages/api-file-manager-server/package.json","/packages/handler/package.json","/packages/handler-aws/package.json"]},{"name":"fecha","version":"4.2.3","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"find-up","version":"8.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","/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":"front-matter","version":"4.0.2","files":["/packages/ai-powerups/package.json","/packages/mcp/package.json","/packages/ai-powerups/package.json","/packages/mcp/package.json"]},{"name":"fs-extra","version":"11.3.6","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","/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.4.2","files":["/packages/db-utils/package.json","/packages/db-utils/package.json"]},{"name":"get-tsconfig","version":"4.14.0","files":["/packages/project/package.json","/packages/project/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","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/project-utils/package.json","/scripts/prepublishOnly/package.json"]},{"name":"graphql","version":"16.14.2","files":["/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/app/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-language-service","version":"5.5.2","files":["/packages/app-graphql-playground/package.json","/packages/app-graphql-playground/package.json"]},{"name":"graphql-request","version":"7.4.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"graphql-scalars","version":"1.25.0","files":["/packages/handler-graphql/package.json","/packages/handler-graphql/package.json"]},{"name":"graphql-tag","version":"2.12.7","files":["/packages/api-headless-cms/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/handler-graphql/package.json","/packages/api-headless-cms/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/handler-graphql/package.json"]},{"name":"history","version":"5.3.0","files":["/packages/app/package.json","/packages/app-admin/package.json","/packages/app/package.json","/packages/app-admin/package.json"]},{"name":"humanize-duration","version":"3.34.0","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/cli-core/package.json","/packages/project/package.json"]},{"name":"inquirer","version":"14.0.2","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/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","/packages/app/package.json","/packages/project-aws/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","/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"jose","version":"6.2.3","files":["/packages/api-core/package.json","/packages/api-core/package.json"]},{"name":"js-yaml","version":"5.2.1","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"jsdom","version":"29.1.1","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"jsesc","version":"3.1.0","files":["/packages/telemetry/package.json","/packages/telemetry/package.json"]},{"name":"jsonpack","version":"1.1.5","files":["/packages/utils/package.json","/packages/utils/package.json"]},{"name":"jsonwebtoken","version":"9.0.3","files":["/packages/api-core/package.json","/packages/auth0/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/api-core/package.json","/packages/auth0/package.json","/packages/cognito/package.json","/packages/okta/package.json"]},{"name":"jwt-decode","version":"4.0.0","files":["/package.json"]},{"name":"knex","version":"3.3.0","files":["/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json"]},{"name":"lexical","version":"0.47.0","files":["/packages/app-admin/package.json","/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json","/packages/app-admin/package.json","/packages/lexical-converter/package.json","/packages/lexical-editor/package.json","/packages/lexical-nodes/package.json","/packages/lexical-theme/package.json"]},{"name":"listr2","version":"10.2.2","files":["/packages/create-webiny-project/package.json","/scripts/buildPackages/package.json","/packages/create-webiny-project/package.json","/scripts/buildPackages/package.json"]},{"name":"load-json-file","version":"7.0.1","files":["/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cjsToEsm/package.json","/scripts/release/package.json","/packages/build-tools/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/packages/telemetry/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/cjsToEsm/package.json","/scripts/release/package.json"]},{"name":"lodash","version":"4.18.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-storage/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-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/cognito/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/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-properties/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-sdk/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api-aco/package.json","/packages/api-core/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-headless-cms-storage/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-audit-logs/package.json","/packages/app-file-manager/package.json","/packages/app-headless-cms/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/cognito/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/project/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-properties/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-sdk/package.json"]},{"name":"markdown-to-jsx","version":"9.8.2","files":["/packages/app-admin/package.json","/packages/app-website-builder/package.json","/packages/app-admin/package.json","/packages/app-website-builder/package.json"]},{"name":"matcher","version":"6.0.0","files":["/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"mime","version":"4.1.0","files":["/packages/api-file-manager/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json","/packages/api-file-manager/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project-aws/package.json"]},{"name":"minimatch","version":"10.2.5","files":["/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project/package.json","/packages/admin-ui/package.json","/packages/api-core/package.json","/packages/app-admin/package.json","/packages/app-file-manager/package.json","/packages/project/package.json"]},{"name":"mobx","version":"6.16.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/languages/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/languages/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json"]},{"name":"mobx-react-lite","version":"4.1.1","files":["/packages/ai-powerups/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/ai-powerups/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cognito/package.json","/packages/okta/package.json","/packages/tenant-manager/package.json","/packages/webhooks/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","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/app-graphql-playground/package.json","/packages/app-sdk-playground/package.json"]},{"name":"mqtt","version":"5.15.2","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"nanoid","version":"5.1.16","files":["/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json","/packages/app/package.json","/packages/react-properties/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"nanoid-dictionary","version":"5.0.0","files":["/packages/utils/package.json","/packages/website-builder-sdk/package.json","/packages/utils/package.json","/packages/website-builder-sdk/package.json"]},{"name":"neverthrow","version":"8.2.0","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"node-notifier","version":"10.0.1","files":["/scripts/buildPackages/package.json","/scripts/buildPackages/package.json"]},{"name":"nodemailer","version":"9.0.3","files":["/packages/api-mailer/package.json","/packages/api-mailer/package.json"]},{"name":"object-hash","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"object-merge-advanced","version":"14.1.3","files":["/packages/background-tasks/package.json","/packages/background-tasks/package.json"]},{"name":"object-sizeof","version":"2.6.5","files":["/packages/background-tasks/package.json","/packages/background-tasks/package.json"]},{"name":"ohash","version":"2.0.11","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"open","version":"11.0.0","files":["/packages/cli-core/package.json","/packages/cli-core/package.json"]},{"name":"ora","version":"9.4.1","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json"]},{"name":"os","version":"0.1.2","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"p-limit","version":"7.3.0","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"p-map","version":"7.0.5","files":["/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/sdk/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/sdk/package.json"]},{"name":"p-reduce","version":"3.0.0","files":["/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json"]},{"name":"p-retry","version":"8.0.0","files":["/packages/api-dynamodb-to-elasticsearch/package.json","/packages/project/package.json","/packages/sdk/package.json","/packages/utils/package.json","/scripts/release/package.json","/packages/api-dynamodb-to-elasticsearch/package.json","/packages/project/package.json","/packages/sdk/package.json","/packages/utils/package.json","/scripts/release/package.json"]},{"name":"pino","version":"10.3.1","files":["/packages/api-core/package.json","/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json","/packages/api-core/package.json","/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/scripts/cli/package.json"]},{"name":"pino-lambda","version":"4.4.1","files":["/packages/api-core/package.json","/packages/handler-aws/package.json","/packages/api-core/package.json","/packages/handler-aws/package.json"]},{"name":"pino-pretty","version":"13.1.3","files":["/packages/cli-core/package.json","/packages/project/package.json","/packages/website-builder-sdk/package.json","/packages/cli-core/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","/packages/api-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"postcss","version":"8.5.16","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"postcss-import","version":"16.1.1","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"postcss-loader","version":"8.2.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"radix-ui","version":"1.6.2","files":["/packages/admin-ui/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json","/packages/admin-ui/package.json","/packages/app-website-builder/package.json","/packages/lexical-editor/package.json"]},{"name":"raw-loader","version":"4.0.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"react","version":"18.3.1","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-aws-template/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-react/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/lexical-nodes/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/project-aws-template/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-react/package.json"]},{"name":"react-color","version":"2.19.3","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json","/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/admin-ui/package.json"]},{"name":"react-day-picker","version":"10.0.1","files":["/packages/admin-ui/package.json","/packages/admin-ui/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","/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","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/react-composition/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json","/packages/ai-powerups/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/languages/package.json","/packages/lexical-editor/package.json","/packages/lexical-editor-actions/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/react-composition/package.json","/packages/tenant-manager/package.json","/packages/webhooks/package.json","/packages/website-builder-react/package.json"]},{"name":"react-helmet","version":"6.1.0","files":["/packages/app-admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/app-admin-ui/package.json","/packages/app-headless-cms/package.json","/packages/app-headless-cms-workflows/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json"]},{"name":"react-lazy-load","version":"4.0.1","files":["/packages/app-file-manager/package.json","/packages/app-file-manager/package.json"]},{"name":"react-refresh","version":"0.18.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"react-resizable-panels","version":"4.12.1","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"react-transition-group","version":"4.4.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"react-virtualized","version":"9.22.6","files":["/packages/admin-ui/package.json","/packages/app-admin/package.json","/packages/admin-ui/package.json","/packages/app-admin/package.json"]},{"name":"replace-in-path","version":"1.1.0","files":["/packages/project/package.json","/packages/project-aws/package.json","/packages/project/package.json","/packages/project-aws/package.json"]},{"name":"reset-css","version":"5.0.2","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"rimraf","version":"6.1.3","files":["/packages/build-tools/package.json","/packages/create-webiny-project/package.json","/packages/build-tools/package.json","/packages/create-webiny-project/package.json"]},{"name":"sanitize-filename","version":"1.6.4","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"sass","version":"1.101.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"sass-loader","version":"17.0.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"semver","version":"7.8.5","files":["/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/release/package.json","/packages/api-sync-system/package.json","/packages/cli-core/package.json","/packages/pulumi-sdk/package.json","/packages/system-requirements/package.json","/scripts/release/package.json"]},{"name":"serialize-error","version":"13.0.1","files":["/packages/project/package.json","/scripts/buildPackages/package.json","/packages/project/package.json","/scripts/buildPackages/package.json"]},{"name":"sharp","version":"0.35.3","files":["/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json"]},{"name":"slugify","version":"1.6.9","files":["/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/form/package.json","/packages/api-headless-cms/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/form/package.json"]},{"name":"sonner","version":"2.0.7","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"standardwebhooks","version":"1.0.0","files":["/packages/sdk/package.json","/packages/webhooks/package.json","/packages/sdk/package.json","/packages/webhooks/package.json"]},{"name":"strip-ansi","version":"7.2.0","files":["/packages/telemetry/package.json","/packages/telemetry/package.json"]},{"name":"strip-json-comments","version":"5.0.3","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"style-loader","version":"4.0.0","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"tailwind-merge","version":"3.6.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"tailwindcss","version":"4.3.2","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"tar","version":"7.5.19","files":["/packages/pulumi-sdk/package.json","/packages/pulumi-sdk/package.json"]},{"name":"tinycolor2","version":"1.6.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"ts-invariant","version":"0.10.3","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"ts-morph","version":"28.0.0","files":["/packages/build-tools/package.json","/packages/project/package.json","/packages/project-aws/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json","/scripts/generateSkills/package.json","/packages/build-tools/package.json","/packages/project/package.json","/packages/project-aws/package.json","/scripts/cli/package.json","/scripts/cjsToEsm/package.json","/scripts/generateSkills/package.json"]},{"name":"tsx","version":"4.23.0","files":["/packages/build-tools/package.json","/packages/cli/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/cjsToEsm/package.json","/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","/packages/admin-ui/package.json"]},{"name":"type-fest","version":"5.8.0","files":["/packages/admin-ui/package.json","/packages/api-websockets-aws/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/api-websockets-aws/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/db/package.json","/scripts/cli/package.json"]},{"name":"typescript","version":"7.0.2","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"unicode-emoji-json","version":"0.9.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/packages/plugins/package.json","/packages/plugins/package.json"]},{"name":"url-loader","version":"4.1.1","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"use-deep-compare-effect","version":"1.8.1","files":["/packages/app-headless-cms/package.json","/packages/app-headless-cms/package.json"]},{"name":"utf-8-validate","version":"6.0.6","files":["/packages/build-tools/package.json","/packages/build-tools/package.json"]},{"name":"validate-npm-package-name","version":"8.0.0","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"vitest","version":"4.1.10","files":["/packages/api-headless-cms-aco/package.json","/packages/api-headless-cms-aco/package.json"]},{"name":"vue","version":"3.5.39","files":["/packages/website-builder-vue/package.json","/packages/website-builder-vue/package.json"]},{"name":"warning","version":"4.0.3","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"write-json-file","version":"7.0.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/global-config/package.json","/packages/project/package.json","/scripts/buildPackages/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json"]},{"name":"ws","version":"8.21.0","files":["/packages/api-websockets-server/package.json","/packages/api-websockets-server/package.json"]},{"name":"yargs","version":"18.0.0","files":["/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json","/scripts/release/package.json","/packages/cli-core/package.json","/packages/create-webiny-project/package.json","/packages/i18n/package.json","/scripts/buildPackages/package.json","/scripts/cli/package.json","/scripts/release/package.json"]},{"name":"yesno","version":"0.4.0","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"zod","version":"4.4.3","files":["/packages/ai-powerups/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-mailer/package.json","/packages/api-notifications/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-server/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/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/webhooks/package.json","/packages/ai-powerups/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-file-manager/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-mailer/package.json","/packages/api-notifications/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-server/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/app-scheduler/package.json","/packages/app-website-builder/package.json","/packages/auth0/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cognito/package.json","/packages/handler-graphql/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/webhooks/package.json"]}],"devDependencies":[{"name":"@4tw/cypress-drag-drop","version":"2.3.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"@babel/code-frame","version":"8.0.0","files":["/package.json"]},{"name":"@commitlint/cli","version":"21.2.1","files":["/package.json"]},{"name":"@commitlint/config-conventional","version":"21.2.0","files":["/package.json"]},{"name":"@emotion/react","version":"11.14.0","files":["/packages/react-rich-text-lexical-renderer/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@faker-js/faker","version":"10.5.0","files":["/packages/api-sync-system/package.json","/packages/api-sync-system/package.json"]},{"name":"@fortawesome/free-solid-svg-icons","version":"7.3.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@grpc/grpc-js","version":"1.14.4","files":["/package.json"]},{"name":"@lexical/code","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/hashtag","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/headless","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/history","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/html","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/list","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/mark","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/overflow","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/react","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/rich-text","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/selection","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/text","version":"0.47.0","files":["/package.json"]},{"name":"@lexical/utils","version":"0.47.0","files":["/package.json"]},{"name":"@material-design-icons/svg","version":"0.14.15","files":["/packages/icons/package.json","/packages/icons/package.json"]},{"name":"@octokit/rest","version":"22.0.1","files":["/package.json"]},{"name":"@rsbuild/core","version":"2.1.5","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@rsbuild/plugin-svgr","version":"2.0.5","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/addon-a11y","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/addon-docs","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@storybook/react","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@tailwindcss/postcss","version":"4.3.2","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@testing-library/cypress","version":"10.1.3","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"@testing-library/react","version":"16.3.2","files":["/packages/app-admin/package.json","/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/app-admin/package.json","/packages/form/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json"]},{"name":"@testing-library/user-event","version":"14.6.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"@types/accounting","version":"0.4.5","files":["/packages/i18n/package.json","/packages/i18n/package.json"]},{"name":"@types/bytes","version":"3.1.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/cli-progress","version":"3.11.6","files":["/scripts/cjsToEsm/package.json","/scripts/cjsToEsm/package.json"]},{"name":"@types/debounce","version":"1.2.4","files":["/packages/project/package.json","/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","/packages/app-website-builder/package.json","/packages/website-builder-sdk/package.json"]},{"name":"@types/fs-extra","version":"11.0.4","files":["/package.json"]},{"name":"@types/graphlib","version":"2.1.12","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/humanize-duration","version":"3.27.4","files":["/packages/project/package.json","/packages/project/package.json"]},{"name":"@types/invariant","version":"2.2.37","files":["/packages/form/package.json","/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","/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","/packages/db-dynamodb/package.json"]},{"name":"@types/js-yaml","version":"4.0.9","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"@types/jsdom","version":"28.0.3","files":["/packages/project/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","/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-core/package.json","/packages/auth0/package.json","/packages/okta/package.json","/packages/api-core/package.json","/packages/auth0/package.json","/packages/okta/package.json"]},{"name":"@types/jwk-to-pem","version":"2.0.3","files":["/packages/auth0/package.json","/packages/okta/package.json","/packages/auth0/package.json","/packages/okta/package.json"]},{"name":"@types/lodash","version":"4.17.24","files":["/packages/api-sync-system/package.json","/packages/app/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/mcp/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/validation/package.json","/packages/api-sync-system/package.json","/packages/app/package.json","/packages/cli/package.json","/packages/cli-core/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/mcp/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/ncp","version":"2.0.8","files":["/packages/mcp/package.json","/packages/project-aws/package.json","/packages/mcp/package.json","/packages/project-aws/package.json"]},{"name":"@types/node","version":"24.13.3","files":["/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/create-webiny-project/_templates/base/template.package.json"]},{"name":"@types/nodemailer","version":"8.0.1","files":["/packages/api-mailer/package.json","/packages/api-mailer/package.json"]},{"name":"@types/object-hash","version":"3.0.6","files":["/packages/api-file-manager/package.json","/packages/api-file-manager/package.json"]},{"name":"@types/pluralize","version":"0.0.33","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"@types/postcss-import","version":"14.0.3","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"@types/react","version":"18.3.31","files":["/package.json","/packages/admin-ui/package.json","/packages/app-aco/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/website-builder-react/package.json","/packages/admin-ui/package.json","/packages/app-aco/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-scheduler/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-workflows/package.json","/packages/cognito/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/website-builder-react/package.json"]},{"name":"@types/react-color","version":"3.0.13","files":["/packages/admin-ui/package.json","/packages/lexical-editor-actions/package.json","/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","/packages/admin-ui/package.json"]},{"name":"@types/react-dom","version":"18.3.7","files":["/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/create-webiny-project/_templates/base/template.package.json"]},{"name":"@types/react-helmet","version":"6.1.11","files":["/packages/app-admin-ui/package.json","/packages/app-admin-ui/package.json"]},{"name":"@types/react-resizable","version":"4.0.0","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/react-transition-group","version":"4.4.12","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/react-virtualized","version":"9.22.3","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"@types/store","version":"2.0.5","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/tinycolor2","version":"1.4.6","files":["/packages/app-admin/package.json","/packages/app-admin/package.json"]},{"name":"@types/uniqid","version":"5.3.4","files":["/packages/feature-flags/package.json","/packages/plugins/package.json","/packages/feature-flags/package.json","/packages/plugins/package.json"]},{"name":"@types/validate-npm-package-name","version":"4.0.2","files":["/packages/create-webiny-project/package.json","/packages/create-webiny-project/package.json"]},{"name":"@types/warning","version":"3.0.4","files":["/packages/app/package.json","/packages/app/package.json"]},{"name":"@types/ws","version":"8.18.1","files":["/packages/api-websockets-server/package.json","/packages/api-websockets-server/package.json"]},{"name":"@types/yargs","version":"17.0.35","files":["/scripts/buildPackages/package.json","/scripts/release/package.json","/scripts/buildPackages/package.json","/scripts/release/package.json"]},{"name":"@vitest/coverage-v8","version":"4.1.10","files":["/package.json"]},{"name":"adio","version":"3.0.1","files":["/package.json"]},{"name":"amazon-cognito-identity-js","version":"6.3.20","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"apollo-graphql","version":"0.9.7","files":["/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"aws-sdk-client-mock","version":"4.1.0","files":["/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json","/packages/api-scheduler-aws/package.json","/packages/api-sync-system/package.json"]},{"name":"better-sqlite3","version":"12.11.1","files":["/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-core-sql/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-websockets-sql/package.json"]},{"name":"chalk","version":"5.6.2","files":["/package.json","/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"cross-env","version":"10.1.0","files":["/package.json"]},{"name":"cypress","version":"15.18.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-image-snapshot","version":"4.0.1","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-mailosaur","version":"5.0.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-vite","version":"1.10.2","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"cypress-wait-until","version":"3.0.2","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"deepmerge","version":"4.3.1","files":["/package.json"]},{"name":"del","version":"8.0.1","files":["/cypress-tests/package.json","/cypress-tests/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/mcp/package.json","/packages/app-audit-logs/package.json","/packages/app-website-builder/package.json","/packages/common-audit-logs/package.json","/packages/mcp/package.json"]},{"name":"fast-glob","version":"3.3.3","files":["/package.json"]},{"name":"fs-extra","version":"11.3.6","files":["/package.json"]},{"name":"get-yarn-workspaces","version":"1.0.2","files":["/package.json"]},{"name":"github-actions-wac","version":"2.0.0","files":["/package.json"]},{"name":"graphql","version":"16.14.2","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-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/testing/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-mailer/package.json","/packages/api-record-locking/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/testing/package.json"]},{"name":"graphql-request","version":"7.4.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"husky","version":"9.1.7","files":["/package.json"]},{"name":"inquirer","version":"14.0.2","files":["/package.json"]},{"name":"jest-dynalite","version":"3.6.1","files":["/packages/api-core-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-sync-system/package.json","/packages/db-dynamodb/package.json","/packages/project-utils/package.json","/packages/api-core-ddb/package.json","/packages/api-headless-cms-ddb/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-sync-system/package.json","/packages/db-dynamodb/package.json","/packages/project-utils/package.json"]},{"name":"jest-extended","version":"7.0.0","files":["/package.json"]},{"name":"jsdom","version":"29.1.1","files":["/packages/lexical-converter/package.json","/packages/lexical-converter/package.json"]},{"name":"lexical","version":"0.47.0","files":["/package.json"]},{"name":"listr2","version":"10.2.2","files":["/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"load-json-file","version":"7.0.1","files":["/package.json","/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"lodash","version":"4.18.1","files":["/package.json","/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"minimatch","version":"10.2.5","files":["/package.json"]},{"name":"mobx","version":"6.16.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"mobx-react-lite","version":"4.1.1","files":["/packages/form/package.json","/packages/form/package.json"]},{"name":"nanoid","version":"5.1.16","files":["/package.json","/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"next","version":"16.2.10","files":["/package.json"]},{"name":"oxfmt","version":"0.58.0","files":["/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms/package.json","/packages/react-properties/package.json","/packages/admin-ui/package.json","/packages/api-headless-cms/package.json","/packages/react-properties/package.json"]},{"name":"oxlint","version":"1.73.0","files":["/package.json"]},{"name":"pino","version":"10.3.1","files":["/packages/logger/package.json","/packages/project-utils/package.json","/packages/logger/package.json","/packages/project-utils/package.json"]},{"name":"pino-pretty","version":"13.1.3","files":["/packages/project-utils/package.json","/packages/project-utils/package.json"]},{"name":"react","version":"18.3.1","files":["/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/package.json"]},{"name":"rimraf","version":"6.1.3","files":["/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webiny/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms-ddb-es/package.json","/packages/api-mailer/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/common-audit-logs/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/tenant-manager/package.json","/packages/testing/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.101.0","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"semver","version":"7.8.5","files":["/package.json"]},{"name":"storybook","version":"10.4.6","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"storybook-react-rsbuild","version":"3.3.4","files":["/packages/admin-ui/package.json","/packages/admin-ui/package.json"]},{"name":"ts-expect","version":"1.3.0","files":["/package.json"]},{"name":"tsx","version":"4.23.0","files":["/package.json","/packages/mcp/package.json","/packages/mcp/package.json"]},{"name":"type-fest","version":"5.8.0","files":["/package.json","/packages/api-collaboration/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-notifications/package.json","/packages/api-record-locking/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/background-tasks/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json","/packages/api-collaboration/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-notifications/package.json","/packages/api-record-locking/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/background-tasks/package.json","/packages/project/package.json","/packages/pulumi-sdk/package.json","/scripts/prepublishOnly/package.json","/scripts/release/package.json"]},{"name":"typescript","version":"7.0.2","files":["/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-core-sql/package.json","/packages/api-dynamodb-to-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-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/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-mailer/package.json","/packages/api-notifications/package.json","/packages/api-notifications-integrations/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/common-audit-logs/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/sdk/package.json","/packages/shared-aco/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/cypress-tests/package.json","/scripts/cli/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-ddb/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-audit-logs-ddb/package.json","/packages/api-audit-logs-sql/package.json","/packages/api-background-tasks-ddb/package.json","/packages/api-background-tasks-os/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-core-ddb/package.json","/packages/api-core-sql/package.json","/packages/api-dynamodb-to-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-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/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-mailer/package.json","/packages/api-notifications/package.json","/packages/api-notifications-integrations/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/package.json","/packages/api-workflows/package.json","/packages/app/package.json","/packages/app-aco/package.json","/packages/app-admin/package.json","/packages/app-admin-ui/package.json","/packages/app-audit-logs/package.json","/packages/app-collaboration/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-notifications/package.json","/packages/app-record-locking/package.json","/packages/app-scheduler/package.json","/packages/app-sdk-playground/package.json","/packages/app-serverless-cms/package.json","/packages/app-utils/package.json","/packages/app-website-builder/package.json","/packages/app-website-builder-scheduler/package.json","/packages/app-website-builder-workflows/package.json","/packages/app-websockets/package.json","/packages/app-workflows/package.json","/packages/auth0/package.json","/packages/aws-sdk/package.json","/packages/background-tasks/package.json","/packages/cli-core/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/common-audit-logs/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/_templates/base/template.package.json","/packages/db/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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-db/package.json","/packages/handler-graphql/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/languages/package.json","/packages/logger/package.json","/packages/mcp/package.json","/packages/okta/package.json","/packages/plugins/package.json","/packages/project/package.json","/packages/project-aws/package.json","/packages/pulumi/package.json","/packages/pulumi-sdk/package.json","/packages/react-composition/package.json","/packages/sdk/package.json","/packages/shared-aco/package.json","/packages/tenant-manager/package.json","/packages/testing/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/wcp/package.json","/packages/webhooks/package.json","/packages/webiny/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-nuxt/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/packages/website-builder-vue/package.json","/cypress-tests/package.json","/scripts/cli/package.json"]},{"name":"uniqid","version":"5.4.0","files":["/cypress-tests/package.json","/cypress-tests/package.json"]},{"name":"validator","version":"13.15.35","files":["/package.json"]},{"name":"verdaccio","version":"6.7.4","files":["/package.json"]},{"name":"vite","version":"8.1.4","files":["/packages/website-builder-nuxt/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"vitest","version":"4.1.10","files":["/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-core-sql/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-mailer/package.json","/packages/api-notifications/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/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-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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/languages/package.json","/packages/lexical-converter/package.json","/packages/plugins/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/scripts/release/package.json","/packages/admin-ui/package.json","/packages/ai-powerups/package.json","/packages/api/package.json","/packages/api-aco/package.json","/packages/api-aco-sql/package.json","/packages/api-audit-logs/package.json","/packages/api-collaboration/package.json","/packages/api-core/package.json","/packages/api-core-sql/package.json","/packages/api-elasticsearch-tasks/package.json","/packages/api-file-manager-aco/package.json","/packages/api-file-manager-s3/package.json","/packages/api-file-manager-server/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-scheduler/package.json","/packages/api-headless-cms-sql/package.json","/packages/api-headless-cms-storage/package.json","/packages/api-headless-cms-tasks/package.json","/packages/api-headless-cms-workflows/package.json","/packages/api-mailer/package.json","/packages/api-notifications/package.json","/packages/api-opensearch/package.json","/packages/api-record-locking/package.json","/packages/api-scheduler/package.json","/packages/api-scheduler-aws/package.json","/packages/api-scheduler-server/package.json","/packages/api-sync-system/package.json","/packages/api-website-builder/package.json","/packages/api-website-builder-scheduler/package.json","/packages/api-website-builder-workflows/package.json","/packages/api-websockets/package.json","/packages/api-websockets-aws/package.json","/packages/api-websockets-ddb/package.json","/packages/api-websockets-server/package.json","/packages/api-websockets-sql/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-graphql-playground/package.json","/packages/app-headless-cms/package.json","/packages/app-website-builder/package.json","/packages/background-tasks/package.json","/packages/cms-nextjs/package.json","/packages/cms-sdk/package.json","/packages/cognito/package.json","/packages/content-sdk/package.json","/packages/content-sdk-nextjs/package.json","/packages/create-webiny-project/package.json","/packages/db-dynamodb/package.json","/packages/db-utils/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/languages/package.json","/packages/lexical-converter/package.json","/packages/plugins/package.json","/packages/project-aws/package.json","/packages/project-utils/package.json","/packages/react-composition/package.json","/packages/react-properties/package.json","/packages/react-rich-text-lexical-renderer/package.json","/packages/sdk/package.json","/packages/utils/package.json","/packages/validation/package.json","/packages/webhooks/package.json","/packages/website-builder-nextjs/package.json","/packages/website-builder-react/package.json","/packages/website-builder-sdk/package.json","/scripts/release/package.json"]},{"name":"vue","version":"3.5.39","files":["/packages/website-builder-nuxt/package.json","/packages/website-builder-nuxt/package.json"]},{"name":"webpack","version":"5.108.4","files":["/packages/website-builder-nextjs/package.json","/packages/website-builder-nextjs/package.json"]},{"name":"write-json-file","version":"7.0.0","files":["/package.json","/packages/api-headless-cms/package.json","/packages/api-headless-cms/package.json"]},{"name":"yargs","version":"18.0.0","files":["/package.json","/packages/project-utils/package.json","/packages/project-utils/package.json"]}],"peerDependencies":[{"name":"aws-lambda","version":"1.0.7","files":["/packages/aws-sdk/package.json","/packages/aws-sdk/package.json"]},{"name":"react","version":"18.3.1","files":["/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json","/packages/app-audit-logs/package.json","/packages/form/package.json","/packages/i18n/package.json","/packages/i18n-react/package.json"]}],"resolutions":[{"name":"@types/react","version":"18.3.31","files":["/package.json"]},{"name":"@types/react-dom","version":"18.3.7","files":["/package.json"]},{"name":"react","version":"18.3.1","files":["/package.json"]},{"name":"react-dom","version":"18.3.1","files":["/package.json"]}],"references":[{"name":"@types/hoist-non-react-statics","versions":[{"version":"3.3.7","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"jwt-decode","versions":[{"version":"4.0.0","files":[{"file":"/package.json","types":["dependencies"]}]}]},{"name":"@babel/code-frame","versions":[{"version":"8.0.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@commitlint/cli","versions":[{"version":"21.2.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@commitlint/config-conventional","versions":[{"version":"21.2.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@grpc/grpc-js","versions":[{"version":"1.14.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@lexical/code","versions":[{"version":"0.47.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.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/headless","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/history","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/html","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"@lexical/list","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/mark","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/overflow","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@lexical/react","versions":[{"version":"0.47.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.47.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.47.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.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]}]}]},{"name":"@lexical/utils","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"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/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@octokit/rest","versions":[{"version":"22.0.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"@types/fs-extra","versions":[{"version":"11.0.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"@types/node","versions":[{"version":"24.13.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]}]}]},{"name":"@types/react","versions":[{"version":"18.3.31","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-collaboration/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-notifications/package.json","types":["devDependencies"]},{"file":"/packages/app-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app-workflows/package.json","types":["devDependencies"]},{"file":"/packages/cognito/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.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/website-builder-react/package.json","types":["devDependencies"]}]}]},{"name":"@types/react-dom","versions":[{"version":"18.3.7","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]}]}]},{"name":"@vitest/coverage-v8","versions":[{"version":"4.1.10","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"adio","versions":[{"version":"3.0.1","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"chalk","versions":[{"version":"5.6.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/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/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"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"cross-env","versions":[{"version":"10.1.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"deepmerge","versions":[{"version":"4.3.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"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/mcp/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"fast-glob","versions":[{"version":"3.3.3","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/i18n/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]}]}]},{"name":"fs-extra","versions":[{"version":"11.3.6","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-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":"github-actions-wac","versions":[{"version":"2.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"graphql","versions":[{"version":"16.14.2","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-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/api-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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-website-builder/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/testing/package.json","types":["devDependencies"]}]}]},{"name":"husky","versions":[{"version":"9.1.7","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"inquirer","versions":[{"version":"14.0.2","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":"7.0.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"lexical","versions":[{"version":"0.47.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"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"]}]}]},{"name":"load-json-file","versions":[{"version":"7.0.1","files":[{"file":"/package.json","types":["devDependencies"]},{"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/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"lodash","versions":[{"version":"4.18.1","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-aco/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-ddb-es/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-storage/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-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-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cognito/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/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/react-properties/package.json","types":["dependencies"]},{"file":"/packages/validation/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"minimatch","versions":[{"version":"10.2.5","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-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"nanoid","versions":[{"version":"5.1.16","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":"next","versions":[{"version":"16.2.10","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"oxfmt","versions":[{"version":"0.58.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/react-properties/package.json","types":["devDependencies"]}]}]},{"name":"oxlint","versions":[{"version":"1.73.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"react","versions":[{"version":"18.3.1","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/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-ui/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["peerDependencies"]},{"file":"/packages/app-collaboration/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-notifications/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.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/languages/package.json","types":["dependencies"]},{"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"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/project-aws-template/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/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/webiny/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"react-dom","versions":[{"version":"18.3.1","files":[{"file":"/package.json","types":["devDependencies","resolutions"]},{"file":"/packages/ai-powerups/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-ui/package.json","types":["dependencies"]},{"file":"/packages/app-collaboration/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-notifications/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["dependencies"]},{"file":"/packages/languages/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor-actions/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/react-composition/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"semver","versions":[{"version":"7.8.5","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]},{"file":"/packages/system-requirements/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"ts-expect","versions":[{"version":"1.3.0","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"tsx","versions":[{"version":"4.23.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"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.8.0","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-collaboration/package.json","types":["devDependencies"]},{"file":"/packages/api-elasticsearch-tasks/package.json","types":["devDependencies"]},{"file":"/packages/api-notifications/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-aws/package.json","types":["dependencies","devDependencies"]},{"file":"/packages/api-workflows/package.json","types":["devDependencies"]},{"file":"/packages/app/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/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/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["devDependencies"]}]}]},{"name":"typescript","versions":[{"version":"7.0.2","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/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-collaboration/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-dynamodb-to-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-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-server/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-storage/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-notifications/package.json","types":["devDependencies"]},{"file":"/packages/api-notifications-integrations/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-server/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-scheduler/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-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-collaboration/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/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-notifications/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/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-scheduler/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/auth0/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["devDependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/cms-sdk/package.json","types":["devDependencies"]},{"file":"/packages/common-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/_templates/base/template.package.json","types":["devDependencies"]},{"file":"/packages/db/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/db-utils/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-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/languages/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"file":"/packages/okta/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/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/react-composition/package.json","types":["devDependencies"]},{"file":"/packages/sdk/package.json","types":["devDependencies"]},{"file":"/packages/shared-aco/package.json","types":["devDependencies"]},{"file":"/packages/tenant-manager/package.json","types":["devDependencies"]},{"file":"/packages/testing/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/webhooks/package.json","types":["devDependencies"]},{"file":"/packages/webiny/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-react/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["devDependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]},{"file":"/scripts/cli/package.json","types":["devDependencies"]}]}]},{"name":"validator","versions":[{"version":"13.15.35","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"verdaccio","versions":[{"version":"6.7.4","files":[{"file":"/package.json","types":["devDependencies"]}]}]},{"name":"vitest","versions":[{"version":"4.1.10","files":[{"file":"/package.json","types":["devDependencies"]},{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/api-collaboration/package.json","types":["devDependencies"]},{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/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-file-manager-server/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-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-storage/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-notifications/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler-server/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-scheduler/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-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-graphql-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/package.json","types":["devDependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/cms-sdk/package.json","types":["devDependencies"]},{"file":"/packages/cognito/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk/package.json","types":["devDependencies"]},{"file":"/packages/content-sdk-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/db-utils/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/languages/package.json","types":["devDependencies"]},{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/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/sdk/package.json","types":["devDependencies"]},{"file":"/packages/utils/package.json","types":["devDependencies"]},{"file":"/packages/validation/package.json","types":["devDependencies"]},{"file":"/packages/webhooks/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":"/scripts/release/package.json","types":["devDependencies"]}]}]},{"name":"write-json-file","versions":[{"version":"7.0.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":"/packages/project/package.json","types":["dependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]},{"file":"/scripts/prepublishOnly/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"yargs","versions":[{"version":"18.0.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/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"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-svg-core","versions":[{"version":"7.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/react-fontawesome","versions":[{"version":"3.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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.4","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-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"@radix-ui/react-scroll-area","versions":[{"version":"1.2.14","files":[{"file":"/packages/admin-ui/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-sync-system/package.json","types":["dependencies"]},{"file":"/packages/app-admin/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"]},{"file":"/packages/app-admin-ui/package.json","types":["dependencies"]}]}]},{"name":"date-fns","versions":[{"version":"4.4.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-storage/package.json","types":["dependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"mobx","versions":[{"version":"6.16.1","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/ai-powerups/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-collaboration/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-notifications/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cms-sdk/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/languages/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-vue/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"]},{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["dependencies"]}]}]},{"name":"radix-ui","versions":[{"version":"1.6.2","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/lexical-editor/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"]}]}]},{"name":"react-day-picker","versions":[{"version":"10.0.1","files":[{"file":"/packages/admin-ui/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":"3.6.0","files":[{"file":"/packages/admin-ui/package.json","types":["dependencies"]}]}]},{"name":"tailwindcss","versions":[{"version":"4.3.2","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":"7.3.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/core","versions":[{"version":"2.1.5","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-svgr","versions":[{"version":"2.0.5","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@storybook/addon-a11y","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/addon-docs","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@storybook/react","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"@tailwindcss/postcss","versions":[{"version":"4.3.2","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@types/react-color","versions":[{"version":"3.0.13","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"]}]}]},{"name":"rimraf","versions":[{"version":"6.1.3","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/ai-powerups/package.json","types":["devDependencies"]},{"file":"/packages/api/package.json","types":["devDependencies"]},{"file":"/packages/api-aco/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/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-core/package.json","types":["devDependencies"]},{"file":"/packages/api-core-ddb/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-s3/package.json","types":["devDependencies"]},{"file":"/packages/api-file-manager-server/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-mailer/package.json","types":["devDependencies"]},{"file":"/packages/api-opensearch/package.json","types":["devDependencies"]},{"file":"/packages/api-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/api-scheduler/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/api-websockets-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-ddb/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/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-ui/package.json","types":["devDependencies"]},{"file":"/packages/app-audit-logs/package.json","types":["devDependencies"]},{"file":"/packages/app-collaboration/package.json","types":["devDependencies"]},{"file":"/packages/app-file-manager/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-notifications/package.json","types":["devDependencies"]},{"file":"/packages/app-record-locking/package.json","types":["devDependencies"]},{"file":"/packages/app-scheduler/package.json","types":["devDependencies"]},{"file":"/packages/app-sdk-playground/package.json","types":["devDependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder/package.json","types":["devDependencies"]},{"file":"/packages/app-website-builder-scheduler/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/auth0/package.json","types":["devDependencies"]},{"file":"/packages/aws-sdk/package.json","types":["devDependencies"]},{"file":"/packages/background-tasks/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/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-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/languages/package.json","types":["devDependencies"]},{"file":"/packages/logger/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]},{"file":"/packages/plugins/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["devDependencies"]},{"file":"/packages/pulumi/package.json","types":["devDependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["devDependencies"]},{"file":"/packages/tenant-manager/package.json","types":["devDependencies"]},{"file":"/packages/testing/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.101.0","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]},{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"storybook","versions":[{"version":"10.4.6","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"storybook-react-rsbuild","versions":[{"version":"3.3.4","files":[{"file":"/packages/admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"ai","versions":[{"version":"7.0.19","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"front-matter","versions":[{"version":"4.0.2","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["dependencies"]}]}]},{"name":"mobx-react-lite","versions":[{"version":"4.1.1","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/app-aco/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-collaboration/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-notifications/package.json","types":["dependencies"]},{"file":"/packages/app-record-locking/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/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/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cms-nextjs/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/tenant-manager/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]}]}]},{"name":"zod","versions":[{"version":"4.4.3","files":[{"file":"/packages/ai-powerups/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs-ddb/package.json","types":["dependencies"]},{"file":"/packages/api-collaboration/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-tasks/package.json","types":["dependencies"]},{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/api-notifications/package.json","types":["dependencies"]},{"file":"/packages/api-scheduler-aws/package.json","types":["dependencies"]},{"file":"/packages/api-sync-system/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-aws/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-server/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/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/auth0/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]},{"file":"/packages/mcp/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]}]}]},{"name":"knex","versions":[{"version":"3.3.0","files":[{"file":"/packages/api-aco-sql/package.json","types":["dependencies"]},{"file":"/packages/api-audit-logs-sql/package.json","types":["dependencies"]},{"file":"/packages/api-core-sql/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["dependencies"]},{"file":"/packages/api-websockets-sql/package.json","types":["dependencies"]}]}]},{"name":"better-sqlite3","versions":[{"version":"12.11.1","files":[{"file":"/packages/api-aco-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-audit-logs-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-core-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-headless-cms-sql/package.json","types":["devDependencies"]},{"file":"/packages/api-websockets-sql/package.json","types":["devDependencies"]}]}]},{"name":"@ai-sdk/anthropic","versions":[{"version":"4.0.11","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"@ai-sdk/openai","versions":[{"version":"4.0.11","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"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":"jose","versions":[{"version":"6.2.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]}]}]},{"name":"jsonwebtoken","versions":[{"version":"9.0.3","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/auth0/package.json","types":["dependencies"]},{"file":"/packages/cognito/package.json","types":["dependencies"]},{"file":"/packages/okta/package.json","types":["dependencies"]}]}]},{"name":"pino","versions":[{"version":"10.3.1","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"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-lambda","versions":[{"version":"4.4.1","files":[{"file":"/packages/api-core/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@types/jsonwebtoken","versions":[{"version":"9.0.10","files":[{"file":"/packages/api-core/package.json","types":["devDependencies"]},{"file":"/packages/auth0/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]}]}]},{"name":"jest-dynalite","versions":[{"version":"3.6.1","files":[{"file":"/packages/api-core-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-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["devDependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]}]}]},{"name":"p-retry","versions":[{"version":"8.0.0","files":[{"file":"/packages/api-dynamodb-to-elasticsearch/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/scripts/release/package.json","types":["dependencies"]}]}]},{"name":"cache-control-parser","versions":[{"version":"2.2.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"mime","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-file-manager/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"object-hash","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"p-map","versions":[{"version":"7.0.5","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/sdk/package.json","types":["dependencies"]}]}]},{"name":"p-reduce","versions":[{"version":"3.0.0","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]},{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"sanitize-filename","versions":[{"version":"1.6.4","files":[{"file":"/packages/api-file-manager/package.json","types":["dependencies"]}]}]},{"name":"@types/object-hash","versions":[{"version":"3.0.6","files":[{"file":"/packages/api-file-manager/package.json","types":["devDependencies"]}]}]},{"name":"exifreader","versions":[{"version":"4.41.0","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"sharp","versions":[{"version":"0.35.3","files":[{"file":"/packages/api-file-manager-s3/package.json","types":["dependencies"]},{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"@fastify/multipart","versions":[{"version":"10.1.0","files":[{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]}]}]},{"name":"fastify","versions":[{"version":"5.10.0","files":[{"file":"/packages/api-file-manager-server/package.json","types":["dependencies"]},{"file":"/packages/handler/package.json","types":["dependencies"]},{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/merge","versions":[{"version":"9.2.0","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.36","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"dot-prop-immutable","versions":[{"version":"2.1.1","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"graphql-tag","versions":[{"version":"2.12.7","files":[{"file":"/packages/api-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"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.9","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-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/form/package.json","types":["dependencies"]}]}]},{"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":"@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":"dot-prop","versions":[{"version":"10.1.0","files":[{"file":"/packages/api-headless-cms-storage/package.json","types":["dependencies"]},{"file":"/packages/db-dynamodb/package.json","types":["dependencies"]}]}]},{"name":"email-addresses","versions":[{"version":"5.0.0","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"nodemailer","versions":[{"version":"9.0.3","files":[{"file":"/packages/api-mailer/package.json","types":["dependencies"]}]}]},{"name":"@types/nodemailer","versions":[{"version":"8.0.1","files":[{"file":"/packages/api-mailer/package.json","types":["devDependencies"]}]}]},{"name":"@opensearch-project/opensearch","versions":[{"version":"3.6.0","files":[{"file":"/packages/api-opensearch/package.json","types":["dependencies"]}]}]},{"name":"aws-sdk-client-mock","versions":[{"version":"4.1.0","files":[{"file":"/packages/api-scheduler-aws/package.json","types":["devDependencies"]},{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"bree","versions":[{"version":"9.2.9","files":[{"file":"/packages/api-scheduler-server/package.json","types":["dependencies"]}]}]},{"name":"@faker-js/faker","versions":[{"version":"10.5.0","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]}]}]},{"name":"@types/lodash","versions":[{"version":"4.17.24","files":[{"file":"/packages/api-sync-system/package.json","types":["devDependencies"]},{"file":"/packages/app/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/mcp/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":"ws","versions":[{"version":"8.21.0","files":[{"file":"/packages/api-websockets-server/package.json","types":["dependencies"]}]}]},{"name":"@types/ws","versions":[{"version":"8.18.1","files":[{"file":"/packages/api-websockets-server/package.json","types":["devDependencies"]}]}]},{"name":"@emotion/styled","versions":[{"version":"11.14.1","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-scheduler/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-serverless-cms/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-admin/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"apollo-link","versions":[{"version":"1.2.14","files":[{"file":"/packages/app/package.json","types":["dependencies"]},{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-serverless-cms/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/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-serverless-cms/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":"warning","versions":[{"version":"4.0.3","files":[{"file":"/packages/app/package.json","types":["dependencies"]}]}]},{"name":"@types/warning","versions":[{"version":"3.0.4","files":[{"file":"/packages/app/package.json","types":["devDependencies"]}]}]},{"name":"@apollo/react-components","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@apollo/react-hooks","versions":[{"version":"3.1.5","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@atlaskit/pragmatic-drag-and-drop","versions":[{"version":"2.0.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@atlaskit/pragmatic-drag-and-drop-hitbox","versions":[{"version":"2.0.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@emotion/css","versions":[{"version":"11.13.5","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/lexical-editor-actions/package.json","types":["dependencies"]}]}]},{"name":"@emotion/react","versions":[{"version":"11.14.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]},{"file":"/packages/app-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/react-rich-text-lexical-renderer/package.json","types":["devDependencies"]}]}]},{"name":"@iconify/json","versions":[{"version":"2.2.498","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"classnames","versions":[{"version":"2.5.1","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/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":"markdown-to-jsx","versions":[{"version":"9.8.2","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]},{"file":"/packages/app-website-builder/package.json","types":["dependencies"]}]}]},{"name":"react-resizable-panels","versions":[{"version":"4.12.1","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":"unicode-emoji-json","versions":[{"version":"0.9.0","files":[{"file":"/packages/app-admin/package.json","types":["dependencies"]}]}]},{"name":"@testing-library/react","versions":[{"version":"16.3.2","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]},{"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"]}]}]},{"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":"4.0.0","files":[{"file":"/packages/app-admin/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"]}]}]},{"name":"@types/tinycolor2","versions":[{"version":"1.4.6","files":[{"file":"/packages/app-admin/package.json","types":["devDependencies"]}]}]},{"name":"react-helmet","versions":[{"version":"6.1.0","files":[{"file":"/packages/app-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"]},{"file":"/packages/app-website-builder-workflows/package.json","types":["dependencies"]},{"file":"/packages/app-workflows/package.json","types":["dependencies"]}]}]},{"name":"@types/react-helmet","versions":[{"version":"6.1.11","files":[{"file":"/packages/app-admin-ui/package.json","types":["devDependencies"]}]}]},{"name":"dayjs","versions":[{"version":"1.11.21","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"react-lazy-load","versions":[{"version":"4.0.1","files":[{"file":"/packages/app-file-manager/package.json","types":["dependencies"]}]}]},{"name":"graphql-language-service","versions":[{"version":"5.5.2","files":[{"file":"/packages/app-graphql-playground/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/fontawesome-common-types","versions":[{"version":"7.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-brands-svg-icons","versions":[{"version":"7.3.0","files":[{"file":"/packages/app-headless-cms/package.json","types":["dependencies"]}]}]},{"name":"@fortawesome/free-regular-svg-icons","versions":[{"version":"7.3.0","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":"dnd-core","versions":[{"version":"16.0.1","files":[{"file":"/packages/app-headless-cms-common/package.json","types":["dependencies"]}]}]},{"name":"@material-design-icons/svg","versions":[{"version":"0.14.15","files":[{"file":"/packages/app-scheduler/package.json","types":["dependencies"]},{"file":"/packages/icons/package.json","types":["devDependencies"]}]}]},{"name":"apollo-link-batch-http","versions":[{"version":"1.2.14","files":[{"file":"/packages/app-serverless-cms/package.json","types":["dependencies"]}]}]},{"name":"deep-equal","versions":[{"version":"2.2.3","files":[{"file":"/packages/app-website-builder/package.json","types":["dependencies"]},{"file":"/packages/background-tasks/package.json","types":["dependencies"]},{"file":"/packages/website-builder-react/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["dependencies"]}]}]},{"name":"matcher","versions":[{"version":"6.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":"@auth0/auth0-spa-js","versions":[{"version":"2.23.0","files":[{"file":"/packages/auth0/package.json","types":["dependencies"]}]}]},{"name":"@types/jwk-to-pem","versions":[{"version":"2.0.3","files":[{"file":"/packages/auth0/package.json","types":["devDependencies"]},{"file":"/packages/okta/package.json","types":["devDependencies"]}]}]},{"name":"@aws-sdk/client-apigatewaymanagementapi","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudfront","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-events","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cloudwatch-logs","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-cognito-identity-provider","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-dynamodb-streams","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-eventbridge","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iam","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-iot","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-lambda","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-s3","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-scheduler","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sfn","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sqs","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/client-sts","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/credential-providers","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-dynamodb","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/lib-storage","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-presigned-post","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/s3-request-presigner","versions":[{"version":"3.1084.0","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@aws-sdk/util-dynamodb","versions":[{"version":"3.996.6","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"@types/aws-lambda","versions":[{"version":"8.10.162","files":[{"file":"/packages/aws-sdk/package.json","types":["dependencies"]}]}]},{"name":"aws-lambda","versions":[{"version":"1.0.7","files":[{"file":"/packages/aws-sdk/package.json","types":["peerDependencies"]}]}]},{"name":"object-merge-advanced","versions":[{"version":"14.1.3","files":[{"file":"/packages/background-tasks/package.json","types":["dependencies"]}]}]},{"name":"object-sizeof","versions":[{"version":"2.6.5","files":[{"file":"/packages/background-tasks/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-react","versions":[{"version":"2.1.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-sass","versions":[{"version":"2.0.1","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsbuild/plugin-type-check","versions":[{"version":"1.5.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rsdoctor/rspack-plugin","versions":[{"version":"1.5.18","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rslib/core","versions":[{"version":"0.23.2","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@rspack/core","versions":[{"version":"2.1.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"@swc/plugin-emotion","versions":[{"version":"14.15.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"css-loader","versions":[{"version":"7.1.4","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"find-up","versions":[{"version":"8.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":"postcss-loader","versions":[{"version":"8.2.1","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"]}]}]},{"name":"react-refresh","versions":[{"version":"0.18.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"sass-loader","versions":[{"version":"17.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"strip-json-comments","versions":[{"version":"5.0.3","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"style-loader","versions":[{"version":"4.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"ts-morph","versions":[{"version":"28.0.0","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/scripts/cli/package.json","types":["dependencies"]},{"file":"/scripts/cjsToEsm/package.json","types":["dependencies"]},{"file":"/scripts/generateSkills/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.6","files":[{"file":"/packages/build-tools/package.json","types":["dependencies"]}]}]},{"name":"ci-info","versions":[{"version":"4.4.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/global-config/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"humanize-duration","versions":[{"version":"3.34.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"open","versions":[{"version":"11.0.0","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]}]}]},{"name":"ora","versions":[{"version":"9.4.1","files":[{"file":"/packages/cli-core/package.json","types":["dependencies"]},{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"pino-pretty","versions":[{"version":"13.1.3","files":[{"file":"/packages/cli-core/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":"fast-json-patch","versions":[{"version":"3.1.1","files":[{"file":"/packages/cms-sdk/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"aws-amplify","versions":[{"version":"6.18.0","files":[{"file":"/packages/cognito/package.json","types":["dependencies"]}]}]},{"name":"js-yaml","versions":[{"version":"5.2.1","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"listr2","versions":[{"version":"10.2.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]},{"file":"/packages/project-utils/package.json","types":["devDependencies"]},{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"os","versions":[{"version":"0.1.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["dependencies"]}]}]},{"name":"validate-npm-package-name","versions":[{"version":"8.0.0","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":"4.0.2","files":[{"file":"/packages/create-webiny-project/package.json","types":["devDependencies"]}]}]},{"name":"dynamodb-toolbox","versions":[{"version":"0.9.5","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":"fuse.js","versions":[{"version":"7.4.2","files":[{"file":"/packages/db-utils/package.json","types":["dependencies"]}]}]},{"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/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":"@fastify/compress","versions":[{"version":"9.0.0","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/cookie","versions":[{"version":"11.1.1","files":[{"file":"/packages/handler/package.json","types":["dependencies"]}]}]},{"name":"@fastify/aws-lambda","versions":[{"version":"6.4.0","files":[{"file":"/packages/handler-aws/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/resolvers-composition","versions":[{"version":"7.0.33","files":[{"file":"/packages/handler-graphql/package.json","types":["dependencies"]}]}]},{"name":"@graphql-tools/utils","versions":[{"version":"11.2.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":"4.2.3","files":[{"file":"/packages/i18n/package.json","types":["dependencies"]}]}]},{"name":"ohash","versions":[{"version":"2.0.11","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":"cheerio","versions":[{"version":"1.2.0","files":[{"file":"/packages/lexical-converter/package.json","types":["dependencies"]}]}]},{"name":"jsdom","versions":[{"version":"29.1.1","files":[{"file":"/packages/lexical-converter/package.json","types":["devDependencies"]},{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"@types/prismjs","versions":[{"version":"1.26.6","files":[{"file":"/packages/lexical-nodes/package.json","types":["dependencies"]}]}]},{"name":"@modelcontextprotocol/sdk","versions":[{"version":"1.29.0","files":[{"file":"/packages/mcp/package.json","types":["dependencies"]}]}]},{"name":"@types/ncp","versions":[{"version":"2.0.8","files":[{"file":"/packages/mcp/package.json","types":["devDependencies"]},{"file":"/packages/project-aws/package.json","types":["devDependencies"]}]}]},{"name":"@okta/okta-auth-js","versions":[{"version":"8.0.1","files":[{"file":"/packages/okta/package.json","types":["dependencies"]}]}]},{"name":"uniqid","versions":[{"version":"5.4.0","files":[{"file":"/packages/plugins/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"chokidar","versions":[{"version":"5.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"debounce","versions":[{"version":"3.0.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"dotenv","versions":[{"version":"17.4.2","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"exit-hook","versions":[{"version":"5.1.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"get-tsconfig","versions":[{"version":"4.14.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"graphql-request","versions":[{"version":"7.4.0","files":[{"file":"/packages/project/package.json","types":["dependencies"]},{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"mqtt","versions":[{"version":"5.15.2","files":[{"file":"/packages/project/package.json","types":["dependencies"]}]}]},{"name":"neverthrow","versions":[{"version":"8.2.0","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"]},{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"serialize-error","versions":[{"version":"13.0.1","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/jsdom","versions":[{"version":"28.0.3","files":[{"file":"/packages/project/package.json","types":["devDependencies"]}]}]},{"name":"@pulumi/aws","versions":[{"version":"7.36.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]},{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"@pulumi/pulumi","versions":[{"version":"3.251.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.21.0","files":[{"file":"/packages/project-aws/package.json","types":["dependencies"]}]}]},{"name":"decompress","versions":[{"version":"4.2.1","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"tar","versions":[{"version":"7.5.19","files":[{"file":"/packages/pulumi-sdk/package.json","types":["dependencies"]}]}]},{"name":"standardwebhooks","versions":[{"version":"1.0.0","files":[{"file":"/packages/sdk/package.json","types":["dependencies"]},{"file":"/packages/webhooks/package.json","types":["dependencies"]}]}]},{"name":"cli-table3","versions":[{"version":"0.6.5","files":[{"file":"/packages/system-requirements/package.json","types":["dependencies"]}]}]},{"name":"jsesc","versions":[{"version":"3.1.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"strip-ansi","versions":[{"version":"7.2.0","files":[{"file":"/packages/telemetry/package.json","types":["dependencies"]}]}]},{"name":"@noble/hashes","versions":[{"version":"2.2.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":"jsonpack","versions":[{"version":"1.1.5","files":[{"file":"/packages/utils/package.json","types":["dependencies"]}]}]},{"name":"nanoid-dictionary","versions":[{"version":"5.0.0","files":[{"file":"/packages/utils/package.json","types":["dependencies"]},{"file":"/packages/website-builder-sdk/package.json","types":["dependencies"]}]}]},{"name":"postcss","versions":[{"version":"8.5.16","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["dependencies"]}]}]},{"name":"postcss-import","versions":[{"version":"16.1.1","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["dependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["dependencies"]}]}]},{"name":"@types/postcss-import","versions":[{"version":"14.0.3","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]}]}]},{"name":"webpack","versions":[{"version":"5.108.4","files":[{"file":"/packages/website-builder-nextjs/package.json","types":["devDependencies"]}]}]},{"name":"vite","versions":[{"version":"8.1.4","files":[{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]}]}]},{"name":"vue","versions":[{"version":"3.5.39","files":[{"file":"/packages/website-builder-nuxt/package.json","types":["devDependencies"]},{"file":"/packages/website-builder-vue/package.json","types":["dependencies"]}]}]},{"name":"csstype","versions":[{"version":"3.2.3","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":"2.3.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"@testing-library/cypress","versions":[{"version":"10.1.3","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"amazon-cognito-identity-js","versions":[{"version":"6.3.20","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress","versions":[{"version":"15.18.1","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":"5.0.0","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-vite","versions":[{"version":"1.10.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"cypress-wait-until","versions":[{"version":"3.0.2","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"del","versions":[{"version":"8.0.1","files":[{"file":"/cypress-tests/package.json","types":["devDependencies"]}]}]},{"name":"node-notifier","versions":[{"version":"10.0.1","files":[{"file":"/scripts/buildPackages/package.json","types":["dependencies"]}]}]},{"name":"@types/yargs","versions":[{"version":"17.0.35","files":[{"file":"/scripts/buildPackages/package.json","types":["devDependencies"]},{"file":"/scripts/release/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.3.0","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/project-aws/_templates/appTemplates/api/graphql/package.json b/packages/project-aws/_templates/appTemplates/api/graphql/package.json index d8a48d5570b..db877dc0b55 100644 --- a/packages/project-aws/_templates/appTemplates/api/graphql/package.json +++ b/packages/project-aws/_templates/appTemplates/api/graphql/package.json @@ -39,6 +39,9 @@ "@webiny/api-websockets-ddb": "latest", "@webiny/api-websockets-server": "latest", "@webiny/api-websockets-sql": "latest", + "@webiny/api-collaboration": "latest", + "@webiny/api-notifications": "latest", + "@webiny/api-notifications-integrations": "latest", "@webiny/api-workflows": "latest", "@webiny/aws-sdk": "latest", "@webiny/build-tools": "latest", diff --git a/packages/project-aws/_templates/appTemplates/api/graphql/src/index.ts b/packages/project-aws/_templates/appTemplates/api/graphql/src/index.ts index b8e678516bb..57af3bf3e60 100644 --- a/packages/project-aws/_templates/appTemplates/api/graphql/src/index.ts +++ b/packages/project-aws/_templates/appTemplates/api/graphql/src/index.ts @@ -31,6 +31,9 @@ import { createMailerContext, createMailerGraphQL } from "@webiny/api-mailer"; import { createWorkflows } from "@webiny/api-workflows"; import { createHeadlessCmsWorkflows } from "@webiny/api-headless-cms-workflows"; import { createWebsiteBuilderWorkflows } from "@webiny/api-website-builder-workflows"; +import { createCollaboration } from "@webiny/api-collaboration"; +import { createNotifications } from "@webiny/api-notifications"; +import { createNotificationsIntegrations } from "@webiny/api-notifications-integrations"; import { createWebsiteBuilderScheduler } from "@webiny/api-website-builder-scheduler"; import { createWebhooks } from "@webiny/webhooks/api"; @@ -71,6 +74,9 @@ export const handler = createHandler({ createWorkflows(), createHeadlessCmsWorkflows(), createWebsiteBuilderWorkflows(), + createCollaboration(), + createNotifications(), + createNotificationsIntegrations(), registerAuditLogsDdbStorageOperations({ documentClient, }), diff --git a/packages/project-aws/_templates/extensions/OpenSearch/api/graphql/src/index.ts b/packages/project-aws/_templates/extensions/OpenSearch/api/graphql/src/index.ts index ba3fe80c012..89f1658bd12 100644 --- a/packages/project-aws/_templates/extensions/OpenSearch/api/graphql/src/index.ts +++ b/packages/project-aws/_templates/extensions/OpenSearch/api/graphql/src/index.ts @@ -32,6 +32,9 @@ import { createMailerContext, createMailerGraphQL } from "@webiny/api-mailer"; import { createWorkflows } from "@webiny/api-workflows"; import { createHeadlessCmsWorkflows } from "@webiny/api-headless-cms-workflows"; import { createWebsiteBuilderWorkflows } from "@webiny/api-website-builder-workflows"; +import { createCollaboration } from "@webiny/api-collaboration"; +import { createNotifications } from "@webiny/api-notifications"; +import { createNotificationsIntegrations } from "@webiny/api-notifications-integrations"; import { createWebsiteBuilderScheduler } from "@webiny/api-website-builder-scheduler"; import { createWebhooks } from "@webiny/webhooks/api"; @@ -83,6 +86,9 @@ export const handler = createHandler({ createWorkflows(), createHeadlessCmsWorkflows(), createWebsiteBuilderWorkflows(), + createCollaboration(), + createNotifications(), + createNotificationsIntegrations(), registerAuditLogsDdbStorageOperations({ documentClient }), diff --git a/packages/project-aws/_templates/extensions/sqlite/api/graphql/src/index.ts b/packages/project-aws/_templates/extensions/sqlite/api/graphql/src/index.ts index 37c9f025eac..98a46d7b2a5 100644 --- a/packages/project-aws/_templates/extensions/sqlite/api/graphql/src/index.ts +++ b/packages/project-aws/_templates/extensions/sqlite/api/graphql/src/index.ts @@ -28,6 +28,9 @@ import { createMailerContext, createMailerGraphQL } from "@webiny/api-mailer"; import { createWorkflows } from "@webiny/api-workflows"; import { createHeadlessCmsWorkflows } from "@webiny/api-headless-cms-workflows"; import { createWebsiteBuilderWorkflows } from "@webiny/api-website-builder-workflows"; +import { createCollaboration } from "@webiny/api-collaboration"; +import { createNotifications } from "@webiny/api-notifications"; +import { createNotificationsIntegrations } from "@webiny/api-notifications-integrations"; import { createWebsiteBuilderScheduler } from "@webiny/api-website-builder-scheduler"; import { createWebhooks } from "@webiny/webhooks/api"; @@ -74,6 +77,9 @@ export const handler = createHandler({ createWorkflows(), createHeadlessCmsWorkflows(), createWebsiteBuilderWorkflows(), + createCollaboration(), + createNotifications(), + createNotificationsIntegrations(), registerAuditLogsSqlStorageOperations({ knex }), diff --git a/yarn.lock b/yarn.lock index 6712e7c4b39..5e19289e0ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10692,6 +10692,28 @@ __metadata: languageName: unknown linkType: soft +"@webiny/api-collaboration@npm:0.0.0, @webiny/api-collaboration@workspace:packages/api-collaboration": + version: 0.0.0-use.local + resolution: "@webiny/api-collaboration@workspace:packages/api-collaboration" + dependencies: + "@webiny/api": "npm:0.0.0" + "@webiny/api-core": "npm:0.0.0" + "@webiny/api-headless-cms": "npm:0.0.0" + "@webiny/build-tools": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" + "@webiny/handler": "npm:0.0.0" + "@webiny/handler-graphql": "npm:0.0.0" + "@webiny/plugins": "npm:0.0.0" + "@webiny/project-utils": "npm:0.0.0" + "@webiny/testing": "npm:0.0.0" + "@webiny/utils": "npm:0.0.0" + type-fest: "npm:^5.8.0" + typescript: "npm:7.0.2" + vitest: "npm:^4.1.10" + zod: "npm:4.4.3" + languageName: unknown + linkType: soft + "@webiny/api-core-ddb@npm:0.0.0, @webiny/api-core-ddb@workspace:packages/api-core-ddb": version: 0.0.0-use.local resolution: "@webiny/api-core-ddb@workspace:packages/api-core-ddb" @@ -11238,6 +11260,43 @@ __metadata: languageName: unknown linkType: soft +"@webiny/api-notifications-integrations@workspace:packages/api-notifications-integrations": + version: 0.0.0-use.local + resolution: "@webiny/api-notifications-integrations@workspace:packages/api-notifications-integrations" + dependencies: + "@webiny/api-collaboration": "npm:0.0.0" + "@webiny/api-core": "npm:0.0.0" + "@webiny/api-notifications": "npm:0.0.0" + "@webiny/api-workflows": "npm:0.0.0" + "@webiny/build-tools": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" + "@webiny/handler": "npm:0.0.0" + "@webiny/project-utils": "npm:0.0.0" + typescript: "npm:7.0.2" + languageName: unknown + linkType: soft + +"@webiny/api-notifications@npm:0.0.0, @webiny/api-notifications@workspace:packages/api-notifications": + version: 0.0.0-use.local + resolution: "@webiny/api-notifications@workspace:packages/api-notifications" + dependencies: + "@webiny/api-core": "npm:0.0.0" + "@webiny/api-headless-cms": "npm:0.0.0" + "@webiny/build-tools": "npm:0.0.0" + "@webiny/feature": "npm:0.0.0" + "@webiny/handler": "npm:0.0.0" + "@webiny/handler-graphql": "npm:0.0.0" + "@webiny/plugins": "npm:0.0.0" + "@webiny/project-utils": "npm:0.0.0" + "@webiny/testing": "npm:0.0.0" + "@webiny/utils": "npm:0.0.0" + type-fest: "npm:^5.8.0" + typescript: "npm:7.0.2" + vitest: "npm:^4.1.10" + zod: "npm:4.4.3" + languageName: unknown + linkType: soft + "@webiny/api-opensearch@npm:0.0.0, @webiny/api-opensearch@workspace:packages/api-opensearch": version: 0.0.0-use.local resolution: "@webiny/api-opensearch@workspace:packages/api-opensearch" @@ -11755,6 +11814,27 @@ __metadata: languageName: unknown linkType: soft +"@webiny/app-collaboration@npm:0.0.0, @webiny/app-collaboration@workspace:packages/app-collaboration": + version: 0.0.0-use.local + resolution: "@webiny/app-collaboration@workspace:packages/app-collaboration" + dependencies: + "@types/react": "npm:18.3.31" + "@webiny/admin-ui": "npm:0.0.0" + "@webiny/app": "npm:0.0.0" + "@webiny/app-admin": "npm:0.0.0" + "@webiny/app-headless-cms": "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.16.1" + mobx-react-lite: "npm:^4.1.1" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + rimraf: "npm:^6.1.3" + typescript: "npm:7.0.2" + languageName: unknown + linkType: soft + "@webiny/app-file-manager@npm:0.0.0, @webiny/app-file-manager@workspace:packages/app-file-manager": version: 0.0.0-use.local resolution: "@webiny/app-file-manager@workspace:packages/app-file-manager" @@ -11960,6 +12040,26 @@ __metadata: languageName: unknown linkType: soft +"@webiny/app-notifications@npm:0.0.0, @webiny/app-notifications@workspace:packages/app-notifications": + version: 0.0.0-use.local + resolution: "@webiny/app-notifications@workspace:packages/app-notifications" + dependencies: + "@types/react": "npm:18.3.31" + "@webiny/admin-ui": "npm:0.0.0" + "@webiny/app": "npm:0.0.0" + "@webiny/app-admin": "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.16.1" + mobx-react-lite: "npm:^4.1.1" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + rimraf: "npm:^6.1.3" + typescript: "npm:7.0.2" + languageName: unknown + linkType: soft + "@webiny/app-record-locking@npm:0.0.0, @webiny/app-record-locking@workspace:packages/app-record-locking": version: 0.0.0-use.local resolution: "@webiny/app-record-locking@workspace:packages/app-record-locking" @@ -12043,12 +12143,14 @@ __metadata: "@webiny/app-admin": "npm:0.0.0" "@webiny/app-admin-ui": "npm:0.0.0" "@webiny/app-audit-logs": "npm:0.0.0" + "@webiny/app-collaboration": "npm:0.0.0" "@webiny/app-file-manager": "npm:0.0.0" "@webiny/app-graphql-playground": "npm:0.0.0" "@webiny/app-headless-cms": "npm:0.0.0" "@webiny/app-headless-cms-scheduler": "npm:0.0.0" "@webiny/app-headless-cms-workflows": "npm:0.0.0" "@webiny/app-mailer": "npm:0.0.0" + "@webiny/app-notifications": "npm:0.0.0" "@webiny/app-record-locking": "npm:0.0.0" "@webiny/app-scheduler": "npm:0.0.0" "@webiny/app-sdk-playground": "npm:0.0.0"