@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 +167 -0
- package/dist/components/Person/Person.d.ts.map +1 -1
- package/dist/components/Person/Person.types.d.ts +3 -14
- package/dist/components/Person/Person.types.d.ts.map +1 -1
- package/dist/hooks/usePersonData.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +287 -203
- package/dist/providers/IProvider.d.ts +1 -5
- package/dist/providers/IProvider.d.ts.map +1 -1
- package/dist/providers/MockProvider.d.ts.map +1 -1
- package/dist/providers/MsalBrowserProvider.d.ts.map +1 -1
- package/dist/providers/ProviderContext.d.ts +6 -0
- package/dist/providers/ProviderContext.d.ts.map +1 -1
- package/dist/providers/TeamsHostedProvider.d.ts +1 -1
- package/dist/providers/TeamsHostedProvider.d.ts.map +1 -1
- package/dist/utils/personCache.d.ts +24 -0
- package/dist/utils/personCache.d.ts.map +1 -0
- package/package.json +11 -4
- package/readme.md +36 -4
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;
|
|
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 {
|
|
5
|
-
|
|
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,
|
|
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;
|
|
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
|
|
2
|
-
import { Persona as
|
|
3
|
-
import { createContext as
|
|
4
|
-
import { Client as
|
|
5
|
-
|
|
6
|
-
|
|
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:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
),
|
|
14
|
-
(
|
|
15
|
-
const
|
|
16
|
-
typeof
|
|
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
|
-
),
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
},
|
|
25
|
-
const {
|
|
87
|
+
}, oe = () => {
|
|
88
|
+
const { personCacheOptions: t } = N(O);
|
|
26
89
|
return t;
|
|
27
|
-
},
|
|
90
|
+
}, ae = (t) => W.init({
|
|
28
91
|
authProvider: async (e) => {
|
|
29
92
|
try {
|
|
30
|
-
const
|
|
31
|
-
e(null,
|
|
32
|
-
} catch (
|
|
33
|
-
e(
|
|
93
|
+
const n = await t.getAccessToken();
|
|
94
|
+
e(null, n);
|
|
95
|
+
} catch (n) {
|
|
96
|
+
e(n, null);
|
|
34
97
|
}
|
|
35
98
|
}
|
|
36
|
-
}),
|
|
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
|
-
},
|
|
40
|
-
const t =
|
|
41
|
-
return
|
|
102
|
+
}, ie = () => {
|
|
103
|
+
const t = G(), e = $();
|
|
104
|
+
return F(() => !t || e !== "SignedIn" ? null : ae(t), [t, e]);
|
|
42
105
|
};
|
|
43
|
-
class
|
|
44
|
-
state =
|
|
106
|
+
class z {
|
|
107
|
+
state = "SignedOut";
|
|
45
108
|
listeners = [];
|
|
46
109
|
constructor(e) {
|
|
47
|
-
e?.autoSignIn && (this.state =
|
|
110
|
+
e?.autoSignIn && (this.state = "SignedIn");
|
|
48
111
|
}
|
|
49
112
|
getAccessToken() {
|
|
50
|
-
return this.state !==
|
|
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 =
|
|
116
|
+
this.state = "SignedIn", this.emit();
|
|
54
117
|
}
|
|
55
118
|
async logout() {
|
|
56
|
-
this.state =
|
|
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((
|
|
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
|
|
76
|
-
|
|
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
|
|
84
|
-
let
|
|
156
|
+
return J(() => {
|
|
157
|
+
let d = !1;
|
|
85
158
|
return (async () => {
|
|
86
|
-
if (
|
|
87
|
-
const
|
|
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
|
-
},
|
|
168
|
+
}, T = {
|
|
96
169
|
availability: "Available",
|
|
97
170
|
activity: "Available",
|
|
98
|
-
id:
|
|
171
|
+
id: h.id,
|
|
99
172
|
odataType: "#microsoft.graph.presence"
|
|
100
173
|
};
|
|
101
|
-
|
|
102
|
-
user:
|
|
103
|
-
presence:
|
|
104
|
-
photoUrl:
|
|
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
|
-
|
|
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
|
-
|
|
194
|
+
d || u((h) => ({ ...h, loading: !0 }));
|
|
122
195
|
return;
|
|
123
196
|
}
|
|
124
|
-
const
|
|
125
|
-
if (!
|
|
126
|
-
|
|
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
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 (
|
|
161
|
-
|
|
260
|
+
} catch (h) {
|
|
261
|
+
d || u({
|
|
162
262
|
user: null,
|
|
163
263
|
presence: null,
|
|
164
264
|
photoUrl: null,
|
|
165
265
|
loading: !1,
|
|
166
|
-
error:
|
|
266
|
+
error: h
|
|
167
267
|
});
|
|
168
268
|
}
|
|
169
269
|
})(), () => {
|
|
170
|
-
|
|
270
|
+
d = !0;
|
|
171
271
|
};
|
|
172
272
|
}, [
|
|
173
273
|
e,
|
|
174
|
-
s,
|
|
175
|
-
r,
|
|
176
274
|
n,
|
|
177
|
-
|
|
275
|
+
r,
|
|
276
|
+
o,
|
|
178
277
|
i,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
},
|
|
297
|
+
}, we = ({
|
|
208
298
|
userId: t,
|
|
209
299
|
userPrincipalName: e,
|
|
210
|
-
email:
|
|
300
|
+
email: n,
|
|
211
301
|
personDetails: r,
|
|
212
|
-
view:
|
|
213
|
-
showPresence:
|
|
214
|
-
|
|
215
|
-
|
|
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:
|
|
224
|
-
userId: r ? void 0 : t || e ||
|
|
225
|
-
fetchPresence:
|
|
226
|
-
fetchPhoto:
|
|
227
|
-
}),
|
|
228
|
-
if (
|
|
229
|
-
return /* @__PURE__ */
|
|
230
|
-
|
|
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
|
-
|
|
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 (!
|
|
320
|
+
if (!m)
|
|
241
321
|
return null;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
|
341
|
+
function de(t) {
|
|
261
342
|
return `See https://aka.ms/msal.js.errors#${t} for details`;
|
|
262
343
|
}
|
|
263
|
-
class
|
|
264
|
-
constructor(e,
|
|
265
|
-
const
|
|
266
|
-
super(
|
|
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
|
|
273
|
-
constructor(e,
|
|
274
|
-
super(e,
|
|
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
|
|
358
|
+
class ke {
|
|
278
359
|
msalInstance;
|
|
279
360
|
defaultScopes;
|
|
280
361
|
account = null;
|
|
281
|
-
state =
|
|
362
|
+
state = "Loading";
|
|
282
363
|
listeners = [];
|
|
283
|
-
constructor(e,
|
|
284
|
-
this.msalInstance = e, this.defaultScopes =
|
|
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(),
|
|
289
|
-
this.account = e ??
|
|
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 },
|
|
293
|
-
this.account =
|
|
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(
|
|
378
|
+
await this.msalInstance.logoutPopup({ account: e }), this.account = null, this.setState("SignedOut");
|
|
298
379
|
}
|
|
299
380
|
async getAccessToken(e) {
|
|
300
|
-
const
|
|
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
|
|
304
|
-
scopes:
|
|
384
|
+
const s = {
|
|
385
|
+
scopes: n,
|
|
305
386
|
account: r
|
|
306
387
|
};
|
|
307
388
|
try {
|
|
308
|
-
return (await this.msalInstance.acquireTokenSilent(
|
|
309
|
-
} catch (
|
|
310
|
-
if (
|
|
389
|
+
return (await this.msalInstance.acquireTokenSilent(s)).accessToken;
|
|
390
|
+
} catch (o) {
|
|
391
|
+
if (o instanceof B) {
|
|
311
392
|
const i = {
|
|
312
|
-
scopes:
|
|
393
|
+
scopes: n,
|
|
313
394
|
account: r
|
|
314
395
|
};
|
|
315
396
|
return (await this.msalInstance.acquireTokenPopup(i)).accessToken;
|
|
316
397
|
}
|
|
317
|
-
throw
|
|
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((
|
|
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
|
|
341
|
-
function
|
|
421
|
+
const fe = 6e4;
|
|
422
|
+
function Ce(t) {
|
|
342
423
|
const e = t.fetchFn ?? fetch;
|
|
343
|
-
return async ({
|
|
344
|
-
|
|
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 ${
|
|
431
|
+
Authorization: `Bearer ${n}`,
|
|
348
432
|
"Content-Type": "application/json"
|
|
349
433
|
},
|
|
350
434
|
body: JSON.stringify({ scopes: r })
|
|
351
|
-
}),
|
|
352
|
-
if (!
|
|
353
|
-
const i =
|
|
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 (!
|
|
440
|
+
if (!o?.accessToken)
|
|
357
441
|
throw new Error("Backend token exchange response did not include accessToken.");
|
|
358
442
|
return {
|
|
359
|
-
accessToken:
|
|
360
|
-
expiresAt:
|
|
443
|
+
accessToken: o.accessToken,
|
|
444
|
+
expiresAt: o.expiresAt
|
|
361
445
|
};
|
|
362
446
|
};
|
|
363
447
|
}
|
|
364
|
-
class
|
|
448
|
+
class ve {
|
|
365
449
|
defaultScopes;
|
|
366
450
|
getTeamsSsoTokenFn;
|
|
367
451
|
exchangeForGraphTokenFn;
|
|
368
452
|
refreshSkewMs;
|
|
369
|
-
state =
|
|
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 ??
|
|
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(
|
|
463
|
+
await this.getAccessToken(), this.setState("SignedIn");
|
|
380
464
|
} catch (e) {
|
|
381
|
-
throw this.clearCache(), this.setState(
|
|
465
|
+
throw this.clearCache(), this.setState("SignedOut"), e;
|
|
382
466
|
}
|
|
383
467
|
}
|
|
384
468
|
async logout() {
|
|
385
|
-
this.clearCache(), this.setState(
|
|
469
|
+
this.clearCache(), this.setState("SignedOut");
|
|
386
470
|
}
|
|
387
471
|
async getAccessToken(e) {
|
|
388
|
-
const
|
|
389
|
-
if (
|
|
390
|
-
return
|
|
391
|
-
const
|
|
392
|
-
if (!
|
|
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:
|
|
396
|
-
scopes:
|
|
397
|
-
}),
|
|
398
|
-
return this.tokenCache.set(r,
|
|
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((
|
|
491
|
+
this.listeners = this.listeners.filter((n) => n !== e);
|
|
408
492
|
}
|
|
409
493
|
normalizeScopes(e) {
|
|
410
|
-
const
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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;
|
|
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;
|
|
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": "
|
|
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="
|
|
93
|
+
view="twolines"
|
|
66
94
|
showPresence
|
|
95
|
+
presenceOnly={false}
|
|
96
|
+
size="large"
|
|
67
97
|
textAlignment="center"
|
|
68
|
-
onClick={() => console.log('
|
|
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="
|
|
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 =
|
|
253
|
+
state: ProviderState = 'SignedOut';
|
|
222
254
|
|
|
223
255
|
async login(): Promise<void> { /* ... */ }
|
|
224
256
|
async logout(): Promise<void> { /* ... */ }
|