@gw2me/client 0.9.3 → 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 ADDED
@@ -0,0 +1,25 @@
1
+ import { r as DPoPParams } from "./types-Bqy6G8sj.js";
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
+ */
8
+ declare function generateDPoPKeyPair(): Promise<CryptoKeyPair>;
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
+ */
23
+ declare function jwkThumbprint(key: CryptoKey): Promise<string>;
24
+ //#endregion
25
+ export { createDPoPJwt, generateDPoPKeyPair, jwkThumbprint };
package/dist/dpop.js ADDED
@@ -0,0 +1 @@
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};
@@ -1,4 +1,4 @@
1
- import { a as Scope, i as Options, n as DPoPCallback, r as DPoPParams, t as ClientInfo } from "./types-cXKOlCJ4.mjs";
1
+ import { a as Scope, i as Options, n as DPoPCallback, r as DPoPParams, t as ClientInfo } from "./types-Bqy6G8sj.js";
2
2
 
3
3
  //#region src/api.d.ts
4
4
  interface UserResponse {
@@ -35,9 +35,25 @@ declare class Gw2MeApi {
35
35
  private access_token;
36
36
  private options?;
37
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
+ */
38
42
  user(): Promise<UserResponse>;
43
+ /**
44
+ * Stores user-specific settings.
45
+ * @see https://gw2.me/dev/docs/users#settings
46
+ */
39
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
+ */
40
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
+ */
41
57
  subtoken(accountId: string, options?: SubtokenOptions): Promise<SubtokenResponse>;
42
58
  }
43
59
  //#endregion
@@ -45,17 +61,24 @@ declare class Gw2MeApi {
45
61
  interface FedCMRequestOptions {
46
62
  scopes: Scope[];
47
63
  mediation?: CredentialMediationRequirement;
48
- mode?: 'passive' | 'active';
64
+ mode?: "passive" | "active";
49
65
  signal?: AbortSignal;
50
66
  code_challenge: string;
51
- code_challenge_method: 'S256';
67
+ code_challenge_method: "S256";
52
68
  /** @default true */
53
69
  include_granted_scopes?: boolean;
54
70
  }
55
71
  declare class Gw2MeFedCM {
56
72
  #private;
57
73
  constructor(configUrl: URL, clientId: string);
74
+ /**
75
+ * Check if FedCM is supported in the current environment.
76
+ */
58
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
+ */
59
82
  request({
60
83
  scopes,
61
84
  mediation,
@@ -76,9 +99,9 @@ interface AuthorizationUrlParams {
76
99
  scopes: Scope[];
77
100
  state?: string;
78
101
  code_challenge?: string;
79
- code_challenge_method?: 'S256';
102
+ code_challenge_method?: "S256";
80
103
  dpop_jkt?: string;
81
- prompt?: 'none' | 'consent';
104
+ prompt?: "none" | "consent";
82
105
  include_granted_scopes?: boolean;
83
106
  verified_accounts_only?: boolean;
84
107
  }
@@ -88,7 +111,7 @@ interface PushedAuthorizationRequestParams extends AuthorizationUrlParams {
88
111
  interface AuthorizationUrlRequestUriParams {
89
112
  request_uri: string;
90
113
  }
91
- type TokenType = 'Bearer' | 'DPoP';
114
+ type TokenType = "Bearer" | "DPoP";
92
115
  interface AuthTokenParams {
93
116
  code: string;
94
117
  token_type?: TokenType;
@@ -103,7 +126,7 @@ interface RefreshTokenParams {
103
126
  }
104
127
  interface TokenResponse {
105
128
  access_token: string;
106
- issued_token_type: 'urn:ietf:params:oauth:token-type:access_token';
129
+ issued_token_type: "urn:ietf:params:oauth:token-type:access_token";
107
130
  token_type: TokenType;
108
131
  expires_in: number;
109
132
  refresh_token?: string;
@@ -127,10 +150,10 @@ declare namespace IntrospectTokenResponse {
127
150
  exp?: number;
128
151
  }
129
152
  interface Bearer extends Common {
130
- token_type: 'Bearer';
153
+ token_type: "Bearer";
131
154
  }
132
155
  interface DPoP extends Common {
133
- token_type: 'DPoP';
156
+ token_type: "DPoP";
134
157
  cnf: {
135
158
  jkt: string;
136
159
  };
@@ -147,8 +170,21 @@ declare class Gw2MeClient {
147
170
  #private;
148
171
  private options?;
149
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
+ */
150
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
+ */
151
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
+ */
152
188
  getAccessToken({
153
189
  code,
154
190
  token_type,
@@ -156,28 +192,45 @@ declare class Gw2MeClient {
156
192
  code_verifier,
157
193
  dpop
158
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
+ */
159
199
  refreshToken({
160
200
  refresh_token,
161
201
  refresh_token_type,
162
202
  dpop
163
203
  }: RefreshTokenParams): Promise<TokenResponse>;
204
+ /**
205
+ * Revokes an access or refresh token, making it invalid for further use.
206
+ */
164
207
  revokeToken({
165
208
  token
166
209
  }: RevokeTokenParams): Promise<void>;
210
+ /**
211
+ * Fetch metadata about an access or refresh token.
212
+ */
167
213
  introspectToken({
168
214
  token
169
215
  }: IntrospectTokenParams): Promise<IntrospectTokenResponse>;
170
216
  /**
171
- * Parses the search params received from gw2.me on the redirect url (code and state).
172
- * If gw2.me returned an error response, this will throw an error.
173
- *
174
- * @returns The code and optional state.
175
- */
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
+ */
176
222
  parseAuthorizationResponseSearchParams(searchParams: URLSearchParams): {
177
223
  code: string;
178
224
  state: string | undefined;
179
225
  };
226
+ /**
227
+ * Access the gw2.me API.
228
+ */
180
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
+ */
181
234
  get fedCM(): Gw2MeFedCM;
182
235
  }
183
236
  //#endregion
@@ -190,5 +243,4 @@ declare class Gw2MeOAuthError extends Gw2MeError {
190
243
  constructor(error: string, error_description?: string | undefined, error_uri?: string | undefined);
191
244
  }
192
245
  //#endregion
193
- 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 };
194
- //# sourceMappingURL=index.d.mts.map
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 ADDED
@@ -0,0 +1 @@
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 ADDED
@@ -0,0 +1,16 @@
1
+ //#region src/pkce.d.ts
2
+ interface PKCEChallenge {
3
+ code_challenge: string;
4
+ code_challenge_method: "S256";
5
+ }
6
+ interface PKCEPair {
7
+ challenge: PKCEChallenge;
8
+ code_verifier: string;
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
+ */
14
+ declare function generatePKCEPair(): Promise<PKCEPair>;
15
+ //#endregion
16
+ export { PKCEChallenge, PKCEPair, generatePKCEPair };
package/dist/pkce.js ADDED
@@ -0,0 +1 @@
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};
@@ -1,4 +1,8 @@
1
1
  //#region src/types.d.ts
2
+ /**
3
+ * Scopes supported by gw2.me.
4
+ * @see https://gw2.me/dev/docs/scopes
5
+ */
2
6
  declare enum Scope {
3
7
  Identify = "identify",
4
8
  Email = "email",
@@ -15,7 +19,7 @@ declare enum Scope {
15
19
  GW2_Wvw = "gw2:wvw",
16
20
  GW2_Builds = "gw2:builds",
17
21
  GW2_Progression = "gw2:progression",
18
- GW2_Guilds = "gw2:guilds",
22
+ GW2_Guilds = "gw2:guilds"
19
23
  }
20
24
  interface ClientInfo {
21
25
  client_id: string;
@@ -25,12 +29,11 @@ interface Options {
25
29
  url: string;
26
30
  }
27
31
  interface DPoPParams {
28
- htm: 'POST' | 'GET' | (string & {});
32
+ htm: "POST" | "GET" | (string & {});
29
33
  htu: string;
30
34
  nonce?: string;
31
35
  accessToken?: string;
32
36
  }
33
37
  type DPoPCallback = (params: DPoPParams) => string | Promise<string>;
34
38
  //#endregion
35
- export { Scope as a, Options as i, DPoPCallback as n, DPoPParams as r, ClientInfo as t };
36
- //# sourceMappingURL=types-cXKOlCJ4.d.mts.map
39
+ export { Scope as a, Options as i, DPoPCallback as n, DPoPParams as r, ClientInfo as t };
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@gw2me/client",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "gw2.me client library",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
8
8
  ".": {
9
- "types": "./dist/index.d.mts",
10
- "default": "./dist/index.mjs"
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
11
  },
12
12
  "./dpop": {
13
- "types": "./dist/dpop.d.mts",
14
- "default": "./dist/dpop.mjs"
13
+ "types": "./dist/dpop.d.ts",
14
+ "default": "./dist/dpop.js"
15
15
  },
16
16
  "./pkce": {
17
- "types": "./dist/pkce.d.mts",
18
- "default": "./dist/pkce.mjs"
17
+ "types": "./dist/pkce.d.ts",
18
+ "default": "./dist/pkce.js"
19
19
  }
20
20
  },
21
21
  "repository": {
@@ -30,9 +30,10 @@
30
30
  "oauth2"
31
31
  ],
32
32
  "files": [
33
- "dist/*.mjs",
34
- "dist/*.d.mts"
33
+ "dist/*.js",
34
+ "dist/*.d.ts"
35
35
  ],
36
+ "sideEffects": false,
36
37
  "author": "darthmaim",
37
38
  "license": "MIT",
38
39
  "bugs": {
@@ -40,14 +41,17 @@
40
41
  },
41
42
  "homepage": "https://github.com/gw2treasures/gw2.me#readme",
42
43
  "devDependencies": {
43
- "@gw2treasures/eslint-config": "0.2.0",
44
+ "@arethetypeswrong/core": "0.18.2",
45
+ "@gw2treasures/eslint-config": "0.2.1",
44
46
  "@gw2treasures/publish-package": "0.1.0",
45
47
  "@gw2treasures/tsconfig": "0.0.1",
46
- "eslint": "9.39.2",
47
- "tsdown": "0.19.0",
48
+ "@types/node": "24.12.0",
49
+ "eslint": "9.39.4",
50
+ "publint": "0.3.18",
51
+ "tsdown": "0.21.4",
48
52
  "typescript": "5.9.3",
49
- "typescript-eslint": "8.53.0",
50
- "undici-types": "7.18.2"
53
+ "typescript-eslint": "8.57.1",
54
+ "undici-types": "7.24.4"
51
55
  },
52
56
  "publishConfig": {
53
57
  "access": "public",
@@ -1,9 +0,0 @@
1
- //#region src/base64.ts
2
- function base64urlEncode(input) {
3
- const data = input instanceof ArrayBuffer ? new Uint8Array(input) : input;
4
- return btoa(String.fromCharCode(...data)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
5
- }
6
-
7
- //#endregion
8
- export { base64urlEncode as t };
9
- //# sourceMappingURL=base64-B0vm543A.mjs.map
package/dist/dpop.d.mts DELETED
@@ -1,14 +0,0 @@
1
- import { r as DPoPParams } from "./types-cXKOlCJ4.mjs";
2
-
3
- //#region src/dpop.d.ts
4
- declare function generateDPoPKeyPair(): Promise<CryptoKeyPair>;
5
- declare function createDPoPJwt({
6
- htm,
7
- htu,
8
- nonce,
9
- accessToken
10
- }: DPoPParams, keyPair: CryptoKeyPair): Promise<string>;
11
- declare function jwkThumbprint(key: CryptoKey): Promise<string>;
12
- //#endregion
13
- export { createDPoPJwt, generateDPoPKeyPair, jwkThumbprint };
14
- //# sourceMappingURL=dpop.d.mts.map
package/dist/dpop.mjs DELETED
@@ -1,53 +0,0 @@
1
- import { t as base64urlEncode } from "./base64-B0vm543A.mjs";
2
-
3
- //#region src/dpop.ts
4
- function generateDPoPKeyPair() {
5
- return crypto.subtle.generateKey({
6
- name: "ECDSA",
7
- namedCurve: "P-256"
8
- }, false, ["sign"]);
9
- }
10
- async function createDPoPJwt({ htm, htu, nonce, accessToken }, keyPair) {
11
- const header = JSON.stringify({
12
- alg: "ES256",
13
- typ: "dpop+jwt",
14
- jwk: await jwk(keyPair.publicKey)
15
- });
16
- const body = JSON.stringify({
17
- iat: Math.floor(Date.now() / 1e3),
18
- jti: base64urlEncode(crypto.getRandomValues(new Uint8Array(32))),
19
- htm,
20
- htu,
21
- nonce,
22
- ath: accessToken ? base64urlEncode(await crypto.subtle.digest("SHA-256", stringToBuffer(accessToken))) : void 0
23
- });
24
- const input = `${base64urlEncode(stringToBuffer(header))}.${base64urlEncode(stringToBuffer(body))}`;
25
- return `${input}.${base64urlEncode(await crypto.subtle.sign({
26
- name: "ECDSA",
27
- hash: "SHA-256"
28
- }, keyPair.privateKey, stringToBuffer(input)))}`;
29
- }
30
- const encoder = new TextEncoder();
31
- function stringToBuffer(value) {
32
- return encoder.encode(value);
33
- }
34
- async function jwk(key) {
35
- const { kty, e, k, n, x, y, crv } = await crypto.subtle.exportKey("jwk", key);
36
- return {
37
- e,
38
- k,
39
- crv,
40
- kty,
41
- n,
42
- x,
43
- y
44
- };
45
- }
46
- async function jwkThumbprint(key) {
47
- const jwkJson = JSON.stringify(await jwk(key));
48
- return base64urlEncode(await crypto.subtle.digest("SHA-256", stringToBuffer(jwkJson)));
49
- }
50
-
51
- //#endregion
52
- export { createDPoPJwt, generateDPoPKeyPair, jwkThumbprint };
53
- //# sourceMappingURL=dpop.mjs.map
package/dist/index.mjs DELETED
@@ -1,289 +0,0 @@
1
- //#region src/types.ts
2
- let Scope = /* @__PURE__ */ function(Scope$1) {
3
- Scope$1["Identify"] = "identify";
4
- Scope$1["Email"] = "email";
5
- Scope$1["Accounts"] = "accounts";
6
- Scope$1["Accounts_Verified"] = "accounts.verified";
7
- Scope$1["Accounts_DisplayName"] = "accounts.displayName";
8
- Scope$1["GW2_Account"] = "gw2:account";
9
- Scope$1["GW2_Inventories"] = "gw2:inventories";
10
- Scope$1["GW2_Characters"] = "gw2:characters";
11
- Scope$1["GW2_Tradingpost"] = "gw2:tradingpost";
12
- Scope$1["GW2_Wallet"] = "gw2:wallet";
13
- Scope$1["GW2_Unlocks"] = "gw2:unlocks";
14
- Scope$1["GW2_Pvp"] = "gw2:pvp";
15
- Scope$1["GW2_Wvw"] = "gw2:wvw";
16
- Scope$1["GW2_Builds"] = "gw2:builds";
17
- Scope$1["GW2_Progression"] = "gw2:progression";
18
- Scope$1["GW2_Guilds"] = "gw2:guilds";
19
- return Scope$1;
20
- }({});
21
-
22
- //#endregion
23
- //#region src/error.ts
24
- var Gw2MeError = class extends Error {};
25
- var Gw2MeOAuthError = class extends Gw2MeError {
26
- constructor(error, error_description, error_uri) {
27
- super(`Received ${error}` + (error_description ? `: ${error_description}` : "") + (error_uri ? ` (${error_uri})` : ""));
28
- this.error = error;
29
- this.error_description = error_description;
30
- this.error_uri = error_uri;
31
- }
32
- };
33
-
34
- //#endregion
35
- //#region src/util.ts
36
- async function jsonOrError(response) {
37
- await okOrError(response);
38
- if (!(response.headers.get("Content-Type") === "application/json")) throw new Gw2MeError("gw2.me did not return a valid JSON response");
39
- return response.json();
40
- }
41
- async function okOrError(response) {
42
- if (!response.ok) {
43
- let errorMessage;
44
- if (response.headers.get("Content-Type") === "application/json") errorMessage = (await response.json()).error_description;
45
- throw new Gw2MeError(`gw2.me returned an error: ${errorMessage ?? "Unknown error"}`);
46
- }
47
- }
48
-
49
- //#endregion
50
- //#region src/api.ts
51
- var Gw2MeApi = class {
52
- constructor(access_token, options) {
53
- this.access_token = access_token;
54
- this.options = options;
55
- }
56
- user() {
57
- return this.#requestWithDpop("api/user").then((request) => fetch(request)).then(jsonOrError);
58
- }
59
- saveSettings(settings) {
60
- return this.#requestWithDpop("api/user/settings", {
61
- method: "POST",
62
- headers: { "Content-Type": "application/json" },
63
- body: JSON.stringify(settings)
64
- }).then((request) => fetch(request)).then(okOrError);
65
- }
66
- accounts() {
67
- return this.#requestWithDpop("api/accounts").then((request) => fetch(request)).then(jsonOrError);
68
- }
69
- subtoken(accountId, options) {
70
- const url = this.#getUrl(`api/accounts/${accountId}/subtoken`);
71
- if (options?.permissions) url.searchParams.set("permissions", options.permissions.join(","));
72
- return this.#requestWithDpop(url).then((request) => fetch(request)).then(jsonOrError);
73
- }
74
- #getUrl(url) {
75
- return new URL(url, this.options?.url || "https://gw2.me/");
76
- }
77
- async #requestWithDpop(endpoint, init) {
78
- const url = endpoint instanceof URL ? endpoint : this.#getUrl(endpoint);
79
- const dpop = this.options?.dpop;
80
- const headers = new Headers(init?.headers);
81
- headers.set("Authorization", `${dpop ? "DPoP" : "Bearer"} ${this.access_token}`);
82
- if (dpop) headers.set("DPoP", await dpop({
83
- htm: init?.method ?? "GET",
84
- htu: url.toString(),
85
- accessToken: this.access_token
86
- }));
87
- return new Request(url, {
88
- cache: "no-cache",
89
- ...init,
90
- headers
91
- });
92
- }
93
- };
94
-
95
- //#endregion
96
- //#region src/fed-cm.ts
97
- var Gw2MeFedCM = class {
98
- #configUrl;
99
- #clientId;
100
- constructor(configUrl, clientId) {
101
- this.#configUrl = configUrl;
102
- this.#clientId = clientId;
103
- }
104
- isSupported() {
105
- return typeof window !== "undefined" && "IdentityCredential" in window;
106
- }
107
- request({ scopes, mediation, signal, mode, code_challenge, code_challenge_method, include_granted_scopes }) {
108
- if (!this.isSupported()) throw new Gw2MeError("FedCM is not supported");
109
- return navigator.credentials.get({
110
- mediation,
111
- signal,
112
- identity: {
113
- providers: [{
114
- configURL: this.#configUrl,
115
- clientId: this.#clientId,
116
- fields: [scopes.includes(Scope.Identify) && "name", scopes.includes(Scope.Email) && "email"].filter(Boolean),
117
- nonce: `${code_challenge_method}:${code_challenge}`,
118
- params: {
119
- scope: scopes.join(" "),
120
- code_challenge,
121
- code_challenge_method,
122
- include_granted_scopes
123
- }
124
- }],
125
- mode
126
- }
127
- });
128
- }
129
- };
130
-
131
- //#endregion
132
- //#region src/client.ts
133
- var Gw2MeClient = class {
134
- #client;
135
- #fedCM;
136
- constructor(client, options) {
137
- this.options = options;
138
- this.#client = client;
139
- this.#fedCM = new Gw2MeFedCM(this.#getUrl("/fed-cm/config.json"), client.client_id);
140
- }
141
- #getUrl(url) {
142
- return new URL(url, this.options?.url || "https://gw2.me/");
143
- }
144
- #getAuthorizationHeader() {
145
- if (!this.#client.client_secret) throw new Gw2MeError("client_secret is required");
146
- return `Basic ${btoa(`${this.#client.client_id}:${this.#client.client_secret}`)}`;
147
- }
148
- getAuthorizationUrl(params) {
149
- const urlParams = "request_uri" in params ? new URLSearchParams({
150
- client_id: this.#client.client_id,
151
- response_type: "code",
152
- request_uri: params.request_uri
153
- }) : constructAuthorizationParams(this.#client.client_id, params);
154
- return this.#getUrl(`/oauth2/authorize?${urlParams.toString()}`).toString();
155
- }
156
- async pushAuthorizationRequest(params) {
157
- const url = this.#getUrl("/oauth2/par");
158
- const headers = { "Content-Type": "application/x-www-form-urlencoded" };
159
- if (params.dpop) headers.DPoP = await params.dpop({
160
- htm: "POST",
161
- htu: url.toString()
162
- });
163
- const urlParams = constructAuthorizationParams(this.#client.client_id, params);
164
- if (this.#client.client_secret) headers.Authorization = this.#getAuthorizationHeader();
165
- return await fetch(url, {
166
- method: "POST",
167
- headers,
168
- body: urlParams,
169
- cache: "no-store"
170
- }).then(jsonOrError);
171
- }
172
- async getAccessToken({ code, token_type, redirect_uri, code_verifier, dpop }) {
173
- const data = new URLSearchParams({
174
- grant_type: "authorization_code",
175
- code,
176
- client_id: this.#client.client_id,
177
- redirect_uri
178
- });
179
- const headers = { "Content-Type": "application/x-www-form-urlencoded" };
180
- if (this.#client.client_secret) headers.Authorization = this.#getAuthorizationHeader();
181
- if (code_verifier) data.set("code_verifier", code_verifier);
182
- const url = this.#getUrl("/api/token");
183
- if (dpop) headers.DPoP = await dpop({
184
- htm: "POST",
185
- htu: url.toString(),
186
- accessToken: token_type === "DPoP" ? code : void 0
187
- });
188
- return await fetch(url, {
189
- method: "POST",
190
- headers,
191
- body: data,
192
- cache: "no-store"
193
- }).then(jsonOrError);
194
- }
195
- async refreshToken({ refresh_token, refresh_token_type, dpop }) {
196
- const data = new URLSearchParams({
197
- grant_type: "refresh_token",
198
- refresh_token,
199
- client_id: this.#client.client_id
200
- });
201
- const headers = { "Content-Type": "application/x-www-form-urlencoded" };
202
- if (this.#client.client_secret) headers["Authorization"] = this.#getAuthorizationHeader();
203
- const url = this.#getUrl("/api/token");
204
- if (dpop) headers.DPoP = await dpop({
205
- htm: "POST",
206
- htu: url.toString(),
207
- accessToken: refresh_token_type === "DPoP" ? refresh_token : void 0
208
- });
209
- return await fetch(url, {
210
- method: "POST",
211
- headers,
212
- body: data,
213
- cache: "no-store"
214
- }).then(jsonOrError);
215
- }
216
- async revokeToken({ token }) {
217
- const body = new URLSearchParams({ token });
218
- const headers = { "Content-Type": "application/x-www-form-urlencoded" };
219
- if (this.#client.client_secret) headers.Authorization = this.#getAuthorizationHeader();
220
- await fetch(this.#getUrl("/api/token/revoke"), {
221
- method: "POST",
222
- cache: "no-store",
223
- headers,
224
- body
225
- }).then(jsonOrError);
226
- }
227
- async introspectToken({ token }) {
228
- const body = new URLSearchParams({ token });
229
- const headers = { "Content-Type": "application/x-www-form-urlencoded" };
230
- if (this.#client.client_secret) headers.Authorization = this.#getAuthorizationHeader();
231
- return await fetch(this.#getUrl("/api/token/introspect"), {
232
- method: "POST",
233
- cache: "no-store",
234
- headers,
235
- body
236
- }).then(jsonOrError);
237
- }
238
- /**
239
- * Parses the search params received from gw2.me on the redirect url (code and state).
240
- * If gw2.me returned an error response, this will throw an error.
241
- *
242
- * @returns The code and optional state.
243
- */
244
- parseAuthorizationResponseSearchParams(searchParams) {
245
- const expectedIssuer = this.#getUrl("/").origin;
246
- const receivedIssuer = searchParams.get("iss");
247
- if (!receivedIssuer) throw new Gw2MeError("Issuer Identifier verification failed: parameter `iss` is missing");
248
- if (receivedIssuer !== expectedIssuer) throw new Gw2MeError(`Issuer Identifier verification failed: expected "${expectedIssuer}", got "${receivedIssuer}"`);
249
- const error = searchParams.get("error");
250
- if (error) throw new Gw2MeOAuthError(error, searchParams.get("error_description") ?? void 0, searchParams.get("error_uri") ?? void 0);
251
- const code = searchParams.get("code");
252
- if (!code) throw new Gw2MeError("Parameter `code` is missing");
253
- return {
254
- code,
255
- state: searchParams.get("state") || void 0
256
- };
257
- }
258
- api(access_token, options) {
259
- return new Gw2MeApi(access_token, {
260
- ...this.options,
261
- ...options
262
- });
263
- }
264
- get fedCM() {
265
- return this.#fedCM;
266
- }
267
- };
268
- function constructAuthorizationParams(client_id, { redirect_uri, scopes, state, code_challenge, code_challenge_method, dpop_jkt, prompt, include_granted_scopes, verified_accounts_only }) {
269
- const params = new URLSearchParams({
270
- client_id,
271
- response_type: "code",
272
- redirect_uri,
273
- scope: scopes.join(" ")
274
- });
275
- if (state) params.append("state", state);
276
- if (code_challenge && code_challenge_method) {
277
- params.append("code_challenge", code_challenge);
278
- params.append("code_challenge_method", code_challenge_method);
279
- }
280
- if (dpop_jkt) params.append("dpop_jkt", dpop_jkt);
281
- if (prompt) params.append("prompt", prompt);
282
- if (include_granted_scopes) params.append("include_granted_scopes", "true");
283
- if (verified_accounts_only) params.append("verified_accounts_only", "true");
284
- return params;
285
- }
286
-
287
- //#endregion
288
- export { Gw2MeApi, Gw2MeClient, Gw2MeError, Gw2MeOAuthError, Scope };
289
- //# sourceMappingURL=index.mjs.map
package/dist/pkce.d.mts DELETED
@@ -1,13 +0,0 @@
1
- //#region src/pkce.d.ts
2
- interface PKCEChallenge {
3
- code_challenge: string;
4
- code_challenge_method: 'S256';
5
- }
6
- interface PKCEPair {
7
- challenge: PKCEChallenge;
8
- code_verifier: string;
9
- }
10
- declare function generatePKCEPair(): Promise<PKCEPair>;
11
- //#endregion
12
- export { PKCEChallenge, PKCEPair, generatePKCEPair };
13
- //# sourceMappingURL=pkce.d.mts.map
package/dist/pkce.mjs DELETED
@@ -1,21 +0,0 @@
1
- import { t as base64urlEncode } from "./base64-B0vm543A.mjs";
2
-
3
- //#region src/pkce.ts
4
- async function generatePKCEPair() {
5
- const verifierBuffer = new Uint8Array(32);
6
- crypto.getRandomValues(verifierBuffer);
7
- const code_verifier = base64urlEncode(verifierBuffer);
8
- const encoder = new TextEncoder();
9
- const challenge = await crypto.subtle.digest("SHA-256", encoder.encode(code_verifier));
10
- return {
11
- code_verifier,
12
- challenge: {
13
- code_challenge_method: "S256",
14
- code_challenge: base64urlEncode(new Uint8Array(challenge))
15
- }
16
- };
17
- }
18
-
19
- //#endregion
20
- export { generatePKCEPair };
21
- //# sourceMappingURL=pkce.mjs.map