@azure/app-configuration 1.9.2-alpha.20251029.1 → 1.9.2-alpha.20251103.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAkDjD"}
1
+ {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAGnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAsDjD"}
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { logger } from "../logger.js";
3
4
  /**
4
5
  * Creates a PipelinePolicy that normalizes query parameters:
5
6
  * - Lowercase names
@@ -10,44 +11,48 @@ export function queryParamPolicy() {
10
11
  return {
11
12
  name: "queryParamPolicy",
12
13
  async sendRequest(request, next) {
14
+ const originalUrl = request.url;
15
+ let url;
13
16
  try {
14
- const originalUrl = request.url;
15
- const url = new URL(originalUrl);
16
- if (url.search === "") {
17
+ url = new URL(originalUrl);
18
+ }
19
+ catch (error) {
20
+ if (error instanceof TypeError) {
21
+ logger.warning(`"[queryParamPolicy] Could not parse URL: ${request.url}"`);
17
22
  return next(request);
18
23
  }
19
- const params = [];
20
- for (const entry of url.search.substring(1).split("&")) {
21
- if (entry === "") {
22
- continue;
23
- }
24
- const equalIndex = entry.indexOf("=");
25
- const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
26
- const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
27
- params.push({ lowercaseName: name.toLowerCase(), value });
28
- }
29
- // Modern JavaScript Array.prototype.sort is stable
30
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
31
- params.sort((a, b) => {
32
- if (a.lowercaseName < b.lowercaseName) {
33
- return -1;
34
- }
35
- else if (a.lowercaseName > b.lowercaseName) {
36
- return 1;
37
- }
38
- return 0;
39
- });
40
- const newSearchParams = params
41
- .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
42
- .join("&");
43
- const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
44
- if (newUrl !== originalUrl) {
45
- request.url = newUrl;
24
+ throw error;
25
+ }
26
+ if (url.search === "") {
27
+ return next(request);
28
+ }
29
+ const params = [];
30
+ for (const entry of url.search.substring(1).split("&")) {
31
+ if (entry === "") {
32
+ continue;
46
33
  }
34
+ const equalIndex = entry.indexOf("=");
35
+ const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
36
+ const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
37
+ params.push({ lowercaseName: name.toLowerCase(), value });
47
38
  }
48
- catch {
49
- // If anything goes wrong, fall back to sending the original request.
50
- console.log("Failed to normalize query parameters.");
39
+ // Modern JavaScript Array.prototype.sort is stable
40
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
41
+ params.sort((a, b) => {
42
+ if (a.lowercaseName < b.lowercaseName) {
43
+ return -1;
44
+ }
45
+ else if (a.lowercaseName > b.lowercaseName) {
46
+ return 1;
47
+ }
48
+ return 0;
49
+ });
50
+ const newSearchParams = params
51
+ .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
52
+ .join("&");
53
+ const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
54
+ if (newUrl !== originalUrl) {
55
+ request.url = newUrl;
51
56
  }
52
57
  return next(request);
53
58
  },
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AASlC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,IAAI,CAAC;gBACH,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;gBAEjC,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBAED,MAAM,MAAM,GAAiB,EAAE,CAAC;gBAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;wBACjB,SAAS;oBACX,CAAC;oBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBAED,mDAAmD;gBACnD,6GAA6G;gBAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBACtC,OAAO,CAAC,CAAC,CAAC;oBACZ,CAAC;yBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBAC7C,OAAO,CAAC,CAAC;oBACX,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAW,MAAM;qBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;qBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;gBACrE,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n try {\n const originalUrl: string = request.url;\n const url = new URL(originalUrl);\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n } catch {\n // If anything goes wrong, fall back to sending the original request.\n console.log(\"Failed to normalize query parameters.\");\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
1
+ {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAQlC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;YACxC,IAAI,GAAQ,CAAC;YACb,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;oBAC/B,MAAM,CAAC,OAAO,CAAC,4CAA4C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;oBAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,mDAAmD;YACnD,6GAA6G;YAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBACtC,OAAO,CAAC,CAAC,CAAC;gBACZ,CAAC;qBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBAC7C,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,OAAO,CAAC,CAAC;YACX,CAAC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAW,MAAM;iBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;iBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;YAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const originalUrl: string = request.url;\n let url: URL;\n try {\n url = new URL(originalUrl);\n } catch (error) {\n if (error instanceof TypeError) {\n logger.warning(`\"[queryParamPolicy] Could not parse URL: ${request.url}\"`);\n return next(request);\n }\n throw error;\n }\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
@@ -18,7 +18,7 @@ export function syncTokenPolicy(syncTokens) {
18
18
  async sendRequest(request, next) {
19
19
  const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();
20
20
  if (syncTokenHeaderValue) {
21
- logger.info("[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}");
21
+ logger.info(`[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`);
22
22
  request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);
23
23
  }
24
24
  const response = await next(request);
@@ -1 +1 @@
1
- {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n \"[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}\",\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
1
+ {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAQlC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAEhD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAAsB;IACpD,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,oBAAoB,GAAG,UAAU,CAAC,uBAAuB,EAAE,CAAC;YAElE,IAAI,oBAAoB,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CACT,0CAA0C,mBAAmB,QAAQ,oBAAoB,EAAE,CAC5F,CAAC;gBACF,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,oBAAoB,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,UAAU,CAAC,2BAA2B,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAClF,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACb,kBAAkB,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE1D;;;;;;;;OAQG;IACH,2BAA2B,CAAC,oBAAwC;QAClE,IAAI,oBAAoB,IAAI,IAAI,IAAI,oBAAoB,KAAK,EAAE,EAAE,CAAC;YAChE,kEAAkE;YAClE,wBAAwB;YACxB,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAEtE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAE/D,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,cAAc,GAAG,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC7E,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;gBACnD,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,uBAAuB;QACrB,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,gBAAgB,GAAG,EAAE,CAAC;QAE5B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC;YACzD,gEAAgE;YAChE,mEAAmE;YACnE,gBAAgB,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,EAAE,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;CACF;AAED,oDAAoD;AACpD,EAAE;AACF,0BAA0B;AAC1B,EAAE;AACF,wBAAwB;AACxB,eAAe;AACf,kBAAkB;AAClB,sBAAsB;AACtB,MAAM,cAAc,GAAG,4BAA4B,CAAC;AAQpD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAEhD,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,+BAA+B,SAAS,gBAAgB,cAAc,CAAC,MAAM,EAAE,CAChF,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhD,IAAI,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1B,qEAAqE;QACrE,iDAAiD;QACjD,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,gCAAgC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;QACd,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACjB,cAAc;KACf,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n `[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`,\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAkDjD"}
1
+ {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAGnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAsDjD"}
@@ -3,6 +3,7 @@
3
3
  // Licensed under the MIT License.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.queryParamPolicy = queryParamPolicy;
6
+ const logger_js_1 = require("../logger.js");
6
7
  /**
7
8
  * Creates a PipelinePolicy that normalizes query parameters:
8
9
  * - Lowercase names
@@ -13,44 +14,48 @@ function queryParamPolicy() {
13
14
  return {
14
15
  name: "queryParamPolicy",
15
16
  async sendRequest(request, next) {
17
+ const originalUrl = request.url;
18
+ let url;
16
19
  try {
17
- const originalUrl = request.url;
18
- const url = new URL(originalUrl);
19
- if (url.search === "") {
20
+ url = new URL(originalUrl);
21
+ }
22
+ catch (error) {
23
+ if (error instanceof TypeError) {
24
+ logger_js_1.logger.warning(`"[queryParamPolicy] Could not parse URL: ${request.url}"`);
20
25
  return next(request);
21
26
  }
22
- const params = [];
23
- for (const entry of url.search.substring(1).split("&")) {
24
- if (entry === "") {
25
- continue;
26
- }
27
- const equalIndex = entry.indexOf("=");
28
- const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
29
- const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
30
- params.push({ lowercaseName: name.toLowerCase(), value });
31
- }
32
- // Modern JavaScript Array.prototype.sort is stable
33
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
34
- params.sort((a, b) => {
35
- if (a.lowercaseName < b.lowercaseName) {
36
- return -1;
37
- }
38
- else if (a.lowercaseName > b.lowercaseName) {
39
- return 1;
40
- }
41
- return 0;
42
- });
43
- const newSearchParams = params
44
- .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
45
- .join("&");
46
- const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
47
- if (newUrl !== originalUrl) {
48
- request.url = newUrl;
27
+ throw error;
28
+ }
29
+ if (url.search === "") {
30
+ return next(request);
31
+ }
32
+ const params = [];
33
+ for (const entry of url.search.substring(1).split("&")) {
34
+ if (entry === "") {
35
+ continue;
49
36
  }
37
+ const equalIndex = entry.indexOf("=");
38
+ const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
39
+ const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
40
+ params.push({ lowercaseName: name.toLowerCase(), value });
50
41
  }
51
- catch {
52
- // If anything goes wrong, fall back to sending the original request.
53
- console.log("Failed to normalize query parameters.");
42
+ // Modern JavaScript Array.prototype.sort is stable
43
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
44
+ params.sort((a, b) => {
45
+ if (a.lowercaseName < b.lowercaseName) {
46
+ return -1;
47
+ }
48
+ else if (a.lowercaseName > b.lowercaseName) {
49
+ return 1;
50
+ }
51
+ return 0;
52
+ });
53
+ const newSearchParams = params
54
+ .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
55
+ .join("&");
56
+ const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
57
+ if (newUrl !== originalUrl) {
58
+ request.url = newUrl;
54
59
  }
55
60
  return next(request);
56
61
  },
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;AAelC,4CAkDC;AAxDD;;;;;GAKG;AACH,SAAgB,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,IAAI,CAAC;gBACH,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;gBAEjC,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBAED,MAAM,MAAM,GAAiB,EAAE,CAAC;gBAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;wBACjB,SAAS;oBACX,CAAC;oBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBAED,mDAAmD;gBACnD,6GAA6G;gBAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBACtC,OAAO,CAAC,CAAC,CAAC;oBACZ,CAAC;yBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBAC7C,OAAO,CAAC,CAAC;oBACX,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAW,MAAM;qBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;qBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;gBACrE,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n try {\n const originalUrl: string = request.url;\n const url = new URL(originalUrl);\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n } catch {\n // If anything goes wrong, fall back to sending the original request.\n console.log(\"Failed to normalize query parameters.\");\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
1
+ {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;AAgBlC,4CAsDC;AA9DD,4CAAsC;AAEtC;;;;;GAKG;AACH,SAAgB,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;YACxC,IAAI,GAAQ,CAAC;YACb,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;oBAC/B,kBAAM,CAAC,OAAO,CAAC,4CAA4C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;oBAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,mDAAmD;YACnD,6GAA6G;YAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBACtC,OAAO,CAAC,CAAC,CAAC;gBACZ,CAAC;qBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBAC7C,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,OAAO,CAAC,CAAC;YACX,CAAC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAW,MAAM;iBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;iBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;YAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const originalUrl: string = request.url;\n let url: URL;\n try {\n url = new URL(originalUrl);\n } catch (error) {\n if (error instanceof TypeError) {\n logger.warning(`\"[queryParamPolicy] Could not parse URL: ${request.url}\"`);\n return next(request);\n }\n throw error;\n }\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
@@ -23,7 +23,7 @@ function syncTokenPolicy(syncTokens) {
23
23
  async sendRequest(request, next) {
24
24
  const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();
25
25
  if (syncTokenHeaderValue) {
26
- logger_js_1.logger.info("[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}");
26
+ logger_js_1.logger.info(`[syncTokenPolicy] Setting headers with ${exports.SyncTokenHeaderName} and ${syncTokenHeaderValue}`);
27
27
  request.headers.set(exports.SyncTokenHeaderName, syncTokenHeaderValue);
28
28
  }
29
29
  const response = await next(request);
@@ -1 +1 @@
1
- {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;;AAsBlC,0CAiBC;AAsFD,wCAsBC;AA3ID,4CAAsC;AAEtC;;;;GAIG;AACU,QAAA,mBAAmB,GAAG,YAAY,CAAC;AAEhD;;;;GAIG;AACH,SAAgB,eAAe,CAAC,UAAsB;IACpD,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,oBAAoB,GAAG,UAAU,CAAC,uBAAuB,EAAE,CAAC;YAElE,IAAI,oBAAoB,EAAE,CAAC;gBACzB,kBAAM,CAAC,IAAI,CACT,2FAA2F,CAC5F,CAAC;gBACF,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,2BAAmB,EAAE,oBAAoB,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YACrC,UAAU,CAAC,2BAA2B,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAAmB,CAAC,CAAC,CAAC;YAClF,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAa,UAAU;IACb,kBAAkB,GAAG,IAAI,GAAG,EAAqB,CAAC;IAE1D;;;;;;;;OAQG;IACH,2BAA2B,CAAC,oBAAwC;QAClE,IAAI,oBAAoB,IAAI,IAAI,IAAI,oBAAoB,KAAK,EAAE,EAAE,CAAC;YAChE,kEAAkE;YAClE,wBAAwB;YACxB,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAEtE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAE/D,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,cAAc,GAAG,QAAQ,CAAC,cAAc,EAAE,CAAC;gBAC7E,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;gBACnD,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,uBAAuB;QACrB,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,gBAAgB,GAAG,EAAE,CAAC;QAE5B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC;YACzD,gEAAgE;YAChE,mEAAmE;YACnE,gBAAgB,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,EAAE,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;CACF;AAlDD,gCAkDC;AAED,oDAAoD;AACpD,EAAE;AACF,0BAA0B;AAC1B,EAAE;AACF,wBAAwB;AACxB,eAAe;AACf,kBAAkB;AAClB,sBAAsB;AACtB,MAAM,cAAc,GAAG,4BAA4B,CAAC;AAQpD;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,SAAiB;IAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAEhD,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,+BAA+B,SAAS,gBAAgB,cAAc,CAAC,MAAM,EAAE,CAChF,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEhD,IAAI,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1B,qEAAqE;QACrE,iDAAiD;QACjD,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,gCAAgC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;QACd,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACjB,cAAc;KACf,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n \"[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}\",\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
1
+ {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n `[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`,\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAkDjD"}
1
+ {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAGnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAsDjD"}
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { logger } from "../logger.js";
3
4
  /**
4
5
  * Creates a PipelinePolicy that normalizes query parameters:
5
6
  * - Lowercase names
@@ -10,44 +11,48 @@ export function queryParamPolicy() {
10
11
  return {
11
12
  name: "queryParamPolicy",
12
13
  async sendRequest(request, next) {
14
+ const originalUrl = request.url;
15
+ let url;
13
16
  try {
14
- const originalUrl = request.url;
15
- const url = new URL(originalUrl);
16
- if (url.search === "") {
17
+ url = new URL(originalUrl);
18
+ }
19
+ catch (error) {
20
+ if (error instanceof TypeError) {
21
+ logger.warning(`"[queryParamPolicy] Could not parse URL: ${request.url}"`);
17
22
  return next(request);
18
23
  }
19
- const params = [];
20
- for (const entry of url.search.substring(1).split("&")) {
21
- if (entry === "") {
22
- continue;
23
- }
24
- const equalIndex = entry.indexOf("=");
25
- const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
26
- const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
27
- params.push({ lowercaseName: name.toLowerCase(), value });
28
- }
29
- // Modern JavaScript Array.prototype.sort is stable
30
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
31
- params.sort((a, b) => {
32
- if (a.lowercaseName < b.lowercaseName) {
33
- return -1;
34
- }
35
- else if (a.lowercaseName > b.lowercaseName) {
36
- return 1;
37
- }
38
- return 0;
39
- });
40
- const newSearchParams = params
41
- .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
42
- .join("&");
43
- const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
44
- if (newUrl !== originalUrl) {
45
- request.url = newUrl;
24
+ throw error;
25
+ }
26
+ if (url.search === "") {
27
+ return next(request);
28
+ }
29
+ const params = [];
30
+ for (const entry of url.search.substring(1).split("&")) {
31
+ if (entry === "") {
32
+ continue;
46
33
  }
34
+ const equalIndex = entry.indexOf("=");
35
+ const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
36
+ const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
37
+ params.push({ lowercaseName: name.toLowerCase(), value });
47
38
  }
48
- catch {
49
- // If anything goes wrong, fall back to sending the original request.
50
- console.log("Failed to normalize query parameters.");
39
+ // Modern JavaScript Array.prototype.sort is stable
40
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
41
+ params.sort((a, b) => {
42
+ if (a.lowercaseName < b.lowercaseName) {
43
+ return -1;
44
+ }
45
+ else if (a.lowercaseName > b.lowercaseName) {
46
+ return 1;
47
+ }
48
+ return 0;
49
+ });
50
+ const newSearchParams = params
51
+ .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
52
+ .join("&");
53
+ const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
54
+ if (newUrl !== originalUrl) {
55
+ request.url = newUrl;
51
56
  }
52
57
  return next(request);
53
58
  },
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AASlC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,IAAI,CAAC;gBACH,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;gBAEjC,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBAED,MAAM,MAAM,GAAiB,EAAE,CAAC;gBAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;wBACjB,SAAS;oBACX,CAAC;oBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBAED,mDAAmD;gBACnD,6GAA6G;gBAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBACtC,OAAO,CAAC,CAAC,CAAC;oBACZ,CAAC;yBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBAC7C,OAAO,CAAC,CAAC;oBACX,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAW,MAAM;qBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;qBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;gBACrE,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n try {\n const originalUrl: string = request.url;\n const url = new URL(originalUrl);\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n } catch {\n // If anything goes wrong, fall back to sending the original request.\n console.log(\"Failed to normalize query parameters.\");\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
1
+ {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAQlC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;YACxC,IAAI,GAAQ,CAAC;YACb,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;oBAC/B,MAAM,CAAC,OAAO,CAAC,4CAA4C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;oBAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,mDAAmD;YACnD,6GAA6G;YAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBACtC,OAAO,CAAC,CAAC,CAAC;gBACZ,CAAC;qBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBAC7C,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,OAAO,CAAC,CAAC;YACX,CAAC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAW,MAAM;iBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;iBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;YAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const originalUrl: string = request.url;\n let url: URL;\n try {\n url = new URL(originalUrl);\n } catch (error) {\n if (error instanceof TypeError) {\n logger.warning(`\"[queryParamPolicy] Could not parse URL: ${request.url}\"`);\n return next(request);\n }\n throw error;\n }\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
@@ -18,7 +18,7 @@ export function syncTokenPolicy(syncTokens) {
18
18
  async sendRequest(request, next) {
19
19
  const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();
20
20
  if (syncTokenHeaderValue) {
21
- logger.info("[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}");
21
+ logger.info(`[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`);
22
22
  request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);
23
23
  }
24
24
  const response = await next(request);
@@ -1 +1 @@
1
- {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n \"[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}\",\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
1
+ {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n `[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`,\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAEnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAkDjD"}
1
+ {"version":3,"file":"queryParamPolicy.d.ts","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,2BAA2B,CAAC;AAGnC;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAsDjD"}
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { logger } from "../logger.js";
3
4
  /**
4
5
  * Creates a PipelinePolicy that normalizes query parameters:
5
6
  * - Lowercase names
@@ -10,44 +11,48 @@ export function queryParamPolicy() {
10
11
  return {
11
12
  name: "queryParamPolicy",
12
13
  async sendRequest(request, next) {
14
+ const originalUrl = request.url;
15
+ let url;
13
16
  try {
14
- const originalUrl = request.url;
15
- const url = new URL(originalUrl);
16
- if (url.search === "") {
17
+ url = new URL(originalUrl);
18
+ }
19
+ catch (error) {
20
+ if (error instanceof TypeError) {
21
+ logger.warning(`"[queryParamPolicy] Could not parse URL: ${request.url}"`);
17
22
  return next(request);
18
23
  }
19
- const params = [];
20
- for (const entry of url.search.substring(1).split("&")) {
21
- if (entry === "") {
22
- continue;
23
- }
24
- const equalIndex = entry.indexOf("=");
25
- const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
26
- const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
27
- params.push({ lowercaseName: name.toLowerCase(), value });
28
- }
29
- // Modern JavaScript Array.prototype.sort is stable
30
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
31
- params.sort((a, b) => {
32
- if (a.lowercaseName < b.lowercaseName) {
33
- return -1;
34
- }
35
- else if (a.lowercaseName > b.lowercaseName) {
36
- return 1;
37
- }
38
- return 0;
39
- });
40
- const newSearchParams = params
41
- .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
42
- .join("&");
43
- const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
44
- if (newUrl !== originalUrl) {
45
- request.url = newUrl;
24
+ throw error;
25
+ }
26
+ if (url.search === "") {
27
+ return next(request);
28
+ }
29
+ const params = [];
30
+ for (const entry of url.search.substring(1).split("&")) {
31
+ if (entry === "") {
32
+ continue;
46
33
  }
34
+ const equalIndex = entry.indexOf("=");
35
+ const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);
36
+ const value = equalIndex === -1 ? "" : entry.substring(equalIndex + 1);
37
+ params.push({ lowercaseName: name.toLowerCase(), value });
47
38
  }
48
- catch {
49
- // If anything goes wrong, fall back to sending the original request.
50
- console.log("Failed to normalize query parameters.");
39
+ // Modern JavaScript Array.prototype.sort is stable
40
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability
41
+ params.sort((a, b) => {
42
+ if (a.lowercaseName < b.lowercaseName) {
43
+ return -1;
44
+ }
45
+ else if (a.lowercaseName > b.lowercaseName) {
46
+ return 1;
47
+ }
48
+ return 0;
49
+ });
50
+ const newSearchParams = params
51
+ .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)
52
+ .join("&");
53
+ const newUrl = url.origin + url.pathname + "?" + newSearchParams + url.hash;
54
+ if (newUrl !== originalUrl) {
55
+ request.url = newUrl;
51
56
  }
52
57
  return next(request);
53
58
  },
@@ -1 +1 @@
1
- {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AASlC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,IAAI,CAAC;gBACH,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;gBACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;gBAEjC,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBAED,MAAM,MAAM,GAAiB,EAAE,CAAC;gBAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;wBACjB,SAAS;oBACX,CAAC;oBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;oBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;oBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;oBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBAED,mDAAmD;gBACnD,6GAA6G;gBAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBACtC,OAAO,CAAC,CAAC,CAAC;oBACZ,CAAC;yBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;wBAC7C,OAAO,CAAC,CAAC;oBACX,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;gBAEH,MAAM,eAAe,GAAW,MAAM;qBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;qBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;gBAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;gBACvB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;gBACrE,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACvD,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n try {\n const originalUrl: string = request.url;\n const url = new URL(originalUrl);\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n } catch {\n // If anything goes wrong, fall back to sending the original request.\n console.log(\"Failed to normalize query parameters.\");\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
1
+ {"version":3,"file":"queryParamPolicy.js","sourceRoot":"","sources":["../../../src/internal/queryParamPolicy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAQlC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,KAAK,CAAC,WAAW,CAAC,OAAwB,EAAE,IAAiB;YAC3D,MAAM,WAAW,GAAW,OAAO,CAAC,GAAG,CAAC;YACxC,IAAI,GAAQ,CAAC;YACb,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;oBAC/B,MAAM,CAAC,OAAO,CAAC,4CAA4C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC;oBAC3E,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;gBACD,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBACtC,MAAM,IAAI,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;gBACxE,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;gBACvE,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;YAED,mDAAmD;YACnD,6GAA6G;YAC7G,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACnB,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBACtC,OAAO,CAAC,CAAC,CAAC;gBACZ,CAAC;qBAAM,IAAI,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;oBAC7C,OAAO,CAAC,CAAC;gBACX,CAAC;gBACD,OAAO,CAAC,CAAC;YACX,CAAC,CAAC,CAAC;YAEH,MAAM,eAAe,GAAW,MAAM;iBACnC,GAAG,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,aAAa,IAAI,KAAK,EAAE,CAAC;iBAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;YAEb,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,eAAe,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5E,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,GAAG,MAAM,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * Creates a PipelinePolicy that normalizes query parameters:\n * - Lowercase names\n * - Sort by lowercase name\n * - Preserve the relative order of duplicates\n */\nexport function queryParamPolicy(): PipelinePolicy {\n return {\n name: \"queryParamPolicy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const originalUrl: string = request.url;\n let url: URL;\n try {\n url = new URL(originalUrl);\n } catch (error) {\n if (error instanceof TypeError) {\n logger.warning(`\"[queryParamPolicy] Could not parse URL: ${request.url}\"`);\n return next(request);\n }\n throw error;\n }\n\n if (url.search === \"\") {\n return next(request);\n }\n\n const params: ParamEntry[] = [];\n for (const entry of url.search.substring(1).split(\"&\")) {\n if (entry === \"\") {\n continue;\n }\n const equalIndex = entry.indexOf(\"=\");\n const name = equalIndex === -1 ? entry : entry.substring(0, equalIndex);\n const value = equalIndex === -1 ? \"\" : entry.substring(equalIndex + 1);\n params.push({ lowercaseName: name.toLowerCase(), value });\n }\n\n // Modern JavaScript Array.prototype.sort is stable\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sort_stability\n params.sort((a, b) => {\n if (a.lowercaseName < b.lowercaseName) {\n return -1;\n } else if (a.lowercaseName > b.lowercaseName) {\n return 1;\n }\n return 0;\n });\n\n const newSearchParams: string = params\n .map(({ lowercaseName, value }) => `${lowercaseName}=${value}`)\n .join(\"&\");\n\n const newUrl = url.origin + url.pathname + \"?\" + newSearchParams + url.hash;\n if (newUrl !== originalUrl) {\n request.url = newUrl;\n }\n\n return next(request);\n },\n };\n}\n\ninterface ParamEntry {\n lowercaseName: string;\n value: string;\n}\n"]}
@@ -18,7 +18,7 @@ export function syncTokenPolicy(syncTokens) {
18
18
  async sendRequest(request, next) {
19
19
  const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();
20
20
  if (syncTokenHeaderValue) {
21
- logger.info("[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}");
21
+ logger.info(`[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`);
22
22
  request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);
23
23
  }
24
24
  const response = await next(request);
@@ -1 +1 @@
1
- {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n \"[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}\",\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
1
+ {"version":3,"file":"syncTokenPolicy.js","sourceRoot":"","sources":["../../../src/internal/syncTokenPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { logger } from \"../logger.js\";\n\n/**\n * The sync token header, as described here:\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n * @internal\n */\nexport const SyncTokenHeaderName = \"sync-token\";\n\n/**\n * A policy factory for injecting sync tokens properly into outgoing requests.\n * @param syncTokens - the sync tokens store to be used across requests.\n * @internal\n */\nexport function syncTokenPolicy(syncTokens: SyncTokens): PipelinePolicy {\n return {\n name: \"Sync Token Policy\",\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n const syncTokenHeaderValue = syncTokens.getSyncTokenHeaderValue();\n\n if (syncTokenHeaderValue) {\n logger.info(\n `[syncTokenPolicy] Setting headers with ${SyncTokenHeaderName} and ${syncTokenHeaderValue}`,\n );\n request.headers.set(SyncTokenHeaderName, syncTokenHeaderValue);\n }\n const response = await next(request);\n syncTokens.addSyncTokenFromHeaderValue(response.headers.get(SyncTokenHeaderName));\n return response;\n },\n };\n}\n\n/**\n * Sync token tracker (allows for real-time consistency, even in the face of\n * caching and load balancing within App Configuration).\n *\n * (protocol and format described here)\n * https://learn.microsoft.com/azure/azure-app-configuration/rest-api-consistency\n *\n * @internal\n */\nexport class SyncTokens {\n private _currentSyncTokens = new Map<string, SyncToken>();\n\n /**\n * Takes the value from the header named after the constant `SyncTokenHeaderName`\n * and adds it to our list of accumulated sync tokens.\n *\n * If given an empty value (or undefined) it clears the current list of sync tokens.\n * (indicates the service has properly absorbed values into the cluster).\n *\n * @param syncTokenHeaderValue - The full value of the sync token header.\n */\n addSyncTokenFromHeaderValue(syncTokenHeaderValue: string | undefined): void {\n if (syncTokenHeaderValue == null || syncTokenHeaderValue === \"\") {\n // eventually everything gets synced up and we don't have to track\n // these headers anymore\n this._currentSyncTokens.clear();\n return;\n }\n\n const newTokens = syncTokenHeaderValue.split(\",\").map(parseSyncToken);\n\n for (const newToken of newTokens) {\n const existingToken = this._currentSyncTokens.get(newToken.id);\n\n if (!existingToken || existingToken.sequenceNumber < newToken.sequenceNumber) {\n this._currentSyncTokens.set(newToken.id, newToken);\n continue;\n }\n }\n }\n\n /**\n * Gets a properly formatted SyncToken header value.\n */\n getSyncTokenHeaderValue(): string | undefined {\n if (this._currentSyncTokens.size === 0) {\n return undefined;\n }\n\n const syncTokenStrings = [];\n\n for (const syncToken of this._currentSyncTokens.values()) {\n // note that you don't include the 'sn' field here - that's only\n // used for internal tracking of the 'version' for the token itself\n syncTokenStrings.push(`${syncToken.id}=${syncToken.value}`);\n }\n\n return syncTokenStrings.join(\",\");\n }\n}\n\n// An example sync token (from their documentation):\n//\n// jtqGc1I4=MDoyOA==;sn=28\n//\n// Which breaks down to:\n// id: jtqGc1I4\n// value: MDoyOA==\n// sequence number: 28\nconst syncTokenRegex = /^([^=]+)=([^;]+);sn=(\\d+)$/;\n\ninterface SyncToken {\n id: string;\n value: string;\n sequenceNumber: number;\n}\n\n/**\n * Parses a single sync token into it's constituent parts.\n *\n * @param syncToken - A single sync token.\n *\n * @internal\n */\nexport function parseSyncToken(syncToken: string): SyncToken {\n const matches = syncToken.match(syncTokenRegex);\n\n if (matches == null) {\n throw new Error(\n `Failed to parse sync token '${syncToken}' with regex ${syncTokenRegex.source}`,\n );\n }\n\n const sequenceNumber = parseInt(matches[3], 10);\n\n if (isNaN(sequenceNumber)) {\n // this should be impossible since our regex restricts to just digits\n // but there's nothing wrong with being thorough.\n throw new Error(`${syncToken}: The sequence number value '${matches[3]}' wasn't a number`);\n }\n\n return {\n id: matches[1],\n value: matches[2],\n sequenceNumber,\n };\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@azure/app-configuration",
3
3
  "author": "Microsoft Corporation",
4
4
  "description": "An isomorphic client library for the Azure App Configuration service.",
5
- "version": "1.9.2-alpha.20251029.1",
5
+ "version": "1.9.2-alpha.20251103.1",
6
6
  "sdk-type": "client",
7
7
  "keywords": [
8
8
  "node",
@@ -78,9 +78,9 @@
78
78
  "tshy": "^3.0.0",
79
79
  "typescript": "~5.9.3",
80
80
  "vitest": "^3.2.3",
81
- "@azure-tools/test-credential": "^2.1.2",
82
81
  "@azure-tools/test-recorder": "^4.1.1",
83
82
  "@azure/dev-tool": "^1.0.0",
83
+ "@azure-tools/test-credential": "^2.1.2",
84
84
  "@azure/eslint-plugin-azure-sdk": "^3.0.0"
85
85
  },
86
86
  "//sampleConfiguration": {