@devsym/graph-toolkit-react 0.1.0-next.2 → 1.0.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,167 @@
1
+ # AGENTS.md — Graph Toolkit React
2
+
3
+ This file is for coding agents that consume `@devsym/graph-toolkit-react`.
4
+ Use this as the primary quick reference for implementation choices.
5
+
6
+ ## Package
7
+
8
+ - Package name: `@devsym/graph-toolkit-react`
9
+ - Peer deps: `react`, `react-dom`, `@fluentui/react-components`
10
+ - Optional peer dep: `@azure/msal-browser` (required only for `MsalBrowserProvider`)
11
+
12
+ Install:
13
+
14
+ ```bash
15
+ npm install @devsym/graph-toolkit-react react react-dom @fluentui/react-components
16
+ ```
17
+
18
+ If using browser MSAL auth:
19
+
20
+ ```bash
21
+ npm install @azure/msal-browser
22
+ ```
23
+
24
+ ## Scenario Decision Matrix
25
+
26
+ Choose one provider and wire it into `GraphProvider`.
27
+
28
+ | Scenario | Use | Why |
29
+ | --- | --- | --- |
30
+ | Local development / UI prototyping | `MockProvider` | No auth setup; deterministic mock data |
31
+ | Browser-hosted app (SPA) with Entra/MSA sign-in | `MsalBrowserProvider` | Native browser MSAL flow |
32
+ | Microsoft Teams-hosted app with consumer-managed Teams auth | `TeamsHostedProvider` | Reuses existing Teams token acquisition + backend exchange |
33
+ | Existing custom auth stack | Custom `IProvider` implementation | Full control over token source and lifecycle |
34
+
35
+ ## Minimal Patterns
36
+
37
+ ### 1) MockProvider (fastest start)
38
+
39
+ ```tsx
40
+ import { FluentProvider, webLightTheme } from '@fluentui/react-components';
41
+ import { GraphProvider, MockProvider, Person } from '@devsym/graph-toolkit-react';
42
+
43
+ const provider = new MockProvider();
44
+
45
+ export function App() {
46
+ return (
47
+ <FluentProvider theme={webLightTheme}>
48
+ <GraphProvider provider={provider}>
49
+ <Person userId="AdeleV@contoso.com" view="twolines" showPresence />
50
+ </GraphProvider>
51
+ </FluentProvider>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ### 2) Browser SPA with MSAL (`MsalBrowserProvider`)
57
+
58
+ ```tsx
59
+ import { PublicClientApplication } from '@azure/msal-browser';
60
+ import { GraphProvider, MsalBrowserProvider, Person } from '@devsym/graph-toolkit-react';
61
+
62
+ const msal = new PublicClientApplication({
63
+ auth: {
64
+ clientId: 'YOUR_CLIENT_ID',
65
+ authority: 'https://login.microsoftonline.com/common',
66
+ redirectUri: window.location.origin,
67
+ },
68
+ });
69
+
70
+ const provider = new MsalBrowserProvider(msal, ['User.Read']);
71
+ await provider.initialize();
72
+
73
+ export function App() {
74
+ return (
75
+ <GraphProvider provider={provider}>
76
+ <Person userId="me" view="threelines" />
77
+ </GraphProvider>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ### 3) Teams-hosted app (`TeamsHostedProvider`)
83
+
84
+ ```tsx
85
+ import {
86
+ createBackendTokenExchange,
87
+ GraphProvider,
88
+ Person,
89
+ TeamsHostedProvider,
90
+ } from '@devsym/graph-toolkit-react';
91
+ import { authentication } from '@microsoft/teams-js';
92
+
93
+ const exchangeForGraphToken = createBackendTokenExchange({
94
+ endpoint: '/api/token/exchange',
95
+ });
96
+
97
+ const provider = new TeamsHostedProvider({
98
+ defaultScopes: ['User.Read'],
99
+ getTeamsSsoToken: async scopes => authentication.getAuthToken({ resources: scopes }),
100
+ exchangeForGraphToken,
101
+ });
102
+
103
+ await provider.login();
104
+
105
+ export function App() {
106
+ return (
107
+ <GraphProvider provider={provider}>
108
+ <Person userId="me" view="threelines" />
109
+ </GraphProvider>
110
+ );
111
+ }
112
+ ```
113
+
114
+ ### 4) Custom auth (`IProvider`)
115
+
116
+ ```tsx
117
+ import { IProvider, ProviderState } from '@devsym/graph-toolkit-react';
118
+
119
+ export class MyProvider implements IProvider {
120
+ state: ProviderState = 'SignedOut';
121
+
122
+ async login(): Promise<void> {
123
+ this.state = 'SignedIn';
124
+ }
125
+
126
+ async logout(): Promise<void> {
127
+ this.state = 'SignedOut';
128
+ }
129
+
130
+ async getAccessToken(): Promise<string> {
131
+ return 'ACCESS_TOKEN';
132
+ }
133
+ }
134
+ ```
135
+
136
+ ## Scopes by Feature
137
+
138
+ | Feature | Minimum delegated scope | Notes |
139
+ | --- | --- | --- |
140
+ | Current user profile (`userId="me"`) | `User.Read` | Required for basic profile fields |
141
+ | Other user profile (`userId="{id/upn}"`) | `User.ReadBasic.All` | May require admin consent |
142
+ | Presence (`showPresence`) | `Presence.Read` | UI still renders without presence |
143
+ | Profile photo (`fetchImage`) | `User.Read` | Falls back to initials if unavailable |
144
+
145
+ ## Common Failures and Fixes
146
+
147
+ | Symptom | Likely Cause | Action |
148
+ | --- | --- | --- |
149
+ | Redirect URI mismatch / `AADSTS50011` | App registration redirect URI does not match dev URL | Add exact SPA redirect URI used by local dev server |
150
+ | Person stays loading | Provider not initialized / login not completed | Ensure `await provider.initialize()` (MSAL) or `await provider.login()` (Teams/custom) before rendering |
151
+ | Presence missing | `Presence.Read` not granted | Add and consent to `Presence.Read`; keep graceful fallback |
152
+ | `Cannot find module '@azure/msal-browser'` | Using `MsalBrowserProvider` without optional peer dependency installed | Install `@azure/msal-browser` |
153
+
154
+ ## Agent Rules
155
+
156
+ - Choose the simplest provider that fits the host environment.
157
+ - Keep scopes minimal and feature-driven.
158
+ - Prefer `userId="me"` for first implementation; expand to other users only when needed.
159
+ - Wrap components with `GraphProvider`; avoid direct token handling in UI components.
160
+ - If uncertain, default to `MockProvider` for local iteration, then switch to real auth provider.
161
+
162
+ ## Source of Truth
163
+
164
+ - Primary docs: `README.md`
165
+ - Migration guide: `docs/MGT_MIGRATION.md`
166
+ - MSAL sample: `samples/react-msal-sample/README.md`
167
+ - API exports and types: package root `dist/index.d.ts` in published artifact
@@ -1 +1 @@
1
- {"version":3,"file":"Person.d.ts","sourceRoot":"","sources":["../../../src/components/Person/Person.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA2C7C,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAuFxC,CAAC"}
1
+ {"version":3,"file":"Person.d.ts","sourceRoot":"","sources":["../../../src/components/Person/Person.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAwB7C,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAkFxC,CAAC"}
@@ -1,12 +1,8 @@
1
1
  /**
2
2
  * Person component types
3
3
  */
4
- import type { CSSProperties } from 'react';
5
- import { User } from '@microsoft/microsoft-graph-types';
6
- export type PersonView = 'avatar' | 'oneline' | 'twolines' | 'threelines';
7
- export type PersonSize = 'small' | 'medium' | 'large' | 'extra-large';
8
- export type PersonTextAlignment = 'start' | 'center';
9
- export type PersonTextPosition = 'before' | 'after' | 'below';
4
+ import type { PersonaProps } from '@fluentui/react-components';
5
+ export type PersonView = 'avatar' | 'oneline' | 'twolines' | 'threelines' | 'fourlines';
10
6
  export interface PersonDetails {
11
7
  displayName?: string;
12
8
  mail?: string;
@@ -16,20 +12,13 @@ export interface PersonDetails {
16
12
  id?: string;
17
13
  userPrincipalName?: string;
18
14
  }
19
- export interface PersonProps {
15
+ export interface PersonProps extends PersonaProps {
20
16
  userId?: string;
21
17
  userPrincipalName?: string;
22
18
  email?: string;
23
19
  personDetails?: PersonDetails;
24
20
  view?: PersonView;
25
21
  showPresence?: boolean;
26
- avatarSize?: PersonSize;
27
- textAlignment?: PersonTextAlignment;
28
- textPosition?: PersonTextPosition;
29
- numericSize?: number;
30
22
  fetchImage?: boolean;
31
- onClick?: (person: User) => void;
32
- className?: string;
33
- style?: CSSProperties;
34
23
  }
35
24
  //# sourceMappingURL=Person.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Person.types.d.ts","sourceRoot":"","sources":["../../../src/components/Person/Person.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,CAAC;AAC1E,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAC;AACtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,CAAC;AACrD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAE1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,aAAa,CAAC,EAAE,aAAa,CAAC;IAG9B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IAGxB,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,UAAU,CAAC,EAAE,OAAO,CAAC;IAGrB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,KAAK,IAAI,CAAC;IAGjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB"}
1
+ {"version":3,"file":"Person.types.d.ts","sourceRoot":"","sources":["../../../src/components/Person/Person.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,CAAC;AAExF,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAE/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,aAAa,CAAC,EAAE,aAAa,CAAC;IAG9B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePersonData.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersonData.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAKlE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,oBAAoB,KAAG,UAgK7D,CAAC"}
1
+ {"version":3,"file":"usePersonData.d.ts","sourceRoot":"","sources":["../../src/hooks/usePersonData.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAYlE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA8BD;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,oBAAoB,KAAG,UA2O7D,CAAC"}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("react/jsx-runtime"),O=require("@fluentui/react-components"),d=require("react"),q=require("@microsoft/microsoft-graph-client");var o=(t=>(t.Loading="Loading",t.SignedOut="SignedOut",t.SignedIn="SignedIn",t))(o||{});const P=d.createContext({provider:null,state:o.Loading}),G=({provider:t,children:e})=>{const s=d.useCallback(()=>t?.getState?.()??o.Loading,[t]),r=d.useCallback(i=>(t.onStateChanged?.(i),()=>{const c=t;typeof c.removeStateChangedHandler=="function"&&c.removeStateChangedHandler(i)}),[t]),n=d.useSyncExternalStore(r,s,s),a=d.useMemo(()=>({provider:t,state:n}),[t,n]);return w.jsx(P.Provider,{value:a,children:e})},I=()=>{const{provider:t}=d.useContext(P);return t},b=()=>{const{state:t}=d.useContext(P);return t},L=t=>q.Client.init({authProvider:async e=>{try{const s=await t.getAccessToken();e(null,s)}catch(s){e(s,null)}}}),z=t=>{const e=t.trim().split(" ");return e.length===1?e[0].substring(0,2).toUpperCase():(e[0][0]+e[e.length-1][0]).toUpperCase()},U=()=>{const t=I(),e=b();return d.useMemo(()=>!t||e!=="SignedIn"?null:L(t),[t,e])};class C{state=o.SignedOut;listeners=[];constructor(e){e?.autoSignIn&&(this.state=o.SignedIn)}getAccessToken(){return this.state!==o.SignedIn?Promise.reject(new Error("Not signed in")):Promise.resolve("mock-token")}async login(){this.state=o.SignedIn,this.emit()}async logout(){this.state=o.SignedOut,this.emit()}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(s=>s!==e)}emit(){for(const e of this.listeners)try{e()}catch{}}}const B=()=>new C({autoSignIn:!0}),j=t=>{const e=U(),s=I(),r=b(),{userId:n,userPrincipalName:a,fetchPresence:i=!1,fetchPhoto:c=!0}=t,[f,p]=d.useState({user:null,presence:null,photoUrl:null,loading:!0,error:null});return d.useEffect(()=>{let h=!1;return(async()=>{if(s instanceof C){const l={id:"00000000-0000-0000-0000-000000000000",displayName:"Adele Vance",userPrincipalName:"adelev@contoso.com",jobTitle:"Product Manager",department:"Marketing",officeLocation:"19/3106",mail:"adelev@contoso.com"},u={availability:"Available",activity:"Available",id:l.id,odataType:"#microsoft.graph.presence"};h||p({user:l,presence:i?u:null,photoUrl:c?"https://ui-avatars.com/api/?name=Adele+Vance&background=0D8ABC&color=ffffff":null,loading:!1,error:null});return}if(r!=="SignedIn"){h||p({user:null,presence:null,photoUrl:null,loading:!1,error:r==="SignedOut"?null:new Error("Provider not signed in")});return}if(!e){h||p(l=>({...l,loading:!0}));return}const m=n||a;if(!m){h||p({user:null,presence:null,photoUrl:null,loading:!1,error:null});return}try{p(g=>({...g,loading:!0,error:null}));const l=m.toLowerCase()==="me",u=await e.api(l?"/me":`/users/${m}`).select("id,displayName,jobTitle,mail,department,officeLocation,userPrincipalName").get();if(h)return;let S=null,k=null;if(i&&u.id)try{S=await e.api(l?"/me/presence":`/users/${u.id}/presence`).get()}catch(g){console.warn("Failed to fetch presence:",g)}if(c&&u.id)try{const g=await e.api(l?"/me/photo/$value":`/users/${u.id}/photo/$value`).get();k=URL.createObjectURL(g)}catch(g){console.warn("Failed to fetch photo:",g)}h||p({user:u,presence:S,photoUrl:k,loading:!1,error:null})}catch(l){h||p({user:null,presence:null,photoUrl:null,loading:!1,error:l})}})(),()=>{h=!0}},[e,s,r,n,a,i,c]),f},N=t=>{switch(t){case"small":return"small";case"large":return"large";case"extra-large":return"extra-large";default:return"medium"}},$=t=>{switch(t?.toLowerCase()){case"available":case"availableidle":return"available";case"away":case"berightback":return"away";case"busy":case"busyidle":case"donotdisturb":return"busy";default:return"offline"}},D=({userId:t,userPrincipalName:e,email:s,personDetails:r,view:n="oneline",showPresence:a=!1,avatarSize:i="medium",textAlignment:c,textPosition:f,numericSize:p,fetchImage:h=!0,onClick:y,className:m,style:l})=>{const{user:u,presence:S,photoUrl:k,loading:g}=j({userId:r?void 0:t||e||s,fetchPresence:a,fetchPhoto:h}),T=r||u,E=p??N(i);if(g)return w.jsx(O.Persona,{size:E,name:"Loading...",textAlignment:c,textPosition:f,className:m,style:l});if(!T)return null;const M=T.displayName||"Unknown User",F=z(M),v={size:E,name:M,textAlignment:c,textPosition:f,avatar:{image:k?{src:k}:void 0,initials:k?void 0:F},className:m,style:l};return a&&S&&(v.presence={status:$(S.availability)}),n!=="avatar"&&((n==="twolines"||n==="threelines")&&(v.secondaryText=T.jobTitle??void 0),n==="threelines"&&(v.tertiaryText=T.department??void 0)),y&&(v.onClick=()=>{u&&y(u)}),w.jsx(O.Persona,{...v})};function H(t){return`See https://aka.ms/msal.js.errors#${t} for details`}class x extends Error{constructor(e,s,r){const n=s||(e?H(e):""),a=n?`${e}: ${n}`:e;super(a),Object.setPrototypeOf(this,x.prototype),this.errorCode=e||"",this.errorMessage=n||"",this.subError=r||"",this.name="AuthError"}setCorrelationId(e){this.correlationId=e}}class A extends x{constructor(e,s,r,n,a,i,c,f){super(e,s,r),Object.setPrototypeOf(this,A.prototype),this.timestamp=n||"",this.traceId=a||"",this.correlationId=i||"",this.claims=c||"",this.name="InteractionRequiredAuthError",this.errorNo=f}}class R{msalInstance;defaultScopes;account=null;state=o.Loading;listeners=[];constructor(e,s){this.msalInstance=e,this.defaultScopes=s}async initialize(){await this.msalInstance.initialize(),await this.msalInstance.handleRedirectPromise();const e=this.msalInstance.getActiveAccount(),s=this.msalInstance.getAllAccounts();this.account=e??s[0]??null,this.account?(this.msalInstance.setActiveAccount(this.account),this.setState(o.SignedIn)):this.setState(o.SignedOut)}async login(){const e={scopes:this.defaultScopes},s=await this.msalInstance.loginPopup(e);this.account=s.account,this.account&&this.msalInstance.setActiveAccount(this.account),this.setState(o.SignedIn)}async logout(){const e=this.msalInstance.getActiveAccount()??this.account??void 0;await this.msalInstance.logoutPopup({account:e}),this.account=null,this.setState(o.SignedOut)}async getAccessToken(e){const s=e&&e.length>0?e:this.defaultScopes,r=this.msalInstance.getActiveAccount()??this.account;if(!r)throw new Error("No active account. User must sign in first.");const n={scopes:s,account:r};try{return(await this.msalInstance.acquireTokenSilent(n)).accessToken}catch(a){if(a instanceof A){const i={scopes:s,account:r};return(await this.msalInstance.acquireTokenPopup(i)).accessToken}throw a}}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(s=>s!==e)}setState(e){this.state=e,this.emit()}emit(){for(const e of this.listeners)try{e()}catch{}}}const K=6e4;function V(t){const e=t.fetchFn??fetch;return async({ssoToken:s,scopes:r})=>{const n=await e(t.endpoint,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({scopes:r})}),a=await n.json().catch(()=>({error:n.statusText||"Token exchange failed"}));if(!n.ok){const i=a?.error||a?.message||n.statusText;throw new Error(`Backend token exchange failed: ${i}`)}if(!a?.accessToken)throw new Error("Backend token exchange response did not include accessToken.");return{accessToken:a.accessToken,expiresAt:a.expiresAt}}}class J{defaultScopes;getTeamsSsoTokenFn;exchangeForGraphTokenFn;refreshSkewMs;state=o.SignedOut;listeners=[];tokenCache=new Map;constructor(e){if(!e.defaultScopes||e.defaultScopes.length===0)throw new Error("TeamsHostedProvider requires at least one default scope.");this.defaultScopes=[...new Set(e.defaultScopes)],this.getTeamsSsoTokenFn=e.getTeamsSsoToken,this.exchangeForGraphTokenFn=e.exchangeForGraphToken,this.refreshSkewMs=e.refreshSkewMs??K}async login(){try{await this.getAccessToken(),this.setState(o.SignedIn)}catch(e){throw this.clearCache(),this.setState(o.SignedOut),e}}async logout(){this.clearCache(),this.setState(o.SignedOut)}async getAccessToken(e){const s=this.normalizeScopes(e),r=this.buildScopeKey(s),n=this.tokenCache.get(r);if(n&&this.isTokenValid(n))return n.accessToken;const a=await this.getTeamsSsoTokenFn(s);if(!a)throw new Error("Teams SSO token acquisition returned empty token.");const i=await this.exchangeForGraphTokenFn({ssoToken:a,scopes:s}),c=this.normalizeExchangedToken(i);return this.tokenCache.set(r,c),this.setState(o.SignedIn),c.accessToken}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(s=>s!==e)}normalizeScopes(e){const s=e&&e.length>0?e:this.defaultScopes,r=[...new Set(s.filter(Boolean))];if(r.length===0)throw new Error("At least one scope is required to request a Graph token.");return r}buildScopeKey(e){return[...e].sort().join(" ")}isTokenValid(e){return e.expiresAtMs?Date.now()+this.refreshSkewMs<e.expiresAtMs:!0}normalizeExchangedToken(e){if(typeof e=="string"){if(!e)throw new Error("Graph token exchange returned empty access token.");return{accessToken:e}}if(!e.accessToken)throw new Error("Graph token exchange returned empty access token.");return{accessToken:e.accessToken,expiresAtMs:this.toEpochMs(e.expiresAt)}}toEpochMs(e){if(e)return e instanceof Date?e.getTime():e}clearCache(){this.tokenCache.clear()}setState(e){this.state!==e&&(this.state=e,this.emit())}emit(){for(const e of this.listeners)try{e()}catch{}}}exports.GraphProvider=G;exports.MockProvider=C;exports.MsalBrowserProvider=R;exports.Person=D;exports.ProviderState=o;exports.TeamsHostedProvider=J;exports.createBackendTokenExchange=V;exports.createMockSignedInProvider=B;exports.useGraphClient=U;exports.usePersonData=j;exports.useProvider=I;exports.useProviderState=b;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const F=require("react/jsx-runtime"),L=require("@fluentui/react-components"),d=require("react"),V=require("@microsoft/microsoft-graph-client"),Q="graph-toolkit-person-cache",P="personData",J=1,W=3600*1e3,X=3600*1e3,Y=300*1e3,q={enabled:!0,userTtlMs:W,photoTtlMs:X,presenceTtlMs:Y},D=()=>typeof window<"u"&&typeof window.indexedDB<"u",G=t=>new Promise((e,n)=>{t.onsuccess=()=>e(t.result),t.onerror=()=>n(t.error)}),R=()=>new Promise((t,e)=>{if(!D()){e(new Error("IndexedDB unavailable"));return}const n=window.indexedDB.open(Q,J);n.onupgradeneeded=()=>{const r=n.result;r.objectStoreNames.contains(P)||r.createObjectStore(P)},n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error)}),Z=t=>`person:${t.trim().toLowerCase()}`,U=(t,e)=>t?Date.now()-t<e:!1,ee=async t=>{if(!D())return null;let e=null;try{e=await R();const r=e.transaction(P,"readonly").objectStore(P);return await G(r.get(t))??null}catch{return null}finally{e?.close()}},te=async(t,e)=>{if(!D())return;let n=null;try{n=await R();const s=n.transaction(P,"readwrite").objectStore(P);await G(s.put(e,t))}catch{return}finally{n?.close()}},O=d.createContext({provider:null,state:"Loading",personCacheOptions:q}),ne=({provider:t,children:e,personCacheOptions:n})=>{const r=d.useCallback(()=>t?.getState?.()??"Loading",[t]),s=d.useCallback(l=>(t.onStateChanged?.(l),()=>{const m=t;typeof m.removeStateChangedHandler=="function"&&m.removeStateChangedHandler(l)}),[t]),o=d.useSyncExternalStore(s,r,r),i=d.useMemo(()=>({...q,...n??{}}),[n]),a=d.useMemo(()=>({provider:t,state:o,personCacheOptions:i}),[t,o,i]);return F.jsx(O.Provider,{value:a,children:e})},N=()=>{const{provider:t}=d.useContext(O);return t},_=()=>{const{state:t}=d.useContext(O);return t},$=()=>{const{personCacheOptions:t}=d.useContext(O);return t},se=t=>V.Client.init({authProvider:async e=>{try{const n=await t.getAccessToken();e(null,n)}catch(n){e(n,null)}}}),re=t=>{const e=t.trim().split(" ");return e.length===1?e[0].substring(0,2).toUpperCase():(e[0][0]+e[e.length-1][0]).toUpperCase()},z=()=>{const t=N(),e=_();return d.useMemo(()=>!t||e!=="SignedIn"?null:se(t),[t,e])};class B{state="SignedOut";listeners=[];constructor(e){e?.autoSignIn&&(this.state="SignedIn")}getAccessToken(){return this.state!=="SignedIn"?Promise.reject(new Error("Not signed in")):Promise.resolve("mock-token")}async login(){this.state="SignedIn",this.emit()}async logout(){this.state="SignedOut",this.emit()}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(n=>n!==e)}emit(){for(const e of this.listeners)try{e()}catch{}}}const oe=()=>new B({autoSignIn:!0}),ae=async t=>{if(!t)return null;if(typeof t=="string")return t;const e=t instanceof Blob?t:t instanceof ArrayBuffer?new Blob([t]):null;return e?new Promise((n,r)=>{const s=new FileReader;s.onloadend=()=>n(typeof s.result=="string"?s.result:null),s.onerror=()=>r(s.error),s.readAsDataURL(e)}):null},K=t=>{const e=z(),n=N(),r=_(),s=$(),{userId:o,userPrincipalName:i,fetchPresence:a=!1,fetchPhoto:l=!0}=t,[m,u]=d.useState({user:null,presence:null,photoUrl:null,loading:!0,error:null});return d.useEffect(()=>{let f=!1;return(async()=>{if(n instanceof B){const h={id:"00000000-0000-0000-0000-000000000000",displayName:"Adele Vance",userPrincipalName:"adelev@contoso.com",jobTitle:"Product Manager",department:"Marketing",officeLocation:"19/3106",mail:"adelev@contoso.com"},y={availability:"Available",activity:"Available",id:h.id,odataType:"#microsoft.graph.presence"};f||u({user:h,presence:a?y:null,photoUrl:l?"https://ui-avatars.com/api/?name=Adele+Vance&background=0D8ABC&color=ffffff":null,loading:!1,error:null});return}if(r!=="SignedIn"){f||u({user:null,presence:null,photoUrl:null,loading:!1,error:r==="SignedOut"?null:new Error("Provider not signed in")});return}if(!e){f||u(h=>({...h,loading:!0}));return}const T=o||i;if(!T){f||u({user:null,presence:null,photoUrl:null,loading:!1,error:null});return}const E=Z(T),c=s.enabled?await ee(E):null,x=!!(c?.user&&U(c.userCachedAt,s.userTtlMs)),A=!!(c?.presenceCachedAt&&U(c.presenceCachedAt,s.presenceTtlMs)),b=!!(c?.photoCachedAt&&U(c.photoCachedAt,s.photoTtlMs));if(x&&(!a||A)&&(!l||b)){f||u({user:c?.user??null,presence:a?c?.presence??null:null,photoUrl:l?c?.photoUrl??null:null,loading:!1,error:null});return}try{u(v=>({...v,loading:!0,error:null}));const h=T.toLowerCase()==="me",y=Date.now();let k=!1,w=!1,C=!1,p=x?c?.user??null:null;if(p||(p=await e.api(h?"/me":`/users/${T}`).select("id,displayName,jobTitle,mail,department,officeLocation,userPrincipalName").get(),k=!0),f)return;let I=null,M=null;if(a){if(A)I=c?.presence??null;else if(p?.id){try{I=await e.api(h?"/me/presence":`/users/${p.id}/presence`).get()}catch(v){console.warn("Failed to fetch presence:",v)}w=!0}}if(l){if(b)M=c?.photoUrl??null;else if(p?.id){try{const v=await e.api(h?"/me/photo/$value":`/users/${p.id}/photo/$value`).get();M=await ae(v)}catch(v){console.warn("Failed to fetch photo:",v)}C=!0}}const g={...c??{}};k&&p&&(g.user=p,g.userCachedAt=y),a&&(w||A)&&(g.presence=w?I:c?.presence,g.presenceCachedAt=w?y:c?.presenceCachedAt),l&&(C||b)&&(g.photoUrl=C?M:c?.photoUrl,g.photoCachedAt=C?y:c?.photoCachedAt),s.enabled&&(k||w||C)&&g.user&&await te(E,g),f||u({user:p,presence:I,photoUrl:M,loading:!1,error:null})}catch(h){f||u({user:null,presence:null,photoUrl:null,loading:!1,error:h})}})(),()=>{f=!0}},[e,n,r,o,i,a,l,s]),m},ce=t=>{switch(t?.toLowerCase()){case"available":case"availableidle":return"available";case"away":case"berightback":return"away";case"busy":case"busyidle":case"donotdisturb":return"busy";default:return"offline"}},ie=({userId:t,userPrincipalName:e,email:n,personDetails:r,view:s="oneline",showPresence:o=!1,fetchImage:i=!0,...a})=>{const{user:l,presence:m,photoUrl:u,loading:f}=K({userId:r?void 0:t||e||n,fetchPresence:o,fetchPhoto:i}),S=r||l;if(f)return F.jsx(L.Persona,{...a,name:a.name??"Loading..."});if(!S)return null;const T=S.displayName||"Unknown User",E=re(T),c=s!=="avatar"&&(s==="twolines"||s==="threelines"||s==="fourlines")?S.jobTitle??void 0:void 0,x=s!=="avatar"&&(s==="threelines"||s==="fourlines")?S.department??void 0:void 0,A=s!=="avatar"&&s==="fourlines"?S.officeLocation??S.mail??void 0:void 0,b=o&&m?{status:ce(m.availability)}:void 0,h=a.presence??b,y=a.avatar??{image:u?{src:u}:void 0,initials:u?void 0:E},k=a.name??T,w=a.secondaryText??c,C=a.tertiaryText??x,p=a.quaternaryText??A;return F.jsx(L.Persona,{...a,name:k,avatar:y,presence:h,secondaryText:w,tertiaryText:C,quaternaryText:p})};function le(t){return`See https://aka.ms/msal.js.errors#${t} for details`}class j extends Error{constructor(e,n,r){const s=n||(e?le(e):""),o=s?`${e}: ${s}`:e;super(o),Object.setPrototypeOf(this,j.prototype),this.errorCode=e||"",this.errorMessage=s||"",this.subError=r||"",this.name="AuthError"}setCorrelationId(e){this.correlationId=e}}class H extends j{constructor(e,n,r,s,o,i,a,l){super(e,n,r),Object.setPrototypeOf(this,H.prototype),this.timestamp=s||"",this.traceId=o||"",this.correlationId=i||"",this.claims=a||"",this.name="InteractionRequiredAuthError",this.errorNo=l}}class ue{msalInstance;defaultScopes;account=null;state="Loading";listeners=[];constructor(e,n){this.msalInstance=e,this.defaultScopes=n}async initialize(){await this.msalInstance.initialize(),await this.msalInstance.handleRedirectPromise();const e=this.msalInstance.getActiveAccount(),n=this.msalInstance.getAllAccounts();this.account=e??n[0]??null,this.account?(this.msalInstance.setActiveAccount(this.account),this.setState("SignedIn")):this.setState("SignedOut")}async login(){const e={scopes:this.defaultScopes},n=await this.msalInstance.loginPopup(e);this.account=n.account,this.account&&this.msalInstance.setActiveAccount(this.account),this.setState("SignedIn")}async logout(){const e=this.msalInstance.getActiveAccount()??this.account??void 0;await this.msalInstance.logoutPopup({account:e}),this.account=null,this.setState("SignedOut")}async getAccessToken(e){const n=e&&e.length>0?e:this.defaultScopes,r=this.msalInstance.getActiveAccount()??this.account;if(!r)throw new Error("No active account. User must sign in first.");const s={scopes:n,account:r};try{return(await this.msalInstance.acquireTokenSilent(s)).accessToken}catch(o){if(o instanceof H){const i={scopes:n,account:r};return(await this.msalInstance.acquireTokenPopup(i)).accessToken}throw o}}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(n=>n!==e)}setState(e){this.state=e,this.emit()}emit(){for(const e of this.listeners)try{e()}catch{}}}const he=6e4;function de(t){const e=t.fetchFn??fetch;return async({ssoToken:n,scopes:r})=>{const s=await e(t.endpoint,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json"},body:JSON.stringify({scopes:r})}),o=await s.json().catch(()=>({error:s.statusText||"Token exchange failed"}));if(!s.ok){const i=o?.error||o?.message||s.statusText;throw new Error(`Backend token exchange failed: ${i}`)}if(!o?.accessToken)throw new Error("Backend token exchange response did not include accessToken.");return{accessToken:o.accessToken,expiresAt:o.expiresAt}}}class fe{defaultScopes;getTeamsSsoTokenFn;exchangeForGraphTokenFn;refreshSkewMs;state="SignedOut";listeners=[];tokenCache=new Map;constructor(e){if(!e.defaultScopes||e.defaultScopes.length===0)throw new Error("TeamsHostedProvider requires at least one default scope.");this.defaultScopes=[...new Set(e.defaultScopes)],this.getTeamsSsoTokenFn=e.getTeamsSsoToken,this.exchangeForGraphTokenFn=e.exchangeForGraphToken,this.refreshSkewMs=e.refreshSkewMs??he}async login(){try{await this.getAccessToken(),this.setState("SignedIn")}catch(e){throw this.clearCache(),this.setState("SignedOut"),e}}async logout(){this.clearCache(),this.setState("SignedOut")}async getAccessToken(e){const n=this.normalizeScopes(e),r=this.buildScopeKey(n),s=this.tokenCache.get(r);if(s&&this.isTokenValid(s))return s.accessToken;const o=await this.getTeamsSsoTokenFn(n);if(!o)throw new Error("Teams SSO token acquisition returned empty token.");const i=await this.exchangeForGraphTokenFn({ssoToken:o,scopes:n}),a=this.normalizeExchangedToken(i);return this.tokenCache.set(r,a),this.setState("SignedIn"),a.accessToken}getState(){return this.state}onStateChanged(e){this.listeners.push(e)}removeStateChangedHandler(e){this.listeners=this.listeners.filter(n=>n!==e)}normalizeScopes(e){const n=e&&e.length>0?e:this.defaultScopes,r=[...new Set(n.filter(Boolean))];if(r.length===0)throw new Error("At least one scope is required to request a Graph token.");return r}buildScopeKey(e){return[...e].sort().join(" ")}isTokenValid(e){return e.expiresAtMs?Date.now()+this.refreshSkewMs<e.expiresAtMs:!0}normalizeExchangedToken(e){if(typeof e=="string"){if(!e)throw new Error("Graph token exchange returned empty access token.");return{accessToken:e}}if(!e.accessToken)throw new Error("Graph token exchange returned empty access token.");return{accessToken:e.accessToken,expiresAtMs:this.toEpochMs(e.expiresAt)}}toEpochMs(e){if(e)return e instanceof Date?e.getTime():e}clearCache(){this.tokenCache.clear()}setState(e){this.state!==e&&(this.state=e,this.emit())}emit(){for(const e of this.listeners)try{e()}catch{}}}exports.GraphProvider=ne;exports.MockProvider=B;exports.MsalBrowserProvider=ue;exports.Person=ie;exports.TeamsHostedProvider=fe;exports.createBackendTokenExchange=de;exports.createMockSignedInProvider=oe;exports.useGraphClient=z;exports.usePersonCacheOptions=$;exports.usePersonData=K;exports.useProvider=N;exports.useProviderState=_;
package/dist/index.mjs CHANGED
@@ -1,59 +1,122 @@
1
- import { jsx as w } from "react/jsx-runtime";
2
- import { Persona as C } from "@fluentui/react-components";
3
- import { createContext as j, useCallback as x, useSyncExternalStore as G, useMemo as E, useContext as M, useState as q, useEffect as z } from "react";
4
- import { Client as N } from "@microsoft/microsoft-graph-client";
5
- var o = /* @__PURE__ */ ((t) => (t.Loading = "Loading", t.SignedOut = "SignedOut", t.SignedIn = "SignedIn", t))(o || {});
6
- const v = j({
1
+ import { jsx as U } from "react/jsx-runtime";
2
+ import { Persona as H } from "@fluentui/react-components";
3
+ import { createContext as K, useCallback as L, useSyncExternalStore as V, useMemo as F, useContext as N, useState as Q, useEffect as J } from "react";
4
+ import { Client as W } from "@microsoft/microsoft-graph-client";
5
+ const X = "graph-toolkit-person-cache", v = "personData", Y = 1, Z = 3600 * 1e3, ee = 3600 * 1e3, te = 300 * 1e3, j = {
6
+ enabled: !0,
7
+ userTtlMs: Z,
8
+ photoTtlMs: ee,
9
+ presenceTtlMs: te
10
+ }, _ = () => typeof window < "u" && typeof window.indexedDB < "u", R = (t) => new Promise((e, n) => {
11
+ t.onsuccess = () => e(t.result), t.onerror = () => n(t.error);
12
+ }), q = () => new Promise((t, e) => {
13
+ if (!_()) {
14
+ e(new Error("IndexedDB unavailable"));
15
+ return;
16
+ }
17
+ const n = window.indexedDB.open(X, Y);
18
+ n.onupgradeneeded = () => {
19
+ const r = n.result;
20
+ r.objectStoreNames.contains(v) || r.createObjectStore(v);
21
+ }, n.onsuccess = () => t(n.result), n.onerror = () => e(n.error);
22
+ }), ne = (t) => `person:${t.trim().toLowerCase()}`, M = (t, e) => t ? Date.now() - t < e : !1, se = async (t) => {
23
+ if (!_())
24
+ return null;
25
+ let e = null;
26
+ try {
27
+ e = await q();
28
+ const r = e.transaction(v, "readonly").objectStore(v);
29
+ return await R(r.get(t)) ?? null;
30
+ } catch {
31
+ return null;
32
+ } finally {
33
+ e?.close();
34
+ }
35
+ }, re = async (t, e) => {
36
+ if (!_())
37
+ return;
38
+ let n = null;
39
+ try {
40
+ n = await q();
41
+ const s = n.transaction(v, "readwrite").objectStore(v);
42
+ await R(s.put(e, t));
43
+ } catch {
44
+ return;
45
+ } finally {
46
+ n?.close();
47
+ }
48
+ }, O = K({
7
49
  provider: null,
8
- state: o.Loading
9
- }), Y = ({ provider: t, children: e }) => {
10
- const s = x(
11
- () => t?.getState?.() ?? o.Loading,
50
+ state: "Loading",
51
+ personCacheOptions: j
52
+ }), Te = ({
53
+ provider: t,
54
+ children: e,
55
+ personCacheOptions: n
56
+ }) => {
57
+ const r = L(
58
+ () => t?.getState?.() ?? "Loading",
12
59
  [t]
13
- ), r = x(
14
- (i) => (t.onStateChanged?.(i), () => {
15
- const c = t;
16
- typeof c.removeStateChangedHandler == "function" && c.removeStateChangedHandler(i);
60
+ ), s = L(
61
+ (l) => (t.onStateChanged?.(l), () => {
62
+ const g = t;
63
+ typeof g.removeStateChangedHandler == "function" && g.removeStateChangedHandler(l);
17
64
  }),
18
65
  [t]
19
- ), n = G(r, s, s), a = E(() => ({ provider: t, state: n }), [t, n]);
20
- return /* @__PURE__ */ w(v.Provider, { value: a, children: e });
21
- }, U = () => {
22
- const { provider: t } = M(v);
66
+ ), o = V(s, r, r), i = F(
67
+ () => ({
68
+ ...j,
69
+ ...n ?? {}
70
+ }),
71
+ [n]
72
+ ), a = F(
73
+ () => ({
74
+ provider: t,
75
+ state: o,
76
+ personCacheOptions: i
77
+ }),
78
+ [t, o, i]
79
+ );
80
+ return /* @__PURE__ */ U(O.Provider, { value: a, children: e });
81
+ }, G = () => {
82
+ const { provider: t } = N(O);
83
+ return t;
84
+ }, $ = () => {
85
+ const { state: t } = N(O);
23
86
  return t;
24
- }, O = () => {
25
- const { state: t } = M(v);
87
+ }, oe = () => {
88
+ const { personCacheOptions: t } = N(O);
26
89
  return t;
27
- }, $ = (t) => N.init({
90
+ }, ae = (t) => W.init({
28
91
  authProvider: async (e) => {
29
92
  try {
30
- const s = await t.getAccessToken();
31
- e(null, s);
32
- } catch (s) {
33
- e(s, null);
93
+ const n = await t.getAccessToken();
94
+ e(null, n);
95
+ } catch (n) {
96
+ e(n, null);
34
97
  }
35
98
  }
36
- }), B = (t) => {
99
+ }), ce = (t) => {
37
100
  const e = t.trim().split(" ");
38
101
  return e.length === 1 ? e[0].substring(0, 2).toUpperCase() : (e[0][0] + e[e.length - 1][0]).toUpperCase();
39
- }, D = () => {
40
- const t = U(), e = O();
41
- return E(() => !t || e !== "SignedIn" ? null : $(t), [t, e]);
102
+ }, ie = () => {
103
+ const t = G(), e = $();
104
+ return F(() => !t || e !== "SignedIn" ? null : ae(t), [t, e]);
42
105
  };
43
- class F {
44
- state = o.SignedOut;
106
+ class z {
107
+ state = "SignedOut";
45
108
  listeners = [];
46
109
  constructor(e) {
47
- e?.autoSignIn && (this.state = o.SignedIn);
110
+ e?.autoSignIn && (this.state = "SignedIn");
48
111
  }
49
112
  getAccessToken() {
50
- return this.state !== o.SignedIn ? Promise.reject(new Error("Not signed in")) : Promise.resolve("mock-token");
113
+ return this.state !== "SignedIn" ? Promise.reject(new Error("Not signed in")) : Promise.resolve("mock-token");
51
114
  }
52
115
  async login() {
53
- this.state = o.SignedIn, this.emit();
116
+ this.state = "SignedIn", this.emit();
54
117
  }
55
118
  async logout() {
56
- this.state = o.SignedOut, this.emit();
119
+ this.state = "SignedOut", this.emit();
57
120
  }
58
121
  getState() {
59
122
  return this.state;
@@ -62,7 +125,7 @@ class F {
62
125
  this.listeners.push(e);
63
126
  }
64
127
  removeStateChangedHandler(e) {
65
- this.listeners = this.listeners.filter((s) => s !== e);
128
+ this.listeners = this.listeners.filter((n) => n !== e);
66
129
  }
67
130
  emit() {
68
131
  for (const e of this.listeners)
@@ -72,19 +135,29 @@ class F {
72
135
  }
73
136
  }
74
137
  }
75
- const Z = () => new F({ autoSignIn: !0 }), H = (t) => {
76
- const e = D(), s = U(), r = O(), { userId: n, userPrincipalName: a, fetchPresence: i = !1, fetchPhoto: c = !0 } = t, [p, d] = q({
138
+ const ye = () => new z({ autoSignIn: !0 }), le = async (t) => {
139
+ if (!t)
140
+ return null;
141
+ if (typeof t == "string")
142
+ return t;
143
+ const e = t instanceof Blob ? t : t instanceof ArrayBuffer ? new Blob([t]) : null;
144
+ return e ? new Promise((n, r) => {
145
+ const s = new FileReader();
146
+ s.onloadend = () => n(typeof s.result == "string" ? s.result : null), s.onerror = () => r(s.error), s.readAsDataURL(e);
147
+ }) : null;
148
+ }, ue = (t) => {
149
+ const e = ie(), n = G(), r = $(), s = oe(), { userId: o, userPrincipalName: i, fetchPresence: a = !1, fetchPhoto: l = !0 } = t, [g, u] = Q({
77
150
  user: null,
78
151
  presence: null,
79
152
  photoUrl: null,
80
153
  loading: !0,
81
154
  error: null
82
155
  });
83
- return z(() => {
84
- let h = !1;
156
+ return J(() => {
157
+ let d = !1;
85
158
  return (async () => {
86
- if (s instanceof F) {
87
- const l = {
159
+ if (n instanceof z) {
160
+ const h = {
88
161
  id: "00000000-0000-0000-0000-000000000000",
89
162
  displayName: "Adele Vance",
90
163
  userPrincipalName: "adelev@contoso.com",
@@ -92,23 +165,23 @@ const Z = () => new F({ autoSignIn: !0 }), H = (t) => {
92
165
  department: "Marketing",
93
166
  officeLocation: "19/3106",
94
167
  mail: "adelev@contoso.com"
95
- }, u = {
168
+ }, T = {
96
169
  availability: "Available",
97
170
  activity: "Available",
98
- id: l.id,
171
+ id: h.id,
99
172
  odataType: "#microsoft.graph.presence"
100
173
  };
101
- h || d({
102
- user: l,
103
- presence: i ? u : null,
104
- photoUrl: c ? "https://ui-avatars.com/api/?name=Adele+Vance&background=0D8ABC&color=ffffff" : null,
174
+ d || u({
175
+ user: h,
176
+ presence: a ? T : null,
177
+ photoUrl: l ? "https://ui-avatars.com/api/?name=Adele+Vance&background=0D8ABC&color=ffffff" : null,
105
178
  loading: !1,
106
179
  error: null
107
180
  });
108
181
  return;
109
182
  }
110
183
  if (r !== "SignedIn") {
111
- h || d({
184
+ d || u({
112
185
  user: null,
113
186
  presence: null,
114
187
  photoUrl: null,
@@ -118,12 +191,12 @@ const Z = () => new F({ autoSignIn: !0 }), H = (t) => {
118
191
  return;
119
192
  }
120
193
  if (!e) {
121
- h || d((l) => ({ ...l, loading: !0 }));
194
+ d || u((h) => ({ ...h, loading: !0 }));
122
195
  return;
123
196
  }
124
- const g = n || a;
125
- if (!g) {
126
- h || d({
197
+ const S = o || i;
198
+ if (!S) {
199
+ d || u({
127
200
  user: null,
128
201
  presence: null,
129
202
  photoUrl: null,
@@ -132,64 +205,81 @@ const Z = () => new F({ autoSignIn: !0 }), H = (t) => {
132
205
  });
133
206
  return;
134
207
  }
208
+ const E = ne(S), c = s.enabled ? await se(E) : null, b = !!(c?.user && M(c.userCachedAt, s.userTtlMs)), A = !!(c?.presenceCachedAt && M(c.presenceCachedAt, s.presenceTtlMs)), P = !!(c?.photoCachedAt && M(c.photoCachedAt, s.photoTtlMs));
209
+ if (b && (!a || A) && (!l || P)) {
210
+ d || u({
211
+ user: c?.user ?? null,
212
+ presence: a ? c?.presence ?? null : null,
213
+ photoUrl: l ? c?.photoUrl ?? null : null,
214
+ loading: !1,
215
+ error: null
216
+ });
217
+ return;
218
+ }
135
219
  try {
136
- d((f) => ({ ...f, loading: !0, error: null }));
137
- const l = g.toLowerCase() === "me", u = await e.api(l ? "/me" : `/users/${g}`).select("id,displayName,jobTitle,mail,department,officeLocation,userPrincipalName").get();
138
- if (h) return;
139
- let m = null, S = null;
140
- if (i && u.id)
141
- try {
142
- m = await e.api(l ? "/me/presence" : `/users/${u.id}/presence`).get();
143
- } catch (f) {
144
- console.warn("Failed to fetch presence:", f);
220
+ u((k) => ({ ...k, loading: !0, error: null }));
221
+ const h = S.toLowerCase() === "me", T = Date.now();
222
+ let C = !1, y = !1, w = !1, f = b ? c?.user ?? null : null;
223
+ if (f || (f = await e.api(h ? "/me" : `/users/${S}`).select("id,displayName,jobTitle,mail,department,officeLocation,userPrincipalName").get(), C = !0), d) return;
224
+ let x = null, I = null;
225
+ if (a) {
226
+ if (A)
227
+ x = c?.presence ?? null;
228
+ else if (f?.id) {
229
+ try {
230
+ x = await e.api(h ? "/me/presence" : `/users/${f.id}/presence`).get();
231
+ } catch (k) {
232
+ console.warn("Failed to fetch presence:", k);
233
+ }
234
+ y = !0;
145
235
  }
146
- if (c && u.id)
147
- try {
148
- const f = await e.api(l ? "/me/photo/$value" : `/users/${u.id}/photo/$value`).get();
149
- S = URL.createObjectURL(f);
150
- } catch (f) {
151
- console.warn("Failed to fetch photo:", f);
236
+ }
237
+ if (l) {
238
+ if (P)
239
+ I = c?.photoUrl ?? null;
240
+ else if (f?.id) {
241
+ try {
242
+ const k = await e.api(h ? "/me/photo/$value" : `/users/${f.id}/photo/$value`).get();
243
+ I = await le(k);
244
+ } catch (k) {
245
+ console.warn("Failed to fetch photo:", k);
246
+ }
247
+ w = !0;
152
248
  }
153
- h || d({
154
- user: u,
155
- presence: m,
156
- photoUrl: S,
249
+ }
250
+ const p = {
251
+ ...c ?? {}
252
+ };
253
+ C && f && (p.user = f, p.userCachedAt = T), a && (y || A) && (p.presence = y ? x : c?.presence, p.presenceCachedAt = y ? T : c?.presenceCachedAt), l && (w || P) && (p.photoUrl = w ? I : c?.photoUrl, p.photoCachedAt = w ? T : c?.photoCachedAt), s.enabled && (C || y || w) && p.user && await re(E, p), d || u({
254
+ user: f,
255
+ presence: x,
256
+ photoUrl: I,
157
257
  loading: !1,
158
258
  error: null
159
259
  });
160
- } catch (l) {
161
- h || d({
260
+ } catch (h) {
261
+ d || u({
162
262
  user: null,
163
263
  presence: null,
164
264
  photoUrl: null,
165
265
  loading: !1,
166
- error: l
266
+ error: h
167
267
  });
168
268
  }
169
269
  })(), () => {
170
- h = !0;
270
+ d = !0;
171
271
  };
172
272
  }, [
173
273
  e,
174
- s,
175
- r,
176
274
  n,
177
- a,
275
+ r,
276
+ o,
178
277
  i,
179
- c
180
- ]), p;
181
- }, R = (t) => {
182
- switch (t) {
183
- case "small":
184
- return "small";
185
- case "large":
186
- return "large";
187
- case "extra-large":
188
- return "extra-large";
189
- default:
190
- return "medium";
191
- }
192
- }, K = (t) => {
278
+ a,
279
+ l,
280
+ s
281
+ ]), g;
282
+ }, he = (t) => {
193
283
  switch (t?.toLowerCase()) {
194
284
  case "available":
195
285
  case "availableidle":
@@ -204,117 +294,108 @@ const Z = () => new F({ autoSignIn: !0 }), H = (t) => {
204
294
  default:
205
295
  return "offline";
206
296
  }
207
- }, ee = ({
297
+ }, we = ({
208
298
  userId: t,
209
299
  userPrincipalName: e,
210
- email: s,
300
+ email: n,
211
301
  personDetails: r,
212
- view: n = "oneline",
213
- showPresence: a = !1,
214
- avatarSize: i = "medium",
215
- textAlignment: c,
216
- textPosition: p,
217
- numericSize: d,
218
- fetchImage: h = !0,
219
- onClick: T,
220
- className: g,
221
- style: l
302
+ view: s = "oneline",
303
+ showPresence: o = !1,
304
+ fetchImage: i = !0,
305
+ ...a
222
306
  }) => {
223
- const { user: u, presence: m, photoUrl: S, loading: f } = H({
224
- userId: r ? void 0 : t || e || s,
225
- fetchPresence: a,
226
- fetchPhoto: h
227
- }), y = r || u, A = d ?? R(i);
228
- if (f)
229
- return /* @__PURE__ */ w(
230
- C,
307
+ const { user: l, presence: g, photoUrl: u, loading: d } = ue({
308
+ userId: r ? void 0 : t || e || n,
309
+ fetchPresence: o,
310
+ fetchPhoto: i
311
+ }), m = r || l;
312
+ if (d)
313
+ return /* @__PURE__ */ U(
314
+ H,
231
315
  {
232
- size: A,
233
- name: "Loading...",
234
- textAlignment: c,
235
- textPosition: p,
236
- className: g,
237
- style: l
316
+ ...a,
317
+ name: a.name ?? "Loading..."
238
318
  }
239
319
  );
240
- if (!y)
320
+ if (!m)
241
321
  return null;
242
- const b = y.displayName || "Unknown User", L = B(b), k = {
243
- size: A,
244
- name: b,
245
- textAlignment: c,
246
- textPosition: p,
247
- avatar: {
248
- image: S ? { src: S } : void 0,
249
- initials: S ? void 0 : L
250
- },
251
- className: g,
252
- style: l
253
- };
254
- return a && m && (k.presence = {
255
- status: K(m.availability)
256
- }), n !== "avatar" && ((n === "twolines" || n === "threelines") && (k.secondaryText = y.jobTitle ?? void 0), n === "threelines" && (k.tertiaryText = y.department ?? void 0)), T && (k.onClick = () => {
257
- u && T(u);
258
- }), /* @__PURE__ */ w(C, { ...k });
322
+ const S = m.displayName || "Unknown User", E = ce(S), c = s !== "avatar" && (s === "twolines" || s === "threelines" || s === "fourlines") ? m.jobTitle ?? void 0 : void 0, b = s !== "avatar" && (s === "threelines" || s === "fourlines") ? m.department ?? void 0 : void 0, A = s !== "avatar" && s === "fourlines" ? m.officeLocation ?? m.mail ?? void 0 : void 0, P = o && g ? {
323
+ status: he(g.availability)
324
+ } : void 0, h = a.presence ?? P, T = a.avatar ?? {
325
+ image: u ? { src: u } : void 0,
326
+ initials: u ? void 0 : E
327
+ }, C = a.name ?? S, y = a.secondaryText ?? c, w = a.tertiaryText ?? b, f = a.quaternaryText ?? A;
328
+ return /* @__PURE__ */ U(
329
+ H,
330
+ {
331
+ ...a,
332
+ name: C,
333
+ avatar: T,
334
+ presence: h,
335
+ secondaryText: y,
336
+ tertiaryText: w,
337
+ quaternaryText: f
338
+ }
339
+ );
259
340
  };
260
- function V(t) {
341
+ function de(t) {
261
342
  return `See https://aka.ms/msal.js.errors#${t} for details`;
262
343
  }
263
- class I extends Error {
264
- constructor(e, s, r) {
265
- const n = s || (e ? V(e) : ""), a = n ? `${e}: ${n}` : e;
266
- super(a), Object.setPrototypeOf(this, I.prototype), this.errorCode = e || "", this.errorMessage = n || "", this.subError = r || "", this.name = "AuthError";
344
+ class D extends Error {
345
+ constructor(e, n, r) {
346
+ const s = n || (e ? de(e) : ""), o = s ? `${e}: ${s}` : e;
347
+ super(o), Object.setPrototypeOf(this, D.prototype), this.errorCode = e || "", this.errorMessage = s || "", this.subError = r || "", this.name = "AuthError";
267
348
  }
268
349
  setCorrelationId(e) {
269
350
  this.correlationId = e;
270
351
  }
271
352
  }
272
- class P extends I {
273
- constructor(e, s, r, n, a, i, c, p) {
274
- super(e, s, r), Object.setPrototypeOf(this, P.prototype), this.timestamp = n || "", this.traceId = a || "", this.correlationId = i || "", this.claims = c || "", this.name = "InteractionRequiredAuthError", this.errorNo = p;
353
+ class B extends D {
354
+ constructor(e, n, r, s, o, i, a, l) {
355
+ super(e, n, r), Object.setPrototypeOf(this, B.prototype), this.timestamp = s || "", this.traceId = o || "", this.correlationId = i || "", this.claims = a || "", this.name = "InteractionRequiredAuthError", this.errorNo = l;
275
356
  }
276
357
  }
277
- class te {
358
+ class ke {
278
359
  msalInstance;
279
360
  defaultScopes;
280
361
  account = null;
281
- state = o.Loading;
362
+ state = "Loading";
282
363
  listeners = [];
283
- constructor(e, s) {
284
- this.msalInstance = e, this.defaultScopes = s;
364
+ constructor(e, n) {
365
+ this.msalInstance = e, this.defaultScopes = n;
285
366
  }
286
367
  async initialize() {
287
368
  await this.msalInstance.initialize(), await this.msalInstance.handleRedirectPromise();
288
- const e = this.msalInstance.getActiveAccount(), s = this.msalInstance.getAllAccounts();
289
- this.account = e ?? s[0] ?? null, this.account ? (this.msalInstance.setActiveAccount(this.account), this.setState(o.SignedIn)) : this.setState(o.SignedOut);
369
+ const e = this.msalInstance.getActiveAccount(), n = this.msalInstance.getAllAccounts();
370
+ this.account = e ?? n[0] ?? null, this.account ? (this.msalInstance.setActiveAccount(this.account), this.setState("SignedIn")) : this.setState("SignedOut");
290
371
  }
291
372
  async login() {
292
- const e = { scopes: this.defaultScopes }, s = await this.msalInstance.loginPopup(e);
293
- this.account = s.account, this.account && this.msalInstance.setActiveAccount(this.account), this.setState(o.SignedIn);
373
+ const e = { scopes: this.defaultScopes }, n = await this.msalInstance.loginPopup(e);
374
+ this.account = n.account, this.account && this.msalInstance.setActiveAccount(this.account), this.setState("SignedIn");
294
375
  }
295
376
  async logout() {
296
377
  const e = this.msalInstance.getActiveAccount() ?? this.account ?? void 0;
297
- await this.msalInstance.logoutPopup({ account: e }), this.account = null, this.setState(o.SignedOut);
378
+ await this.msalInstance.logoutPopup({ account: e }), this.account = null, this.setState("SignedOut");
298
379
  }
299
380
  async getAccessToken(e) {
300
- const s = e && e.length > 0 ? e : this.defaultScopes, r = this.msalInstance.getActiveAccount() ?? this.account;
381
+ const n = e && e.length > 0 ? e : this.defaultScopes, r = this.msalInstance.getActiveAccount() ?? this.account;
301
382
  if (!r)
302
383
  throw new Error("No active account. User must sign in first.");
303
- const n = {
304
- scopes: s,
384
+ const s = {
385
+ scopes: n,
305
386
  account: r
306
387
  };
307
388
  try {
308
- return (await this.msalInstance.acquireTokenSilent(n)).accessToken;
309
- } catch (a) {
310
- if (a instanceof P) {
389
+ return (await this.msalInstance.acquireTokenSilent(s)).accessToken;
390
+ } catch (o) {
391
+ if (o instanceof B) {
311
392
  const i = {
312
- scopes: s,
393
+ scopes: n,
313
394
  account: r
314
395
  };
315
396
  return (await this.msalInstance.acquireTokenPopup(i)).accessToken;
316
397
  }
317
- throw a;
398
+ throw o;
318
399
  }
319
400
  }
320
401
  getState() {
@@ -324,7 +405,7 @@ class te {
324
405
  this.listeners.push(e);
325
406
  }
326
407
  removeStateChangedHandler(e) {
327
- this.listeners = this.listeners.filter((s) => s !== e);
408
+ this.listeners = this.listeners.filter((n) => n !== e);
328
409
  }
329
410
  setState(e) {
330
411
  this.state = e, this.emit();
@@ -337,65 +418,68 @@ class te {
337
418
  }
338
419
  }
339
420
  }
340
- const J = 6e4;
341
- function se(t) {
421
+ const fe = 6e4;
422
+ function Ce(t) {
342
423
  const e = t.fetchFn ?? fetch;
343
- return async ({ ssoToken: s, scopes: r }) => {
344
- const n = await e(t.endpoint, {
424
+ return async ({
425
+ ssoToken: n,
426
+ scopes: r
427
+ }) => {
428
+ const s = await e(t.endpoint, {
345
429
  method: "POST",
346
430
  headers: {
347
- Authorization: `Bearer ${s}`,
431
+ Authorization: `Bearer ${n}`,
348
432
  "Content-Type": "application/json"
349
433
  },
350
434
  body: JSON.stringify({ scopes: r })
351
- }), a = await n.json().catch(() => ({ error: n.statusText || "Token exchange failed" }));
352
- if (!n.ok) {
353
- const i = a?.error || a?.message || n.statusText;
435
+ }), o = await s.json().catch(() => ({ error: s.statusText || "Token exchange failed" }));
436
+ if (!s.ok) {
437
+ const i = o?.error || o?.message || s.statusText;
354
438
  throw new Error(`Backend token exchange failed: ${i}`);
355
439
  }
356
- if (!a?.accessToken)
440
+ if (!o?.accessToken)
357
441
  throw new Error("Backend token exchange response did not include accessToken.");
358
442
  return {
359
- accessToken: a.accessToken,
360
- expiresAt: a.expiresAt
443
+ accessToken: o.accessToken,
444
+ expiresAt: o.expiresAt
361
445
  };
362
446
  };
363
447
  }
364
- class ne {
448
+ class ve {
365
449
  defaultScopes;
366
450
  getTeamsSsoTokenFn;
367
451
  exchangeForGraphTokenFn;
368
452
  refreshSkewMs;
369
- state = o.SignedOut;
453
+ state = "SignedOut";
370
454
  listeners = [];
371
455
  tokenCache = /* @__PURE__ */ new Map();
372
456
  constructor(e) {
373
457
  if (!e.defaultScopes || e.defaultScopes.length === 0)
374
458
  throw new Error("TeamsHostedProvider requires at least one default scope.");
375
- this.defaultScopes = [...new Set(e.defaultScopes)], this.getTeamsSsoTokenFn = e.getTeamsSsoToken, this.exchangeForGraphTokenFn = e.exchangeForGraphToken, this.refreshSkewMs = e.refreshSkewMs ?? J;
459
+ this.defaultScopes = [...new Set(e.defaultScopes)], this.getTeamsSsoTokenFn = e.getTeamsSsoToken, this.exchangeForGraphTokenFn = e.exchangeForGraphToken, this.refreshSkewMs = e.refreshSkewMs ?? fe;
376
460
  }
377
461
  async login() {
378
462
  try {
379
- await this.getAccessToken(), this.setState(o.SignedIn);
463
+ await this.getAccessToken(), this.setState("SignedIn");
380
464
  } catch (e) {
381
- throw this.clearCache(), this.setState(o.SignedOut), e;
465
+ throw this.clearCache(), this.setState("SignedOut"), e;
382
466
  }
383
467
  }
384
468
  async logout() {
385
- this.clearCache(), this.setState(o.SignedOut);
469
+ this.clearCache(), this.setState("SignedOut");
386
470
  }
387
471
  async getAccessToken(e) {
388
- const s = this.normalizeScopes(e), r = this.buildScopeKey(s), n = this.tokenCache.get(r);
389
- if (n && this.isTokenValid(n))
390
- return n.accessToken;
391
- const a = await this.getTeamsSsoTokenFn(s);
392
- if (!a)
472
+ const n = this.normalizeScopes(e), r = this.buildScopeKey(n), s = this.tokenCache.get(r);
473
+ if (s && this.isTokenValid(s))
474
+ return s.accessToken;
475
+ const o = await this.getTeamsSsoTokenFn(n);
476
+ if (!o)
393
477
  throw new Error("Teams SSO token acquisition returned empty token.");
394
478
  const i = await this.exchangeForGraphTokenFn({
395
- ssoToken: a,
396
- scopes: s
397
- }), c = this.normalizeExchangedToken(i);
398
- return this.tokenCache.set(r, c), this.setState(o.SignedIn), c.accessToken;
479
+ ssoToken: o,
480
+ scopes: n
481
+ }), a = this.normalizeExchangedToken(i);
482
+ return this.tokenCache.set(r, a), this.setState("SignedIn"), a.accessToken;
399
483
  }
400
484
  getState() {
401
485
  return this.state;
@@ -404,10 +488,10 @@ class ne {
404
488
  this.listeners.push(e);
405
489
  }
406
490
  removeStateChangedHandler(e) {
407
- this.listeners = this.listeners.filter((s) => s !== e);
491
+ this.listeners = this.listeners.filter((n) => n !== e);
408
492
  }
409
493
  normalizeScopes(e) {
410
- const s = e && e.length > 0 ? e : this.defaultScopes, r = [...new Set(s.filter(Boolean))];
494
+ const n = e && e.length > 0 ? e : this.defaultScopes, r = [...new Set(n.filter(Boolean))];
411
495
  if (r.length === 0)
412
496
  throw new Error("At least one scope is required to request a Graph token.");
413
497
  return r;
@@ -450,16 +534,16 @@ class ne {
450
534
  }
451
535
  }
452
536
  export {
453
- Y as GraphProvider,
454
- F as MockProvider,
455
- te as MsalBrowserProvider,
456
- ee as Person,
457
- o as ProviderState,
458
- ne as TeamsHostedProvider,
459
- se as createBackendTokenExchange,
460
- Z as createMockSignedInProvider,
461
- D as useGraphClient,
462
- H as usePersonData,
463
- U as useProvider,
464
- O as useProviderState
537
+ Te as GraphProvider,
538
+ z as MockProvider,
539
+ ke as MsalBrowserProvider,
540
+ we as Person,
541
+ ve as TeamsHostedProvider,
542
+ Ce as createBackendTokenExchange,
543
+ ye as createMockSignedInProvider,
544
+ ie as useGraphClient,
545
+ oe as usePersonCacheOptions,
546
+ ue as usePersonData,
547
+ G as useProvider,
548
+ $ as useProviderState
465
549
  };
@@ -1,11 +1,7 @@
1
1
  /**
2
2
  * Provider interface for Microsoft Graph authentication
3
3
  */
4
- export declare enum ProviderState {
5
- Loading = "Loading",
6
- SignedOut = "SignedOut",
7
- SignedIn = "SignedIn"
8
- }
4
+ export type ProviderState = 'Loading' | 'SignedOut' | 'SignedIn';
9
5
  export interface IProvider {
10
6
  /**
11
7
  * Get an access token for the specified scopes
@@ -1 +1 @@
1
- {"version":3,"file":"IProvider.d.ts","sourceRoot":"","sources":["../../src/providers/IProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oBAAY,aAAa;IACvB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,QAAQ,aAAa;CACtB;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnD;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;OAEG;IACH,QAAQ,IAAI,aAAa,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAChD"}
1
+ {"version":3,"file":"IProvider.d.ts","sourceRoot":"","sources":["../../src/providers/IProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnD;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;OAEG;IACH,QAAQ,IAAI,aAAa,CAAC;IAE1B;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"MockProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MockProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvD,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,SAAS,CAAuB;gBAC5B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAMnE,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,IAAI;CASb;AAED,eAAO,MAAM,0BAA0B,oBAA+C,CAAC"}
1
+ {"version":3,"file":"MockProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MockProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvD,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAAuB;gBAC5B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAMnE,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAK7B,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,IAAI;CASb;AAED,eAAO,MAAM,0BAA0B,oBAA+C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"MsalBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MsalBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAIH,uBAAuB,EAE1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEvD,qBAAa,mBAAoB,YAAW,SAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0B;IACvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,SAAS,CAAyB;gBAE9B,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,EAAE;IAKpE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOvB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA8BxD,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;CASf"}
1
+ {"version":3,"file":"MsalBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/MsalBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAIL,uBAAuB,EAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEvD,qBAAa,mBAAoB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0B;IACvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,SAAS,CAAyB;gBAE9B,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,EAAE;IAKpE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOvB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA8BxD,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;CASb"}
@@ -3,9 +3,11 @@
3
3
  */
4
4
  import React, { ReactNode } from 'react';
5
5
  import { IProvider, ProviderState } from './IProvider';
6
+ import { PersonCacheOptions } from '../utils/personCache';
6
7
  export interface GraphProviderProps {
7
8
  provider: IProvider;
8
9
  children: ReactNode;
10
+ personCacheOptions?: Partial<PersonCacheOptions>;
9
11
  }
10
12
  export declare const GraphProvider: React.FC<GraphProviderProps>;
11
13
  /**
@@ -16,4 +18,8 @@ export declare const useProvider: () => IProvider | null;
16
18
  * Hook to access the current provider state
17
19
  */
18
20
  export declare const useProviderState: () => ProviderState;
21
+ /**
22
+ * Hook to access configured person cache options
23
+ */
24
+ export declare const usePersonCacheOptions: () => PersonCacheOptions;
19
25
  //# sourceMappingURL=ProviderContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProviderContext.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderContext.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,EAIZ,SAAS,EAGV,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYvD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA2BtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,QAAO,SAAS,GAAG,IAG1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,aAGnC,CAAC"}
1
+ {"version":3,"file":"ProviderContext.d.ts","sourceRoot":"","sources":["../../src/providers/ProviderContext.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,EAIZ,SAAS,EAGV,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAgC,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAcxF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAClD;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA8CtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,QAAO,SAAS,GAAG,IAG1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAAO,aAGnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAO,kBAGxC,CAAC"}
@@ -24,7 +24,7 @@ export interface CreateBackendTokenExchangeOptions {
24
24
  * Creates a typed callback for exchanging a Teams SSO token with a backend
25
25
  * endpoint that performs OBO and returns a Graph token.
26
26
  */
27
- export declare function createBackendTokenExchange(options: CreateBackendTokenExchangeOptions): ({ ssoToken, scopes }: ExchangeForGraphTokenParams) => Promise<BackendTokenExchangeResponse>;
27
+ export declare function createBackendTokenExchange(options: CreateBackendTokenExchangeOptions): ({ ssoToken, scopes, }: ExchangeForGraphTokenParams) => Promise<BackendTokenExchangeResponse>;
28
28
  /**
29
29
  * Teams-hosted auth provider for Graph Toolkit React.
30
30
  *
@@ -1 +1 @@
1
- {"version":3,"file":"TeamsHostedProvider.d.ts","sourceRoot":"","sources":["../../src/providers/TeamsHostedProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvD,MAAM,WAAW,2BAA2B;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACvC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,qBAAqB,EAAE,CACnB,MAAM,EAAE,2BAA2B,KAClC,OAAO,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA6B,SAAQ,mBAAmB;IACrE,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iCAAiC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AASD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,IAGnE,sBAAsB,2BAA2B,KAAG,OAAO,CAAC,4BAA4B,CAAC,CA4B1G;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,SAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAwC;IAC3E,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAEG;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;gBAEjD,OAAO,EAAE,0BAA0B;IAWzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BxD,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,uBAAuB;IAmB/B,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,IAAI;CASf"}
1
+ {"version":3,"file":"TeamsHostedProvider.d.ts","sourceRoot":"","sources":["../../src/providers/TeamsHostedProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIvD,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,qBAAqB,EAAE,CACrB,MAAM,EAAE,2BAA2B,KAChC,OAAO,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,4BAA6B,SAAQ,mBAAmB;IACvE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AASD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,IAGrE,uBAGX,2BAA2B,KAAG,OAAO,CAAC,4BAA4B,CAAC,CA4BvE;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAW;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAwC;IAC3E,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAEG;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;gBAEjD,OAAO,EAAE,0BAA0B;IAWzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BxD,QAAQ,IAAI,aAAa;IAIzB,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIzC,yBAAyB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAIpD,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,uBAAuB;IAmB/B,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,IAAI;CASb"}
@@ -0,0 +1,24 @@
1
+ import { Presence, User } from '@microsoft/microsoft-graph-types';
2
+ export declare const USER_CACHE_TTL_MS: number;
3
+ export declare const PHOTO_CACHE_TTL_MS: number;
4
+ export declare const PRESENCE_CACHE_TTL_MS: number;
5
+ export interface PersonCacheOptions {
6
+ enabled: boolean;
7
+ userTtlMs: number;
8
+ photoTtlMs: number;
9
+ presenceTtlMs: number;
10
+ }
11
+ export declare const DEFAULT_PERSON_CACHE_OPTIONS: PersonCacheOptions;
12
+ export interface PersonCacheEntry {
13
+ user?: User | null;
14
+ userCachedAt?: number;
15
+ presence?: Presence | null;
16
+ presenceCachedAt?: number;
17
+ photoUrl?: string | null;
18
+ photoCachedAt?: number;
19
+ }
20
+ export declare const getPersonCacheKey: (identifier: string) => string;
21
+ export declare const isTimestampFresh: (timestamp: number | undefined, ttlMs: number) => boolean;
22
+ export declare const readPersonCache: (key: string) => Promise<PersonCacheEntry | null>;
23
+ export declare const writePersonCache: (key: string, value: PersonCacheEntry) => Promise<void>;
24
+ //# sourceMappingURL=personCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personCache.d.ts","sourceRoot":"","sources":["../../src/utils/personCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AAMlE,eAAO,MAAM,iBAAiB,QAAiB,CAAC;AAChD,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AACjD,eAAO,MAAM,qBAAqB,QAAgB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,4BAA4B,EAAE,kBAK1C,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAkCD,eAAO,MAAM,iBAAiB,GAAI,YAAY,MAAM,KAAG,MAEtD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,GAAG,SAAS,EAAE,OAAO,MAAM,KAAG,OAK/E,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAkBlF,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,EAAE,OAAO,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAiBzF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devsym/graph-toolkit-react",
3
- "version": "0.1.0-next.2",
3
+ "version": "1.0.0-next.6",
4
4
  "description": "React components for Microsoft Graph powered by Fluent UI",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -15,6 +15,8 @@
15
15
  "files": [
16
16
  "dist",
17
17
  "README.md",
18
+ "AGENTS.md",
19
+ "MGT_MIGRATION.md",
18
20
  "LICENSE"
19
21
  ],
20
22
  "scripts": {
@@ -22,7 +24,7 @@
22
24
  "dev": "vite build --watch",
23
25
  "changeset": "changeset",
24
26
  "version-packages": "changeset version",
25
- "publish-packages": "changeset publish",
27
+ "publish-packages": "changeset publish --provenance",
26
28
  "type-check": "tsc --noEmit",
27
29
  "lint": "eslint src --ext .ts,.tsx",
28
30
  "lint:fix": "eslint src --ext .ts,.tsx --fix",
@@ -43,8 +45,13 @@
43
45
  ],
44
46
  "author": "",
45
47
  "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/ThomasPe/graph-toolkit-react"
51
+ },
46
52
  "publishConfig": {
47
- "access": "public"
53
+ "access": "public",
54
+ "tag": "next"
48
55
  },
49
56
  "peerDependencies": {
50
57
  "@azure/msal-browser": "^4.0.0 || ^5.0.0",
@@ -91,4 +98,4 @@
91
98
  "vite-plugin-dts": "^4.5.4",
92
99
  "vitest": "^4.0.18"
93
100
  }
94
- }
101
+ }
package/readme.md CHANGED
@@ -13,6 +13,8 @@
13
13
 
14
14
  <h3 align="center">
15
15
  <a href="#getting-started">Get Started</a> •
16
+ <a href="#migration-from-mgt">Migration</a> •
17
+ <a href="#agent-quick-reference">Agent Quick Reference</a> •
16
18
  <a href="#storybook">Storybook</a> •
17
19
  <a href="#contribute">Contribute</a>
18
20
  </h3>
@@ -50,6 +52,32 @@ npm install @devsym/graph-toolkit-react
50
52
  | ------- | ------- | ----------- |
51
53
  | `@devsym/graph-toolkit-react` | `0.1.0-alpha.1` | React components for Microsoft Graph powered by Fluent UI |
52
54
 
55
+ <a id="migration-from-mgt"></a>
56
+ ## 🔄 Migration from MGT
57
+
58
+ For migration from Microsoft Graph Toolkit (including Teams-hosted scenarios), use [`docs/MGT_MIGRATION.md`](./docs/MGT_MIGRATION.md).
59
+
60
+ The guide includes:
61
+
62
+ - Concept mapping (MGT patterns to this package)
63
+ - Step-by-step migration checklist
64
+ - Teams-specific migration section (`TeamsHostedProvider` + backend token exchange)
65
+ - Common migration pitfalls and validation checklist
66
+
67
+ <a id="agent-quick-reference"></a>
68
+ ## 🤖 Agent Quick Reference
69
+
70
+ If you are using a coding agent (Copilot/Codex/Claude/etc.), use the scenario-first reference in [`AGENTS.md`](./AGENTS.md).
71
+
72
+ It includes:
73
+
74
+ - Provider decision matrix by hosting scenario
75
+ - Minimal copy-ready snippets for `MockProvider`, `MsalBrowserProvider`, `TeamsHostedProvider`, and custom `IProvider`
76
+ - Scopes-by-feature mapping
77
+ - Common failures and exact remediation steps
78
+
79
+ For end-to-end browser-hosted setup with MSAL, also see [`samples/react-msal-sample/README.md`](./samples/react-msal-sample/README.md).
80
+
53
81
  ## 🎨 Components
54
82
 
55
83
  Currently available in alpha:
@@ -62,13 +90,17 @@ import { Person } from '@devsym/graph-toolkit-react';
62
90
 
63
91
  <Person
64
92
  userId="user@contoso.com"
65
- view="twoLines"
93
+ view="twolines"
66
94
  showPresence
95
+ presenceOnly={false}
96
+ size="large"
67
97
  textAlignment="center"
68
- onClick={() => console.log('Clicked!')}
98
+ onClick={() => console.log('Persona clicked!')}
69
99
  />
70
100
  ```
71
101
 
102
+ `Person` supports Fluent UI Persona props directly.
103
+
72
104
 
73
105
  <a id="getting-started"></a>
74
106
  ## 🚀 Getting Started
@@ -115,7 +147,7 @@ function App() {
115
147
  return (
116
148
  <FluentProvider theme={webLightTheme}>
117
149
  <GraphProvider provider={provider}>
118
- <Person userId="AdeleV@contoso.com" view="twoLines" showPresence />
150
+ <Person userId="AdeleV@contoso.com" view="twolines" showPresence />
119
151
  </GraphProvider>
120
152
  </FluentProvider>
121
153
  );
@@ -218,7 +250,7 @@ Use explicit mode selection in your app:
218
250
  import { IProvider, ProviderState } from '@devsym/graph-toolkit-react';
219
251
 
220
252
  class MyAuthProvider implements IProvider {
221
- state = ProviderState.SignedOut;
253
+ state: ProviderState = 'SignedOut';
222
254
 
223
255
  async login(): Promise<void> { /* ... */ }
224
256
  async logout(): Promise<void> { /* ... */ }