@azure/container-registry 1.1.1-alpha.20250619.1 → 1.1.1-alpha.20250730.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.
- package/dist/browser/containerRegistryChallengeHandler.js +6 -3
- package/dist/browser/containerRegistryChallengeHandler.js.map +1 -1
- package/dist/browser/containerRegistryClient.js +42 -50
- package/dist/browser/containerRegistryClient.js.map +1 -1
- package/dist/browser/containerRegistryTokenCredential.js +9 -1
- package/dist/browser/containerRegistryTokenCredential.js.map +1 -1
- package/dist/browser/containerRepository.js +41 -46
- package/dist/browser/containerRepository.js.map +1 -1
- package/dist/browser/content/containerRegistryContentClient.js +34 -32
- package/dist/browser/content/containerRegistryContentClient.js.map +1 -1
- package/dist/browser/generated/generatedClient.js +12 -3
- package/dist/browser/generated/generatedClient.js.map +1 -1
- package/dist/browser/generated/generatedClientContext.js +9 -2
- package/dist/browser/generated/generatedClientContext.js.map +1 -1
- package/dist/browser/generated/models/mappers.js +63 -29
- package/dist/browser/generated/models/mappers.js.map +1 -1
- package/dist/browser/generated/operations/authentication.js +1 -0
- package/dist/browser/generated/operations/authentication.js.map +1 -1
- package/dist/browser/generated/operations/containerRegistry.js +1 -0
- package/dist/browser/generated/operations/containerRegistry.js.map +1 -1
- package/dist/browser/generated/operations/containerRegistryBlob.js +1 -0
- package/dist/browser/generated/operations/containerRegistryBlob.js.map +1 -1
- package/dist/browser/registryArtifact.js +69 -53
- package/dist/browser/registryArtifact.js.map +1 -1
- package/dist/browser/transformations.js +13 -3
- package/dist/browser/transformations.js.map +1 -1
- package/dist/browser/utils/helpers.js +19 -35
- package/dist/browser/utils/helpers.js.map +1 -1
- package/dist/browser/utils/retriableReadableStream.js +64 -56
- package/dist/browser/utils/retriableReadableStream.js.map +1 -1
- package/dist/browser/utils/tokenCycler.js +7 -6
- package/dist/browser/utils/tokenCycler.js.map +1 -1
- package/dist/commonjs/containerRegistryChallengeHandler.js +6 -3
- package/dist/commonjs/containerRegistryChallengeHandler.js.map +1 -1
- package/dist/commonjs/containerRegistryClient.js +42 -50
- package/dist/commonjs/containerRegistryClient.js.map +1 -1
- package/dist/commonjs/containerRegistryTokenCredential.js +9 -1
- package/dist/commonjs/containerRegistryTokenCredential.js.map +1 -1
- package/dist/commonjs/containerRepository.js +41 -46
- package/dist/commonjs/containerRepository.js.map +1 -1
- package/dist/commonjs/content/containerRegistryContentClient.js +34 -31
- package/dist/commonjs/content/containerRegistryContentClient.js.map +1 -1
- package/dist/commonjs/generated/generatedClient.js +12 -3
- package/dist/commonjs/generated/generatedClient.js.map +1 -1
- package/dist/commonjs/generated/generatedClientContext.js +9 -2
- package/dist/commonjs/generated/generatedClientContext.js.map +1 -1
- package/dist/commonjs/generated/models/mappers.js +63 -29
- package/dist/commonjs/generated/models/mappers.js.map +1 -1
- package/dist/commonjs/generated/operations/authentication.js +1 -0
- package/dist/commonjs/generated/operations/authentication.js.map +1 -1
- package/dist/commonjs/generated/operations/containerRegistry.js +1 -0
- package/dist/commonjs/generated/operations/containerRegistry.js.map +1 -1
- package/dist/commonjs/generated/operations/containerRegistryBlob.js +1 -0
- package/dist/commonjs/generated/operations/containerRegistryBlob.js.map +1 -1
- package/dist/commonjs/registryArtifact.js +69 -53
- package/dist/commonjs/registryArtifact.js.map +1 -1
- package/dist/commonjs/transformations.js +13 -3
- package/dist/commonjs/transformations.js.map +1 -1
- package/dist/commonjs/tsdoc-metadata.json +11 -11
- package/dist/commonjs/utils/helpers.js +19 -35
- package/dist/commonjs/utils/helpers.js.map +1 -1
- package/dist/commonjs/utils/retriableReadableStream.js +64 -56
- package/dist/commonjs/utils/retriableReadableStream.js.map +1 -1
- package/dist/commonjs/utils/tokenCycler.js +7 -6
- package/dist/commonjs/utils/tokenCycler.js.map +1 -1
- package/dist/esm/containerRegistryChallengeHandler.js +6 -3
- package/dist/esm/containerRegistryChallengeHandler.js.map +1 -1
- package/dist/esm/containerRegistryClient.js +42 -50
- package/dist/esm/containerRegistryClient.js.map +1 -1
- package/dist/esm/containerRegistryTokenCredential.js +9 -1
- package/dist/esm/containerRegistryTokenCredential.js.map +1 -1
- package/dist/esm/containerRepository.js +41 -46
- package/dist/esm/containerRepository.js.map +1 -1
- package/dist/esm/content/containerRegistryContentClient.js +34 -32
- package/dist/esm/content/containerRegistryContentClient.js.map +1 -1
- package/dist/esm/generated/generatedClient.js +12 -3
- package/dist/esm/generated/generatedClient.js.map +1 -1
- package/dist/esm/generated/generatedClientContext.js +9 -2
- package/dist/esm/generated/generatedClientContext.js.map +1 -1
- package/dist/esm/generated/models/mappers.js +63 -29
- package/dist/esm/generated/models/mappers.js.map +1 -1
- package/dist/esm/generated/operations/authentication.js +1 -0
- package/dist/esm/generated/operations/authentication.js.map +1 -1
- package/dist/esm/generated/operations/containerRegistry.js +1 -0
- package/dist/esm/generated/operations/containerRegistry.js.map +1 -1
- package/dist/esm/generated/operations/containerRegistryBlob.js +1 -0
- package/dist/esm/generated/operations/containerRegistryBlob.js.map +1 -1
- package/dist/esm/registryArtifact.js +69 -53
- package/dist/esm/registryArtifact.js.map +1 -1
- package/dist/esm/transformations.js +13 -3
- package/dist/esm/transformations.js.map +1 -1
- package/dist/esm/utils/helpers.js +19 -35
- package/dist/esm/utils/helpers.js.map +1 -1
- package/dist/esm/utils/retriableReadableStream.js +64 -56
- package/dist/esm/utils/retriableReadableStream.js.map +1 -1
- package/dist/esm/utils/tokenCycler.js +7 -6
- package/dist/esm/utils/tokenCycler.js.map +1 -1
- package/dist/react-native/containerRegistryChallengeHandler.js +6 -3
- package/dist/react-native/containerRegistryChallengeHandler.js.map +1 -1
- package/dist/react-native/containerRegistryClient.js +42 -50
- package/dist/react-native/containerRegistryClient.js.map +1 -1
- package/dist/react-native/containerRegistryTokenCredential.js +9 -1
- package/dist/react-native/containerRegistryTokenCredential.js.map +1 -1
- package/dist/react-native/containerRepository.js +41 -46
- package/dist/react-native/containerRepository.js.map +1 -1
- package/dist/react-native/content/containerRegistryContentClient.js +34 -32
- package/dist/react-native/content/containerRegistryContentClient.js.map +1 -1
- package/dist/react-native/generated/generatedClient.js +12 -3
- package/dist/react-native/generated/generatedClient.js.map +1 -1
- package/dist/react-native/generated/generatedClientContext.js +9 -2
- package/dist/react-native/generated/generatedClientContext.js.map +1 -1
- package/dist/react-native/generated/models/mappers.js +63 -29
- package/dist/react-native/generated/models/mappers.js.map +1 -1
- package/dist/react-native/generated/operations/authentication.js +1 -0
- package/dist/react-native/generated/operations/authentication.js.map +1 -1
- package/dist/react-native/generated/operations/containerRegistry.js +1 -0
- package/dist/react-native/generated/operations/containerRegistry.js.map +1 -1
- package/dist/react-native/generated/operations/containerRegistryBlob.js +1 -0
- package/dist/react-native/generated/operations/containerRegistryBlob.js.map +1 -1
- package/dist/react-native/registryArtifact.js +69 -53
- package/dist/react-native/registryArtifact.js.map +1 -1
- package/dist/react-native/transformations.js +13 -3
- package/dist/react-native/transformations.js.map +1 -1
- package/dist/react-native/utils/helpers.js +19 -35
- package/dist/react-native/utils/helpers.js.map +1 -1
- package/dist/react-native/utils/retriableReadableStream.js +64 -56
- package/dist/react-native/utils/retriableReadableStream.js.map +1 -1
- package/dist/react-native/utils/tokenCycler.js +7 -6
- package/dist/react-native/utils/tokenCycler.js.map +1 -1
- package/package.json +2 -2
|
@@ -12,9 +12,19 @@ export function toManifestWritableProperties(from) {
|
|
|
12
12
|
: undefined;
|
|
13
13
|
}
|
|
14
14
|
export function toArtifactManifestProperties(from, repositoryName, registryLoginServer) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
repositoryName,
|
|
15
|
+
return {
|
|
16
|
+
registryLoginServer,
|
|
17
|
+
repositoryName,
|
|
18
|
+
digest: from.digest,
|
|
19
|
+
sizeInBytes: from.size,
|
|
20
|
+
createdOn: from.createdOn,
|
|
21
|
+
lastUpdatedOn: from.lastUpdatedOn,
|
|
22
|
+
architecture: from.architecture ?? undefined,
|
|
23
|
+
operatingSystem: from.operatingSystem ?? undefined,
|
|
24
|
+
relatedArtifacts: from.relatedArtifacts ?? [],
|
|
25
|
+
tags: from.tags ?? [],
|
|
26
|
+
...toManifestWritableProperties(from),
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
export function toServiceTagOrderBy(orderBy) {
|
|
20
30
|
return orderBy === "LastUpdatedOnAscending"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transformations.js","sourceRoot":"","sources":["../../src/transformations.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AA0BlC,MAAM,UAAU,4BAA4B,CAC1C,IAAwC;IAExC,qFAAqF;IACrF,OAAO,IAAI;QACT,CAAC,CAAC;YACE,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB;QACH,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,IAAuC,EACvC,cAAsB,EACtB,mBAA2B
|
|
1
|
+
{"version":3,"file":"transformations.js","sourceRoot":"","sources":["../../src/transformations.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AA0BlC,MAAM,UAAU,4BAA4B,CAC1C,IAAwC;IAExC,qFAAqF;IACrF,OAAO,IAAI;QACT,CAAC,CAAC;YACE,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB;QACH,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,IAAuC,EACvC,cAAsB,EACtB,mBAA2B;IAE3B,OAAO;QACL,mBAAmB;QACnB,cAAc;QACd,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,WAAW,EAAE,IAAI,CAAC,IAAI;QACtB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAC5C,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,SAAS;QAClD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,EAAE;QAC7C,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;QACrB,GAAG,4BAA4B,CAAC,IAAI,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAA0B;IAC5D,OAAO,OAAO,KAAK,wBAAwB;QACzC,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,OAAO,KAAK,yBAAyB;YACrC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,OAA+B;IAE/B,OAAO,OAAO,KAAK,wBAAwB;QACzC,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,OAAO,KAAK,yBAAyB;YACrC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,SAAS,CAAC;AAClB,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n ArtifactTagOrderBy as ServiceTagOrderBy,\n ArtifactManifestOrderBy as ServiceManifestOrderBy,\n ManifestWriteableProperties as ServiceManifestWritableProperties,\n ArtifactManifestProperties as ServiceArtifactManifestProperties,\n} from \"./generated/models/index.js\";\nimport type {\n ArtifactManifestProperties,\n ArtifactTagOrder,\n ArtifactManifestOrder,\n} from \"./models.js\";\n\n/** Changeable attributes. Filter out `quarantineState` and `quarantineDetails` returned by service */\ninterface ManifestWriteableProperties {\n /** Delete enabled */\n canDelete?: boolean;\n /** Write enabled */\n canWrite?: boolean;\n /** List enabled */\n canList?: boolean;\n /** Read enabled */\n canRead?: boolean;\n}\n\nexport function toManifestWritableProperties(\n from?: ServiceManifestWritableProperties,\n): ManifestWriteableProperties | undefined {\n // don't return unwanted properties, namely `quarantineState` and `quarantineDetails`\n return from\n ? {\n canDelete: from.canDelete,\n canList: from.canList,\n canRead: from.canRead,\n canWrite: from.canWrite,\n }\n : undefined;\n}\n\nexport function toArtifactManifestProperties(\n from: ServiceArtifactManifestProperties,\n repositoryName: string,\n registryLoginServer: string,\n): ArtifactManifestProperties {\n return {\n registryLoginServer,\n repositoryName,\n digest: from.digest,\n sizeInBytes: from.size,\n createdOn: from.createdOn,\n lastUpdatedOn: from.lastUpdatedOn,\n architecture: from.architecture ?? undefined,\n operatingSystem: from.operatingSystem ?? undefined,\n relatedArtifacts: from.relatedArtifacts ?? [],\n tags: from.tags ?? [],\n ...toManifestWritableProperties(from),\n };\n}\n\nexport function toServiceTagOrderBy(orderBy?: ArtifactTagOrder): ServiceTagOrderBy | undefined {\n return orderBy === \"LastUpdatedOnAscending\"\n ? \"timeasc\"\n : orderBy === \"LastUpdatedOnDescending\"\n ? \"timedesc\"\n : undefined;\n}\n\nexport function toServiceManifestOrderBy(\n orderBy?: ArtifactManifestOrder,\n): ServiceManifestOrderBy | undefined {\n return orderBy === \"LastUpdatedOnAscending\"\n ? \"timeasc\"\n : orderBy === \"LastUpdatedOnDescending\"\n ? \"timedesc\"\n : undefined;\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import { __asyncGenerator, __asyncValues, __await } from "tslib";
|
|
4
3
|
/**
|
|
5
4
|
* Extract the path part from the next link value returned by the service,
|
|
6
5
|
* @internal
|
|
@@ -9,7 +8,7 @@ export function extractNextLink(value) {
|
|
|
9
8
|
// The link value has this pattern
|
|
10
9
|
// `</acr/v1/name/...&n=2&orderby=>; rel="next"`
|
|
11
10
|
// and we only want the part inside of <...>
|
|
12
|
-
return value
|
|
11
|
+
return value?.substr(1, value.indexOf(">") - 1);
|
|
13
12
|
}
|
|
14
13
|
/**
|
|
15
14
|
* Checks whether a string is a digest
|
|
@@ -33,40 +32,25 @@ export async function readStreamToEnd(stream, maxLength) {
|
|
|
33
32
|
stream.on("error", (err) => reject(err));
|
|
34
33
|
});
|
|
35
34
|
}
|
|
36
|
-
export function readChunksFromStream(stream, chunkSize) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
dataCursor += bytesCopied;
|
|
51
|
-
chunkCursor += bytesCopied;
|
|
52
|
-
if (chunkCursor >= chunkSize) {
|
|
53
|
-
yield yield __await(chunk);
|
|
54
|
-
chunkCursor = 0;
|
|
55
|
-
chunk = Buffer.alloc(chunkSize);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
35
|
+
export async function* readChunksFromStream(stream, chunkSize) {
|
|
36
|
+
let chunk = Buffer.alloc(chunkSize);
|
|
37
|
+
let chunkCursor = 0;
|
|
38
|
+
for await (const data of stream) {
|
|
39
|
+
const dataAsBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf8");
|
|
40
|
+
let dataCursor = 0;
|
|
41
|
+
while (dataCursor < dataAsBuffer.length) {
|
|
42
|
+
const bytesCopied = dataAsBuffer.copy(chunk, chunkCursor, dataCursor);
|
|
43
|
+
dataCursor += bytesCopied;
|
|
44
|
+
chunkCursor += bytesCopied;
|
|
45
|
+
if (chunkCursor >= chunkSize) {
|
|
46
|
+
yield chunk;
|
|
47
|
+
chunkCursor = 0;
|
|
48
|
+
chunk = Buffer.alloc(chunkSize);
|
|
58
49
|
}
|
|
59
50
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
finally { if (e_1) throw e_1.error; }
|
|
66
|
-
}
|
|
67
|
-
if (chunkCursor > 0) {
|
|
68
|
-
yield yield __await(chunk.subarray(0, chunkCursor));
|
|
69
|
-
}
|
|
70
|
-
});
|
|
51
|
+
}
|
|
52
|
+
if (chunkCursor > 0) {
|
|
53
|
+
yield chunk.subarray(0, chunkCursor);
|
|
54
|
+
}
|
|
71
55
|
}
|
|
72
56
|
//# sourceMappingURL=helpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAyB;IACvD,kCAAkC;IAClC,oDAAoD;IACpD,4CAA4C;IAC5C,OAAO,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,WAAmB;IAC1C,OAAO,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA6B,EAC7B,SAAkB;IAElB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;YAE1B,IAAI,SAAS,IAAI,SAAS,GAAG,SAAS,EAAE,CAAC;gBACvC,MAAM,CAAC,IAAI,KAAK,CAAC,6CAA6C,SAAS,SAAS,CAAC,CAAC,CAAC;YACrF,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,oBAAoB,CACzC,MAA6B,EAC7B,SAAiB;IAEjB,IAAI,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAChC,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC9E,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,OAAO,UAAU,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;YACtE,UAAU,IAAI,WAAW,CAAC;YAC1B,WAAW,IAAI,WAAW,CAAC;YAC3B,IAAI,WAAW,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,KAAK,CAAC;gBACZ,WAAW,GAAG,CAAC,CAAC;gBAChB,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACvC,CAAC;AACH,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\n/**\n * Extract the path part from the next link value returned by the service,\n * @internal\n */\nexport function extractNextLink(value: string | undefined): string | undefined {\n // The link value has this pattern\n // `</acr/v1/name/...&n=2&orderby=>; rel=\"next\"`\n // and we only want the part inside of <...>\n return value?.substr(1, value.indexOf(\">\") - 1);\n}\n\n/**\n * Checks whether a string is a digest\n * @internal\n */\nexport function isDigest(tagOrDigest: string): boolean {\n return tagOrDigest.includes(\":\");\n}\n\nexport async function readStreamToEnd(\n stream: NodeJS.ReadableStream,\n maxLength?: number,\n): Promise<Buffer> {\n const buffers: Buffer[] = [];\n let bytesRead = 0;\n\n return new Promise((resolve, reject) => {\n stream.on(\"data\", (chunk) => {\n buffers.push(chunk);\n bytesRead += chunk.length;\n\n if (maxLength && bytesRead > maxLength) {\n reject(new Error(`Stream exceeded maximum allowed length of ${maxLength} bytes.`));\n }\n });\n stream.on(\"end\", () => resolve(Buffer.concat(buffers)));\n stream.on(\"error\", (err) => reject(err));\n });\n}\n\nexport async function* readChunksFromStream(\n stream: NodeJS.ReadableStream,\n chunkSize: number,\n): AsyncGenerator<Buffer> {\n let chunk = Buffer.alloc(chunkSize);\n let chunkCursor = 0;\n\n for await (const data of stream) {\n const dataAsBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data, \"utf8\");\n let dataCursor = 0;\n\n while (dataCursor < dataAsBuffer.length) {\n const bytesCopied = dataAsBuffer.copy(chunk, chunkCursor, dataCursor);\n dataCursor += bytesCopied;\n chunkCursor += bytesCopied;\n if (chunkCursor >= chunkSize) {\n yield chunk;\n chunkCursor = 0;\n chunk = Buffer.alloc(chunkSize);\n }\n }\n }\n\n if (chunkCursor > 0) {\n yield chunk.subarray(0, chunkCursor);\n }\n}\n"]}
|
|
@@ -8,6 +8,17 @@ import { Readable } from "node:stream";
|
|
|
8
8
|
* A Node.js ReadableStream will internally retry when internal ReadableStream unexpected ends.
|
|
9
9
|
*/
|
|
10
10
|
export class RetriableReadableStream extends Readable {
|
|
11
|
+
source;
|
|
12
|
+
getter;
|
|
13
|
+
offset;
|
|
14
|
+
start;
|
|
15
|
+
end;
|
|
16
|
+
retries = 0;
|
|
17
|
+
maxRetryRequests;
|
|
18
|
+
onData;
|
|
19
|
+
onEnd;
|
|
20
|
+
onProgress;
|
|
21
|
+
options;
|
|
11
22
|
/**
|
|
12
23
|
* Creates an instance of RetriableReadableStream.
|
|
13
24
|
*
|
|
@@ -23,62 +34,6 @@ export class RetriableReadableStream extends Readable {
|
|
|
23
34
|
this.source = source;
|
|
24
35
|
this.getter = getter;
|
|
25
36
|
this.offset = offset;
|
|
26
|
-
this.retries = 0;
|
|
27
|
-
this.sourceDataHandler = (data) => {
|
|
28
|
-
var _a, _b;
|
|
29
|
-
if (this.options.doInjectErrorOnce) {
|
|
30
|
-
this.options.doInjectErrorOnce = undefined;
|
|
31
|
-
this.source.pause();
|
|
32
|
-
this.sourceErrorOrEndHandler();
|
|
33
|
-
this.source.destroy();
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
this.offset += data.length;
|
|
37
|
-
if (this.offset > this.end + 1) {
|
|
38
|
-
this.destroy(new Error(`Data corruption failure: Received more data than original request, data needed offset is ${this.end}, received offset: ${this.offset - 1}`));
|
|
39
|
-
}
|
|
40
|
-
(_a = this.onData) === null || _a === void 0 ? void 0 : _a.call(this, data);
|
|
41
|
-
(_b = this.onProgress) === null || _b === void 0 ? void 0 : _b.call(this, { loadedBytes: this.offset - this.start });
|
|
42
|
-
if (!this.push(data)) {
|
|
43
|
-
this.source.pause();
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
this.sourceAbortedHandler = () => {
|
|
47
|
-
const abortError = new AbortError("The operation was aborted.");
|
|
48
|
-
this.destroy(abortError);
|
|
49
|
-
};
|
|
50
|
-
this.sourceErrorOrEndHandler = (err) => {
|
|
51
|
-
var _a;
|
|
52
|
-
if (err && err.name === "AbortError") {
|
|
53
|
-
this.destroy(err);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
this.removeSourceEventHandlers();
|
|
57
|
-
if (this.offset - 1 === this.end) {
|
|
58
|
-
(_a = this.onEnd) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
59
|
-
this.push(null);
|
|
60
|
-
}
|
|
61
|
-
else if (this.offset <= this.end) {
|
|
62
|
-
if (this.retries < this.maxRetryRequests) {
|
|
63
|
-
this.retries += 1;
|
|
64
|
-
this.getter(this.offset)
|
|
65
|
-
.then((newSource) => {
|
|
66
|
-
this.source = newSource;
|
|
67
|
-
this.setSourceEventHandlers();
|
|
68
|
-
return;
|
|
69
|
-
})
|
|
70
|
-
.catch((error) => {
|
|
71
|
-
this.destroy(error);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
this.destroy(new Error(`Data corruption failure: received less data than required and reached maxRetires limitation. Received data offset: ${this.offset - 1}, data needed offset: ${this.end}, retries: ${this.retries}, max retries: ${this.maxRetryRequests}`));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
this.destroy(new Error(`Data corruption failure: Received more data than original request, data needed offset is ${this.end}, received offset: ${this.offset - 1}`));
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
37
|
this.getter = getter;
|
|
83
38
|
this.start = offset;
|
|
84
39
|
this.end = offset + count - 1;
|
|
@@ -106,6 +61,59 @@ export class RetriableReadableStream extends Readable {
|
|
|
106
61
|
this.source.removeListener("error", this.sourceErrorOrEndHandler);
|
|
107
62
|
this.source.removeListener("aborted", this.sourceAbortedHandler);
|
|
108
63
|
}
|
|
64
|
+
sourceDataHandler = (data) => {
|
|
65
|
+
if (this.options.doInjectErrorOnce) {
|
|
66
|
+
this.options.doInjectErrorOnce = undefined;
|
|
67
|
+
this.source.pause();
|
|
68
|
+
this.sourceErrorOrEndHandler();
|
|
69
|
+
this.source.destroy();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.offset += data.length;
|
|
73
|
+
if (this.offset > this.end + 1) {
|
|
74
|
+
this.destroy(new Error(`Data corruption failure: Received more data than original request, data needed offset is ${this.end}, received offset: ${this.offset - 1}`));
|
|
75
|
+
}
|
|
76
|
+
this.onData?.(data);
|
|
77
|
+
this.onProgress?.({ loadedBytes: this.offset - this.start });
|
|
78
|
+
if (!this.push(data)) {
|
|
79
|
+
this.source.pause();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
sourceAbortedHandler = () => {
|
|
83
|
+
const abortError = new AbortError("The operation was aborted.");
|
|
84
|
+
this.destroy(abortError);
|
|
85
|
+
};
|
|
86
|
+
sourceErrorOrEndHandler = (err) => {
|
|
87
|
+
if (err && err.name === "AbortError") {
|
|
88
|
+
this.destroy(err);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.removeSourceEventHandlers();
|
|
92
|
+
if (this.offset - 1 === this.end) {
|
|
93
|
+
this.onEnd?.();
|
|
94
|
+
this.push(null);
|
|
95
|
+
}
|
|
96
|
+
else if (this.offset <= this.end) {
|
|
97
|
+
if (this.retries < this.maxRetryRequests) {
|
|
98
|
+
this.retries += 1;
|
|
99
|
+
this.getter(this.offset)
|
|
100
|
+
.then((newSource) => {
|
|
101
|
+
this.source = newSource;
|
|
102
|
+
this.setSourceEventHandlers();
|
|
103
|
+
return;
|
|
104
|
+
})
|
|
105
|
+
.catch((error) => {
|
|
106
|
+
this.destroy(error);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.destroy(new Error(`Data corruption failure: received less data than required and reached maxRetires limitation. Received data offset: ${this.offset - 1}, data needed offset: ${this.end}, retries: ${this.retries}, max retries: ${this.maxRetryRequests}`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.destroy(new Error(`Data corruption failure: Received more data than original request, data needed offset is ${this.end}, received offset: ${this.offset - 1}`));
|
|
115
|
+
}
|
|
116
|
+
};
|
|
109
117
|
_destroy(error, callback) {
|
|
110
118
|
// remove listener from source and release source
|
|
111
119
|
this.removeSourceEventHandlers();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retriableReadableStream.js","sourceRoot":"","sources":["../../../src/utils/retriableReadableStream.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { AbortError } from \"@azure/abort-controller\";\nimport type { TransferProgressEvent } from \"@azure/core-rest-pipeline\";\nimport { Readable } from \"node:stream\";\n\nexport type ReadableStreamGetter = (offset: number) => Promise<NodeJS.ReadableStream>;\n\nexport interface RetriableReadableStreamOptions {\n /**\n * Max retry count (greater than or equal to 0), undefined or invalid value means no retry\n */\n maxRetryRequests?: number;\n\n /**\n * Read progress event handler\n */\n onProgress?: (progress: TransferProgressEvent) => void;\n\n /**\n * On data event handler\n */\n onData?: (data: Buffer) => void;\n\n /**\n * On end (success) event handler\n */\n onEnd?: () => void;\n\n /**\n * Debug purpose only. Used to inject an unexpected end to existing internal stream,\n * to test stream retry works well or not.\n *\n * When assign it to true, for next incoming \"data\" event of internal stream,\n * RetriableReadableStream will try to emit an \"end\" event to existing internal\n * stream to force it end and start retry from the breaking point.\n * The value will then update to \"undefined\", once the injection works.\n */\n doInjectErrorOnce?: boolean;\n\n /**\n * A threshold, not a limit. Dictates the amount of data that a stream buffers before it stops asking for more data.\n */\n highWaterMark?: number;\n}\n\n/**\n * ONLY AVAILABLE IN NODE.JS RUNTIME.\n *\n * A Node.js ReadableStream will internally retry when internal ReadableStream unexpected ends.\n */\nexport class RetriableReadableStream extends Readable {\n private start: number;\n private end: number;\n private retries: number = 0;\n private maxRetryRequests: number;\n private onData?: (data: Buffer) => void;\n private onEnd?: () => void;\n private onProgress?: (progress: TransferProgressEvent) => void;\n private options: RetriableReadableStreamOptions;\n\n /**\n * Creates an instance of RetriableReadableStream.\n *\n * @param source - The current ReadableStream returned from getter\n * @param getter - A method calling downloading request returning\n * a new ReadableStream from specified offset\n * @param offset - Offset position in original data source to read\n * @param count - How much data in original data source to read\n * @param options -\n */\n public constructor(\n private source: NodeJS.ReadableStream,\n private getter: ReadableStreamGetter,\n private offset: number,\n count: number,\n options: RetriableReadableStreamOptions = {},\n ) {\n super({ highWaterMark: options.highWaterMark });\n this.getter = getter;\n this.start = offset;\n this.end = offset + count - 1;\n this.maxRetryRequests =\n options.maxRetryRequests && options.maxRetryRequests >= 0 ? options.maxRetryRequests : 0;\n this.onData = options.onData;\n this.onEnd = options.onEnd;\n this.onProgress = options.onProgress;\n this.options = options;\n\n this.setSourceEventHandlers();\n }\n\n public _read(): void {\n this.source.resume();\n }\n\n private setSourceEventHandlers(): void {\n this.source.on(\"data\", this.sourceDataHandler);\n this.source.on(\"end\", this.sourceErrorOrEndHandler);\n this.source.on(\"error\", this.sourceErrorOrEndHandler);\n // needed for Node14\n this.source.on(\"aborted\", this.sourceAbortedHandler);\n }\n\n private removeSourceEventHandlers(): void {\n this.source.removeListener(\"data\", this.sourceDataHandler);\n this.source.removeListener(\"end\", this.sourceErrorOrEndHandler);\n this.source.removeListener(\"error\", this.sourceErrorOrEndHandler);\n this.source.removeListener(\"aborted\", this.sourceAbortedHandler);\n }\n\n private sourceDataHandler = (data: Buffer): void => {\n if (this.options.doInjectErrorOnce) {\n this.options.doInjectErrorOnce = undefined;\n this.source.pause();\n this.sourceErrorOrEndHandler();\n (this.source as Readable).destroy();\n return;\n }\n\n this.offset += data.length;\n\n if (this.offset > this.end + 1) {\n this.destroy(\n new Error(\n `Data corruption failure: Received more data than original request, data needed offset is ${\n this.end\n }, received offset: ${this.offset - 1}`,\n ),\n );\n }\n\n this.onData?.(data);\n this.onProgress?.({ loadedBytes: this.offset - this.start });\n\n if (!this.push(data)) {\n this.source.pause();\n }\n };\n\n private sourceAbortedHandler = (): void => {\n const abortError = new AbortError(\"The operation was aborted.\");\n this.destroy(abortError);\n };\n\n private sourceErrorOrEndHandler = (err?: Error): void => {\n if (err && err.name === \"AbortError\") {\n this.destroy(err);\n return;\n }\n\n this.removeSourceEventHandlers();\n if (this.offset - 1 === this.end) {\n this.onEnd?.();\n this.push(null);\n } else if (this.offset <= this.end) {\n if (this.retries < this.maxRetryRequests) {\n this.retries += 1;\n this.getter(this.offset)\n .then((newSource) => {\n this.source = newSource;\n this.setSourceEventHandlers();\n return;\n })\n .catch((error) => {\n this.destroy(error);\n });\n } else {\n this.destroy(\n new Error(\n `Data corruption failure: received less data than required and reached maxRetires limitation. Received data offset: ${\n this.offset - 1\n }, data needed offset: ${this.end}, retries: ${this.retries}, max retries: ${\n this.maxRetryRequests\n }`,\n ),\n );\n }\n } else {\n this.destroy(\n new Error(\n `Data corruption failure: Received more data than original request, data needed offset is ${\n this.end\n }, received offset: ${this.offset - 1}`,\n ),\n );\n }\n };\n\n _destroy(error: Error | null, callback: (error?: Error) => void): void {\n // remove listener from source and release source\n this.removeSourceEventHandlers();\n (this.source as Readable).destroy();\n\n callback(error === null ? undefined : error);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"retriableReadableStream.js","sourceRoot":"","sources":["../../../src/utils/retriableReadableStream.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { AbortError } from \"@azure/abort-controller\";\nimport type { TransferProgressEvent } from \"@azure/core-rest-pipeline\";\nimport { Readable } from \"node:stream\";\n\nexport type ReadableStreamGetter = (offset: number) => Promise<NodeJS.ReadableStream>;\n\nexport interface RetriableReadableStreamOptions {\n /**\n * Max retry count (greater than or equal to 0), undefined or invalid value means no retry\n */\n maxRetryRequests?: number;\n\n /**\n * Read progress event handler\n */\n onProgress?: (progress: TransferProgressEvent) => void;\n\n /**\n * On data event handler\n */\n onData?: (data: Buffer) => void;\n\n /**\n * On end (success) event handler\n */\n onEnd?: () => void;\n\n /**\n * Debug purpose only. Used to inject an unexpected end to existing internal stream,\n * to test stream retry works well or not.\n *\n * When assign it to true, for next incoming \"data\" event of internal stream,\n * RetriableReadableStream will try to emit an \"end\" event to existing internal\n * stream to force it end and start retry from the breaking point.\n * The value will then update to \"undefined\", once the injection works.\n */\n doInjectErrorOnce?: boolean;\n\n /**\n * A threshold, not a limit. Dictates the amount of data that a stream buffers before it stops asking for more data.\n */\n highWaterMark?: number;\n}\n\n/**\n * ONLY AVAILABLE IN NODE.JS RUNTIME.\n *\n * A Node.js ReadableStream will internally retry when internal ReadableStream unexpected ends.\n */\nexport class RetriableReadableStream extends Readable {\n private start: number;\n private end: number;\n private retries: number = 0;\n private maxRetryRequests: number;\n private onData?: (data: Buffer) => void;\n private onEnd?: () => void;\n private onProgress?: (progress: TransferProgressEvent) => void;\n private options: RetriableReadableStreamOptions;\n\n /**\n * Creates an instance of RetriableReadableStream.\n *\n * @param source - The current ReadableStream returned from getter\n * @param getter - A method calling downloading request returning\n * a new ReadableStream from specified offset\n * @param offset - Offset position in original data source to read\n * @param count - How much data in original data source to read\n * @param options -\n */\n public constructor(\n private source: NodeJS.ReadableStream,\n private getter: ReadableStreamGetter,\n private offset: number,\n count: number,\n options: RetriableReadableStreamOptions = {},\n ) {\n super({ highWaterMark: options.highWaterMark });\n this.getter = getter;\n this.start = offset;\n this.end = offset + count - 1;\n this.maxRetryRequests =\n options.maxRetryRequests && options.maxRetryRequests >= 0 ? options.maxRetryRequests : 0;\n this.onData = options.onData;\n this.onEnd = options.onEnd;\n this.onProgress = options.onProgress;\n this.options = options;\n\n this.setSourceEventHandlers();\n }\n\n public _read(): void {\n this.source.resume();\n }\n\n private setSourceEventHandlers(): void {\n this.source.on(\"data\", this.sourceDataHandler);\n this.source.on(\"end\", this.sourceErrorOrEndHandler);\n this.source.on(\"error\", this.sourceErrorOrEndHandler);\n // needed for Node14\n this.source.on(\"aborted\", this.sourceAbortedHandler);\n }\n\n private removeSourceEventHandlers(): void {\n this.source.removeListener(\"data\", this.sourceDataHandler);\n this.source.removeListener(\"end\", this.sourceErrorOrEndHandler);\n this.source.removeListener(\"error\", this.sourceErrorOrEndHandler);\n this.source.removeListener(\"aborted\", this.sourceAbortedHandler);\n }\n\n private sourceDataHandler = (data: Buffer): void => {\n if (this.options.doInjectErrorOnce) {\n this.options.doInjectErrorOnce = undefined;\n this.source.pause();\n this.sourceErrorOrEndHandler();\n (this.source as Readable).destroy();\n return;\n }\n\n this.offset += data.length;\n\n if (this.offset > this.end + 1) {\n this.destroy(\n new Error(\n `Data corruption failure: Received more data than original request, data needed offset is ${\n this.end\n }, received offset: ${this.offset - 1}`,\n ),\n );\n }\n\n this.onData?.(data);\n this.onProgress?.({ loadedBytes: this.offset - this.start });\n\n if (!this.push(data)) {\n this.source.pause();\n }\n };\n\n private sourceAbortedHandler = (): void => {\n const abortError = new AbortError(\"The operation was aborted.\");\n this.destroy(abortError);\n };\n\n private sourceErrorOrEndHandler = (err?: Error): void => {\n if (err && err.name === \"AbortError\") {\n this.destroy(err);\n return;\n }\n\n this.removeSourceEventHandlers();\n if (this.offset - 1 === this.end) {\n this.onEnd?.();\n this.push(null);\n } else if (this.offset <= this.end) {\n if (this.retries < this.maxRetryRequests) {\n this.retries += 1;\n this.getter(this.offset)\n .then((newSource) => {\n this.source = newSource;\n this.setSourceEventHandlers();\n return;\n })\n .catch((error) => {\n this.destroy(error);\n });\n } else {\n this.destroy(\n new Error(\n `Data corruption failure: received less data than required and reached maxRetires limitation. Received data offset: ${\n this.offset - 1\n }, data needed offset: ${this.end}, retries: ${this.retries}, max retries: ${\n this.maxRetryRequests\n }`,\n ),\n );\n }\n } else {\n this.destroy(\n new Error(\n `Data corruption failure: Received more data than original request, data needed offset is ${\n this.end\n }, received offset: ${this.offset - 1}`,\n ),\n );\n }\n };\n\n _destroy(error: Error | null, callback: (error?: Error) => void): void {\n // remove listener from source and release source\n this.removeSourceEventHandlers();\n (this.source as Readable).destroy();\n\n callback(error === null ? undefined : error);\n }\n}\n"]}
|
|
@@ -27,7 +27,7 @@ async function beginRefresh(getAccessToken, retryIntervalInMs, refreshTimeout) {
|
|
|
27
27
|
try {
|
|
28
28
|
return await getAccessToken();
|
|
29
29
|
}
|
|
30
|
-
catch
|
|
30
|
+
catch {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -64,7 +64,10 @@ async function beginRefresh(getAccessToken, retryIntervalInMs, refreshTimeout) {
|
|
|
64
64
|
export function createTokenCycler(credential, tokenCyclerOptions) {
|
|
65
65
|
let refreshWorker = null;
|
|
66
66
|
let token = null;
|
|
67
|
-
const options =
|
|
67
|
+
const options = {
|
|
68
|
+
...DEFAULT_CYCLER_OPTIONS,
|
|
69
|
+
...tokenCyclerOptions,
|
|
70
|
+
};
|
|
68
71
|
/**
|
|
69
72
|
* This little holder defines several predicates that we use to construct
|
|
70
73
|
* the rules of refreshing the token.
|
|
@@ -81,9 +84,8 @@ export function createTokenCycler(credential, tokenCyclerOptions) {
|
|
|
81
84
|
* window and not already refreshing)
|
|
82
85
|
*/
|
|
83
86
|
get shouldRefresh() {
|
|
84
|
-
var _a;
|
|
85
87
|
return (!cycler.isRefreshing &&
|
|
86
|
-
(
|
|
88
|
+
(token?.expiresOnTimestamp ?? 0) - options.refreshWindowInMs < Date.now());
|
|
87
89
|
},
|
|
88
90
|
/**
|
|
89
91
|
* Produces true if the cycler MUST refresh (null or nearly-expired
|
|
@@ -98,7 +100,6 @@ export function createTokenCycler(credential, tokenCyclerOptions) {
|
|
|
98
100
|
* running.
|
|
99
101
|
*/
|
|
100
102
|
function refresh(scopes, getTokenOptions) {
|
|
101
|
-
var _a;
|
|
102
103
|
if (!cycler.isRefreshing) {
|
|
103
104
|
// We bind `scopes` here to avoid passing it around a lot
|
|
104
105
|
const tryGetAccessToken = () => credential.getToken(scopes, getTokenOptions);
|
|
@@ -106,7 +107,7 @@ export function createTokenCycler(credential, tokenCyclerOptions) {
|
|
|
106
107
|
// before the refresh can be considered done.
|
|
107
108
|
refreshWorker = beginRefresh(tryGetAccessToken, options.retryIntervalInMs,
|
|
108
109
|
// If we don't have a token, then we should timeout immediately
|
|
109
|
-
|
|
110
|
+
token?.expiresOnTimestamp ?? Date.now())
|
|
110
111
|
.then((_token) => {
|
|
111
112
|
refreshWorker = null;
|
|
112
113
|
token = _token;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenCycler.js","sourceRoot":"","sources":["../../../src/utils/tokenCycler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\n\nfunction delay<T>(t: number, value?: T): Promise<T | void> {\n return new Promise((resolve) => setTimeout(() => resolve(value), t));\n}\n/**\n * A function that gets a promise of an access token and allows providing\n * options.\n *\n * @param options - the options to pass to the underlying token provider\n */\nexport type AccessTokenGetter<T extends GetTokenOptions> = (\n scopes: string | string[],\n options: T,\n) => Promise<AccessToken>;\n\n/**\n * The response of the\n */\nexport interface AccessTokenRefresher<T extends GetTokenOptions> {\n cachedToken?: AccessToken;\n getToken: AccessTokenGetter<T>;\n}\n\nexport interface TokenCyclerOptions {\n /**\n * The window of time before token expiration during which the token will be\n * considered unusable due to risk of the token expiring before sending the\n * request.\n *\n * This will only become meaningful if the refresh fails for over\n * (refreshWindow - forcedRefreshWindow) milliseconds.\n */\n forcedRefreshWindowInMs: number;\n /**\n * Interval in milliseconds to retry failed token refreshes.\n */\n retryIntervalInMs: number;\n /**\n * The window of time before token expiration during which\n * we will attempt to refresh the token.\n */\n refreshWindowInMs: number;\n}\n\n// Default options for the cycler if none are provided\nexport const DEFAULT_CYCLER_OPTIONS: TokenCyclerOptions = {\n forcedRefreshWindowInMs: 1000, // Force waiting for a refresh 1s before the token expires\n retryIntervalInMs: 3000, // Allow refresh attempts every 3s\n refreshWindowInMs: 1000 * 60 * 2, // Start refreshing 2m before expiry\n};\n\n/**\n * Converts an an unreliable access token getter (which may resolve with null)\n * into an AccessTokenGetter by retrying the unreliable getter in a regular\n * interval.\n *\n * @param getAccessToken - A function that produces a promise of an access token that may fail by returning null.\n * @param retryIntervalInMs - The time (in milliseconds) to wait between retry attempts.\n * @param refreshTimeout - The timestamp after which the refresh attempt will fail, throwing an exception.\n * @returns - A promise that, if it resolves, will resolve with an access token.\n */\nasync function beginRefresh(\n getAccessToken: () => Promise<AccessToken | null>,\n retryIntervalInMs: number,\n refreshTimeout: number,\n): Promise<AccessToken> {\n // This wrapper handles exceptions gracefully as long as we haven't exceeded\n // the timeout.\n async function tryGetAccessToken(): Promise<AccessToken | null> {\n if (Date.now() < refreshTimeout) {\n try {\n return await getAccessToken();\n } catch {\n return null;\n }\n } else {\n const finalToken = await getAccessToken();\n\n // Timeout is up, so throw if it's still null\n if (finalToken === null) {\n throw new Error(\"Failed to refresh access token.\");\n }\n\n return finalToken;\n }\n }\n\n let token: AccessToken | null = await tryGetAccessToken();\n\n while (token === null) {\n await delay(retryIntervalInMs);\n\n token = await tryGetAccessToken();\n }\n\n return token;\n}\n\n/**\n * Creates a token cycler from a credential, scopes, and optional settings.\n *\n * A token cycler represents a way to reliably retrieve a valid access token\n * from a TokenCredential. It will handle initializing the token, refreshing it\n * when it nears expiration, and synchronizes refresh attempts to avoid\n * concurrency hazards.\n *\n * @param credential - the underlying TokenCredential that provides the access\n * token\n * @param tokenCyclerOptions - optionally override default settings for the cycler\n *\n * @returns - a function that reliably produces a valid access token\n */\nexport function createTokenCycler<T extends GetTokenOptions>(\n credential: TokenCredential,\n tokenCyclerOptions?: Partial<TokenCyclerOptions>,\n): AccessTokenRefresher<T> {\n let refreshWorker: Promise<AccessToken> | null = null;\n let token: AccessToken | null = null;\n\n const options = {\n ...DEFAULT_CYCLER_OPTIONS,\n ...tokenCyclerOptions,\n };\n\n /**\n * This little holder defines several predicates that we use to construct\n * the rules of refreshing the token.\n */\n const cycler = {\n /**\n * Produces true if a refresh job is currently in progress.\n */\n get isRefreshing(): boolean {\n return refreshWorker !== null;\n },\n /**\n * Produces true if the cycler SHOULD refresh (we are within the refresh\n * window and not already refreshing)\n */\n get shouldRefresh(): boolean {\n return (\n !cycler.isRefreshing &&\n (token?.expiresOnTimestamp ?? 0) - options.refreshWindowInMs < Date.now()\n );\n },\n /**\n * Produces true if the cycler MUST refresh (null or nearly-expired\n * token).\n */\n get mustRefresh(): boolean {\n return (\n token === null || token.expiresOnTimestamp - options.forcedRefreshWindowInMs < Date.now()\n );\n },\n };\n\n /**\n * Starts a refresh job or returns the existing job if one is already\n * running.\n */\n function refresh(scopes: string | string[], getTokenOptions: T): Promise<AccessToken> {\n if (!cycler.isRefreshing) {\n // We bind `scopes` here to avoid passing it around a lot\n const tryGetAccessToken = (): Promise<AccessToken | null> =>\n credential.getToken(scopes, getTokenOptions);\n\n // Take advantage of promise chaining to insert an assignment to `token`\n // before the refresh can be considered done.\n refreshWorker = beginRefresh(\n tryGetAccessToken,\n options.retryIntervalInMs,\n // If we don't have a token, then we should timeout immediately\n token?.expiresOnTimestamp ?? Date.now(),\n )\n .then((_token) => {\n refreshWorker = null;\n token = _token;\n return token;\n })\n .catch((reason) => {\n // We also should reset the refresher if we enter a failed state. All\n // existing awaiters will throw, but subsequent requests will start a\n // new retry chain.\n refreshWorker = null;\n token = null;\n throw reason;\n });\n }\n\n return refreshWorker as Promise<AccessToken>;\n }\n\n return {\n get cachedToken(): AccessToken | undefined {\n return token || undefined;\n },\n getToken: async (scopes: string | string[], tokenOptions: T): Promise<AccessToken> => {\n //\n // Simple rules:\n // - If we MUST refresh, then return the refresh task, blocking\n // the pipeline until a token is available.\n // - If we SHOULD refresh, then run refresh but don't return it\n // (we can still use the cached token).\n // - Return the token, since it's fine if we didn't return in\n // step 1.\n //\n if (cycler.mustRefresh) return refresh(scopes, tokenOptions);\n\n if (cycler.shouldRefresh) {\n refresh(scopes, tokenOptions);\n }\n\n return token as AccessToken;\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tokenCycler.js","sourceRoot":"","sources":["../../../src/utils/tokenCycler.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\n\nfunction delay<T>(t: number, value?: T): Promise<T | void> {\n return new Promise((resolve) => setTimeout(() => resolve(value), t));\n}\n/**\n * A function that gets a promise of an access token and allows providing\n * options.\n *\n * @param options - the options to pass to the underlying token provider\n */\nexport type AccessTokenGetter<T extends GetTokenOptions> = (\n scopes: string | string[],\n options: T,\n) => Promise<AccessToken>;\n\n/**\n * The response of the\n */\nexport interface AccessTokenRefresher<T extends GetTokenOptions> {\n cachedToken?: AccessToken;\n getToken: AccessTokenGetter<T>;\n}\n\nexport interface TokenCyclerOptions {\n /**\n * The window of time before token expiration during which the token will be\n * considered unusable due to risk of the token expiring before sending the\n * request.\n *\n * This will only become meaningful if the refresh fails for over\n * (refreshWindow - forcedRefreshWindow) milliseconds.\n */\n forcedRefreshWindowInMs: number;\n /**\n * Interval in milliseconds to retry failed token refreshes.\n */\n retryIntervalInMs: number;\n /**\n * The window of time before token expiration during which\n * we will attempt to refresh the token.\n */\n refreshWindowInMs: number;\n}\n\n// Default options for the cycler if none are provided\nexport const DEFAULT_CYCLER_OPTIONS: TokenCyclerOptions = {\n forcedRefreshWindowInMs: 1000, // Force waiting for a refresh 1s before the token expires\n retryIntervalInMs: 3000, // Allow refresh attempts every 3s\n refreshWindowInMs: 1000 * 60 * 2, // Start refreshing 2m before expiry\n};\n\n/**\n * Converts an an unreliable access token getter (which may resolve with null)\n * into an AccessTokenGetter by retrying the unreliable getter in a regular\n * interval.\n *\n * @param getAccessToken - A function that produces a promise of an access token that may fail by returning null.\n * @param retryIntervalInMs - The time (in milliseconds) to wait between retry attempts.\n * @param refreshTimeout - The timestamp after which the refresh attempt will fail, throwing an exception.\n * @returns - A promise that, if it resolves, will resolve with an access token.\n */\nasync function beginRefresh(\n getAccessToken: () => Promise<AccessToken | null>,\n retryIntervalInMs: number,\n refreshTimeout: number,\n): Promise<AccessToken> {\n // This wrapper handles exceptions gracefully as long as we haven't exceeded\n // the timeout.\n async function tryGetAccessToken(): Promise<AccessToken | null> {\n if (Date.now() < refreshTimeout) {\n try {\n return await getAccessToken();\n } catch {\n return null;\n }\n } else {\n const finalToken = await getAccessToken();\n\n // Timeout is up, so throw if it's still null\n if (finalToken === null) {\n throw new Error(\"Failed to refresh access token.\");\n }\n\n return finalToken;\n }\n }\n\n let token: AccessToken | null = await tryGetAccessToken();\n\n while (token === null) {\n await delay(retryIntervalInMs);\n\n token = await tryGetAccessToken();\n }\n\n return token;\n}\n\n/**\n * Creates a token cycler from a credential, scopes, and optional settings.\n *\n * A token cycler represents a way to reliably retrieve a valid access token\n * from a TokenCredential. It will handle initializing the token, refreshing it\n * when it nears expiration, and synchronizes refresh attempts to avoid\n * concurrency hazards.\n *\n * @param credential - the underlying TokenCredential that provides the access\n * token\n * @param tokenCyclerOptions - optionally override default settings for the cycler\n *\n * @returns - a function that reliably produces a valid access token\n */\nexport function createTokenCycler<T extends GetTokenOptions>(\n credential: TokenCredential,\n tokenCyclerOptions?: Partial<TokenCyclerOptions>,\n): AccessTokenRefresher<T> {\n let refreshWorker: Promise<AccessToken> | null = null;\n let token: AccessToken | null = null;\n\n const options = {\n ...DEFAULT_CYCLER_OPTIONS,\n ...tokenCyclerOptions,\n };\n\n /**\n * This little holder defines several predicates that we use to construct\n * the rules of refreshing the token.\n */\n const cycler = {\n /**\n * Produces true if a refresh job is currently in progress.\n */\n get isRefreshing(): boolean {\n return refreshWorker !== null;\n },\n /**\n * Produces true if the cycler SHOULD refresh (we are within the refresh\n * window and not already refreshing)\n */\n get shouldRefresh(): boolean {\n return (\n !cycler.isRefreshing &&\n (token?.expiresOnTimestamp ?? 0) - options.refreshWindowInMs < Date.now()\n );\n },\n /**\n * Produces true if the cycler MUST refresh (null or nearly-expired\n * token).\n */\n get mustRefresh(): boolean {\n return (\n token === null || token.expiresOnTimestamp - options.forcedRefreshWindowInMs < Date.now()\n );\n },\n };\n\n /**\n * Starts a refresh job or returns the existing job if one is already\n * running.\n */\n function refresh(scopes: string | string[], getTokenOptions: T): Promise<AccessToken> {\n if (!cycler.isRefreshing) {\n // We bind `scopes` here to avoid passing it around a lot\n const tryGetAccessToken = (): Promise<AccessToken | null> =>\n credential.getToken(scopes, getTokenOptions);\n\n // Take advantage of promise chaining to insert an assignment to `token`\n // before the refresh can be considered done.\n refreshWorker = beginRefresh(\n tryGetAccessToken,\n options.retryIntervalInMs,\n // If we don't have a token, then we should timeout immediately\n token?.expiresOnTimestamp ?? Date.now(),\n )\n .then((_token) => {\n refreshWorker = null;\n token = _token;\n return token;\n })\n .catch((reason) => {\n // We also should reset the refresher if we enter a failed state. All\n // existing awaiters will throw, but subsequent requests will start a\n // new retry chain.\n refreshWorker = null;\n token = null;\n throw reason;\n });\n }\n\n return refreshWorker as Promise<AccessToken>;\n }\n\n return {\n get cachedToken(): AccessToken | undefined {\n return token || undefined;\n },\n getToken: async (scopes: string | string[], tokenOptions: T): Promise<AccessToken> => {\n //\n // Simple rules:\n // - If we MUST refresh, then return the refresh task, blocking\n // the pipeline until a token is available.\n // - If we SHOULD refresh, then run refresh but don't return it\n // (we can still use the cached token).\n // - Return the token, since it's fine if we didn't return in\n // step 1.\n //\n if (cycler.mustRefresh) return refresh(scopes, tokenOptions);\n\n if (cycler.shouldRefresh) {\n refresh(scopes, tokenOptions);\n }\n\n return token as AccessToken;\n },\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure/container-registry",
|
|
3
|
-
"version": "1.1.1-alpha.
|
|
3
|
+
"version": "1.1.1-alpha.20250730.1",
|
|
4
4
|
"description": "An isomorphic client library for the Azure Container Registry service.",
|
|
5
5
|
"sdk-type": "client",
|
|
6
6
|
"main": "./dist/commonjs/index.js",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"react-native"
|
|
117
117
|
],
|
|
118
118
|
"selfLink": false,
|
|
119
|
-
"project": "
|
|
119
|
+
"project": "../../../tsconfig.src.build.json"
|
|
120
120
|
},
|
|
121
121
|
"exports": {
|
|
122
122
|
"./package.json": "./package.json",
|