@gw2me/client 0.9.2 → 0.9.4

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.
@@ -0,0 +1 @@
1
+ function e(e){let t=e instanceof ArrayBuffer?new Uint8Array(e):e;return Uint8Array.prototype.toBase64===void 0?btoa(String.fromCharCode(...t)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``):t.toBase64({alphabet:`base64url`,omitPadding:!0})}export{e as t};
package/dist/dpop.d.ts CHANGED
@@ -1,7 +1,25 @@
1
- import { D as DPoPParams } from './types-yBP8cksw.js';
1
+ import { r as DPoPParams } from "./types-Bqy6G8sj.js";
2
2
 
3
+ //#region src/dpop.d.ts
4
+ /**
5
+ * Generates a new key pair for use with DPoP.
6
+ * @see https://gw2.me/dev/docs/access-tokens#dpop
7
+ */
3
8
  declare function generateDPoPKeyPair(): Promise<CryptoKeyPair>;
4
- declare function createDPoPJwt({ htm, htu, nonce, accessToken }: DPoPParams, keyPair: CryptoKeyPair): Promise<string>;
9
+ /**
10
+ * Creates a DPoP proof JWT with the given parameters and key pair.
11
+ * @see https://gw2.me/dev/docs/access-tokens#dpop
12
+ */
13
+ declare function createDPoPJwt({
14
+ htm,
15
+ htu,
16
+ nonce,
17
+ accessToken
18
+ }: DPoPParams, keyPair: CryptoKeyPair): Promise<string>;
19
+ /**
20
+ * Calculates the JWK thumbprint for a given public key.
21
+ * @see https://gw2.me/dev/docs/access-tokens#dpop
22
+ */
5
23
  declare function jwkThumbprint(key: CryptoKey): Promise<string>;
6
-
7
- export { createDPoPJwt, generateDPoPKeyPair, jwkThumbprint };
24
+ //#endregion
25
+ export { createDPoPJwt, generateDPoPKeyPair, jwkThumbprint };
package/dist/dpop.js CHANGED
@@ -1 +1 @@
1
- import{a as e}from"./chunk-EXOYQILJ.js";import"./chunk-OSZ7DOEH.js";function d(){return crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"])}async function m({htm:t,htu:n,nonce:r,accessToken:a},s){let c=JSON.stringify({alg:"ES256",typ:"dpop+jwt",jwk:await u(s.publicKey)}),y=JSON.stringify({iat:Math.floor(Date.now()/1e3),jti:e(crypto.getRandomValues(new Uint8Array(32))),htm:t,htu:n,nonce:r,ath:a?e(await crypto.subtle.digest("SHA-256",o(a))):void 0}),i=`${e(o(c))}.${e(o(y))}`,p={name:"ECDSA",hash:"SHA-256"},g=e(await crypto.subtle.sign(p,s.privateKey,o(i)));return`${i}.${g}`}var w=new TextEncoder;function o(t){return w.encode(t)}async function u(t){let{kty:n,e:r,k:a,n:s,x:c,y,crv:i}=await crypto.subtle.exportKey("jwk",t);return{e:r,k:a,crv:i,kty:n,n:s,x:c,y}}async function P(t){let n=JSON.stringify(await u(t)),r=await crypto.subtle.digest("SHA-256",o(n));return e(r)}export{m as createDPoPJwt,d as generateDPoPKeyPair,P as jwkThumbprint};
1
+ import{t as e}from"./base64-p0iUDSbg.js";function t(){return crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},!1,[`sign`])}async function n({htm:t,htu:n,nonce:r,accessToken:o},s){let c=JSON.stringify({alg:`ES256`,typ:`dpop+jwt`,jwk:await a(s.publicKey)}),l=JSON.stringify({iat:Math.floor(Date.now()/1e3),jti:e(crypto.getRandomValues(new Uint8Array(32))),htm:t,htu:n,nonce:r,ath:o?e(await crypto.subtle.digest(`SHA-256`,i(o))):void 0}),u=`${e(i(c))}.${e(i(l))}`;return`${u}.${e(await crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},s.privateKey,i(u)))}`}const r=new TextEncoder;function i(e){return r.encode(e)}async function a(e){let{kty:t,e:n,k:r,n:i,x:a,y:o,crv:s}=await crypto.subtle.exportKey(`jwk`,e);return{e:n,k:r,crv:s,kty:t,n:i,x:a,y:o}}async function o(t){let n=JSON.stringify(await a(t));return e(await crypto.subtle.digest(`SHA-256`,i(n)))}export{n as createDPoPJwt,t as generateDPoPKeyPair,o as jwkThumbprint};
package/dist/index.d.ts CHANGED
@@ -1,169 +1,246 @@
1
- import { O as Options, a as DPoPCallback, S as Scope, C as ClientInfo } from './types-yBP8cksw.js';
2
- export { D as DPoPParams } from './types-yBP8cksw.js';
1
+ import { a as Scope, i as Options, n as DPoPCallback, r as DPoPParams, t as ClientInfo } from "./types-Bqy6G8sj.js";
3
2
 
3
+ //#region src/api.d.ts
4
4
  interface UserResponse {
5
- sub: string;
6
- user: {
7
- id: string;
8
- name: string;
9
- email?: string;
10
- emailVerified?: boolean;
11
- };
12
- settings?: unknown;
5
+ sub: string;
6
+ user: {
7
+ id: string;
8
+ name: string;
9
+ email?: string;
10
+ emailVerified?: boolean;
11
+ };
12
+ settings?: unknown;
13
13
  }
14
14
  interface AccountsResponse {
15
- accounts: {
16
- id: string;
17
- name: string;
18
- shared: boolean;
19
- verified?: boolean;
20
- displayName?: string | null;
21
- }[];
15
+ accounts: {
16
+ id: string;
17
+ name: string;
18
+ shared: boolean;
19
+ verified?: boolean;
20
+ displayName?: string | null;
21
+ }[];
22
22
  }
23
23
  interface SubtokenOptions {
24
- permissions?: string[];
24
+ permissions?: string[];
25
25
  }
26
26
  interface SubtokenResponse {
27
- subtoken: string;
28
- expiresAt: string;
27
+ subtoken: string;
28
+ expiresAt: string;
29
29
  }
30
30
  interface ApiOptions extends Options {
31
- dpop?: DPoPCallback;
31
+ dpop?: DPoPCallback;
32
32
  }
33
33
  declare class Gw2MeApi {
34
- #private;
35
- private access_token;
36
- private options?;
37
- constructor(access_token: string, options?: Partial<ApiOptions> | undefined);
38
- user(): Promise<UserResponse>;
39
- saveSettings(settings: unknown): Promise<void>;
40
- accounts(): Promise<AccountsResponse>;
41
- subtoken(accountId: string, options?: SubtokenOptions): Promise<SubtokenResponse>;
42
- }
43
-
34
+ #private;
35
+ private access_token;
36
+ private options?;
37
+ constructor(access_token: string, options?: Partial<ApiOptions> | undefined);
38
+ /**
39
+ * Fetches information about the current user. Requires the `identify` scope.
40
+ * @see https://gw2.me/dev/docs/users
41
+ */
42
+ user(): Promise<UserResponse>;
43
+ /**
44
+ * Stores user-specific settings.
45
+ * @see https://gw2.me/dev/docs/users#settings
46
+ */
47
+ saveSettings(settings: unknown): Promise<void>;
48
+ /**
49
+ * Fetches the Guild Wars 2 accounts linked to the current user. Requires the `accounts` scope.
50
+ * @see https://gw2.me/dev/docs/gw2-api#accounts
51
+ */
52
+ accounts(): Promise<AccountsResponse>;
53
+ /**
54
+ * Generates a subtoken that can be used to authenticate to the Guild Wars 2 API.
55
+ * @see https://gw2.me/dev/docs/gw2-api#subtoken
56
+ */
57
+ subtoken(accountId: string, options?: SubtokenOptions): Promise<SubtokenResponse>;
58
+ }
59
+ //#endregion
60
+ //#region src/fed-cm.d.ts
44
61
  interface FedCMRequestOptions {
45
- scopes: Scope[];
46
- mediation?: CredentialMediationRequirement;
47
- mode?: 'passive' | 'active';
48
- signal?: AbortSignal;
49
- code_challenge: string;
50
- code_challenge_method: 'S256';
51
- /** @default true */
52
- include_granted_scopes?: boolean;
62
+ scopes: Scope[];
63
+ mediation?: CredentialMediationRequirement;
64
+ mode?: "passive" | "active";
65
+ signal?: AbortSignal;
66
+ code_challenge: string;
67
+ code_challenge_method: "S256";
68
+ /** @default true */
69
+ include_granted_scopes?: boolean;
53
70
  }
54
71
  declare class Gw2MeFedCM {
55
- #private;
56
- constructor(configUrl: URL, clientId: string);
57
- isSupported(): boolean;
58
- request({ scopes, mediation, signal, mode, code_challenge, code_challenge_method, include_granted_scopes }: FedCMRequestOptions): Promise<null | {
59
- token: string;
60
- type: "identity";
61
- }>;
72
+ #private;
73
+ constructor(configUrl: URL, clientId: string);
74
+ /**
75
+ * Check if FedCM is supported in the current environment.
76
+ */
77
+ isSupported(): boolean;
78
+ /**
79
+ * Start a FedCM sign-in flow with the given options. Resolves with the credential containing the access token on success, or null if the user cancels the flow.
80
+ * @see https://gw2.me/dev/docs/fed-cm
81
+ */
82
+ request({
83
+ scopes,
84
+ mediation,
85
+ signal,
86
+ mode,
87
+ code_challenge,
88
+ code_challenge_method,
89
+ include_granted_scopes
90
+ }: FedCMRequestOptions): Promise<null | {
91
+ token: string;
92
+ type: "identity";
93
+ }>;
62
94
  }
63
-
95
+ //#endregion
96
+ //#region src/client.d.ts
64
97
  interface AuthorizationUrlParams {
65
- redirect_uri: string;
66
- scopes: Scope[];
67
- state?: string;
68
- code_challenge?: string;
69
- code_challenge_method?: 'S256';
70
- dpop_jkt?: string;
71
- prompt?: 'none' | 'consent';
72
- include_granted_scopes?: boolean;
73
- verified_accounts_only?: boolean;
98
+ redirect_uri: string;
99
+ scopes: Scope[];
100
+ state?: string;
101
+ code_challenge?: string;
102
+ code_challenge_method?: "S256";
103
+ dpop_jkt?: string;
104
+ prompt?: "none" | "consent";
105
+ include_granted_scopes?: boolean;
106
+ verified_accounts_only?: boolean;
74
107
  }
75
108
  interface PushedAuthorizationRequestParams extends AuthorizationUrlParams {
76
- dpop?: DPoPCallback;
109
+ dpop?: DPoPCallback;
77
110
  }
78
111
  interface AuthorizationUrlRequestUriParams {
79
- request_uri: string;
112
+ request_uri: string;
80
113
  }
81
- type TokenType = 'Bearer' | 'DPoP';
114
+ type TokenType = "Bearer" | "DPoP";
82
115
  interface AuthTokenParams {
83
- code: string;
84
- token_type?: TokenType;
85
- redirect_uri: string;
86
- code_verifier?: string;
87
- dpop?: DPoPCallback;
116
+ code: string;
117
+ token_type?: TokenType;
118
+ redirect_uri: string;
119
+ code_verifier?: string;
120
+ dpop?: DPoPCallback;
88
121
  }
89
122
  interface RefreshTokenParams {
90
- refresh_token: string;
91
- refresh_token_type?: TokenType;
92
- dpop?: DPoPCallback;
123
+ refresh_token: string;
124
+ refresh_token_type?: TokenType;
125
+ dpop?: DPoPCallback;
93
126
  }
94
127
  interface TokenResponse {
95
- access_token: string;
96
- issued_token_type: 'urn:ietf:params:oauth:token-type:access_token';
97
- token_type: TokenType;
98
- expires_in: number;
99
- refresh_token?: string;
100
- scope: string;
128
+ access_token: string;
129
+ issued_token_type: "urn:ietf:params:oauth:token-type:access_token";
130
+ token_type: TokenType;
131
+ expires_in: number;
132
+ refresh_token?: string;
133
+ scope: string;
101
134
  }
102
135
  interface RevokeTokenParams {
103
- token: string;
136
+ token: string;
104
137
  }
105
138
  interface IntrospectTokenParams {
106
- token: string;
139
+ token: string;
107
140
  }
108
141
  declare namespace IntrospectTokenResponse {
109
- interface Inactive {
110
- active: false;
142
+ interface Inactive {
143
+ active: false;
144
+ }
145
+ namespace Active {
146
+ interface Common {
147
+ active: true;
148
+ scope: string;
149
+ client_id: string;
150
+ exp?: number;
111
151
  }
112
- namespace Active {
113
- interface Common {
114
- active: true;
115
- scope: string;
116
- client_id: string;
117
- exp?: number;
118
- }
119
- interface Bearer extends Common {
120
- token_type: 'Bearer';
121
- }
122
- interface DPoP extends Common {
123
- token_type: 'DPoP';
124
- cnf: {
125
- jkt: string;
126
- };
127
- }
152
+ interface Bearer extends Common {
153
+ token_type: "Bearer";
128
154
  }
129
- type Active = Active.Bearer | Active.DPoP;
155
+ interface DPoP extends Common {
156
+ token_type: "DPoP";
157
+ cnf: {
158
+ jkt: string;
159
+ };
160
+ }
161
+ }
162
+ type Active = Active.Bearer | Active.DPoP;
130
163
  }
131
164
  type IntrospectTokenResponse = IntrospectTokenResponse.Inactive | IntrospectTokenResponse.Active;
132
165
  interface PushedAuthorizationRequestResponse {
133
- request_uri: string;
134
- expires_in: number;
166
+ request_uri: string;
167
+ expires_in: number;
135
168
  }
136
169
  declare class Gw2MeClient {
137
- #private;
138
- private options?;
139
- constructor(client: ClientInfo, options?: Partial<Options> | undefined);
140
- getAuthorizationUrl(params: AuthorizationUrlParams | AuthorizationUrlRequestUriParams): string;
141
- pushAuthorizationRequest(params: PushedAuthorizationRequestParams): Promise<PushedAuthorizationRequestResponse>;
142
- getAccessToken({ code, token_type, redirect_uri, code_verifier, dpop }: AuthTokenParams): Promise<TokenResponse>;
143
- refreshToken({ refresh_token, refresh_token_type, dpop }: RefreshTokenParams): Promise<TokenResponse>;
144
- revokeToken({ token }: RevokeTokenParams): Promise<void>;
145
- introspectToken({ token }: IntrospectTokenParams): Promise<IntrospectTokenResponse>;
146
- /**
147
- * Parses the search params received from gw2.me on the redirect url (code and state).
148
- * If gw2.me returned an error response, this will throw an error.
149
- *
150
- * @returns The code and optional state.
151
- */
152
- parseAuthorizationResponseSearchParams(searchParams: URLSearchParams): {
153
- code: string;
154
- state: string | undefined;
155
- };
156
- api(access_token: string, options?: Partial<Omit<ApiOptions, keyof Options>>): Gw2MeApi;
157
- get fedCM(): Gw2MeFedCM;
158
- }
159
-
160
- declare class Gw2MeError extends Error {
161
- }
170
+ #private;
171
+ private options?;
172
+ constructor(client: ClientInfo, options?: Partial<Options> | undefined);
173
+ /**
174
+ * Generate an authorization URL with the given parameters you can navigate the user to in order to start the authorization code flow.
175
+ * @see https://gw2.me/dev/docs/access-tokens#user-authorization
176
+ */
177
+ getAuthorizationUrl(params: AuthorizationUrlParams | AuthorizationUrlRequestUriParams): string;
178
+ /**
179
+ * Create a pushed authorization request (PAR) with the given parameters.
180
+ * The returned `request_uri` can the be passed to {@link getAuthorizationUrl} to start the authorization code flow.
181
+ * @see https://gw2.me/dev/docs/access-tokens#par
182
+ */
183
+ pushAuthorizationRequest(params: PushedAuthorizationRequestParams): Promise<PushedAuthorizationRequestResponse>;
184
+ /**
185
+ * Exchanges an authorization code for an access token.
186
+ * @see https://gw2.me/dev/docs/access-tokens#access-token
187
+ */
188
+ getAccessToken({
189
+ code,
190
+ token_type,
191
+ redirect_uri,
192
+ code_verifier,
193
+ dpop
194
+ }: AuthTokenParams): Promise<TokenResponse>;
195
+ /**
196
+ * Use a refresh token to get a new access token.
197
+ * @see https://gw2.me/dev/docs/refresh-tokens
198
+ */
199
+ refreshToken({
200
+ refresh_token,
201
+ refresh_token_type,
202
+ dpop
203
+ }: RefreshTokenParams): Promise<TokenResponse>;
204
+ /**
205
+ * Revokes an access or refresh token, making it invalid for further use.
206
+ */
207
+ revokeToken({
208
+ token
209
+ }: RevokeTokenParams): Promise<void>;
210
+ /**
211
+ * Fetch metadata about an access or refresh token.
212
+ */
213
+ introspectToken({
214
+ token
215
+ }: IntrospectTokenParams): Promise<IntrospectTokenResponse>;
216
+ /**
217
+ * Parses the search params received from gw2.me on the redirect url (code and state).
218
+ * If gw2.me returned an error response, this will throw an error.
219
+ *
220
+ * @returns The code and optional state.
221
+ */
222
+ parseAuthorizationResponseSearchParams(searchParams: URLSearchParams): {
223
+ code: string;
224
+ state: string | undefined;
225
+ };
226
+ /**
227
+ * Access the gw2.me API.
228
+ */
229
+ api(access_token: string, options?: Partial<Omit<ApiOptions, keyof Options>>): Gw2MeApi;
230
+ /**
231
+ * Use Federated Credential Management (FedCM) to sign in users.
232
+ * @see https://gw2.me/dev/docs/fed-cm
233
+ */
234
+ get fedCM(): Gw2MeFedCM;
235
+ }
236
+ //#endregion
237
+ //#region src/error.d.ts
238
+ declare class Gw2MeError extends Error {}
162
239
  declare class Gw2MeOAuthError extends Gw2MeError {
163
- error: string;
164
- error_description?: string | undefined;
165
- error_uri?: string | undefined;
166
- constructor(error: string, error_description?: string | undefined, error_uri?: string | undefined);
240
+ error: string;
241
+ error_description?: string | undefined;
242
+ error_uri?: string | undefined;
243
+ constructor(error: string, error_description?: string | undefined, error_uri?: string | undefined);
167
244
  }
168
-
169
- export { type AccountsResponse, type ApiOptions, type AuthTokenParams, type AuthorizationUrlParams, type AuthorizationUrlRequestUriParams, ClientInfo, DPoPCallback, type FedCMRequestOptions, Gw2MeApi, Gw2MeClient, Gw2MeError, Gw2MeFedCM, Gw2MeOAuthError, type IntrospectTokenParams, IntrospectTokenResponse, Options, type PushedAuthorizationRequestParams, type PushedAuthorizationRequestResponse, type RefreshTokenParams, type RevokeTokenParams, Scope, type SubtokenOptions, type SubtokenResponse, type TokenResponse, type TokenType, type UserResponse };
245
+ //#endregion
246
+ export { AccountsResponse, ApiOptions, AuthTokenParams, AuthorizationUrlParams, AuthorizationUrlRequestUriParams, ClientInfo, DPoPCallback, DPoPParams, FedCMRequestOptions, Gw2MeApi, Gw2MeClient, Gw2MeError, Gw2MeFedCM, Gw2MeOAuthError, IntrospectTokenParams, IntrospectTokenResponse, Options, PushedAuthorizationRequestParams, PushedAuthorizationRequestResponse, RefreshTokenParams, RevokeTokenParams, Scope, SubtokenOptions, SubtokenResponse, TokenResponse, TokenType, UserResponse };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a as s,b as m,c as _,d as o}from"./chunk-OSZ7DOEH.js";var z=(p=>(p.Identify="identify",p.Email="email",p.Accounts="accounts",p.Accounts_Verified="accounts.verified",p.Accounts_DisplayName="accounts.displayName",p.GW2_Account="gw2:account",p.GW2_Inventories="gw2:inventories",p.GW2_Characters="gw2:characters",p.GW2_Tradingpost="gw2:tradingpost",p.GW2_Wallet="gw2:wallet",p.GW2_Unlocks="gw2:unlocks",p.GW2_Pvp="gw2:pvp",p.GW2_Wvw="gw2:wvw",p.GW2_Builds="gw2:builds",p.GW2_Progression="gw2:progression",p.GW2_Guilds="gw2:guilds",p))(z||{});var h=class extends Error{},b=class extends h{constructor(t,n,r){super(`Received ${t}`+(n?`: ${n}`:"")+(r?` (${r})`:""));this.error=t;this.error_description=n;this.error_uri=r}};async function f(u){if(await C(u),!(u.headers.get("Content-Type")==="application/json"))throw new h("gw2.me did not return a valid JSON response");return u.json()}async function C(u){if(!u.ok){let e;throw u.headers.get("Content-Type")==="application/json"&&(e=(await u.json()).error_description),new h(`gw2.me returned an error: ${e??"Unknown error"}`)}}var g,U,x,v=class{constructor(e,t){this.access_token=e;this.options=t;m(this,g)}user(){return o(this,g,x).call(this,"api/user").then(e=>fetch(e)).then(f)}saveSettings(e){return o(this,g,x).call(this,"api/user/settings",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(t=>fetch(t)).then(C)}accounts(){return o(this,g,x).call(this,"api/accounts").then(e=>fetch(e)).then(f)}subtoken(e,t){let n=o(this,g,U).call(this,`api/accounts/${e}/subtoken`);return t?.permissions&&n.searchParams.set("permissions",t.permissions.join(",")),o(this,g,x).call(this,n).then(r=>fetch(r)).then(f)}};g=new WeakSet,U=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},x=async function(e,t){let n=e instanceof URL?e:o(this,g,U).call(this,e),r=this.options?.dpop,i=new Headers(t?.headers);return i.set("Authorization",`${r?"DPoP":"Bearer"} ${this.access_token}`),r&&i.set("DPoP",await r({htm:t?.method??"GET",htu:n.toString(),accessToken:this.access_token})),new Request(n,{cache:"no-cache",...t,headers:i})};var R,T,O=class{constructor(e,t){m(this,R);m(this,T);_(this,R,e),_(this,T,t)}isSupported(){return typeof window<"u"&&"IdentityCredential"in window}request({scopes:e,mediation:t,signal:n,mode:r,code_challenge:i,code_challenge_method:d,include_granted_scopes:l}){if(!this.isSupported())throw new h("FedCM is not supported");return navigator.credentials.get({mediation:t,signal:n,identity:{providers:[{configURL:s(this,R),clientId:s(this,T),fields:[e.includes("identify")&&"name",e.includes("email")&&"email"].filter(Boolean),nonce:`${d}:${i}`,params:{scope:e.join(" "),code_challenge:i,code_challenge_method:d,include_granted_scopes:l}}],mode:r}})}};R=new WeakMap,T=new WeakMap;var a,A,c,P,y,S=class{constructor(e,t){this.options=t;m(this,c);m(this,a);m(this,A);_(this,a,e),_(this,A,new O(o(this,c,P).call(this,"/fed-cm/config.json"),e.client_id))}getAuthorizationUrl(e){let t="request_uri"in e?new URLSearchParams({client_id:s(this,a).client_id,response_type:"code",request_uri:e.request_uri}):q(s(this,a).client_id,e);return o(this,c,P).call(this,`/oauth2/authorize?${t.toString()}`).toString()}async pushAuthorizationRequest(e){let t=o(this,c,P).call(this,"/oauth2/par"),n={"Content-Type":"application/x-www-form-urlencoded"};e.dpop&&(n.DPoP=await e.dpop({htm:"POST",htu:t.toString()}));let r=q(s(this,a).client_id,e);return s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(t,{method:"POST",headers:n,body:r,cache:"no-store"}).then(f)}async getAccessToken({code:e,token_type:t,redirect_uri:n,code_verifier:r,dpop:i}){let d=new URLSearchParams({grant_type:"authorization_code",code:e,client_id:s(this,a).client_id,redirect_uri:n}),l={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(l.Authorization=o(this,c,y).call(this)),r&&d.set("code_verifier",r);let w=o(this,c,P).call(this,"/api/token");return i&&(l.DPoP=await i({htm:"POST",htu:w.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(w,{method:"POST",headers:l,body:d,cache:"no-store"}).then(f)}async refreshToken({refresh_token:e,refresh_token_type:t,dpop:n}){let r=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:s(this,a).client_id}),i={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(i.Authorization=o(this,c,y).call(this));let d=o(this,c,P).call(this,"/api/token");return n&&(i.DPoP=await n({htm:"POST",htu:d.toString(),accessToken:t==="DPoP"?e:void 0})),await fetch(d,{method:"POST",headers:i,body:r,cache:"no-store"}).then(f)}async revokeToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":"application/x-www-form-urlencoded"};s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(o(this,c,P).call(this,"/api/token/revoke"),{method:"POST",cache:"no-store",headers:n,body:t}).then(f)}async introspectToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":"application/x-www-form-urlencoded"};return s(this,a).client_secret&&(n.Authorization=o(this,c,y).call(this)),await fetch(o(this,c,P).call(this,"/api/token/introspect"),{method:"POST",cache:"no-store",headers:n,body:t}).then(f)}parseAuthorizationResponseSearchParams(e){let t=o(this,c,P).call(this,"/").origin,n=e.get("iss");if(!n)throw new h("Issuer Identifier verification failed: parameter `iss` is missing");if(n!==t)throw new h(`Issuer Identifier verification failed: expected "${t}", got "${n}"`);let r=e.get("error");if(r){let l=e.get("error_description")??void 0,w=e.get("error_uri")??void 0;throw new b(r,l,w)}let i=e.get("code");if(!i)throw new h("Parameter `code` is missing");let d=e.get("state")||void 0;return{code:i,state:d}}api(e,t){return new v(e,{...this.options,...t})}get fedCM(){return s(this,A)}};a=new WeakMap,A=new WeakMap,c=new WeakSet,P=function(e){return new URL(e,this.options?.url||"https://gw2.me/")},y=function(){if(!s(this,a).client_secret)throw new h("client_secret is required");return`Basic ${btoa(`${s(this,a).client_id}:${s(this,a).client_secret}`)}`};function q(u,{redirect_uri:e,scopes:t,state:n,code_challenge:r,code_challenge_method:i,dpop_jkt:d,prompt:l,include_granted_scopes:w,verified_accounts_only:I}){let k=new URLSearchParams({client_id:u,response_type:"code",redirect_uri:e,scope:t.join(" ")});return n&&k.append("state",n),r&&i&&(k.append("code_challenge",r),k.append("code_challenge_method",i)),d&&k.append("dpop_jkt",d),l&&k.append("prompt",l),w&&k.append("include_granted_scopes","true"),I&&k.append("verified_accounts_only","true"),k}export{v as Gw2MeApi,S as Gw2MeClient,h as Gw2MeError,b as Gw2MeOAuthError,z as Scope};
1
+ let e=function(e){return e.Identify=`identify`,e.Email=`email`,e.Accounts=`accounts`,e.Accounts_Verified=`accounts.verified`,e.Accounts_DisplayName=`accounts.displayName`,e.GW2_Account=`gw2:account`,e.GW2_Inventories=`gw2:inventories`,e.GW2_Characters=`gw2:characters`,e.GW2_Tradingpost=`gw2:tradingpost`,e.GW2_Wallet=`gw2:wallet`,e.GW2_Unlocks=`gw2:unlocks`,e.GW2_Pvp=`gw2:pvp`,e.GW2_Wvw=`gw2:wvw`,e.GW2_Builds=`gw2:builds`,e.GW2_Progression=`gw2:progression`,e.GW2_Guilds=`gw2:guilds`,e}({});var t=class extends Error{},n=class extends t{constructor(e,t,n){super(`Received ${e}`+(t?`: ${t}`:``)+(n?` (${n})`:``)),this.error=e,this.error_description=t,this.error_uri=n}};async function r(e){if(await i(e),!a(e))throw new t(`gw2.me did not return a valid JSON response`);return e.json()}async function i(e){if(!e.ok){let n;throw a(e)&&(n=(await e.json()).error_description),new t(`gw2.me returned an error: ${n??`Unknown error`}`)}}function a(e){return e.headers.get(`Content-Type`)?.split(`;`)[0].trim().toLowerCase()===`application/json`}var o=class{constructor(e,t){this.access_token=e,this.options=t}user(){return this.#t(`api/user`).then(e=>fetch(e)).then(r)}saveSettings(e){return this.#t(`api/user/settings`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)}).then(e=>fetch(e)).then(i)}accounts(){return this.#t(`api/accounts`).then(e=>fetch(e)).then(r)}subtoken(e,t){let n=this.#e(`api/accounts/${e}/subtoken`);return t?.permissions&&n.searchParams.set(`permissions`,t.permissions.join(`,`)),this.#t(n).then(e=>fetch(e)).then(r)}#e(e){return new URL(e,this.options?.url||`https://gw2.me/`)}async#t(e,t){let n=e instanceof URL?e:this.#e(e),r=this.options?.dpop,i=new Headers(t?.headers);return i.set(`Authorization`,`${r?`DPoP`:`Bearer`} ${this.access_token}`),r&&i.set(`DPoP`,await r({htm:t?.method??`GET`,htu:n.toString(),accessToken:this.access_token})),new Request(n,{cache:`no-cache`,...t,headers:i})}},s=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}isSupported(){return typeof window<`u`&&`IdentityCredential`in window}request({scopes:n,mediation:r,signal:i,mode:a,code_challenge:o,code_challenge_method:s,include_granted_scopes:c}){if(!this.isSupported())throw new t(`FedCM is not supported`);return navigator.credentials.get({mediation:r,signal:i,identity:{providers:[{configURL:this.#e,clientId:this.#t,fields:[n.includes(e.Identify)&&`name`,n.includes(e.Email)&&`email`].filter(Boolean),nonce:`${s}:${o}`,params:{scope:n.join(` `),code_challenge:o,code_challenge_method:s,include_granted_scopes:c}}],mode:a}})}},c=class{#e;#t;constructor(e,t){this.options=t,this.#e=e,this.#t=new s(this.#n(`/fed-cm/config.json`),e.client_id)}#n(e){return new URL(e,this.options?.url||`https://gw2.me/`)}#r(){if(!this.#e.client_secret)throw new t(`client_secret is required`);return`Basic ${btoa(`${this.#e.client_id}:${this.#e.client_secret}`)}`}getAuthorizationUrl(e){let t=`request_uri`in e?new URLSearchParams({client_id:this.#e.client_id,response_type:`code`,request_uri:e.request_uri}):l(this.#e.client_id,e);return this.#n(`/oauth2/authorize?${t.toString()}`).toString()}async pushAuthorizationRequest(e){let t=this.#n(`/oauth2/par`),n={"Content-Type":`application/x-www-form-urlencoded`};e.dpop&&(n.DPoP=await e.dpop({htm:`POST`,htu:t.toString()}));let i=l(this.#e.client_id,e);return this.#e.client_secret&&(n.Authorization=this.#r()),await fetch(t,{method:`POST`,headers:n,body:i,cache:`no-store`}).then(r)}async getAccessToken({code:e,token_type:t,redirect_uri:n,code_verifier:i,dpop:a}){let o=new URLSearchParams({grant_type:`authorization_code`,code:e,client_id:this.#e.client_id,redirect_uri:n}),s={"Content-Type":`application/x-www-form-urlencoded`};this.#e.client_secret&&(s.Authorization=this.#r()),i&&o.set(`code_verifier`,i);let c=this.#n(`/api/token`);return a&&(s.DPoP=await a({htm:`POST`,htu:c.toString(),accessToken:t===`DPoP`?e:void 0})),await fetch(c,{method:`POST`,headers:s,body:o,cache:`no-store`}).then(r)}async refreshToken({refresh_token:e,refresh_token_type:t,dpop:n}){let i=new URLSearchParams({grant_type:`refresh_token`,refresh_token:e,client_id:this.#e.client_id}),a={"Content-Type":`application/x-www-form-urlencoded`};this.#e.client_secret&&(a.Authorization=this.#r());let o=this.#n(`/api/token`);return n&&(a.DPoP=await n({htm:`POST`,htu:o.toString(),accessToken:t===`DPoP`?e:void 0})),await fetch(o,{method:`POST`,headers:a,body:i,cache:`no-store`}).then(r)}async revokeToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":`application/x-www-form-urlencoded`};this.#e.client_secret&&(n.Authorization=this.#r()),await fetch(this.#n(`/api/token/revoke`),{method:`POST`,cache:`no-store`,headers:n,body:t}).then(r)}async introspectToken({token:e}){let t=new URLSearchParams({token:e}),n={"Content-Type":`application/x-www-form-urlencoded`};return this.#e.client_secret&&(n.Authorization=this.#r()),await fetch(this.#n(`/api/token/introspect`),{method:`POST`,cache:`no-store`,headers:n,body:t}).then(r)}parseAuthorizationResponseSearchParams(e){let r=this.#n(`/`).origin,i=e.get(`iss`);if(!i)throw new t("Issuer Identifier verification failed: parameter `iss` is missing");if(i!==r)throw new t(`Issuer Identifier verification failed: expected "${r}", got "${i}"`);let a=e.get(`error`);if(a)throw new n(a,e.get(`error_description`)??void 0,e.get(`error_uri`)??void 0);let o=e.get(`code`);if(!o)throw new t("Parameter `code` is missing");return{code:o,state:e.get(`state`)||void 0}}api(e,t){return new o(e,{...this.options,...t})}get fedCM(){return this.#t}};function l(e,{redirect_uri:t,scopes:n,state:r,code_challenge:i,code_challenge_method:a,dpop_jkt:o,prompt:s,include_granted_scopes:c,verified_accounts_only:l}){let u=new URLSearchParams({client_id:e,response_type:`code`,redirect_uri:t,scope:n.join(` `)});return r&&u.append(`state`,r),i&&a&&(u.append(`code_challenge`,i),u.append(`code_challenge_method`,a)),o&&u.append(`dpop_jkt`,o),s&&u.append(`prompt`,s),c&&u.append(`include_granted_scopes`,`true`),l&&u.append(`verified_accounts_only`,`true`),u}export{o as Gw2MeApi,c as Gw2MeClient,t as Gw2MeError,n as Gw2MeOAuthError,e as Scope};
package/dist/pkce.d.ts CHANGED
@@ -1,11 +1,16 @@
1
+ //#region src/pkce.d.ts
1
2
  interface PKCEChallenge {
2
- code_challenge: string;
3
- code_challenge_method: 'S256';
3
+ code_challenge: string;
4
+ code_challenge_method: "S256";
4
5
  }
5
6
  interface PKCEPair {
6
- challenge: PKCEChallenge;
7
- code_verifier: string;
7
+ challenge: PKCEChallenge;
8
+ code_verifier: string;
8
9
  }
10
+ /**
11
+ * Generates a PKCE pair containing a random code verifier and a corresponding code challenge.
12
+ * @see https://gw2.me/dev/docs/access-tokens#pkce
13
+ */
9
14
  declare function generatePKCEPair(): Promise<PKCEPair>;
10
-
11
- export { type PKCEChallenge, type PKCEPair, generatePKCEPair };
15
+ //#endregion
16
+ export { PKCEChallenge, PKCEPair, generatePKCEPair };
package/dist/pkce.js CHANGED
@@ -1 +1 @@
1
- import{a as e}from"./chunk-EXOYQILJ.js";import"./chunk-OSZ7DOEH.js";async function a(){let n=new Uint8Array(32);crypto.getRandomValues(n);let r=e(n),c=new TextEncoder,o=await crypto.subtle.digest("SHA-256",c.encode(r));return{code_verifier:r,challenge:{code_challenge_method:"S256",code_challenge:e(new Uint8Array(o))}}}export{a as generatePKCEPair};
1
+ import{t as e}from"./base64-p0iUDSbg.js";async function t(){let t=new Uint8Array(32);crypto.getRandomValues(t);let n=e(t),r=new TextEncoder,i=await crypto.subtle.digest(`SHA-256`,r.encode(n));return{code_verifier:n,challenge:{code_challenge_method:`S256`,code_challenge:e(new Uint8Array(i))}}}export{t as generatePKCEPair};
@@ -0,0 +1,39 @@
1
+ //#region src/types.d.ts
2
+ /**
3
+ * Scopes supported by gw2.me.
4
+ * @see https://gw2.me/dev/docs/scopes
5
+ */
6
+ declare enum Scope {
7
+ Identify = "identify",
8
+ Email = "email",
9
+ Accounts = "accounts",
10
+ Accounts_Verified = "accounts.verified",
11
+ Accounts_DisplayName = "accounts.displayName",
12
+ GW2_Account = "gw2:account",
13
+ GW2_Inventories = "gw2:inventories",
14
+ GW2_Characters = "gw2:characters",
15
+ GW2_Tradingpost = "gw2:tradingpost",
16
+ GW2_Wallet = "gw2:wallet",
17
+ GW2_Unlocks = "gw2:unlocks",
18
+ GW2_Pvp = "gw2:pvp",
19
+ GW2_Wvw = "gw2:wvw",
20
+ GW2_Builds = "gw2:builds",
21
+ GW2_Progression = "gw2:progression",
22
+ GW2_Guilds = "gw2:guilds"
23
+ }
24
+ interface ClientInfo {
25
+ client_id: string;
26
+ client_secret?: string;
27
+ }
28
+ interface Options {
29
+ url: string;
30
+ }
31
+ interface DPoPParams {
32
+ htm: "POST" | "GET" | (string & {});
33
+ htu: string;
34
+ nonce?: string;
35
+ accessToken?: string;
36
+ }
37
+ type DPoPCallback = (params: DPoPParams) => string | Promise<string>;
38
+ //#endregion
39
+ export { Scope as a, Options as i, DPoPCallback as n, DPoPParams as r, ClientInfo as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gw2me/client",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "gw2.me client library",
5
5
  "type": "module",
6
6
  "exports": {
@@ -30,8 +30,10 @@
30
30
  "oauth2"
31
31
  ],
32
32
  "files": [
33
- "dist/"
33
+ "dist/*.js",
34
+ "dist/*.d.ts"
34
35
  ],
36
+ "sideEffects": false,
35
37
  "author": "darthmaim",
36
38
  "license": "MIT",
37
39
  "bugs": {
@@ -39,24 +41,24 @@
39
41
  },
40
42
  "homepage": "https://github.com/gw2treasures/gw2.me#readme",
41
43
  "devDependencies": {
42
- "@gw2treasures/eslint-config": "0.2.0",
44
+ "@arethetypeswrong/core": "0.18.2",
45
+ "@gw2treasures/eslint-config": "0.2.1",
43
46
  "@gw2treasures/publish-package": "0.1.0",
44
47
  "@gw2treasures/tsconfig": "0.0.1",
45
- "eslint": "9.39.2",
46
- "tsup": "8.5.1",
48
+ "@types/node": "24.12.0",
49
+ "eslint": "9.39.4",
50
+ "publint": "0.3.18",
51
+ "tsdown": "0.21.4",
47
52
  "typescript": "5.9.3",
48
- "typescript-eslint": "8.51.0",
49
- "undici-types": "7.16.0"
53
+ "typescript-eslint": "8.57.1",
54
+ "undici-types": "7.24.4"
50
55
  },
51
56
  "publishConfig": {
52
57
  "access": "public",
53
58
  "provenance": true
54
59
  },
55
60
  "scripts": {
56
- "build": "pnpm run clean && if test $CI; then pnpm run build:ci; else pnpm run build:local; fi",
57
- "build:local": "tsup src/index.ts src/dpop.ts src/pkce.ts --format esm && tsc --emitDeclarationOnly --declaration --declarationMap",
58
- "build:ci": "tsup src/index.ts src/dpop.ts src/pkce.ts --format esm --minify --dts",
59
- "clean": "rm -rf dist/",
61
+ "build": "tsdown src/index.ts src/dpop.ts src/pkce.ts",
60
62
  "lint": "eslint .",
61
63
  "publish-package": "gw2treasures-publish-package"
62
64
  }
@@ -1 +0,0 @@
1
- function a(r){let e=r instanceof ArrayBuffer?new Uint8Array(r):r;return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}export{a};
@@ -1 +0,0 @@
1
- var f=a=>{throw TypeError(a)};var d=(a,b,c)=>b.has(a)||f("Cannot "+c);var g=(a,b,c)=>(d(a,b,"read from private field"),c?c.call(a):b.get(a)),h=(a,b,c)=>b.has(a)?f("Cannot add the same private member more than once"):b instanceof WeakSet?b.add(a):b.set(a,c),i=(a,b,c,e)=>(d(a,b,"write to private field"),e?e.call(a,c):b.set(a,c),c),j=(a,b,c)=>(d(a,b,"access private method"),c);export{g as a,h as b,i as c,j as d};
@@ -1,34 +0,0 @@
1
- declare enum Scope {
2
- Identify = "identify",
3
- Email = "email",
4
- Accounts = "accounts",
5
- Accounts_Verified = "accounts.verified",
6
- Accounts_DisplayName = "accounts.displayName",
7
- GW2_Account = "gw2:account",
8
- GW2_Inventories = "gw2:inventories",
9
- GW2_Characters = "gw2:characters",
10
- GW2_Tradingpost = "gw2:tradingpost",
11
- GW2_Wallet = "gw2:wallet",
12
- GW2_Unlocks = "gw2:unlocks",
13
- GW2_Pvp = "gw2:pvp",
14
- GW2_Wvw = "gw2:wvw",
15
- GW2_Builds = "gw2:builds",
16
- GW2_Progression = "gw2:progression",
17
- GW2_Guilds = "gw2:guilds"
18
- }
19
- interface ClientInfo {
20
- client_id: string;
21
- client_secret?: string;
22
- }
23
- interface Options {
24
- url: string;
25
- }
26
- interface DPoPParams {
27
- htm: 'POST' | 'GET' | (string & {});
28
- htu: string;
29
- nonce?: string;
30
- accessToken?: string;
31
- }
32
- type DPoPCallback = (params: DPoPParams) => string | Promise<string>;
33
-
34
- export { type ClientInfo as C, type DPoPParams as D, type Options as O, Scope as S, type DPoPCallback as a };