matrix-js-sdk 42.4.0 → 43.0.0

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/lib/@types/PushRules.d.ts +3 -1
  3. package/lib/@types/PushRules.d.ts.map +1 -1
  4. package/lib/@types/PushRules.js +2 -0
  5. package/lib/@types/PushRules.js.map +1 -1
  6. package/lib/@types/event.d.ts +1 -0
  7. package/lib/@types/event.d.ts.map +1 -1
  8. package/lib/@types/event.js.map +1 -1
  9. package/lib/@types/matrix-sdk-crypto-wasm.d.js.map +1 -1
  10. package/lib/client.d.ts +88 -37
  11. package/lib/client.d.ts.map +1 -1
  12. package/lib/client.js +72 -47
  13. package/lib/client.js.map +1 -1
  14. package/lib/crypto-api/index.d.ts +3 -3
  15. package/lib/crypto-api/index.d.ts.map +1 -1
  16. package/lib/crypto-api/index.js.map +1 -1
  17. package/lib/embedded.d.ts +17 -1
  18. package/lib/embedded.d.ts.map +1 -1
  19. package/lib/embedded.js +46 -1
  20. package/lib/embedded.js.map +1 -1
  21. package/lib/errors.d.ts +1 -1
  22. package/lib/errors.d.ts.map +1 -1
  23. package/lib/errors.js +2 -2
  24. package/lib/errors.js.map +1 -1
  25. package/lib/http-api/fetch.d.ts +7 -1
  26. package/lib/http-api/fetch.d.ts.map +1 -1
  27. package/lib/http-api/fetch.js +14 -5
  28. package/lib/http-api/fetch.js.map +1 -1
  29. package/lib/http-api/interface.d.ts +34 -8
  30. package/lib/http-api/interface.d.ts.map +1 -1
  31. package/lib/http-api/interface.js +10 -3
  32. package/lib/http-api/interface.js.map +1 -1
  33. package/lib/http-api/refresh.d.ts +30 -11
  34. package/lib/http-api/refresh.d.ts.map +1 -1
  35. package/lib/http-api/refresh.js +72 -5
  36. package/lib/http-api/refresh.js.map +1 -1
  37. package/lib/matrix.d.ts +1 -1
  38. package/lib/matrix.d.ts.map +1 -1
  39. package/lib/matrix.js.map +1 -1
  40. package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
  41. package/lib/matrixrtc/CallMembership.js +20 -6
  42. package/lib/matrixrtc/CallMembership.js.map +1 -1
  43. package/lib/matrixrtc/IMembershipManager.d.ts +5 -0
  44. package/lib/matrixrtc/IMembershipManager.d.ts.map +1 -1
  45. package/lib/matrixrtc/IMembershipManager.js.map +1 -1
  46. package/lib/matrixrtc/MatrixRTCSession.d.ts +27 -1
  47. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
  48. package/lib/matrixrtc/MatrixRTCSession.js +58 -36
  49. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  50. package/lib/matrixrtc/MatrixRTCSessionManager.js +1 -1
  51. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
  52. package/lib/matrixrtc/MembershipManager.d.ts +76 -0
  53. package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
  54. package/lib/matrixrtc/MembershipManager.js +264 -47
  55. package/lib/matrixrtc/MembershipManager.js.map +1 -1
  56. package/lib/matrixrtc/index.d.ts +1 -1
  57. package/lib/matrixrtc/index.d.ts.map +1 -1
  58. package/lib/matrixrtc/index.js +1 -1
  59. package/lib/matrixrtc/index.js.map +1 -1
  60. package/lib/matrixrtc/membershipData/common.d.ts +8 -0
  61. package/lib/matrixrtc/membershipData/common.d.ts.map +1 -1
  62. package/lib/matrixrtc/membershipData/common.js +10 -0
  63. package/lib/matrixrtc/membershipData/common.js.map +1 -1
  64. package/lib/matrixrtc/membershipData/index.d.ts +1 -1
  65. package/lib/matrixrtc/membershipData/index.d.ts.map +1 -1
  66. package/lib/matrixrtc/membershipData/index.js +1 -1
  67. package/lib/matrixrtc/membershipData/index.js.map +1 -1
  68. package/lib/matrixrtc/types.d.ts +104 -11
  69. package/lib/matrixrtc/types.d.ts.map +1 -1
  70. package/lib/matrixrtc/types.js +263 -19
  71. package/lib/matrixrtc/types.js.map +1 -1
  72. package/lib/matrixrtc/utils.d.ts +22 -1
  73. package/lib/matrixrtc/utils.d.ts.map +1 -1
  74. package/lib/matrixrtc/utils.js +38 -0
  75. package/lib/matrixrtc/utils.js.map +1 -1
  76. package/lib/models/room-sticky-events.d.ts +7 -0
  77. package/lib/models/room-sticky-events.d.ts.map +1 -1
  78. package/lib/models/room-sticky-events.js +27 -0
  79. package/lib/models/room-sticky-events.js.map +1 -1
  80. package/lib/models/thread.d.ts.map +1 -1
  81. package/lib/models/thread.js +8 -0
  82. package/lib/models/thread.js.map +1 -1
  83. package/lib/oauth/authorize.js +1 -1
  84. package/lib/oauth/authorize.js.map +1 -1
  85. package/lib/oauth/index.d.ts +3 -5
  86. package/lib/oauth/index.d.ts.map +1 -1
  87. package/lib/oauth/index.js +7 -7
  88. package/lib/oauth/index.js.map +1 -1
  89. package/lib/oauth/tokenRefresher.d.ts +10 -4
  90. package/lib/oauth/tokenRefresher.d.ts.map +1 -1
  91. package/lib/oauth/tokenRefresher.js +34 -24
  92. package/lib/oauth/tokenRefresher.js.map +1 -1
  93. package/lib/receipt-accumulator.d.ts +10 -11
  94. package/lib/receipt-accumulator.d.ts.map +1 -1
  95. package/lib/receipt-accumulator.js +26 -25
  96. package/lib/receipt-accumulator.js.map +1 -1
  97. package/lib/room-summary-api.d.ts +71 -0
  98. package/lib/room-summary-api.d.ts.map +1 -0
  99. package/lib/room-summary-api.js +82 -0
  100. package/lib/room-summary-api.js.map +1 -0
  101. package/lib/rust-crypto/backup.d.ts +7 -0
  102. package/lib/rust-crypto/backup.d.ts.map +1 -1
  103. package/lib/rust-crypto/backup.js +41 -25
  104. package/lib/rust-crypto/backup.js.map +1 -1
  105. package/lib/sync.d.ts.map +1 -1
  106. package/lib/sync.js +6 -1
  107. package/lib/sync.js.map +1 -1
  108. package/package.json +6 -6
  109. package/src/@types/PushRules.ts +2 -0
  110. package/src/@types/event.ts +1 -1
  111. package/src/@types/matrix-sdk-crypto-wasm.d.ts +15 -12
  112. package/src/client.ts +105 -62
  113. package/src/crypto-api/index.ts +3 -3
  114. package/src/embedded.ts +61 -2
  115. package/src/errors.ts +2 -1
  116. package/src/http-api/fetch.ts +14 -5
  117. package/src/http-api/interface.ts +36 -8
  118. package/src/http-api/refresh.ts +101 -17
  119. package/src/matrix.ts +1 -1
  120. package/src/matrixrtc/CallMembership.ts +22 -4
  121. package/src/matrixrtc/IMembershipManager.ts +6 -0
  122. package/src/matrixrtc/MatrixRTCSession.ts +72 -49
  123. package/src/matrixrtc/MatrixRTCSessionManager.ts +1 -1
  124. package/src/matrixrtc/MembershipManager.ts +284 -61
  125. package/src/matrixrtc/index.ts +7 -1
  126. package/src/matrixrtc/membershipData/common.ts +12 -0
  127. package/src/matrixrtc/membershipData/index.ts +1 -1
  128. package/src/matrixrtc/types.ts +351 -23
  129. package/src/matrixrtc/utils.ts +54 -1
  130. package/src/models/room-sticky-events.ts +26 -1
  131. package/src/models/thread.ts +9 -0
  132. package/src/oauth/authorize.ts +1 -1
  133. package/src/oauth/index.ts +7 -8
  134. package/src/oauth/tokenRefresher.ts +15 -5
  135. package/src/receipt-accumulator.ts +30 -26
  136. package/src/room-summary-api.ts +114 -0
  137. package/src/rust-crypto/backup.ts +46 -33
  138. package/src/sync.ts +9 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/oauth/index.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n type BearerTokenResponse,\n type DeviceAccessTokenError,\n type DeviceAccessTokenResponse,\n type DeviceAuthorizationResponse,\n generateScope,\n normalizeBearerTokenResponseTokenType,\n startDeviceAuthorization,\n validateBearerTokenResponse,\n waitForDeviceAuthorization,\n} from \"./authorize.ts\";\nimport type { ValidatedAuthMetadata } from \"./discover.ts\";\nimport {\n OAuthGrantType,\n type OAuthRegistrationRequest,\n urlHasCommonBase,\n validateRegistrationResponse,\n} from \"./register.ts\";\nimport { encodeUnpaddedBase64Url } from \"../base64.ts\";\nimport { sha256 } from \"../digest.ts\";\nimport { HTTPError, isMatrixErrorResponse, MatrixError, Method } from \"../http-api/index.ts\";\nimport { type Logger, logger as rootLogger } from \"../logger.ts\";\nimport { fetchWithLogging } from \"./fetch.ts\";\nimport { isOAuth2ErrorResponse, OAuth2Error, OAuth2HTTPError } from \"./error.ts\";\nimport { secureRandomString } from \"../randomstring.ts\";\nimport { type NonEmptyArray } from \"../@types/common.ts\";\n\nexport * from \"./authorize.ts\";\nexport * from \"./error.ts\";\nexport * from \"./register.ts\";\nexport * from \"./tokenRefresher.ts\";\nexport * from \"./discover.ts\";\n\n/**\n * Type representing the persistent context needed for typical OAuth flows\n */\ntype Context = {\n /** The OAuth client ID */\n clientId: string;\n /** The desired device ID */\n deviceId?: string;\n /** The seed used to generate the challenge code */\n codeVerifier?: string;\n /** The URI to redirect the user to with credentials after auth */\n redirectUri: string;\n};\n\nexport class OAuth2 {\n /**\n * Attempts dynamic registration against the configured registration endpoint.\n * Will ignore any URIs that do not use client_uri as a common base as per the spec.\n * @param authMetadata - Auth config from {@link MatrixClient.getAuthMetadata}\n * @param clientMetadata - The metadata for the client which to register,\n * grant_types & response_types & token_endpoint_auth_method will be sanely calculated if omitted.\n * @param logger - Optional logger to use for the request, defaults to the root logger of the js-sdk.\n * @returns Promise<string> resolved with registered clientId\n * @throws when registration is not supported, on failed request or invalid response\n */\n public static async registerClient(\n authMetadata: ValidatedAuthMetadata,\n clientMetadata: OAuthRegistrationRequest,\n logger: Logger = rootLogger,\n ): Promise<string> {\n const defaultGrantTypes: NonEmptyArray<string> = [\n OAuthGrantType.AuthorizationCode,\n OAuthGrantType.RefreshToken,\n ];\n // ask for device authorization grant if supported\n if (authMetadata.grant_types_supported.includes(OAuthGrantType.DeviceAuthorization)) {\n defaultGrantTypes.push(OAuthGrantType.DeviceAuthorization);\n }\n\n const grantTypes = clientMetadata.grant_types ?? defaultGrantTypes;\n if (grantTypes.some((scope) => !authMetadata.grant_types_supported.includes(scope))) {\n throw new Error(OAuth2Error.DynamicRegistrationNotSupported);\n }\n\n const commonBase = new URL(clientMetadata.client_uri);\n\n const request: OAuthRegistrationRequest = {\n // Apply some defaults\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n ...clientMetadata,\n grant_types: grantTypes,\n logo_uri: urlHasCommonBase(commonBase, clientMetadata.logo_uri) ? clientMetadata.logo_uri : undefined,\n policy_uri: urlHasCommonBase(commonBase, clientMetadata.policy_uri) ? clientMetadata.policy_uri : undefined,\n tos_uri: urlHasCommonBase(commonBase, clientMetadata.tos_uri) ? clientMetadata.tos_uri : undefined,\n };\n\n try {\n const response = await fetchWithLogging(logger, authMetadata.registration_endpoint, {\n method: Method.Post,\n headers: {\n \"Accept\": \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n if (response.status >= 400) {\n throw new Error(OAuth2Error.DynamicRegistrationFailed);\n }\n\n const registrationResponse = await response.json();\n if (validateRegistrationResponse(registrationResponse)) {\n return registrationResponse.client_id;\n }\n\n throw new Error(OAuth2Error.DynamicRegistrationInvalid);\n } catch (error) {\n if (Object.values(OAuth2Error).includes((error as Error).message as OAuth2Error)) {\n throw error;\n } else {\n logger.error(\"Dynamic registration request failed\", error);\n throw new Error(OAuth2Error.DynamicRegistrationFailed, { cause: error });\n }\n }\n }\n\n public readonly context: Required<Context>;\n\n /**\n * @param metadata - The validated OAuth 2.0 metadata for the Identity Provider.\n * @param context - The persistent context needed for the OAuth flows.\n * @param logger - Optional logger to use for the requests made by this instance,\n * defaults to the root logger of the js-sdk.\n */\n public constructor(\n public readonly metadata: ValidatedAuthMetadata,\n context: Context,\n private readonly logger: Logger = rootLogger,\n ) {\n this.context = {\n clientId: context.clientId,\n redirectUri: context.redirectUri,\n deviceId: context.deviceId ?? secureRandomString(10),\n codeVerifier: context.codeVerifier ?? secureRandomString(96),\n };\n }\n\n /**\n * Generate a URL to attempt authorization with the OP\n * See https://spec.matrix.org/v1.18/client-server-api/#authorization-code-flow\n * @param state - A unique opaque identifier, like a transaction ID,\n * that will allow the client to maintain state between the authorization request and the callback.\n * The app should use this to key the storage for where the rest of the auth context is saved.\n * @param responseMode - The manner in which the IdP should send the secrets back to the app. Defaults to `fragment` for privacy.\n * @param prompt - Optional prompt parameter to pass to the IdP to signal intent, e.g. `create` for User registration.\n * @param scope - The OAuth2 scope to request, will be generated based on the device ID if omitted.\n * @returns a Promise with the url as a string\n */\n public async generateAuthorizationCodeGrantUrl(\n state: string,\n responseMode: \"fragment\" | \"query\" = \"fragment\",\n prompt?: string,\n scope?: string,\n ): Promise<string> {\n const challenge = encodeUnpaddedBase64Url(await sha256(this.context.codeVerifier));\n\n const url = new URL(this.metadata.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"response_mode\", responseMode);\n url.searchParams.set(\"client_id\", this.context.clientId);\n url.searchParams.set(\"redirect_uri\", this.context.redirectUri);\n url.searchParams.set(\"scope\", scope ?? generateScope(this.context.deviceId));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"code_challenge\", challenge);\n\n if (prompt) {\n url.searchParams.set(\"prompt\", prompt);\n }\n\n return url.toString();\n }\n\n /**\n * Attempt to exchange authorization code for bearer token.\n *\n * Takes the authorization code returned by the OAuth2 Provider via the authorization URL, and makes a\n * request to the Token Endpoint, to obtain the access token, refresh token, etc.\n *\n * @param code - authorization code as returned by IdP during authorization\n * @returns a validated bearer token response\n * @throws An `Error` with `message` set to an entry in {@link OAuth2Error},\n * when the request fails, or the returned token response is invalid.\n */\n public async completeAuthorizationCodeGrant(code: string): Promise<BearerTokenResponse> {\n const params = new URLSearchParams();\n params.append(\"grant_type\", \"authorization_code\");\n params.append(\"client_id\", this.context.clientId);\n params.append(\"code_verifier\", this.context.codeVerifier);\n params.append(\"redirect_uri\", this.context.redirectUri);\n params.append(\"code\", code);\n\n const res = await this.fetch(\"token\", params, OAuth2Error.CodeExchangeFailed);\n const tokenResponse = await res.json();\n\n // throws when response is invalid\n validateBearerTokenResponse(tokenResponse);\n return normalizeBearerTokenResponseTokenType(tokenResponse);\n }\n\n /**\n * Refresh the access token using the given refresh token and the refresh token grant\n * @param refreshToken - the token to use to refresh the access token\n */\n public async performRefreshTokenGrant(refreshToken: string): Promise<BearerTokenResponse> {\n const params = new URLSearchParams();\n params.append(\"grant_type\", \"refresh_token\");\n params.append(\"client_id\", this.context.clientId);\n params.append(\"refresh_token\", refreshToken);\n\n const res = await this.fetch(\"token\", params, OAuth2Error.RefreshTokenFailed);\n const tokenResponse = await res.json();\n\n // throws when response is invalid\n validateBearerTokenResponse(tokenResponse);\n return normalizeBearerTokenResponseTokenType(tokenResponse);\n }\n\n /**\n * Revokes the given token\n * @param token - the token to remove\n * @param type - the type of token, acts as a hint to the IdP\n */\n public async revokeToken(token: string, type?: \"access_token\" | \"refresh_token\"): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"token\", token);\n params.append(\"client_id\", this.context.clientId);\n if (type) {\n params.append(\"token_type_hint\", type);\n }\n\n // [RFC 7009 section 2.2](https://www.rfc-editor.org/rfc/rfc7009.html#section-2.2) says:\n // > The content of the response body is ignored by the client as all\n // > necessary information is conveyed in the response code.\n // so, we don't do anything with the response body.\n await this.fetch(\"revocation\", params, OAuth2Error.RevokeTokenFailed);\n }\n\n /**\n * Begin OAuth2 device authorization flow.\n * @param scope - the scope to request for authorization.\n * @returns a promise that resolves to a device access token response,\n * or an error response if the user denies authorization or the device code expires.\n */\n public async startDeviceAuthorizationGrant(scope?: string): Promise<DeviceAuthorizationResponse> {\n return startDeviceAuthorization({\n scope: scope ?? generateScope(this.context.deviceId),\n metadata: this.metadata,\n clientId: this.context.clientId,\n logger: this.logger,\n });\n }\n\n /**\n * Polls the OAuth2 token endpoint until we get a device access token response, or encounter an unrecoverable error.\n * @param session - The session returned from a previous call to {@link OAuth2.startDeviceAuthorizationGrant}.\n * @returns a promise that resolves to a device access token response,\n * or an error response if the user denies authorization or the device code expires.\n */\n public async waitForDeviceAuthorizationGrant(\n session: DeviceAuthorizationResponse,\n ): Promise<DeviceAccessTokenResponse | DeviceAccessTokenError> {\n return waitForDeviceAuthorization({\n session,\n metadata: this.metadata,\n clientId: this.context.clientId,\n logger: this.logger,\n });\n }\n\n /**\n * Make a request to one of the OAuth 2.0 endpoints, throwing if it responds with an error.\n * @returns the {@link Response}, for the caller to parse if the endpoint returns a body.\n */\n private async fetch(\n target: \"token\" | \"registration\" | \"revocation\",\n params: URLSearchParams,\n error: OAuth2Error,\n ): Promise<Response> {\n const url = this.metadata[`${target}_endpoint`];\n const res = await fetchWithLogging(this.logger, url, {\n method: Method.Post,\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"Accept\": \"application/json\",\n },\n body: params,\n });\n\n if (res.status >= 400) {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n // The endpoint didn't give us a JSON body, so we can't determine the error type. We'll throw a generic\n // HTTPError below.\n }\n // Because the Matrix C-S API error response format is so similar to the OAuth 2.0 error response format\n // the ordering of these checks is important. We want to check for a Matrix error response first, and only\n // if it isn't one do we check for an OAuth 2.0 error response.\n // This essentially relies on `errcode` not being present in an OAuth 2.0 error response.\n if (isMatrixErrorResponse(body)) {\n throw new MatrixError(body, res.status, undefined, undefined, res.headers);\n }\n if (isOAuth2ErrorResponse(body)) {\n throw new OAuth2HTTPError(error, res.status, res.headers, body);\n }\n throw new HTTPError(error, res.status, res.headers);\n }\n\n return res;\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/oauth/index.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport {\n type BearerTokenResponse,\n type DeviceAccessTokenError,\n type DeviceAccessTokenResponse,\n type DeviceAuthorizationResponse,\n generateScope,\n normalizeBearerTokenResponseTokenType,\n startDeviceAuthorization,\n validateBearerTokenResponse,\n waitForDeviceAuthorization,\n} from \"./authorize.ts\";\nimport type { ValidatedAuthMetadata } from \"./discover.ts\";\nimport {\n OAuthGrantType,\n type OAuthRegistrationRequest,\n urlHasCommonBase,\n validateRegistrationResponse,\n} from \"./register.ts\";\nimport { encodeUnpaddedBase64Url } from \"../base64.ts\";\nimport { sha256 } from \"../digest.ts\";\nimport { HTTPError, isMatrixErrorResponse, MatrixError } from \"../http-api/errors.ts\";\nimport { Method } from \"../http-api/method.ts\";\nimport { type Logger, logger as rootLogger } from \"../logger.ts\";\nimport { fetchWithLogging } from \"./fetch.ts\";\nimport { isOAuth2ErrorResponse, OAuth2Error, OAuth2HTTPError } from \"./error.ts\";\nimport { secureRandomString } from \"../randomstring.ts\";\nimport { type NonEmptyArray } from \"../@types/common.ts\";\n\nexport * from \"./authorize.ts\";\nexport * from \"./error.ts\";\nexport * from \"./register.ts\";\nexport * from \"./discover.ts\";\n\n/**\n * Type representing the persistent context needed for typical OAuth flows\n */\ntype Context = {\n /** The OAuth client ID */\n clientId: string;\n /** The desired device ID */\n deviceId?: string;\n /** The seed used to generate the challenge code */\n codeVerifier?: string;\n};\n\nexport class OAuth2 {\n /**\n * Attempts dynamic registration against the configured registration endpoint.\n * Will ignore any URIs that do not use client_uri as a common base as per the spec.\n * @param authMetadata - Auth config from {@link MatrixClient.getAuthMetadata}\n * @param clientMetadata - The metadata for the client which to register,\n * grant_types & response_types & token_endpoint_auth_method will be sanely calculated if omitted.\n * @param logger - Optional logger to use for the request, defaults to the root logger of the js-sdk.\n * @returns Promise<string> resolved with registered clientId\n * @throws when registration is not supported, on failed request or invalid response\n */\n public static async registerClient(\n authMetadata: ValidatedAuthMetadata,\n clientMetadata: OAuthRegistrationRequest,\n logger: Logger = rootLogger,\n ): Promise<string> {\n const defaultGrantTypes: NonEmptyArray<string> = [\n OAuthGrantType.AuthorizationCode,\n OAuthGrantType.RefreshToken,\n ];\n // ask for device authorization grant if supported\n if (authMetadata.grant_types_supported.includes(OAuthGrantType.DeviceAuthorization)) {\n defaultGrantTypes.push(OAuthGrantType.DeviceAuthorization);\n }\n\n const grantTypes = clientMetadata.grant_types ?? defaultGrantTypes;\n if (grantTypes.some((scope) => !authMetadata.grant_types_supported.includes(scope))) {\n throw new Error(OAuth2Error.DynamicRegistrationNotSupported);\n }\n\n const commonBase = new URL(clientMetadata.client_uri);\n\n const request: OAuthRegistrationRequest = {\n // Apply some defaults\n response_types: [\"code\"],\n token_endpoint_auth_method: \"none\",\n ...clientMetadata,\n grant_types: grantTypes,\n logo_uri: urlHasCommonBase(commonBase, clientMetadata.logo_uri) ? clientMetadata.logo_uri : undefined,\n policy_uri: urlHasCommonBase(commonBase, clientMetadata.policy_uri) ? clientMetadata.policy_uri : undefined,\n tos_uri: urlHasCommonBase(commonBase, clientMetadata.tos_uri) ? clientMetadata.tos_uri : undefined,\n };\n\n try {\n const response = await fetchWithLogging(logger, authMetadata.registration_endpoint, {\n method: Method.Post,\n headers: {\n \"Accept\": \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n });\n\n if (response.status >= 400) {\n throw new Error(OAuth2Error.DynamicRegistrationFailed);\n }\n\n const registrationResponse = await response.json();\n if (validateRegistrationResponse(registrationResponse)) {\n return registrationResponse.client_id;\n }\n\n throw new Error(OAuth2Error.DynamicRegistrationInvalid);\n } catch (error) {\n if (Object.values(OAuth2Error).includes((error as Error).message as OAuth2Error)) {\n throw error;\n } else {\n logger.error(\"Dynamic registration request failed\", error);\n throw new Error(OAuth2Error.DynamicRegistrationFailed, { cause: error });\n }\n }\n }\n\n public readonly context: Required<Context>;\n\n /**\n * @param metadata - The validated OAuth 2.0 metadata for the Identity Provider.\n * @param context - The persistent context needed for the OAuth flows.\n * @param logger - Optional logger to use for the requests made by this instance,\n * defaults to the root logger of the js-sdk.\n */\n public constructor(\n public readonly metadata: ValidatedAuthMetadata,\n context: Context,\n private readonly logger: Logger = rootLogger,\n ) {\n this.context = {\n clientId: context.clientId,\n deviceId: context.deviceId ?? secureRandomString(10),\n codeVerifier: context.codeVerifier ?? secureRandomString(96),\n };\n }\n\n /**\n * Generate a URL to attempt authorization with the OP\n * See https://spec.matrix.org/v1.18/client-server-api/#authorization-code-flow\n * @param state - A unique opaque identifier, like a transaction ID,\n * that will allow the client to maintain state between the authorization request and the callback.\n * The app should use this to key the storage for where the rest of the auth context is saved.\n * @param responseMode - The manner in which the IdP should send the secrets back to the app. Defaults to `fragment` for privacy.\n * @param redirectUri - The redirect URI this application is registered with the delegated auth server under.\n * @param prompt - Optional prompt parameter to pass to the IdP to signal intent, e.g. `create` for User registration.\n * @param scope - The OAuth2 scope to request, will be generated based on the device ID if omitted.\n * @returns a Promise with the url as a string\n */\n public async generateAuthorizationCodeGrantUrl(\n state: string,\n redirectUri: string,\n responseMode: \"fragment\" | \"query\" = \"fragment\",\n prompt?: string,\n scope?: string,\n ): Promise<string> {\n const challenge = encodeUnpaddedBase64Url(await sha256(this.context.codeVerifier));\n\n const url = new URL(this.metadata.authorization_endpoint);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"response_mode\", responseMode);\n url.searchParams.set(\"client_id\", this.context.clientId);\n url.searchParams.set(\"redirect_uri\", redirectUri);\n url.searchParams.set(\"scope\", scope ?? generateScope(this.context.deviceId));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n url.searchParams.set(\"code_challenge\", challenge);\n\n if (prompt) {\n url.searchParams.set(\"prompt\", prompt);\n }\n\n return url.toString();\n }\n\n /**\n * Attempt to exchange authorization code for bearer token.\n *\n * Takes the authorization code returned by the OAuth2 Provider via the authorization URL, and makes a\n * request to the Token Endpoint, to obtain the access token, refresh token, etc.\n *\n * @param code - authorization code as returned by IdP during authorization\n * @returns a validated bearer token response\n * @throws An `Error` with `message` set to an entry in {@link OAuth2Error},\n * when the request fails, or the returned token response is invalid.\n */\n public async completeAuthorizationCodeGrant(code: string, redirectUri: string): Promise<BearerTokenResponse> {\n const params = new URLSearchParams();\n params.append(\"grant_type\", \"authorization_code\");\n params.append(\"client_id\", this.context.clientId);\n params.append(\"code_verifier\", this.context.codeVerifier);\n params.append(\"redirect_uri\", redirectUri);\n params.append(\"code\", code);\n\n const res = await this.fetch(\"token\", params, OAuth2Error.CodeExchangeFailed);\n const tokenResponse = await res.json();\n\n // throws when response is invalid\n validateBearerTokenResponse(tokenResponse);\n return normalizeBearerTokenResponseTokenType(tokenResponse);\n }\n\n /**\n * Refresh the access token using the given refresh token and the refresh token grant\n * @param refreshToken - the token to use to refresh the access token\n */\n public async performRefreshTokenGrant(refreshToken: string): Promise<BearerTokenResponse> {\n const params = new URLSearchParams();\n params.append(\"grant_type\", \"refresh_token\");\n params.append(\"client_id\", this.context.clientId);\n params.append(\"refresh_token\", refreshToken);\n\n const res = await this.fetch(\"token\", params, OAuth2Error.RefreshTokenFailed);\n const tokenResponse = await res.json();\n\n // throws when response is invalid\n validateBearerTokenResponse(tokenResponse);\n return normalizeBearerTokenResponseTokenType(tokenResponse);\n }\n\n /**\n * Revokes the given token\n * @param token - the token to remove\n * @param type - the type of token, acts as a hint to the IdP\n */\n public async revokeToken(token: string, type?: \"access_token\" | \"refresh_token\"): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"token\", token);\n params.append(\"client_id\", this.context.clientId);\n if (type) {\n params.append(\"token_type_hint\", type);\n }\n\n // [RFC 7009 section 2.2](https://www.rfc-editor.org/rfc/rfc7009.html#section-2.2) says:\n // > The content of the response body is ignored by the client as all\n // > necessary information is conveyed in the response code.\n // so, we don't do anything with the response body.\n await this.fetch(\"revocation\", params, OAuth2Error.RevokeTokenFailed);\n }\n\n /**\n * Begin OAuth2 device authorization flow.\n * @param scope - the scope to request for authorization.\n * @returns a promise that resolves to a device access token response,\n * or an error response if the user denies authorization or the device code expires.\n */\n public async startDeviceAuthorizationGrant(scope?: string): Promise<DeviceAuthorizationResponse> {\n return startDeviceAuthorization({\n scope: scope ?? generateScope(this.context.deviceId),\n metadata: this.metadata,\n clientId: this.context.clientId,\n logger: this.logger,\n });\n }\n\n /**\n * Polls the OAuth2 token endpoint until we get a device access token response, or encounter an unrecoverable error.\n * @param session - The session returned from a previous call to {@link OAuth2.startDeviceAuthorizationGrant}.\n * @returns a promise that resolves to a device access token response,\n * or an error response if the user denies authorization or the device code expires.\n */\n public async waitForDeviceAuthorizationGrant(\n session: DeviceAuthorizationResponse,\n ): Promise<DeviceAccessTokenResponse | DeviceAccessTokenError> {\n return waitForDeviceAuthorization({\n session,\n metadata: this.metadata,\n clientId: this.context.clientId,\n logger: this.logger,\n });\n }\n\n /**\n * Make a request to one of the OAuth 2.0 endpoints, throwing if it responds with an error.\n * @returns the {@link Response}, for the caller to parse if the endpoint returns a body.\n */\n private async fetch(\n target: \"token\" | \"registration\" | \"revocation\",\n params: URLSearchParams,\n error: OAuth2Error,\n ): Promise<Response> {\n const url = this.metadata[`${target}_endpoint`];\n const res = await fetchWithLogging(this.logger, url, {\n method: Method.Post,\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"Accept\": \"application/json\",\n },\n body: params,\n });\n\n if (res.status >= 400) {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n // The endpoint didn't give us a JSON body, so we can't determine the error type. We'll throw a generic\n // HTTPError below.\n }\n // Because the Matrix C-S API error response format is so similar to the OAuth 2.0 error response format\n // the ordering of these checks is important. We want to check for a Matrix error response first, and only\n // if it isn't one do we check for an OAuth 2.0 error response.\n // This essentially relies on `errcode` not being present in an OAuth 2.0 error response.\n if (isMatrixErrorResponse(body)) {\n throw new MatrixError(body, res.status, undefined, undefined, res.headers);\n }\n if (isOAuth2ErrorResponse(body)) {\n throw new OAuth2HTTPError(error, res.status, res.headers, body);\n }\n throw new HTTPError(error, res.status, res.headers);\n }\n\n return res;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,4 @@
1
- import { type AccessTokens, type TokenRefreshFunction } from "../http-api/index.ts";
1
+ import { type AccessTokens } from "../http-api/interface.ts";
2
2
  import { type OAuth2 } from "./index.ts";
3
3
  /**
4
4
  * Class responsible for refreshing OAuth2 access tokens
@@ -7,14 +7,20 @@ export declare class TokenRefresher {
7
7
  private readonly auth;
8
8
  private readonly onRefresh;
9
9
  private inflightRefreshRequest?;
10
- constructor(auth: OAuth2, onRefresh: (tokens: AccessTokens) => Promise<void>);
10
+ constructor(auth: OAuth2, onRefresh: (tokens: AccessTokens) => void | Promise<void>);
11
11
  /**
12
12
  * Attempt token refresh using given refresh token
13
13
  * @param refreshToken - refresh token to use in request with token issuer
14
- * @returns tokens - Promise that resolves with new access and refresh tokens
15
14
  * @throws when token refresh fails
16
15
  */
17
- tokenRefreshFunction: TokenRefreshFunction;
16
+ refreshTokens(refreshToken: string): Promise<AccessTokens>;
17
+ /**
18
+ * Revoke a token with the OP, e.g. as part of logging out.
19
+ * @param token - the token to revoke
20
+ * @param type - the type of token, acts as a hint to the OP
21
+ * @throws when revocation fails
22
+ */
23
+ revokeToken(token: string, type?: "access_token" | "refresh_token"): Promise<void>;
18
24
  private shouldLogoutOnError;
19
25
  private getNewTokens;
20
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../../src/oauth/tokenRefresher.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAAa,KAAK,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AAExH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;GAEG;AACH,qBAAa,cAAc;IAInB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ9B,OAAO,CAAC,sBAAsB,CAAC,CAAwB;IAEvD,YACqB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,EACnE;IAEJ;;;;;OAKG;IACI,oBAAoB,EAAE,oBAAoB,CAiB/C;IAEF,OAAO,CAAC,mBAAmB;YAWb,YAAY;CAgB7B"}
1
+ {"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../../src/oauth/tokenRefresher.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;GAEG;AACH,qBAAa,cAAc;IAInB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ9B,OAAO,CAAC,sBAAsB,CAAC,CAAwB;IAEvD,YACqB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC1E;IAEJ;;;;OAIG;IACU,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiBtE;IAED;;;;;OAKG;IACU,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9F;IAED,OAAO,CAAC,mBAAmB;YAWb,YAAY;CAgB7B"}
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  */
17
17
 
18
- import { HTTPError, TokenRefreshLogoutError } from "../http-api/index.js";
18
+ import { HTTPError, TokenRefreshLogoutError } from "../http-api/errors.js";
19
19
  import { OAuth2HTTPError } from "./error.js";
20
20
  /**
21
21
  * Class responsible for refreshing OAuth2 access tokens
@@ -23,32 +23,42 @@ import { OAuth2HTTPError } from "./error.js";
23
23
  export class TokenRefresher {
24
24
  constructor(auth, onRefresh) {
25
25
  _defineProperty(this, "inflightRefreshRequest", void 0);
26
- /**
27
- * Attempt token refresh using given refresh token
28
- * @param refreshToken - refresh token to use in request with token issuer
29
- * @returns tokens - Promise that resolves with new access and refresh tokens
30
- * @throws when token refresh fails
31
- */
32
- _defineProperty(this, "tokenRefreshFunction", async refreshToken => {
33
- if (!this.inflightRefreshRequest) {
34
- this.inflightRefreshRequest = this.getNewTokens(refreshToken);
35
- }
36
- try {
37
- const tokens = await this.inflightRefreshRequest;
38
- return tokens;
39
- } catch (e) {
40
- // If we encounter a 40x error then signal that it should cause a logout by upgrading it to a TokenRefreshLogoutError
41
- if (e instanceof HTTPError && this.shouldLogoutOnError(e)) {
42
- throw new TokenRefreshLogoutError(e);
43
- }
44
- throw e;
45
- } finally {
46
- this.inflightRefreshRequest = undefined;
47
- }
48
- });
49
26
  this.auth = auth;
50
27
  this.onRefresh = onRefresh;
51
28
  }
29
+
30
+ /**
31
+ * Attempt token refresh using given refresh token
32
+ * @param refreshToken - refresh token to use in request with token issuer
33
+ * @throws when token refresh fails
34
+ */
35
+ async refreshTokens(refreshToken) {
36
+ if (!this.inflightRefreshRequest) {
37
+ this.inflightRefreshRequest = this.getNewTokens(refreshToken);
38
+ }
39
+ try {
40
+ const tokens = await this.inflightRefreshRequest;
41
+ return tokens;
42
+ } catch (e) {
43
+ // If we encounter a 40x error then signal that it should cause a logout by upgrading it to a TokenRefreshLogoutError
44
+ if (e instanceof HTTPError && this.shouldLogoutOnError(e)) {
45
+ throw new TokenRefreshLogoutError(e);
46
+ }
47
+ throw e;
48
+ } finally {
49
+ this.inflightRefreshRequest = undefined;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Revoke a token with the OP, e.g. as part of logging out.
55
+ * @param token - the token to revoke
56
+ * @param type - the type of token, acts as a hint to the OP
57
+ * @throws when revocation fails
58
+ */
59
+ async revokeToken(token, type) {
60
+ await this.auth.revokeToken(token, type);
61
+ }
52
62
  shouldLogoutOnError(error) {
53
63
  // Treat as logout as per https://spec.matrix.org/v1.18/client-server-api/#refresh-token-grant
54
64
  // after making sure it is an RFC 6749 section 5.2 error response
@@ -1 +1 @@
1
- {"version":3,"file":"tokenRefresher.js","names":[],"sources":["../../src/oauth/tokenRefresher.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type AccessTokens, HTTPError, type TokenRefreshFunction, TokenRefreshLogoutError } from \"../http-api/index.ts\";\nimport { OAuth2HTTPError } from \"./error.ts\";\nimport { type OAuth2 } from \"./index.ts\";\n\n/**\n * Class responsible for refreshing OAuth2 access tokens\n */\nexport class TokenRefresher {\n private inflightRefreshRequest?: Promise<AccessTokens>;\n\n public constructor(\n private readonly auth: OAuth2,\n private readonly onRefresh: (tokens: AccessTokens) => Promise<void>,\n ) {}\n\n /**\n * Attempt token refresh using given refresh token\n * @param refreshToken - refresh token to use in request with token issuer\n * @returns tokens - Promise that resolves with new access and refresh tokens\n * @throws when token refresh fails\n */\n public tokenRefreshFunction: TokenRefreshFunction = async (refreshToken: string): Promise<AccessTokens> => {\n if (!this.inflightRefreshRequest) {\n this.inflightRefreshRequest = this.getNewTokens(refreshToken);\n }\n\n try {\n const tokens = await this.inflightRefreshRequest;\n return tokens;\n } catch (e) {\n // If we encounter a 40x error then signal that it should cause a logout by upgrading it to a TokenRefreshLogoutError\n if (e instanceof HTTPError && this.shouldLogoutOnError(e)) {\n throw new TokenRefreshLogoutError(e);\n }\n throw e;\n } finally {\n this.inflightRefreshRequest = undefined;\n }\n };\n\n private shouldLogoutOnError(error: HTTPError): boolean {\n // Treat as logout as per https://spec.matrix.org/v1.18/client-server-api/#refresh-token-grant\n // after making sure it is an RFC 6749 section 5.2 error response\n return (\n error instanceof OAuth2HTTPError &&\n typeof error.httpStatus === \"number\" &&\n error.httpStatus < 500 &&\n error.httpStatus >= 400\n );\n }\n\n private async getNewTokens(refreshToken: string): Promise<AccessTokens> {\n const requestStart = Date.now();\n\n const response = await this.auth.performRefreshTokenGrant(refreshToken);\n\n const tokens = {\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n // We use the request start time to calculate the expiry time as we don't know when the server received our request\n expiry: response.expires_in ? new Date(requestStart + response.expires_in * 1000) : undefined,\n } satisfies AccessTokens;\n\n await this.onRefresh(tokens);\n\n return tokens;\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAA4B,SAAS,EAA6B,uBAAuB,QAAQ,sBAAsB;AACvH,SAAS,eAAe,QAAQ,YAAY;AAG5C;AACA;AACA;AACA,OAAO,MAAM,cAAc,CAAC;EAGjB,WAAW,CACG,IAAY,EACZ,SAAkD,EACrE;IAAA;IAEF;AACJ;AACA;AACA;AACA;AACA;IALI,8CAMoD,MAAO,YAAoB,IAA4B;MACvG,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;QAC9B,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC;MACjE;MAEA,IAAI;QACA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB;QAChD,OAAO,MAAM;MACjB,CAAC,CAAC,OAAO,CAAC,EAAE;QACR;QACA,IAAI,CAAC,YAAY,SAAS,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE;UACvD,MAAM,IAAI,uBAAuB,CAAC,CAAC,CAAC;QACxC;QACA,MAAM,CAAC;MACX,CAAC,SAAS;QACN,IAAI,CAAC,sBAAsB,GAAG,SAAS;MAC3C;IACJ,CAAC;IAAA,KA3BoB,IAAY,GAAZ,IAAY;IAAA,KACZ,SAAkD,GAAlD,SAAkD;EACpE;EA2BK,mBAAmB,CAAC,KAAgB,EAAW;IACnD;IACA;IACA,OACI,KAAK,YAAY,eAAe,IAChC,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IACpC,KAAK,CAAC,UAAU,GAAG,GAAG,IACtB,KAAK,CAAC,UAAU,IAAI,GAAG;EAE/B;EAEA,MAAc,YAAY,CAAC,YAAoB,EAAyB;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAC,YAAY,CAAC;IAEvE,MAAM,MAAM,GAAG;MACX,WAAW,EAAE,QAAQ,CAAC,YAAY;MAClC,YAAY,EAAE,QAAQ,CAAC,aAAa;MACpC;MACA,MAAM,EAAE,QAAQ,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG;IACxF,CAAwB;IAExB,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;IAE5B,OAAO,MAAM;EACjB;AACJ","ignoreList":[]}
1
+ {"version":3,"file":"tokenRefresher.js","names":[],"sources":["../../src/oauth/tokenRefresher.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { HTTPError, TokenRefreshLogoutError } from \"../http-api/errors.ts\";\nimport { type AccessTokens } from \"../http-api/interface.ts\";\nimport { OAuth2HTTPError } from \"./error.ts\";\nimport { type OAuth2 } from \"./index.ts\";\n\n/**\n * Class responsible for refreshing OAuth2 access tokens\n */\nexport class TokenRefresher {\n private inflightRefreshRequest?: Promise<AccessTokens>;\n\n public constructor(\n private readonly auth: OAuth2,\n private readonly onRefresh: (tokens: AccessTokens) => void | Promise<void>,\n ) {}\n\n /**\n * Attempt token refresh using given refresh token\n * @param refreshToken - refresh token to use in request with token issuer\n * @throws when token refresh fails\n */\n public async refreshTokens(refreshToken: string): Promise<AccessTokens> {\n if (!this.inflightRefreshRequest) {\n this.inflightRefreshRequest = this.getNewTokens(refreshToken);\n }\n\n try {\n const tokens = await this.inflightRefreshRequest;\n return tokens;\n } catch (e) {\n // If we encounter a 40x error then signal that it should cause a logout by upgrading it to a TokenRefreshLogoutError\n if (e instanceof HTTPError && this.shouldLogoutOnError(e)) {\n throw new TokenRefreshLogoutError(e);\n }\n throw e;\n } finally {\n this.inflightRefreshRequest = undefined;\n }\n }\n\n /**\n * Revoke a token with the OP, e.g. as part of logging out.\n * @param token - the token to revoke\n * @param type - the type of token, acts as a hint to the OP\n * @throws when revocation fails\n */\n public async revokeToken(token: string, type?: \"access_token\" | \"refresh_token\"): Promise<void> {\n await this.auth.revokeToken(token, type);\n }\n\n private shouldLogoutOnError(error: HTTPError): boolean {\n // Treat as logout as per https://spec.matrix.org/v1.18/client-server-api/#refresh-token-grant\n // after making sure it is an RFC 6749 section 5.2 error response\n return (\n error instanceof OAuth2HTTPError &&\n typeof error.httpStatus === \"number\" &&\n error.httpStatus < 500 &&\n error.httpStatus >= 400\n );\n }\n\n private async getNewTokens(refreshToken: string): Promise<AccessTokens> {\n const requestStart = Date.now();\n\n const response = await this.auth.performRefreshTokenGrant(refreshToken);\n\n const tokens = {\n accessToken: response.access_token,\n refreshToken: response.refresh_token,\n // We use the request start time to calculate the expiry time as we don't know when the server received our request\n expiry: response.expires_in ? new Date(requestStart + response.expires_in * 1000) : undefined,\n } satisfies AccessTokens;\n\n await this.onRefresh(tokens);\n\n return tokens;\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAAS,SAAS,EAAE,uBAAuB,QAAQ,uBAAuB;AAE1E,SAAS,eAAe,QAAQ,YAAY;AAG5C;AACA;AACA;AACA,OAAO,MAAM,cAAc,CAAC;EAGjB,WAAW,CACG,IAAY,EACZ,SAAyD,EAC5E;IAAA;IAAA,KAFmB,IAAY,GAAZ,IAAY;IAAA,KACZ,SAAyD,GAAzD,SAAyD;EAC3E;;EAEH;AACJ;AACA;AACA;AACA;EACI,MAAa,aAAa,CAAC,YAAoB,EAAyB;IACpE,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;MAC9B,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC;IACjE;IAEA,IAAI;MACA,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,sBAAsB;MAChD,OAAO,MAAM;IACjB,CAAC,CAAC,OAAO,CAAC,EAAE;MACR;MACA,IAAI,CAAC,YAAY,SAAS,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE;QACvD,MAAM,IAAI,uBAAuB,CAAC,CAAC,CAAC;MACxC;MACA,MAAM,CAAC;IACX,CAAC,SAAS;MACN,IAAI,CAAC,sBAAsB,GAAG,SAAS;IAC3C;EACJ;;EAEA;AACJ;AACA;AACA;AACA;AACA;EACI,MAAa,WAAW,CAAC,KAAa,EAAE,IAAuC,EAAiB;IAC5F,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC;EAC5C;EAEQ,mBAAmB,CAAC,KAAgB,EAAW;IACnD;IACA;IACA,OACI,KAAK,YAAY,eAAe,IAChC,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IACpC,KAAK,CAAC,UAAU,GAAG,GAAG,IACtB,KAAK,CAAC,UAAU,IAAI,GAAG;EAE/B;EAEA,MAAc,YAAY,CAAC,YAAoB,EAAyB;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAC,YAAY,CAAC;IAEvE,MAAM,MAAM,GAAG;MACX,WAAW,EAAE,QAAQ,CAAC,YAAY;MAClC,YAAY,EAAE,QAAQ,CAAC,aAAa;MACpC;MACA,MAAM,EAAE,QAAQ,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG;IACxF,CAAwB;IAExB,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;IAE5B,OAAO,MAAM;EACjB;AACJ","ignoreList":[]}
@@ -11,31 +11,30 @@ import { type IMinimalEvent } from "./sync-accumulator.ts";
11
11
  * the most recently received receipt in each thread.
12
12
  */
13
13
  export declare class ReceiptAccumulator {
14
- /** user_id -\> most-recently-received unthreaded receipt */
14
+ /** user_id -\> receipt_type -\> most-recently-received unthreaded receipt */
15
15
  private unthreadedReadReceipts;
16
- /** thread_id -\> user_id -\> most-recently-received receipt for this thread */
16
+ /** thread_id -\> user_id -\> receipt_type -\> most-recently-received receipt for this thread */
17
17
  private threadedReadReceipts;
18
18
  /**
19
19
  * Provide an unthreaded receipt for this user. Overwrites any other
20
- * unthreaded receipt we have for this user.
20
+ * unthreaded receipt of this type we have for this user.
21
21
  */
22
22
  private setUnthreaded;
23
23
  /**
24
24
  * Provide a receipt for this user in this thread. Overwrites any other
25
- * receipt we have for this user in this thread.
25
+ * receipt of this type we have for this user in this thread.
26
26
  */
27
27
  private setThreaded;
28
28
  /**
29
- * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the
30
- * most recently-received unthreaded receipts for each user.
31
- * @yields pairs of [userId, AccumulatedReceipt]
29
+ * @yields pairs of [userId, AccumulatedReceipt] for every receipt type.
30
+ */
31
+ private allForUsers;
32
+ /**
33
+ * @yields all unthreaded receipts of each type for each user.
32
34
  */
33
35
  private allUnthreaded;
34
36
  /**
35
- * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the
36
- * most recently-received threaded receipts for each user, in all
37
- * threads.
38
- * @yields pairs of [userId, AccumulatedReceipt]
37
+ * @yields all threaded receipts of each type for each user, in all threads.
39
38
  */
40
39
  private allThreaded;
41
40
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"receipt-accumulator.d.ts","sourceRoot":"","sources":["../src/receipt-accumulator.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAW3D;;;;;;;;;;GAUG;AACH,qBAAa,kBAAkB;IAC3B,4DAA4D;IAC5D,OAAO,CAAC,sBAAsB,CAA8C;IAE5E,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB,CAE1B;IAEF;;;OAGG;IACH,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAInB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;;OAKG;IACH,OAAO,CAAE,WAAW;IAQpB;;;OAGG;IACI,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,IAAI,CA4DvE;IAED;;;;OAIG;IACI,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA+BxE;CACJ"}
1
+ {"version":3,"file":"receipt-accumulator.d.ts","sourceRoot":"","sources":["../src/receipt-accumulator.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAW3D;;;;;;;;;;GAUG;AACH,qBAAa,kBAAkB;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,sBAAsB,CAE5B;IAEF,gGAAgG;IAChG,OAAO,CAAC,oBAAoB,CACsC;IAElE;;;OAGG;IACH,OAAO,CAAC,aAAa;IAIrB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAE,WAAW;IAUpB;;OAEG;IACH,OAAO,CAAE,aAAa;IAItB;;OAEG;IACH,OAAO,CAAE,WAAW;IAMpB;;;OAGG;IACI,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,SAAS,GAAG,IAAI,CAyDvE;IAED;;;;OAIG;IACI,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA+BxE;CACJ"}
@@ -30,47 +30,51 @@ import { isSupportedReceiptType, MapWithDefault, recursiveMapToObject } from "./
30
30
  */
31
31
  export class ReceiptAccumulator {
32
32
  constructor() {
33
- /** user_id -\> most-recently-received unthreaded receipt */
34
- _defineProperty(this, "unthreadedReadReceipts", new Map());
35
- /** thread_id -\> user_id -\> most-recently-received receipt for this thread */
36
- _defineProperty(this, "threadedReadReceipts", new MapWithDefault(() => new Map()));
33
+ /** user_id -\> receipt_type -\> most-recently-received unthreaded receipt */
34
+ _defineProperty(this, "unthreadedReadReceipts", new MapWithDefault(() => new Map()));
35
+ /** thread_id -\> user_id -\> receipt_type -\> most-recently-received receipt for this thread */
36
+ _defineProperty(this, "threadedReadReceipts", new MapWithDefault(() => new MapWithDefault(() => new Map())));
37
37
  }
38
38
  /**
39
39
  * Provide an unthreaded receipt for this user. Overwrites any other
40
- * unthreaded receipt we have for this user.
40
+ * unthreaded receipt of this type we have for this user.
41
41
  */
42
42
  setUnthreaded(userId, receipt) {
43
- this.unthreadedReadReceipts.set(userId, receipt);
43
+ this.unthreadedReadReceipts.getOrCreate(userId).set(receipt.type, receipt);
44
44
  }
45
45
 
46
46
  /**
47
47
  * Provide a receipt for this user in this thread. Overwrites any other
48
- * receipt we have for this user in this thread.
48
+ * receipt of this type we have for this user in this thread.
49
49
  */
50
50
  setThreaded(threadId, userId, receipt) {
51
- this.threadedReadReceipts.getOrCreate(threadId).set(userId, receipt);
51
+ this.threadedReadReceipts.getOrCreate(threadId).getOrCreate(userId).set(receipt.type, receipt);
52
52
  }
53
53
 
54
54
  /**
55
- * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the
56
- * most recently-received unthreaded receipts for each user.
57
- * @yields pairs of [userId, AccumulatedReceipt]
55
+ * @yields pairs of [userId, AccumulatedReceipt] for every receipt type.
58
56
  */
59
- allUnthreaded() {
60
- return this.unthreadedReadReceipts.entries();
57
+ *allForUsers(receiptsByUser) {
58
+ for (const [userId, receiptsForUser] of receiptsByUser) {
59
+ for (const receipt of receiptsForUser.values()) {
60
+ yield [userId, receipt];
61
+ }
62
+ }
61
63
  }
62
64
 
63
65
  /**
64
- * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the
65
- * most recently-received threaded receipts for each user, in all
66
- * threads.
67
- * @yields pairs of [userId, AccumulatedReceipt]
66
+ * @yields all unthreaded receipts of each type for each user.
67
+ */
68
+ *allUnthreaded() {
69
+ yield* this.allForUsers(this.unthreadedReadReceipts);
70
+ }
71
+
72
+ /**
73
+ * @yields all threaded receipts of each type for each user, in all threads.
68
74
  */
69
75
  *allThreaded() {
70
76
  for (const receiptsForThread of this.threadedReadReceipts.values()) {
71
- for (const e of receiptsForThread.entries()) {
72
- yield e;
73
- }
77
+ yield* this.allForUsers(receiptsForThread);
74
78
  }
75
79
  }
76
80
 
@@ -91,11 +95,8 @@ export class ReceiptAccumulator {
91
95
  // but they are keyed in the event as:
92
96
  // content:{ $event_id: { $receipt_type: { $user_id: {json} }}}
93
97
  // so store them in the former so we can accumulate receipt deltas
94
- // quickly and efficiently (we expect a lot of them). Fold the
95
- // receipt type into the key name since we only have 1 at the
96
- // moment (m.read) and nested JSON objects are slower and more
97
- // of a hassle to work with. We'll inflate this back out when
98
- // getJSON() is called.
98
+ // quickly and efficiently (we expect a lot of them). We'll inflate
99
+ // this back out when getJSON() is called.
99
100
  Object.keys(e.content).forEach(eventId => {
100
101
  Object.entries(e.content[eventId]).forEach(([key, value]) => {
101
102
  if (!isSupportedReceiptType(key)) return;
@@ -1 +1 @@
1
- {"version":3,"file":"receipt-accumulator.js","names":[],"sources":["../src/receipt-accumulator.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type IMinimalEvent } from \"./sync-accumulator.ts\";\nimport { EventType } from \"./@types/event.ts\";\nimport { isSupportedReceiptType, MapWithDefault, recursiveMapToObject } from \"./utils.ts\";\nimport { type ReceiptContent, type ReceiptType } from \"./@types/read_receipts.ts\";\n\ninterface AccumulatedReceipt {\n data: IMinimalEvent;\n type: ReceiptType;\n eventId: string;\n}\n\n/**\n * Summarises the read receipts within a room. Used by the sync accumulator.\n *\n * Given receipts for users, picks the most recently-received one and provides\n * the results in a new fake receipt event returned from\n * buildAccumulatedReceiptEvent().\n *\n * Handles unthreaded receipts and receipts in each thread separately, so the\n * returned event contains the most recently received unthreaded receipt, and\n * the most recently received receipt in each thread.\n */\nexport class ReceiptAccumulator {\n /** user_id -\\> most-recently-received unthreaded receipt */\n private unthreadedReadReceipts: Map<string, AccumulatedReceipt> = new Map();\n\n /** thread_id -\\> user_id -\\> most-recently-received receipt for this thread */\n private threadedReadReceipts: MapWithDefault<string, Map<string, AccumulatedReceipt>> = new MapWithDefault(\n () => new Map(),\n );\n\n /**\n * Provide an unthreaded receipt for this user. Overwrites any other\n * unthreaded receipt we have for this user.\n */\n private setUnthreaded(userId: string, receipt: AccumulatedReceipt): void {\n this.unthreadedReadReceipts.set(userId, receipt);\n }\n\n /**\n * Provide a receipt for this user in this thread. Overwrites any other\n * receipt we have for this user in this thread.\n */\n private setThreaded(threadId: string, userId: string, receipt: AccumulatedReceipt): void {\n this.threadedReadReceipts.getOrCreate(threadId).set(userId, receipt);\n }\n\n /**\n * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the\n * most recently-received unthreaded receipts for each user.\n * @yields pairs of [userId, AccumulatedReceipt]\n */\n private allUnthreaded(): IterableIterator<[string, AccumulatedReceipt]> {\n return this.unthreadedReadReceipts.entries();\n }\n\n /**\n * @returns an iterator of pairs of [userId, AccumulatedReceipt] - all the\n * most recently-received threaded receipts for each user, in all\n * threads.\n * @yields pairs of [userId, AccumulatedReceipt]\n */\n private *allThreaded(): IterableIterator<[string, AccumulatedReceipt]> {\n for (const receiptsForThread of this.threadedReadReceipts.values()) {\n for (const e of receiptsForThread.entries()) {\n yield e;\n }\n }\n }\n\n /**\n * Given a list of ephemeral events, find the receipts and store the\n * relevant ones to be returned later from buildAccumulatedReceiptEvent().\n */\n public consumeEphemeralEvents(events: IMinimalEvent[] | undefined): void {\n events?.forEach((e) => {\n if (e.type !== EventType.Receipt || !e.content) {\n // This means we'll drop unknown ephemeral events but that\n // seems okay.\n return;\n }\n\n // Handle m.receipt events. They clobber based on:\n // (user_id, receipt_type)\n // but they are keyed in the event as:\n // content:{ $event_id: { $receipt_type: { $user_id: {json} }}}\n // so store them in the former so we can accumulate receipt deltas\n // quickly and efficiently (we expect a lot of them). Fold the\n // receipt type into the key name since we only have 1 at the\n // moment (m.read) and nested JSON objects are slower and more\n // of a hassle to work with. We'll inflate this back out when\n // getJSON() is called.\n Object.keys(e.content).forEach((eventId) => {\n Object.entries<ReceiptContent>(e.content[eventId]).forEach(([key, value]) => {\n if (!isSupportedReceiptType(key)) return;\n\n for (const userId of Object.keys(value)) {\n const data = e.content[eventId][key][userId];\n\n const receipt = {\n data: e.content[eventId][key][userId],\n type: key as ReceiptType,\n eventId,\n };\n\n // In a world that supports threads, read receipts normally have\n // a `thread_id` which is either the thread they belong in or\n // `MAIN_ROOM_TIMELINE`, so we normally use `setThreaded(...)`\n // here. The `MAIN_ROOM_TIMELINE` is just treated as another\n // thread.\n //\n // We still encounter read receipts that are \"unthreaded\"\n // (missing the `thread_id` property). These come from clients\n // that don't support threads, and from threaded clients that\n // are doing a \"Mark room as read\" operation. Unthreaded\n // receipts mark everything \"before\" them as read, in all\n // threads, where \"before\" means in Sync Order i.e. the order\n // the events were received from the homeserver in a sync.\n // [Note: we have some bugs where we use timestamp order instead\n // of Sync Order, because we don't correctly remember the Sync\n // Order. See #3325.]\n //\n // Calling the wrong method will cause incorrect behavior like\n // messages re-appearing as \"new\" when you already read them\n // previously.\n if (!data.thread_id) {\n this.setUnthreaded(userId, receipt);\n } else {\n this.setThreaded(data.thread_id, userId, receipt);\n }\n }\n });\n });\n });\n }\n\n /**\n * Build a receipt event that contains all relevant information for this\n * room, taking the most recently received receipt for each user in an\n * unthreaded context, and in each thread.\n */\n public buildAccumulatedReceiptEvent(roomId: string): IMinimalEvent | null {\n const receiptEvent: IMinimalEvent = {\n type: EventType.Receipt,\n room_id: roomId,\n content: {\n // $event_id: { \"m.read\": { $user_id: $json } }\n },\n };\n\n const receiptEventContent: MapWithDefault<\n string,\n MapWithDefault<ReceiptType, Map<string, object>>\n > = new MapWithDefault(() => new MapWithDefault(() => new Map()));\n\n for (const [userId, receiptData] of this.allUnthreaded()) {\n receiptEventContent\n .getOrCreate(receiptData.eventId)\n .getOrCreate(receiptData.type)\n .set(userId, receiptData.data);\n }\n\n for (const [userId, receiptData] of this.allThreaded()) {\n receiptEventContent\n .getOrCreate(receiptData.eventId)\n .getOrCreate(receiptData.type)\n .set(userId, receiptData.data);\n }\n\n receiptEvent.content = recursiveMapToObject(receiptEventContent);\n\n return receiptEventContent.size > 0 ? receiptEvent : null;\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SAAS,SAAS,QAAQ,mBAAmB;AAC7C,SAAS,sBAAsB,EAAE,cAAc,EAAE,oBAAoB,QAAQ,YAAY;AASzF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,kBAAkB,CAAC;EAAA;IAC5B;IAAA,gDACkE,IAAI,GAAG,CAAC,CAAC;IAE3E;IAAA,8CACwF,IAAI,cAAc,CACtG,MAAM,IAAI,GAAG,CAAC,CAClB,CAAC;EAAA;EAED;AACJ;AACA;AACA;EACY,aAAa,CAAC,MAAc,EAAE,OAA2B,EAAQ;IACrE,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;EACpD;;EAEA;AACJ;AACA;AACA;EACY,WAAW,CAAC,QAAgB,EAAE,MAAc,EAAE,OAA2B,EAAQ;IACrF,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;EACxE;;EAEA;AACJ;AACA;AACA;AACA;EACY,aAAa,GAAmD;IACpE,OAAO,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;EAChD;;EAEA;AACJ;AACA;AACA;AACA;AACA;EACI,CAAS,WAAW,GAAmD;IACnE,KAAK,MAAM,iBAAiB,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,EAAE;MAChE,KAAK,MAAM,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,EAAE;QACzC,MAAM,CAAC;MACX;IACJ;EACJ;;EAEA;AACJ;AACA;AACA;EACW,sBAAsB,CAAC,MAAmC,EAAQ;IACrE,MAAM,EAAE,OAAO,CAAE,CAAC,IAAK;MACnB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE;QAC5C;QACA;QACA;MACJ;;MAEA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MACA,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAE,OAAO,IAAK;QACxC,MAAM,CAAC,OAAO,CAAiB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK;UACzE,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE;UAElC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACrC,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YAE5C,MAAM,OAAO,GAAG;cACZ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;cACrC,IAAI,EAAE,GAAkB;cACxB;YACJ,CAAC;;YAED;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;cACjB,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC;YACvC,CAAC,MAAM;cACH,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;YACrD;UACJ;QACJ,CAAC,CAAC;MACN,CAAC,CAAC;IACN,CAAC,CAAC;EACN;;EAEA;AACJ;AACA;AACA;AACA;EACW,4BAA4B,CAAC,MAAc,EAAwB;IACtE,MAAM,YAA2B,GAAG;MAChC,IAAI,EAAE,SAAS,CAAC,OAAO;MACvB,OAAO,EAAE,MAAM;MACf,OAAO,EAAE;QACL;MAAA;IAER,CAAC;IAED,MAAM,mBAGL,GAAG,IAAI,cAAc,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;IAEjE,KAAK,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE;MACtD,mBAAmB,CACd,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAChC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAC7B,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC;IACtC;IAEA,KAAK,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE;MACpD,mBAAmB,CACd,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAChC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAC7B,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC;IACtC;IAEA,YAAY,CAAC,OAAO,GAAG,oBAAoB,CAAC,mBAAmB,CAAC;IAEhE,OAAO,mBAAmB,CAAC,IAAI,GAAG,CAAC,GAAG,YAAY,GAAG,IAAI;EAC7D;AACJ","ignoreList":[]}
1
+ {"version":3,"file":"receipt-accumulator.js","names":[],"sources":["../src/receipt-accumulator.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type IMinimalEvent } from \"./sync-accumulator.ts\";\nimport { EventType } from \"./@types/event.ts\";\nimport { isSupportedReceiptType, MapWithDefault, recursiveMapToObject } from \"./utils.ts\";\nimport { type ReceiptContent, type ReceiptType } from \"./@types/read_receipts.ts\";\n\ninterface AccumulatedReceipt {\n data: IMinimalEvent;\n type: ReceiptType;\n eventId: string;\n}\n\n/**\n * Summarises the read receipts within a room. Used by the sync accumulator.\n *\n * Given receipts for users, picks the most recently-received one and provides\n * the results in a new fake receipt event returned from\n * buildAccumulatedReceiptEvent().\n *\n * Handles unthreaded receipts and receipts in each thread separately, so the\n * returned event contains the most recently received unthreaded receipt, and\n * the most recently received receipt in each thread.\n */\nexport class ReceiptAccumulator {\n /** user_id -\\> receipt_type -\\> most-recently-received unthreaded receipt */\n private unthreadedReadReceipts: MapWithDefault<string, Map<ReceiptType, AccumulatedReceipt>> = new MapWithDefault(\n () => new Map(),\n );\n\n /** thread_id -\\> user_id -\\> receipt_type -\\> most-recently-received receipt for this thread */\n private threadedReadReceipts: MapWithDefault<string, MapWithDefault<string, Map<ReceiptType, AccumulatedReceipt>>> =\n new MapWithDefault(() => new MapWithDefault(() => new Map()));\n\n /**\n * Provide an unthreaded receipt for this user. Overwrites any other\n * unthreaded receipt of this type we have for this user.\n */\n private setUnthreaded(userId: string, receipt: AccumulatedReceipt): void {\n this.unthreadedReadReceipts.getOrCreate(userId).set(receipt.type, receipt);\n }\n\n /**\n * Provide a receipt for this user in this thread. Overwrites any other\n * receipt of this type we have for this user in this thread.\n */\n private setThreaded(threadId: string, userId: string, receipt: AccumulatedReceipt): void {\n this.threadedReadReceipts.getOrCreate(threadId).getOrCreate(userId).set(receipt.type, receipt);\n }\n\n /**\n * @yields pairs of [userId, AccumulatedReceipt] for every receipt type.\n */\n private *allForUsers(\n receiptsByUser: ReadonlyMap<string, ReadonlyMap<ReceiptType, AccumulatedReceipt>>,\n ): IterableIterator<[string, AccumulatedReceipt]> {\n for (const [userId, receiptsForUser] of receiptsByUser) {\n for (const receipt of receiptsForUser.values()) {\n yield [userId, receipt];\n }\n }\n }\n\n /**\n * @yields all unthreaded receipts of each type for each user.\n */\n private *allUnthreaded(): IterableIterator<[string, AccumulatedReceipt]> {\n yield* this.allForUsers(this.unthreadedReadReceipts);\n }\n\n /**\n * @yields all threaded receipts of each type for each user, in all threads.\n */\n private *allThreaded(): IterableIterator<[string, AccumulatedReceipt]> {\n for (const receiptsForThread of this.threadedReadReceipts.values()) {\n yield* this.allForUsers(receiptsForThread);\n }\n }\n\n /**\n * Given a list of ephemeral events, find the receipts and store the\n * relevant ones to be returned later from buildAccumulatedReceiptEvent().\n */\n public consumeEphemeralEvents(events: IMinimalEvent[] | undefined): void {\n events?.forEach((e) => {\n if (e.type !== EventType.Receipt || !e.content) {\n // This means we'll drop unknown ephemeral events but that\n // seems okay.\n return;\n }\n\n // Handle m.receipt events. They clobber based on:\n // (user_id, receipt_type)\n // but they are keyed in the event as:\n // content:{ $event_id: { $receipt_type: { $user_id: {json} }}}\n // so store them in the former so we can accumulate receipt deltas\n // quickly and efficiently (we expect a lot of them). We'll inflate\n // this back out when getJSON() is called.\n Object.keys(e.content).forEach((eventId) => {\n Object.entries<ReceiptContent>(e.content[eventId]).forEach(([key, value]) => {\n if (!isSupportedReceiptType(key)) return;\n\n for (const userId of Object.keys(value)) {\n const data = e.content[eventId][key][userId];\n\n const receipt = {\n data: e.content[eventId][key][userId],\n type: key as ReceiptType,\n eventId,\n };\n\n // In a world that supports threads, read receipts normally have\n // a `thread_id` which is either the thread they belong in or\n // `MAIN_ROOM_TIMELINE`, so we normally use `setThreaded(...)`\n // here. The `MAIN_ROOM_TIMELINE` is just treated as another\n // thread.\n //\n // We still encounter read receipts that are \"unthreaded\"\n // (missing the `thread_id` property). These come from clients\n // that don't support threads, and from threaded clients that\n // are doing a \"Mark room as read\" operation. Unthreaded\n // receipts mark everything \"before\" them as read, in all\n // threads, where \"before\" means in Sync Order i.e. the order\n // the events were received from the homeserver in a sync.\n // [Note: we have some bugs where we use timestamp order instead\n // of Sync Order, because we don't correctly remember the Sync\n // Order. See #3325.]\n //\n // Calling the wrong method will cause incorrect behavior like\n // messages re-appearing as \"new\" when you already read them\n // previously.\n if (!data.thread_id) {\n this.setUnthreaded(userId, receipt);\n } else {\n this.setThreaded(data.thread_id, userId, receipt);\n }\n }\n });\n });\n });\n }\n\n /**\n * Build a receipt event that contains all relevant information for this\n * room, taking the most recently received receipt for each user in an\n * unthreaded context, and in each thread.\n */\n public buildAccumulatedReceiptEvent(roomId: string): IMinimalEvent | null {\n const receiptEvent: IMinimalEvent = {\n type: EventType.Receipt,\n room_id: roomId,\n content: {\n // $event_id: { \"m.read\": { $user_id: $json } }\n },\n };\n\n const receiptEventContent: MapWithDefault<\n string,\n MapWithDefault<ReceiptType, Map<string, object>>\n > = new MapWithDefault(() => new MapWithDefault(() => new Map()));\n\n for (const [userId, receiptData] of this.allUnthreaded()) {\n receiptEventContent\n .getOrCreate(receiptData.eventId)\n .getOrCreate(receiptData.type)\n .set(userId, receiptData.data);\n }\n\n for (const [userId, receiptData] of this.allThreaded()) {\n receiptEventContent\n .getOrCreate(receiptData.eventId)\n .getOrCreate(receiptData.type)\n .set(userId, receiptData.data);\n }\n\n receiptEvent.content = recursiveMapToObject(receiptEventContent);\n\n return receiptEventContent.size > 0 ? receiptEvent : null;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The room summary endpoint, which describes a room the user may not be in.
3
+ * https://spec.matrix.org/latest/client-server-api/#room-summaries
4
+ *
5
+ * Not to be confused with {@link RoomSummary} in `./models/room-summary.ts`, which holds the
6
+ * heroes and member counts that `/sync` sends for rooms we are in.
7
+ */
8
+ import { type IPublicRoomsChunkRoom, type IServerVersions } from "./client.ts";
9
+ import { type IHttpOpts, type MatrixHttpApi } from "./http-api/index.ts";
10
+ import { type JoinRule } from "./@types/partials.ts";
11
+ import { type Membership } from "./@types/membership.ts";
12
+ /**
13
+ * The summary of a room, as returned by the room summary endpoint.
14
+ * https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1room_summaryroomidoralias
15
+ *
16
+ * Also covers the initial version of MSC3266 implemented in older versions of Synapse, which
17
+ * prefixed some of the fields. See {@link MatrixClient.getRoomSummary}.
18
+ */
19
+ export interface RoomSummary extends Omit<IPublicRoomsChunkRoom, "canonical_alias" | "aliases" | "join_rule"> {
20
+ /**
21
+ * The current membership of this user in the room.
22
+ */
23
+ "membership"?: Membership;
24
+ /**
25
+ * The join rule of the room. Unlike the join rule of a public rooms chunk, this may be any
26
+ * join rule, since the room does not have to be published to the room directory.
27
+ */
28
+ "join_rule"?: JoinRule;
29
+ /**
30
+ * The canonical alias of the room, if any.
31
+ */
32
+ "canonical_alias"?: string;
33
+ /**
34
+ * Version of the room.
35
+ */
36
+ "room_version"?: string;
37
+ /**
38
+ * The encryption algorithm used for this room, if the room is encrypted.
39
+ */
40
+ "encryption"?: string;
41
+ /**
42
+ * For restricted rooms, the room IDs which are specified by the join rule.
43
+ */
44
+ "allowed_room_ids"?: string[];
45
+ /**
46
+ * Version of the room.
47
+ * @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.room_version}.
48
+ */
49
+ "im.nheko.summary.room_version"?: string;
50
+ /**
51
+ * The encryption algorithm used for this room, if the room is encrypted.
52
+ * @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.encryption}.
53
+ */
54
+ "im.nheko.summary.encryption"?: string;
55
+ }
56
+ /**
57
+ * Fetch the summary of a room.
58
+ *
59
+ * Uses the path from the initial version of MSC3266, as implemented in older versions of Synapse,
60
+ * unless the server advertises support for the spec version which stabilised it.
61
+ *
62
+ * @param http - The HTTP API to make the request with.
63
+ * @param versions - The server's `/versions` response, used to pick the endpoint.
64
+ * @param roomIdOrAlias - The ID or alias of the room to get the summary of.
65
+ * @param via - The servers to attempt to request the summary from, when the local server cannot
66
+ * generate it (for instance, because it has no local user in the room).
67
+ */
68
+ export declare function fetchRoomSummary(http: MatrixHttpApi<IHttpOpts & {
69
+ onlyData: true;
70
+ }>, versions: IServerVersions, roomIdOrAlias: string, via?: string[]): Promise<RoomSummary>;
71
+ //# sourceMappingURL=room-summary-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-summary-api.d.ts","sourceRoot":"","sources":["../src/room-summary-api.ts"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAU,MAAM,qBAAqB,CAAC;AAC/F,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;;GAMG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,GAAG,SAAS,GAAG,WAAW,CAAC;IACzG;;OAEG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC;;;OAGG;IACH,6BAA6B,CAAC,EAAE,MAAM,CAAC;CAC1C;AAmBD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC5B,IAAI,EAAE,aAAa,CAAC,SAAS,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC,EACnD,QAAQ,EAAE,eAAe,EACzB,aAAa,EAAE,MAAM,EACrB,GAAG,CAAC,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,WAAW,CAAC,CAItB"}
@@ -0,0 +1,82 @@
1
+ /*
2
+ Copyright 2026 The Matrix.org Foundation C.I.C.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * The room summary endpoint, which describes a room the user may not be in.
19
+ * https://spec.matrix.org/latest/client-server-api/#room-summaries
20
+ *
21
+ * Not to be confused with {@link RoomSummary} in `./models/room-summary.ts`, which holds the
22
+ * heroes and member counts that `/sync` sends for rooms we are in.
23
+ */
24
+
25
+ import { ClientPrefix, Method } from "./http-api/index.js";
26
+ import * as utils from "./utils.js";
27
+
28
+ /**
29
+ * The summary of a room, as returned by the room summary endpoint.
30
+ * https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1room_summaryroomidoralias
31
+ *
32
+ * Also covers the initial version of MSC3266 implemented in older versions of Synapse, which
33
+ * prefixed some of the fields. See {@link MatrixClient.getRoomSummary}.
34
+ */
35
+
36
+ /** The endpoint stabilised in Matrix 1.15. */
37
+ const STABLE_ENDPOINT = {
38
+ path: "/room_summary/$roomid",
39
+ prefix: ClientPrefix.V1
40
+ };
41
+
42
+ /** The endpoint used by the initial version of MSC3266, as implemented in older versions of Synapse. */
43
+ const UNSTABLE_ENDPOINT = {
44
+ path: "/summary/$roomid",
45
+ prefix: "/_matrix/client/unstable/im.nheko.summary"
46
+ };
47
+
48
+ /** The spec version which stabilised MSC3266 as `/_matrix/client/v1/room_summary/{roomIdOrAlias}`. */
49
+ const STABLE_VERSION = "v1.15";
50
+
51
+ /** The unstable feature flag advertised for MSC3266. */
52
+ const UNSTABLE_FEATURE = "org.matrix.msc3266";
53
+
54
+ /** Whether the server advertises the stable room summary endpoint in its `/versions` response. */
55
+ function supportsStableEndpoint(versions) {
56
+ return versions.versions?.includes(STABLE_VERSION) || !!versions.unstable_features?.[UNSTABLE_FEATURE];
57
+ }
58
+
59
+ /**
60
+ * Fetch the summary of a room.
61
+ *
62
+ * Uses the path from the initial version of MSC3266, as implemented in older versions of Synapse,
63
+ * unless the server advertises support for the spec version which stabilised it.
64
+ *
65
+ * @param http - The HTTP API to make the request with.
66
+ * @param versions - The server's `/versions` response, used to pick the endpoint.
67
+ * @param roomIdOrAlias - The ID or alias of the room to get the summary of.
68
+ * @param via - The servers to attempt to request the summary from, when the local server cannot
69
+ * generate it (for instance, because it has no local user in the room).
70
+ */
71
+ export function fetchRoomSummary(http, versions, roomIdOrAlias, via) {
72
+ const endpoint = supportsStableEndpoint(versions) ? STABLE_ENDPOINT : UNSTABLE_ENDPOINT;
73
+ const path = utils.encodeUri(endpoint.path, {
74
+ $roomid: roomIdOrAlias
75
+ });
76
+ return http.authedRequest(Method.Get, path, {
77
+ via
78
+ }, undefined, {
79
+ prefix: endpoint.prefix
80
+ });
81
+ }
82
+ //# sourceMappingURL=room-summary-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-summary-api.js","names":[],"sources":["../src/room-summary-api.ts"],"sourcesContent":["/*\nCopyright 2026 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * The room summary endpoint, which describes a room the user may not be in.\n * https://spec.matrix.org/latest/client-server-api/#room-summaries\n *\n * Not to be confused with {@link RoomSummary} in `./models/room-summary.ts`, which holds the\n * heroes and member counts that `/sync` sends for rooms we are in.\n */\n\nimport { type IPublicRoomsChunkRoom, type IServerVersions } from \"./client.ts\";\nimport { ClientPrefix, type IHttpOpts, type MatrixHttpApi, Method } from \"./http-api/index.ts\";\nimport { type JoinRule } from \"./@types/partials.ts\";\nimport { type Membership } from \"./@types/membership.ts\";\nimport * as utils from \"./utils.ts\";\n\n/**\n * The summary of a room, as returned by the room summary endpoint.\n * https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1room_summaryroomidoralias\n *\n * Also covers the initial version of MSC3266 implemented in older versions of Synapse, which\n * prefixed some of the fields. See {@link MatrixClient.getRoomSummary}.\n */\nexport interface RoomSummary extends Omit<IPublicRoomsChunkRoom, \"canonical_alias\" | \"aliases\" | \"join_rule\"> {\n /**\n * The current membership of this user in the room.\n */\n \"membership\"?: Membership;\n /**\n * The join rule of the room. Unlike the join rule of a public rooms chunk, this may be any\n * join rule, since the room does not have to be published to the room directory.\n */\n \"join_rule\"?: JoinRule;\n /**\n * The canonical alias of the room, if any.\n */\n \"canonical_alias\"?: string;\n /**\n * Version of the room.\n */\n \"room_version\"?: string;\n /**\n * The encryption algorithm used for this room, if the room is encrypted.\n */\n \"encryption\"?: string;\n /**\n * For restricted rooms, the room IDs which are specified by the join rule.\n */\n \"allowed_room_ids\"?: string[];\n /**\n * Version of the room.\n * @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.room_version}.\n */\n \"im.nheko.summary.room_version\"?: string;\n /**\n * The encryption algorithm used for this room, if the room is encrypted.\n * @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.encryption}.\n */\n \"im.nheko.summary.encryption\"?: string;\n}\n\n/** The endpoint stabilised in Matrix 1.15. */\nconst STABLE_ENDPOINT = { path: \"/room_summary/$roomid\", prefix: ClientPrefix.V1 };\n\n/** The endpoint used by the initial version of MSC3266, as implemented in older versions of Synapse. */\nconst UNSTABLE_ENDPOINT = { path: \"/summary/$roomid\", prefix: \"/_matrix/client/unstable/im.nheko.summary\" };\n\n/** The spec version which stabilised MSC3266 as `/_matrix/client/v1/room_summary/{roomIdOrAlias}`. */\nconst STABLE_VERSION = \"v1.15\";\n\n/** The unstable feature flag advertised for MSC3266. */\nconst UNSTABLE_FEATURE = \"org.matrix.msc3266\";\n\n/** Whether the server advertises the stable room summary endpoint in its `/versions` response. */\nfunction supportsStableEndpoint(versions: IServerVersions): boolean {\n return versions.versions?.includes(STABLE_VERSION) || !!versions.unstable_features?.[UNSTABLE_FEATURE];\n}\n\n/**\n * Fetch the summary of a room.\n *\n * Uses the path from the initial version of MSC3266, as implemented in older versions of Synapse,\n * unless the server advertises support for the spec version which stabilised it.\n *\n * @param http - The HTTP API to make the request with.\n * @param versions - The server's `/versions` response, used to pick the endpoint.\n * @param roomIdOrAlias - The ID or alias of the room to get the summary of.\n * @param via - The servers to attempt to request the summary from, when the local server cannot\n * generate it (for instance, because it has no local user in the room).\n */\nexport function fetchRoomSummary(\n http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,\n versions: IServerVersions,\n roomIdOrAlias: string,\n via?: string[],\n): Promise<RoomSummary> {\n const endpoint = supportsStableEndpoint(versions) ? STABLE_ENDPOINT : UNSTABLE_ENDPOINT;\n const path = utils.encodeUri(endpoint.path, { $roomid: roomIdOrAlias });\n return http.authedRequest<RoomSummary>(Method.Get, path, { via }, undefined, { prefix: endpoint.prefix });\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SAAS,YAAY,EAAsC,MAAM,QAAQ,qBAAqB;AAG9F,OAAO,KAAK,KAAK,MAAM,YAAY;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;;AAuCA;AACA,MAAM,eAAe,GAAG;EAAE,IAAI,EAAE,uBAAuB;EAAE,MAAM,EAAE,YAAY,CAAC;AAAG,CAAC;;AAElF;AACA,MAAM,iBAAiB,GAAG;EAAE,IAAI,EAAE,kBAAkB;EAAE,MAAM,EAAE;AAA4C,CAAC;;AAE3G;AACA,MAAM,cAAc,GAAG,OAAO;;AAE9B;AACA,MAAM,gBAAgB,GAAG,oBAAoB;;AAE7C;AACA,SAAS,sBAAsB,CAAC,QAAyB,EAAW;EAChE,OAAO,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;AAC1G;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAAS,gBAAgB,CAC5B,IAAmD,EACnD,QAAyB,EACzB,aAAqB,EACrB,GAAc,EACM;EACpB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,QAAQ,CAAC,GAAG,eAAe,GAAG,iBAAiB;EACvF,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE;EAAc,CAAC,CAAC;EACvE,OAAO,IAAI,CAAC,aAAa,CAAc,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE;IAAE;EAAI,CAAC,EAAE,SAAS,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;EAAO,CAAC,CAAC;AAC7G","ignoreList":[]}