@azure/storage-file-share
Version:
Microsoft Azure Storage SDK for JavaScript - File
1,344 lines (1,331 loc) • 590 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var coreRestPipeline = require('@azure/core-rest-pipeline');
var coreUtil = require('@azure/core-util');
var tslib = require('tslib');
var coreAuth = require('@azure/core-auth');
var coreHttpCompat = require('@azure/core-http-compat');
var coreClient = require('@azure/core-client');
var coreXml = require('@azure/core-xml');
var logger$1 = require('@azure/logger');
var abortController = require('@azure/abort-controller');
var crypto = require('crypto');
var coreTracing = require('@azure/core-tracing');
var stream = require('stream');
var events = require('events');
var fs = require('fs');
var util = require('util');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var coreHttpCompat__namespace = /*#__PURE__*/_interopNamespaceDefault(coreHttpCompat);
var coreClient__namespace = /*#__PURE__*/_interopNamespaceDefault(coreClient);
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
var util__namespace = /*#__PURE__*/_interopNamespaceDefault(util);
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* ONLY AVAILABLE IN NODE.JS RUNTIME.
*
* This is a helper class to construct a string representing the permissions granted by an AccountSAS. Setting a value
* to true means that any SAS which uses these permissions will grant permissions for that operation. Once all the
* values are set, this should be serialized with toString and set as the permissions field on an
* {@link AccountSASSignatureValues} object. It is possible to construct the permissions string without this class, but
* the order of the permissions is particular and this class guarantees correctness.
*/
class AccountSASPermissions {
constructor() {
/**
* Permission to read resources and list queues and tables granted.
*/
this.read = false;
/**
* Permission to write resources granted.
*/
this.write = false;
/**
* Permission to delete blobs and files granted.
*/
this.delete = false;
/**
* Permission to list blob containers, blobs, shares, directories, and files granted.
*/
this.list = false;
/**
* Permission to add messages, table entities, and append to blobs granted.
*/
this.add = false;
/**
* Permission to create blobs and files granted.
*/
this.create = false;
/**
* Permissions to update messages and table entities granted.
*/
this.update = false;
/**
* Permission to get and delete messages granted.
*/
this.process = false;
}
/**
* Parse initializes the AccountSASPermissions fields from a string.
*
* @param permissions -
*/
static parse(permissions) {
const accountSASPermissions = new AccountSASPermissions();
for (const c of permissions) {
switch (c) {
case "r":
accountSASPermissions.read = true;
break;
case "w":
accountSASPermissions.write = true;
break;
case "d":
accountSASPermissions.delete = true;
break;
case "l":
accountSASPermissions.list = true;
break;
case "a":
accountSASPermissions.add = true;
break;
case "c":
accountSASPermissions.create = true;
break;
case "u":
accountSASPermissions.update = true;
break;
case "p":
accountSASPermissions.process = true;
break;
default:
throw new RangeError(`Invalid permission character: ${c}`);
}
}
return accountSASPermissions;
}
/**
* Produces the SAS permissions string for an Azure Storage account.
* Call this method to set AccountSASSignatureValues Permissions field.
*
* Using this method will guarantee the resource types are in
* an order accepted by the service.
*
* @see https://learn.microsoft.com/en-us/rest/api/storageservices/constructing-an-account-sas
*
*/
toString() {
// The order of the characters should be as specified here to ensure correctness:
// https://learn.microsoft.com/en-us/rest/api/storageservices/constructing-an-account-sas
// Use a string array instead of string concatenating += operator for performance
const permissions = [];
if (this.read) {
permissions.push("r");
}
if (this.write) {
permissions.push("w");
}
if (this.delete) {
permissions.push("d");
}
if (this.list) {
permissions.push("l");
}
if (this.add) {
permissions.push("a");
}
if (this.create) {
permissions.push("c");
}
if (this.update) {
permissions.push("u");
}
if (this.process) {
permissions.push("p");
}
return permissions.join("");
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* ONLY AVAILABLE IN NODE.JS RUNTIME.
*
* This is a helper class to construct a string representing the resources accessible by an AccountSAS. Setting a value
* to true means that any SAS which uses these permissions will grant access to that resource type. Once all the
* values are set, this should be serialized with toString and set as the resources field on an
* {@link AccountSASSignatureValues} object. It is possible to construct the resources string without this class, but
* the order of the resources is particular and this class guarantees correctness.
*/
class AccountSASResourceTypes {
constructor() {
/**
* Permission to access service level APIs granted.
*/
this.service = false;
/**
* Permission to access container level APIs (Blob Containers, Tables, Queues, File Shares) granted.
*/
this.container = false;
/**
* Permission to access object level APIs (Blobs, Table Entities, Queue Messages, Files) granted.
*/
this.object = false;
}
/**
* Creates an {@link AccountSASResourceTypes} from the specified resource types string. This method will throw an
* Error if it encounters a character that does not correspond to a valid resource type.
*
* @param resourceTypes -
*/
static parse(resourceTypes) {
const accountSASResourceTypes = new AccountSASResourceTypes();
for (const c of resourceTypes) {
switch (c) {
case "s":
accountSASResourceTypes.service = true;
break;
case "c":
accountSASResourceTypes.container = true;
break;
case "o":
accountSASResourceTypes.object = true;
break;
default:
throw new RangeError(`Invalid resource type: ${c}`);
}
}
return accountSASResourceTypes;
}
/**
* Converts the given resource types to a string.
*
* @see https://learn.microsoft.com/en-us/rest/api/storageservices/constructing-an-account-sas
*
*/
toString() {
const resourceTypes = [];
if (this.service) {
resourceTypes.push("s");
}
if (this.container) {
resourceTypes.push("c");
}
if (this.object) {
resourceTypes.push("o");
}
return resourceTypes.join("");
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* ONLY AVAILABLE IN NODE.JS RUNTIME.
*
* This is a helper class to construct a string representing the services accessible by an AccountSAS. Setting a value
* to true means that any SAS which uses these permissions will grant access to that service. Once all the
* values are set, this should be serialized with toString and set as the services field on an
* {@link AccountSASSignatureValues} object. It is possible to construct the services string without this class, but
* the order of the services is particular and this class guarantees correctness.
*/
class AccountSASServices {
constructor() {
/**
* Permission to access blob resources granted.
*/
this.blob = false;
/**
* Permission to access file resources granted.
*/
this.file = false;
/**
* Permission to access queue resources granted.
*/
this.queue = false;
/**
* Permission to access table resources granted.
*/
this.table = false;
}
/**
* Creates an {@link AccountSASServices} from the specified services string. This method will throw an
* Error if it encounters a character that does not correspond to a valid service.
*
* @param services -
*/
static parse(services) {
const accountSASServices = new AccountSASServices();
for (const c of services) {
switch (c) {
case "b":
accountSASServices.blob = true;
break;
case "f":
accountSASServices.file = true;
break;
case "q":
accountSASServices.queue = true;
break;
case "t":
accountSASServices.table = true;
break;
default:
throw new RangeError(`Invalid service character: ${c}`);
}
}
return accountSASServices;
}
/**
* Converts the given services to a string.
*
*/
toString() {
const services = [];
if (this.blob) {
services.push("b");
}
if (this.table) {
services.push("t");
}
if (this.queue) {
services.push("q");
}
if (this.file) {
services.push("f");
}
return services.join("");
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Generate SasIPRange format string. For example:
*
* "8.8.8.8" or "1.1.1.1-255.255.255.255"
*
* @param ipRange - A range of IP addresses.
* @returns string representation of the IP range.
*/
function ipRangeToString(ipRange) {
return ipRange.end ? `${ipRange.start}-${ipRange.end}` : ipRange.start;
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const SDK_VERSION = "12.27.0";
const SERVICE_VERSION = "2025-05-05";
const FILE_MAX_SIZE_BYTES = 4 * 1024 * 1024 * 1024 * 1024; // 4TB
const FILE_RANGE_MAX_SIZE_BYTES = 4 * 1024 * 1024; // 4MB
const DEFAULT_MAX_DOWNLOAD_RETRY_REQUESTS = 5;
const DEFAULT_HIGH_LEVEL_CONCURRENCY = 5;
const REQUEST_TIMEOUT = 100 * 1000; // In ms
/**
* The OAuth scope to use with Azure Storage.
*/
const StorageOAuthScopes = "https://storage.azure.com/.default";
const URLConstants$1 = {
Parameters: {
SHARE_SNAPSHOT: "sharesnapshot",
TIMEOUT: "timeout",
},
};
const StorageFileLoggingAllowedHeaderNames = [
"Access-Control-Allow-Origin",
"Cache-Control",
"Content-Length",
"Content-Type",
"Date",
"Request-Id",
"traceparent",
"Transfer-Encoding",
"User-Agent",
"x-ms-client-request-id",
"x-ms-date",
"x-ms-error-code",
"x-ms-request-id",
"x-ms-return-client-request-id",
"x-ms-version",
"Accept-Ranges",
"Content-Disposition",
"Content-Encoding",
"Content-Language",
"Content-MD5",
"Content-Range",
"ETag",
"Last-Modified",
"Server",
"Vary",
"x-ms-content-crc64",
"x-ms-copy-action",
"x-ms-copy-completion-time",
"x-ms-copy-id",
"x-ms-copy-progress",
"x-ms-copy-status",
"x-ms-has-immutability-policy",
"x-ms-has-legal-hold",
"x-ms-lease-state",
"x-ms-lease-status",
"x-ms-range",
"x-ms-request-server-encrypted",
"x-ms-server-encrypted",
"x-ms-snapshot",
"x-ms-source-range",
"x-ms-cache-control",
"x-ms-content-disposition",
"x-ms-content-encoding",
"x-ms-content-language",
"x-ms-content-length",
"x-ms-content-md5",
"x-ms-content-type",
"x-ms-copy-source-error-code",
"x-ms-copy-source-status-code",
"x-ms-file-attributes",
"x-ms-file-change-time",
"x-ms-file-creation-time",
"x-ms-file-id",
"x-ms-file-last-write-time",
"x-ms-file-parent-id",
"x-ms-handle-id",
"x-ms-number-of-handles-closed",
"x-ms-recursive",
"x-ms-share-quota",
"x-ms-type",
"x-ms-write",
];
const StorageFileLoggingAllowedQueryParameters = [
"comp",
"maxresults",
"rscc",
"rscd",
"rsce",
"rscl",
"rsct",
"se",
"si",
"sip",
"sp",
"spr",
"sr",
"srt",
"ss",
"st",
"sv",
"copyid",
"restype",
];
/// List of ports used for path style addressing.
/// Path style addressing means that storage account is put in URI's Path segment in instead of in host.
const PathStylePorts = [
"10000",
"10001",
"10002",
"10003",
"10004",
"10100",
"10101",
"10102",
"10103",
"10104",
"11000",
"11001",
"11002",
"11003",
"11004",
"11100",
"11101",
"11102",
"11103",
"11104",
];
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Reserved URL characters must be properly escaped for Storage services like Blob or File.
*
* ## URL encode and escape strategy for JS SDKs
*
* When customers pass a URL string into XXXClient classes constructor, the URL string may already be URL encoded or not.
* But before sending to Azure Storage server, the URL must be encoded. However, it's hard for a SDK to guess whether the URL
* string has been encoded or not. We have 2 potential strategies, and chose strategy two for the XXXClient constructors.
*
* ### Strategy One: Assume the customer URL string is not encoded, and always encode URL string in SDK.
*
* This is what legacy V2 SDK does, simple and works for most of the cases.
* - When customer URL string is "http://account.blob.core.windows.net/con/b:",
* SDK will encode it to "http://account.blob.core.windows.net/con/b%3A" and send to server. A blob named "b:" will be created.
* - When customer URL string is "http://account.blob.core.windows.net/con/b%3A",
* SDK will encode it to "http://account.blob.core.windows.net/con/b%253A" and send to server. A blob named "b%3A" will be created.
*
* But this strategy will make it not possible to create a blob with "?" in it's name. Because when customer URL string is
* "http://account.blob.core.windows.net/con/blob?name", the "?name" will be treated as URL paramter instead of blob name.
* If customer URL string is "http://account.blob.core.windows.net/con/blob%3Fname", a blob named "blob%3Fname" will be created.
* V2 SDK doesn't have this issue because it doesn't allow customer pass in a full URL, it accepts a separate blob name and encodeURIComponent for it.
* We cannot accept a SDK cannot create a blob name with "?". So we implement strategy two:
*
* ### Strategy Two: SDK doesn't assume the URL has been encoded or not. It will just escape the special characters.
*
* This is what V10 Blob Go SDK does. It accepts a URL type in Go, and call url.EscapedPath() to escape the special chars unescaped.
* - When customer URL string is "http://account.blob.core.windows.net/con/b:",
* SDK will escape ":" like "http://account.blob.core.windows.net/con/b%3A" and send to server. A blob named "b:" will be created.
* - When customer URL string is "http://account.blob.core.windows.net/con/b%3A",
* There is no special characters, so send "http://account.blob.core.windows.net/con/b%3A" to server. A blob named "b:" will be created.
* - When customer URL string is "http://account.blob.core.windows.net/con/b%253A",
* There is no special characters, so send "http://account.blob.core.windows.net/con/b%253A" to server. A blob named "b%3A" will be created.
*
* This strategy gives us flexibility to create with any special characters. But "%" will be treated as a special characters, if the URL string
* is not encoded, there shouldn't a "%" in the URL string, otherwise the URL is not a valid URL.
* If customer needs to create a blob with "%" in it's blob name, use "%25" insead of "%". Just like above 3rd sample.
* And following URL strings are invalid:
* - "http://account.blob.core.windows.net/con/b%"
* - "http://account.blob.core.windows.net/con/b%2"
* - "http://account.blob.core.windows.net/con/b%G"
*
* Another special character is "?", use "%2F" to represent a blob name with "?" in a URL string.
*
* ### Strategy for containerName, blobName or other specific XXXName parameters in methods such as `ContainerClient.getBlobClient(blobName)`
*
* We will apply strategy one, and call encodeURIComponent for these parameters like blobName. Because what customers passes in is a plain name instead of a URL.
*
* @see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
* @see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata
*
* @param url -
*/
function escapeURLPath(url) {
const urlParsed = new URL(url);
let path = urlParsed.pathname;
path = path || "/";
path = escape(path);
urlParsed.pathname = path;
return urlParsed.toString();
}
function getValueInConnString(connectionString, argument) {
const elements = connectionString.split(";");
for (const element of elements) {
if (element.trim().startsWith(argument)) {
return element.trim().match(argument + "=(.*)")[1];
}
}
return "";
}
/**
* Extracts the parts of an Azure Storage account connection string.
*
* @param connectionString - Connection string.
* @returns String key value pairs of the storage account's url and credentials.
*/
function extractConnectionStringParts(connectionString) {
// Matching FileEndpoint in the Account connection string
let fileEndpoint = getValueInConnString(connectionString, "FileEndpoint");
// Slicing off '/' at the end if exists
// (The methods that use `extractConnectionStringParts` expect the url to not have `/` at the end)
fileEndpoint = fileEndpoint.endsWith("/") ? fileEndpoint.slice(0, -1) : fileEndpoint;
if (connectionString.search("DefaultEndpointsProtocol=") !== -1 &&
connectionString.search("AccountKey=") !== -1) {
// Account connection string
let defaultEndpointsProtocol = "";
let accountName = "";
let accountKey = Buffer.from("accountKey", "base64");
let endpointSuffix = "";
// Get account name and key
accountName = getValueInConnString(connectionString, "AccountName");
accountKey = Buffer.from(getValueInConnString(connectionString, "AccountKey"), "base64");
if (!fileEndpoint) {
// FileEndpoint is not present in the Account connection string
// Can be obtained from `${defaultEndpointsProtocol}://${accountName}.file.${endpointSuffix}`
defaultEndpointsProtocol = getValueInConnString(connectionString, "DefaultEndpointsProtocol");
const protocol = defaultEndpointsProtocol.toLowerCase();
if (protocol !== "https" && protocol !== "http") {
throw new Error("Invalid DefaultEndpointsProtocol in the provided Connection String. Expecting 'https' or 'http'");
}
endpointSuffix = getValueInConnString(connectionString, "EndpointSuffix");
if (!endpointSuffix) {
throw new Error("Invalid EndpointSuffix in the provided Connection String");
}
fileEndpoint = `${defaultEndpointsProtocol}://${accountName}.file.${endpointSuffix}`;
}
if (!accountName) {
throw new Error("Invalid AccountName in the provided Connection String");
}
else if (accountKey.length === 0) {
throw new Error("Invalid AccountKey in the provided Connection String");
}
return {
kind: "AccountConnString",
url: fileEndpoint,
accountName,
accountKey,
};
}
else {
// SAS connection string
const accountSas = getValueInConnString(connectionString, "SharedAccessSignature");
let accountName = getValueInConnString(connectionString, "AccountName");
// if accountName is empty, try to read it from BlobEndpoint
if (!accountName) {
accountName = getAccountNameFromUrl(fileEndpoint);
}
if (!fileEndpoint) {
throw new Error("Invalid FileEndpoint in the provided SAS Connection String");
}
else if (!accountSas) {
throw new Error("Invalid SharedAccessSignature in the provided SAS Connection String");
}
return { kind: "SASConnString", url: fileEndpoint, accountName, accountSas };
}
}
/**
* Internal escape method implemented Strategy Two mentioned in escapeURL() description.
*
* @param text -
*/
function escape(text) {
return encodeURIComponent(text)
.replace(/%2F/g, "/") // Don't escape for "/"
.replace(/'/g, "%27") // Escape for "'"
.replace(/\+/g, "%20")
.replace(/%25/g, "%"); // Revert encoded "%"
}
/**
* Append a string to URL path. Will remove duplicated "/" in front of the string
* when URL path ends with a "/".
*
* @param url - Source URL string
* @param name - String to be appended to URL
* @returns An updated URL string
*/
function appendToURLPath(url, name) {
const urlParsed = new URL(url);
let path = urlParsed.pathname;
path = path ? (path.endsWith("/") ? `${path}${name}` : `${path}/${name}`) : name;
urlParsed.pathname = path;
return urlParsed.toString();
}
/**
* Append a string to URL query.
*
* @param url - Source URL string.
* @param queryParts - String to be appended to the URL query.
* @returns An updated URL string.
*/
function appendToURLQuery(url, queryParts) {
const urlParsed = new URL(url);
let query = urlParsed.search;
if (query) {
query += "&" + queryParts;
}
else {
query = queryParts;
}
urlParsed.search = query;
return urlParsed.toString();
}
/**
* Set URL parameter name and value. If name exists in URL parameters, old value
* will be replaced by name key. If not provide value, the parameter will be deleted.
*
* @param url - Source URL string
* @param name - Parameter name
* @param value - Parameter value
* @returns An updated URL string
*/
function setURLParameter$1(url, name, value) {
const urlParsed = new URL(url);
const encodedName = encodeURIComponent(name);
const encodedValue = value ? encodeURIComponent(value) : undefined;
// mutating searchParams will change the encoding, so we have to do this ourselves
const searchString = urlParsed.search === "" ? "?" : urlParsed.search;
const searchPieces = [];
for (const pair of searchString.slice(1).split("&")) {
if (pair) {
const [key] = pair.split("=", 2);
if (key !== encodedName) {
searchPieces.push(pair);
}
}
}
if (encodedValue) {
searchPieces.push(`${encodedName}=${encodedValue}`);
}
urlParsed.search = searchPieces.length ? `?${searchPieces.join("&")}` : "";
return urlParsed.toString();
}
/**
* Rounds a date off to seconds.
*
* @param date -
* @param withMilliseconds - If true, YYYY-MM-DDThh:mm:ss.fffffffZ will be returned;
* If false, YYYY-MM-DDThh:mm:ssZ will be returned.
* @returns Date string in ISO8061 format, with or without 7 milliseconds component
*/
function truncatedISO8061Date(date, withMilliseconds = true) {
// Date.toISOString() will return like "2018-10-29T06:34:36.139Z"
const dateString = date.toISOString();
return withMilliseconds
? dateString.substring(0, dateString.length - 1) + "0000" + "Z"
: dateString.substring(0, dateString.length - 5) + "Z";
}
/**
* Delay specified time interval.
*
* @param timeInMs -
* @param aborter -
* @param abortError -
*/
async function delay(timeInMs, aborter, abortError) {
return new Promise((resolve, reject) => {
/* eslint-disable-next-line prefer-const */
let timeout;
const abortHandler = () => {
if (timeout !== undefined) {
clearTimeout(timeout);
}
reject(abortError);
};
const resolveHandler = () => {
if (aborter !== undefined) {
aborter.removeEventListener("abort", abortHandler);
}
resolve();
};
timeout = setTimeout(resolveHandler, timeInMs);
if (aborter !== undefined) {
aborter.addEventListener("abort", abortHandler);
}
});
}
/**
* Extracts account name from the url
* @param url - url to extract the account name from
* @returns with the account name
*/
function getAccountNameFromUrl(url) {
const parsedUrl = new URL(url);
let accountName;
try {
if (parsedUrl.hostname.split(".")[1] === "file") {
// `${defaultEndpointsProtocol}://${accountName}.file.${endpointSuffix}`;
// Slicing off '/' at the end if exists
url = url.endsWith("/") ? url.slice(0, -1) : url;
accountName = parsedUrl.hostname.split(".")[0];
}
else if (isIpEndpointStyle(parsedUrl)) {
// IPv4/IPv6 address hosts... Example - http://192.0.0.10:10001/devstoreaccount1/
// Single word domain without a [dot] in the endpoint... Example - http://localhost:10001/devstoreaccount1/
// .getPath() -> /devstoreaccount1/
accountName = parsedUrl.pathname.split("/")[1];
}
else {
// Custom domain case: "https://customdomain.com/containername/blob".
accountName = "";
}
return accountName;
}
catch (error) {
throw new Error("Unable to extract accountName with provided information.");
}
}
function isIpEndpointStyle(parsedUrl) {
const host = parsedUrl.host;
// Case 1: Ipv6, use a broad regex to find out candidates whose host contains two ':'.
// Case 2: localhost(:port) or host.docker.internal, use broad regex to match port part.
// Case 3: Ipv4, use broad regex which just check if host contains Ipv4.
// For valid host please refer to https://man7.org/linux/man-pages/man7/hostname.7.html.
return (/^.*:.*:.*$|^(localhost|host.docker.internal)(:[0-9]+)?$|^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])){3}(:[0-9]+)?$/.test(host) ||
(Boolean(parsedUrl.port) && PathStylePorts.includes(parsedUrl.port)));
}
function getShareNameAndPathFromUrl(url) {
// URL may look like the following
// "https://myaccount.file.core.windows.net/myshare/mydirectory/file?sasString";
// "https://myaccount.file.core.windows.net/myshare/mydirectory/file";
// "https://myaccount.file.core.windows.net/myshare/mydirectory?sasString";
// "https://myaccount.file.core.windows.net/myshare/mydirectory";
// "https://myaccount.file.core.windows.net/myshare?sasString";
// "https://myaccount.file.core.windows.net/myshare";
// IPv4/IPv6 address hosts, Endpoints - `http://187.24.0.1:1000/devstoreaccount1/mydirectory/file`
// http://localhost:1000/devstoreaccount1/mydirectory/file
// mydirectory can consist of multiple directories - dir1/dir2/dir3
let shareName;
let path;
let baseName;
try {
const parsedUrl = new URL(url);
if (parsedUrl.hostname.split(".")[1] === "file") {
// "https://myaccount.file.core.windows.net/myshare/mydirectory/file";
// .getPath() -> /myshare/mydirectory/file
const pathComponents = parsedUrl.pathname.match("/([^/]*)(/(.*))?");
shareName = pathComponents[1];
path = pathComponents[3];
}
else if (isIpEndpointStyle(parsedUrl)) {
// IPv4/IPv6 address hosts... Example - http://187.24.0.1:1000/devstoreaccount1/mydirectory/file
// Single word domain without a [dot] in the endpoint... Example - http://localhost:1000/devstoreaccount1/mydirectory/file
// .getPath() -> /devstoreaccount1/mydirectory/file
const pathComponents = parsedUrl.pathname.match("/([^/]*)/([^/]*)(/(.*))?");
shareName = pathComponents[2];
path = pathComponents[4];
}
else {
// "https://customdomain.com/myshare/mydirectory/file";
// .getPath() -> /myshare/mydirectory/file
const pathComponents = parsedUrl.pathname.match("/([^/]*)(/(.*))?");
shareName = pathComponents[1];
path = pathComponents[3];
}
// decode the encoded shareName and filePath - to get all the special characters that might be present in it
shareName = decodeURIComponent(shareName);
path = decodeURIComponent(path);
// Cast to string is required as TypeScript cannot infer that split() always returns
// an array with length >= 1
baseName = path.split("/").pop();
if (!shareName) {
throw new Error("Provided shareName is invalid.");
}
else {
return { baseName, shareName, path };
}
}
catch (error) {
throw new Error("Unable to extract shareName and filePath/directoryPath with provided information.");
}
}
function httpAuthorizationToString(httpAuthorization) {
return httpAuthorization ? httpAuthorization.scheme + " " + httpAuthorization.value : undefined;
}
/**
* Set URL path.
*
* @param url - URL to change path to.
* @param path - Path to set into the URL.
*/
function setURLPath(url, path) {
const urlParsed = new URL(url);
urlParsed.pathname = path;
return urlParsed.toString();
}
/**
* Set URL query string.
*
* @param url - URL to set query string to.
* @param queryString - Query string to set to the URL.
*/
function setURLQueries(url, queryString) {
const urlParsed = new URL(url);
urlParsed.search = queryString;
return urlParsed.toString();
}
/**
* Escape the file or directory name but keep path separator ('/').
*/
function EscapePath(pathName) {
const split = pathName.split("/");
for (let i = 0; i < split.length; i++) {
split[i] = encodeURIComponent(split[i]);
}
return split.join("/");
}
/**
* A typesafe helper for ensuring that a given response object has
* the original _response attached.
* @param response - A response object from calling a client operation
* @returns The same object, but with known _response property
*/
function assertResponse(response) {
if (`_response` in response) {
return response;
}
throw new TypeError(`Unexpected response object ${response}`);
}
function StringEncodedToString(name) {
if (name.encoded) {
return decodeURIComponent(name.content);
}
else {
return name.content;
}
}
function ConvertInternalResponseOfListFiles(internalResponse) {
const wrappedResponse = Object.assign(Object.assign({}, internalResponse), { prefix: undefined, directoryPath: StringEncodedToString({
encoded: internalResponse.encoded,
content: internalResponse.directoryPath,
}), segment: {
fileItems: internalResponse.segment.fileItems.map((fileItemInternal) => {
const fileItem = Object.assign(Object.assign({}, fileItemInternal), { name: StringEncodedToString(fileItemInternal.name) });
return fileItem;
}),
directoryItems: internalResponse.segment.directoryItems.map((directoryItemInternal) => {
const directoryItem = Object.assign(Object.assign({}, directoryItemInternal), { name: StringEncodedToString(directoryItemInternal.name) });
return directoryItem;
}),
} });
delete wrappedResponse.encoded;
const listResponse = wrappedResponse;
if (internalResponse.prefix) {
listResponse.prefix = StringEncodedToString(internalResponse.prefix);
}
return listResponse;
}
function ConvertInternalResponseOfListHandles(internalResponse) {
const wrappedResponse = Object.assign(Object.assign({}, internalResponse), { handleList: internalResponse.handleList
? internalResponse.handleList.map((handleItemInternal) => {
const handleItem = Object.assign(Object.assign({}, handleItemInternal), { path: StringEncodedToString(handleItemInternal.path) });
return handleItem;
})
: undefined });
return wrappedResponse;
}
/**
* A small helper to handle converting an empty string "" into undefined
* This is used in the case of query parameters (like continuation token) where
* we don't want to send an empty query parameter to the service since the signing
* policy for shared key will fail.
* @internal
*/
function removeEmptyString(value) {
return value ? value : undefined;
}
function asSharePermission(value) {
const castSharePermission = value;
if (castSharePermission["permission"] !== undefined) {
return {
permission: castSharePermission.permission,
format: castSharePermission.format,
};
}
return {
permission: value,
};
}
/**
* Parse 4-digit octal string representation of a File Mode to a {@link NfsFileMode} structure.
*/
function parseOctalFileMode(input) {
if (input === undefined) {
return undefined;
}
if ((input === null || input === void 0 ? void 0 : input.length) !== 4) {
throw new Error("Invalid format of input string");
}
const nfsFileMode = {
owner: parseOctalRolePermissions(input[1]),
group: parseOctalRolePermissions(input[2]),
other: parseOctalRolePermissions(input[3]),
effectiveUserIdentity: false,
effectiveGroupIdentity: false,
stickyBit: false,
};
const value = Number.parseInt(input[0]);
if ((value & 4) > 0) {
nfsFileMode.effectiveUserIdentity = true;
}
if ((value & 2) > 0) {
nfsFileMode.effectiveGroupIdentity = true;
}
if ((value & 1) > 0) {
nfsFileMode.stickyBit = true;
}
return nfsFileMode;
}
/**
* Convert {@link NfsFileMode} structure to a 4-digit octal string represenation.
*/
function toOctalFileMode(input) {
if (input === undefined)
return undefined;
let higherOrderDigit = 0;
if (input === null || input === void 0 ? void 0 : input.effectiveUserIdentity) {
higherOrderDigit |= 4;
}
if (input === null || input === void 0 ? void 0 : input.effectiveGroupIdentity) {
higherOrderDigit |= 2;
}
if (input === null || input === void 0 ? void 0 : input.stickyBit) {
higherOrderDigit |= 1;
}
let stringFileMode = higherOrderDigit.toString();
stringFileMode += toOctalRolePermissions(input.owner);
stringFileMode += toOctalRolePermissions(input.group);
stringFileMode += toOctalRolePermissions(input.other);
return stringFileMode;
}
/**
* Convert a {@link NfsFileMode} to a string in symbolic notation.
*/
function toSymbolicFileMode(input) {
if (input === undefined)
return undefined;
let ownerPermissions = toSymbolicRolePermissions(input.owner);
let groupPermissions = toSymbolicRolePermissions(input.group);
let otherPermissions = toSymbolicRolePermissions(input.other);
if (input.effectiveUserIdentity) {
if (ownerPermissions[2] === "x") {
ownerPermissions = ownerPermissions.substring(0, 2) + "s";
}
else {
ownerPermissions = ownerPermissions.substring(0, 2) + "S";
}
}
if (input.effectiveGroupIdentity) {
if (groupPermissions[2] === "x") {
groupPermissions = groupPermissions.substring(0, 2) + "s";
}
else {
groupPermissions = groupPermissions.substring(0, 2) + "S";
}
}
if (input.stickyBit) {
if (otherPermissions[2] === "x") {
otherPermissions = otherPermissions.substring(0, 2) + "t";
}
else {
otherPermissions = otherPermissions.substring(0, 2) + "T";
}
}
return ownerPermissions + groupPermissions + otherPermissions;
}
/**
* Parse a 9-character symbolic string representation of a File Mode to a {@link NfsFileMode} structure.
*/
function parseSymbolicFileMode(input) {
if (input === undefined)
return undefined;
if ((input === null || input === void 0 ? void 0 : input.length) !== 9) {
throw new Error("Invalid format of input string");
}
const ownerPermissions = parseSymbolicRolePermissions(input.substring(0, 3));
const groupPermissions = parseSymbolicRolePermissions(input.substring(3, 6));
const otherPermissions = parseSymbolicRolePermissions(input.substring(6, 9));
const nfsFileMode = {
owner: ownerPermissions.rolePermissions,
group: groupPermissions.rolePermissions,
other: otherPermissions.rolePermissions,
effectiveUserIdentity: ownerPermissions.setSticky,
effectiveGroupIdentity: groupPermissions.setSticky,
stickyBit: otherPermissions.setSticky,
};
return nfsFileMode;
}
function parseOctalRolePermissions(c) {
const rolePermissions = {
read: false,
write: false,
execute: false,
};
const value = Number.parseInt(c);
if (value < 0 || value > 7) {
throw new Error("MustBeBetweenInclusive");
}
if ((value & 4) > 0) {
rolePermissions.read = true;
}
if ((value & 2) > 0) {
rolePermissions.write = true;
}
if ((value & 1) > 0) {
rolePermissions.execute = true;
}
return rolePermissions;
}
function toOctalRolePermissions(rolePermissions) {
let result = 0;
if (rolePermissions.read === true) {
result |= 4;
}
if (rolePermissions.write === true) {
result |= 2;
}
if (rolePermissions.execute === true) {
result |= 1;
}
return result.toString();
}
function toSymbolicRolePermissions(rolePermissions) {
let symbolicRolePermissions = "";
if (rolePermissions.read === true) {
symbolicRolePermissions += "r";
}
else {
symbolicRolePermissions += "-";
}
if (rolePermissions.write === true) {
symbolicRolePermissions += "w";
}
else {
symbolicRolePermissions += "-";
}
if (rolePermissions.execute === true) {
symbolicRolePermissions += "x";
}
else {
symbolicRolePermissions += "-";
}
return symbolicRolePermissions;
}
function parseSymbolicRolePermissions(input) {
if (input.length !== 3) {
throw new Error("input must be 3 characters long");
}
const rolePermissions = {
read: false,
write: false,
execute: false,
};
let setSticky = false;
// Read character
if (input[0] === "r") {
rolePermissions.read = true;
}
else if (input[0] !== "-") {
throw new Error(`Invalid character in symbolic role permission: ${input[0]}`);
}
// Write character
if (input[1] === "w") {
rolePermissions.write = true;
}
else if (input[1] !== "-") {
throw new Error(`Invalid character in symbolic role permission: ${input[1]}`);
}
// Execute character
if (input[2] === "x" || input[2] === "s" || input[2] === "t") {
rolePermissions.execute = true;
if (input[2] === "s" || input[2] === "t") {
setSticky = true;
}
}
if (input[2] === "S" || input[2] === "T") {
setSticky = true;
}
if (input[2] !== "x" &&
input[2] !== "s" &&
input[2] !== "S" &&
input[2] !== "t" &&
input[2] !== "T" &&
input[2] !== "-") {
throw new Error(`Invalid character in symbolic role permission: ${input[2]}`);
}
return {
rolePermissions,
setSticky,
};
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Protocols for generated SAS.
*/
exports.SASProtocol = void 0;
(function (SASProtocol) {
/**
* Protocol that allows HTTPS only
*/
SASProtocol["Https"] = "https";
/**
* Protocol that allows both HTTPS and HTTP
*/
SASProtocol["HttpsAndHttp"] = "https,http";
})(exports.SASProtocol || (exports.SASProtocol = {}));
/**
* Represents the components that make up an Azure Storage SAS' query parameters. This type is not constructed directly
* by the user; it is only generated by the {@link AccountSASSignatureValues} and {@link FileSASSignatureValues}
* types. Once generated, it can be encoded into a {@link String} and appended to a URL directly (though caution should
* be taken here in case there are existing query parameters, which might affect the appropriate means of appending
* these query parameters).
*
* NOTE: Instances of this class are immutable.
*/
class SASQueryParameters {
/**
* Optional. IP range allowed for this SAS.
*
* @readonly
*/
get ipRange() {
if (this.ipRangeInner) {
return {
end: this.ipRangeInner.end,
start: this.ipRangeInner.start,
};
}
return undefined;
}
/**
* Creates an instance of SASQueryParameters.
*
* @param version - Representing the storage version
* @param signature - Representing the signature for the SAS token
* @param permissions - Representing the storage permissions
* @param services - Representing the storage services being accessed (only for Account SAS)
* @param resourceTypes - Representing the storage resource types being accessed (only for Account SAS)
* @param protocol - Representing the allowed HTTP protocol(s)
* @param startsOn - Representing the start time for this SAS token
* @param expiresOn - Representing the expiry time for this SAS token
* @param ipRange - Representing the range of valid IP addresses for this SAS token
* @param identifier - Representing the signed identifier (only for Service SAS)
* @param resource - Representing the storage container or blob (only for Service SAS)
* @param cacheControl - Representing the cache-control header (only for Blob/File Service SAS)
* @param contentDisposition - Representing the content-disposition header (only for Blob/File Service SAS)
* @param contentEncoding - Representing the content-encoding header (only for Blob/File Service SAS)
* @param contentLanguage - Representing the content-language header (only for Blob/File Service SAS)
* @param contentType - Representing the content-type header (only for Blob/File Service SAS)
*/
constructor(version, signature, permissions, services, resourceTypes, protocol, startsOn, expiresOn, ipRange, identifier, resource, cacheControl, contentDisposition, contentEncoding, contentLanguage, contentType) {
this.version = version;
this.services = services;
this.resourceTypes = resourceTypes;
this.expiresOn = expiresOn;
this.permissions = permissions;
this.protocol = protocol;
this.startsOn = startsOn;
this.ipRangeInner = ipRange;
this.identifier = identifier;
this.resource = resource;
this.signature = signature;
this.cacheControl = cacheControl;
this.contentDisposition = contentDisposition;
this.contentEncoding = contentEncoding;
this.contentLanguage = contentLanguage;
this.contentType = contentType;
}
/**
* Encodes all SAS query parameters into a string that can be appended to a URL.
*
*/
toString() {
const params = [
"sv",
"ss",
"srt",
"spr",
"st",
"se",
"sip",
"si",
"sr",
"sp",
"sig",
"rscc",
"rscd",
"rsce",
"rscl",
"rsct",
];
const queries = [];
for (const param of params) {
switch (param) {
case "sv":
this.tryAppendQueryParameter(queries, param, this.version);
break;
case "ss":
this.tryAppendQueryParameter(queries, param, this.services);
break;
case "srt":
this.tryAppendQueryParameter(queries, param, this.resourceTypes);
break;
case "spr":
this.tryAppendQueryParameter(queries, param, this.protocol);
break;
case "st":
this.tryAppendQueryParameter(queries, param, this.startsOn ? truncatedISO8061Date(this.startsOn, false) : undefined);
break;
case "se":
this.tryAppendQueryParameter(queries, param, this.expiresOn ? truncatedISO8061Date(this.expiresOn, false) : undefined);
break;
case "sip":
this.tryAppendQueryParameter(queries, param, this.ipRange ? ipRangeToString(this.ipRange) : undefined);
break;
case "si":
this.tryAppendQueryParameter(queries, param, this.identifier);
break;
case "sr":
this.tryAppendQueryParameter(queries, param, this.resource);
break;
case "sp":
this.tryAppendQueryParameter(queries, param, this.permissions);
break;
case "sig":
this.tryAppendQueryParameter(queries, param, this.signature);
break;
case "rscc":
this.tryAppendQueryParameter(queries, param, this.cacheControl);
break;
case "rscd":
this.tryAppendQueryParameter(queries, param, this.contentDisposition);
break;
case "rsce":
this.tryAppendQueryParameter(queries, param, this.contentEncoding);
break;
case "rscl":
this.tryAppendQueryParameter(queries, param, this.contentLanguage);
break;
case "rsct":
this.tryAppendQueryParameter(queries, param, this.contentType);
break;
}
}
return queries.join("&");
}
/**
* A private helper method used to filter and append query key/value pairs into an array.
*
* @param queries -
* @param key -
* @param value -
*/
tryAppendQueryParameter(queries, key, value) {
if (!value) {
return;
}
key = encodeURIComponent(key);
value = encodeURIComponent(value);
if (key.length > 0 && value.length > 0) {
queries.push(`${key}=${value}`);
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* ONLY AVAILABLE IN NODE.JS RUNTIME.
*
* Generates a {@link SASQueryParameters} object which contains all SAS query parameters needed to make an actual
* REST request.
*
* @see https://learn.microsoft.com/en-us/rest/api/storageservices/constructing-an-account-sas
*
* @param sharedKeyCredential -
*/
function generateAccountSASQueryParameters(accountSASSignatureValues, sharedKeyCredential) {
return generateAccountSASQueryParametersInternal(accountSASSignatureValues, sharedKeyCredential)
.sasQueryParameters;
}
function generateAccountSASQueryParametersInternal(accountSASSignatureValues, sharedKeyCredential) {
const version = accountSASSignatureValues.version
? accountSASSignatureValues.version
: SERVICE_VERSION;
const parsedPermissions = AccountSASPermissions.parse(accountSASSignatureValues.permissions.toString()).toString();
const parsedServices = AccountSASServices.parse(accountSASSignatureValues.services).toString();
const parsedResourceTypes = AccountSASResourceTypes.parse(accountSASSignatureValues.resourceTypes).toString();
let stringToSign;
if (version >= "2020-12-06") {
stringToSign = [
sharedKeyCredential.accountName,
parsedPermissions,
parsedServices,
parsedResourceTypes,
accountSASSignatureValues.startsOn
? truncatedISO8061Date(accountSASSignatureValues.startsOn, false)