@fluidframework/routerlicious-driver 0.54.0 → 0.55.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.
package/.eslintrc.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  module.exports = {
7
7
  "extends": [
8
- "@fluidframework/eslint-config-fluid/eslint7"
8
+ "@fluidframework/eslint-config-fluid"
9
9
  ],
10
10
  "parserOptions": {
11
11
  "project": ["./tsconfig.json", "./src/test/tsconfig.json"]
@@ -19,7 +19,7 @@ const defaultRouterliciousDriverPolicies = {
19
19
  maxConcurrentOrdererRequests: 100,
20
20
  aggregateBlobsSmallerThanBytes: undefined,
21
21
  enableWholeSummaryUpload: false,
22
- enableRestLess: false,
22
+ enableRestLess: true,
23
23
  };
24
24
  /**
25
25
  * Factory for creating the routerlicious document service. Use this if you want to
@@ -1 +1 @@
1
- {"version":3,"file":"documentServiceFactory.js","sourceRoot":"","sources":["../src/documentServiceFactory.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport {\n IDocumentService,\n IDocumentServiceFactory,\n IResolvedUrl,\n} from \"@fluidframework/driver-definitions\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ISnapshotTree, ISummaryTree } from \"@fluidframework/protocol-definitions\";\nimport {\n ensureFluidResolvedUrl,\n getDocAttributesFromProtocolSummary,\n getQuorumValuesFromProtocolSummary,\n RateLimiter,\n} from \"@fluidframework/driver-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { DocumentService } from \"./documentService\";\nimport { IRouterliciousDriverPolicies } from \"./policies\";\nimport { ITokenProvider } from \"./tokens\";\nimport { RouterliciousOrdererRestWrapper } from \"./restWrapper\";\nimport { convertSummaryToCreateNewSummary } from \"./createNewUtils\";\nimport { parseFluidUrl, replaceDocumentIdInPath } from \"./urlUtils\";\nimport { InMemoryCache } from \"./cache\";\n\nconst defaultRouterliciousDriverPolicies: IRouterliciousDriverPolicies = {\n enablePrefetch: true,\n maxConcurrentStorageRequests: 100,\n maxConcurrentOrdererRequests: 100,\n aggregateBlobsSmallerThanBytes: undefined,\n enableWholeSummaryUpload: false,\n enableRestLess: false,\n};\n\n/**\n * Factory for creating the routerlicious document service. Use this if you want to\n * use the routerlicious implementation.\n */\nexport class RouterliciousDocumentServiceFactory implements IDocumentServiceFactory {\n public readonly protocolName = \"fluid:\";\n private readonly driverPolicies: IRouterliciousDriverPolicies;\n private readonly blobCache = new InMemoryCache<ArrayBufferLike>();\n private readonly snapshotTreeCache = new InMemoryCache<ISnapshotTree>();\n\n constructor(\n private readonly tokenProvider: ITokenProvider,\n driverPolicies: Partial<IRouterliciousDriverPolicies> = {},\n ) {\n this.driverPolicies = {\n ...defaultRouterliciousDriverPolicies,\n ...driverPolicies,\n };\n }\n\n public async createContainer(\n createNewSummary: ISummaryTree | undefined,\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n assert(!!createNewSummary, 0x204 /* \"create empty file not supported\" */);\n assert(!!resolvedUrl.endpoints.ordererUrl, 0x0b2 /* \"Missing orderer URL!\" */);\n const parsedUrl = parseFluidUrl(resolvedUrl.url);\n if (!parsedUrl.pathname) {\n throw new Error(\"Parsed url should contain tenant and doc Id!!\");\n }\n const [, tenantId] = parsedUrl.pathname.split(\"/\");\n\n const protocolSummary = createNewSummary.tree[\".protocol\"] as ISummaryTree;\n const appSummary = createNewSummary.tree[\".app\"] as ISummaryTree;\n if (!(protocolSummary && appSummary)) {\n throw new Error(\"Protocol and App Summary required in the full summary\");\n }\n const documentAttributes = getDocAttributesFromProtocolSummary(protocolSummary);\n const quorumValues = getQuorumValuesFromProtocolSummary(protocolSummary);\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n const rateLimiter = new RateLimiter(this.driverPolicies.maxConcurrentOrdererRequests);\n const ordererRestWrapper = await RouterliciousOrdererRestWrapper.load(\n tenantId,\n undefined,\n this.tokenProvider,\n logger2,\n rateLimiter,\n this.driverPolicies.enableRestLess,\n resolvedUrl.endpoints.ordererUrl,\n );\n // the backend responds with the actual document ID associated with the new container.\n const documentId = await ordererRestWrapper.post<string>(\n `/documents/${tenantId}`,\n {\n summary: convertSummaryToCreateNewSummary(appSummary),\n sequenceNumber: documentAttributes.sequenceNumber,\n values: quorumValues,\n },\n );\n parsedUrl.set(\"pathname\", replaceDocumentIdInPath(parsedUrl.pathname, documentId));\n const deltaStorageUrl = resolvedUrl.endpoints.deltaStorageUrl;\n if (!deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [deltaStorageUrl:${deltaStorageUrl}]`);\n }\n const parsedDeltaStorageUrl = new URL(deltaStorageUrl);\n parsedDeltaStorageUrl.pathname = replaceDocumentIdInPath(parsedDeltaStorageUrl.pathname, documentId);\n\n return this.createDocumentService(\n {\n ...resolvedUrl,\n url: parsedUrl.toString(),\n id: documentId,\n endpoints: {\n ...resolvedUrl.endpoints,\n deltaStorageUrl: parsedDeltaStorageUrl.toString(),\n },\n },\n logger);\n }\n\n /**\n * Creates the document service after extracting different endpoints URLs from a resolved URL.\n *\n * @param resolvedUrl - URL containing different endpoint URLs.\n * @returns Routerlicious document service.\n */\n public async createDocumentService(\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n\n const fluidResolvedUrl = resolvedUrl;\n const storageUrl = fluidResolvedUrl.endpoints.storageUrl;\n const ordererUrl = fluidResolvedUrl.endpoints.ordererUrl;\n const deltaStorageUrl = fluidResolvedUrl.endpoints.deltaStorageUrl;\n if (!ordererUrl || !deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [ordererUrl:${ordererUrl}][deltaStorageUrl:${deltaStorageUrl}]`);\n }\n\n const parsedUrl = parseFluidUrl(fluidResolvedUrl.url);\n const [, tenantId, documentId] = parsedUrl.pathname.split(\"/\");\n if (!documentId || !tenantId) {\n throw new Error(\n `Couldn't parse documentId and/or tenantId. [documentId:${documentId}][tenantId:${tenantId}]`);\n }\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n\n return new DocumentService(\n fluidResolvedUrl,\n ordererUrl,\n deltaStorageUrl,\n storageUrl,\n logger2,\n this.tokenProvider,\n tenantId,\n documentId,\n this.driverPolicies,\n this.blobCache,\n this.snapshotTreeCache);\n }\n}\n"]}
1
+ {"version":3,"file":"documentServiceFactory.js","sourceRoot":"","sources":["../src/documentServiceFactory.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport {\n IDocumentService,\n IDocumentServiceFactory,\n IResolvedUrl,\n} from \"@fluidframework/driver-definitions\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ISnapshotTree, ISummaryTree } from \"@fluidframework/protocol-definitions\";\nimport {\n ensureFluidResolvedUrl,\n getDocAttributesFromProtocolSummary,\n getQuorumValuesFromProtocolSummary,\n RateLimiter,\n} from \"@fluidframework/driver-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { DocumentService } from \"./documentService\";\nimport { IRouterliciousDriverPolicies } from \"./policies\";\nimport { ITokenProvider } from \"./tokens\";\nimport { RouterliciousOrdererRestWrapper } from \"./restWrapper\";\nimport { convertSummaryToCreateNewSummary } from \"./createNewUtils\";\nimport { parseFluidUrl, replaceDocumentIdInPath } from \"./urlUtils\";\nimport { InMemoryCache } from \"./cache\";\n\nconst defaultRouterliciousDriverPolicies: IRouterliciousDriverPolicies = {\n enablePrefetch: true,\n maxConcurrentStorageRequests: 100,\n maxConcurrentOrdererRequests: 100,\n aggregateBlobsSmallerThanBytes: undefined,\n enableWholeSummaryUpload: false,\n enableRestLess: true,\n};\n\n/**\n * Factory for creating the routerlicious document service. Use this if you want to\n * use the routerlicious implementation.\n */\nexport class RouterliciousDocumentServiceFactory implements IDocumentServiceFactory {\n public readonly protocolName = \"fluid:\";\n private readonly driverPolicies: IRouterliciousDriverPolicies;\n private readonly blobCache = new InMemoryCache<ArrayBufferLike>();\n private readonly snapshotTreeCache = new InMemoryCache<ISnapshotTree>();\n\n constructor(\n private readonly tokenProvider: ITokenProvider,\n driverPolicies: Partial<IRouterliciousDriverPolicies> = {},\n ) {\n this.driverPolicies = {\n ...defaultRouterliciousDriverPolicies,\n ...driverPolicies,\n };\n }\n\n public async createContainer(\n createNewSummary: ISummaryTree | undefined,\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n assert(!!createNewSummary, 0x204 /* \"create empty file not supported\" */);\n assert(!!resolvedUrl.endpoints.ordererUrl, 0x0b2 /* \"Missing orderer URL!\" */);\n const parsedUrl = parseFluidUrl(resolvedUrl.url);\n if (!parsedUrl.pathname) {\n throw new Error(\"Parsed url should contain tenant and doc Id!!\");\n }\n const [, tenantId] = parsedUrl.pathname.split(\"/\");\n\n const protocolSummary = createNewSummary.tree[\".protocol\"] as ISummaryTree;\n const appSummary = createNewSummary.tree[\".app\"] as ISummaryTree;\n if (!(protocolSummary && appSummary)) {\n throw new Error(\"Protocol and App Summary required in the full summary\");\n }\n const documentAttributes = getDocAttributesFromProtocolSummary(protocolSummary);\n const quorumValues = getQuorumValuesFromProtocolSummary(protocolSummary);\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n const rateLimiter = new RateLimiter(this.driverPolicies.maxConcurrentOrdererRequests);\n const ordererRestWrapper = await RouterliciousOrdererRestWrapper.load(\n tenantId,\n undefined,\n this.tokenProvider,\n logger2,\n rateLimiter,\n this.driverPolicies.enableRestLess,\n resolvedUrl.endpoints.ordererUrl,\n );\n // the backend responds with the actual document ID associated with the new container.\n const documentId = await ordererRestWrapper.post<string>(\n `/documents/${tenantId}`,\n {\n summary: convertSummaryToCreateNewSummary(appSummary),\n sequenceNumber: documentAttributes.sequenceNumber,\n values: quorumValues,\n },\n );\n parsedUrl.set(\"pathname\", replaceDocumentIdInPath(parsedUrl.pathname, documentId));\n const deltaStorageUrl = resolvedUrl.endpoints.deltaStorageUrl;\n if (!deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [deltaStorageUrl:${deltaStorageUrl}]`);\n }\n const parsedDeltaStorageUrl = new URL(deltaStorageUrl);\n parsedDeltaStorageUrl.pathname = replaceDocumentIdInPath(parsedDeltaStorageUrl.pathname, documentId);\n\n return this.createDocumentService(\n {\n ...resolvedUrl,\n url: parsedUrl.toString(),\n id: documentId,\n endpoints: {\n ...resolvedUrl.endpoints,\n deltaStorageUrl: parsedDeltaStorageUrl.toString(),\n },\n },\n logger);\n }\n\n /**\n * Creates the document service after extracting different endpoints URLs from a resolved URL.\n *\n * @param resolvedUrl - URL containing different endpoint URLs.\n * @returns Routerlicious document service.\n */\n public async createDocumentService(\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n\n const fluidResolvedUrl = resolvedUrl;\n const storageUrl = fluidResolvedUrl.endpoints.storageUrl;\n const ordererUrl = fluidResolvedUrl.endpoints.ordererUrl;\n const deltaStorageUrl = fluidResolvedUrl.endpoints.deltaStorageUrl;\n if (!ordererUrl || !deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [ordererUrl:${ordererUrl}][deltaStorageUrl:${deltaStorageUrl}]`);\n }\n\n const parsedUrl = parseFluidUrl(fluidResolvedUrl.url);\n const [, tenantId, documentId] = parsedUrl.pathname.split(\"/\");\n if (!documentId || !tenantId) {\n throw new Error(\n `Couldn't parse documentId and/or tenantId. [documentId:${documentId}][tenantId:${tenantId}]`);\n }\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n\n return new DocumentService(\n fluidResolvedUrl,\n ordererUrl,\n deltaStorageUrl,\n storageUrl,\n logger2,\n this.tokenProvider,\n tenantId,\n documentId,\n this.driverPolicies,\n this.blobCache,\n this.snapshotTreeCache);\n }\n}\n"]}
@@ -13,10 +13,10 @@ var R11sErrorType;
13
13
  function createR11sNetworkError(fluidErrorCode, errorMessage, statusCode, retryAfterMs) {
14
14
  switch (statusCode) {
15
15
  case undefined:
16
- // If a service is temporarily down or a browser resource limit is reached, Axios will throw
16
+ // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw
17
17
  // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and
18
- // error message, "Network Error".
19
- return new driver_utils_1.GenericNetworkError(fluidErrorCode, errorMessage, errorMessage === "Network Error", { statusCode });
18
+ // the error message will start with NetworkError as defined in restWrapper.ts
19
+ return new driver_utils_1.GenericNetworkError(fluidErrorCode, errorMessage, errorMessage.startsWith("NetworkError"), { statusCode });
20
20
  case 401:
21
21
  case 403:
22
22
  return new driver_utils_1.AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });
@@ -1 +1 @@
1
- {"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../src/errorUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAKsC;AAEtC,IAAY,aAEX;AAFD,WAAY,aAAa;IACrB,oFAAmE,CAAA;AACvE,CAAC,EAFW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAExB;AAiCD,SAAgB,sBAAsB,CAClC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,QAAQ,UAAU,EAAE;QAChB,KAAK,SAAS;YACV,4FAA4F;YAC5F,wFAAwF;YACxF,kCAAkC;YAClC,OAAO,IAAI,kCAAmB,CAC1B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,eAAe,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACxF,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACJ,OAAO,IAAI,iCAAkB,CAAC,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACtG,KAAK,GAAG;YACJ,OAAO,IAAI,gCAAiB,CACxB,cAAc,EAAE,YAAY,EAAE,aAAa,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACrG,KAAK,GAAG;YACJ,OAAO,wCAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1E,KAAK,GAAG;YACJ,OAAO,IAAI,kCAAmB,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF;YACI,OAAO,wCAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,SAAS,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;KACnG;AACL,CAAC;AA5BD,wDA4BC;AAED,SAAgB,qBAAqB,CACjC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,MAAM,YAAY,GAAG,sBAAsB,CACvC,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,CAAC,CAAC;IAElB,+DAA+D;IAC/D,MAAM,YAAY,CAAC;AACvB,CAAC;AAdD,sDAcC;AAED;;GAEG;AACH,SAAgB,0BAA0B,CAAC,WAA6B,EAAE,OAAe;IACrF,MAAM,OAAO,GAAG,oBAAoB,OAAO,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IACvE,OAAO,sBAAsB,CACzB,iBAAiB,EACjB,OAAO,EACP,WAAW,CAAC,IAAI,EAChB,WAAW,CAAC,YAAY,CAC3B,CAAC;AACN,CAAC;AARD,gEAQC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { DriverError } from \"@fluidframework/driver-definitions\";\nimport {\n NonRetryableError,\n GenericNetworkError,\n createGenericNetworkError,\n AuthorizationError,\n} from \"@fluidframework/driver-utils\";\n\nexport enum R11sErrorType {\n fileNotFoundOrAccessDeniedError = \"fileNotFoundOrAccessDeniedError\",\n}\n\n/**\n * Interface for error responses for the WebSocket connection\n */\nexport interface IR11sSocketError {\n /**\n * An error code number for the error that occurred.\n * It will be a valid HTTP status code.\n */\n code: number;\n\n /**\n * A message about the error that occurred for debugging / logging purposes.\n * This should not be displayed to the user directly.\n */\n message: string;\n\n /**\n * Optional Retry-After time in seconds.\n * The client should wait this many seconds before retrying its request.\n */\n retryAfterMs?: number;\n}\n\nexport interface IR11sError {\n readonly errorType: R11sErrorType;\n readonly message: string;\n canRetry: boolean;\n}\n\nexport type R11sError = DriverError | IR11sError;\n\nexport function createR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): R11sError {\n switch (statusCode) {\n case undefined:\n // If a service is temporarily down or a browser resource limit is reached, Axios will throw\n // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and\n // error message, \"Network Error\".\n return new GenericNetworkError(\n fluidErrorCode, errorMessage, errorMessage === \"Network Error\", { statusCode });\n case 401:\n case 403:\n return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });\n case 404:\n return new NonRetryableError(\n fluidErrorCode, errorMessage, R11sErrorType.fileNotFoundOrAccessDeniedError, { statusCode });\n case 429:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, true, retryAfterMs, { statusCode });\n case 500:\n return new GenericNetworkError(fluidErrorCode, errorMessage, true, { statusCode });\n default:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, retryAfterMs !== undefined, retryAfterMs, { statusCode });\n }\n}\n\nexport function throwR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): never {\n const networkError = createR11sNetworkError(\n fluidErrorCode,\n errorMessage,\n statusCode,\n retryAfterMs);\n\n // eslint-disable-next-line @typescript-eslint/no-throw-literal\n throw networkError;\n}\n\n/**\n * Returns network error based on error object from R11s socket (IR11sSocketError)\n */\nexport function errorObjectFromSocketError(socketError: IR11sSocketError, handler: string): R11sError {\n const message = `R11sSocketError (${handler}): ${socketError.message}`;\n return createR11sNetworkError(\n \"r11sSocketError\",\n message,\n socketError.code,\n socketError.retryAfterMs,\n );\n}\n"]}
1
+ {"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../src/errorUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAKsC;AAEtC,IAAY,aAEX;AAFD,WAAY,aAAa;IACrB,oFAAmE,CAAA;AACvE,CAAC,EAFW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAExB;AAiCD,SAAgB,sBAAsB,CAClC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,QAAQ,UAAU,EAAE;QAChB,KAAK,SAAS;YACV,kGAAkG;YAClG,wFAAwF;YACxF,8EAA8E;YAC9E,OAAO,IAAI,kCAAmB,CAC1B,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/F,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACJ,OAAO,IAAI,iCAAkB,CAAC,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACtG,KAAK,GAAG;YACJ,OAAO,IAAI,gCAAiB,CACxB,cAAc,EAAE,YAAY,EAAE,aAAa,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACrG,KAAK,GAAG;YACJ,OAAO,wCAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1E,KAAK,GAAG;YACJ,OAAO,IAAI,kCAAmB,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF;YACI,OAAO,wCAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,SAAS,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;KACnG;AACL,CAAC;AA5BD,wDA4BC;AAED,SAAgB,qBAAqB,CACjC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,MAAM,YAAY,GAAG,sBAAsB,CACvC,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,CAAC,CAAC;IAElB,+DAA+D;IAC/D,MAAM,YAAY,CAAC;AACvB,CAAC;AAdD,sDAcC;AAED;;GAEG;AACH,SAAgB,0BAA0B,CAAC,WAA6B,EAAE,OAAe;IACrF,MAAM,OAAO,GAAG,oBAAoB,OAAO,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IACvE,OAAO,sBAAsB,CACzB,iBAAiB,EACjB,OAAO,EACP,WAAW,CAAC,IAAI,EAChB,WAAW,CAAC,YAAY,CAC3B,CAAC;AACN,CAAC;AARD,gEAQC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { DriverError } from \"@fluidframework/driver-definitions\";\nimport {\n NonRetryableError,\n GenericNetworkError,\n createGenericNetworkError,\n AuthorizationError,\n} from \"@fluidframework/driver-utils\";\n\nexport enum R11sErrorType {\n fileNotFoundOrAccessDeniedError = \"fileNotFoundOrAccessDeniedError\",\n}\n\n/**\n * Interface for error responses for the WebSocket connection\n */\nexport interface IR11sSocketError {\n /**\n * An error code number for the error that occurred.\n * It will be a valid HTTP status code.\n */\n code: number;\n\n /**\n * A message about the error that occurred for debugging / logging purposes.\n * This should not be displayed to the user directly.\n */\n message: string;\n\n /**\n * Optional Retry-After time in seconds.\n * The client should wait this many seconds before retrying its request.\n */\n retryAfterMs?: number;\n}\n\nexport interface IR11sError {\n readonly errorType: R11sErrorType;\n readonly message: string;\n canRetry: boolean;\n}\n\nexport type R11sError = DriverError | IR11sError;\n\nexport function createR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): R11sError {\n switch (statusCode) {\n case undefined:\n // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw\n // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and\n // the error message will start with NetworkError as defined in restWrapper.ts\n return new GenericNetworkError(\n fluidErrorCode, errorMessage, errorMessage.startsWith(\"NetworkError\"), { statusCode });\n case 401:\n case 403:\n return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });\n case 404:\n return new NonRetryableError(\n fluidErrorCode, errorMessage, R11sErrorType.fileNotFoundOrAccessDeniedError, { statusCode });\n case 429:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, true, retryAfterMs, { statusCode });\n case 500:\n return new GenericNetworkError(fluidErrorCode, errorMessage, true, { statusCode });\n default:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, retryAfterMs !== undefined, retryAfterMs, { statusCode });\n }\n}\n\nexport function throwR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): never {\n const networkError = createR11sNetworkError(\n fluidErrorCode,\n errorMessage,\n statusCode,\n retryAfterMs);\n\n // eslint-disable-next-line @typescript-eslint/no-throw-literal\n throw networkError;\n}\n\n/**\n * Returns network error based on error object from R11s socket (IR11sSocketError)\n */\nexport function errorObjectFromSocketError(socketError: IR11sSocketError, handler: string): R11sError {\n const message = `R11sSocketError (${handler}): ${socketError.message}`;\n return createR11sNetworkError(\n \"r11sSocketError\",\n message,\n socketError.code,\n socketError.retryAfterMs,\n );\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/routerlicious-driver";
8
- export declare const pkgVersion = "0.54.0";
8
+ export declare const pkgVersion = "0.55.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/routerlicious-driver";
11
- exports.pkgVersion = "0.54.0";
11
+ exports.pkgVersion = "0.55.0";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,sCAAsC,CAAC;AACjD,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/routerlicious-driver\";\nexport const pkgVersion = \"0.54.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,sCAAsC,CAAC;AACjD,QAAA,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/routerlicious-driver\";\nexport const pkgVersion = \"0.55.0\";\n"]}
@@ -33,7 +33,7 @@ export interface IRouterliciousDriverPolicies {
33
33
  enableWholeSummaryUpload: boolean;
34
34
  /**
35
35
  * Enable using RestLess which avoids CORS preflight requests.
36
- * Default: false
36
+ * Default: true
37
37
  */
38
38
  enableRestLess: boolean;
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport interface IRouterliciousDriverPolicies {\n /**\n * Enable prefetching entire snapshot tree into memory before it is loaded by the runtime.\n * Default: true\n */\n enablePrefetch: boolean;\n /**\n * Rate limit concurrent storage requests.\n * Default: 100\n */\n maxConcurrentStorageRequests: number;\n /**\n * Rate limit concurrent orderer requests.\n * Default: 100\n */\n maxConcurrentOrdererRequests: number;\n /**\n * Give hosts the option to change blob aggregation behavior to suit their needs.\n * Larger number means fewer blob individual requests, but less blob-deduping.\n * Smaller number means more blob individual requests, but more blob-deduping.\n * Setting to `undefined` disables blob aggregration.\n * Default: undefined\n */\n aggregateBlobsSmallerThanBytes: number | undefined;\n /**\n * Enable uploading entire summary tree as a IWholeSummaryPayload to storage.\n * Default: false\n */\n enableWholeSummaryUpload: boolean;\n /**\n * Enable using RestLess which avoids CORS preflight requests.\n * Default: false\n */\n enableRestLess: boolean;\n}\n"]}
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport interface IRouterliciousDriverPolicies {\n /**\n * Enable prefetching entire snapshot tree into memory before it is loaded by the runtime.\n * Default: true\n */\n enablePrefetch: boolean;\n /**\n * Rate limit concurrent storage requests.\n * Default: 100\n */\n maxConcurrentStorageRequests: number;\n /**\n * Rate limit concurrent orderer requests.\n * Default: 100\n */\n maxConcurrentOrdererRequests: number;\n /**\n * Give hosts the option to change blob aggregation behavior to suit their needs.\n * Larger number means fewer blob individual requests, but less blob-deduping.\n * Smaller number means more blob individual requests, but more blob-deduping.\n * Setting to `undefined` disables blob aggregration.\n * Default: undefined\n */\n aggregateBlobsSmallerThanBytes: number | undefined;\n /**\n * Enable uploading entire summary tree as a IWholeSummaryPayload to storage.\n * Default: false\n */\n enableWholeSummaryUpload: boolean;\n /**\n * Enable using RestLess which avoids CORS preflight requests.\n * Default: true\n */\n enableRestLess: boolean;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  /*!
2
3
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
4
  * Licensed under the MIT License.
@@ -6,11 +7,11 @@ import * as querystring from "querystring";
6
7
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
8
  import { RateLimiter } from "@fluidframework/driver-utils";
8
9
  import { RestWrapper } from "@fluidframework/server-services-client";
9
- import { AxiosRequestConfig } from "axios";
10
+ import type { AxiosRequestConfig } from "axios";
10
11
  import { ITokenProvider } from "./tokens";
11
12
  declare type AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;
13
+ export declare const fetch: (request: RequestInfo, config?: RequestInit | undefined) => Promise<Response>;
12
14
  export declare class RouterliciousRestWrapper extends RestWrapper {
13
- private readonly logger;
14
15
  private readonly rateLimiter;
15
16
  private readonly getAuthorizationHeader;
16
17
  private readonly useRestLess;
@@ -1 +1 @@
1
- {"version":3,"file":"restWrapper.d.ts","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAGH,WAAW,EACd,MAAM,wCAAwC,CAAC;AAChD,OAAc,EAAc,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAI9D,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,aAAK,yBAAyB,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAEpF,qBAAa,wBAAyB,SAAQ,WAAW;IAKjD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAPhC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAG5B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,sBAAsB,EAAE,yBAAyB,EACjD,WAAW,EAAE,OAAO,EACrC,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,GAAE,WAAW,CAAC,mBAAwB;IAK/C,IAAI;cAID,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAiD9G,OAAO,CAAC,eAAe;CAS1B;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CA8B9C;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CAsB9C"}
1
+ {"version":3,"file":"restWrapper.d.ts","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAGH,WAAW,EACd,MAAM,wCAAwC,CAAC;AAMhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,aAAK,yBAAyB,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAMpF,eAAO,MAAM,KAAK,YAAmB,WAAW,uCAAyB,QAAQ,QAAQ,CACI,CAAC;AAc9F,qBAAa,wBAAyB,SAAQ,WAAW;IAMjD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAPhC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAG7C,MAAM,EAAE,gBAAgB,EACP,WAAW,EAAE,WAAW,EACxB,sBAAsB,EAAE,yBAAyB,EACjD,WAAW,EAAE,OAAO,EACrC,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,GAAE,WAAW,CAAC,mBAAwB;IAK/C,IAAI;cAID,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAkD9G,OAAO,CAAC,eAAe;CAS1B;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CA8B9C;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CAsB9C"}
@@ -3,17 +3,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RouterliciousOrdererRestWrapper = exports.RouterliciousStorageRestWrapper = exports.RouterliciousRestWrapper = void 0;
6
+ exports.RouterliciousOrdererRestWrapper = exports.RouterliciousStorageRestWrapper = exports.RouterliciousRestWrapper = exports.fetch = void 0;
7
7
  const common_utils_1 = require("@fluidframework/common-utils");
8
8
  const server_services_client_1 = require("@fluidframework/server-services-client");
9
- const axios_1 = __importDefault(require("axios"));
9
+ const node_fetch_1 = __importDefault(require("node-fetch"));
10
10
  const json_stringify_safe_1 = __importDefault(require("json-stringify-safe"));
11
11
  const uuid_1 = require("uuid");
12
12
  const errorUtils_1 = require("./errorUtils");
13
+ // Borrowed from @fluidframework/odsp-driver's fetch.ts
14
+ // The only purpose of this helper is to work around the slight misalignments between the
15
+ // Browser's fetch API and the 'node-fetch' package by wrapping the call to the 'node-fetch' API
16
+ // in the browser's types from 'lib.dom.d.ts'.
17
+ const fetch = async (request, config) => node_fetch_1.default(request, config);
18
+ exports.fetch = fetch;
19
+ const axiosRequestConfigToFetchRequestConfig = (requestConfig) => {
20
+ var _a, _b;
21
+ const requestInfo = requestConfig.baseURL !== undefined
22
+ ? `${requestConfig.baseURL}${(_a = requestConfig.url) !== null && _a !== void 0 ? _a : ""}`
23
+ : (_b = requestConfig.url) !== null && _b !== void 0 ? _b : "";
24
+ const requestInit = {
25
+ method: requestConfig.method,
26
+ headers: requestConfig.headers,
27
+ body: requestConfig.data,
28
+ };
29
+ return [requestInfo, requestInit];
30
+ };
13
31
  class RouterliciousRestWrapper extends server_services_client_1.RestWrapper {
14
32
  constructor(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString = {}) {
15
33
  super(baseurl, defaultQueryString);
16
- this.logger = logger;
17
34
  this.rateLimiter = rateLimiter;
18
35
  this.getAuthorizationHeader = getAuthorizationHeader;
19
36
  this.useRestLess = useRestLess;
@@ -23,43 +40,38 @@ class RouterliciousRestWrapper extends server_services_client_1.RestWrapper {
23
40
  this.authorizationHeader = await this.getAuthorizationHeader();
24
41
  }
25
42
  async request(requestConfig, statusCode, canRetry = true) {
26
- var _a, _b, _c, _d, _e;
27
43
  const config = Object.assign(Object.assign({}, requestConfig), { headers: this.generateHeaders(requestConfig.headers) });
28
44
  const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
29
- try {
30
- const response = await this.rateLimiter.schedule(async () => axios_1.default.request(translatedConfig));
31
- return response.data;
45
+ const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
46
+ const response = await this.rateLimiter.schedule(async () => exports.fetch(...fetchRequestConfig)
47
+ .catch(async (error) => {
48
+ // Fetch throws a TypeError on network error
49
+ const isNetworkError = error instanceof TypeError;
50
+ errorUtils_1.throwR11sNetworkError("r11sFetchError", isNetworkError ? `NetworkError: ${error.message}` : json_stringify_safe_1.default(error));
51
+ }));
52
+ const responseBody = await response.clone().json().catch(async () => response.text());
53
+ // Success
54
+ if (response.ok || response.status === statusCode) {
55
+ const result = responseBody;
56
+ return result;
57
+ }
58
+ // Failure
59
+ if (response.status === 401 && canRetry) {
60
+ // Refresh Authorization header and retry once
61
+ this.authorizationHeader = await this.getAuthorizationHeader(true);
62
+ return this.request(config, statusCode, false);
32
63
  }
33
- catch (reason) {
34
- if (!reason || !(reason === null || reason === void 0 ? void 0 : reason.isAxiosError)) {
35
- // Unknown error, treat as critical error and immediately throw as non-retriable
36
- this.logger.sendErrorEvent({
37
- eventName: "CriticalRequestError",
38
- correlationId: config.headers["x-correlation-id"],
39
- }, reason);
40
- errorUtils_1.throwR11sNetworkError("r11sRequestFailed", `Unknown Error on [${config.method}] to [${config.url}]: ${json_stringify_safe_1.default(reason)}`);
41
- }
42
- const axiosError = reason;
43
- if (((_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status) === statusCode) {
44
- // Axios misinterpreted as error, return as successful response
45
- return axiosError.response.data;
46
- }
47
- if (((_b = axiosError.response) === null || _b === void 0 ? void 0 : _b.status) === 401 && canRetry) {
48
- // Refresh Authorization header and retry once
49
- this.authorizationHeader = await this.getAuthorizationHeader(true);
50
- return this.request(config, statusCode, false);
51
- }
52
- if (((_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === 429 && ((_d = axiosError.response.data) === null || _d === void 0 ? void 0 : _d.retryAfter) > 0) {
53
- // Retry based on retryAfter[Seconds]
54
- return new Promise((resolve, reject) => setTimeout(() => {
55
- this.request(config, statusCode)
56
- .then(resolve)
57
- .catch(reject);
58
- }, axiosError.response.data.retryAfter * 1000));
59
- }
60
- // Allow anything else to be handled upstream
61
- errorUtils_1.throwR11sNetworkError("r11sAxiosError", axiosError.message, (_e = axiosError.response) === null || _e === void 0 ? void 0 : _e.status);
64
+ if (response.status === 429 && (responseBody === null || responseBody === void 0 ? void 0 : responseBody.retryAfter) > 0) {
65
+ // Retry based on retryAfter[Seconds]
66
+ return new Promise((resolve, reject) => setTimeout(() => {
67
+ this.request(config, statusCode)
68
+ .then(resolve)
69
+ .catch(reject);
70
+ }, responseBody.retryAfter * 1000));
62
71
  }
72
+ errorUtils_1.throwR11sNetworkError("r11sFetchError", responseBody !== undefined
73
+ ? typeof responseBody === "string" ? responseBody : json_stringify_safe_1.default(responseBody)
74
+ : response.statusText, response.status, responseBody === null || responseBody === void 0 ? void 0 : responseBody.retryAfter);
63
75
  }
64
76
  generateHeaders(requestHeaders) {
65
77
  const correlationId = (requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders["x-correlation-id"]) || uuid_1.v4();
@@ -1 +1 @@
1
- {"version":3,"file":"restWrapper.js","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport * as querystring from \"querystring\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { fromUtf8ToBase64 } from \"@fluidframework/common-utils\";\nimport { RateLimiter } from \"@fluidframework/driver-utils\";\nimport {\n getAuthorizationTokenFromCredentials,\n RestLessClient,\n RestWrapper,\n} from \"@fluidframework/server-services-client\";\nimport Axios, { AxiosError, AxiosRequestConfig } from \"axios\";\nimport safeStringify from \"json-stringify-safe\";\nimport { v4 as uuid } from \"uuid\";\nimport { throwR11sNetworkError } from \"./errorUtils\";\nimport { ITokenProvider } from \"./tokens\";\n\ntype AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;\n\nexport class RouterliciousRestWrapper extends RestWrapper {\n private authorizationHeader: string | undefined;\n private readonly restLess = new RestLessClient();\n\n constructor(\n private readonly logger: ITelemetryLogger,\n private readonly rateLimiter: RateLimiter,\n private readonly getAuthorizationHeader: AuthorizationHeaderGetter,\n private readonly useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(baseurl, defaultQueryString);\n }\n\n public async load() {\n this.authorizationHeader = await this.getAuthorizationHeader();\n }\n\n protected async request<T>(requestConfig: AxiosRequestConfig, statusCode: number, canRetry = true): Promise<T> {\n const config = {\n ...requestConfig,\n headers: this.generateHeaders(requestConfig.headers),\n };\n\n const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;\n\n try {\n const response = await this.rateLimiter.schedule(async () => Axios.request<T>(translatedConfig));\n return response.data;\n } catch (reason: any) {\n if (!reason || !reason?.isAxiosError) {\n // Unknown error, treat as critical error and immediately throw as non-retriable\n this.logger.sendErrorEvent({\n eventName: \"CriticalRequestError\",\n correlationId: config.headers[\"x-correlation-id\"] as string,\n }, reason);\n throwR11sNetworkError(\"r11sRequestFailed\", `Unknown Error on [${config.method}] to [${config.url}]: ${\n safeStringify(reason)\n }`);\n }\n\n const axiosError = reason as AxiosError;\n if (axiosError.response?.status === statusCode) {\n // Axios misinterpreted as error, return as successful response\n return axiosError.response.data as T;\n }\n\n if (axiosError.response?.status === 401 && canRetry) {\n // Refresh Authorization header and retry once\n this.authorizationHeader = await this.getAuthorizationHeader(true);\n return this.request<T>(config, statusCode, false);\n }\n\n if (axiosError.response?.status === 429 && axiosError.response.data?.retryAfter > 0) {\n // Retry based on retryAfter[Seconds]\n return new Promise<T>((resolve, reject) => setTimeout(() => {\n this.request<T>(config, statusCode)\n .then(resolve)\n .catch(reject);\n }, axiosError.response!.data.retryAfter * 1000));\n }\n\n // Allow anything else to be handled upstream\n throwR11sNetworkError(\"r11sAxiosError\", axiosError.message, axiosError.response?.status);\n }\n }\n\n private generateHeaders(requestHeaders?: Record<string, unknown>): Record<string, unknown> {\n const correlationId = requestHeaders?.[\"x-correlation-id\"] || uuid();\n\n return {\n ...requestHeaders,\n \"x-correlation-id\": correlationId,\n \"Authorization\": this.authorizationHeader,\n };\n }\n}\n\nexport class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousStorageRestWrapper> {\n const defaultQueryString = {\n token: `${fromUtf8ToBase64(tenantId)}`,\n };\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n // Craft credentials using tenant id and token\n const storageToken = await tokenProvider.fetchStorageToken(\n tenantId,\n documentId,\n refresh,\n );\n const credentials = {\n password: storageToken.jwt,\n user: tenantId,\n };\n return getAuthorizationTokenFromCredentials(credentials);\n };\n\n const restWrapper = new RouterliciousStorageRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n\nexport class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string | undefined,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousOrdererRestWrapper> {\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n const ordererToken = await tokenProvider.fetchOrdererToken(\n tenantId,\n documentId,\n refresh,\n );\n return `Basic ${ordererToken.jwt}`;\n };\n\n const restWrapper = new RouterliciousOrdererRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n"]}
1
+ {"version":3,"file":"restWrapper.js","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport * as querystring from \"querystring\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { fromUtf8ToBase64 } from \"@fluidframework/common-utils\";\nimport { RateLimiter } from \"@fluidframework/driver-utils\";\nimport {\n getAuthorizationTokenFromCredentials,\n RestLessClient,\n RestWrapper,\n} from \"@fluidframework/server-services-client\";\nimport {\n default as nodeFetch,\n RequestInfo as FetchRequestInfo,\n RequestInit as FetchRequestInit,\n} from \"node-fetch\";\nimport type { AxiosRequestConfig } from \"axios\";\nimport safeStringify from \"json-stringify-safe\";\nimport { v4 as uuid } from \"uuid\";\nimport { throwR11sNetworkError } from \"./errorUtils\";\nimport { ITokenProvider } from \"./tokens\";\n\ntype AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;\n\n// Borrowed from @fluidframework/odsp-driver's fetch.ts\n// The only purpose of this helper is to work around the slight misalignments between the\n// Browser's fetch API and the 'node-fetch' package by wrapping the call to the 'node-fetch' API\n// in the browser's types from 'lib.dom.d.ts'.\nexport const fetch = async (request: RequestInfo, config?: RequestInit): Promise<Response> =>\n nodeFetch(request as FetchRequestInfo, config as FetchRequestInit) as unknown as Response;\n\nconst axiosRequestConfigToFetchRequestConfig = (requestConfig: AxiosRequestConfig): [RequestInfo, RequestInit] => {\n const requestInfo: string = requestConfig.baseURL !== undefined\n ? `${requestConfig.baseURL}${requestConfig.url ?? \"\"}`\n : requestConfig.url ?? \"\";\n const requestInit: RequestInit = {\n method: requestConfig.method,\n headers: requestConfig.headers,\n body: requestConfig.data,\n };\n return [requestInfo, requestInit];\n};\n\nexport class RouterliciousRestWrapper extends RestWrapper {\n private authorizationHeader: string | undefined;\n private readonly restLess = new RestLessClient();\n\n constructor(\n logger: ITelemetryLogger,\n private readonly rateLimiter: RateLimiter,\n private readonly getAuthorizationHeader: AuthorizationHeaderGetter,\n private readonly useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(baseurl, defaultQueryString);\n }\n\n public async load() {\n this.authorizationHeader = await this.getAuthorizationHeader();\n }\n\n protected async request<T>(requestConfig: AxiosRequestConfig, statusCode: number, canRetry = true): Promise<T> {\n const config = {\n ...requestConfig,\n headers: this.generateHeaders(requestConfig.headers),\n };\n\n const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;\n const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);\n\n const response: Response = await this.rateLimiter.schedule(async () => fetch(...fetchRequestConfig)\n .catch(async (error) => {\n // Fetch throws a TypeError on network error\n const isNetworkError = error instanceof TypeError;\n throwR11sNetworkError(\n \"r11sFetchError\",\n isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error));\n }));\n\n const responseBody: any = await response.clone().json().catch(async () => response.text());\n\n // Success\n if (response.ok || response.status === statusCode) {\n const result: T = responseBody;\n return result;\n }\n // Failure\n if (response.status === 401 && canRetry) {\n // Refresh Authorization header and retry once\n this.authorizationHeader = await this.getAuthorizationHeader(true);\n return this.request<T>(config, statusCode, false);\n }\n if (response.status === 429 && responseBody?.retryAfter > 0) {\n // Retry based on retryAfter[Seconds]\n return new Promise<T>((resolve, reject) => setTimeout(() => {\n this.request<T>(config, statusCode)\n .then(resolve)\n .catch(reject);\n }, responseBody.retryAfter * 1000));\n }\n\n throwR11sNetworkError(\n \"r11sFetchError\",\n responseBody !== undefined\n ? typeof responseBody === \"string\" ? responseBody : safeStringify(responseBody)\n : response.statusText,\n response.status,\n responseBody?.retryAfter,\n );\n }\n\n private generateHeaders(requestHeaders?: Record<string, unknown>): Record<string, unknown> {\n const correlationId = requestHeaders?.[\"x-correlation-id\"] || uuid();\n\n return {\n ...requestHeaders,\n \"x-correlation-id\": correlationId,\n \"Authorization\": this.authorizationHeader,\n };\n }\n}\n\nexport class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousStorageRestWrapper> {\n const defaultQueryString = {\n token: `${fromUtf8ToBase64(tenantId)}`,\n };\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n // Craft credentials using tenant id and token\n const storageToken = await tokenProvider.fetchStorageToken(\n tenantId,\n documentId,\n refresh,\n );\n const credentials = {\n password: storageToken.jwt,\n user: tenantId,\n };\n return getAuthorizationTokenFromCredentials(credentials);\n };\n\n const restWrapper = new RouterliciousStorageRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n\nexport class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string | undefined,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousOrdererRestWrapper> {\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n const ordererToken = await tokenProvider.fetchOrdererToken(\n tenantId,\n documentId,\n refresh,\n );\n return `Basic ${ordererToken.jwt}`;\n };\n\n const restWrapper = new RouterliciousOrdererRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n"]}
@@ -16,7 +16,7 @@ const defaultRouterliciousDriverPolicies = {
16
16
  maxConcurrentOrdererRequests: 100,
17
17
  aggregateBlobsSmallerThanBytes: undefined,
18
18
  enableWholeSummaryUpload: false,
19
- enableRestLess: false,
19
+ enableRestLess: true,
20
20
  };
21
21
  /**
22
22
  * Factory for creating the routerlicious document service. Use this if you want to
@@ -1 +1 @@
1
- {"version":3,"file":"documentServiceFactory.js","sourceRoot":"","sources":["../src/documentServiceFactory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAQtD,OAAO,EACH,sBAAsB,EACtB,mCAAmC,EACnC,kCAAkC,EAClC,WAAW,GACd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,kCAAkC,GAAiC;IACrE,cAAc,EAAE,IAAI;IACpB,4BAA4B,EAAE,GAAG;IACjC,4BAA4B,EAAE,GAAG;IACjC,8BAA8B,EAAE,SAAS;IACzC,wBAAwB,EAAE,KAAK;IAC/B,cAAc,EAAE,KAAK;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,mCAAmC;IAM5C,YACqB,aAA6B,EAC9C,iBAAwD,EAAE;QADzC,kBAAa,GAAb,aAAa,CAAgB;QANlC,iBAAY,GAAG,QAAQ,CAAC;QAEvB,cAAS,GAAG,IAAI,aAAa,EAAmB,CAAC;QACjD,sBAAiB,GAAG,IAAI,aAAa,EAAiB,CAAC;QAMpE,IAAI,CAAC,cAAc,mCACZ,kCAAkC,GAClC,cAAc,CACpB,CAAC;IACN,CAAC;IAEM,KAAK,CAAC,eAAe,CACxB,gBAA0C,EAC1C,WAAyB,EACzB,MAA6B;QAE7B,sBAAsB,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,gBAAgB,EAAE,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC1E,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC/E,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE;YACrB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;SACpE;QACD,MAAM,CAAC,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEnD,MAAM,eAAe,GAAG,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAiB,CAAC;QAC3E,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAiB,CAAC;QACjE,IAAI,CAAC,CAAC,eAAe,IAAI,UAAU,CAAC,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;SAC5E;QACD,MAAM,kBAAkB,GAAG,mCAAmC,CAAC,eAAe,CAAC,CAAC;QAChF,MAAM,YAAY,GAAG,kCAAkC,CAAC,eAAe,CAAC,CAAC;QAEzE,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;QAClE,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,4BAA4B,CAAC,CAAC;QACtF,MAAM,kBAAkB,GAAG,MAAM,+BAA+B,CAAC,IAAI,CACjE,QAAQ,EACR,SAAS,EACT,IAAI,CAAC,aAAa,EAClB,OAAO,EACP,WAAW,EACX,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,WAAW,CAAC,SAAS,CAAC,UAAU,CACnC,CAAC;QACF,sFAAsF;QACtF,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAC5C,cAAc,QAAQ,EAAE,EACxB;YACI,OAAO,EAAE,gCAAgC,CAAC,UAAU,CAAC;YACrD,cAAc,EAAE,kBAAkB,CAAC,cAAc;YACjD,MAAM,EAAE,YAAY;SACvB,CACJ,CAAC;QACF,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,uBAAuB,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;QACnF,MAAM,eAAe,GAAG,WAAW,CAAC,SAAS,CAAC,eAAe,CAAC;QAC9D,IAAI,CAAC,eAAe,EAAE;YAClB,MAAM,IAAI,KAAK,CACX,yDAAyD,eAAe,GAAG,CAAC,CAAC;SACpF;QACD,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;QACvD,qBAAqB,CAAC,QAAQ,GAAG,uBAAuB,CAAC,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAErG,OAAO,IAAI,CAAC,qBAAqB,iCAEtB,WAAW,KACd,GAAG,EAAE,SAAS,CAAC,QAAQ,EAAE,EACzB,EAAE,EAAE,UAAU,EACd,SAAS,kCACF,WAAW,CAAC,SAAS,KACxB,eAAe,EAAE,qBAAqB,CAAC,QAAQ,EAAE,QAGzD,MAAM,CAAC,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,qBAAqB,CAC9B,WAAyB,EACzB,MAA6B;QAE7B,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAEpC,MAAM,gBAAgB,GAAG,WAAW,CAAC;QACrC,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC;QACzD,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC;QACzD,MAAM,eAAe,GAAG,gBAAgB,CAAC,SAAS,CAAC,eAAe,CAAC;QACnE,IAAI,CAAC,UAAU,IAAI,CAAC,eAAe,EAAE;YACjC,MAAM,IAAI,KAAK,CACX,oDAAoD,UAAU,qBAAqB,eAAe,GAAG,CAAC,CAAC;SAC9G;QAED,MAAM,SAAS,GAAG,aAAa,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtD,MAAM,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE;YAC1B,MAAM,IAAI,KAAK,CACX,0DAA0D,UAAU,cAAc,QAAQ,GAAG,CAAC,CAAC;SACtG;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;QAElE,OAAO,IAAI,eAAe,CACtB,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,UAAU,EACV,OAAO,EACP,IAAI,CAAC,aAAa,EAClB,QAAQ,EACR,UAAU,EACV,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAChC,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport {\n IDocumentService,\n IDocumentServiceFactory,\n IResolvedUrl,\n} from \"@fluidframework/driver-definitions\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ISnapshotTree, ISummaryTree } from \"@fluidframework/protocol-definitions\";\nimport {\n ensureFluidResolvedUrl,\n getDocAttributesFromProtocolSummary,\n getQuorumValuesFromProtocolSummary,\n RateLimiter,\n} from \"@fluidframework/driver-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { DocumentService } from \"./documentService\";\nimport { IRouterliciousDriverPolicies } from \"./policies\";\nimport { ITokenProvider } from \"./tokens\";\nimport { RouterliciousOrdererRestWrapper } from \"./restWrapper\";\nimport { convertSummaryToCreateNewSummary } from \"./createNewUtils\";\nimport { parseFluidUrl, replaceDocumentIdInPath } from \"./urlUtils\";\nimport { InMemoryCache } from \"./cache\";\n\nconst defaultRouterliciousDriverPolicies: IRouterliciousDriverPolicies = {\n enablePrefetch: true,\n maxConcurrentStorageRequests: 100,\n maxConcurrentOrdererRequests: 100,\n aggregateBlobsSmallerThanBytes: undefined,\n enableWholeSummaryUpload: false,\n enableRestLess: false,\n};\n\n/**\n * Factory for creating the routerlicious document service. Use this if you want to\n * use the routerlicious implementation.\n */\nexport class RouterliciousDocumentServiceFactory implements IDocumentServiceFactory {\n public readonly protocolName = \"fluid:\";\n private readonly driverPolicies: IRouterliciousDriverPolicies;\n private readonly blobCache = new InMemoryCache<ArrayBufferLike>();\n private readonly snapshotTreeCache = new InMemoryCache<ISnapshotTree>();\n\n constructor(\n private readonly tokenProvider: ITokenProvider,\n driverPolicies: Partial<IRouterliciousDriverPolicies> = {},\n ) {\n this.driverPolicies = {\n ...defaultRouterliciousDriverPolicies,\n ...driverPolicies,\n };\n }\n\n public async createContainer(\n createNewSummary: ISummaryTree | undefined,\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n assert(!!createNewSummary, 0x204 /* \"create empty file not supported\" */);\n assert(!!resolvedUrl.endpoints.ordererUrl, 0x0b2 /* \"Missing orderer URL!\" */);\n const parsedUrl = parseFluidUrl(resolvedUrl.url);\n if (!parsedUrl.pathname) {\n throw new Error(\"Parsed url should contain tenant and doc Id!!\");\n }\n const [, tenantId] = parsedUrl.pathname.split(\"/\");\n\n const protocolSummary = createNewSummary.tree[\".protocol\"] as ISummaryTree;\n const appSummary = createNewSummary.tree[\".app\"] as ISummaryTree;\n if (!(protocolSummary && appSummary)) {\n throw new Error(\"Protocol and App Summary required in the full summary\");\n }\n const documentAttributes = getDocAttributesFromProtocolSummary(protocolSummary);\n const quorumValues = getQuorumValuesFromProtocolSummary(protocolSummary);\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n const rateLimiter = new RateLimiter(this.driverPolicies.maxConcurrentOrdererRequests);\n const ordererRestWrapper = await RouterliciousOrdererRestWrapper.load(\n tenantId,\n undefined,\n this.tokenProvider,\n logger2,\n rateLimiter,\n this.driverPolicies.enableRestLess,\n resolvedUrl.endpoints.ordererUrl,\n );\n // the backend responds with the actual document ID associated with the new container.\n const documentId = await ordererRestWrapper.post<string>(\n `/documents/${tenantId}`,\n {\n summary: convertSummaryToCreateNewSummary(appSummary),\n sequenceNumber: documentAttributes.sequenceNumber,\n values: quorumValues,\n },\n );\n parsedUrl.set(\"pathname\", replaceDocumentIdInPath(parsedUrl.pathname, documentId));\n const deltaStorageUrl = resolvedUrl.endpoints.deltaStorageUrl;\n if (!deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [deltaStorageUrl:${deltaStorageUrl}]`);\n }\n const parsedDeltaStorageUrl = new URL(deltaStorageUrl);\n parsedDeltaStorageUrl.pathname = replaceDocumentIdInPath(parsedDeltaStorageUrl.pathname, documentId);\n\n return this.createDocumentService(\n {\n ...resolvedUrl,\n url: parsedUrl.toString(),\n id: documentId,\n endpoints: {\n ...resolvedUrl.endpoints,\n deltaStorageUrl: parsedDeltaStorageUrl.toString(),\n },\n },\n logger);\n }\n\n /**\n * Creates the document service after extracting different endpoints URLs from a resolved URL.\n *\n * @param resolvedUrl - URL containing different endpoint URLs.\n * @returns Routerlicious document service.\n */\n public async createDocumentService(\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n\n const fluidResolvedUrl = resolvedUrl;\n const storageUrl = fluidResolvedUrl.endpoints.storageUrl;\n const ordererUrl = fluidResolvedUrl.endpoints.ordererUrl;\n const deltaStorageUrl = fluidResolvedUrl.endpoints.deltaStorageUrl;\n if (!ordererUrl || !deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [ordererUrl:${ordererUrl}][deltaStorageUrl:${deltaStorageUrl}]`);\n }\n\n const parsedUrl = parseFluidUrl(fluidResolvedUrl.url);\n const [, tenantId, documentId] = parsedUrl.pathname.split(\"/\");\n if (!documentId || !tenantId) {\n throw new Error(\n `Couldn't parse documentId and/or tenantId. [documentId:${documentId}][tenantId:${tenantId}]`);\n }\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n\n return new DocumentService(\n fluidResolvedUrl,\n ordererUrl,\n deltaStorageUrl,\n storageUrl,\n logger2,\n this.tokenProvider,\n tenantId,\n documentId,\n this.driverPolicies,\n this.blobCache,\n this.snapshotTreeCache);\n }\n}\n"]}
1
+ {"version":3,"file":"documentServiceFactory.js","sourceRoot":"","sources":["../src/documentServiceFactory.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/common-utils\";\nimport {\n IDocumentService,\n IDocumentServiceFactory,\n IResolvedUrl,\n} from \"@fluidframework/driver-definitions\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ISnapshotTree, ISummaryTree } from \"@fluidframework/protocol-definitions\";\nimport {\n ensureFluidResolvedUrl,\n getDocAttributesFromProtocolSummary,\n getQuorumValuesFromProtocolSummary,\n RateLimiter,\n} from \"@fluidframework/driver-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { DocumentService } from \"./documentService\";\nimport { IRouterliciousDriverPolicies } from \"./policies\";\nimport { ITokenProvider } from \"./tokens\";\nimport { RouterliciousOrdererRestWrapper } from \"./restWrapper\";\nimport { convertSummaryToCreateNewSummary } from \"./createNewUtils\";\nimport { parseFluidUrl, replaceDocumentIdInPath } from \"./urlUtils\";\nimport { InMemoryCache } from \"./cache\";\n\nconst defaultRouterliciousDriverPolicies: IRouterliciousDriverPolicies = {\n enablePrefetch: true,\n maxConcurrentStorageRequests: 100,\n maxConcurrentOrdererRequests: 100,\n aggregateBlobsSmallerThanBytes: undefined,\n enableWholeSummaryUpload: false,\n enableRestLess: true,\n};\n\n/**\n * Factory for creating the routerlicious document service. Use this if you want to\n * use the routerlicious implementation.\n */\nexport class RouterliciousDocumentServiceFactory implements IDocumentServiceFactory {\n public readonly protocolName = \"fluid:\";\n private readonly driverPolicies: IRouterliciousDriverPolicies;\n private readonly blobCache = new InMemoryCache<ArrayBufferLike>();\n private readonly snapshotTreeCache = new InMemoryCache<ISnapshotTree>();\n\n constructor(\n private readonly tokenProvider: ITokenProvider,\n driverPolicies: Partial<IRouterliciousDriverPolicies> = {},\n ) {\n this.driverPolicies = {\n ...defaultRouterliciousDriverPolicies,\n ...driverPolicies,\n };\n }\n\n public async createContainer(\n createNewSummary: ISummaryTree | undefined,\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n assert(!!createNewSummary, 0x204 /* \"create empty file not supported\" */);\n assert(!!resolvedUrl.endpoints.ordererUrl, 0x0b2 /* \"Missing orderer URL!\" */);\n const parsedUrl = parseFluidUrl(resolvedUrl.url);\n if (!parsedUrl.pathname) {\n throw new Error(\"Parsed url should contain tenant and doc Id!!\");\n }\n const [, tenantId] = parsedUrl.pathname.split(\"/\");\n\n const protocolSummary = createNewSummary.tree[\".protocol\"] as ISummaryTree;\n const appSummary = createNewSummary.tree[\".app\"] as ISummaryTree;\n if (!(protocolSummary && appSummary)) {\n throw new Error(\"Protocol and App Summary required in the full summary\");\n }\n const documentAttributes = getDocAttributesFromProtocolSummary(protocolSummary);\n const quorumValues = getQuorumValuesFromProtocolSummary(protocolSummary);\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n const rateLimiter = new RateLimiter(this.driverPolicies.maxConcurrentOrdererRequests);\n const ordererRestWrapper = await RouterliciousOrdererRestWrapper.load(\n tenantId,\n undefined,\n this.tokenProvider,\n logger2,\n rateLimiter,\n this.driverPolicies.enableRestLess,\n resolvedUrl.endpoints.ordererUrl,\n );\n // the backend responds with the actual document ID associated with the new container.\n const documentId = await ordererRestWrapper.post<string>(\n `/documents/${tenantId}`,\n {\n summary: convertSummaryToCreateNewSummary(appSummary),\n sequenceNumber: documentAttributes.sequenceNumber,\n values: quorumValues,\n },\n );\n parsedUrl.set(\"pathname\", replaceDocumentIdInPath(parsedUrl.pathname, documentId));\n const deltaStorageUrl = resolvedUrl.endpoints.deltaStorageUrl;\n if (!deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [deltaStorageUrl:${deltaStorageUrl}]`);\n }\n const parsedDeltaStorageUrl = new URL(deltaStorageUrl);\n parsedDeltaStorageUrl.pathname = replaceDocumentIdInPath(parsedDeltaStorageUrl.pathname, documentId);\n\n return this.createDocumentService(\n {\n ...resolvedUrl,\n url: parsedUrl.toString(),\n id: documentId,\n endpoints: {\n ...resolvedUrl.endpoints,\n deltaStorageUrl: parsedDeltaStorageUrl.toString(),\n },\n },\n logger);\n }\n\n /**\n * Creates the document service after extracting different endpoints URLs from a resolved URL.\n *\n * @param resolvedUrl - URL containing different endpoint URLs.\n * @returns Routerlicious document service.\n */\n public async createDocumentService(\n resolvedUrl: IResolvedUrl,\n logger?: ITelemetryBaseLogger,\n ): Promise<IDocumentService> {\n ensureFluidResolvedUrl(resolvedUrl);\n\n const fluidResolvedUrl = resolvedUrl;\n const storageUrl = fluidResolvedUrl.endpoints.storageUrl;\n const ordererUrl = fluidResolvedUrl.endpoints.ordererUrl;\n const deltaStorageUrl = fluidResolvedUrl.endpoints.deltaStorageUrl;\n if (!ordererUrl || !deltaStorageUrl) {\n throw new Error(\n `All endpoints urls must be provided. [ordererUrl:${ordererUrl}][deltaStorageUrl:${deltaStorageUrl}]`);\n }\n\n const parsedUrl = parseFluidUrl(fluidResolvedUrl.url);\n const [, tenantId, documentId] = parsedUrl.pathname.split(\"/\");\n if (!documentId || !tenantId) {\n throw new Error(\n `Couldn't parse documentId and/or tenantId. [documentId:${documentId}][tenantId:${tenantId}]`);\n }\n\n const logger2 = ChildLogger.create(logger, \"RouterliciousDriver\");\n\n return new DocumentService(\n fluidResolvedUrl,\n ordererUrl,\n deltaStorageUrl,\n storageUrl,\n logger2,\n this.tokenProvider,\n tenantId,\n documentId,\n this.driverPolicies,\n this.blobCache,\n this.snapshotTreeCache);\n }\n}\n"]}
package/lib/errorUtils.js CHANGED
@@ -10,10 +10,10 @@ export var R11sErrorType;
10
10
  export function createR11sNetworkError(fluidErrorCode, errorMessage, statusCode, retryAfterMs) {
11
11
  switch (statusCode) {
12
12
  case undefined:
13
- // If a service is temporarily down or a browser resource limit is reached, Axios will throw
13
+ // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw
14
14
  // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and
15
- // error message, "Network Error".
16
- return new GenericNetworkError(fluidErrorCode, errorMessage, errorMessage === "Network Error", { statusCode });
15
+ // the error message will start with NetworkError as defined in restWrapper.ts
16
+ return new GenericNetworkError(fluidErrorCode, errorMessage, errorMessage.startsWith("NetworkError"), { statusCode });
17
17
  case 401:
18
18
  case 403:
19
19
  return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });
@@ -1 +1 @@
1
- {"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../src/errorUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,CAAN,IAAY,aAEX;AAFD,WAAY,aAAa;IACrB,oFAAmE,CAAA;AACvE,CAAC,EAFW,aAAa,KAAb,aAAa,QAExB;AAiCD,MAAM,UAAU,sBAAsB,CAClC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,QAAQ,UAAU,EAAE;QAChB,KAAK,SAAS;YACV,4FAA4F;YAC5F,wFAAwF;YACxF,kCAAkC;YAClC,OAAO,IAAI,mBAAmB,CAC1B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,eAAe,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACxF,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACJ,OAAO,IAAI,kBAAkB,CAAC,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACtG,KAAK,GAAG;YACJ,OAAO,IAAI,iBAAiB,CACxB,cAAc,EAAE,YAAY,EAAE,aAAa,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACrG,KAAK,GAAG;YACJ,OAAO,yBAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1E,KAAK,GAAG;YACJ,OAAO,IAAI,mBAAmB,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF;YACI,OAAO,yBAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,SAAS,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;KACnG;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CACjC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,MAAM,YAAY,GAAG,sBAAsB,CACvC,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,CAAC,CAAC;IAElB,+DAA+D;IAC/D,MAAM,YAAY,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAA6B,EAAE,OAAe;IACrF,MAAM,OAAO,GAAG,oBAAoB,OAAO,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IACvE,OAAO,sBAAsB,CACzB,iBAAiB,EACjB,OAAO,EACP,WAAW,CAAC,IAAI,EAChB,WAAW,CAAC,YAAY,CAC3B,CAAC;AACN,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { DriverError } from \"@fluidframework/driver-definitions\";\nimport {\n NonRetryableError,\n GenericNetworkError,\n createGenericNetworkError,\n AuthorizationError,\n} from \"@fluidframework/driver-utils\";\n\nexport enum R11sErrorType {\n fileNotFoundOrAccessDeniedError = \"fileNotFoundOrAccessDeniedError\",\n}\n\n/**\n * Interface for error responses for the WebSocket connection\n */\nexport interface IR11sSocketError {\n /**\n * An error code number for the error that occurred.\n * It will be a valid HTTP status code.\n */\n code: number;\n\n /**\n * A message about the error that occurred for debugging / logging purposes.\n * This should not be displayed to the user directly.\n */\n message: string;\n\n /**\n * Optional Retry-After time in seconds.\n * The client should wait this many seconds before retrying its request.\n */\n retryAfterMs?: number;\n}\n\nexport interface IR11sError {\n readonly errorType: R11sErrorType;\n readonly message: string;\n canRetry: boolean;\n}\n\nexport type R11sError = DriverError | IR11sError;\n\nexport function createR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): R11sError {\n switch (statusCode) {\n case undefined:\n // If a service is temporarily down or a browser resource limit is reached, Axios will throw\n // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and\n // error message, \"Network Error\".\n return new GenericNetworkError(\n fluidErrorCode, errorMessage, errorMessage === \"Network Error\", { statusCode });\n case 401:\n case 403:\n return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });\n case 404:\n return new NonRetryableError(\n fluidErrorCode, errorMessage, R11sErrorType.fileNotFoundOrAccessDeniedError, { statusCode });\n case 429:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, true, retryAfterMs, { statusCode });\n case 500:\n return new GenericNetworkError(fluidErrorCode, errorMessage, true, { statusCode });\n default:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, retryAfterMs !== undefined, retryAfterMs, { statusCode });\n }\n}\n\nexport function throwR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): never {\n const networkError = createR11sNetworkError(\n fluidErrorCode,\n errorMessage,\n statusCode,\n retryAfterMs);\n\n // eslint-disable-next-line @typescript-eslint/no-throw-literal\n throw networkError;\n}\n\n/**\n * Returns network error based on error object from R11s socket (IR11sSocketError)\n */\nexport function errorObjectFromSocketError(socketError: IR11sSocketError, handler: string): R11sError {\n const message = `R11sSocketError (${handler}): ${socketError.message}`;\n return createR11sNetworkError(\n \"r11sSocketError\",\n message,\n socketError.code,\n socketError.retryAfterMs,\n );\n}\n"]}
1
+ {"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../src/errorUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,EACzB,kBAAkB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,CAAN,IAAY,aAEX;AAFD,WAAY,aAAa;IACrB,oFAAmE,CAAA;AACvE,CAAC,EAFW,aAAa,KAAb,aAAa,QAExB;AAiCD,MAAM,UAAU,sBAAsB,CAClC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,QAAQ,UAAU,EAAE;QAChB,KAAK,SAAS;YACV,kGAAkG;YAClG,wFAAwF;YACxF,8EAA8E;YAC9E,OAAO,IAAI,mBAAmB,CAC1B,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/F,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACJ,OAAO,IAAI,kBAAkB,CAAC,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACtG,KAAK,GAAG;YACJ,OAAO,IAAI,iBAAiB,CACxB,cAAc,EAAE,YAAY,EAAE,aAAa,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACrG,KAAK,GAAG;YACJ,OAAO,yBAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1E,KAAK,GAAG;YACJ,OAAO,IAAI,mBAAmB,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACvF;YACI,OAAO,yBAAyB,CAC5B,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,SAAS,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;KACnG;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CACjC,cAAsB,EACtB,YAAoB,EACpB,UAAmB,EACnB,YAAqB;IAErB,MAAM,YAAY,GAAG,sBAAsB,CACvC,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,CAAC,CAAC;IAElB,+DAA+D;IAC/D,MAAM,YAAY,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAA6B,EAAE,OAAe;IACrF,MAAM,OAAO,GAAG,oBAAoB,OAAO,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IACvE,OAAO,sBAAsB,CACzB,iBAAiB,EACjB,OAAO,EACP,WAAW,CAAC,IAAI,EAChB,WAAW,CAAC,YAAY,CAC3B,CAAC;AACN,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { DriverError } from \"@fluidframework/driver-definitions\";\nimport {\n NonRetryableError,\n GenericNetworkError,\n createGenericNetworkError,\n AuthorizationError,\n} from \"@fluidframework/driver-utils\";\n\nexport enum R11sErrorType {\n fileNotFoundOrAccessDeniedError = \"fileNotFoundOrAccessDeniedError\",\n}\n\n/**\n * Interface for error responses for the WebSocket connection\n */\nexport interface IR11sSocketError {\n /**\n * An error code number for the error that occurred.\n * It will be a valid HTTP status code.\n */\n code: number;\n\n /**\n * A message about the error that occurred for debugging / logging purposes.\n * This should not be displayed to the user directly.\n */\n message: string;\n\n /**\n * Optional Retry-After time in seconds.\n * The client should wait this many seconds before retrying its request.\n */\n retryAfterMs?: number;\n}\n\nexport interface IR11sError {\n readonly errorType: R11sErrorType;\n readonly message: string;\n canRetry: boolean;\n}\n\nexport type R11sError = DriverError | IR11sError;\n\nexport function createR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): R11sError {\n switch (statusCode) {\n case undefined:\n // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw\n // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and\n // the error message will start with NetworkError as defined in restWrapper.ts\n return new GenericNetworkError(\n fluidErrorCode, errorMessage, errorMessage.startsWith(\"NetworkError\"), { statusCode });\n case 401:\n case 403:\n return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });\n case 404:\n return new NonRetryableError(\n fluidErrorCode, errorMessage, R11sErrorType.fileNotFoundOrAccessDeniedError, { statusCode });\n case 429:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, true, retryAfterMs, { statusCode });\n case 500:\n return new GenericNetworkError(fluidErrorCode, errorMessage, true, { statusCode });\n default:\n return createGenericNetworkError(\n fluidErrorCode, errorMessage, retryAfterMs !== undefined, retryAfterMs, { statusCode });\n }\n}\n\nexport function throwR11sNetworkError(\n fluidErrorCode: string,\n errorMessage: string,\n statusCode?: number,\n retryAfterMs?: number,\n): never {\n const networkError = createR11sNetworkError(\n fluidErrorCode,\n errorMessage,\n statusCode,\n retryAfterMs);\n\n // eslint-disable-next-line @typescript-eslint/no-throw-literal\n throw networkError;\n}\n\n/**\n * Returns network error based on error object from R11s socket (IR11sSocketError)\n */\nexport function errorObjectFromSocketError(socketError: IR11sSocketError, handler: string): R11sError {\n const message = `R11sSocketError (${handler}): ${socketError.message}`;\n return createR11sNetworkError(\n \"r11sSocketError\",\n message,\n socketError.code,\n socketError.retryAfterMs,\n );\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/routerlicious-driver";
8
- export declare const pkgVersion = "0.54.0";
8
+ export declare const pkgVersion = "0.55.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/routerlicious-driver";
8
- export const pkgVersion = "0.54.0";
8
+ export const pkgVersion = "0.55.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,sCAAsC,CAAC;AAC9D,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/routerlicious-driver\";\nexport const pkgVersion = \"0.54.0\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,sCAAsC,CAAC;AAC9D,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/routerlicious-driver\";\nexport const pkgVersion = \"0.55.0\";\n"]}
package/lib/policies.d.ts CHANGED
@@ -33,7 +33,7 @@ export interface IRouterliciousDriverPolicies {
33
33
  enableWholeSummaryUpload: boolean;
34
34
  /**
35
35
  * Enable using RestLess which avoids CORS preflight requests.
36
- * Default: false
36
+ * Default: true
37
37
  */
38
38
  enableRestLess: boolean;
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport interface IRouterliciousDriverPolicies {\n /**\n * Enable prefetching entire snapshot tree into memory before it is loaded by the runtime.\n * Default: true\n */\n enablePrefetch: boolean;\n /**\n * Rate limit concurrent storage requests.\n * Default: 100\n */\n maxConcurrentStorageRequests: number;\n /**\n * Rate limit concurrent orderer requests.\n * Default: 100\n */\n maxConcurrentOrdererRequests: number;\n /**\n * Give hosts the option to change blob aggregation behavior to suit their needs.\n * Larger number means fewer blob individual requests, but less blob-deduping.\n * Smaller number means more blob individual requests, but more blob-deduping.\n * Setting to `undefined` disables blob aggregration.\n * Default: undefined\n */\n aggregateBlobsSmallerThanBytes: number | undefined;\n /**\n * Enable uploading entire summary tree as a IWholeSummaryPayload to storage.\n * Default: false\n */\n enableWholeSummaryUpload: boolean;\n /**\n * Enable using RestLess which avoids CORS preflight requests.\n * Default: false\n */\n enableRestLess: boolean;\n}\n"]}
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport interface IRouterliciousDriverPolicies {\n /**\n * Enable prefetching entire snapshot tree into memory before it is loaded by the runtime.\n * Default: true\n */\n enablePrefetch: boolean;\n /**\n * Rate limit concurrent storage requests.\n * Default: 100\n */\n maxConcurrentStorageRequests: number;\n /**\n * Rate limit concurrent orderer requests.\n * Default: 100\n */\n maxConcurrentOrdererRequests: number;\n /**\n * Give hosts the option to change blob aggregation behavior to suit their needs.\n * Larger number means fewer blob individual requests, but less blob-deduping.\n * Smaller number means more blob individual requests, but more blob-deduping.\n * Setting to `undefined` disables blob aggregration.\n * Default: undefined\n */\n aggregateBlobsSmallerThanBytes: number | undefined;\n /**\n * Enable uploading entire summary tree as a IWholeSummaryPayload to storage.\n * Default: false\n */\n enableWholeSummaryUpload: boolean;\n /**\n * Enable using RestLess which avoids CORS preflight requests.\n * Default: true\n */\n enableRestLess: boolean;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  /*!
2
3
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
4
  * Licensed under the MIT License.
@@ -6,11 +7,11 @@ import * as querystring from "querystring";
6
7
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
8
  import { RateLimiter } from "@fluidframework/driver-utils";
8
9
  import { RestWrapper } from "@fluidframework/server-services-client";
9
- import { AxiosRequestConfig } from "axios";
10
+ import type { AxiosRequestConfig } from "axios";
10
11
  import { ITokenProvider } from "./tokens";
11
12
  declare type AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;
13
+ export declare const fetch: (request: RequestInfo, config?: RequestInit | undefined) => Promise<Response>;
12
14
  export declare class RouterliciousRestWrapper extends RestWrapper {
13
- private readonly logger;
14
15
  private readonly rateLimiter;
15
16
  private readonly getAuthorizationHeader;
16
17
  private readonly useRestLess;
@@ -1 +1 @@
1
- {"version":3,"file":"restWrapper.d.ts","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAGH,WAAW,EACd,MAAM,wCAAwC,CAAC;AAChD,OAAc,EAAc,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAI9D,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,aAAK,yBAAyB,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAEpF,qBAAa,wBAAyB,SAAQ,WAAW;IAKjD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAPhC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAG5B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,sBAAsB,EAAE,yBAAyB,EACjD,WAAW,EAAE,OAAO,EACrC,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,GAAE,WAAW,CAAC,mBAAwB;IAK/C,IAAI;cAID,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAiD9G,OAAO,CAAC,eAAe;CAS1B;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CA8B9C;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CAsB9C"}
1
+ {"version":3,"file":"restWrapper.d.ts","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAGH,WAAW,EACd,MAAM,wCAAwC,CAAC;AAMhD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,aAAK,yBAAyB,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAMpF,eAAO,MAAM,KAAK,YAAmB,WAAW,uCAAyB,QAAQ,QAAQ,CACI,CAAC;AAc9F,qBAAa,wBAAyB,SAAQ,WAAW;IAMjD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAPhC,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAG7C,MAAM,EAAE,gBAAgB,EACP,WAAW,EAAE,WAAW,EACxB,sBAAsB,EAAE,yBAAyB,EACjD,WAAW,EAAE,OAAO,EACrC,OAAO,CAAC,EAAE,MAAM,EAChB,kBAAkB,GAAE,WAAW,CAAC,mBAAwB;IAK/C,IAAI;cAID,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAkD9G,OAAO,CAAC,eAAe;CAS1B;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CA8B9C;AAED,qBAAa,+BAAgC,SAAQ,wBAAwB;IACzE,OAAO;WAWa,IAAI,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,cAAc,EAC7B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,+BAA+B,CAAC;CAsB9C"}
@@ -1,13 +1,29 @@
1
1
  import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
2
2
  import { getAuthorizationTokenFromCredentials, RestLessClient, RestWrapper, } from "@fluidframework/server-services-client";
3
- import Axios from "axios";
3
+ import { default as nodeFetch, } from "node-fetch";
4
4
  import safeStringify from "json-stringify-safe";
5
5
  import { v4 as uuid } from "uuid";
6
6
  import { throwR11sNetworkError } from "./errorUtils";
7
+ // Borrowed from @fluidframework/odsp-driver's fetch.ts
8
+ // The only purpose of this helper is to work around the slight misalignments between the
9
+ // Browser's fetch API and the 'node-fetch' package by wrapping the call to the 'node-fetch' API
10
+ // in the browser's types from 'lib.dom.d.ts'.
11
+ export const fetch = async (request, config) => nodeFetch(request, config);
12
+ const axiosRequestConfigToFetchRequestConfig = (requestConfig) => {
13
+ var _a, _b;
14
+ const requestInfo = requestConfig.baseURL !== undefined
15
+ ? `${requestConfig.baseURL}${(_a = requestConfig.url) !== null && _a !== void 0 ? _a : ""}`
16
+ : (_b = requestConfig.url) !== null && _b !== void 0 ? _b : "";
17
+ const requestInit = {
18
+ method: requestConfig.method,
19
+ headers: requestConfig.headers,
20
+ body: requestConfig.data,
21
+ };
22
+ return [requestInfo, requestInit];
23
+ };
7
24
  export class RouterliciousRestWrapper extends RestWrapper {
8
25
  constructor(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString = {}) {
9
26
  super(baseurl, defaultQueryString);
10
- this.logger = logger;
11
27
  this.rateLimiter = rateLimiter;
12
28
  this.getAuthorizationHeader = getAuthorizationHeader;
13
29
  this.useRestLess = useRestLess;
@@ -17,43 +33,38 @@ export class RouterliciousRestWrapper extends RestWrapper {
17
33
  this.authorizationHeader = await this.getAuthorizationHeader();
18
34
  }
19
35
  async request(requestConfig, statusCode, canRetry = true) {
20
- var _a, _b, _c, _d, _e;
21
36
  const config = Object.assign(Object.assign({}, requestConfig), { headers: this.generateHeaders(requestConfig.headers) });
22
37
  const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
23
- try {
24
- const response = await this.rateLimiter.schedule(async () => Axios.request(translatedConfig));
25
- return response.data;
38
+ const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
39
+ const response = await this.rateLimiter.schedule(async () => fetch(...fetchRequestConfig)
40
+ .catch(async (error) => {
41
+ // Fetch throws a TypeError on network error
42
+ const isNetworkError = error instanceof TypeError;
43
+ throwR11sNetworkError("r11sFetchError", isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error));
44
+ }));
45
+ const responseBody = await response.clone().json().catch(async () => response.text());
46
+ // Success
47
+ if (response.ok || response.status === statusCode) {
48
+ const result = responseBody;
49
+ return result;
50
+ }
51
+ // Failure
52
+ if (response.status === 401 && canRetry) {
53
+ // Refresh Authorization header and retry once
54
+ this.authorizationHeader = await this.getAuthorizationHeader(true);
55
+ return this.request(config, statusCode, false);
26
56
  }
27
- catch (reason) {
28
- if (!reason || !(reason === null || reason === void 0 ? void 0 : reason.isAxiosError)) {
29
- // Unknown error, treat as critical error and immediately throw as non-retriable
30
- this.logger.sendErrorEvent({
31
- eventName: "CriticalRequestError",
32
- correlationId: config.headers["x-correlation-id"],
33
- }, reason);
34
- throwR11sNetworkError("r11sRequestFailed", `Unknown Error on [${config.method}] to [${config.url}]: ${safeStringify(reason)}`);
35
- }
36
- const axiosError = reason;
37
- if (((_a = axiosError.response) === null || _a === void 0 ? void 0 : _a.status) === statusCode) {
38
- // Axios misinterpreted as error, return as successful response
39
- return axiosError.response.data;
40
- }
41
- if (((_b = axiosError.response) === null || _b === void 0 ? void 0 : _b.status) === 401 && canRetry) {
42
- // Refresh Authorization header and retry once
43
- this.authorizationHeader = await this.getAuthorizationHeader(true);
44
- return this.request(config, statusCode, false);
45
- }
46
- if (((_c = axiosError.response) === null || _c === void 0 ? void 0 : _c.status) === 429 && ((_d = axiosError.response.data) === null || _d === void 0 ? void 0 : _d.retryAfter) > 0) {
47
- // Retry based on retryAfter[Seconds]
48
- return new Promise((resolve, reject) => setTimeout(() => {
49
- this.request(config, statusCode)
50
- .then(resolve)
51
- .catch(reject);
52
- }, axiosError.response.data.retryAfter * 1000));
53
- }
54
- // Allow anything else to be handled upstream
55
- throwR11sNetworkError("r11sAxiosError", axiosError.message, (_e = axiosError.response) === null || _e === void 0 ? void 0 : _e.status);
57
+ if (response.status === 429 && (responseBody === null || responseBody === void 0 ? void 0 : responseBody.retryAfter) > 0) {
58
+ // Retry based on retryAfter[Seconds]
59
+ return new Promise((resolve, reject) => setTimeout(() => {
60
+ this.request(config, statusCode)
61
+ .then(resolve)
62
+ .catch(reject);
63
+ }, responseBody.retryAfter * 1000));
56
64
  }
65
+ throwR11sNetworkError("r11sFetchError", responseBody !== undefined
66
+ ? typeof responseBody === "string" ? responseBody : safeStringify(responseBody)
67
+ : response.statusText, response.status, responseBody === null || responseBody === void 0 ? void 0 : responseBody.retryAfter);
57
68
  }
58
69
  generateHeaders(requestHeaders) {
59
70
  const correlationId = (requestHeaders === null || requestHeaders === void 0 ? void 0 : requestHeaders["x-correlation-id"]) || uuid();
@@ -1 +1 @@
1
- {"version":3,"file":"restWrapper.js","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport * as querystring from \"querystring\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { fromUtf8ToBase64 } from \"@fluidframework/common-utils\";\nimport { RateLimiter } from \"@fluidframework/driver-utils\";\nimport {\n getAuthorizationTokenFromCredentials,\n RestLessClient,\n RestWrapper,\n} from \"@fluidframework/server-services-client\";\nimport Axios, { AxiosError, AxiosRequestConfig } from \"axios\";\nimport safeStringify from \"json-stringify-safe\";\nimport { v4 as uuid } from \"uuid\";\nimport { throwR11sNetworkError } from \"./errorUtils\";\nimport { ITokenProvider } from \"./tokens\";\n\ntype AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;\n\nexport class RouterliciousRestWrapper extends RestWrapper {\n private authorizationHeader: string | undefined;\n private readonly restLess = new RestLessClient();\n\n constructor(\n private readonly logger: ITelemetryLogger,\n private readonly rateLimiter: RateLimiter,\n private readonly getAuthorizationHeader: AuthorizationHeaderGetter,\n private readonly useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(baseurl, defaultQueryString);\n }\n\n public async load() {\n this.authorizationHeader = await this.getAuthorizationHeader();\n }\n\n protected async request<T>(requestConfig: AxiosRequestConfig, statusCode: number, canRetry = true): Promise<T> {\n const config = {\n ...requestConfig,\n headers: this.generateHeaders(requestConfig.headers),\n };\n\n const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;\n\n try {\n const response = await this.rateLimiter.schedule(async () => Axios.request<T>(translatedConfig));\n return response.data;\n } catch (reason: any) {\n if (!reason || !reason?.isAxiosError) {\n // Unknown error, treat as critical error and immediately throw as non-retriable\n this.logger.sendErrorEvent({\n eventName: \"CriticalRequestError\",\n correlationId: config.headers[\"x-correlation-id\"] as string,\n }, reason);\n throwR11sNetworkError(\"r11sRequestFailed\", `Unknown Error on [${config.method}] to [${config.url}]: ${\n safeStringify(reason)\n }`);\n }\n\n const axiosError = reason as AxiosError;\n if (axiosError.response?.status === statusCode) {\n // Axios misinterpreted as error, return as successful response\n return axiosError.response.data as T;\n }\n\n if (axiosError.response?.status === 401 && canRetry) {\n // Refresh Authorization header and retry once\n this.authorizationHeader = await this.getAuthorizationHeader(true);\n return this.request<T>(config, statusCode, false);\n }\n\n if (axiosError.response?.status === 429 && axiosError.response.data?.retryAfter > 0) {\n // Retry based on retryAfter[Seconds]\n return new Promise<T>((resolve, reject) => setTimeout(() => {\n this.request<T>(config, statusCode)\n .then(resolve)\n .catch(reject);\n }, axiosError.response!.data.retryAfter * 1000));\n }\n\n // Allow anything else to be handled upstream\n throwR11sNetworkError(\"r11sAxiosError\", axiosError.message, axiosError.response?.status);\n }\n }\n\n private generateHeaders(requestHeaders?: Record<string, unknown>): Record<string, unknown> {\n const correlationId = requestHeaders?.[\"x-correlation-id\"] || uuid();\n\n return {\n ...requestHeaders,\n \"x-correlation-id\": correlationId,\n \"Authorization\": this.authorizationHeader,\n };\n }\n}\n\nexport class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousStorageRestWrapper> {\n const defaultQueryString = {\n token: `${fromUtf8ToBase64(tenantId)}`,\n };\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n // Craft credentials using tenant id and token\n const storageToken = await tokenProvider.fetchStorageToken(\n tenantId,\n documentId,\n refresh,\n );\n const credentials = {\n password: storageToken.jwt,\n user: tenantId,\n };\n return getAuthorizationTokenFromCredentials(credentials);\n };\n\n const restWrapper = new RouterliciousStorageRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n\nexport class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string | undefined,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousOrdererRestWrapper> {\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n const ordererToken = await tokenProvider.fetchOrdererToken(\n tenantId,\n documentId,\n refresh,\n );\n return `Basic ${ordererToken.jwt}`;\n };\n\n const restWrapper = new RouterliciousOrdererRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n"]}
1
+ {"version":3,"file":"restWrapper.js","sourceRoot":"","sources":["../src/restWrapper.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport * as querystring from \"querystring\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { fromUtf8ToBase64 } from \"@fluidframework/common-utils\";\nimport { RateLimiter } from \"@fluidframework/driver-utils\";\nimport {\n getAuthorizationTokenFromCredentials,\n RestLessClient,\n RestWrapper,\n} from \"@fluidframework/server-services-client\";\nimport {\n default as nodeFetch,\n RequestInfo as FetchRequestInfo,\n RequestInit as FetchRequestInit,\n} from \"node-fetch\";\nimport type { AxiosRequestConfig } from \"axios\";\nimport safeStringify from \"json-stringify-safe\";\nimport { v4 as uuid } from \"uuid\";\nimport { throwR11sNetworkError } from \"./errorUtils\";\nimport { ITokenProvider } from \"./tokens\";\n\ntype AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;\n\n// Borrowed from @fluidframework/odsp-driver's fetch.ts\n// The only purpose of this helper is to work around the slight misalignments between the\n// Browser's fetch API and the 'node-fetch' package by wrapping the call to the 'node-fetch' API\n// in the browser's types from 'lib.dom.d.ts'.\nexport const fetch = async (request: RequestInfo, config?: RequestInit): Promise<Response> =>\n nodeFetch(request as FetchRequestInfo, config as FetchRequestInit) as unknown as Response;\n\nconst axiosRequestConfigToFetchRequestConfig = (requestConfig: AxiosRequestConfig): [RequestInfo, RequestInit] => {\n const requestInfo: string = requestConfig.baseURL !== undefined\n ? `${requestConfig.baseURL}${requestConfig.url ?? \"\"}`\n : requestConfig.url ?? \"\";\n const requestInit: RequestInit = {\n method: requestConfig.method,\n headers: requestConfig.headers,\n body: requestConfig.data,\n };\n return [requestInfo, requestInit];\n};\n\nexport class RouterliciousRestWrapper extends RestWrapper {\n private authorizationHeader: string | undefined;\n private readonly restLess = new RestLessClient();\n\n constructor(\n logger: ITelemetryLogger,\n private readonly rateLimiter: RateLimiter,\n private readonly getAuthorizationHeader: AuthorizationHeaderGetter,\n private readonly useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(baseurl, defaultQueryString);\n }\n\n public async load() {\n this.authorizationHeader = await this.getAuthorizationHeader();\n }\n\n protected async request<T>(requestConfig: AxiosRequestConfig, statusCode: number, canRetry = true): Promise<T> {\n const config = {\n ...requestConfig,\n headers: this.generateHeaders(requestConfig.headers),\n };\n\n const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;\n const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);\n\n const response: Response = await this.rateLimiter.schedule(async () => fetch(...fetchRequestConfig)\n .catch(async (error) => {\n // Fetch throws a TypeError on network error\n const isNetworkError = error instanceof TypeError;\n throwR11sNetworkError(\n \"r11sFetchError\",\n isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error));\n }));\n\n const responseBody: any = await response.clone().json().catch(async () => response.text());\n\n // Success\n if (response.ok || response.status === statusCode) {\n const result: T = responseBody;\n return result;\n }\n // Failure\n if (response.status === 401 && canRetry) {\n // Refresh Authorization header and retry once\n this.authorizationHeader = await this.getAuthorizationHeader(true);\n return this.request<T>(config, statusCode, false);\n }\n if (response.status === 429 && responseBody?.retryAfter > 0) {\n // Retry based on retryAfter[Seconds]\n return new Promise<T>((resolve, reject) => setTimeout(() => {\n this.request<T>(config, statusCode)\n .then(resolve)\n .catch(reject);\n }, responseBody.retryAfter * 1000));\n }\n\n throwR11sNetworkError(\n \"r11sFetchError\",\n responseBody !== undefined\n ? typeof responseBody === \"string\" ? responseBody : safeStringify(responseBody)\n : response.statusText,\n response.status,\n responseBody?.retryAfter,\n );\n }\n\n private generateHeaders(requestHeaders?: Record<string, unknown>): Record<string, unknown> {\n const correlationId = requestHeaders?.[\"x-correlation-id\"] || uuid();\n\n return {\n ...requestHeaders,\n \"x-correlation-id\": correlationId,\n \"Authorization\": this.authorizationHeader,\n };\n }\n}\n\nexport class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousStorageRestWrapper> {\n const defaultQueryString = {\n token: `${fromUtf8ToBase64(tenantId)}`,\n };\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n // Craft credentials using tenant id and token\n const storageToken = await tokenProvider.fetchStorageToken(\n tenantId,\n documentId,\n refresh,\n );\n const credentials = {\n password: storageToken.jwt,\n user: tenantId,\n };\n return getAuthorizationTokenFromCredentials(credentials);\n };\n\n const restWrapper = new RouterliciousStorageRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n\nexport class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {\n private constructor(\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n getAuthorizationHeader: AuthorizationHeaderGetter,\n useRestLess: boolean,\n baseurl?: string,\n defaultQueryString: querystring.ParsedUrlQueryInput = {},\n ) {\n super(logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl, defaultQueryString);\n }\n\n public static async load(\n tenantId: string,\n documentId: string | undefined,\n tokenProvider: ITokenProvider,\n logger: ITelemetryLogger,\n rateLimiter: RateLimiter,\n useRestLess: boolean,\n baseurl?: string,\n ): Promise<RouterliciousOrdererRestWrapper> {\n const getAuthorizationHeader: AuthorizationHeaderGetter = async (refresh?: boolean): Promise<string> => {\n const ordererToken = await tokenProvider.fetchOrdererToken(\n tenantId,\n documentId,\n refresh,\n );\n return `Basic ${ordererToken.jwt}`;\n };\n\n const restWrapper = new RouterliciousOrdererRestWrapper(\n logger, rateLimiter, getAuthorizationHeader, useRestLess, baseurl);\n try {\n await restWrapper.load();\n } catch (e) {\n logger.sendErrorEvent({\n eventName: \"R11sRestWrapperLoadFailure\",\n }, e);\n await restWrapper.load();\n }\n return restWrapper;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/routerlicious-driver",
3
- "version": "0.54.0",
3
+ "version": "0.55.0",
4
4
  "description": "Socket.IO + Git implementation of Fluid service API",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": "https://github.com/microsoft/FluidFramework",
@@ -23,7 +23,7 @@
23
23
  "ci:build:docs": "api-extractor run --typescript-compiler-folder ../../../node_modules/typescript && copyfiles -u 1 ./_api-extractor-temp/* ../../../_api-extractor-temp/",
24
24
  "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
25
25
  "eslint": "eslint --format stylish src",
26
- "eslint:fix": "eslint --format stylish src --fix",
26
+ "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
27
27
  "lint": "npm run eslint",
28
28
  "lint:fix": "npm run eslint:fix",
29
29
  "test": "npm run test:mocha",
@@ -57,47 +57,52 @@
57
57
  "dependencies": {
58
58
  "@fluidframework/common-definitions": "^0.20.1",
59
59
  "@fluidframework/common-utils": "^0.32.1",
60
- "@fluidframework/driver-base": "^0.54.0",
60
+ "@fluidframework/driver-base": "^0.55.0",
61
61
  "@fluidframework/driver-definitions": "^0.43.0",
62
- "@fluidframework/driver-utils": "^0.54.0",
62
+ "@fluidframework/driver-utils": "^0.55.0",
63
63
  "@fluidframework/gitresources": "^0.1034.0",
64
64
  "@fluidframework/protocol-base": "^0.1034.0",
65
65
  "@fluidframework/protocol-definitions": "^0.1026.0",
66
66
  "@fluidframework/server-services-client": "^0.1034.0",
67
- "@fluidframework/telemetry-utils": "^0.54.0",
68
- "axios": "^0.21.2",
67
+ "@fluidframework/telemetry-utils": "^0.55.0",
69
68
  "json-stringify-safe": "5.0.1",
69
+ "node-fetch": "^2.6.1",
70
70
  "socket.io-client": "^2.4.0",
71
71
  "url-parse": "^1.5.3",
72
72
  "uuid": "^8.3.1"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@fluidframework/build-common": "^0.23.0",
76
- "@fluidframework/eslint-config-fluid": "^0.24.0",
77
- "@fluidframework/mocha-test-setup": "^0.54.0",
76
+ "@fluidframework/eslint-config-fluid": "^0.25.0",
77
+ "@fluidframework/mocha-test-setup": "^0.55.0",
78
78
  "@microsoft/api-extractor": "^7.16.1",
79
+ "@rushstack/eslint-config": "^2.5.1",
79
80
  "@types/mocha": "^8.2.2",
80
81
  "@types/nock": "^9.3.0",
82
+ "@types/node-fetch": "^2.5.10",
83
+ "@types/sinon": "^7.0.13",
81
84
  "@types/socket.io-client": "^1.4.32",
82
85
  "@types/url-parse": "^1.4.4",
83
86
  "@types/uuid": "^8.3.0",
84
- "@typescript-eslint/eslint-plugin": "~4.14.0",
85
- "@typescript-eslint/parser": "~4.14.0",
86
- "axios-mock-adapter": "1.19.0",
87
+ "@typescript-eslint/eslint-plugin": "~5.9.0",
88
+ "@typescript-eslint/parser": "~5.9.0",
89
+ "axios": "^0.21.2",
87
90
  "concurrently": "^6.2.0",
88
91
  "copyfiles": "^2.1.0",
89
92
  "cross-env": "^7.0.2",
90
- "eslint": "~7.18.0",
93
+ "eslint": "~8.6.0",
94
+ "eslint-plugin-editorconfig": "~3.2.0",
91
95
  "eslint-plugin-eslint-comments": "~3.2.0",
92
- "eslint-plugin-import": "~2.22.1",
96
+ "eslint-plugin-import": "~2.25.4",
93
97
  "eslint-plugin-no-null": "~1.0.2",
94
- "eslint-plugin-prefer-arrow": "~1.2.2",
95
- "eslint-plugin-react": "~7.22.0",
96
- "eslint-plugin-unicorn": "~26.0.1",
98
+ "eslint-plugin-react": "~7.28.0",
99
+ "eslint-plugin-unicorn": "~40.0.0",
100
+ "fetch-mock": "^9.11.0",
97
101
  "mocha": "^8.4.0",
98
102
  "nock": "^10.0.1",
99
103
  "nyc": "^15.0.0",
100
104
  "rimraf": "^2.6.2",
105
+ "sinon": "^7.4.2",
101
106
  "typescript": "~4.1.3",
102
107
  "typescript-formatter": "7.1.0"
103
108
  }
@@ -32,7 +32,7 @@ const defaultRouterliciousDriverPolicies: IRouterliciousDriverPolicies = {
32
32
  maxConcurrentOrdererRequests: 100,
33
33
  aggregateBlobsSmallerThanBytes: undefined,
34
34
  enableWholeSummaryUpload: false,
35
- enableRestLess: false,
35
+ enableRestLess: true,
36
36
  };
37
37
 
38
38
  /**
package/src/errorUtils.ts CHANGED
@@ -54,11 +54,11 @@ export function createR11sNetworkError(
54
54
  ): R11sError {
55
55
  switch (statusCode) {
56
56
  case undefined:
57
- // If a service is temporarily down or a browser resource limit is reached, Axios will throw
57
+ // If a service is temporarily down or a browser resource limit is reached, RestWrapper will throw
58
58
  // a network error with no status code (e.g. err:ERR_CONN_REFUSED or err:ERR_FAILED) and
59
- // error message, "Network Error".
59
+ // the error message will start with NetworkError as defined in restWrapper.ts
60
60
  return new GenericNetworkError(
61
- fluidErrorCode, errorMessage, errorMessage === "Network Error", { statusCode });
61
+ fluidErrorCode, errorMessage, errorMessage.startsWith("NetworkError"), { statusCode });
62
62
  case 401:
63
63
  case 403:
64
64
  return new AuthorizationError(fluidErrorCode, errorMessage, undefined, undefined, { statusCode });
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/routerlicious-driver";
9
- export const pkgVersion = "0.54.0";
9
+ export const pkgVersion = "0.55.0";
package/src/policies.ts CHANGED
@@ -34,7 +34,7 @@ export interface IRouterliciousDriverPolicies {
34
34
  enableWholeSummaryUpload: boolean;
35
35
  /**
36
36
  * Enable using RestLess which avoids CORS preflight requests.
37
- * Default: false
37
+ * Default: true
38
38
  */
39
39
  enableRestLess: boolean;
40
40
  }
@@ -11,7 +11,12 @@ import {
11
11
  RestLessClient,
12
12
  RestWrapper,
13
13
  } from "@fluidframework/server-services-client";
14
- import Axios, { AxiosError, AxiosRequestConfig } from "axios";
14
+ import {
15
+ default as nodeFetch,
16
+ RequestInfo as FetchRequestInfo,
17
+ RequestInit as FetchRequestInit,
18
+ } from "node-fetch";
19
+ import type { AxiosRequestConfig } from "axios";
15
20
  import safeStringify from "json-stringify-safe";
16
21
  import { v4 as uuid } from "uuid";
17
22
  import { throwR11sNetworkError } from "./errorUtils";
@@ -19,12 +24,31 @@ import { ITokenProvider } from "./tokens";
19
24
 
20
25
  type AuthorizationHeaderGetter = (refresh?: boolean) => Promise<string | undefined>;
21
26
 
27
+ // Borrowed from @fluidframework/odsp-driver's fetch.ts
28
+ // The only purpose of this helper is to work around the slight misalignments between the
29
+ // Browser's fetch API and the 'node-fetch' package by wrapping the call to the 'node-fetch' API
30
+ // in the browser's types from 'lib.dom.d.ts'.
31
+ export const fetch = async (request: RequestInfo, config?: RequestInit): Promise<Response> =>
32
+ nodeFetch(request as FetchRequestInfo, config as FetchRequestInit) as unknown as Response;
33
+
34
+ const axiosRequestConfigToFetchRequestConfig = (requestConfig: AxiosRequestConfig): [RequestInfo, RequestInit] => {
35
+ const requestInfo: string = requestConfig.baseURL !== undefined
36
+ ? `${requestConfig.baseURL}${requestConfig.url ?? ""}`
37
+ : requestConfig.url ?? "";
38
+ const requestInit: RequestInit = {
39
+ method: requestConfig.method,
40
+ headers: requestConfig.headers,
41
+ body: requestConfig.data,
42
+ };
43
+ return [requestInfo, requestInit];
44
+ };
45
+
22
46
  export class RouterliciousRestWrapper extends RestWrapper {
23
47
  private authorizationHeader: string | undefined;
24
48
  private readonly restLess = new RestLessClient();
25
49
 
26
50
  constructor(
27
- private readonly logger: ITelemetryLogger,
51
+ logger: ITelemetryLogger,
28
52
  private readonly rateLimiter: RateLimiter,
29
53
  private readonly getAuthorizationHeader: AuthorizationHeaderGetter,
30
54
  private readonly useRestLess: boolean,
@@ -45,46 +69,47 @@ export class RouterliciousRestWrapper extends RestWrapper {
45
69
  };
46
70
 
47
71
  const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
48
-
49
- try {
50
- const response = await this.rateLimiter.schedule(async () => Axios.request<T>(translatedConfig));
51
- return response.data;
52
- } catch (reason: any) {
53
- if (!reason || !reason?.isAxiosError) {
54
- // Unknown error, treat as critical error and immediately throw as non-retriable
55
- this.logger.sendErrorEvent({
56
- eventName: "CriticalRequestError",
57
- correlationId: config.headers["x-correlation-id"] as string,
58
- }, reason);
59
- throwR11sNetworkError("r11sRequestFailed", `Unknown Error on [${config.method}] to [${config.url}]: ${
60
- safeStringify(reason)
61
- }`);
62
- }
63
-
64
- const axiosError = reason as AxiosError;
65
- if (axiosError.response?.status === statusCode) {
66
- // Axios misinterpreted as error, return as successful response
67
- return axiosError.response.data as T;
68
- }
69
-
70
- if (axiosError.response?.status === 401 && canRetry) {
71
- // Refresh Authorization header and retry once
72
- this.authorizationHeader = await this.getAuthorizationHeader(true);
73
- return this.request<T>(config, statusCode, false);
74
- }
75
-
76
- if (axiosError.response?.status === 429 && axiosError.response.data?.retryAfter > 0) {
77
- // Retry based on retryAfter[Seconds]
78
- return new Promise<T>((resolve, reject) => setTimeout(() => {
79
- this.request<T>(config, statusCode)
80
- .then(resolve)
81
- .catch(reject);
82
- }, axiosError.response!.data.retryAfter * 1000));
83
- }
84
-
85
- // Allow anything else to be handled upstream
86
- throwR11sNetworkError("r11sAxiosError", axiosError.message, axiosError.response?.status);
72
+ const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
73
+
74
+ const response: Response = await this.rateLimiter.schedule(async () => fetch(...fetchRequestConfig)
75
+ .catch(async (error) => {
76
+ // Fetch throws a TypeError on network error
77
+ const isNetworkError = error instanceof TypeError;
78
+ throwR11sNetworkError(
79
+ "r11sFetchError",
80
+ isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error));
81
+ }));
82
+
83
+ const responseBody: any = await response.clone().json().catch(async () => response.text());
84
+
85
+ // Success
86
+ if (response.ok || response.status === statusCode) {
87
+ const result: T = responseBody;
88
+ return result;
89
+ }
90
+ // Failure
91
+ if (response.status === 401 && canRetry) {
92
+ // Refresh Authorization header and retry once
93
+ this.authorizationHeader = await this.getAuthorizationHeader(true);
94
+ return this.request<T>(config, statusCode, false);
87
95
  }
96
+ if (response.status === 429 && responseBody?.retryAfter > 0) {
97
+ // Retry based on retryAfter[Seconds]
98
+ return new Promise<T>((resolve, reject) => setTimeout(() => {
99
+ this.request<T>(config, statusCode)
100
+ .then(resolve)
101
+ .catch(reject);
102
+ }, responseBody.retryAfter * 1000));
103
+ }
104
+
105
+ throwR11sNetworkError(
106
+ "r11sFetchError",
107
+ responseBody !== undefined
108
+ ? typeof responseBody === "string" ? responseBody : safeStringify(responseBody)
109
+ : response.statusText,
110
+ response.status,
111
+ responseBody?.retryAfter,
112
+ );
88
113
  }
89
114
 
90
115
  private generateHeaders(requestHeaders?: Record<string, unknown>): Record<string, unknown> {