UNPKG

@azure/storage-blob

Version:
1,471 lines • 67.6 kB
import { getDefaultProxySettings } from "@azure/core-rest-pipeline";
import { isNodeLike } from "@azure/core-util";
import { isTokenCredential } from "@azure/core-auth";
import { AnonymousCredential, StorageResponseFormat, StorageSharedKeyCredential, } from "@azure/storage-common";
import { newPipeline, isPipelineLike } from "./Pipeline.js";
import { StorageClient } from "./StorageClient.js";
import { tracingClient } from "./utils/tracing.js";
import { appendToURLPath, appendToURLQuery, assertResponse, BlobNameToString, ConvertInternalResponseOfListBlobFlat, ConvertInternalResponseOfListBlobHierarchy, EscapePath, extractConnectionStringParts, isIpEndpointStyle, parseObjectReplicationRecord, resolveResponseFormat, toTags, truncatedISO8061Date, } from "./utils/utils.common.js";
import { parseBlobListArrowResponse } from "./utils/blobListArrowParser.js";
import { deserializeListBlobFlatSegmentXml, deserializeListBlobHierarchySegmentXml, } from "./utils/blobListXmlParser.js";
import { ApacheArrowContentType } from "./utils/constants.js";
import { generateBlobSASQueryParameters, generateBlobSASQueryParametersInternal, } from "./sas/BlobSASSignatureValues.js";
import { BlobLeaseClient } from "./BlobLeaseClient.js";
import { AppendBlobClient, BlobClient, BlockBlobClient, PageBlobClient } from "./Clients.js";
import { BlobBatchClient } from "./BlobBatchClient.js";
/**
 * Maps a generated internal blob item (with a structured `name`) to the public
 * {@link BlobItem} shape, decoding the name and parsing tags / object-replication
 * metadata. Shared by the XML and Apache Arrow list paths so they cannot drift.
 */
function mapBlobItemInternal(blobItemInternal) {
    const blobItem = {
        ...blobItemInternal,
        name: BlobNameToString(blobItemInternal.name),
        tags: toTags(blobItemInternal.blobTags),
        objectReplicationSourceProperties: parseObjectReplicationRecord(blobItemInternal.objectReplicationMetadata),
    };
    return blobItem;
}
/**
 * Maps a generated internal blob prefix (with a structured `name`) to the public
 * {@link BlobPrefix} shape. Shared by the XML and Apache Arrow list paths.
 */
function mapBlobPrefixInternal(blobPrefixInternal) {
    const blobPrefix = {
        ...blobPrefixInternal,
        name: BlobNameToString(blobPrefixInternal.name),
    };
    return blobPrefix;
}
/**
 * Returns true when a raw List Blobs response was actually returned as Apache Arrow.
 * The service falls back to XML for accounts that do not support Apache Arrow, so the
 * Content-Type header (ignoring parameters such as charset) is how the two are told apart.
 */
function isApacheArrow(contentType) {
    return (contentType ?? "").split(";")[0].trim().toLowerCase() === ApacheArrowContentType;
}
/**
 * Attaches the response metadata common to every List Blobs segment response - the
 * request IDs, service version, date, content-type, and `_response` - so the
 * flat/hierarchy and Apache Arrow/XML paths don't each repeat it.
 *
 * The raw stream body has already been consumed by the caller, so the stream
 * operation's `_response` carries only headers/status. To honor the
 * `ContainerListBlob*SegmentResponse` contract (whose `_response.parsedBody` is
 * non-optional), the caller passes the segment it just parsed as `parsedBody`, which
 * mirrors what the XML path exposes. `bodyAsText` carries the decoded XML for the
 * XML-fallback path, or `""` for a native Apache Arrow body (binary, no text form).
 */
function withListSegmentResponseMetadata(base, rawResponse, parsedBody, bodyAsText = "") {
    return {
        ...base,
        clientRequestId: rawResponse.clientRequestId,
        requestId: rawResponse.requestId,
        version: rawResponse.version,
        date: rawResponse.date,
        contentType: rawResponse.contentType,
        _response: {
            ...rawResponse._response,
            bodyAsText,
            parsedBody,
        },
    };
}
/**
 * A ContainerClient represents a URL to the Azure Storage container allowing you to manipulate its blobs.
 */
export class ContainerClient extends StorageClient {
    /**
     * containerContext provided by protocol layer.
     */
    containerContext;
    _containerName;
    blobClientConfig;
    /**
     * The name of the container.
     */
    get containerName() {
        return this._containerName;
    }
    constructor(urlOrConnectionString, credentialOrPipelineOrContainerName, 
    // Legacy, no fix for eslint error without breaking. Disable it for this interface.
    /* eslint-disable-next-line @azure/azure-sdk/ts-naming-options*/
    options) {
        let pipeline;
        let url;
        options = options || {};
        if (isPipelineLike(credentialOrPipelineOrContainerName)) {
            // (url: string, pipeline: Pipeline, options?: BlobClientConfig)
            url = urlOrConnectionString;
            pipeline = credentialOrPipelineOrContainerName;
        }
        else if ((isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential) ||
            credentialOrPipelineOrContainerName instanceof AnonymousCredential ||
            isTokenCredential(credentialOrPipelineOrContainerName)) {
            // (url: string, credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential, options?: StoragePipelineOptions)
            url = urlOrConnectionString;
            pipeline = newPipeline(credentialOrPipelineOrContainerName, options);
        }
        else if (!credentialOrPipelineOrContainerName &&
            typeof credentialOrPipelineOrContainerName !== "string") {
            // (url: string, credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential, options?: StoragePipelineOptions)
            // The second parameter is undefined. Use anonymous credential.
            url = urlOrConnectionString;
            pipeline = newPipeline(new AnonymousCredential(), options);
        }
        else if (credentialOrPipelineOrContainerName &&
            typeof credentialOrPipelineOrContainerName === "string") {
            // (connectionString: string, containerName: string, blobName: string, options?: StoragePipelineOptions)
            const containerName = credentialOrPipelineOrContainerName;
            const extractedCreds = extractConnectionStringParts(urlOrConnectionString);
            if (extractedCreds.kind === "AccountConnString") {
                if (isNodeLike) {
                    const sharedKeyCredential = new StorageSharedKeyCredential(extractedCreds.accountName, extractedCreds.accountKey);
                    url = appendToURLPath(extractedCreds.url, encodeURIComponent(containerName));
                    if (!options.proxyOptions) {
                        options.proxyOptions = getDefaultProxySettings(extractedCreds.proxyUri);
                    }
                    pipeline = newPipeline(sharedKeyCredential, options);
                }
                else {
                    throw new Error("Account connection string is only supported in Node.js environment");
                }
            }
            else if (extractedCreds.kind === "SASConnString") {
                url =
                    appendToURLPath(extractedCreds.url, encodeURIComponent(containerName)) +
                        "?" +
                        extractedCreds.accountSas;
                pipeline = newPipeline(new AnonymousCredential(), options);
            }
            else {
                throw new Error("Connection string must be either an Account connection string or a SAS connection string");
            }
        }
        else {
            throw new Error("Expecting non-empty strings for containerName parameter");
        }
        super(url, pipeline);
        this._containerName = this.getContainerNameFromUrl();
        this.containerContext = this.storageClientContext.container;
        this.blobClientConfig = options;
    }
    /**
     * Creates a new container under the specified account. If the container with
     * the same name already exists, the operation fails.
     * @see https://learn.microsoft.com/rest/api/storageservices/create-container
     * Naming rules: @see https://learn.microsoft.com/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
     *
     * @param options - Options to Container Create operation.
     *
     *
     * Example usage:
     *
     * ```ts snippet:ContainerClientCreate
     * import { BlobServiceClient } from "@azure/storage-blob";
     * import { DefaultAzureCredential } from "@azure/identity";
     *
     * const account = "<account>";
     * const blobServiceClient = new BlobServiceClient(
     *   `https://${account}.blob.core.windows.net`,
     *   new DefaultAzureCredential(),
     * );
     *
     * const containerName = "<container name>";
     * const containerClient = blobServiceClient.getContainerClient(containerName);
     * const createContainerResponse = await containerClient.create();
     * console.log("Container was created successfully", createContainerResponse.requestId);
     * ```
     */
    async create(options = {}) {
        return tracingClient.withSpan("ContainerClient-create", options, async (updatedOptions) => {
            return assertResponse(await this.containerContext.create(updatedOptions));
        });
    }
    /**
     * Creates a new container under the specified account. If the container with
     * the same name already exists, it is not changed.
     * @see https://learn.microsoft.com/rest/api/storageservices/create-container
     * Naming rules: @see https://learn.microsoft.com/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
     *
     * @param options -
     */
    async createIfNotExists(options = {}) {
        return tracingClient.withSpan("ContainerClient-createIfNotExists", options, async (updatedOptions) => {
            try {
                const res = await this.create(updatedOptions);
                return {
                    succeeded: true,
                    ...res,
                    _response: res._response, // _response is made non-enumerable
                };
            }
            catch (e) {
                if (e.details?.errorCode === "ContainerAlreadyExists") {
                    return {
                        succeeded: false,
                        ...e.response?.parsedHeaders,
                        _response: e.response,
                    };
                }
                else {
                    throw e;
                }
            }
        });
    }
    /**
     * Returns true if the Azure container resource represented by this client exists; false otherwise.
     *
     * NOTE: use this function with care since an existing container might be deleted by other clients or
     * applications. Vice versa new containers with the same name might be added by other clients or
     * applications after this function completes.
     *
     * @param options -
     */
    async exists(options = {}) {
        return tracingClient.withSpan("ContainerClient-exists", options, async (updatedOptions) => {
            try {
                await this.getProperties({
                    abortSignal: options.abortSignal,
                    tracingOptions: updatedOptions.tracingOptions,
                });
                return true;
            }
            catch (e) {
                if (e.statusCode === 404) {
                    return false;
                }
                throw e;
            }
        });
    }
    /**
     * Creates a {@link BlobClient}
     *
     * @param blobName - A blob name
     * @returns A new BlobClient object for the given blob name.
     */
    getBlobClient(blobName) {
        return new BlobClient(appendToURLPath(this.url, EscapePath(blobName)), this.pipeline, this.blobClientConfig);
    }
    /**
     * Creates an {@link AppendBlobClient}
     *
     * @param blobName - An append blob name
     */
    getAppendBlobClient(blobName) {
        return new AppendBlobClient(appendToURLPath(this.url, EscapePath(blobName)), this.pipeline, this.blobClientConfig);
    }
    /**
     * Creates a {@link BlockBlobClient}
     *
     * @param blobName - A block blob name
     *
     *
     * Example usage:
     *
     * ```ts snippet:ClientsUpload
     * import { BlobServiceClient } from "@azure/storage-blob";
     * import { DefaultAzureCredential } from "@azure/identity";
     *
     * const account = "<account>";
     * const blobServiceClient = new BlobServiceClient(
     *   `https://${account}.blob.core.windows.net`,
     *   new DefaultAzureCredential(),
     * );
     *
     * const containerName = "<container name>";
     * const blobName = "<blob name>";
     * const containerClient = blobServiceClient.getContainerClient(containerName);
     * const blockBlobClient = containerClient.getBlockBlobClient(blobName);
     *
     * const content = "Hello world!";
     * const uploadBlobResponse = await blockBlobClient.upload(content, content.length);
     * ```
     */
    getBlockBlobClient(blobName) {
        return new BlockBlobClient(appendToURLPath(this.url, EscapePath(blobName)), this.pipeline, this.blobClientConfig);
    }
    /**
     * Creates a {@link PageBlobClient}
     *
     * @param blobName - A page blob name
     */
    getPageBlobClient(blobName) {
        return new PageBlobClient(appendToURLPath(this.url, EscapePath(blobName)), this.pipeline, this.blobClientConfig);
    }
    /**
     * Returns all user-defined metadata and system properties for the specified
     * container. The data returned does not include the container's list of blobs.
     * @see https://learn.microsoft.com/rest/api/storageservices/get-container-properties
     *
     * WARNING: The `metadata` object returned in the response will have its keys in lowercase, even if
     * they originally contained uppercase characters. This differs from the metadata keys returned by
     * the `listContainers` method of {@link BlobServiceClient} using the `includeMetadata` option, which
     * will retain their original casing.
     *
     * @param options - Options to Container Get Properties operation.
     */
    async getProperties(options = {}) {
        if (!options.conditions) {
            options.conditions = {};
        }
        return tracingClient.withSpan("ContainerClient-getProperties", options, async (updatedOptions) => {
            return assertResponse(await this.containerContext.getProperties({
                abortSignal: options.abortSignal,
                ...options.conditions,
                tracingOptions: updatedOptions.tracingOptions,
            }));
        });
    }
    /**
     * Marks the specified container for deletion. The container and any blobs
     * contained within it are later deleted during garbage collection.
     * @see https://learn.microsoft.com/rest/api/storageservices/delete-container
     *
     * @param options - Options to Container Delete operation.
     */
    async delete(options = {}) {
        if (!options.conditions) {
            options.conditions = {};
        }
        return tracingClient.withSpan("ContainerClient-delete", options, async (updatedOptions) => {
            return assertResponse(await this.containerContext.delete({
                abortSignal: options.abortSignal,
                leaseAccessConditions: options.conditions,
                modifiedAccessConditions: options.conditions,
                tracingOptions: updatedOptions.tracingOptions,
            }));
        });
    }
    /**
     * Marks the specified container for deletion if it exists. The container and any blobs
     * contained within it are later deleted during garbage collection.
     * @see https://learn.microsoft.com/rest/api/storageservices/delete-container
     *
     * @param options - Options to Container Delete operation.
     */
    async deleteIfExists(options = {}) {
        return tracingClient.withSpan("ContainerClient-deleteIfExists", options, async (updatedOptions) => {
            try {
                const res = await this.delete(updatedOptions);
                return {
                    succeeded: true,
                    ...res,
                    _response: res._response,
                };
            }
            catch (e) {
                if (e.details?.errorCode === "ContainerNotFound") {
                    return {
                        succeeded: false,
                        ...e.response?.parsedHeaders,
                        _response: e.response,
                    };
                }
                throw e;
            }
        });
    }
    /**
     * Sets one or more user-defined name-value pairs for the specified container.
     *
     * If no option provided, or no metadata defined in the parameter, the container
     * metadata will be removed.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/set-container-metadata
     *
     * @param metadata - Replace existing metadata with this value.
     *                            If no value provided the existing metadata will be removed.
     * @param options - Options to Container Set Metadata operation.
     */
    async setMetadata(metadata, options = {}) {
        if (!options.conditions) {
            options.conditions = {};
        }
        if (options.conditions.ifUnmodifiedSince) {
            throw new RangeError("the IfUnmodifiedSince must have their default values because they are ignored by the blob service");
        }
        return tracingClient.withSpan("ContainerClient-setMetadata", options, async (updatedOptions) => {
            return assertResponse(await this.containerContext.setMetadata({
                abortSignal: options.abortSignal,
                leaseAccessConditions: options.conditions,
                metadata,
                modifiedAccessConditions: options.conditions,
                tracingOptions: updatedOptions.tracingOptions,
            }));
        });
    }
    /**
     * Gets the permissions for the specified container. The permissions indicate
     * whether container data may be accessed publicly.
     *
     * WARNING: JavaScript Date will potentially lose precision when parsing startsOn and expiresOn strings.
     * For example, new Date("2018-12-31T03:44:23.8827891Z").toISOString() will get "2018-12-31T03:44:23.882Z".
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/get-container-acl
     *
     * @param options - Options to Container Get Access Policy operation.
     */
    async getAccessPolicy(options = {}) {
        if (!options.conditions) {
            options.conditions = {};
        }
        return tracingClient.withSpan("ContainerClient-getAccessPolicy", options, async (updatedOptions) => {
            const response = assertResponse(await this.containerContext.getAccessPolicy({
                abortSignal: options.abortSignal,
                leaseAccessConditions: options.conditions,
                tracingOptions: updatedOptions.tracingOptions,
            }));
            const res = {
                _response: response._response,
                blobPublicAccess: response.blobPublicAccess,
                date: response.date,
                etag: response.etag,
                errorCode: response.errorCode,
                lastModified: response.lastModified,
                requestId: response.requestId,
                clientRequestId: response.clientRequestId,
                signedIdentifiers: [],
                version: response.version,
            };
            for (const identifier of response) {
                let accessPolicy = undefined;
                if (identifier.accessPolicy) {
                    accessPolicy = {
                        permissions: identifier.accessPolicy.permissions,
                    };
                    if (identifier.accessPolicy.expiresOn) {
                        accessPolicy.expiresOn = new Date(identifier.accessPolicy.expiresOn);
                    }
                    if (identifier.accessPolicy.startsOn) {
                        accessPolicy.startsOn = new Date(identifier.accessPolicy.startsOn);
                    }
                }
                res.signedIdentifiers.push({
                    accessPolicy,
                    id: identifier.id,
                });
            }
            return res;
        });
    }
    /**
     * Sets the permissions for the specified container. The permissions indicate
     * whether blobs in a container may be accessed publicly.
     *
     * When you set permissions for a container, the existing permissions are replaced.
     * If no access or containerAcl provided, the existing container ACL will be
     * removed.
     *
     * When you establish a stored access policy on a container, it may take up to 30 seconds to take effect.
     * During this interval, a shared access signature that is associated with the stored access policy will
     * fail with status code 403 (Forbidden), until the access policy becomes active.
     * @see https://learn.microsoft.com/rest/api/storageservices/set-container-acl
     *
     * @param access - The level of public access to data in the container.
     * @param containerAcl - Array of elements each having a unique Id and details of the access policy.
     * @param options - Options to Container Set Access Policy operation.
     */
    async setAccessPolicy(access, containerAcl, options = {}) {
        options.conditions = options.conditions || {};
        return tracingClient.withSpan("ContainerClient-setAccessPolicy", options, async (updatedOptions) => {
            const acl = [];
            for (const identifier of containerAcl || []) {
                acl.push({
                    accessPolicy: {
                        expiresOn: identifier.accessPolicy.expiresOn
                            ? truncatedISO8061Date(identifier.accessPolicy.expiresOn)
                            : "",
                        permissions: identifier.accessPolicy.permissions,
                        startsOn: identifier.accessPolicy.startsOn
                            ? truncatedISO8061Date(identifier.accessPolicy.startsOn)
                            : "",
                    },
                    id: identifier.id,
                });
            }
            return assertResponse(await this.containerContext.setAccessPolicy({
                abortSignal: options.abortSignal,
                access,
                containerAcl: acl,
                leaseAccessConditions: options.conditions,
                modifiedAccessConditions: options.conditions,
                tracingOptions: updatedOptions.tracingOptions,
            }));
        });
    }
    /**
     * Get a {@link BlobLeaseClient} that manages leases on the container.
     *
     * @param proposeLeaseId - Initial proposed lease Id.
     * @returns A new BlobLeaseClient object for managing leases on the container.
     */
    getBlobLeaseClient(proposeLeaseId) {
        return new BlobLeaseClient(this, proposeLeaseId);
    }
    /**
     * Creates a new block blob, or updates the content of an existing block blob.
     *
     * Updating an existing block blob overwrites any existing metadata on the blob.
     * Partial updates are not supported; the content of the existing blob is
     * overwritten with the new content. To perform a partial update of a block blob's,
     * use {@link BlockBlobClient.stageBlock} and {@link BlockBlobClient.commitBlockList}.
     *
     * This is a non-parallel uploading method, please use {@link BlockBlobClient.uploadFile},
     * {@link BlockBlobClient.uploadStream} or {@link BlockBlobClient.uploadBrowserData} for better
     * performance with concurrency uploading.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/put-blob
     *
     * @param blobName - Name of the block blob to create or update.
     * @param body - Blob, string, ArrayBuffer, ArrayBufferView or a function
     *                               which returns a new Readable stream whose offset is from data source beginning.
     * @param contentLength - Length of body in bytes. Use Buffer.byteLength() to calculate body length for a
     *                               string including non non-Base64/Hex-encoded characters.
     * @param options - Options to configure the Block Blob Upload operation.
     * @returns Block Blob upload response data and the corresponding BlockBlobClient instance.
     */
    async uploadBlockBlob(blobName, body, contentLength, options = {}) {
        return tracingClient.withSpan("ContainerClient-uploadBlockBlob", options, async (updatedOptions) => {
            const blockBlobClient = this.getBlockBlobClient(blobName);
            const response = await blockBlobClient.upload(body, contentLength, updatedOptions);
            return {
                blockBlobClient,
                response,
            };
        });
    }
    /**
     * Marks the specified blob or snapshot for deletion. The blob is later deleted
     * during garbage collection. Note that in order to delete a blob, you must delete
     * all of its snapshots. You can delete both at the same time with the Delete
     * Blob operation.
     * @see https://learn.microsoft.com/rest/api/storageservices/delete-blob
     *
     * @param blobName -
     * @param options - Options to Blob Delete operation.
     * @returns Block blob deletion response data.
     */
    async deleteBlob(blobName, options = {}) {
        return tracingClient.withSpan("ContainerClient-deleteBlob", options, async (updatedOptions) => {
            let blobClient = this.getBlobClient(blobName);
            if (options.versionId) {
                blobClient = blobClient.withVersion(options.versionId);
            }
            return blobClient.delete(updatedOptions);
        });
    }
    /**
     * listBlobFlatSegment returns a single segment of blobs starting from the
     * specified Marker. Use an empty Marker to start enumeration from the beginning.
     * After getting a segment, process it, and then call listBlobsFlatSegment again
     * (passing the the previously-returned Marker) to get the next segment.
     * @see https://learn.microsoft.com/rest/api/storageservices/list-blobs
     *
     * @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
     * @param options - Options to Container List Blob Flat Segment operation.
     */
    async listBlobFlatSegment(marker, options = {}) {
        return tracingClient.withSpan("ContainerClient-listBlobFlatSegment", options, async (updatedOptions) => {
            if (resolveResponseFormat(options.responseFormat) === StorageResponseFormat.Arrow) {
                return this.listBlobFlatSegmentApacheArrow(marker, {
                    ...options,
                    tracingOptions: updatedOptions.tracingOptions,
                });
            }
            const response = assertResponse(await this.containerContext.listBlobFlatSegment({
                marker,
                ...options,
                tracingOptions: updatedOptions.tracingOptions,
            }));
            const wrappedResponse = {
                ...response,
                _response: {
                    ...response._response,
                    parsedBody: ConvertInternalResponseOfListBlobFlat(response._response.parsedBody),
                }, // _response is made non-enumerable
                segment: {
                    ...response.segment,
                    blobItems: response.segment.blobItems.map(mapBlobItemInternal),
                },
            };
            return wrappedResponse;
        });
    }
    /**
     * Lists a single segment of blobs using the Apache Arrow response format. The service
     * returns a raw stream that is either Apache Arrow or XML (when the account does not
     * support Apache Arrow); both formats are parsed here and produce the same result.
     *
     * @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
     * @param options - Options to Container List Blob Flat Segment operation.
     */
    async listBlobFlatSegmentApacheArrow(marker, options) {
        const rawResponse = assertResponse(await this.containerContext.listBlobFlatSegmentApacheArrow({
            marker,
            ...options,
        }));
        // The service falls back to XML for accounts that do not support Apache Arrow.
        // The Content-Type header indicates which format we actually received. When it
        // is not Apache Arrow, parse the already-received XML stream in place
        // instead of issuing a second request.
        if (!isApacheArrow(rawResponse.contentType)) {
            const { parsed: internalResponse, bodyAsText } = await deserializeListBlobFlatSegmentXml(rawResponse);
            return withListSegmentResponseMetadata({
                ...internalResponse,
                segment: {
                    ...internalResponse.segment,
                    blobItems: (internalResponse.segment?.blobItems ?? []).map(mapBlobItemInternal),
                },
            }, rawResponse, ConvertInternalResponseOfListBlobFlat(internalResponse), bodyAsText);
        }
        const parsed = await parseBlobListArrowResponse(rawResponse);
        const serviceUrl = new URL(this.url);
        const containerPath = serviceUrl.pathname.replace(/\/+$/, "");
        serviceUrl.pathname = containerPath.slice(0, containerPath.lastIndexOf("/") + 1);
        serviceUrl.search = "";
        serviceUrl.hash = "";
        const serviceEndpoint = serviceUrl.toString();
        const internalResponse = {
            serviceEndpoint,
            containerName: this.containerName,
            prefix: options.prefix,
            marker,
            maxPageSize: options.maxPageSize,
            segment: { blobItems: parsed.blobItems },
            continuationToken: parsed.nextMarker,
        };
        return withListSegmentResponseMetadata({
            serviceEndpoint,
            containerName: this.containerName,
            prefix: options.prefix,
            marker,
            maxPageSize: options.maxPageSize,
            segment: {
                blobItems: parsed.blobItems.map(mapBlobItemInternal),
            },
            continuationToken: parsed.nextMarker,
        }, rawResponse, ConvertInternalResponseOfListBlobFlat(internalResponse));
    }
    /**
     * listBlobHierarchySegment returns a single segment of blobs starting from
     * the specified Marker. Use an empty Marker to start enumeration from the
     * beginning. After getting a segment, process it, and then call listBlobsHierarchicalSegment
     * again (passing the the previously-returned Marker) to get the next segment.
     * @see https://learn.microsoft.com/rest/api/storageservices/list-blobs
     *
     * @param delimiter - The character or string used to define the virtual hierarchy
     * @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
     * @param options - Options to Container List Blob Hierarchy Segment operation.
     */
    async listBlobHierarchySegment(delimiter, marker, options = {}) {
        return tracingClient.withSpan("ContainerClient-listBlobHierarchySegment", options, async (updatedOptions) => {
            if (resolveResponseFormat(options.responseFormat) === StorageResponseFormat.Arrow) {
                return this.listBlobHierarchySegmentApacheArrow(delimiter, marker, {
                    ...options,
                    tracingOptions: updatedOptions.tracingOptions,
                });
            }
            const response = assertResponse(await this.containerContext.listBlobHierarchySegment(delimiter, {
                marker,
                ...options,
                tracingOptions: updatedOptions.tracingOptions,
            }));
            const wrappedResponse = {
                ...response,
                _response: {
                    ...response._response,
                    parsedBody: ConvertInternalResponseOfListBlobHierarchy(response._response.parsedBody),
                }, // _response is made non-enumerable
                segment: {
                    ...response.segment,
                    blobItems: response.segment.blobItems.map(mapBlobItemInternal),
                    blobPrefixes: response.segment.blobPrefixes?.map(mapBlobPrefixInternal),
                },
            };
            return wrappedResponse;
        });
    }
    /**
     * Lists a single segment of blobs by hierarchy using the Apache Arrow response format.
     * The service returns a raw stream that is either Apache Arrow or XML (when the account
     * does not support Apache Arrow); both formats are parsed here and produce the same result.
     *
     * @param delimiter - The character or string used to define the virtual hierarchy
     * @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
     * @param options - Options to Container List Blob Hierarchy Segment operation.
     */
    async listBlobHierarchySegmentApacheArrow(delimiter, marker, options) {
        const rawResponse = assertResponse(await this.containerContext.listBlobHierarchySegmentApacheArrow(delimiter, {
            marker,
            ...options,
        }));
        // The service falls back to XML for accounts that do not support Apache Arrow.
        // The Content-Type header indicates which format we actually received. When it
        // is not Apache Arrow, parse the already-received XML stream in place instead of
        // issuing a second request.
        if (!isApacheArrow(rawResponse.contentType)) {
            const { parsed: internalResponse, bodyAsText } = await deserializeListBlobHierarchySegmentXml(rawResponse);
            return withListSegmentResponseMetadata({
                ...internalResponse,
                segment: {
                    ...internalResponse.segment,
                    blobItems: (internalResponse.segment?.blobItems ?? []).map(mapBlobItemInternal),
                    blobPrefixes: internalResponse.segment?.blobPrefixes?.map(mapBlobPrefixInternal),
                },
            }, rawResponse, ConvertInternalResponseOfListBlobHierarchy(internalResponse), bodyAsText);
        }
        const parsed = await parseBlobListArrowResponse(rawResponse);
        const serviceUrl = new URL(this.url);
        const containerPath = serviceUrl.pathname.replace(/\/+$/, "");
        serviceUrl.pathname = containerPath.slice(0, containerPath.lastIndexOf("/") + 1);
        serviceUrl.search = "";
        serviceUrl.hash = "";
        const serviceEndpoint = serviceUrl.toString();
        const internalResponse = {
            serviceEndpoint,
            containerName: this.containerName,
            prefix: options.prefix,
            marker,
            maxPageSize: options.maxPageSize,
            delimiter,
            segment: { blobItems: parsed.blobItems, blobPrefixes: parsed.blobPrefixes },
            continuationToken: parsed.nextMarker,
        };
        return withListSegmentResponseMetadata({
            serviceEndpoint,
            containerName: this.containerName,
            prefix: options.prefix,
            marker,
            maxPageSize: options.maxPageSize,
            delimiter,
            segment: {
                blobItems: parsed.blobItems.map(mapBlobItemInternal),
                blobPrefixes: parsed.blobPrefixes.map(mapBlobPrefixInternal),
            },
            continuationToken: parsed.nextMarker,
        }, rawResponse, ConvertInternalResponseOfListBlobHierarchy(internalResponse));
    }
    /**
     * Returns an AsyncIterableIterator for ContainerListBlobFlatSegmentResponse
     *
     * @param marker - A string value that identifies the portion of
     *                          the list of blobs to be returned with the next listing operation. The
     *                          operation returns the ContinuationToken value within the response body if the
     *                          listing operation did not return all blobs remaining to be listed
     *                          with the current page. The ContinuationToken value can be used as the value for
     *                          the marker parameter in a subsequent call to request the next page of list
     *                          items. The marker value is opaque to the client.
     * @param options - Options to list blobs operation.
     */
    async *listSegments(marker, options = {}) {
        let listBlobsFlatSegmentResponse;
        if (!!marker || marker === undefined) {
            do {
                listBlobsFlatSegmentResponse = await this.listBlobFlatSegment(marker, options);
                marker = listBlobsFlatSegmentResponse.continuationToken;
                yield await listBlobsFlatSegmentResponse;
            } while (marker);
        }
    }
    /**
     * Returns an AsyncIterableIterator of {@link BlobItem} objects
     *
     * @param options - Options to list blobs operation.
     */
    async *listItems(options = {}) {
        let marker;
        for await (const listBlobsFlatSegmentResponse of this.listSegments(marker, options)) {
            yield* listBlobsFlatSegmentResponse.segment.blobItems;
        }
    }
    /**
     * Returns an async iterable iterator to list all the blobs
     * under the specified account.
     *
     * .byPage() returns an async iterable iterator to list the blobs in pages.
     *
     * ```ts snippet:ReadmeSampleListBlobs_Multiple
     * import { BlobServiceClient } from "@azure/storage-blob";
     * import { DefaultAzureCredential } from "@azure/identity";
     *
     * const account = "<account>";
     * const blobServiceClient = new BlobServiceClient(
     *   `https://${account}.blob.core.windows.net`,
     *   new DefaultAzureCredential(),
     * );
     *
     * const containerName = "<container name>";
     * const containerClient = blobServiceClient.getContainerClient(containerName);
     *
     * // Example using `for await` syntax
     * let i = 1;
     * const blobs = containerClient.listBlobsFlat();
     * for await (const blob of blobs) {
     *   console.log(`Blob ${i++}: ${blob.name}`);
     * }
     *
     * // Example using `iter.next()` syntax
     * i = 1;
     * const iter = containerClient.listBlobsFlat();
     * let { value, done } = await iter.next();
     * while (!done) {
     *   console.log(`Blob ${i++}: ${value.name}`);
     *   ({ value, done } = await iter.next());
     * }
     *
     * // Example using `byPage()` syntax
     * i = 1;
     * for await (const page of containerClient.listBlobsFlat().byPage({ maxPageSize: 20 })) {
     *   for (const blob of page.segment.blobItems) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     *
     * // Example using paging with a marker
     * i = 1;
     * let iterator = containerClient.listBlobsFlat().byPage({ maxPageSize: 2 });
     * let response = (await iterator.next()).value;
     * // Prints 2 blob names
     * if (response.segment.blobItems) {
     *   for (const blob of response.segment.blobItems) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     * // Gets next marker
     * let marker = response.continuationToken;
     * // Passing next marker as continuationToken
     * iterator = containerClient.listBlobsFlat().byPage({ continuationToken: marker, maxPageSize: 10 });
     * response = (await iterator.next()).value;
     * // Prints 10 blob names
     * if (response.segment.blobItems) {
     *   for (const blob of response.segment.blobItems) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     * ```
     *
     * @param options - Options to list blobs.
     * @returns An asyncIterableIterator that supports paging.
     */
    listBlobsFlat(options = {}) {
        if (options.endBefore !== undefined &&
            resolveResponseFormat(options.responseFormat) !== StorageResponseFormat.Arrow) {
            throw new RangeError("The 'endBefore' option is only supported when 'responseFormat' is StorageResponseFormat.Arrow.");
        }
        const include = [];
        if (options.includeCopy) {
            include.push("copy");
        }
        if (options.includeDeleted) {
            include.push("deleted");
        }
        if (options.includeMetadata) {
            include.push("metadata");
        }
        if (options.includeSnapshots) {
            include.push("snapshots");
        }
        if (options.includeVersions) {
            include.push("versions");
        }
        if (options.includeUncommitedBlobs) {
            include.push("uncommittedblobs");
        }
        if (options.includeTags) {
            include.push("tags");
        }
        if (options.includeDeletedWithVersions) {
            include.push("deletedwithversions");
        }
        if (options.includeImmutabilityPolicy) {
            include.push("immutabilitypolicy");
        }
        if (options.includeLegalHold) {
            include.push("legalhold");
        }
        if (options.prefix === "") {
            options.prefix = undefined;
        }
        const updatedOptions = {
            ...options,
            ...(include.length > 0 ? { include: include } : {}),
        };
        // AsyncIterableIterator to iterate over blobs
        const iter = this.listItems(updatedOptions);
        return {
            /**
             * The next method, part of the iteration protocol
             */
            next() {
                return iter.next();
            },
            /**
             * The connection to the async iterator, part of the iteration protocol
             */
            [Symbol.asyncIterator]() {
                return this;
            },
            /**
             * Return an AsyncIterableIterator that works a page at a time
             */
            byPage: (settings = {}) => {
                return this.listSegments(settings.continuationToken, {
                    maxPageSize: settings.maxPageSize,
                    ...updatedOptions,
                });
            },
        };
    }
    /**
     * Returns an AsyncIterableIterator for ContainerListBlobHierarchySegmentResponse
     *
     * @param delimiter - The character or string used to define the virtual hierarchy
     * @param marker - A string value that identifies the portion of
     *                          the list of blobs to be returned with the next listing operation. The
     *                          operation returns the ContinuationToken value within the response body if the
     *                          listing operation did not return all blobs remaining to be listed
     *                          with the current page. The ContinuationToken value can be used as the value for
     *                          the marker parameter in a subsequent call to request the next page of list
     *                          items. The marker value is opaque to the client.
     * @param options - Options to list blobs operation.
     */
    async *listHierarchySegments(delimiter, marker, options = {}) {
        let listBlobsHierarchySegmentResponse;
        if (!!marker || marker === undefined) {
            do {
                listBlobsHierarchySegmentResponse = await this.listBlobHierarchySegment(delimiter, marker, options);
                marker = listBlobsHierarchySegmentResponse.continuationToken;
                yield await listBlobsHierarchySegmentResponse;
            } while (marker);
        }
    }
    /**
     * Returns an AsyncIterableIterator for {@link BlobPrefix} and {@link BlobItem} objects.
     *
     * @param delimiter - The character or string used to define the virtual hierarchy
     * @param options - Options to list blobs operation.
     */
    async *listItemsByHierarchy(delimiter, options = {}) {
        let marker;
        for await (const listBlobsHierarchySegmentResponse of this.listHierarchySegments(delimiter, marker, options)) {
            const segment = listBlobsHierarchySegmentResponse.segment;
            if (segment.blobPrefixes) {
                for (const prefix of segment.blobPrefixes) {
                    yield {
                        kind: "prefix",
                        ...prefix,
                    };
                }
            }
            for (const blob of segment.blobItems) {
                yield { kind: "blob", ...blob };
            }
        }
    }
    /**
     * Returns an async iterable iterator to list all the blobs by hierarchy.
     * under the specified account.
     *
     * .byPage() returns an async iterable iterator to list the blobs by hierarchy in pages.
     *
     * ```ts snippet:ReadmeSampleListBlobsByHierarchy
     * import { BlobServiceClient } from "@azure/storage-blob";
     * import { DefaultAzureCredential } from "@azure/identity";
     *
     * const account = "<account>";
     * const blobServiceClient = new BlobServiceClient(
     *   `https://${account}.blob.core.windows.net`,
     *   new DefaultAzureCredential(),
     * );
     *
     * const containerName = "<container name>";
     * const containerClient = blobServiceClient.getContainerClient(containerName);
     *
     * // Example using `for await` syntax
     * let i = 1;
     * const blobs = containerClient.listBlobsByHierarchy("/");
     * for await (const blob of blobs) {
     *   if (blob.kind === "prefix") {
     *     console.log(`\tBlobPrefix: ${blob.name}`);
     *   } else {
     *     console.log(`\tBlobItem: name - ${blob.name}`);
     *   }
     * }
     *
     * // Example using `iter.next()` syntax
     * i = 1;
     * const iter = containerClient.listBlobsByHierarchy("/");
     * let { value, done } = await iter.next();
     * while (!done) {
     *   if (value.kind === "prefix") {
     *     console.log(`\tBlobPrefix: ${value.name}`);
     *   } else {
     *     console.log(`\tBlobItem: name - ${value.name}`);
     *   }
     *   ({ value, done } = await iter.next());
     * }
     *
     * // Example using `byPage()` syntax
     * i = 1;
     * for await (const page of containerClient.listBlobsByHierarchy("/").byPage({ maxPageSize: 20 })) {
     *   const segment = page.segment;
     *   if (segment.blobPrefixes) {
     *     for (const prefix of segment.blobPrefixes) {
     *       console.log(`\tBlobPrefix: ${prefix.name}`);
     *     }
     *   }
     *   for (const blob of page.segment.blobItems) {
     *     console.log(`\tBlobItem: name - ${blob.name}`);
     *   }
     * }
     *
     * // Example using paging with a marker
     * i = 1;
     * let iterator = containerClient.listBlobsByHierarchy("/").byPage({ maxPageSize: 2 });
     * let response = (await iterator.next()).value;
     * // Prints 2 blob names
     * if (response.blobPrefixes) {
     *   for (const prefix of response.blobPrefixes) {
     *     console.log(`\tBlobPrefix: ${prefix.name}`);
     *   }
     * }
     * if (response.segment.blobItems) {
     *   for (const blob of response.segment.blobItems) {
     *     console.log(`\tBlobItem: name - ${blob.name}`);
     *   }
     * }
     * // Gets next marker
     * let marker = response.continuationToken;
     * // Passing next marker as continuationToken
     * iterator = containerClient
     *   .listBlobsByHierarchy("/")
     *   .byPage({ continuationToken: marker, maxPageSize: 10 });
     * response = (await iterator.next()).value;
     * // Prints 10 blob names
     * if (response.blobPrefixes) {
     *   for (const prefix of response.blobPrefixes) {
     *     console.log(`\tBlobPrefix: ${prefix.name}`);
     *   }
     * }
     * if (response.segment.blobItems) {
     *   for (const blob of response.segment.blobItems) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     * ```
     *
     * @param delimiter - The character or string used to define the virtual hierarchy
     * @param options - Options to list blobs operation.
     */
    listBlobsByHierarchy(delimiter, options = {}) {
        if (delimiter === "") {
            throw new RangeError("delimiter should contain one or more characters");
        }
        if (options.endBefore !== undefined &&
            resolveResponseFormat(options.responseFormat) !== StorageResponseFormat.Arrow) {
            throw new RangeError("The 'endBefore' option is only supported when 'responseFormat' is StorageResponseFormat.Arrow.");
        }
        const include = [];
        if (options.includeCopy) {
            include.push("copy");
        }
        if (options.includeDeleted) {
            include.push("deleted");
        }
        if (options.includeMetadata) {
            include.push("metadata");
        }
        if (options.includeSnapshots) {
            include.push("snapshots");
        }
        if (options.includeVersions) {
            include.push("versions");
        }
        if (options.includeUncommitedBlobs) {
            include.push("uncommittedblobs");
        }
        if (options.includeTags) {
            include.push("tags");
        }
        if (options.includeDeletedWithVersions) {
            include.push("deletedwithversions");
        }
        if (options.includeImmutabilityPolicy) {
            include.push("immutabilitypolicy");
        }
        if (options.includeLegalHold) {
            include.push("legalhold");
        }
        if (options.prefix === "") {
            options.prefix = undefined;
        }
        const updatedOptions = {
            ...options,
            ...(include.length > 0 ? { include: include } : {}),
        };
        // AsyncIterableIterator to iterate over blob prefixes and blobs
        const iter = this.listItemsByHierarchy(delimiter, updatedOptions);
        return {
            /**
             * The next method, part of the iteration protocol
             */
            async next() {
                return iter.next();
            },
            /**
             * The connection to the async iterator, part of the iteration protocol
             */
            [Symbol.asyncIterator]() {
                return this;
            },
            /**
             * Return an AsyncIterableIterator that works a page at a time
             */
            byPage: (settings = {}) => {
                return this.listHierarchySegments(delimiter, settings.continuationToken, {
                    maxPageSize: settings.maxPageSize,
                    ...updatedOptions,
                });
            },
        };
    }
    /**
     * The Filter Blobs operation enables callers to list blobs in the container whose tags
     * match a given search expression.
     *
     * @param tagFilterSqlExpression - The where parameter enables the caller to query blobs whose tags match a given expression.
     *                                        The given expression must evaluate to true for a blob to be returned in the results.
     *                                        The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
     *                                        however, only a subset of the OData filter syntax is supported in the Blob service.
     * @param marker - A string value that identifies the portion of
     *                          the list of blobs to be returned with the next listing operation. The
     *                          operation returns the continuationToken value within the response body if the
     *                          listing operation did not return all blobs remaining to be listed
     *                          with the current page. The continuationToken value can be used as the value for
     *                          the marker parameter in a subsequent call to request the next page of list
     *                          items. The marker value is opaque to the client.
     * @param options - Options to find blobs by tags.
     */
    async findBlobsByTagsSegment(tagFilterSqlExpression, marker, options = {}) {
        return tracingClient.withSpan("ContainerClient-findBlobsByTagsSegment", options, async (updatedOptions) => {
            const response = assertResponse(await this.containerContext.filterBlobs({
                abortSignal: options.abortSignal,
                where: tagFilterSqlExpression,
                marker,
                maxPageSize: options.maxPageSize,
                tracingOptions: updatedOptions.tracingOptions,
            }));
            const wrappedResponse = {
                ...response,
                _response: response._response, // _response is made non-enumerable
                blobs: response.blobs.map((blob) => {
                    let tagValue = "";
                    if (blob.tags?.blobTagSet.length === 1) {
                        tagValue = blob.tags.blobTagSet[0].value;
                    }
                    return { ...blob, tags: toTags(blob.tags), tagValue };
                }),
            };
            return wrappedResponse;
        });
    }
    /**
     * Returns an AsyncIterableIterator for ContainerFindBlobsByTagsSegmentResponse.
     *
     * @param tagFilterSqlExpression -  The where parameter enables the caller to query blobs whose tags match a given expression.
     *                                         The given expression must evaluate to true for a blob to be returned in the results.
     *                                         The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
     *                                         however, only a subset of the OData filter syntax is supported in the Blob service.
     * @param marker - A string value that identifies the portion of
     *                          the list of blobs to be returned with the next listing operation. The
     *                          operation returns the continuationToken value within the response body if the
     *                          listing operation did not return all blobs remaining to be listed
     *                          with the current page. The continuationToken value can be used as the value for
     *                          the marker parameter in a subsequent call to request the next page of list
     *                          items. The marker value is opaque to the client.
     * @param options - Options to find blobs by tags.
     */
    async *findBlobsByTagsSegments(tagFilterSqlExpression, marker, options = {}) {
        let response;
        if (!!marker || marker === undefined) {
            do {
                response = await this.findBlobsByTagsSegment(tagFilterSqlExpression, marker, options);
                response.blobs = response.blobs || [];
                marker = response.continuationToken;
                yield response;
            } while (marker);
        }
    }
    /**
     * Returns an AsyncIterableIterator for blobs.
     *
     * @param tagFilterSqlExpression -  The where parameter enables the caller to query blobs whose tags match a given expression.
     *                                         The given expression must evaluate to true for a blob to be returned in the results.
     *                                         The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
     *                                         however, only a subset of the OData filter syntax is supported in the Blob service.
     * @param options - Options to findBlobsByTagsItems.
     */
    async *findBlobsByTagsItems(tagFilterSqlExpression, options = {}) {
        let marker;
        for await (const segment of this.findBlobsByTagsSegments(tagFilterSqlExpression, marker, options)) {
            yield* segment.blobs;
        }
    }
    /**
     * Returns an async iterable iterator to find all blobs with specified tag
     * under the specified container.
     *
     * .byPage() returns an async iterable iterator to list the blobs in pages.
     *
     * Example using `for await` syntax:
     *
     * ```ts snippet:ReadmeSampleFindBlobsByTags
     * import { BlobServiceClient } from "@azure/storage-blob";
     * import { DefaultAzureCredential } from "@azure/identity";
     *
     * const account = "<account>";
     * const blobServiceClient = new BlobServiceClient(
     *   `https://${account}.blob.core.windows.net`,
     *   new DefaultAzureCredential(),
     * );
     *
     * const containerName = "<container name>";
     * const containerClient = blobServiceClient.getContainerClient(containerName);
     *
     * // Example using `for await` syntax
     * let i = 1;
     * for await (const blob of containerClient.findBlobsByTags("tagkey='tagvalue'")) {
     *   console.log(`Blob ${i++}: ${blob.name}`);
     * }
     *
     * // Example using `iter.next()` syntax
     * i = 1;
     * const iter = containerClient.findBlobsByTags("tagkey='tagvalue'");
     * let { value, done } = await iter.next();
     * while (!done) {
     *   console.log(`Blob ${i++}: ${value.name}`);
     *   ({ value, done } = await iter.next());
     * }
     *
     * // Example using `byPage()` syntax
     * i = 1;
     * for await (const page of containerClient
     *   .findBlobsByTags("tagkey='tagvalue'")
     *   .byPage({ maxPageSize: 20 })) {
     *   for (const blob of page.blobs) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     *
     * // Example using paging with a marker
     * i = 1;
     * let iterator = containerClient.findBlobsByTags("tagkey='tagvalue'").byPage({ maxPageSize: 2 });
     * let response = (await iterator.next()).value;
     * // Prints 2 blob names
     * if (response.blobs) {
     *   for (const blob of response.blobs) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     * // Gets next marker
     * let marker = response.continuationToken;
     * // Passing next marker as continuationToken
     * iterator = containerClient
     *   .findBlobsByTags("tagkey='tagvalue'")
     *   .byPage({ continuationToken: marker, maxPageSize: 10 });
     * response = (await iterator.next()).value;
     * // Prints 10 blob names
     * if (response.blobs) {
     *   for (const blob of response.blobs) {
     *     console.log(`Blob ${i++}: ${blob.name}`);
     *   }
     * }
     * ```
     *
     * @param tagFilterSqlExpression -  The where parameter enables the caller to query blobs whose tags match a given expression.
     *                                         The given expression must evaluate to true for a blob to be returned in the results.
     *                                         The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
     *                                         however, only a subset of the OData filter syntax is supported in the Blob service.
     * @param options - Options to find blobs by tags.
     */
    findBlobsByTags(tagFilterSqlExpression, options = {}) {
        // AsyncIterableIterator to iterate over blobs
        const listSegmentOptions = {
            ...options,
        };
        const iter = this.findBlobsByTagsItems(tagFilterSqlExpression, listSegmentOptions);
        return {
            /**
             * The next method, part of the iteration protocol
             */
            next() {
                return iter.next();
            },
            /**
             * The connection to the async iterator, part of the iteration protocol
             */
            [Symbol.asyncIterator]() {
                return this;
            },
            /**
             * Return an AsyncIterableIterator that works a page at a time
             */
            byPage: (settings = {}) => {
                return this.findBlobsByTagsSegments(tagFilterSqlExpression, settings.continuationToken, {
                    maxPageSize: settings.maxPageSize,
                    ...listSegmentOptions,
                });
            },
        };
    }
    /**
     * The Get Account Information operation returns the sku name and account kind
     * for the specified account.
     * The Get Account Information operation is available on service versions beginning
     * with version 2018-03-28.
     * @see https://learn.microsoft.com/rest/api/storageservices/get-account-information
     *
     * @param options - Options to the Service Get Account Info operation.
     * @returns Response data for the Service Get Account Info operation.
     */
    async getAccountInfo(options = {}) {
        return tracingClient.withSpan("ContainerClient-getAccountInfo", options, async (updatedOptions) => {
            return assertResponse(await this.containerContext.getAccountInfo({
                abortSignal: options.abortSignal,
                tracingOptions: updatedOptions.tracingOptions,
            }));
        });
    }
    getContainerNameFromUrl() {
        let containerName;
        try {
            //  URL may look like the following
            // "https://myaccount.blob.core.windows.net/mycontainer?sasString";
            // "https://myaccount.blob.core.windows.net/mycontainer";
            // IPv4/IPv6 address hosts, Endpoints - `http://127.0.0.1:10000/devstoreaccount1/containername`
            // http://localhost:10001/devstoreaccount1/containername
            const parsedUrl = new URL(this.url);
            if (parsedUrl.hostname.split(".")[1] === "blob") {
                // "https://myaccount.blob.core.windows.net/containername".
                // "https://customdomain.com/containername".
                // .getPath() -> /containername
                containerName = parsedUrl.pathname.split("/")[1];
            }
            else if (isIpEndpointStyle(parsedUrl)) {
                // IPv4/IPv6 address hosts... Example - http://192.0.0.10:10001/devstoreaccount1/containername
                // Single word domain without a [dot] in the endpoint... Example - http://localhost:10001/devstoreaccount1/containername
                // .getPath() -> /devstoreaccount1/containername
                containerName = parsedUrl.pathname.split("/")[2];
            }
            else {
                // "https://customdomain.com/containername".
                // .getPath() -> /containername
                containerName = parsedUrl.pathname.split("/")[1];
            }
            // decode the encoded containerName - to get all the special characters that might be present in it
            containerName = decodeURIComponent(containerName);
            if (!containerName) {
                throw new Error("Provided containerName is invalid.");
            }
            return containerName;
        }
        catch (error) {
            throw new Error("Unable to extract containerName with provided information.");
        }
    }
    /**
     * Only available for ContainerClient constructed with a shared key credential.
     *
     * Generates a Blob Container Service Shared Access Signature (SAS) URI based on the client properties
     * and parameters passed in. The SAS is signed by the shared key credential of the client.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/constructing-a-service-sas
     *
     * @param options - Optional parameters.
     * @returns The SAS URI consisting of the URI to the resource represented by this client, followed by the generated SAS token.
     */
    generateSasUrl(options) {
        return new Promise((resolve) => {
            if (!(this.credential instanceof StorageSharedKeyCredential)) {
                throw new RangeError("Can only generate the SAS when the client is initialized with a shared key credential");
            }
            const sas = generateBlobSASQueryParameters({
                containerName: this._containerName,
                ...options,
            }, this.credential).toString();
            resolve(appendToURLQuery(this.url, sas));
        });
    }
    /**
     * Only available for ContainerClient constructed with a shared key credential.
     *
     * Generates string to sign for a Blob Container Service Shared Access Signature (SAS) URI
     * based on the client properties and parameters passed in. The SAS is signed by the shared key credential of the client.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/constructing-a-service-sas
     *
     * @param options - Optional parameters.
     * @returns The SAS URI consisting of the URI to the resource represented by this client, followed by the generated SAS token.
     */
    /* eslint-disable-next-line @azure/azure-sdk/ts-naming-options*/
    generateSasStringToSign(options) {
        if (!(this.credential instanceof StorageSharedKeyCredential)) {
            throw new RangeError("Can only generate the SAS when the client is initialized with a shared key credential");
        }
        return generateBlobSASQueryParametersInternal({
            containerName: this._containerName,
            ...options,
        }, this.credential).stringToSign;
    }
    /**
     * Generates a Blob Container Service Shared Access Signature (SAS) URI based on the client properties
     * and parameters passed in. The SAS is signed by the input user delegation key.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/constructing-a-service-sas
     *
     * @param options - Optional parameters.
     * @param userDelegationKey -  Return value of `blobServiceClient.getUserDelegationKey()`
     * @returns The SAS URI consisting of the URI to the resource represented by this client, followed by the generated SAS token.
     */
    generateUserDelegationSasUrl(options, userDelegationKey) {
        return new Promise((resolve) => {
            const sas = generateBlobSASQueryParameters({
                containerName: this._containerName,
                ...options,
            }, userDelegationKey, this.accountName).toString();
            resolve(appendToURLQuery(this.url, sas));
        });
    }
    /**
     * Generates string to sign for a Blob Container Service Shared Access Signature (SAS) URI
     * based on the client properties and parameters passed in. The SAS is signed by the input user delegation key.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/constructing-a-service-sas
     *
     * @param options - Optional parameters.
     * @param userDelegationKey -  Return value of `blobServiceClient.getUserDelegationKey()`
     * @returns The SAS URI consisting of the URI to the resource represented by this client, followed by the generated SAS token.
     */
    generateUserDelegationSasStringToSign(options, userDelegationKey) {
        return generateBlobSASQueryParametersInternal({
            containerName: this._containerName,
            ...options,
        }, userDelegationKey, this.accountName).stringToSign;
    }
    /**
     * Creates a BlobBatchClient object to conduct batch operations.
     *
     * @see https://learn.microsoft.com/rest/api/storageservices/blob-batch
     *
     * @returns A new BlobBatchClient object for this container.
     */
    getBlobBatchClient() {
        return new BlobBatchClient(this.url, this.pipeline);
    }
}
//# sourceMappingURL=ContainerClient.js.map