From 86784bab55fb81bbdea5955056da6c2809db8b44 Mon Sep 17 00:00:00 2001 From: Jason Hartman Date: Fri, 23 May 2025 16:52:50 -0700 Subject: [PATCH 1/2] feat(client-presence): promote to beta leaving Notifications at alpha --- .changeset/fluffy-donkeys-obey.md | 9 + docs/docs/build/presence.md | 2 +- examples/apps/ai-collab/src/app/page.tsx | 2 +- examples/apps/ai-collab/src/app/presence.ts | 2 +- .../apps/presence-tracker/src/FocusTracker.ts | 4 +- .../apps/presence-tracker/src/MouseTracker.ts | 4 +- .../apps/presence-tracker/src/reactions.ts | 10 +- .../external-controller/src/app.ts | 2 +- .../external-controller/src/presence.ts | 2 +- .../external-controller/src/view.ts | 2 +- packages/framework/presence/.eslintrc.cjs | 1 + packages/framework/presence/README.md | 16 +- .../api-extractor-lint-beta.cjs.json | 5 + .../api-extractor-lint-beta.esm.json | 5 + .../presence/api-report/presence.alpha.api.md | 87 ++--- .../presence/api-report/presence.beta.api.md | 296 ++++++++++++++++++ packages/framework/presence/package.json | 12 + packages/framework/presence/src/baseTypes.ts | 2 +- .../presence/src/broadcastControls.ts | 4 +- .../src/datastorePresenceManagerFactory.ts | 2 +- .../presence/src/experimentalAccess.ts | 23 +- .../presence/src/exposedInternalTypes.ts | 2 +- packages/framework/presence/src/index.ts | 3 +- .../framework/presence/src/internalTypes.ts | 1 + .../presence/src/latestMapValueManager.ts | 16 +- .../presence/src/latestValueManager.ts | 8 +- .../presence/src/latestValueTypes.ts | 6 +- .../presence/src/notificationsManager.ts | 2 +- packages/framework/presence/src/presence.ts | 27 +- .../presence/src/presenceDatastoreManager.ts | 7 +- .../framework/presence/src/presenceManager.ts | 7 +- .../framework/presence/src/presenceStates.ts | 6 +- .../framework/presence/src/stateDatastore.ts | 6 +- .../framework/presence/src/stateFactory.ts | 2 +- .../framework/presence/src/systemWorkspace.ts | 2 +- .../src/test/latestMapValueManager.spec.ts | 4 +- .../src/test/latestValueManager.spec.ts | 4 +- .../presence/src/test/presenceStates.spec.ts | 2 +- packages/framework/presence/src/types.ts | 18 +- .../src/test/multiprocess/messageTypes.ts | 2 +- 40 files changed, 499 insertions(+), 118 deletions(-) create mode 100644 .changeset/fluffy-donkeys-obey.md create mode 100644 packages/framework/presence/api-extractor/api-extractor-lint-beta.cjs.json create mode 100644 packages/framework/presence/api-extractor/api-extractor-lint-beta.esm.json diff --git a/.changeset/fluffy-donkeys-obey.md b/.changeset/fluffy-donkeys-obey.md new file mode 100644 index 000000000000..c62a15738723 --- /dev/null +++ b/.changeset/fluffy-donkeys-obey.md @@ -0,0 +1,9 @@ +--- +"@fluidframework/presence": minor +"__section": feature +--- +Presence now available at beta support + +Importing via `@fluidframework/presence/beta` is now supported. + +Note: `Notifications` support remains only supported via `/alpha` imports. Cast `Presence` to `PresenceWithNotifications` to access notifications only workspace support. diff --git a/docs/docs/build/presence.md b/docs/docs/build/presence.md index 9a5d43692053..7758da5f161c 100644 --- a/docs/docs/build/presence.md +++ b/docs/docs/build/presence.md @@ -59,7 +59,7 @@ Notifications are special case where no data is retained during a session and al To access Presence APIs, use `getPresence()` with any `IFluidContainer`. ```typescript -import { getPresence } from "@fluidframework/presence/alpha"; +import { getPresence } from "@fluidframework/presence/beta"; function usePresence(container: IFluidContainer): void { const presence = await getPresence(container); diff --git a/examples/apps/ai-collab/src/app/page.tsx b/examples/apps/ai-collab/src/app/page.tsx index 86222c590329..52ad53f837ed 100644 --- a/examples/apps/ai-collab/src/app/page.tsx +++ b/examples/apps/ai-collab/src/app/page.tsx @@ -5,7 +5,7 @@ "use client"; -import { getPresence } from "@fluidframework/presence/alpha"; +import { getPresence } from "@fluidframework/presence/beta"; import { Box, Button, diff --git a/examples/apps/ai-collab/src/app/presence.ts b/examples/apps/ai-collab/src/app/presence.ts index ff5724492401..cf8e951c4d3c 100644 --- a/examples/apps/ai-collab/src/app/presence.ts +++ b/examples/apps/ai-collab/src/app/presence.ts @@ -9,7 +9,7 @@ import { type Attendee, type StatesWorkspace, type StatesWorkspaceSchema, -} from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; import { getProfilePhoto } from "@/infra/authHelper"; diff --git a/examples/apps/presence-tracker/src/FocusTracker.ts b/examples/apps/presence-tracker/src/FocusTracker.ts index 43f080170538..405a9829fafa 100644 --- a/examples/apps/presence-tracker/src/FocusTracker.ts +++ b/examples/apps/presence-tracker/src/FocusTracker.ts @@ -10,8 +10,8 @@ import type { LatestRaw, Presence, StatesWorkspace, -} from "@fluidframework/presence/alpha"; -import { AttendeeStatus, StateFactory } from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; +import { AttendeeStatus, StateFactory } from "@fluidframework/presence/beta"; /** * IFocusState is the data that individual session clients share via presence. diff --git a/examples/apps/presence-tracker/src/MouseTracker.ts b/examples/apps/presence-tracker/src/MouseTracker.ts index 45bc916eea01..92dc35192ed4 100644 --- a/examples/apps/presence-tracker/src/MouseTracker.ts +++ b/examples/apps/presence-tracker/src/MouseTracker.ts @@ -10,8 +10,8 @@ import type { Attendee, LatestRaw, StatesWorkspace, -} from "@fluidframework/presence/alpha"; -import { AttendeeStatus, StateFactory } from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; +import { AttendeeStatus, StateFactory } from "@fluidframework/presence/beta"; /** * IMousePosition is the data that individual session clients share via presence. diff --git a/examples/apps/presence-tracker/src/reactions.ts b/examples/apps/presence-tracker/src/reactions.ts index a730c6aa9be5..486d5ed985d3 100644 --- a/examples/apps/presence-tracker/src/reactions.ts +++ b/examples/apps/presence-tracker/src/reactions.ts @@ -4,7 +4,11 @@ */ import { Notifications } from "@fluidframework/presence/alpha"; -import type { Attendee, Presence } from "@fluidframework/presence/alpha"; +import type { + Attendee, + Presence, + PresenceWithNotifications, +} from "@fluidframework/presence/alpha"; import type { IMousePosition, MouseTracker } from "./MouseTracker.js"; @@ -17,7 +21,9 @@ export function initializeReactions(presence: Presence, mouseTracker: MouseTrack // Create a notifications workspace to send reactions-related notifications. This workspace will be created if it // doesn't exist. We also create a NotificationsManager. You can also // add presence objects to the workspace later. - const notificationsWorkspace = presence.notifications.getWorkspace( + const notificationsWorkspace = ( + presence as PresenceWithNotifications + ).notifications.getWorkspace( // A unique key identifying this workspace. "name:reactions", { diff --git a/examples/service-clients/azure-client/external-controller/src/app.ts b/examples/service-clients/azure-client/external-controller/src/app.ts index e344a427f55e..c7ecc1c70717 100644 --- a/examples/service-clients/azure-client/external-controller/src/app.ts +++ b/examples/service-clients/azure-client/external-controller/src/app.ts @@ -11,7 +11,7 @@ import { } from "@fluidframework/azure-client"; import { createDevtoolsLogger, initializeDevtools } from "@fluidframework/devtools/beta"; import { ISharedMap, IValueChanged, SharedMap } from "@fluidframework/map/legacy"; -import { getPresence } from "@fluidframework/presence/alpha"; +import { getPresence } from "@fluidframework/presence/beta"; import { createChildLogger } from "@fluidframework/telemetry-utils/legacy"; // eslint-disable-next-line import/no-internal-modules -- #26985: `test-runtime-utils` internal used in example import { InsecureTokenProvider } from "@fluidframework/test-runtime-utils/internal"; diff --git a/examples/service-clients/azure-client/external-controller/src/presence.ts b/examples/service-clients/azure-client/external-controller/src/presence.ts index 2b9d8790e28b..b3be8c01703d 100644 --- a/examples/service-clients/azure-client/external-controller/src/presence.ts +++ b/examples/service-clients/azure-client/external-controller/src/presence.ts @@ -8,7 +8,7 @@ import { StateFactory, type StatesWorkspace, type StatesWorkspaceSchema, -} from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; import type { DieValue } from "./controller.js"; diff --git a/examples/service-clients/azure-client/external-controller/src/view.ts b/examples/service-clients/azure-client/external-controller/src/view.ts index 26a74da964ee..80f307e1ea90 100644 --- a/examples/service-clients/azure-client/external-controller/src/view.ts +++ b/examples/service-clients/azure-client/external-controller/src/view.ts @@ -4,7 +4,7 @@ */ import { AzureMember, IAzureAudience } from "@fluidframework/azure-client"; -import type { LatestRaw, Presence } from "@fluidframework/presence/alpha"; +import type { LatestRaw, Presence } from "@fluidframework/presence/beta"; import { ICustomUserDetails } from "./app.js"; import { IDiceRollerController } from "./controller.js"; diff --git a/packages/framework/presence/.eslintrc.cjs b/packages/framework/presence/.eslintrc.cjs index ed6601a70c63..8ba9b4c89ef8 100644 --- a/packages/framework/presence/.eslintrc.cjs +++ b/packages/framework/presence/.eslintrc.cjs @@ -22,6 +22,7 @@ module.exports = { "@fluidframework/*/internal{,/**}", "*/index.js", "@fluidframework/presence/alpha", + "@fluidframework/presence/beta", ], }, ], diff --git a/packages/framework/presence/README.md b/packages/framework/presence/README.md index ad26ad08ba30..15c0cd09757a 100644 --- a/packages/framework/presence/README.md +++ b/packages/framework/presence/README.md @@ -31,6 +31,8 @@ For more information on the related support guarantees, see [API Support Levels] To access the `public` ([SemVer](https://semver.org/)) APIs, import via `@fluidframework/presence` like normal. +To access the `beta` APIs, import via `@fluidframework/presence/beta`. + To access the `alpha` APIs, import via `@fluidframework/presence/alpha`. ## API Documentation @@ -80,21 +82,15 @@ Notifications are special case where no data is retained during a session and al ## Onboarding -While this package is developing and other Fluid Framework internals are being updated to accommodate it, a temporary Shared Object must be added within container to gain access. +To access Presence APIs, use `getPresence()` with any `IFluidContainer`. ```typescript -import { getPresenceViaDataObject, ExperimentalPresenceManager } from "@fluidframework/presence/alpha"; - -const containerSchema = { - initialObjects: { - presence: ExperimentalPresenceManager - } -} satisfies ContainerSchema; +import { getPresence } from "@fluidframework/presence/beta"; -const presence = await getPresenceViaDataObject(container.initialObjects.presence); +function usePresence(container: IFluidContainer): void { + const presence = await getPresence(container); ``` - ## Limitations ### States Reliability diff --git a/packages/framework/presence/api-extractor/api-extractor-lint-beta.cjs.json b/packages/framework/presence/api-extractor/api-extractor-lint-beta.cjs.json new file mode 100644 index 000000000000..7af093bb825b --- /dev/null +++ b/packages/framework/presence/api-extractor/api-extractor-lint-beta.cjs.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "/../../../common/build/build-common/api-extractor-lint.entrypoint.json", + "mainEntryPointFilePath": "/dist/beta.d.ts" +} diff --git a/packages/framework/presence/api-extractor/api-extractor-lint-beta.esm.json b/packages/framework/presence/api-extractor/api-extractor-lint-beta.esm.json new file mode 100644 index 000000000000..82773789eaee --- /dev/null +++ b/packages/framework/presence/api-extractor/api-extractor-lint-beta.esm.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "/../../../common/build/build-common/api-extractor-lint.entrypoint.json", + "mainEntryPointFilePath": "/lib/beta.d.ts" +} diff --git a/packages/framework/presence/api-report/presence.alpha.api.md b/packages/framework/presence/api-report/presence.alpha.api.md index a4dafc4900fe..116ba0484450 100644 --- a/packages/framework/presence/api-report/presence.alpha.api.md +++ b/packages/framework/presence/api-report/presence.alpha.api.md @@ -4,19 +4,19 @@ ```ts -// @alpha @sealed +// @beta @sealed export interface Attendee { readonly attendeeId: SpecificAttendeeId; getConnectionId(): ClientConnectionId; getConnectionStatus(): AttendeeStatus; } -// @alpha +// @beta export type AttendeeId = SessionId & { readonly AttendeeId: "AttendeeId"; }; -// @alpha @sealed (undocumented) +// @beta @sealed (undocumented) export interface AttendeesEvents { // @eventProperty attendeeConnected: (attendee: Attendee) => void; @@ -24,26 +24,26 @@ export interface AttendeesEvents { attendeeDisconnected: (attendee: Attendee) => void; } -// @alpha +// @beta export const AttendeeStatus: { readonly Connected: "Connected"; readonly Disconnected: "Disconnected"; }; -// @alpha +// @beta export type AttendeeStatus = (typeof AttendeeStatus)[keyof typeof AttendeeStatus]; -// @alpha @sealed +// @beta @sealed export interface BroadcastControls { allowableUpdateLatencyMs: number | undefined; } -// @alpha +// @beta export interface BroadcastControlSettings { readonly allowableUpdateLatencyMs?: number; } -// @alpha +// @beta export type ClientConnectionId = string; // @alpha @sealed @deprecated @@ -53,13 +53,16 @@ export class ExperimentalPresenceDO { // @alpha @deprecated export const ExperimentalPresenceManager: SharedObjectKind; +// @beta +export const getPresence: (fluidContainer: IFluidContainer) => Presence; + // @alpha -export function getPresence(fluidContainer: IFluidContainer): Presence; +export function getPresenceAlpha(fluidContainer: IFluidContainer): PresenceWithNotifications; // @alpha @deprecated -export function getPresenceViaDataObject(fluidLoadable: ExperimentalPresenceDO): Presence; +export function getPresenceViaDataObject(fluidLoadable: ExperimentalPresenceDO): PresenceWithNotifications; -// @alpha @system +// @beta @system export namespace InternalTypes { // @system export type ManagerFactory, TManager> = { @@ -139,22 +142,22 @@ export namespace InternalUtilityTypes { }; } -// @alpha +// @beta export function latest(args: LatestArguments): InternalTypes.ManagerFactory, LatestRaw>; -// @alpha +// @beta export interface LatestArguments { local: JsonSerializable & JsonDeserialized & (object | null); settings?: BroadcastControlSettings | undefined; } -// @alpha @sealed +// @beta @sealed export interface LatestClientData extends LatestData { // (undocumented) attendee: Attendee; } -// @alpha @sealed +// @beta @sealed export interface LatestData { // (undocumented) metadata: LatestMetadata; @@ -162,10 +165,10 @@ export interface LatestData { value: DeepReadonly>; } -// @alpha +// @beta export function latestMap(args?: LatestMapArguments): InternalTypes.ManagerFactory, LatestMapRaw>; -// @alpha +// @beta export interface LatestMapArguments { local?: { [K in Keys]: JsonSerializable & JsonDeserialized; @@ -173,14 +176,14 @@ export interface LatestMapArguments { attendee: Attendee; // (undocumented) items: ReadonlyMap>; } -// @alpha @sealed +// @beta @sealed export interface LatestMapItemRemovedClientData { // (undocumented) attendee: Attendee; @@ -190,13 +193,13 @@ export interface LatestMapItemRemovedClientData { metadata: LatestMetadata; } -// @alpha @sealed +// @beta @sealed export interface LatestMapItemUpdatedClientData extends LatestClientData { // (undocumented) key: K; } -// @alpha @sealed +// @beta @sealed export interface LatestMapRaw { readonly controls: BroadcastControls; readonly events: Listenable>; @@ -207,7 +210,7 @@ export interface LatestMapRaw readonly presence: Presence; } -// @alpha @sealed (undocumented) +// @beta @sealed (undocumented) export interface LatestMapRawEvents { // @eventProperty localItemRemoved: (removedItem: { @@ -226,13 +229,13 @@ export interface LatestMapRawEvents { remoteUpdated: (updates: LatestMapClientData) => void; } -// @alpha @sealed +// @beta @sealed export interface LatestMetadata { revision: number; timestamp: number; } -// @alpha @sealed +// @beta @sealed export interface LatestRaw { readonly controls: BroadcastControls; readonly events: Listenable>; @@ -244,7 +247,7 @@ export interface LatestRaw { readonly presence: Presence; } -// @alpha @sealed (undocumented) +// @beta @sealed (undocumented) export interface LatestRawEvents { // @eventProperty localUpdated: (update: { @@ -274,7 +277,7 @@ export interface NotificationsManager; readonly events: Listenable; readonly notifications: NotificationListenable; - readonly presence: Presence; + readonly presence: PresenceWithNotifications; } // @alpha @sealed (undocumented) @@ -292,7 +295,7 @@ export type NotificationSubscriptions { add, TManager extends NotificationsManager>(key: TKey, manager: InternalTypes.ManagerFactory): asserts this is NotificationsWorkspace>>; readonly notifications: StatesWorkspaceEntries; - readonly presence: Presence; + readonly presence: PresenceWithNotifications; } // @alpha @@ -301,7 +304,7 @@ export interface NotificationsWorkspaceSchema { [key: string]: InternalTypes.ManagerFactory, NotificationsManager>; } -// @alpha @sealed +// @beta @sealed export interface Presence { // (undocumented) readonly attendees: { @@ -312,27 +315,31 @@ export interface Presence { }; readonly events: Listenable; // (undocumented) - readonly notifications: { - getWorkspace(notificationsId: WorkspaceAddress, requestedNotifications: NotificationsSchema): NotificationsWorkspace; - }; - // (undocumented) readonly states: { getWorkspace(workspaceAddress: WorkspaceAddress, requestedStates: StatesSchema, controls?: BroadcastControlSettings): StatesWorkspace; }; } -// @alpha @sealed (undocumented) +// @beta @sealed (undocumented) export interface PresenceEvents { workspaceActivated: (workspaceAddress: WorkspaceAddress, type: "States" | "Notifications" | "Unknown") => void; } -// @alpha +// @alpha @sealed +export interface PresenceWithNotifications extends Presence { + // (undocumented) + readonly notifications: { + getWorkspace(notificationsId: WorkspaceAddress, requestedNotifications: NotificationsSchema): NotificationsWorkspace; + }; +} + +// @beta export const StateFactory: { latest: typeof latest; latestMap: typeof latestMap; }; -// @alpha @sealed +// @beta @sealed export interface StateMap { clear(): void; // (undocumented) @@ -347,7 +354,7 @@ export interface StateMap { readonly size: number; } -// @alpha @sealed +// @beta @sealed export interface StatesWorkspace { add, TManager extends TManagerConstraints>(key: TKey, manager: InternalTypes.ManagerFactory): asserts this is StatesWorkspace>, TManagerConstraints>; readonly controls: BroadcastControls; @@ -355,7 +362,7 @@ export interface StatesWorkspace; } -// @alpha @sealed +// @beta @sealed export type StatesWorkspaceEntries = { /** * Registered State objects. @@ -363,16 +370,16 @@ export type StatesWorkspaceEntries = { readonly [Key in keyof TSchema]: ReturnType["manager"] extends InternalTypes.StateValue ? TManager : never; }; -// @alpha +// @beta export type StatesWorkspaceEntry, TManager = unknown> = InternalTypes.ManagerFactory; -// @alpha +// @beta export interface StatesWorkspaceSchema { // (undocumented) [key: string]: StatesWorkspaceEntry>; } -// @alpha +// @beta export type WorkspaceAddress = `${string}:${string}`; ``` diff --git a/packages/framework/presence/api-report/presence.beta.api.md b/packages/framework/presence/api-report/presence.beta.api.md index 382a8836fac8..c68db527364d 100644 --- a/packages/framework/presence/api-report/presence.beta.api.md +++ b/packages/framework/presence/api-report/presence.beta.api.md @@ -4,4 +4,300 @@ ```ts +// @beta @sealed +export interface Attendee { + readonly attendeeId: SpecificAttendeeId; + getConnectionId(): ClientConnectionId; + getConnectionStatus(): AttendeeStatus; +} + +// @beta +export type AttendeeId = SessionId & { + readonly AttendeeId: "AttendeeId"; +}; + +// @beta @sealed (undocumented) +export interface AttendeesEvents { + // @eventProperty + attendeeConnected: (attendee: Attendee) => void; + // @eventProperty + attendeeDisconnected: (attendee: Attendee) => void; +} + +// @beta +export const AttendeeStatus: { + readonly Connected: "Connected"; + readonly Disconnected: "Disconnected"; +}; + +// @beta +export type AttendeeStatus = (typeof AttendeeStatus)[keyof typeof AttendeeStatus]; + +// @beta @sealed +export interface BroadcastControls { + allowableUpdateLatencyMs: number | undefined; +} + +// @beta +export interface BroadcastControlSettings { + readonly allowableUpdateLatencyMs?: number; +} + +// @beta +export type ClientConnectionId = string; + +// @beta +export const getPresence: (fluidContainer: IFluidContainer) => Presence; + +// @beta @system +export namespace InternalTypes { + // @system + export type ManagerFactory, TManager> = { + instanceBase: new (...args: any[]) => any; + } & ((key: TKey, datastoreHandle: StateDatastoreHandle) => { + initialData?: { + value: TValue; + allowableUpdateLatencyMs: number | undefined; + }; + manager: StateValue; + }); + // @system (undocumented) + export interface MapValueState { + // (undocumented) + items: { + [name in Keys]: ValueOptionalState; + }; + // (undocumented) + rev: number; + } + // @system (undocumented) + export interface NotificationType { + // (undocumented) + args: (JsonSerializable & JsonDeserialized)[]; + // (undocumented) + name: string; + } + // @system (undocumented) + export class StateDatastoreHandle> { + } + // @system + export type StateValue = T & StateValueBrand; + // @system + export class StateValueBrand { + } + // @system (undocumented) + export interface ValueDirectory { + // (undocumented) + items: { + [name: string | number]: ValueOptionalState | ValueDirectory; + }; + // (undocumented) + rev: number; + } + // @system (undocumented) + export type ValueDirectoryOrState = ValueRequiredState | ValueDirectory; + // @system (undocumented) + export interface ValueOptionalState extends ValueStateMetadata { + // (undocumented) + value?: JsonDeserialized; + } + // @system (undocumented) + export interface ValueRequiredState extends ValueStateMetadata { + // (undocumented) + value: JsonDeserialized; + } + // @system (undocumented) + export interface ValueStateMetadata { + // (undocumented) + rev: number; + // (undocumented) + timestamp: number; + } +} + +// @beta +export function latest(args: LatestArguments): InternalTypes.ManagerFactory, LatestRaw>; + +// @beta +export interface LatestArguments { + local: JsonSerializable & JsonDeserialized & (object | null); + settings?: BroadcastControlSettings | undefined; +} + +// @beta @sealed +export interface LatestClientData extends LatestData { + // (undocumented) + attendee: Attendee; +} + +// @beta @sealed +export interface LatestData { + // (undocumented) + metadata: LatestMetadata; + // (undocumented) + value: DeepReadonly>; +} + +// @beta +export function latestMap(args?: LatestMapArguments): InternalTypes.ManagerFactory, LatestMapRaw>; + +// @beta +export interface LatestMapArguments { + local?: { + [K in Keys]: JsonSerializable & JsonDeserialized; + }; + settings?: BroadcastControlSettings | undefined; +} + +// @beta @sealed +export interface LatestMapClientData { + attendee: Attendee; + // (undocumented) + items: ReadonlyMap>; +} + +// @beta @sealed +export interface LatestMapItemRemovedClientData { + // (undocumented) + attendee: Attendee; + // (undocumented) + key: K; + // (undocumented) + metadata: LatestMetadata; +} + +// @beta @sealed +export interface LatestMapItemUpdatedClientData extends LatestClientData { + // (undocumented) + key: K; +} + +// @beta @sealed +export interface LatestMapRaw { + readonly controls: BroadcastControls; + readonly events: Listenable>; + getRemote(attendee: Attendee): ReadonlyMap>; + getRemotes(): IterableIterator>; + getStateAttendees(): Attendee[]; + readonly local: StateMap; + readonly presence: Presence; +} + +// @beta @sealed (undocumented) +export interface LatestMapRawEvents { + // @eventProperty + localItemRemoved: (removedItem: { + key: K; + }) => void; + // @eventProperty + localItemUpdated: (updatedItem: { + value: DeepReadonly & JsonDeserialized>; + key: K; + }) => void; + // @eventProperty + remoteItemRemoved: (removedItem: LatestMapItemRemovedClientData) => void; + // @eventProperty + remoteItemUpdated: (updatedItem: LatestMapItemUpdatedClientData) => void; + // @eventProperty + remoteUpdated: (updates: LatestMapClientData) => void; +} + +// @beta @sealed +export interface LatestMetadata { + revision: number; + timestamp: number; +} + +// @beta @sealed +export interface LatestRaw { + readonly controls: BroadcastControls; + readonly events: Listenable>; + getRemote(attendee: Attendee): LatestData; + getRemotes(): IterableIterator>; + getStateAttendees(): Attendee[]; + get local(): DeepReadonly>; + set local(value: JsonSerializable & JsonDeserialized); + readonly presence: Presence; +} + +// @beta @sealed (undocumented) +export interface LatestRawEvents { + // @eventProperty + localUpdated: (update: { + value: DeepReadonly & JsonDeserialized>; + }) => void; + // @eventProperty + remoteUpdated: (update: LatestClientData) => void; +} + +// @beta @sealed +export interface Presence { + // (undocumented) + readonly attendees: { + readonly events: Listenable; + getAttendees(): ReadonlySet; + getAttendee(clientId: ClientConnectionId | AttendeeId): Attendee; + getMyself(): Attendee; + }; + readonly events: Listenable; + // (undocumented) + readonly states: { + getWorkspace(workspaceAddress: WorkspaceAddress, requestedStates: StatesSchema, controls?: BroadcastControlSettings): StatesWorkspace; + }; +} + +// @beta @sealed (undocumented) +export interface PresenceEvents { + workspaceActivated: (workspaceAddress: WorkspaceAddress, type: "States" | "Notifications" | "Unknown") => void; +} + +// @beta +export const StateFactory: { + latest: typeof latest; + latestMap: typeof latestMap; +}; + +// @beta @sealed +export interface StateMap { + clear(): void; + // (undocumented) + delete(key: K): boolean; + forEach(callbackfn: (value: DeepReadonly>, key: K, map: StateMap) => void, thisArg?: unknown): void; + get(key: K): DeepReadonly> | undefined; + // (undocumented) + has(key: K): boolean; + keys(): IterableIterator; + set(key: K, value: JsonSerializable & JsonDeserialized): this; + // (undocumented) + readonly size: number; +} + +// @beta @sealed +export interface StatesWorkspace { + add, TManager extends TManagerConstraints>(key: TKey, manager: InternalTypes.ManagerFactory): asserts this is StatesWorkspace>, TManagerConstraints>; + readonly controls: BroadcastControls; + readonly presence: Presence; + readonly states: StatesWorkspaceEntries; +} + +// @beta @sealed +export type StatesWorkspaceEntries = { + /** + * Registered State objects. + */ + readonly [Key in keyof TSchema]: ReturnType["manager"] extends InternalTypes.StateValue ? TManager : never; +}; + +// @beta +export type StatesWorkspaceEntry, TManager = unknown> = InternalTypes.ManagerFactory; + +// @beta +export interface StatesWorkspaceSchema { + // (undocumented) + [key: string]: StatesWorkspaceEntry>; +} + +// @beta +export type WorkspaceAddress = `${string}:${string}`; + ``` diff --git a/packages/framework/presence/package.json b/packages/framework/presence/package.json index 4f16256d19e5..228a0bd9f262 100644 --- a/packages/framework/presence/package.json +++ b/packages/framework/presence/package.json @@ -13,6 +13,16 @@ "sideEffects": false, "type": "module", "exports": { + "./beta": { + "import": { + "types": "./lib/beta.d.ts", + "default": "./lib/index.js" + }, + "require": { + "types": "./dist/beta.d.ts", + "default": "./dist/index.js" + } + }, "./alpha": { "import": { "types": "./lib/alpha.d.ts", @@ -49,7 +59,9 @@ "check:exports": "concurrently \"npm:check:exports:*\"", "check:exports:bundle-release-tags": "api-extractor run --config api-extractor/api-extractor-lint-bundle.json", "check:exports:cjs:alpha": "api-extractor run --config api-extractor/api-extractor-lint-alpha.cjs.json", + "check:exports:cjs:beta": "api-extractor run --config api-extractor/api-extractor-lint-beta.cjs.json", "check:exports:esm:alpha": "api-extractor run --config api-extractor/api-extractor-lint-alpha.esm.json", + "check:exports:esm:beta": "api-extractor run --config api-extractor/api-extractor-lint-beta.esm.json", "check:format": "npm run check:biome", "ci:build:docs": "api-extractor run", "clean": "rimraf --glob dist lib \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc", diff --git a/packages/framework/presence/src/baseTypes.ts b/packages/framework/presence/src/baseTypes.ts index 7e1972decbe3..21bba5af732d 100644 --- a/packages/framework/presence/src/baseTypes.ts +++ b/packages/framework/presence/src/baseTypes.ts @@ -19,6 +19,6 @@ * strings. Branding broadly is likely a breaking change and may take decent * effort to manage. * - * @alpha + * @beta */ export type ClientConnectionId = string; diff --git a/packages/framework/presence/src/broadcastControls.ts b/packages/framework/presence/src/broadcastControls.ts index aec305addab3..5caf41681b39 100644 --- a/packages/framework/presence/src/broadcastControls.ts +++ b/packages/framework/presence/src/broadcastControls.ts @@ -7,7 +7,7 @@ * Common controls for States objects. * * @sealed - * @alpha + * @beta */ export interface BroadcastControls { /** @@ -40,7 +40,7 @@ export interface BroadcastControls { /** * Value set to configure {@link BroadcastControls}. * - * @alpha + * @beta */ export interface BroadcastControlSettings { /** diff --git a/packages/framework/presence/src/datastorePresenceManagerFactory.ts b/packages/framework/presence/src/datastorePresenceManagerFactory.ts index 31043475c5d3..dcce018a5d54 100644 --- a/packages/framework/presence/src/datastorePresenceManagerFactory.ts +++ b/packages/framework/presence/src/datastorePresenceManagerFactory.ts @@ -18,7 +18,7 @@ import type { IInboundSignalMessage } from "@fluidframework/runtime-definitions/ import type { SharedObjectKind } from "@fluidframework/shared-object-base"; import { BasicDataStoreFactory, LoadableFluidObject } from "./datastoreSupport.js"; -import type { Presence } from "./presence.js"; +import type { PresenceWithNotifications as Presence } from "./presence.js"; import { createPresenceManager } from "./presenceManager.js"; import type { OutboundClientJoinMessage, diff --git a/packages/framework/presence/src/experimentalAccess.ts b/packages/framework/presence/src/experimentalAccess.ts index 48009ea1b9c4..1d85995c1ae9 100644 --- a/packages/framework/presence/src/experimentalAccess.ts +++ b/packages/framework/presence/src/experimentalAccess.ts @@ -13,7 +13,7 @@ import type { IFluidContainer } from "@fluidframework/fluid-static"; import { isInternalFluidContainer } from "@fluidframework/fluid-static/internal"; import type { ExtensionHost, ExtensionRuntimeProperties } from "./internalTypes.js"; -import type { Presence } from "./presence.js"; +import type { Presence, PresenceWithNotifications } from "./presence.js"; import type { PresenceExtensionInterface } from "./presenceManager.js"; import { createPresenceManager } from "./presenceManager.js"; import type { SignalMessages } from "./protocol.js"; @@ -24,10 +24,12 @@ import type { SignalMessages } from "./protocol.js"; class ContainerPresenceManager implements ContainerExtension, - InstanceType> + InstanceType< + ContainerExtensionFactory + > { // ContainerExtensionFactory return elements - public readonly interface: Presence; + public readonly interface: PresenceWithNotifications; public readonly extension = this; private readonly manager: PresenceExtensionInterface; @@ -57,13 +59,22 @@ class ContainerPresenceManager } /** - * Acquire an Presence from a Fluid Container + * Acquire a {@link Presence} from a Fluid Container * @param fluidContainer - Fluid Container to acquire the map from - * @returns the Presence + * @returns the {@link Presence} + * + * @beta + */ +export const getPresence: (fluidContainer: IFluidContainer) => Presence = getPresenceAlpha; + +/** + * Acquire a {@link PresenceWithNotifications} from a Fluid Container + * @param fluidContainer - Fluid Container to acquire the map from + * @returns the {@link PresenceWithNotifications} * * @alpha */ -export function getPresence(fluidContainer: IFluidContainer): Presence { +export function getPresenceAlpha(fluidContainer: IFluidContainer): PresenceWithNotifications { assert( isInternalFluidContainer(fluidContainer), 0xa2f /* IFluidContainer was not recognized. Only Containers generated by the Fluid Framework are supported. */, diff --git a/packages/framework/presence/src/exposedInternalTypes.ts b/packages/framework/presence/src/exposedInternalTypes.ts index 6d86d9b2f71f..46e457098129 100644 --- a/packages/framework/presence/src/exposedInternalTypes.ts +++ b/packages/framework/presence/src/exposedInternalTypes.ts @@ -12,7 +12,7 @@ import type { * Collection of value types that are not intended to be used/imported * directly outside of this package. * - * @alpha + * @beta * @system */ // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/packages/framework/presence/src/index.ts b/packages/framework/presence/src/index.ts index 182ad758b1c9..86f73569af5c 100644 --- a/packages/framework/presence/src/index.ts +++ b/packages/framework/presence/src/index.ts @@ -30,6 +30,7 @@ export { AttendeeStatus, type Presence, type PresenceEvents, + type PresenceWithNotifications, } from "./presence.js"; export type { @@ -37,7 +38,7 @@ export type { BroadcastControlSettings, } from "./broadcastControls.js"; -export { getPresence } from "./experimentalAccess.js"; +export { getPresence, getPresenceAlpha } from "./experimentalAccess.js"; export { getPresenceViaDataObject, diff --git a/packages/framework/presence/src/internalTypes.ts b/packages/framework/presence/src/internalTypes.ts index 57b59019c043..1aabba9d66b1 100644 --- a/packages/framework/presence/src/internalTypes.ts +++ b/packages/framework/presence/src/internalTypes.ts @@ -14,6 +14,7 @@ import type { } from "./protocol.js"; /** + * Presence {@link ContainerExtension} version of {@link @fluidframework/container-runtime-definitions#ExtensionRuntimeProperties} * @internal */ export interface ExtensionRuntimeProperties { diff --git a/packages/framework/presence/src/latestMapValueManager.ts b/packages/framework/presence/src/latestMapValueManager.ts index b59fe47b7f63..17e17bf6b8f6 100644 --- a/packages/framework/presence/src/latestMapValueManager.ts +++ b/packages/framework/presence/src/latestMapValueManager.ts @@ -26,7 +26,7 @@ import { brandIVM } from "./valueManager.js"; * Collection of latest known values for a specific client. * * @sealed - * @alpha + * @beta */ export interface LatestMapClientData< T, @@ -49,7 +49,7 @@ export interface LatestMapClientData< * State of a single item value, its key, and its metadata. * * @sealed - * @alpha + * @beta */ export interface LatestMapItemUpdatedClientData extends LatestClientData { @@ -60,7 +60,7 @@ export interface LatestMapItemUpdatedClientData * Identifier and metadata for a removed item. * * @sealed - * @alpha + * @beta */ export interface LatestMapItemRemovedClientData { attendee: Attendee; @@ -70,7 +70,7 @@ export interface LatestMapItemRemovedClientData { /** * @sealed - * @alpha + * @beta */ export interface LatestMapRawEvents { /** @@ -125,7 +125,7 @@ export interface LatestMapRawEvents { * Map of local client's values. Modifications are transmitted to all other connected clients. * * @sealed - * @alpha + * @beta */ export interface StateMap { /** @@ -308,7 +308,7 @@ class ValueMapImpl implements StateMap { * @remarks Create using {@link StateFactory.latestMap} registered to {@link StatesWorkspace}. * * @sealed - * @alpha + * @beta */ export interface LatestMapRaw { /** @@ -492,7 +492,7 @@ class LatestMapRawValueManagerImpl< /** * Arguments that are passed to the {@link StateFactory.latestMap} function. * - * @alpha + * @beta */ export interface LatestMapArguments { /** @@ -511,7 +511,7 @@ export interface LatestMapArguments { /** @@ -51,7 +51,7 @@ export interface LatestRawEvents { * @remarks Create using {@link StateFactory.latest} registered to {@link StatesWorkspace}. * * @sealed - * @alpha + * @beta */ export interface LatestRaw { /** @@ -184,7 +184,7 @@ class LatestValueManagerImpl /** * Arguments that are passed to the {@link StateFactory.latest} function. * - * @alpha + * @beta */ export interface LatestArguments { /** @@ -202,7 +202,7 @@ export interface LatestArguments { /** * Factory for creating a {@link LatestRaw} State object. * - * @alpha + * @beta */ export function latest( args: LatestArguments, diff --git a/packages/framework/presence/src/latestValueTypes.ts b/packages/framework/presence/src/latestValueTypes.ts index af287fe79dfe..6b58d0c9f611 100644 --- a/packages/framework/presence/src/latestValueTypes.ts +++ b/packages/framework/presence/src/latestValueTypes.ts @@ -14,7 +14,7 @@ import type { Attendee } from "./presence.js"; * Metadata for the value state. * * @sealed - * @alpha + * @beta */ export interface LatestMetadata { /** @@ -32,7 +32,7 @@ export interface LatestMetadata { * State of a value and its metadata. * * @sealed - * @alpha + * @beta */ export interface LatestData { value: DeepReadonly>; @@ -43,7 +43,7 @@ export interface LatestData { * State of a specific attendee's value and its metadata. * * @sealed - * @alpha + * @beta */ export interface LatestClientData extends LatestData { attendee: Attendee; diff --git a/packages/framework/presence/src/notificationsManager.ts b/packages/framework/presence/src/notificationsManager.ts index b734e9165fd4..12857b260599 100644 --- a/packages/framework/presence/src/notificationsManager.ts +++ b/packages/framework/presence/src/notificationsManager.ts @@ -10,7 +10,7 @@ import type { JsonTypeWith } from "@fluidframework/core-interfaces/internal"; import type { InternalTypes } from "./exposedInternalTypes.js"; import type { InternalUtilityTypes } from "./exposedUtilityTypes.js"; import type { PostUpdateAction, ValueManager } from "./internalTypes.js"; -import type { Attendee, Presence } from "./presence.js"; +import type { Attendee, PresenceWithNotifications as Presence } from "./presence.js"; import { datastoreFromHandle, type StateDatastore } from "./stateDatastore.js"; import { brandIVM } from "./valueManager.js"; diff --git a/packages/framework/presence/src/presence.ts b/packages/framework/presence/src/presence.ts index 8fa06b72b953..f47412d078a3 100644 --- a/packages/framework/presence/src/presence.ts +++ b/packages/framework/presence/src/presence.ts @@ -26,14 +26,14 @@ import type { * identify clients in a session. {@link Attendee.attendeeId} will provide * the session ID. * - * @alpha + * @beta */ export type AttendeeId = SessionId & { readonly AttendeeId: "AttendeeId" }; /** * The connection status of the {@link Attendee}. * - * @alpha + * @beta */ export const AttendeeStatus = { /** @@ -57,7 +57,7 @@ export const AttendeeStatus = { * - State changes are kept locally and communicated to others upon reconnect. * - Notification requests are discarded (silently). * - * @alpha + * @beta */ export type AttendeeStatus = (typeof AttendeeStatus)[keyof typeof AttendeeStatus]; @@ -77,7 +77,7 @@ export type AttendeeStatus = (typeof AttendeeStatus)[keyof typeof AttendeeStatus * Audience, and Quorum representations of clients and users. * * @sealed - * @alpha + * @beta */ export interface Attendee { /** @@ -117,7 +117,7 @@ export type SpecificAttendee = /** * @sealed - * @alpha + * @beta */ export interface AttendeesEvents { /** @@ -137,7 +137,7 @@ export interface AttendeesEvents { /** * @sealed - * @alpha + * @beta */ export interface PresenceEvents { /** @@ -159,10 +159,10 @@ export interface PresenceEvents { } /** - * Presence represents known clients within a session and their custom states and notifications. + * Presence represents known clients within a session and their custom states. * * @sealed - * @alpha + * @beta */ export interface Presence { /** @@ -215,7 +215,18 @@ export interface Presence { controls?: BroadcastControlSettings, ): StatesWorkspace; }; +} +/** + * Presence represents known clients within a session and their custom states and notifications. + * + * @remarks + * To access this alpha API, cast any `{@link Presence}` to `PresenceWithNotifications`. + * + * @sealed + * @alpha + */ +export interface PresenceWithNotifications extends Presence { readonly notifications: { /** * Acquires a Notifications workspace from store or adds new one. diff --git a/packages/framework/presence/src/presenceDatastoreManager.ts b/packages/framework/presence/src/presenceDatastoreManager.ts index 26291e92c8c6..bf566a404b01 100644 --- a/packages/framework/presence/src/presenceDatastoreManager.ts +++ b/packages/framework/presence/src/presenceDatastoreManager.ts @@ -12,7 +12,12 @@ import type { ClientConnectionId } from "./baseTypes.js"; import type { BroadcastControlSettings } from "./broadcastControls.js"; import type { IEphemeralRuntime, PostUpdateAction } from "./internalTypes.js"; import { objectEntries } from "./internalUtils.js"; -import type { AttendeeId, Attendee, Presence, PresenceEvents } from "./presence.js"; +import type { + AttendeeId, + Attendee, + PresenceWithNotifications as Presence, + PresenceEvents, +} from "./presence.js"; import type { ClientUpdateEntry, RuntimeLocalUpdateOptions, diff --git a/packages/framework/presence/src/presenceManager.ts b/packages/framework/presence/src/presenceManager.ts index 3c4f105f923e..b2b94d8ebaec 100644 --- a/packages/framework/presence/src/presenceManager.ts +++ b/packages/framework/presence/src/presenceManager.ts @@ -19,7 +19,12 @@ import { createChildMonitoringContext } from "@fluidframework/telemetry-utils/in import type { ClientConnectionId } from "./baseTypes.js"; import type { BroadcastControlSettings } from "./broadcastControls.js"; import type { ExtensionRuntimeProperties, IEphemeralRuntime } from "./internalTypes.js"; -import type { AttendeesEvents, AttendeeId, Presence, PresenceEvents } from "./presence.js"; +import type { + AttendeesEvents, + AttendeeId, + PresenceWithNotifications as Presence, + PresenceEvents, +} from "./presence.js"; import type { PresenceDatastoreManager } from "./presenceDatastoreManager.js"; import { PresenceDatastoreManagerImpl } from "./presenceDatastoreManager.js"; import type { SignalMessages } from "./protocol.js"; diff --git a/packages/framework/presence/src/presenceStates.ts b/packages/framework/presence/src/presenceStates.ts index b058e531ef2d..7c4f3741d011 100644 --- a/packages/framework/presence/src/presenceStates.ts +++ b/packages/framework/presence/src/presenceStates.ts @@ -12,7 +12,11 @@ import type { InternalTypes } from "./exposedInternalTypes.js"; import type { ClientRecord, PostUpdateAction } from "./internalTypes.js"; import type { RecordEntryTypes } from "./internalUtils.js"; import { getOrCreateRecord, objectEntries } from "./internalUtils.js"; -import type { AttendeeId, Attendee, Presence } from "./presence.js"; +import type { + AttendeeId, + Attendee, + PresenceWithNotifications as Presence, +} from "./presence.js"; import type { LocalStateUpdateOptions, StateDatastore } from "./stateDatastore.js"; import { handleFromDatastore } from "./stateDatastore.js"; import type { AnyWorkspace, StatesWorkspace, StatesWorkspaceSchema } from "./types.js"; diff --git a/packages/framework/presence/src/stateDatastore.ts b/packages/framework/presence/src/stateDatastore.ts index ef971a8ab62e..96ac4e23bd66 100644 --- a/packages/framework/presence/src/stateDatastore.ts +++ b/packages/framework/presence/src/stateDatastore.ts @@ -6,7 +6,11 @@ import type { ClientConnectionId } from "./baseTypes.js"; import type { InternalTypes } from "./exposedInternalTypes.js"; import type { ClientRecord } from "./internalTypes.js"; -import type { Attendee, AttendeeId, Presence } from "./presence.js"; +import type { + Attendee, + AttendeeId, + PresenceWithNotifications as Presence, +} from "./presence.js"; // type StateDatastoreSchemaNode< // TValue extends InternalTypes.ValueDirectoryOrState = InternalTypes.ValueDirectoryOrState, diff --git a/packages/framework/presence/src/stateFactory.ts b/packages/framework/presence/src/stateFactory.ts index bb2d1101178d..7f58eca808e5 100644 --- a/packages/framework/presence/src/stateFactory.ts +++ b/packages/framework/presence/src/stateFactory.ts @@ -9,7 +9,7 @@ import { latest } from "./latestValueManager.js"; /** * Factory for creating presence State objects. * - * @alpha + * @beta */ export const StateFactory = { /** diff --git a/packages/framework/presence/src/systemWorkspace.ts b/packages/framework/presence/src/systemWorkspace.ts index 8ff9deef9c21..b4675e4221fe 100644 --- a/packages/framework/presence/src/systemWorkspace.ts +++ b/packages/framework/presence/src/systemWorkspace.ts @@ -66,7 +66,7 @@ class SessionClient implements Attendee { */ export interface SystemWorkspace // Portion of Presence that is handled by SystemWorkspace along with - // responsiblity for emitting "attendeeConnected" events. + // responsibility for emitting "attendeeConnected" events. extends Exclude { /** * Must be called when the current client acquires a new connection. diff --git a/packages/framework/presence/src/test/latestMapValueManager.spec.ts b/packages/framework/presence/src/test/latestMapValueManager.spec.ts index 62889232688d..8f416cc5bdef 100644 --- a/packages/framework/presence/src/test/latestMapValueManager.spec.ts +++ b/packages/framework/presence/src/test/latestMapValueManager.spec.ts @@ -15,8 +15,8 @@ import type { LatestMapRaw, LatestMapItemUpdatedClientData, Presence, -} from "@fluidframework/presence/alpha"; -import { StateFactory } from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; +import { StateFactory } from "@fluidframework/presence/beta"; const testWorkspaceName = "name:testWorkspaceA"; diff --git a/packages/framework/presence/src/test/latestValueManager.spec.ts b/packages/framework/presence/src/test/latestValueManager.spec.ts index 8971a4f1d446..7afc32adf988 100644 --- a/packages/framework/presence/src/test/latestValueManager.spec.ts +++ b/packages/framework/presence/src/test/latestValueManager.spec.ts @@ -14,8 +14,8 @@ import type { BroadcastControlSettings, LatestClientData, Presence, -} from "@fluidframework/presence/alpha"; -import { StateFactory } from "@fluidframework/presence/alpha"; +} from "@fluidframework/presence/beta"; +import { StateFactory } from "@fluidframework/presence/beta"; const testWorkspaceName = "name:testWorkspaceA"; diff --git a/packages/framework/presence/src/test/presenceStates.spec.ts b/packages/framework/presence/src/test/presenceStates.spec.ts index 9e3918751027..1aa11dd87885 100644 --- a/packages/framework/presence/src/test/presenceStates.spec.ts +++ b/packages/framework/presence/src/test/presenceStates.spec.ts @@ -17,7 +17,7 @@ import { createPresenceManager } from "../presenceManager.js"; import { addControlsTests } from "./broadcastControlsTests.js"; import { MockEphemeralRuntime } from "./mockEphemeralRuntime.js"; -import { StateFactory } from "@fluidframework/presence/alpha"; +import { StateFactory } from "@fluidframework/presence/beta"; const testWorkspaceName = "name:testWorkspaceA"; diff --git a/packages/framework/presence/src/types.ts b/packages/framework/presence/src/types.ts index 8ef92afaf2e7..1fa0590babee 100644 --- a/packages/framework/presence/src/types.ts +++ b/packages/framework/presence/src/types.ts @@ -6,7 +6,7 @@ import type { BroadcastControls } from "./broadcastControls.js"; import type { InternalTypes } from "./exposedInternalTypes.js"; import type { NotificationsManager } from "./notificationsManager.js"; -import type { Presence } from "./presence.js"; +import type { Presence, PresenceWithNotifications } from "./presence.js"; /** * Unique address within a session. @@ -22,14 +22,14 @@ import type { Presence } from "./presence.js"; * "address:object0/sub-object2:pointers" * ``` * - * @alpha + * @beta */ export type WorkspaceAddress = `${string}:${string}`; /** * Single entry in {@link StatesWorkspaceSchema} or {@link NotificationsWorkspaceSchema}. * - * @alpha + * @beta */ export type StatesWorkspaceEntry< TKey extends string, @@ -44,7 +44,7 @@ export type StatesWorkspaceEntry< * * Keys of schema are the keys of the {@link StatesWorkspace} providing access to State objects. * - * @alpha + * @beta */ export interface StatesWorkspaceSchema { [key: string]: StatesWorkspaceEntry>; @@ -54,7 +54,7 @@ export interface StatesWorkspaceSchema { * Map of State objects registered with {@link StatesWorkspace}. * * @sealed - * @alpha + * @beta */ export type StatesWorkspaceEntries = { /** @@ -75,7 +75,7 @@ export type StatesWorkspaceEntries = { * each client's state is independent and may only be updated by originating client. * * @sealed - * @alpha + * @beta */ export interface StatesWorkspace< TSchema extends StatesWorkspaceSchema, @@ -169,9 +169,9 @@ export interface NotificationsWorkspace; /** - * Containing {@link Presence} + * Containing {@link PresenceWithNotifications} */ - readonly presence: Presence; + readonly presence: PresenceWithNotifications; } // #endregion NotificationsWorkspace @@ -186,4 +186,6 @@ export interface AnyWorkspace< TManagerConstraints = unknown, > extends StatesWorkspace { readonly notifications: StatesWorkspaceEntries; + // TO be removed if/when notifications are fully supported + readonly presence: PresenceWithNotifications; } diff --git a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/messageTypes.ts b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/messageTypes.ts index 42ee16e5f9eb..5474a52a6e08 100644 --- a/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/messageTypes.ts +++ b/packages/service-clients/end-to-end-tests/azure-client/src/test/multiprocess/messageTypes.ts @@ -5,7 +5,7 @@ import type { AzureUser } from "@fluidframework/azure-client/internal"; // eslint-disable-next-line import/no-internal-modules -import type { AttendeeId } from "@fluidframework/presence/alpha"; +import type { AttendeeId } from "@fluidframework/presence/beta"; export type MessageToChild = ConnectCommand | DisconnectSelfCommand; interface ConnectCommand { From a8be12ec69af11125010e103760daf911d91a3a5 Mon Sep 17 00:00:00 2001 From: Jason Hartman Date: Tue, 27 May 2025 03:08:03 -0700 Subject: [PATCH 2/2] fix(client-presence): CommonJs package.json - remove stale internal exports - add /beta exports --- packages/framework/presence/src/cjs/package.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/framework/presence/src/cjs/package.json b/packages/framework/presence/src/cjs/package.json index 59557d8a18bc..14ae27cea6fb 100644 --- a/packages/framework/presence/src/cjs/package.json +++ b/packages/framework/presence/src/cjs/package.json @@ -2,12 +2,13 @@ "name": "@fluidframework/presence", "type": "commonjs", "exports": { + "./beta": { + "types": "./beta.d.ts", + "default": "./index.js" + }, "./alpha": { "types": "./alpha.d.ts", "default": "./index.js" - }, - "./internal/container-definitions/internal": "./container-definitions/index.js", - "./internal/exposedInternalTypes": "./exposedInternalTypes.js", - "./internal/exposedUtilityTypes": "./exposedUtilityTypes.js" + } } }