@fileway/azure 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -152,6 +152,7 @@ function classifyAzureError(status, code) {
152
152
  case "ContainerBeingDeleted":
153
153
  return "bucket-not-found";
154
154
  case "BlobNotFound":
155
+ case "CannotVerifyCopySource":
155
156
  case "ResourceNotFound":
156
157
  return "not-found";
157
158
  case "AuthenticationFailed":
@@ -164,6 +165,8 @@ function classifyAzureError(status, code) {
164
165
  case "RequestBodyTooLarge":
165
166
  case "BlockCountExceedsLimit":
166
167
  return "size-exceeded";
168
+ case "BlobAlreadyExists":
169
+ return "conflict";
167
170
  case "ServerBusy":
168
171
  case "TooManyRequests":
169
172
  return "rate-limited";
@@ -406,12 +409,78 @@ var AzureDriver = class {
406
409
  const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\/UncommittedBlocks>/s)?.[1] ?? "";
407
410
  return uncommitted.includes("<Block>");
408
411
  }
412
+ /** One page of the blobs under `options.prefix` (List Blobs). */
413
+ async list(options) {
414
+ const res = await this.request("GET", "", {
415
+ query: {
416
+ restype: "container",
417
+ comp: "list",
418
+ prefix: options?.prefix ?? "",
419
+ maxresults: String(options?.limit ?? 100),
420
+ ...options?.recursive ? {} : { delimiter: "/" },
421
+ ...options?.cursor !== void 0 ? { marker: (0, import_core.decodeCursor)(options.cursor) } : {}
422
+ },
423
+ ...withSignal(options)
424
+ });
425
+ const body = await res.text().catch(() => "");
426
+ if (!res.ok) throw azureErrorFrom("Azure list failed", res, body);
427
+ const objects = xmlBlocks(body, "Blob").map((blob) => {
428
+ const modified = xmlValue(blob, "Last-Modified");
429
+ const etag = xmlValue(blob, "Etag");
430
+ const type = xmlValue(blob, "Content-Type");
431
+ return {
432
+ path: unescapeXml(xmlValue(blob, "Name") ?? ""),
433
+ size: Number(xmlValue(blob, "Content-Length") ?? 0),
434
+ ...modified ? { lastModified: new Date(modified) } : {},
435
+ ...etag ? { etag } : {},
436
+ ...type ? { contentType: unescapeXml(type) } : {}
437
+ };
438
+ });
439
+ const folders = xmlBlocks(body, "BlobPrefix").map((p) => unescapeXml(xmlValue(p, "Name") ?? ""));
440
+ const next = xmlValue(body, "NextMarker");
441
+ return { objects, folders, ...next ? { cursor: (0, import_core.encodeCursor)(next) } : {} };
442
+ }
443
+ /**
444
+ * Copies a blob in this container (Copy Blob), waiting for it to finish when
445
+ * the service copies in the background. With `overwrite: false` the copy is
446
+ * made only if `to` does not exist (`If-None-Match: *`), atomically.
447
+ */
448
+ async copy(from, to, options) {
449
+ const source = new URL(this.blobUrl(from));
450
+ if (this.auth.sasToken && !this.auth.accountKey) {
451
+ for (const [k, v] of new URLSearchParams(this.auth.sasToken)) source.searchParams.set(k, v);
452
+ }
453
+ const res = await this.request("PUT", to, {
454
+ headers: { "x-ms-copy-source": source.href, ...options?.overwrite ? {} : { "if-none-match": "*" } },
455
+ ...withSignal(options)
456
+ });
457
+ if (!res.ok) throw await azureError("Azure copy failed", res);
458
+ let status = res.headers.get("x-ms-copy-status");
459
+ for (let wait = 200; status === "pending"; wait = Math.min(wait * 2, 2e3)) {
460
+ await delay(wait, options?.signal);
461
+ const head = await this.request("HEAD", to, withSignal(options));
462
+ if (!head.ok) throw await azureError("Azure copy status check failed", head);
463
+ status = head.headers.get("x-ms-copy-status");
464
+ if (status === "failed" || status === "aborted") {
465
+ throw new import_core.StorageError(
466
+ "provider-error",
467
+ `Azure copy ${status}: ${head.headers.get("x-ms-copy-status-description") ?? "no details"}`,
468
+ { provider: PROVIDER }
469
+ );
470
+ }
471
+ }
472
+ }
473
+ /** Copies, then deletes the source. Blob Storage has no rename. */
474
+ async move(from, to, options) {
475
+ await this.copy(from, to, options);
476
+ await this.delete(from, options);
477
+ }
409
478
  blobUrl(path) {
410
479
  return `${this.endpoint}/${this.container}/${path.split("/").map(encodeURIComponent).join("/")}`;
411
480
  }
412
481
  /** A signed (Shared Key) or SAS-authorized request to one blob. */
413
482
  async request(method, path, init = {}) {
414
- const url = new URL(this.blobUrl(path));
483
+ const url = new URL(path === "" ? `${this.endpoint}/${this.container}` : this.blobUrl(path));
415
484
  for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);
416
485
  let headers;
417
486
  const { accountName, accountKey, sasToken } = this.auth;
@@ -433,6 +502,29 @@ var AzureDriver = class {
433
502
  });
434
503
  }
435
504
  };
505
+ function xmlBlocks(xml, tag) {
506
+ return [...xml.matchAll(new RegExp(`<${tag}>(.*?)</${tag}>`, "gs"))].map((m) => m[1] ?? "");
507
+ }
508
+ function xmlValue(xml, tag) {
509
+ return xml.match(new RegExp(`<${tag}>(.*?)</${tag}>`, "s"))?.[1] || void 0;
510
+ }
511
+ function unescapeXml(value) {
512
+ return value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n))).replace(/&#x([0-9a-f]+);/gi, (_, n) => String.fromCodePoint(parseInt(n, 16))).replace(/&amp;/g, "&");
513
+ }
514
+ function delay(ms, signal) {
515
+ return new Promise((resolve, reject) => {
516
+ if (signal?.aborted) return reject((0, import_core.abortError)("copy aborted"));
517
+ const timer = setTimeout(() => {
518
+ signal?.removeEventListener("abort", onAbort);
519
+ resolve();
520
+ }, ms);
521
+ const onAbort = () => {
522
+ clearTimeout(timer);
523
+ reject((0, import_core.abortError)("copy aborted"));
524
+ };
525
+ signal?.addEventListener("abort", onAbort, { once: true });
526
+ });
527
+ }
436
528
  function withSignal(options) {
437
529
  return options?.signal ? { signal: options.signal } : {};
438
530
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sharedkey.ts","../src/internal.ts"],"sourcesContent":["import type { BaseDriver, OperationOptions, PresignedUrlOptions, StoredObject, UploadContext, UploadOptions, UploadResult } from \"@fileway/core\";\nimport { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError } from \"@fileway/core\";\nimport { blobReadSas, bytesToBase64, signRequest, SERVICE_VERSION } from \"./sharedkey.js\";\nimport { classifyAzureError, parseConnectionString } from \"./internal.js\";\n\nconst randomUUID = () => globalThis.crypto.randomUUID();\nconst PROVIDER = \"azure\";\n\nconst DEFAULT_BLOCK_SIZE = 8 * 1024 * 1024;\nconst MIN_BLOCK_SIZE = 1024 * 1024;\n// Put Block accepts up to 4000 MiB per block.\nconst MAX_BLOCK_SIZE = 4000 * 1024 * 1024;\n// A block blob holds at most 50,000 committed blocks.\nconst MAX_BLOCKS = 50_000;\nconst PRESIGN_DEFAULT_EXPIRY = 3600;\nconst PRESIGN_MIN_EXPIRY = 1;\nconst PRESIGN_MAX_EXPIRY = 604800;\n// Metadata names must be valid C# identifiers, so no hyphens here.\nconst TOKEN_KEY = \"fileway_upload_token\";\nconst TOKEN_HEADER = `x-ms-meta-${TOKEN_KEY}`;\nconst METADATA_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n\nexport interface AzureDriverConfig {\n /** Container to store blobs in. */\n container: string;\n /** Storage account name. Required unless a connection string provides it. */\n accountName?: string;\n /** Base64 account key, used for Shared Key auth and presigned URLs. */\n accountKey?: string;\n /** A SAS token (with or without the leading `?`), used instead of an account key. */\n sasToken?: string;\n /** An Azure Storage connection string, e.g. from the portal or\n * `UseDevelopmentStorage=true` for Azurite. Explicit fields override it. */\n connectionString?: string;\n /** Blob service endpoint. Defaults to `https://<account>.blob.core.windows.net`.\n * For Azurite: `http://127.0.0.1:10000/devstoreaccount1`. */\n endpoint?: string;\n /** Public base URL for `getUrl` (e.g. a CDN). Defaults to `<endpoint>/<container>`. */\n baseUrl?: string;\n /** Block size in bytes. Default 8 MiB, clamped to 1 MiB..4000 MiB. Also the\n * largest upload sent as a single Put Blob. */\n blockSize?: number;\n /** Reject uploads larger than this many bytes with a `size-exceeded` error. */\n maxSizeBytes?: number;\n}\n\nexport type AzureMeta = {\n container: string;\n etag?: string;\n};\n\ninterface ResolvedAuth {\n accountName: string;\n accountKey?: string;\n sasToken?: string;\n}\n\nexport class AzureDriver implements BaseDriver<AzureMeta> {\n readonly name = \"azure\";\n private container: string;\n private auth: ResolvedAuth;\n private endpoint: string;\n private baseUrl: string;\n private blockSize: number;\n private maxSizeBytes: number | undefined;\n\n constructor(config: AzureDriverConfig) {\n const fromString = config.connectionString ? parseConnectionString(config.connectionString) : {};\n const accountName = config.accountName ?? fromString.accountName;\n const accountKey = config.accountKey ?? fromString.accountKey;\n const sasToken = config.sasToken ?? fromString.sasToken;\n const endpoint = config.endpoint ?? fromString.endpoint;\n\n if (!config.container) {\n throw new StorageError(\"config\", \"AzureDriver requires a container\", { provider: PROVIDER });\n }\n const name = accountName ?? (endpoint ? accountFromEndpoint(endpoint) : undefined);\n if (!name) {\n throw new StorageError(\"config\", \"AzureDriver requires accountName (or a connection string or endpoint)\", { provider: PROVIDER });\n }\n if (!accountKey && !sasToken) {\n throw new StorageError(\"config\", \"AzureDriver requires accountKey, sasToken or a connection string with credentials\", { provider: PROVIDER });\n }\n\n this.container = config.container;\n this.auth = {\n accountName: name,\n ...(accountKey ? { accountKey } : {}),\n ...(sasToken ? { sasToken: sasToken.replace(/^\\?/, \"\") } : {}),\n };\n this.endpoint = (endpoint ?? `https://${name}.blob.core.windows.net`).replace(/\\/+$/, \"\");\n this.baseUrl = (config.baseUrl ?? `${this.endpoint}/${config.container}`).replace(/\\/+$/, \"\");\n this.blockSize = Math.min(Math.max(config.blockSize ?? DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE), MAX_BLOCK_SIZE);\n this.maxSizeBytes = config.maxSizeBytes;\n }\n\n async upload(\n stream: ReadableStream<Uint8Array>,\n options: UploadOptions,\n context?: UploadContext,\n ): Promise<UploadResult<AzureMeta>> {\n validateUploadOptions(options);\n for (const key of Object.keys(options.metadata ?? {})) {\n if (!METADATA_NAME.test(key)) {\n throw new ValidationError(\n `Azure metadata keys must be letters, digits and underscores, not starting with a digit (got \"${key}\")`,\n );\n }\n }\n if (this.maxSizeBytes !== undefined && options.size !== undefined && options.size > this.maxSizeBytes) {\n throw new StorageError(\"size-exceeded\", `file size ${options.size} exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n\n const id = options.id ?? randomUUID();\n const path = this.pathFor({ ...options, id });\n const contentType = options.mimeType ?? \"application/octet-stream\";\n const metaHeaders: Record<string, string> = {\n ...Object.fromEntries(Object.entries(options.metadata ?? {}).map(([k, v]) => [`x-ms-meta-${k}`, v])),\n ...(context?.uploadToken ? { [TOKEN_HEADER]: context.uploadToken } : {}),\n };\n const signal = options.signal;\n\n // Read one block at a time, so peak memory is bounded by `blockSize`.\n const reader = stream.getReader();\n let aborted = signal?.aborted ?? false;\n const onAbort = () => {\n aborted = true;\n // Cancelling the reader wakes any pending `read()` with `done: true`.\n reader.cancel().catch(() => {});\n };\n if (signal && !aborted) signal.addEventListener(\"abort\", onAbort, { once: true });\n // Uncommitted blocks are discarded by the service after 7 days, and never\n // touch an existing committed blob, so an abort needs no server cleanup.\n const ensureNotAborted = () => {\n if (aborted) throw abortError(\"upload aborted\");\n };\n\n let buffer: Uint8Array[] = [];\n let bufferedLen = 0;\n let totalBytes = 0;\n const blockIds: string[] = [];\n\n const putBlock = async (bytes: Uint8Array) => {\n if (blockIds.length >= MAX_BLOCKS) {\n throw new StorageError(\"size-exceeded\", `upload exceeds ${MAX_BLOCKS} blocks; raise blockSize`, { provider: PROVIDER });\n }\n const blockId = bytesToBase64(new TextEncoder().encode(`fileway-${String(blockIds.length).padStart(6, \"0\")}`));\n const res = await this.request(\"PUT\", path, {\n query: { comp: \"block\", blockid: blockId },\n body: bytes,\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(`Azure put block ${blockIds.length + 1} failed`, res);\n blockIds.push(blockId);\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n ensureNotAborted();\n if (done) break;\n totalBytes += value.byteLength;\n if (this.maxSizeBytes !== undefined && totalBytes > this.maxSizeBytes) {\n await reader.cancel().catch(() => {});\n throw new StorageError(\"size-exceeded\", `upload exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n buffer.push(value);\n bufferedLen += value.byteLength;\n\n while (bufferedLen >= this.blockSize) {\n const merged = concatBytes(buffer, bufferedLen);\n await putBlock(merged.subarray(0, this.blockSize));\n const rest = merged.subarray(this.blockSize);\n buffer = rest.byteLength > 0 ? [rest] : [];\n bufferedLen = rest.byteLength;\n }\n }\n\n if (options.size !== undefined && totalBytes !== options.size) {\n throw new ValidationError(`stream delivered ${totalBytes} bytes, expected ${options.size}`);\n }\n\n let res: Response;\n if (blockIds.length === 0) {\n // Small file: one Put Blob carries content, type and metadata.\n res = await this.request(\"PUT\", path, {\n headers: { \"x-ms-blob-type\": \"BlockBlob\", \"content-type\": contentType, ...metaHeaders },\n body: concatBytes(buffer, bufferedLen),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure upload failed\", res);\n } else {\n if (bufferedLen > 0) await putBlock(concatBytes(buffer, bufferedLen));\n ensureNotAborted();\n const xml = `<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList>${blockIds\n .map((b) => `<Latest>${b}</Latest>`)\n .join(\"\")}</BlockList>`;\n res = await this.request(\"PUT\", path, {\n query: { comp: \"blocklist\" },\n headers: { \"content-type\": \"application/xml\", \"x-ms-blob-content-type\": contentType, ...metaHeaders },\n body: new TextEncoder().encode(xml),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure put block list failed\", res);\n }\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n return {\n id,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: totalBytes,\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n };\n } catch (err) {\n reader.cancel().catch(() => {});\n throw err;\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n /** Deletes the blob; a missing blob is not an error. */\n async delete(path: string, options?: OperationOptions): Promise<void> {\n const res = await this.request(\"DELETE\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return;\n if (!res.ok) throw await azureError(\"Azure delete failed\", res);\n }\n\n async getUrl(path: string, _options?: OperationOptions): Promise<string> {\n return `${this.baseUrl}/${urlEncodePath(path)}`;\n }\n\n /** Streams the blob. `options.signal` also cancels reading the body. */\n async get(path: string, options?: OperationOptions): Promise<ReadableStream<Uint8Array>> {\n const res = await this.request(\"GET\", path, withSignal(options));\n if (!res.ok) throw await azureError(\"Azure get failed\", res);\n if (!res.body) {\n throw new StorageError(\"provider-error\", \"Azure get returned no response body\", { provider: PROVIDER });\n }\n return res.body;\n }\n\n /**\n * A read-only service SAS URL for `path`, valid for `expiresIn` seconds\n * (integer in 1..604800, default 3600). Needs an account key.\n */\n async getPresignedUrl(path: string, options?: PresignedUrlOptions): Promise<string> {\n const expiresIn = options?.expiresIn ?? PRESIGN_DEFAULT_EXPIRY;\n if (!Number.isInteger(expiresIn) || expiresIn < PRESIGN_MIN_EXPIRY || expiresIn > PRESIGN_MAX_EXPIRY) {\n throw new ValidationError(\n `expiresIn must be an integer between ${PRESIGN_MIN_EXPIRY} and ${PRESIGN_MAX_EXPIRY} seconds`,\n );\n }\n const { accountName, accountKey } = this.auth;\n if (!accountKey) {\n throw new StorageError(\"config\", \"AzureDriver needs accountKey to create presigned URLs\", { provider: PROVIDER });\n }\n const sas = await blobReadSas(accountName, accountKey, this.container, path, new Date(Date.now() + expiresIn * 1000));\n return `${this.blobUrl(path)}?${sas}`;\n }\n\n /** The blob name an upload with `options.id` is stored under: `<path>/<id>.<ext>`. */\n pathFor(options: UploadOptions & { id: string }): string {\n const ext = options.filename.includes(\".\") ? options.filename.split(\".\").pop()! : \"\";\n const filename = ext ? `${options.id}.${ext}` : options.id;\n return options.path ? `${options.path}/${filename}` : filename;\n }\n\n /** The blob at `path` (a `HEAD` request), or `null` if it does not exist. */\n async head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null> {\n const res = await this.request(\"HEAD\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return null;\n if (!res.ok) throw await azureError(\"Azure head failed\", res);\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n const token = res.headers.get(TOKEN_HEADER) ?? undefined;\n const modified = res.headers.get(\"last-modified\");\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n const name = path.split(\"/\").pop() ?? path;\n return {\n id: name.includes(\".\") ? name.slice(0, name.lastIndexOf(\".\")) : name,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: Number(res.headers.get(\"content-length\") ?? 0),\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n ...(token ? { uploadToken: token } : {}),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(contentType ? { contentType } : {}),\n };\n }\n\n /** Whether blocks have been staged for `path` but not yet committed. */\n async isPending(path: string, options?: OperationOptions): Promise<boolean> {\n const res = await this.request(\"GET\", path, {\n query: { comp: \"blocklist\", blocklisttype: \"uncommitted\" },\n ...withSignal(options),\n });\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return false;\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure get block list failed\", res, body);\n const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\\/UncommittedBlocks>/s)?.[1] ?? \"\";\n return uncommitted.includes(\"<Block>\");\n }\n\n private blobUrl(path: string): string {\n return `${this.endpoint}/${this.container}/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n }\n\n /** A signed (Shared Key) or SAS-authorized request to one blob. */\n private async request(\n method: string,\n path: string,\n init: {\n query?: Record<string, string>;\n headers?: Record<string, string>;\n body?: Uint8Array;\n signal?: AbortSignal;\n } = {},\n ): Promise<Response> {\n const url = new URL(this.blobUrl(path));\n for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);\n\n let headers: Record<string, string>;\n const { accountName, accountKey, sasToken } = this.auth;\n if (accountKey) {\n headers = await signRequest(method, url, accountName, accountKey, init.headers ?? {}, init.body?.byteLength);\n } else {\n for (const [k, v] of new URLSearchParams(sasToken)) url.searchParams.set(k, v);\n headers = {\n ...init.headers,\n \"x-ms-date\": new Date().toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n }\n\n return fetchWithAbort(url, {\n method,\n headers,\n ...(init.body ? { body: init.body as BodyInit } : {}),\n signal: init.signal ?? null,\n });\n }\n}\n\nfunction withSignal(options: OperationOptions | undefined): { signal?: AbortSignal } {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/** The account name from a `<account>.blob.<suffix>` host or an Azurite-style\n * `http://host:port/<account>` endpoint. */\nfunction accountFromEndpoint(endpoint: string): string | undefined {\n const url = new URL(endpoint);\n const fromHost = url.hostname.match(/^([a-z0-9]+)\\.blob\\./)?.[1];\n return fromHost ?? (url.pathname.split(\"/\").filter(Boolean)[0] || undefined);\n}\n\n/** `fetch` that converts non-abort failures into a typed `network` error,\n * while letting aborts (`AbortError`) pass through untouched. */\nasync function fetchWithAbort(url: string | URL, init: RequestInit): Promise<Response> {\n try {\n return await fetch(url, init);\n } catch (err) {\n if (isAbortError(err)) throw err;\n const cause = err instanceof Error ? err : new Error(String(err));\n throw new StorageError(\"network\", `${PROVIDER} network error: ${cause.message}`, { provider: PROVIDER, cause });\n }\n}\n\n/** The service error code: the `x-ms-error-code` header, which Azure sends on\n * every failure (including bodiless HEAD responses), or the XML `<Code>`. */\nfunction errorCode(res: Response, body?: string): string | undefined {\n return res.headers.get(\"x-ms-error-code\") ?? body?.match(/<Code>(.*?)<\\/Code>/s)?.[1];\n}\n\nfunction azureErrorFrom(prefix: string, res: Response, body: string): StorageError {\n const code = errorCode(res, body);\n return new StorageError(\n classifyAzureError(res.status, code),\n `${prefix}: ${res.status} ${res.statusText}${code ? ` (${code})` : \"\"}`,\n { statusCode: res.status, provider: PROVIDER },\n );\n}\n\nasync function azureError(prefix: string, res: Response): Promise<StorageError> {\n const body = await res.text().catch(() => \"\");\n return azureErrorFrom(prefix, res, body);\n}\n\nfunction concatBytes(chunks: Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n","// Azure Storage Shared Key and service SAS signing, using only Web Crypto.\n// https://learn.microsoft.com/rest/api/storageservices/authorize-with-shared-key\n// https://learn.microsoft.com/rest/api/storageservices/create-service-sas\n\nexport const SERVICE_VERSION = \"2023-11-03\";\n\nconst encoder = new TextEncoder();\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction importKey(accountKey: string): Promise<CryptoKey> {\n let key = keyCache.get(accountKey);\n if (!key) {\n key = globalThis.crypto.subtle.importKey(\n \"raw\",\n base64ToBytes(accountKey),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n keyCache.set(accountKey, key);\n }\n return key;\n}\n\n/** Base64 HMAC-SHA256 of `message` with the base64-encoded account key. */\nexport async function hmacBase64(accountKey: string, message: string): Promise<string> {\n const key = await importKey(accountKey);\n const sig = await globalThis.crypto.subtle.sign(\"HMAC\", key, encoder.encode(message));\n return bytesToBase64(new Uint8Array(sig));\n}\n\n/**\n * The Shared Key string-to-sign for a request. `headers` are the request\n * headers (any case); `contentLength` is the body size in bytes (0 or\n * undefined signs as an empty value, as the service requires since 2015-02-21).\n */\nexport function buildStringToSign(\n method: string,\n url: URL,\n accountName: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n): string {\n const lower: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n const h = (name: string) => lower[name] ?? \"\";\n\n const canonicalHeaders = Object.keys(lower)\n .filter((k) => k.startsWith(\"x-ms-\"))\n .sort()\n .map((k) => `${k}:${(lower[k] ?? \"\").replace(/\\s+/g, \" \").trim()}\\n`)\n .join(\"\");\n\n // Query parameters: lowercase names, sorted, decoded values joined by commas.\n const params = new Map<string, string[]>();\n for (const [k, v] of url.searchParams) {\n const name = k.toLowerCase();\n params.set(name, [...(params.get(name) ?? []), v]);\n }\n const canonicalQuery = [...params.keys()]\n .sort()\n .map((k) => `\\n${k}:${(params.get(k) ?? []).sort().join(\",\")}`)\n .join(\"\");\n const canonicalResource = `/${accountName}${url.pathname}${canonicalQuery}`;\n\n return [\n method.toUpperCase(),\n h(\"content-encoding\"),\n h(\"content-language\"),\n contentLength ? String(contentLength) : \"\",\n h(\"content-md5\"),\n h(\"content-type\"),\n h(\"date\"),\n h(\"if-modified-since\"),\n h(\"if-match\"),\n h(\"if-none-match\"),\n h(\"if-unmodified-since\"),\n h(\"range\"),\n canonicalHeaders + canonicalResource,\n ].join(\"\\n\");\n}\n\n/** `x-ms-date`, `x-ms-version` and `Authorization: SharedKey` headers for a request. */\nexport async function signRequest(\n method: string,\n url: URL,\n accountName: string,\n accountKey: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n now: Date = new Date(),\n): Promise<Record<string, string>> {\n const signed = {\n ...headers,\n \"x-ms-date\": now.toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n const stringToSign = buildStringToSign(method, url, accountName, signed, contentLength);\n const signature = await hmacBase64(accountKey, stringToSign);\n return { ...signed, authorization: `SharedKey ${accountName}:${signature}` };\n}\n\n/**\n * Query string for a read-only service SAS on one blob. `blobName` is the raw\n * (unencoded) blob name; `expiry` is when the link stops working.\n */\nexport async function blobReadSas(\n accountName: string,\n accountKey: string,\n container: string,\n blobName: string,\n expiry: Date,\n): Promise<string> {\n const se = expiry.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n const fields = {\n sp: \"r\",\n st: \"\",\n se,\n resource: `/blob/${accountName}/${container}/${blobName}`,\n si: \"\",\n sip: \"\",\n spr: \"\",\n sv: SERVICE_VERSION,\n sr: \"b\",\n snapshot: \"\",\n ses: \"\",\n rscc: \"\",\n rscd: \"\",\n rsce: \"\",\n rscl: \"\",\n rsct: \"\",\n };\n const signature = await hmacBase64(accountKey, Object.values(fields).join(\"\\n\"));\n const query = new URLSearchParams({ sv: SERVICE_VERSION, se, sr: \"b\", sp: \"r\", sig: signature });\n return query.toString();\n}\n\nexport function base64ToBytes(value: string) {\n const binary = atob(value);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n","import type { StorageErrorCode } from \"@fileway/core\";\n\n// Internal: connection-string parsing and error classification for\n// AzureDriver. Not exported from the package.\n\n// Azurite's well-known development account (public, documented by Microsoft).\nconst DEV_ACCOUNT = \"devstoreaccount1\";\nconst DEV_KEY = \"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==\";\nconst DEV_ENDPOINT = \"http://127.0.0.1:10000/devstoreaccount1\";\n\n/** Splits a connection string into the fields this driver understands. */\nexport function parseConnectionString(value: string): {\n accountName?: string;\n accountKey?: string;\n sasToken?: string;\n endpoint?: string;\n} {\n const parts: Record<string, string> = {};\n for (const segment of value.split(\";\")) {\n const eq = segment.indexOf(\"=\");\n if (eq > 0) parts[segment.slice(0, eq).trim()] = segment.slice(eq + 1).trim();\n }\n if (parts.UseDevelopmentStorage === \"true\") {\n return { accountName: DEV_ACCOUNT, accountKey: DEV_KEY, endpoint: DEV_ENDPOINT };\n }\n const accountName = parts.AccountName;\n const endpoint =\n parts.BlobEndpoint ??\n (accountName\n ? `${parts.DefaultEndpointsProtocol ?? \"https\"}://${accountName}.blob.${parts.EndpointSuffix ?? \"core.windows.net\"}`\n : undefined);\n return {\n ...(accountName ? { accountName } : {}),\n ...(parts.AccountKey ? { accountKey: parts.AccountKey } : {}),\n ...(parts.SharedAccessSignature ? { sasToken: parts.SharedAccessSignature } : {}),\n ...(endpoint ? { endpoint } : {}),\n };\n}\n\n/** Classifies an Azure failure into a typed code from the service error code,\n * falling back to the HTTP status. */\nexport function classifyAzureError(status: number, code: string | undefined): StorageErrorCode {\n switch (code) {\n case \"ContainerNotFound\":\n case \"ContainerBeingDeleted\":\n return \"bucket-not-found\";\n case \"BlobNotFound\":\n case \"ResourceNotFound\":\n return \"not-found\";\n case \"AuthenticationFailed\":\n case \"AuthorizationFailure\":\n case \"AuthorizationPermissionMismatch\":\n case \"AuthorizationResourceTypeMismatch\":\n case \"NoAuthenticationInformation\":\n case \"InvalidAuthenticationInfo\":\n return \"auth-failed\";\n case \"RequestBodyTooLarge\":\n case \"BlockCountExceedsLimit\":\n return \"size-exceeded\";\n case \"ServerBusy\":\n case \"TooManyRequests\":\n return \"rate-limited\";\n }\n if (status === 429) return \"rate-limited\";\n if (status === 401 || status === 403) return \"auth-failed\";\n if (status === 404) return \"not-found\";\n if (status === 413) return \"size-exceeded\";\n return \"provider-error\";\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sharedkey.ts","../src/internal.ts"],"sourcesContent":["import type { BaseDriver, CopyOptions, ListedObject, ListOptions, ListPage, OperationOptions, PresignedUrlOptions, StoredObject, UploadContext, UploadOptions, UploadResult } from \"@fileway/core\";\nimport { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError, encodeCursor, decodeCursor } from \"@fileway/core\";\nimport { blobReadSas, bytesToBase64, signRequest, SERVICE_VERSION } from \"./sharedkey.js\";\nimport { classifyAzureError, parseConnectionString } from \"./internal.js\";\n\nconst randomUUID = () => globalThis.crypto.randomUUID();\nconst PROVIDER = \"azure\";\n\nconst DEFAULT_BLOCK_SIZE = 8 * 1024 * 1024;\nconst MIN_BLOCK_SIZE = 1024 * 1024;\n// Put Block accepts up to 4000 MiB per block.\nconst MAX_BLOCK_SIZE = 4000 * 1024 * 1024;\n// A block blob holds at most 50,000 committed blocks.\nconst MAX_BLOCKS = 50_000;\nconst PRESIGN_DEFAULT_EXPIRY = 3600;\nconst PRESIGN_MIN_EXPIRY = 1;\nconst PRESIGN_MAX_EXPIRY = 604800;\n// Metadata names must be valid C# identifiers, so no hyphens here.\nconst TOKEN_KEY = \"fileway_upload_token\";\nconst TOKEN_HEADER = `x-ms-meta-${TOKEN_KEY}`;\nconst METADATA_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n\nexport interface AzureDriverConfig {\n /** Container to store blobs in. */\n container: string;\n /** Storage account name. Required unless a connection string provides it. */\n accountName?: string;\n /** Base64 account key, used for Shared Key auth and presigned URLs. */\n accountKey?: string;\n /** A SAS token (with or without the leading `?`), used instead of an account key. */\n sasToken?: string;\n /** An Azure Storage connection string, e.g. from the portal or\n * `UseDevelopmentStorage=true` for Azurite. Explicit fields override it. */\n connectionString?: string;\n /** Blob service endpoint. Defaults to `https://<account>.blob.core.windows.net`.\n * For Azurite: `http://127.0.0.1:10000/devstoreaccount1`. */\n endpoint?: string;\n /** Public base URL for `getUrl` (e.g. a CDN). Defaults to `<endpoint>/<container>`. */\n baseUrl?: string;\n /** Block size in bytes. Default 8 MiB, clamped to 1 MiB..4000 MiB. Also the\n * largest upload sent as a single Put Blob. */\n blockSize?: number;\n /** Reject uploads larger than this many bytes with a `size-exceeded` error. */\n maxSizeBytes?: number;\n}\n\nexport type AzureMeta = {\n container: string;\n etag?: string;\n};\n\ninterface ResolvedAuth {\n accountName: string;\n accountKey?: string;\n sasToken?: string;\n}\n\nexport class AzureDriver implements BaseDriver<AzureMeta> {\n readonly name = \"azure\";\n private container: string;\n private auth: ResolvedAuth;\n private endpoint: string;\n private baseUrl: string;\n private blockSize: number;\n private maxSizeBytes: number | undefined;\n\n constructor(config: AzureDriverConfig) {\n const fromString = config.connectionString ? parseConnectionString(config.connectionString) : {};\n const accountName = config.accountName ?? fromString.accountName;\n const accountKey = config.accountKey ?? fromString.accountKey;\n const sasToken = config.sasToken ?? fromString.sasToken;\n const endpoint = config.endpoint ?? fromString.endpoint;\n\n if (!config.container) {\n throw new StorageError(\"config\", \"AzureDriver requires a container\", { provider: PROVIDER });\n }\n const name = accountName ?? (endpoint ? accountFromEndpoint(endpoint) : undefined);\n if (!name) {\n throw new StorageError(\"config\", \"AzureDriver requires accountName (or a connection string or endpoint)\", { provider: PROVIDER });\n }\n if (!accountKey && !sasToken) {\n throw new StorageError(\"config\", \"AzureDriver requires accountKey, sasToken or a connection string with credentials\", { provider: PROVIDER });\n }\n\n this.container = config.container;\n this.auth = {\n accountName: name,\n ...(accountKey ? { accountKey } : {}),\n ...(sasToken ? { sasToken: sasToken.replace(/^\\?/, \"\") } : {}),\n };\n this.endpoint = (endpoint ?? `https://${name}.blob.core.windows.net`).replace(/\\/+$/, \"\");\n this.baseUrl = (config.baseUrl ?? `${this.endpoint}/${config.container}`).replace(/\\/+$/, \"\");\n this.blockSize = Math.min(Math.max(config.blockSize ?? DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE), MAX_BLOCK_SIZE);\n this.maxSizeBytes = config.maxSizeBytes;\n }\n\n async upload(\n stream: ReadableStream<Uint8Array>,\n options: UploadOptions,\n context?: UploadContext,\n ): Promise<UploadResult<AzureMeta>> {\n validateUploadOptions(options);\n for (const key of Object.keys(options.metadata ?? {})) {\n if (!METADATA_NAME.test(key)) {\n throw new ValidationError(\n `Azure metadata keys must be letters, digits and underscores, not starting with a digit (got \"${key}\")`,\n );\n }\n }\n if (this.maxSizeBytes !== undefined && options.size !== undefined && options.size > this.maxSizeBytes) {\n throw new StorageError(\"size-exceeded\", `file size ${options.size} exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n\n const id = options.id ?? randomUUID();\n const path = this.pathFor({ ...options, id });\n const contentType = options.mimeType ?? \"application/octet-stream\";\n const metaHeaders: Record<string, string> = {\n ...Object.fromEntries(Object.entries(options.metadata ?? {}).map(([k, v]) => [`x-ms-meta-${k}`, v])),\n ...(context?.uploadToken ? { [TOKEN_HEADER]: context.uploadToken } : {}),\n };\n const signal = options.signal;\n\n // Read one block at a time, so peak memory is bounded by `blockSize`.\n const reader = stream.getReader();\n let aborted = signal?.aborted ?? false;\n const onAbort = () => {\n aborted = true;\n // Cancelling the reader wakes any pending `read()` with `done: true`.\n reader.cancel().catch(() => {});\n };\n if (signal && !aborted) signal.addEventListener(\"abort\", onAbort, { once: true });\n // Uncommitted blocks are discarded by the service after 7 days, and never\n // touch an existing committed blob, so an abort needs no server cleanup.\n const ensureNotAborted = () => {\n if (aborted) throw abortError(\"upload aborted\");\n };\n\n let buffer: Uint8Array[] = [];\n let bufferedLen = 0;\n let totalBytes = 0;\n const blockIds: string[] = [];\n\n const putBlock = async (bytes: Uint8Array) => {\n if (blockIds.length >= MAX_BLOCKS) {\n throw new StorageError(\"size-exceeded\", `upload exceeds ${MAX_BLOCKS} blocks; raise blockSize`, { provider: PROVIDER });\n }\n const blockId = bytesToBase64(new TextEncoder().encode(`fileway-${String(blockIds.length).padStart(6, \"0\")}`));\n const res = await this.request(\"PUT\", path, {\n query: { comp: \"block\", blockid: blockId },\n body: bytes,\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(`Azure put block ${blockIds.length + 1} failed`, res);\n blockIds.push(blockId);\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n ensureNotAborted();\n if (done) break;\n totalBytes += value.byteLength;\n if (this.maxSizeBytes !== undefined && totalBytes > this.maxSizeBytes) {\n await reader.cancel().catch(() => {});\n throw new StorageError(\"size-exceeded\", `upload exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n buffer.push(value);\n bufferedLen += value.byteLength;\n\n while (bufferedLen >= this.blockSize) {\n const merged = concatBytes(buffer, bufferedLen);\n await putBlock(merged.subarray(0, this.blockSize));\n const rest = merged.subarray(this.blockSize);\n buffer = rest.byteLength > 0 ? [rest] : [];\n bufferedLen = rest.byteLength;\n }\n }\n\n if (options.size !== undefined && totalBytes !== options.size) {\n throw new ValidationError(`stream delivered ${totalBytes} bytes, expected ${options.size}`);\n }\n\n let res: Response;\n if (blockIds.length === 0) {\n // Small file: one Put Blob carries content, type and metadata.\n res = await this.request(\"PUT\", path, {\n headers: { \"x-ms-blob-type\": \"BlockBlob\", \"content-type\": contentType, ...metaHeaders },\n body: concatBytes(buffer, bufferedLen),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure upload failed\", res);\n } else {\n if (bufferedLen > 0) await putBlock(concatBytes(buffer, bufferedLen));\n ensureNotAborted();\n const xml = `<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList>${blockIds\n .map((b) => `<Latest>${b}</Latest>`)\n .join(\"\")}</BlockList>`;\n res = await this.request(\"PUT\", path, {\n query: { comp: \"blocklist\" },\n headers: { \"content-type\": \"application/xml\", \"x-ms-blob-content-type\": contentType, ...metaHeaders },\n body: new TextEncoder().encode(xml),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure put block list failed\", res);\n }\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n return {\n id,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: totalBytes,\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n };\n } catch (err) {\n reader.cancel().catch(() => {});\n throw err;\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n /** Deletes the blob; a missing blob is not an error. */\n async delete(path: string, options?: OperationOptions): Promise<void> {\n const res = await this.request(\"DELETE\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return;\n if (!res.ok) throw await azureError(\"Azure delete failed\", res);\n }\n\n async getUrl(path: string, _options?: OperationOptions): Promise<string> {\n return `${this.baseUrl}/${urlEncodePath(path)}`;\n }\n\n /** Streams the blob. `options.signal` also cancels reading the body. */\n async get(path: string, options?: OperationOptions): Promise<ReadableStream<Uint8Array>> {\n const res = await this.request(\"GET\", path, withSignal(options));\n if (!res.ok) throw await azureError(\"Azure get failed\", res);\n if (!res.body) {\n throw new StorageError(\"provider-error\", \"Azure get returned no response body\", { provider: PROVIDER });\n }\n return res.body;\n }\n\n /**\n * A read-only service SAS URL for `path`, valid for `expiresIn` seconds\n * (integer in 1..604800, default 3600). Needs an account key.\n */\n async getPresignedUrl(path: string, options?: PresignedUrlOptions): Promise<string> {\n const expiresIn = options?.expiresIn ?? PRESIGN_DEFAULT_EXPIRY;\n if (!Number.isInteger(expiresIn) || expiresIn < PRESIGN_MIN_EXPIRY || expiresIn > PRESIGN_MAX_EXPIRY) {\n throw new ValidationError(\n `expiresIn must be an integer between ${PRESIGN_MIN_EXPIRY} and ${PRESIGN_MAX_EXPIRY} seconds`,\n );\n }\n const { accountName, accountKey } = this.auth;\n if (!accountKey) {\n throw new StorageError(\"config\", \"AzureDriver needs accountKey to create presigned URLs\", { provider: PROVIDER });\n }\n const sas = await blobReadSas(accountName, accountKey, this.container, path, new Date(Date.now() + expiresIn * 1000));\n return `${this.blobUrl(path)}?${sas}`;\n }\n\n /** The blob name an upload with `options.id` is stored under: `<path>/<id>.<ext>`. */\n pathFor(options: UploadOptions & { id: string }): string {\n const ext = options.filename.includes(\".\") ? options.filename.split(\".\").pop()! : \"\";\n const filename = ext ? `${options.id}.${ext}` : options.id;\n return options.path ? `${options.path}/${filename}` : filename;\n }\n\n /** The blob at `path` (a `HEAD` request), or `null` if it does not exist. */\n async head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null> {\n const res = await this.request(\"HEAD\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return null;\n if (!res.ok) throw await azureError(\"Azure head failed\", res);\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n const token = res.headers.get(TOKEN_HEADER) ?? undefined;\n const modified = res.headers.get(\"last-modified\");\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n const name = path.split(\"/\").pop() ?? path;\n return {\n id: name.includes(\".\") ? name.slice(0, name.lastIndexOf(\".\")) : name,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: Number(res.headers.get(\"content-length\") ?? 0),\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n ...(token ? { uploadToken: token } : {}),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(contentType ? { contentType } : {}),\n };\n }\n\n /** Whether blocks have been staged for `path` but not yet committed. */\n async isPending(path: string, options?: OperationOptions): Promise<boolean> {\n const res = await this.request(\"GET\", path, {\n query: { comp: \"blocklist\", blocklisttype: \"uncommitted\" },\n ...withSignal(options),\n });\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return false;\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure get block list failed\", res, body);\n const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\\/UncommittedBlocks>/s)?.[1] ?? \"\";\n return uncommitted.includes(\"<Block>\");\n }\n\n /** One page of the blobs under `options.prefix` (List Blobs). */\n async list(options?: ListOptions): Promise<ListPage> {\n const res = await this.request(\"GET\", \"\", {\n query: {\n restype: \"container\",\n comp: \"list\",\n prefix: options?.prefix ?? \"\",\n maxresults: String(options?.limit ?? 100),\n ...(options?.recursive ? {} : { delimiter: \"/\" }),\n ...(options?.cursor !== undefined ? { marker: decodeCursor<string>(options.cursor) } : {}),\n },\n ...withSignal(options),\n });\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure list failed\", res, body);\n\n const objects: ListedObject[] = xmlBlocks(body, \"Blob\").map((blob) => {\n const modified = xmlValue(blob, \"Last-Modified\");\n const etag = xmlValue(blob, \"Etag\");\n const type = xmlValue(blob, \"Content-Type\");\n return {\n path: unescapeXml(xmlValue(blob, \"Name\") ?? \"\"),\n size: Number(xmlValue(blob, \"Content-Length\") ?? 0),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(etag ? { etag } : {}),\n ...(type ? { contentType: unescapeXml(type) } : {}),\n };\n });\n const folders = xmlBlocks(body, \"BlobPrefix\").map((p) => unescapeXml(xmlValue(p, \"Name\") ?? \"\"));\n const next = xmlValue(body, \"NextMarker\");\n return { objects, folders, ...(next ? { cursor: encodeCursor(next) } : {}) };\n }\n\n /**\n * Copies a blob in this container (Copy Blob), waiting for it to finish when\n * the service copies in the background. With `overwrite: false` the copy is\n * made only if `to` does not exist (`If-None-Match: *`), atomically.\n */\n async copy(from: string, to: string, options?: CopyOptions): Promise<void> {\n const source = new URL(this.blobUrl(from));\n if (this.auth.sasToken && !this.auth.accountKey) {\n for (const [k, v] of new URLSearchParams(this.auth.sasToken)) source.searchParams.set(k, v);\n }\n const res = await this.request(\"PUT\", to, {\n headers: { \"x-ms-copy-source\": source.href, ...(options?.overwrite ? {} : { \"if-none-match\": \"*\" }) },\n ...withSignal(options),\n });\n if (!res.ok) throw await azureError(\"Azure copy failed\", res);\n\n let status = res.headers.get(\"x-ms-copy-status\");\n for (let wait = 200; status === \"pending\"; wait = Math.min(wait * 2, 2000)) {\n await delay(wait, options?.signal);\n const head = await this.request(\"HEAD\", to, withSignal(options));\n if (!head.ok) throw await azureError(\"Azure copy status check failed\", head);\n status = head.headers.get(\"x-ms-copy-status\");\n if (status === \"failed\" || status === \"aborted\") {\n throw new StorageError(\n \"provider-error\",\n `Azure copy ${status}: ${head.headers.get(\"x-ms-copy-status-description\") ?? \"no details\"}`,\n { provider: PROVIDER },\n );\n }\n }\n }\n\n /** Copies, then deletes the source. Blob Storage has no rename. */\n async move(from: string, to: string, options?: CopyOptions): Promise<void> {\n await this.copy(from, to, options);\n await this.delete(from, options);\n }\n\n private blobUrl(path: string): string {\n return `${this.endpoint}/${this.container}/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n }\n\n /** A signed (Shared Key) or SAS-authorized request to one blob. */\n private async request(\n method: string,\n path: string,\n init: {\n query?: Record<string, string>;\n headers?: Record<string, string>;\n body?: Uint8Array;\n signal?: AbortSignal;\n } = {},\n ): Promise<Response> {\n // An empty path addresses the container itself (e.g. List Blobs).\n const url = new URL(path === \"\" ? `${this.endpoint}/${this.container}` : this.blobUrl(path));\n for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);\n\n let headers: Record<string, string>;\n const { accountName, accountKey, sasToken } = this.auth;\n if (accountKey) {\n headers = await signRequest(method, url, accountName, accountKey, init.headers ?? {}, init.body?.byteLength);\n } else {\n for (const [k, v] of new URLSearchParams(sasToken)) url.searchParams.set(k, v);\n headers = {\n ...init.headers,\n \"x-ms-date\": new Date().toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n }\n\n return fetchWithAbort(url, {\n method,\n headers,\n ...(init.body ? { body: init.body as BodyInit } : {}),\n signal: init.signal ?? null,\n });\n }\n}\n\n/** The inner XML of every `<tag>...</tag>` block. */\nfunction xmlBlocks(xml: string, tag: string): string[] {\n return [...xml.matchAll(new RegExp(`<${tag}>(.*?)</${tag}>`, \"gs\"))].map((m) => m[1] ?? \"\");\n}\n\nfunction xmlValue(xml: string, tag: string): string | undefined {\n return xml.match(new RegExp(`<${tag}>(.*?)</${tag}>`, \"s\"))?.[1] || undefined;\n}\n\nfunction unescapeXml(value: string): string {\n return value\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&#(\\d+);/g, (_, n: string) => String.fromCodePoint(Number(n)))\n .replace(/&#x([0-9a-f]+);/gi, (_, n: string) => String.fromCodePoint(parseInt(n, 16)))\n .replace(/&amp;/g, \"&\");\n}\n\n/** Waits `ms`, or rejects with an `AbortError` when `signal` aborts first. */\nfunction delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(abortError(\"copy aborted\"));\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(\"copy aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction withSignal(options: OperationOptions | undefined): { signal?: AbortSignal } {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/** The account name from a `<account>.blob.<suffix>` host or an Azurite-style\n * `http://host:port/<account>` endpoint. */\nfunction accountFromEndpoint(endpoint: string): string | undefined {\n const url = new URL(endpoint);\n const fromHost = url.hostname.match(/^([a-z0-9]+)\\.blob\\./)?.[1];\n return fromHost ?? (url.pathname.split(\"/\").filter(Boolean)[0] || undefined);\n}\n\n/** `fetch` that converts non-abort failures into a typed `network` error,\n * while letting aborts (`AbortError`) pass through untouched. */\nasync function fetchWithAbort(url: string | URL, init: RequestInit): Promise<Response> {\n try {\n return await fetch(url, init);\n } catch (err) {\n if (isAbortError(err)) throw err;\n const cause = err instanceof Error ? err : new Error(String(err));\n throw new StorageError(\"network\", `${PROVIDER} network error: ${cause.message}`, { provider: PROVIDER, cause });\n }\n}\n\n/** The service error code: the `x-ms-error-code` header, which Azure sends on\n * every failure (including bodiless HEAD responses), or the XML `<Code>`. */\nfunction errorCode(res: Response, body?: string): string | undefined {\n return res.headers.get(\"x-ms-error-code\") ?? body?.match(/<Code>(.*?)<\\/Code>/s)?.[1];\n}\n\nfunction azureErrorFrom(prefix: string, res: Response, body: string): StorageError {\n const code = errorCode(res, body);\n return new StorageError(\n classifyAzureError(res.status, code),\n `${prefix}: ${res.status} ${res.statusText}${code ? ` (${code})` : \"\"}`,\n { statusCode: res.status, provider: PROVIDER },\n );\n}\n\nasync function azureError(prefix: string, res: Response): Promise<StorageError> {\n const body = await res.text().catch(() => \"\");\n return azureErrorFrom(prefix, res, body);\n}\n\nfunction concatBytes(chunks: Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n","// Azure Storage Shared Key and service SAS signing, using only Web Crypto.\n// https://learn.microsoft.com/rest/api/storageservices/authorize-with-shared-key\n// https://learn.microsoft.com/rest/api/storageservices/create-service-sas\n\nexport const SERVICE_VERSION = \"2023-11-03\";\n\nconst encoder = new TextEncoder();\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction importKey(accountKey: string): Promise<CryptoKey> {\n let key = keyCache.get(accountKey);\n if (!key) {\n key = globalThis.crypto.subtle.importKey(\n \"raw\",\n base64ToBytes(accountKey),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n keyCache.set(accountKey, key);\n }\n return key;\n}\n\n/** Base64 HMAC-SHA256 of `message` with the base64-encoded account key. */\nexport async function hmacBase64(accountKey: string, message: string): Promise<string> {\n const key = await importKey(accountKey);\n const sig = await globalThis.crypto.subtle.sign(\"HMAC\", key, encoder.encode(message));\n return bytesToBase64(new Uint8Array(sig));\n}\n\n/**\n * The Shared Key string-to-sign for a request. `headers` are the request\n * headers (any case); `contentLength` is the body size in bytes (0 or\n * undefined signs as an empty value, as the service requires since 2015-02-21).\n */\nexport function buildStringToSign(\n method: string,\n url: URL,\n accountName: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n): string {\n const lower: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n const h = (name: string) => lower[name] ?? \"\";\n\n const canonicalHeaders = Object.keys(lower)\n .filter((k) => k.startsWith(\"x-ms-\"))\n .sort()\n .map((k) => `${k}:${(lower[k] ?? \"\").replace(/\\s+/g, \" \").trim()}\\n`)\n .join(\"\");\n\n // Query parameters: lowercase names, sorted, decoded values joined by commas.\n const params = new Map<string, string[]>();\n for (const [k, v] of url.searchParams) {\n const name = k.toLowerCase();\n params.set(name, [...(params.get(name) ?? []), v]);\n }\n const canonicalQuery = [...params.keys()]\n .sort()\n .map((k) => `\\n${k}:${(params.get(k) ?? []).sort().join(\",\")}`)\n .join(\"\");\n const canonicalResource = `/${accountName}${url.pathname}${canonicalQuery}`;\n\n return [\n method.toUpperCase(),\n h(\"content-encoding\"),\n h(\"content-language\"),\n contentLength ? String(contentLength) : \"\",\n h(\"content-md5\"),\n h(\"content-type\"),\n h(\"date\"),\n h(\"if-modified-since\"),\n h(\"if-match\"),\n h(\"if-none-match\"),\n h(\"if-unmodified-since\"),\n h(\"range\"),\n canonicalHeaders + canonicalResource,\n ].join(\"\\n\");\n}\n\n/** `x-ms-date`, `x-ms-version` and `Authorization: SharedKey` headers for a request. */\nexport async function signRequest(\n method: string,\n url: URL,\n accountName: string,\n accountKey: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n now: Date = new Date(),\n): Promise<Record<string, string>> {\n const signed = {\n ...headers,\n \"x-ms-date\": now.toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n const stringToSign = buildStringToSign(method, url, accountName, signed, contentLength);\n const signature = await hmacBase64(accountKey, stringToSign);\n return { ...signed, authorization: `SharedKey ${accountName}:${signature}` };\n}\n\n/**\n * Query string for a read-only service SAS on one blob. `blobName` is the raw\n * (unencoded) blob name; `expiry` is when the link stops working.\n */\nexport async function blobReadSas(\n accountName: string,\n accountKey: string,\n container: string,\n blobName: string,\n expiry: Date,\n): Promise<string> {\n const se = expiry.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n const fields = {\n sp: \"r\",\n st: \"\",\n se,\n resource: `/blob/${accountName}/${container}/${blobName}`,\n si: \"\",\n sip: \"\",\n spr: \"\",\n sv: SERVICE_VERSION,\n sr: \"b\",\n snapshot: \"\",\n ses: \"\",\n rscc: \"\",\n rscd: \"\",\n rsce: \"\",\n rscl: \"\",\n rsct: \"\",\n };\n const signature = await hmacBase64(accountKey, Object.values(fields).join(\"\\n\"));\n const query = new URLSearchParams({ sv: SERVICE_VERSION, se, sr: \"b\", sp: \"r\", sig: signature });\n return query.toString();\n}\n\nexport function base64ToBytes(value: string) {\n const binary = atob(value);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n","import type { StorageErrorCode } from \"@fileway/core\";\n\n// Internal: connection-string parsing and error classification for\n// AzureDriver. Not exported from the package.\n\n// Azurite's well-known development account (public, documented by Microsoft).\nconst DEV_ACCOUNT = \"devstoreaccount1\";\nconst DEV_KEY = \"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==\";\nconst DEV_ENDPOINT = \"http://127.0.0.1:10000/devstoreaccount1\";\n\n/** Splits a connection string into the fields this driver understands. */\nexport function parseConnectionString(value: string): {\n accountName?: string;\n accountKey?: string;\n sasToken?: string;\n endpoint?: string;\n} {\n const parts: Record<string, string> = {};\n for (const segment of value.split(\";\")) {\n const eq = segment.indexOf(\"=\");\n if (eq > 0) parts[segment.slice(0, eq).trim()] = segment.slice(eq + 1).trim();\n }\n if (parts.UseDevelopmentStorage === \"true\") {\n return { accountName: DEV_ACCOUNT, accountKey: DEV_KEY, endpoint: DEV_ENDPOINT };\n }\n const accountName = parts.AccountName;\n const endpoint =\n parts.BlobEndpoint ??\n (accountName\n ? `${parts.DefaultEndpointsProtocol ?? \"https\"}://${accountName}.blob.${parts.EndpointSuffix ?? \"core.windows.net\"}`\n : undefined);\n return {\n ...(accountName ? { accountName } : {}),\n ...(parts.AccountKey ? { accountKey: parts.AccountKey } : {}),\n ...(parts.SharedAccessSignature ? { sasToken: parts.SharedAccessSignature } : {}),\n ...(endpoint ? { endpoint } : {}),\n };\n}\n\n/** Classifies an Azure failure into a typed code from the service error code,\n * falling back to the HTTP status. */\nexport function classifyAzureError(status: number, code: string | undefined): StorageErrorCode {\n switch (code) {\n case \"ContainerNotFound\":\n case \"ContainerBeingDeleted\":\n return \"bucket-not-found\";\n case \"BlobNotFound\":\n case \"CannotVerifyCopySource\":\n case \"ResourceNotFound\":\n return \"not-found\";\n case \"AuthenticationFailed\":\n case \"AuthorizationFailure\":\n case \"AuthorizationPermissionMismatch\":\n case \"AuthorizationResourceTypeMismatch\":\n case \"NoAuthenticationInformation\":\n case \"InvalidAuthenticationInfo\":\n return \"auth-failed\";\n case \"RequestBodyTooLarge\":\n case \"BlockCountExceedsLimit\":\n return \"size-exceeded\";\n case \"BlobAlreadyExists\":\n return \"conflict\";\n case \"ServerBusy\":\n case \"TooManyRequests\":\n return \"rate-limited\";\n }\n if (status === 429) return \"rate-limited\";\n if (status === 401 || status === 403) return \"auth-failed\";\n if (status === 404) return \"not-found\";\n if (status === 413) return \"size-exceeded\";\n return \"provider-error\";\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseDriver, UploadOptions, UploadContext, UploadResult, OperationOptions, PresignedUrlOptions, StoredObject } from '@fileway/core';
1
+ import { BaseDriver, UploadOptions, UploadContext, UploadResult, OperationOptions, PresignedUrlOptions, StoredObject, ListOptions, ListPage, CopyOptions } from '@fileway/core';
2
2
 
3
3
  interface AzureDriverConfig {
4
4
  /** Container to store blobs in. */
@@ -55,6 +55,16 @@ declare class AzureDriver implements BaseDriver<AzureMeta> {
55
55
  head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null>;
56
56
  /** Whether blocks have been staged for `path` but not yet committed. */
57
57
  isPending(path: string, options?: OperationOptions): Promise<boolean>;
58
+ /** One page of the blobs under `options.prefix` (List Blobs). */
59
+ list(options?: ListOptions): Promise<ListPage>;
60
+ /**
61
+ * Copies a blob in this container (Copy Blob), waiting for it to finish when
62
+ * the service copies in the background. With `overwrite: false` the copy is
63
+ * made only if `to` does not exist (`If-None-Match: *`), atomically.
64
+ */
65
+ copy(from: string, to: string, options?: CopyOptions): Promise<void>;
66
+ /** Copies, then deletes the source. Blob Storage has no rename. */
67
+ move(from: string, to: string, options?: CopyOptions): Promise<void>;
58
68
  private blobUrl;
59
69
  /** A signed (Shared Key) or SAS-authorized request to one blob. */
60
70
  private request;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseDriver, UploadOptions, UploadContext, UploadResult, OperationOptions, PresignedUrlOptions, StoredObject } from '@fileway/core';
1
+ import { BaseDriver, UploadOptions, UploadContext, UploadResult, OperationOptions, PresignedUrlOptions, StoredObject, ListOptions, ListPage, CopyOptions } from '@fileway/core';
2
2
 
3
3
  interface AzureDriverConfig {
4
4
  /** Container to store blobs in. */
@@ -55,6 +55,16 @@ declare class AzureDriver implements BaseDriver<AzureMeta> {
55
55
  head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null>;
56
56
  /** Whether blocks have been staged for `path` but not yet committed. */
57
57
  isPending(path: string, options?: OperationOptions): Promise<boolean>;
58
+ /** One page of the blobs under `options.prefix` (List Blobs). */
59
+ list(options?: ListOptions): Promise<ListPage>;
60
+ /**
61
+ * Copies a blob in this container (Copy Blob), waiting for it to finish when
62
+ * the service copies in the background. With `overwrite: false` the copy is
63
+ * made only if `to` does not exist (`If-None-Match: *`), atomically.
64
+ */
65
+ copy(from: string, to: string, options?: CopyOptions): Promise<void>;
66
+ /** Copies, then deletes the source. Blob Storage has no rename. */
67
+ move(from: string, to: string, options?: CopyOptions): Promise<void>;
58
68
  private blobUrl;
59
69
  /** A signed (Shared Key) or SAS-authorized request to one blob. */
60
70
  private request;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError } from "@fileway/core";
2
+ import { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError, encodeCursor, decodeCursor } from "@fileway/core";
3
3
 
4
4
  // src/sharedkey.ts
5
5
  var SERVICE_VERSION = "2023-11-03";
@@ -128,6 +128,7 @@ function classifyAzureError(status, code) {
128
128
  case "ContainerBeingDeleted":
129
129
  return "bucket-not-found";
130
130
  case "BlobNotFound":
131
+ case "CannotVerifyCopySource":
131
132
  case "ResourceNotFound":
132
133
  return "not-found";
133
134
  case "AuthenticationFailed":
@@ -140,6 +141,8 @@ function classifyAzureError(status, code) {
140
141
  case "RequestBodyTooLarge":
141
142
  case "BlockCountExceedsLimit":
142
143
  return "size-exceeded";
144
+ case "BlobAlreadyExists":
145
+ return "conflict";
143
146
  case "ServerBusy":
144
147
  case "TooManyRequests":
145
148
  return "rate-limited";
@@ -382,12 +385,78 @@ var AzureDriver = class {
382
385
  const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\/UncommittedBlocks>/s)?.[1] ?? "";
383
386
  return uncommitted.includes("<Block>");
384
387
  }
388
+ /** One page of the blobs under `options.prefix` (List Blobs). */
389
+ async list(options) {
390
+ const res = await this.request("GET", "", {
391
+ query: {
392
+ restype: "container",
393
+ comp: "list",
394
+ prefix: options?.prefix ?? "",
395
+ maxresults: String(options?.limit ?? 100),
396
+ ...options?.recursive ? {} : { delimiter: "/" },
397
+ ...options?.cursor !== void 0 ? { marker: decodeCursor(options.cursor) } : {}
398
+ },
399
+ ...withSignal(options)
400
+ });
401
+ const body = await res.text().catch(() => "");
402
+ if (!res.ok) throw azureErrorFrom("Azure list failed", res, body);
403
+ const objects = xmlBlocks(body, "Blob").map((blob) => {
404
+ const modified = xmlValue(blob, "Last-Modified");
405
+ const etag = xmlValue(blob, "Etag");
406
+ const type = xmlValue(blob, "Content-Type");
407
+ return {
408
+ path: unescapeXml(xmlValue(blob, "Name") ?? ""),
409
+ size: Number(xmlValue(blob, "Content-Length") ?? 0),
410
+ ...modified ? { lastModified: new Date(modified) } : {},
411
+ ...etag ? { etag } : {},
412
+ ...type ? { contentType: unescapeXml(type) } : {}
413
+ };
414
+ });
415
+ const folders = xmlBlocks(body, "BlobPrefix").map((p) => unescapeXml(xmlValue(p, "Name") ?? ""));
416
+ const next = xmlValue(body, "NextMarker");
417
+ return { objects, folders, ...next ? { cursor: encodeCursor(next) } : {} };
418
+ }
419
+ /**
420
+ * Copies a blob in this container (Copy Blob), waiting for it to finish when
421
+ * the service copies in the background. With `overwrite: false` the copy is
422
+ * made only if `to` does not exist (`If-None-Match: *`), atomically.
423
+ */
424
+ async copy(from, to, options) {
425
+ const source = new URL(this.blobUrl(from));
426
+ if (this.auth.sasToken && !this.auth.accountKey) {
427
+ for (const [k, v] of new URLSearchParams(this.auth.sasToken)) source.searchParams.set(k, v);
428
+ }
429
+ const res = await this.request("PUT", to, {
430
+ headers: { "x-ms-copy-source": source.href, ...options?.overwrite ? {} : { "if-none-match": "*" } },
431
+ ...withSignal(options)
432
+ });
433
+ if (!res.ok) throw await azureError("Azure copy failed", res);
434
+ let status = res.headers.get("x-ms-copy-status");
435
+ for (let wait = 200; status === "pending"; wait = Math.min(wait * 2, 2e3)) {
436
+ await delay(wait, options?.signal);
437
+ const head = await this.request("HEAD", to, withSignal(options));
438
+ if (!head.ok) throw await azureError("Azure copy status check failed", head);
439
+ status = head.headers.get("x-ms-copy-status");
440
+ if (status === "failed" || status === "aborted") {
441
+ throw new StorageError(
442
+ "provider-error",
443
+ `Azure copy ${status}: ${head.headers.get("x-ms-copy-status-description") ?? "no details"}`,
444
+ { provider: PROVIDER }
445
+ );
446
+ }
447
+ }
448
+ }
449
+ /** Copies, then deletes the source. Blob Storage has no rename. */
450
+ async move(from, to, options) {
451
+ await this.copy(from, to, options);
452
+ await this.delete(from, options);
453
+ }
385
454
  blobUrl(path) {
386
455
  return `${this.endpoint}/${this.container}/${path.split("/").map(encodeURIComponent).join("/")}`;
387
456
  }
388
457
  /** A signed (Shared Key) or SAS-authorized request to one blob. */
389
458
  async request(method, path, init = {}) {
390
- const url = new URL(this.blobUrl(path));
459
+ const url = new URL(path === "" ? `${this.endpoint}/${this.container}` : this.blobUrl(path));
391
460
  for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);
392
461
  let headers;
393
462
  const { accountName, accountKey, sasToken } = this.auth;
@@ -409,6 +478,29 @@ var AzureDriver = class {
409
478
  });
410
479
  }
411
480
  };
481
+ function xmlBlocks(xml, tag) {
482
+ return [...xml.matchAll(new RegExp(`<${tag}>(.*?)</${tag}>`, "gs"))].map((m) => m[1] ?? "");
483
+ }
484
+ function xmlValue(xml, tag) {
485
+ return xml.match(new RegExp(`<${tag}>(.*?)</${tag}>`, "s"))?.[1] || void 0;
486
+ }
487
+ function unescapeXml(value) {
488
+ return value.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&#(\d+);/g, (_, n) => String.fromCodePoint(Number(n))).replace(/&#x([0-9a-f]+);/gi, (_, n) => String.fromCodePoint(parseInt(n, 16))).replace(/&amp;/g, "&");
489
+ }
490
+ function delay(ms, signal) {
491
+ return new Promise((resolve, reject) => {
492
+ if (signal?.aborted) return reject(abortError("copy aborted"));
493
+ const timer = setTimeout(() => {
494
+ signal?.removeEventListener("abort", onAbort);
495
+ resolve();
496
+ }, ms);
497
+ const onAbort = () => {
498
+ clearTimeout(timer);
499
+ reject(abortError("copy aborted"));
500
+ };
501
+ signal?.addEventListener("abort", onAbort, { once: true });
502
+ });
503
+ }
412
504
  function withSignal(options) {
413
505
  return options?.signal ? { signal: options.signal } : {};
414
506
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/sharedkey.ts","../src/internal.ts"],"sourcesContent":["import type { BaseDriver, OperationOptions, PresignedUrlOptions, StoredObject, UploadContext, UploadOptions, UploadResult } from \"@fileway/core\";\nimport { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError } from \"@fileway/core\";\nimport { blobReadSas, bytesToBase64, signRequest, SERVICE_VERSION } from \"./sharedkey.js\";\nimport { classifyAzureError, parseConnectionString } from \"./internal.js\";\n\nconst randomUUID = () => globalThis.crypto.randomUUID();\nconst PROVIDER = \"azure\";\n\nconst DEFAULT_BLOCK_SIZE = 8 * 1024 * 1024;\nconst MIN_BLOCK_SIZE = 1024 * 1024;\n// Put Block accepts up to 4000 MiB per block.\nconst MAX_BLOCK_SIZE = 4000 * 1024 * 1024;\n// A block blob holds at most 50,000 committed blocks.\nconst MAX_BLOCKS = 50_000;\nconst PRESIGN_DEFAULT_EXPIRY = 3600;\nconst PRESIGN_MIN_EXPIRY = 1;\nconst PRESIGN_MAX_EXPIRY = 604800;\n// Metadata names must be valid C# identifiers, so no hyphens here.\nconst TOKEN_KEY = \"fileway_upload_token\";\nconst TOKEN_HEADER = `x-ms-meta-${TOKEN_KEY}`;\nconst METADATA_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n\nexport interface AzureDriverConfig {\n /** Container to store blobs in. */\n container: string;\n /** Storage account name. Required unless a connection string provides it. */\n accountName?: string;\n /** Base64 account key, used for Shared Key auth and presigned URLs. */\n accountKey?: string;\n /** A SAS token (with or without the leading `?`), used instead of an account key. */\n sasToken?: string;\n /** An Azure Storage connection string, e.g. from the portal or\n * `UseDevelopmentStorage=true` for Azurite. Explicit fields override it. */\n connectionString?: string;\n /** Blob service endpoint. Defaults to `https://<account>.blob.core.windows.net`.\n * For Azurite: `http://127.0.0.1:10000/devstoreaccount1`. */\n endpoint?: string;\n /** Public base URL for `getUrl` (e.g. a CDN). Defaults to `<endpoint>/<container>`. */\n baseUrl?: string;\n /** Block size in bytes. Default 8 MiB, clamped to 1 MiB..4000 MiB. Also the\n * largest upload sent as a single Put Blob. */\n blockSize?: number;\n /** Reject uploads larger than this many bytes with a `size-exceeded` error. */\n maxSizeBytes?: number;\n}\n\nexport type AzureMeta = {\n container: string;\n etag?: string;\n};\n\ninterface ResolvedAuth {\n accountName: string;\n accountKey?: string;\n sasToken?: string;\n}\n\nexport class AzureDriver implements BaseDriver<AzureMeta> {\n readonly name = \"azure\";\n private container: string;\n private auth: ResolvedAuth;\n private endpoint: string;\n private baseUrl: string;\n private blockSize: number;\n private maxSizeBytes: number | undefined;\n\n constructor(config: AzureDriverConfig) {\n const fromString = config.connectionString ? parseConnectionString(config.connectionString) : {};\n const accountName = config.accountName ?? fromString.accountName;\n const accountKey = config.accountKey ?? fromString.accountKey;\n const sasToken = config.sasToken ?? fromString.sasToken;\n const endpoint = config.endpoint ?? fromString.endpoint;\n\n if (!config.container) {\n throw new StorageError(\"config\", \"AzureDriver requires a container\", { provider: PROVIDER });\n }\n const name = accountName ?? (endpoint ? accountFromEndpoint(endpoint) : undefined);\n if (!name) {\n throw new StorageError(\"config\", \"AzureDriver requires accountName (or a connection string or endpoint)\", { provider: PROVIDER });\n }\n if (!accountKey && !sasToken) {\n throw new StorageError(\"config\", \"AzureDriver requires accountKey, sasToken or a connection string with credentials\", { provider: PROVIDER });\n }\n\n this.container = config.container;\n this.auth = {\n accountName: name,\n ...(accountKey ? { accountKey } : {}),\n ...(sasToken ? { sasToken: sasToken.replace(/^\\?/, \"\") } : {}),\n };\n this.endpoint = (endpoint ?? `https://${name}.blob.core.windows.net`).replace(/\\/+$/, \"\");\n this.baseUrl = (config.baseUrl ?? `${this.endpoint}/${config.container}`).replace(/\\/+$/, \"\");\n this.blockSize = Math.min(Math.max(config.blockSize ?? DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE), MAX_BLOCK_SIZE);\n this.maxSizeBytes = config.maxSizeBytes;\n }\n\n async upload(\n stream: ReadableStream<Uint8Array>,\n options: UploadOptions,\n context?: UploadContext,\n ): Promise<UploadResult<AzureMeta>> {\n validateUploadOptions(options);\n for (const key of Object.keys(options.metadata ?? {})) {\n if (!METADATA_NAME.test(key)) {\n throw new ValidationError(\n `Azure metadata keys must be letters, digits and underscores, not starting with a digit (got \"${key}\")`,\n );\n }\n }\n if (this.maxSizeBytes !== undefined && options.size !== undefined && options.size > this.maxSizeBytes) {\n throw new StorageError(\"size-exceeded\", `file size ${options.size} exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n\n const id = options.id ?? randomUUID();\n const path = this.pathFor({ ...options, id });\n const contentType = options.mimeType ?? \"application/octet-stream\";\n const metaHeaders: Record<string, string> = {\n ...Object.fromEntries(Object.entries(options.metadata ?? {}).map(([k, v]) => [`x-ms-meta-${k}`, v])),\n ...(context?.uploadToken ? { [TOKEN_HEADER]: context.uploadToken } : {}),\n };\n const signal = options.signal;\n\n // Read one block at a time, so peak memory is bounded by `blockSize`.\n const reader = stream.getReader();\n let aborted = signal?.aborted ?? false;\n const onAbort = () => {\n aborted = true;\n // Cancelling the reader wakes any pending `read()` with `done: true`.\n reader.cancel().catch(() => {});\n };\n if (signal && !aborted) signal.addEventListener(\"abort\", onAbort, { once: true });\n // Uncommitted blocks are discarded by the service after 7 days, and never\n // touch an existing committed blob, so an abort needs no server cleanup.\n const ensureNotAborted = () => {\n if (aborted) throw abortError(\"upload aborted\");\n };\n\n let buffer: Uint8Array[] = [];\n let bufferedLen = 0;\n let totalBytes = 0;\n const blockIds: string[] = [];\n\n const putBlock = async (bytes: Uint8Array) => {\n if (blockIds.length >= MAX_BLOCKS) {\n throw new StorageError(\"size-exceeded\", `upload exceeds ${MAX_BLOCKS} blocks; raise blockSize`, { provider: PROVIDER });\n }\n const blockId = bytesToBase64(new TextEncoder().encode(`fileway-${String(blockIds.length).padStart(6, \"0\")}`));\n const res = await this.request(\"PUT\", path, {\n query: { comp: \"block\", blockid: blockId },\n body: bytes,\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(`Azure put block ${blockIds.length + 1} failed`, res);\n blockIds.push(blockId);\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n ensureNotAborted();\n if (done) break;\n totalBytes += value.byteLength;\n if (this.maxSizeBytes !== undefined && totalBytes > this.maxSizeBytes) {\n await reader.cancel().catch(() => {});\n throw new StorageError(\"size-exceeded\", `upload exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n buffer.push(value);\n bufferedLen += value.byteLength;\n\n while (bufferedLen >= this.blockSize) {\n const merged = concatBytes(buffer, bufferedLen);\n await putBlock(merged.subarray(0, this.blockSize));\n const rest = merged.subarray(this.blockSize);\n buffer = rest.byteLength > 0 ? [rest] : [];\n bufferedLen = rest.byteLength;\n }\n }\n\n if (options.size !== undefined && totalBytes !== options.size) {\n throw new ValidationError(`stream delivered ${totalBytes} bytes, expected ${options.size}`);\n }\n\n let res: Response;\n if (blockIds.length === 0) {\n // Small file: one Put Blob carries content, type and metadata.\n res = await this.request(\"PUT\", path, {\n headers: { \"x-ms-blob-type\": \"BlockBlob\", \"content-type\": contentType, ...metaHeaders },\n body: concatBytes(buffer, bufferedLen),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure upload failed\", res);\n } else {\n if (bufferedLen > 0) await putBlock(concatBytes(buffer, bufferedLen));\n ensureNotAborted();\n const xml = `<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList>${blockIds\n .map((b) => `<Latest>${b}</Latest>`)\n .join(\"\")}</BlockList>`;\n res = await this.request(\"PUT\", path, {\n query: { comp: \"blocklist\" },\n headers: { \"content-type\": \"application/xml\", \"x-ms-blob-content-type\": contentType, ...metaHeaders },\n body: new TextEncoder().encode(xml),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure put block list failed\", res);\n }\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n return {\n id,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: totalBytes,\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n };\n } catch (err) {\n reader.cancel().catch(() => {});\n throw err;\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n /** Deletes the blob; a missing blob is not an error. */\n async delete(path: string, options?: OperationOptions): Promise<void> {\n const res = await this.request(\"DELETE\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return;\n if (!res.ok) throw await azureError(\"Azure delete failed\", res);\n }\n\n async getUrl(path: string, _options?: OperationOptions): Promise<string> {\n return `${this.baseUrl}/${urlEncodePath(path)}`;\n }\n\n /** Streams the blob. `options.signal` also cancels reading the body. */\n async get(path: string, options?: OperationOptions): Promise<ReadableStream<Uint8Array>> {\n const res = await this.request(\"GET\", path, withSignal(options));\n if (!res.ok) throw await azureError(\"Azure get failed\", res);\n if (!res.body) {\n throw new StorageError(\"provider-error\", \"Azure get returned no response body\", { provider: PROVIDER });\n }\n return res.body;\n }\n\n /**\n * A read-only service SAS URL for `path`, valid for `expiresIn` seconds\n * (integer in 1..604800, default 3600). Needs an account key.\n */\n async getPresignedUrl(path: string, options?: PresignedUrlOptions): Promise<string> {\n const expiresIn = options?.expiresIn ?? PRESIGN_DEFAULT_EXPIRY;\n if (!Number.isInteger(expiresIn) || expiresIn < PRESIGN_MIN_EXPIRY || expiresIn > PRESIGN_MAX_EXPIRY) {\n throw new ValidationError(\n `expiresIn must be an integer between ${PRESIGN_MIN_EXPIRY} and ${PRESIGN_MAX_EXPIRY} seconds`,\n );\n }\n const { accountName, accountKey } = this.auth;\n if (!accountKey) {\n throw new StorageError(\"config\", \"AzureDriver needs accountKey to create presigned URLs\", { provider: PROVIDER });\n }\n const sas = await blobReadSas(accountName, accountKey, this.container, path, new Date(Date.now() + expiresIn * 1000));\n return `${this.blobUrl(path)}?${sas}`;\n }\n\n /** The blob name an upload with `options.id` is stored under: `<path>/<id>.<ext>`. */\n pathFor(options: UploadOptions & { id: string }): string {\n const ext = options.filename.includes(\".\") ? options.filename.split(\".\").pop()! : \"\";\n const filename = ext ? `${options.id}.${ext}` : options.id;\n return options.path ? `${options.path}/${filename}` : filename;\n }\n\n /** The blob at `path` (a `HEAD` request), or `null` if it does not exist. */\n async head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null> {\n const res = await this.request(\"HEAD\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return null;\n if (!res.ok) throw await azureError(\"Azure head failed\", res);\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n const token = res.headers.get(TOKEN_HEADER) ?? undefined;\n const modified = res.headers.get(\"last-modified\");\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n const name = path.split(\"/\").pop() ?? path;\n return {\n id: name.includes(\".\") ? name.slice(0, name.lastIndexOf(\".\")) : name,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: Number(res.headers.get(\"content-length\") ?? 0),\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n ...(token ? { uploadToken: token } : {}),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(contentType ? { contentType } : {}),\n };\n }\n\n /** Whether blocks have been staged for `path` but not yet committed. */\n async isPending(path: string, options?: OperationOptions): Promise<boolean> {\n const res = await this.request(\"GET\", path, {\n query: { comp: \"blocklist\", blocklisttype: \"uncommitted\" },\n ...withSignal(options),\n });\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return false;\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure get block list failed\", res, body);\n const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\\/UncommittedBlocks>/s)?.[1] ?? \"\";\n return uncommitted.includes(\"<Block>\");\n }\n\n private blobUrl(path: string): string {\n return `${this.endpoint}/${this.container}/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n }\n\n /** A signed (Shared Key) or SAS-authorized request to one blob. */\n private async request(\n method: string,\n path: string,\n init: {\n query?: Record<string, string>;\n headers?: Record<string, string>;\n body?: Uint8Array;\n signal?: AbortSignal;\n } = {},\n ): Promise<Response> {\n const url = new URL(this.blobUrl(path));\n for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);\n\n let headers: Record<string, string>;\n const { accountName, accountKey, sasToken } = this.auth;\n if (accountKey) {\n headers = await signRequest(method, url, accountName, accountKey, init.headers ?? {}, init.body?.byteLength);\n } else {\n for (const [k, v] of new URLSearchParams(sasToken)) url.searchParams.set(k, v);\n headers = {\n ...init.headers,\n \"x-ms-date\": new Date().toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n }\n\n return fetchWithAbort(url, {\n method,\n headers,\n ...(init.body ? { body: init.body as BodyInit } : {}),\n signal: init.signal ?? null,\n });\n }\n}\n\nfunction withSignal(options: OperationOptions | undefined): { signal?: AbortSignal } {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/** The account name from a `<account>.blob.<suffix>` host or an Azurite-style\n * `http://host:port/<account>` endpoint. */\nfunction accountFromEndpoint(endpoint: string): string | undefined {\n const url = new URL(endpoint);\n const fromHost = url.hostname.match(/^([a-z0-9]+)\\.blob\\./)?.[1];\n return fromHost ?? (url.pathname.split(\"/\").filter(Boolean)[0] || undefined);\n}\n\n/** `fetch` that converts non-abort failures into a typed `network` error,\n * while letting aborts (`AbortError`) pass through untouched. */\nasync function fetchWithAbort(url: string | URL, init: RequestInit): Promise<Response> {\n try {\n return await fetch(url, init);\n } catch (err) {\n if (isAbortError(err)) throw err;\n const cause = err instanceof Error ? err : new Error(String(err));\n throw new StorageError(\"network\", `${PROVIDER} network error: ${cause.message}`, { provider: PROVIDER, cause });\n }\n}\n\n/** The service error code: the `x-ms-error-code` header, which Azure sends on\n * every failure (including bodiless HEAD responses), or the XML `<Code>`. */\nfunction errorCode(res: Response, body?: string): string | undefined {\n return res.headers.get(\"x-ms-error-code\") ?? body?.match(/<Code>(.*?)<\\/Code>/s)?.[1];\n}\n\nfunction azureErrorFrom(prefix: string, res: Response, body: string): StorageError {\n const code = errorCode(res, body);\n return new StorageError(\n classifyAzureError(res.status, code),\n `${prefix}: ${res.status} ${res.statusText}${code ? ` (${code})` : \"\"}`,\n { statusCode: res.status, provider: PROVIDER },\n );\n}\n\nasync function azureError(prefix: string, res: Response): Promise<StorageError> {\n const body = await res.text().catch(() => \"\");\n return azureErrorFrom(prefix, res, body);\n}\n\nfunction concatBytes(chunks: Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n","// Azure Storage Shared Key and service SAS signing, using only Web Crypto.\n// https://learn.microsoft.com/rest/api/storageservices/authorize-with-shared-key\n// https://learn.microsoft.com/rest/api/storageservices/create-service-sas\n\nexport const SERVICE_VERSION = \"2023-11-03\";\n\nconst encoder = new TextEncoder();\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction importKey(accountKey: string): Promise<CryptoKey> {\n let key = keyCache.get(accountKey);\n if (!key) {\n key = globalThis.crypto.subtle.importKey(\n \"raw\",\n base64ToBytes(accountKey),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n keyCache.set(accountKey, key);\n }\n return key;\n}\n\n/** Base64 HMAC-SHA256 of `message` with the base64-encoded account key. */\nexport async function hmacBase64(accountKey: string, message: string): Promise<string> {\n const key = await importKey(accountKey);\n const sig = await globalThis.crypto.subtle.sign(\"HMAC\", key, encoder.encode(message));\n return bytesToBase64(new Uint8Array(sig));\n}\n\n/**\n * The Shared Key string-to-sign for a request. `headers` are the request\n * headers (any case); `contentLength` is the body size in bytes (0 or\n * undefined signs as an empty value, as the service requires since 2015-02-21).\n */\nexport function buildStringToSign(\n method: string,\n url: URL,\n accountName: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n): string {\n const lower: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n const h = (name: string) => lower[name] ?? \"\";\n\n const canonicalHeaders = Object.keys(lower)\n .filter((k) => k.startsWith(\"x-ms-\"))\n .sort()\n .map((k) => `${k}:${(lower[k] ?? \"\").replace(/\\s+/g, \" \").trim()}\\n`)\n .join(\"\");\n\n // Query parameters: lowercase names, sorted, decoded values joined by commas.\n const params = new Map<string, string[]>();\n for (const [k, v] of url.searchParams) {\n const name = k.toLowerCase();\n params.set(name, [...(params.get(name) ?? []), v]);\n }\n const canonicalQuery = [...params.keys()]\n .sort()\n .map((k) => `\\n${k}:${(params.get(k) ?? []).sort().join(\",\")}`)\n .join(\"\");\n const canonicalResource = `/${accountName}${url.pathname}${canonicalQuery}`;\n\n return [\n method.toUpperCase(),\n h(\"content-encoding\"),\n h(\"content-language\"),\n contentLength ? String(contentLength) : \"\",\n h(\"content-md5\"),\n h(\"content-type\"),\n h(\"date\"),\n h(\"if-modified-since\"),\n h(\"if-match\"),\n h(\"if-none-match\"),\n h(\"if-unmodified-since\"),\n h(\"range\"),\n canonicalHeaders + canonicalResource,\n ].join(\"\\n\");\n}\n\n/** `x-ms-date`, `x-ms-version` and `Authorization: SharedKey` headers for a request. */\nexport async function signRequest(\n method: string,\n url: URL,\n accountName: string,\n accountKey: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n now: Date = new Date(),\n): Promise<Record<string, string>> {\n const signed = {\n ...headers,\n \"x-ms-date\": now.toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n const stringToSign = buildStringToSign(method, url, accountName, signed, contentLength);\n const signature = await hmacBase64(accountKey, stringToSign);\n return { ...signed, authorization: `SharedKey ${accountName}:${signature}` };\n}\n\n/**\n * Query string for a read-only service SAS on one blob. `blobName` is the raw\n * (unencoded) blob name; `expiry` is when the link stops working.\n */\nexport async function blobReadSas(\n accountName: string,\n accountKey: string,\n container: string,\n blobName: string,\n expiry: Date,\n): Promise<string> {\n const se = expiry.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n const fields = {\n sp: \"r\",\n st: \"\",\n se,\n resource: `/blob/${accountName}/${container}/${blobName}`,\n si: \"\",\n sip: \"\",\n spr: \"\",\n sv: SERVICE_VERSION,\n sr: \"b\",\n snapshot: \"\",\n ses: \"\",\n rscc: \"\",\n rscd: \"\",\n rsce: \"\",\n rscl: \"\",\n rsct: \"\",\n };\n const signature = await hmacBase64(accountKey, Object.values(fields).join(\"\\n\"));\n const query = new URLSearchParams({ sv: SERVICE_VERSION, se, sr: \"b\", sp: \"r\", sig: signature });\n return query.toString();\n}\n\nexport function base64ToBytes(value: string) {\n const binary = atob(value);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n","import type { StorageErrorCode } from \"@fileway/core\";\n\n// Internal: connection-string parsing and error classification for\n// AzureDriver. Not exported from the package.\n\n// Azurite's well-known development account (public, documented by Microsoft).\nconst DEV_ACCOUNT = \"devstoreaccount1\";\nconst DEV_KEY = \"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==\";\nconst DEV_ENDPOINT = \"http://127.0.0.1:10000/devstoreaccount1\";\n\n/** Splits a connection string into the fields this driver understands. */\nexport function parseConnectionString(value: string): {\n accountName?: string;\n accountKey?: string;\n sasToken?: string;\n endpoint?: string;\n} {\n const parts: Record<string, string> = {};\n for (const segment of value.split(\";\")) {\n const eq = segment.indexOf(\"=\");\n if (eq > 0) parts[segment.slice(0, eq).trim()] = segment.slice(eq + 1).trim();\n }\n if (parts.UseDevelopmentStorage === \"true\") {\n return { accountName: DEV_ACCOUNT, accountKey: DEV_KEY, endpoint: DEV_ENDPOINT };\n }\n const accountName = parts.AccountName;\n const endpoint =\n parts.BlobEndpoint ??\n (accountName\n ? `${parts.DefaultEndpointsProtocol ?? \"https\"}://${accountName}.blob.${parts.EndpointSuffix ?? \"core.windows.net\"}`\n : undefined);\n return {\n ...(accountName ? { accountName } : {}),\n ...(parts.AccountKey ? { accountKey: parts.AccountKey } : {}),\n ...(parts.SharedAccessSignature ? { sasToken: parts.SharedAccessSignature } : {}),\n ...(endpoint ? { endpoint } : {}),\n };\n}\n\n/** Classifies an Azure failure into a typed code from the service error code,\n * falling back to the HTTP status. */\nexport function classifyAzureError(status: number, code: string | undefined): StorageErrorCode {\n switch (code) {\n case \"ContainerNotFound\":\n case \"ContainerBeingDeleted\":\n return \"bucket-not-found\";\n case \"BlobNotFound\":\n case \"ResourceNotFound\":\n return \"not-found\";\n case \"AuthenticationFailed\":\n case \"AuthorizationFailure\":\n case \"AuthorizationPermissionMismatch\":\n case \"AuthorizationResourceTypeMismatch\":\n case \"NoAuthenticationInformation\":\n case \"InvalidAuthenticationInfo\":\n return \"auth-failed\";\n case \"RequestBodyTooLarge\":\n case \"BlockCountExceedsLimit\":\n return \"size-exceeded\";\n case \"ServerBusy\":\n case \"TooManyRequests\":\n return \"rate-limited\";\n }\n if (status === 429) return \"rate-limited\";\n if (status === 401 || status === 403) return \"auth-failed\";\n if (status === 404) return \"not-found\";\n if (status === 413) return \"size-exceeded\";\n return \"provider-error\";\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/sharedkey.ts","../src/internal.ts"],"sourcesContent":["import type { BaseDriver, CopyOptions, ListedObject, ListOptions, ListPage, OperationOptions, PresignedUrlOptions, StoredObject, UploadContext, UploadOptions, UploadResult } from \"@fileway/core\";\nimport { validateUploadOptions, ValidationError, urlEncodePath, abortError, isAbortError, StorageError, encodeCursor, decodeCursor } from \"@fileway/core\";\nimport { blobReadSas, bytesToBase64, signRequest, SERVICE_VERSION } from \"./sharedkey.js\";\nimport { classifyAzureError, parseConnectionString } from \"./internal.js\";\n\nconst randomUUID = () => globalThis.crypto.randomUUID();\nconst PROVIDER = \"azure\";\n\nconst DEFAULT_BLOCK_SIZE = 8 * 1024 * 1024;\nconst MIN_BLOCK_SIZE = 1024 * 1024;\n// Put Block accepts up to 4000 MiB per block.\nconst MAX_BLOCK_SIZE = 4000 * 1024 * 1024;\n// A block blob holds at most 50,000 committed blocks.\nconst MAX_BLOCKS = 50_000;\nconst PRESIGN_DEFAULT_EXPIRY = 3600;\nconst PRESIGN_MIN_EXPIRY = 1;\nconst PRESIGN_MAX_EXPIRY = 604800;\n// Metadata names must be valid C# identifiers, so no hyphens here.\nconst TOKEN_KEY = \"fileway_upload_token\";\nconst TOKEN_HEADER = `x-ms-meta-${TOKEN_KEY}`;\nconst METADATA_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n\nexport interface AzureDriverConfig {\n /** Container to store blobs in. */\n container: string;\n /** Storage account name. Required unless a connection string provides it. */\n accountName?: string;\n /** Base64 account key, used for Shared Key auth and presigned URLs. */\n accountKey?: string;\n /** A SAS token (with or without the leading `?`), used instead of an account key. */\n sasToken?: string;\n /** An Azure Storage connection string, e.g. from the portal or\n * `UseDevelopmentStorage=true` for Azurite. Explicit fields override it. */\n connectionString?: string;\n /** Blob service endpoint. Defaults to `https://<account>.blob.core.windows.net`.\n * For Azurite: `http://127.0.0.1:10000/devstoreaccount1`. */\n endpoint?: string;\n /** Public base URL for `getUrl` (e.g. a CDN). Defaults to `<endpoint>/<container>`. */\n baseUrl?: string;\n /** Block size in bytes. Default 8 MiB, clamped to 1 MiB..4000 MiB. Also the\n * largest upload sent as a single Put Blob. */\n blockSize?: number;\n /** Reject uploads larger than this many bytes with a `size-exceeded` error. */\n maxSizeBytes?: number;\n}\n\nexport type AzureMeta = {\n container: string;\n etag?: string;\n};\n\ninterface ResolvedAuth {\n accountName: string;\n accountKey?: string;\n sasToken?: string;\n}\n\nexport class AzureDriver implements BaseDriver<AzureMeta> {\n readonly name = \"azure\";\n private container: string;\n private auth: ResolvedAuth;\n private endpoint: string;\n private baseUrl: string;\n private blockSize: number;\n private maxSizeBytes: number | undefined;\n\n constructor(config: AzureDriverConfig) {\n const fromString = config.connectionString ? parseConnectionString(config.connectionString) : {};\n const accountName = config.accountName ?? fromString.accountName;\n const accountKey = config.accountKey ?? fromString.accountKey;\n const sasToken = config.sasToken ?? fromString.sasToken;\n const endpoint = config.endpoint ?? fromString.endpoint;\n\n if (!config.container) {\n throw new StorageError(\"config\", \"AzureDriver requires a container\", { provider: PROVIDER });\n }\n const name = accountName ?? (endpoint ? accountFromEndpoint(endpoint) : undefined);\n if (!name) {\n throw new StorageError(\"config\", \"AzureDriver requires accountName (or a connection string or endpoint)\", { provider: PROVIDER });\n }\n if (!accountKey && !sasToken) {\n throw new StorageError(\"config\", \"AzureDriver requires accountKey, sasToken or a connection string with credentials\", { provider: PROVIDER });\n }\n\n this.container = config.container;\n this.auth = {\n accountName: name,\n ...(accountKey ? { accountKey } : {}),\n ...(sasToken ? { sasToken: sasToken.replace(/^\\?/, \"\") } : {}),\n };\n this.endpoint = (endpoint ?? `https://${name}.blob.core.windows.net`).replace(/\\/+$/, \"\");\n this.baseUrl = (config.baseUrl ?? `${this.endpoint}/${config.container}`).replace(/\\/+$/, \"\");\n this.blockSize = Math.min(Math.max(config.blockSize ?? DEFAULT_BLOCK_SIZE, MIN_BLOCK_SIZE), MAX_BLOCK_SIZE);\n this.maxSizeBytes = config.maxSizeBytes;\n }\n\n async upload(\n stream: ReadableStream<Uint8Array>,\n options: UploadOptions,\n context?: UploadContext,\n ): Promise<UploadResult<AzureMeta>> {\n validateUploadOptions(options);\n for (const key of Object.keys(options.metadata ?? {})) {\n if (!METADATA_NAME.test(key)) {\n throw new ValidationError(\n `Azure metadata keys must be letters, digits and underscores, not starting with a digit (got \"${key}\")`,\n );\n }\n }\n if (this.maxSizeBytes !== undefined && options.size !== undefined && options.size > this.maxSizeBytes) {\n throw new StorageError(\"size-exceeded\", `file size ${options.size} exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n\n const id = options.id ?? randomUUID();\n const path = this.pathFor({ ...options, id });\n const contentType = options.mimeType ?? \"application/octet-stream\";\n const metaHeaders: Record<string, string> = {\n ...Object.fromEntries(Object.entries(options.metadata ?? {}).map(([k, v]) => [`x-ms-meta-${k}`, v])),\n ...(context?.uploadToken ? { [TOKEN_HEADER]: context.uploadToken } : {}),\n };\n const signal = options.signal;\n\n // Read one block at a time, so peak memory is bounded by `blockSize`.\n const reader = stream.getReader();\n let aborted = signal?.aborted ?? false;\n const onAbort = () => {\n aborted = true;\n // Cancelling the reader wakes any pending `read()` with `done: true`.\n reader.cancel().catch(() => {});\n };\n if (signal && !aborted) signal.addEventListener(\"abort\", onAbort, { once: true });\n // Uncommitted blocks are discarded by the service after 7 days, and never\n // touch an existing committed blob, so an abort needs no server cleanup.\n const ensureNotAborted = () => {\n if (aborted) throw abortError(\"upload aborted\");\n };\n\n let buffer: Uint8Array[] = [];\n let bufferedLen = 0;\n let totalBytes = 0;\n const blockIds: string[] = [];\n\n const putBlock = async (bytes: Uint8Array) => {\n if (blockIds.length >= MAX_BLOCKS) {\n throw new StorageError(\"size-exceeded\", `upload exceeds ${MAX_BLOCKS} blocks; raise blockSize`, { provider: PROVIDER });\n }\n const blockId = bytesToBase64(new TextEncoder().encode(`fileway-${String(blockIds.length).padStart(6, \"0\")}`));\n const res = await this.request(\"PUT\", path, {\n query: { comp: \"block\", blockid: blockId },\n body: bytes,\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(`Azure put block ${blockIds.length + 1} failed`, res);\n blockIds.push(blockId);\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n ensureNotAborted();\n if (done) break;\n totalBytes += value.byteLength;\n if (this.maxSizeBytes !== undefined && totalBytes > this.maxSizeBytes) {\n await reader.cancel().catch(() => {});\n throw new StorageError(\"size-exceeded\", `upload exceeds maxSizeBytes ${this.maxSizeBytes}`, { provider: PROVIDER });\n }\n buffer.push(value);\n bufferedLen += value.byteLength;\n\n while (bufferedLen >= this.blockSize) {\n const merged = concatBytes(buffer, bufferedLen);\n await putBlock(merged.subarray(0, this.blockSize));\n const rest = merged.subarray(this.blockSize);\n buffer = rest.byteLength > 0 ? [rest] : [];\n bufferedLen = rest.byteLength;\n }\n }\n\n if (options.size !== undefined && totalBytes !== options.size) {\n throw new ValidationError(`stream delivered ${totalBytes} bytes, expected ${options.size}`);\n }\n\n let res: Response;\n if (blockIds.length === 0) {\n // Small file: one Put Blob carries content, type and metadata.\n res = await this.request(\"PUT\", path, {\n headers: { \"x-ms-blob-type\": \"BlockBlob\", \"content-type\": contentType, ...metaHeaders },\n body: concatBytes(buffer, bufferedLen),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure upload failed\", res);\n } else {\n if (bufferedLen > 0) await putBlock(concatBytes(buffer, bufferedLen));\n ensureNotAborted();\n const xml = `<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList>${blockIds\n .map((b) => `<Latest>${b}</Latest>`)\n .join(\"\")}</BlockList>`;\n res = await this.request(\"PUT\", path, {\n query: { comp: \"blocklist\" },\n headers: { \"content-type\": \"application/xml\", \"x-ms-blob-content-type\": contentType, ...metaHeaders },\n body: new TextEncoder().encode(xml),\n ...(signal ? { signal } : {}),\n });\n if (!res.ok) throw await azureError(\"Azure put block list failed\", res);\n }\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n return {\n id,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: totalBytes,\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n };\n } catch (err) {\n reader.cancel().catch(() => {});\n throw err;\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n /** Deletes the blob; a missing blob is not an error. */\n async delete(path: string, options?: OperationOptions): Promise<void> {\n const res = await this.request(\"DELETE\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return;\n if (!res.ok) throw await azureError(\"Azure delete failed\", res);\n }\n\n async getUrl(path: string, _options?: OperationOptions): Promise<string> {\n return `${this.baseUrl}/${urlEncodePath(path)}`;\n }\n\n /** Streams the blob. `options.signal` also cancels reading the body. */\n async get(path: string, options?: OperationOptions): Promise<ReadableStream<Uint8Array>> {\n const res = await this.request(\"GET\", path, withSignal(options));\n if (!res.ok) throw await azureError(\"Azure get failed\", res);\n if (!res.body) {\n throw new StorageError(\"provider-error\", \"Azure get returned no response body\", { provider: PROVIDER });\n }\n return res.body;\n }\n\n /**\n * A read-only service SAS URL for `path`, valid for `expiresIn` seconds\n * (integer in 1..604800, default 3600). Needs an account key.\n */\n async getPresignedUrl(path: string, options?: PresignedUrlOptions): Promise<string> {\n const expiresIn = options?.expiresIn ?? PRESIGN_DEFAULT_EXPIRY;\n if (!Number.isInteger(expiresIn) || expiresIn < PRESIGN_MIN_EXPIRY || expiresIn > PRESIGN_MAX_EXPIRY) {\n throw new ValidationError(\n `expiresIn must be an integer between ${PRESIGN_MIN_EXPIRY} and ${PRESIGN_MAX_EXPIRY} seconds`,\n );\n }\n const { accountName, accountKey } = this.auth;\n if (!accountKey) {\n throw new StorageError(\"config\", \"AzureDriver needs accountKey to create presigned URLs\", { provider: PROVIDER });\n }\n const sas = await blobReadSas(accountName, accountKey, this.container, path, new Date(Date.now() + expiresIn * 1000));\n return `${this.blobUrl(path)}?${sas}`;\n }\n\n /** The blob name an upload with `options.id` is stored under: `<path>/<id>.<ext>`. */\n pathFor(options: UploadOptions & { id: string }): string {\n const ext = options.filename.includes(\".\") ? options.filename.split(\".\").pop()! : \"\";\n const filename = ext ? `${options.id}.${ext}` : options.id;\n return options.path ? `${options.path}/${filename}` : filename;\n }\n\n /** The blob at `path` (a `HEAD` request), or `null` if it does not exist. */\n async head(path: string, options?: OperationOptions): Promise<StoredObject<AzureMeta> | null> {\n const res = await this.request(\"HEAD\", path, withSignal(options));\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return null;\n if (!res.ok) throw await azureError(\"Azure head failed\", res);\n\n const etag = res.headers.get(\"etag\") ?? undefined;\n const token = res.headers.get(TOKEN_HEADER) ?? undefined;\n const modified = res.headers.get(\"last-modified\");\n const contentType = res.headers.get(\"content-type\") ?? undefined;\n const name = path.split(\"/\").pop() ?? path;\n return {\n id: name.includes(\".\") ? name.slice(0, name.lastIndexOf(\".\")) : name,\n url: `${this.baseUrl}/${urlEncodePath(path)}`,\n path,\n size: Number(res.headers.get(\"content-length\") ?? 0),\n meta: { container: this.container, ...(etag ? { etag } : {}) },\n ...(token ? { uploadToken: token } : {}),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(contentType ? { contentType } : {}),\n };\n }\n\n /** Whether blocks have been staged for `path` but not yet committed. */\n async isPending(path: string, options?: OperationOptions): Promise<boolean> {\n const res = await this.request(\"GET\", path, {\n query: { comp: \"blocklist\", blocklisttype: \"uncommitted\" },\n ...withSignal(options),\n });\n if (res.status === 404 && errorCode(res) !== \"ContainerNotFound\") return false;\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure get block list failed\", res, body);\n const uncommitted = body.match(/<UncommittedBlocks>(.*?)<\\/UncommittedBlocks>/s)?.[1] ?? \"\";\n return uncommitted.includes(\"<Block>\");\n }\n\n /** One page of the blobs under `options.prefix` (List Blobs). */\n async list(options?: ListOptions): Promise<ListPage> {\n const res = await this.request(\"GET\", \"\", {\n query: {\n restype: \"container\",\n comp: \"list\",\n prefix: options?.prefix ?? \"\",\n maxresults: String(options?.limit ?? 100),\n ...(options?.recursive ? {} : { delimiter: \"/\" }),\n ...(options?.cursor !== undefined ? { marker: decodeCursor<string>(options.cursor) } : {}),\n },\n ...withSignal(options),\n });\n const body = await res.text().catch(() => \"\");\n if (!res.ok) throw azureErrorFrom(\"Azure list failed\", res, body);\n\n const objects: ListedObject[] = xmlBlocks(body, \"Blob\").map((blob) => {\n const modified = xmlValue(blob, \"Last-Modified\");\n const etag = xmlValue(blob, \"Etag\");\n const type = xmlValue(blob, \"Content-Type\");\n return {\n path: unescapeXml(xmlValue(blob, \"Name\") ?? \"\"),\n size: Number(xmlValue(blob, \"Content-Length\") ?? 0),\n ...(modified ? { lastModified: new Date(modified) } : {}),\n ...(etag ? { etag } : {}),\n ...(type ? { contentType: unescapeXml(type) } : {}),\n };\n });\n const folders = xmlBlocks(body, \"BlobPrefix\").map((p) => unescapeXml(xmlValue(p, \"Name\") ?? \"\"));\n const next = xmlValue(body, \"NextMarker\");\n return { objects, folders, ...(next ? { cursor: encodeCursor(next) } : {}) };\n }\n\n /**\n * Copies a blob in this container (Copy Blob), waiting for it to finish when\n * the service copies in the background. With `overwrite: false` the copy is\n * made only if `to` does not exist (`If-None-Match: *`), atomically.\n */\n async copy(from: string, to: string, options?: CopyOptions): Promise<void> {\n const source = new URL(this.blobUrl(from));\n if (this.auth.sasToken && !this.auth.accountKey) {\n for (const [k, v] of new URLSearchParams(this.auth.sasToken)) source.searchParams.set(k, v);\n }\n const res = await this.request(\"PUT\", to, {\n headers: { \"x-ms-copy-source\": source.href, ...(options?.overwrite ? {} : { \"if-none-match\": \"*\" }) },\n ...withSignal(options),\n });\n if (!res.ok) throw await azureError(\"Azure copy failed\", res);\n\n let status = res.headers.get(\"x-ms-copy-status\");\n for (let wait = 200; status === \"pending\"; wait = Math.min(wait * 2, 2000)) {\n await delay(wait, options?.signal);\n const head = await this.request(\"HEAD\", to, withSignal(options));\n if (!head.ok) throw await azureError(\"Azure copy status check failed\", head);\n status = head.headers.get(\"x-ms-copy-status\");\n if (status === \"failed\" || status === \"aborted\") {\n throw new StorageError(\n \"provider-error\",\n `Azure copy ${status}: ${head.headers.get(\"x-ms-copy-status-description\") ?? \"no details\"}`,\n { provider: PROVIDER },\n );\n }\n }\n }\n\n /** Copies, then deletes the source. Blob Storage has no rename. */\n async move(from: string, to: string, options?: CopyOptions): Promise<void> {\n await this.copy(from, to, options);\n await this.delete(from, options);\n }\n\n private blobUrl(path: string): string {\n return `${this.endpoint}/${this.container}/${path.split(\"/\").map(encodeURIComponent).join(\"/\")}`;\n }\n\n /** A signed (Shared Key) or SAS-authorized request to one blob. */\n private async request(\n method: string,\n path: string,\n init: {\n query?: Record<string, string>;\n headers?: Record<string, string>;\n body?: Uint8Array;\n signal?: AbortSignal;\n } = {},\n ): Promise<Response> {\n // An empty path addresses the container itself (e.g. List Blobs).\n const url = new URL(path === \"\" ? `${this.endpoint}/${this.container}` : this.blobUrl(path));\n for (const [k, v] of Object.entries(init.query ?? {})) url.searchParams.set(k, v);\n\n let headers: Record<string, string>;\n const { accountName, accountKey, sasToken } = this.auth;\n if (accountKey) {\n headers = await signRequest(method, url, accountName, accountKey, init.headers ?? {}, init.body?.byteLength);\n } else {\n for (const [k, v] of new URLSearchParams(sasToken)) url.searchParams.set(k, v);\n headers = {\n ...init.headers,\n \"x-ms-date\": new Date().toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n }\n\n return fetchWithAbort(url, {\n method,\n headers,\n ...(init.body ? { body: init.body as BodyInit } : {}),\n signal: init.signal ?? null,\n });\n }\n}\n\n/** The inner XML of every `<tag>...</tag>` block. */\nfunction xmlBlocks(xml: string, tag: string): string[] {\n return [...xml.matchAll(new RegExp(`<${tag}>(.*?)</${tag}>`, \"gs\"))].map((m) => m[1] ?? \"\");\n}\n\nfunction xmlValue(xml: string, tag: string): string | undefined {\n return xml.match(new RegExp(`<${tag}>(.*?)</${tag}>`, \"s\"))?.[1] || undefined;\n}\n\nfunction unescapeXml(value: string): string {\n return value\n .replace(/&lt;/g, \"<\")\n .replace(/&gt;/g, \">\")\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&#(\\d+);/g, (_, n: string) => String.fromCodePoint(Number(n)))\n .replace(/&#x([0-9a-f]+);/gi, (_, n: string) => String.fromCodePoint(parseInt(n, 16)))\n .replace(/&amp;/g, \"&\");\n}\n\n/** Waits `ms`, or rejects with an `AbortError` when `signal` aborts first. */\nfunction delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(abortError(\"copy aborted\"));\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(timer);\n reject(abortError(\"copy aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\nfunction withSignal(options: OperationOptions | undefined): { signal?: AbortSignal } {\n return options?.signal ? { signal: options.signal } : {};\n}\n\n/** The account name from a `<account>.blob.<suffix>` host or an Azurite-style\n * `http://host:port/<account>` endpoint. */\nfunction accountFromEndpoint(endpoint: string): string | undefined {\n const url = new URL(endpoint);\n const fromHost = url.hostname.match(/^([a-z0-9]+)\\.blob\\./)?.[1];\n return fromHost ?? (url.pathname.split(\"/\").filter(Boolean)[0] || undefined);\n}\n\n/** `fetch` that converts non-abort failures into a typed `network` error,\n * while letting aborts (`AbortError`) pass through untouched. */\nasync function fetchWithAbort(url: string | URL, init: RequestInit): Promise<Response> {\n try {\n return await fetch(url, init);\n } catch (err) {\n if (isAbortError(err)) throw err;\n const cause = err instanceof Error ? err : new Error(String(err));\n throw new StorageError(\"network\", `${PROVIDER} network error: ${cause.message}`, { provider: PROVIDER, cause });\n }\n}\n\n/** The service error code: the `x-ms-error-code` header, which Azure sends on\n * every failure (including bodiless HEAD responses), or the XML `<Code>`. */\nfunction errorCode(res: Response, body?: string): string | undefined {\n return res.headers.get(\"x-ms-error-code\") ?? body?.match(/<Code>(.*?)<\\/Code>/s)?.[1];\n}\n\nfunction azureErrorFrom(prefix: string, res: Response, body: string): StorageError {\n const code = errorCode(res, body);\n return new StorageError(\n classifyAzureError(res.status, code),\n `${prefix}: ${res.status} ${res.statusText}${code ? ` (${code})` : \"\"}`,\n { statusCode: res.status, provider: PROVIDER },\n );\n}\n\nasync function azureError(prefix: string, res: Response): Promise<StorageError> {\n const body = await res.text().catch(() => \"\");\n return azureErrorFrom(prefix, res, body);\n}\n\nfunction concatBytes(chunks: Uint8Array[], total: number): Uint8Array {\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n","// Azure Storage Shared Key and service SAS signing, using only Web Crypto.\n// https://learn.microsoft.com/rest/api/storageservices/authorize-with-shared-key\n// https://learn.microsoft.com/rest/api/storageservices/create-service-sas\n\nexport const SERVICE_VERSION = \"2023-11-03\";\n\nconst encoder = new TextEncoder();\nconst keyCache = new Map<string, Promise<CryptoKey>>();\n\nfunction importKey(accountKey: string): Promise<CryptoKey> {\n let key = keyCache.get(accountKey);\n if (!key) {\n key = globalThis.crypto.subtle.importKey(\n \"raw\",\n base64ToBytes(accountKey),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n keyCache.set(accountKey, key);\n }\n return key;\n}\n\n/** Base64 HMAC-SHA256 of `message` with the base64-encoded account key. */\nexport async function hmacBase64(accountKey: string, message: string): Promise<string> {\n const key = await importKey(accountKey);\n const sig = await globalThis.crypto.subtle.sign(\"HMAC\", key, encoder.encode(message));\n return bytesToBase64(new Uint8Array(sig));\n}\n\n/**\n * The Shared Key string-to-sign for a request. `headers` are the request\n * headers (any case); `contentLength` is the body size in bytes (0 or\n * undefined signs as an empty value, as the service requires since 2015-02-21).\n */\nexport function buildStringToSign(\n method: string,\n url: URL,\n accountName: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n): string {\n const lower: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n const h = (name: string) => lower[name] ?? \"\";\n\n const canonicalHeaders = Object.keys(lower)\n .filter((k) => k.startsWith(\"x-ms-\"))\n .sort()\n .map((k) => `${k}:${(lower[k] ?? \"\").replace(/\\s+/g, \" \").trim()}\\n`)\n .join(\"\");\n\n // Query parameters: lowercase names, sorted, decoded values joined by commas.\n const params = new Map<string, string[]>();\n for (const [k, v] of url.searchParams) {\n const name = k.toLowerCase();\n params.set(name, [...(params.get(name) ?? []), v]);\n }\n const canonicalQuery = [...params.keys()]\n .sort()\n .map((k) => `\\n${k}:${(params.get(k) ?? []).sort().join(\",\")}`)\n .join(\"\");\n const canonicalResource = `/${accountName}${url.pathname}${canonicalQuery}`;\n\n return [\n method.toUpperCase(),\n h(\"content-encoding\"),\n h(\"content-language\"),\n contentLength ? String(contentLength) : \"\",\n h(\"content-md5\"),\n h(\"content-type\"),\n h(\"date\"),\n h(\"if-modified-since\"),\n h(\"if-match\"),\n h(\"if-none-match\"),\n h(\"if-unmodified-since\"),\n h(\"range\"),\n canonicalHeaders + canonicalResource,\n ].join(\"\\n\");\n}\n\n/** `x-ms-date`, `x-ms-version` and `Authorization: SharedKey` headers for a request. */\nexport async function signRequest(\n method: string,\n url: URL,\n accountName: string,\n accountKey: string,\n headers: Record<string, string>,\n contentLength: number | undefined,\n now: Date = new Date(),\n): Promise<Record<string, string>> {\n const signed = {\n ...headers,\n \"x-ms-date\": now.toUTCString(),\n \"x-ms-version\": SERVICE_VERSION,\n };\n const stringToSign = buildStringToSign(method, url, accountName, signed, contentLength);\n const signature = await hmacBase64(accountKey, stringToSign);\n return { ...signed, authorization: `SharedKey ${accountName}:${signature}` };\n}\n\n/**\n * Query string for a read-only service SAS on one blob. `blobName` is the raw\n * (unencoded) blob name; `expiry` is when the link stops working.\n */\nexport async function blobReadSas(\n accountName: string,\n accountKey: string,\n container: string,\n blobName: string,\n expiry: Date,\n): Promise<string> {\n const se = expiry.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n const fields = {\n sp: \"r\",\n st: \"\",\n se,\n resource: `/blob/${accountName}/${container}/${blobName}`,\n si: \"\",\n sip: \"\",\n spr: \"\",\n sv: SERVICE_VERSION,\n sr: \"b\",\n snapshot: \"\",\n ses: \"\",\n rscc: \"\",\n rscd: \"\",\n rsce: \"\",\n rscl: \"\",\n rsct: \"\",\n };\n const signature = await hmacBase64(accountKey, Object.values(fields).join(\"\\n\"));\n const query = new URLSearchParams({ sv: SERVICE_VERSION, se, sr: \"b\", sp: \"r\", sig: signature });\n return query.toString();\n}\n\nexport function base64ToBytes(value: string) {\n const binary = atob(value);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n","import type { StorageErrorCode } from \"@fileway/core\";\n\n// Internal: connection-string parsing and error classification for\n// AzureDriver. Not exported from the package.\n\n// Azurite's well-known development account (public, documented by Microsoft).\nconst DEV_ACCOUNT = \"devstoreaccount1\";\nconst DEV_KEY = \"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==\";\nconst DEV_ENDPOINT = \"http://127.0.0.1:10000/devstoreaccount1\";\n\n/** Splits a connection string into the fields this driver understands. */\nexport function parseConnectionString(value: string): {\n accountName?: string;\n accountKey?: string;\n sasToken?: string;\n endpoint?: string;\n} {\n const parts: Record<string, string> = {};\n for (const segment of value.split(\";\")) {\n const eq = segment.indexOf(\"=\");\n if (eq > 0) parts[segment.slice(0, eq).trim()] = segment.slice(eq + 1).trim();\n }\n if (parts.UseDevelopmentStorage === \"true\") {\n return { accountName: DEV_ACCOUNT, accountKey: DEV_KEY, endpoint: DEV_ENDPOINT };\n }\n const accountName = parts.AccountName;\n const endpoint =\n parts.BlobEndpoint ??\n (accountName\n ? `${parts.DefaultEndpointsProtocol ?? \"https\"}://${accountName}.blob.${parts.EndpointSuffix ?? \"core.windows.net\"}`\n : undefined);\n return {\n ...(accountName ? { accountName } : {}),\n ...(parts.AccountKey ? { accountKey: parts.AccountKey } : {}),\n ...(parts.SharedAccessSignature ? { sasToken: parts.SharedAccessSignature } : {}),\n ...(endpoint ? { endpoint } : {}),\n };\n}\n\n/** Classifies an Azure failure into a typed code from the service error code,\n * falling back to the HTTP status. */\nexport function classifyAzureError(status: number, code: string | undefined): StorageErrorCode {\n switch (code) {\n case \"ContainerNotFound\":\n case \"ContainerBeingDeleted\":\n return \"bucket-not-found\";\n case \"BlobNotFound\":\n case \"CannotVerifyCopySource\":\n case \"ResourceNotFound\":\n return \"not-found\";\n case \"AuthenticationFailed\":\n case \"AuthorizationFailure\":\n case \"AuthorizationPermissionMismatch\":\n case \"AuthorizationResourceTypeMismatch\":\n case \"NoAuthenticationInformation\":\n case \"InvalidAuthenticationInfo\":\n return \"auth-failed\";\n case \"RequestBodyTooLarge\":\n case \"BlockCountExceedsLimit\":\n return \"size-exceeded\";\n case \"BlobAlreadyExists\":\n return \"conflict\";\n case \"ServerBusy\":\n case \"TooManyRequests\":\n return \"rate-limited\";\n }\n if (status === 429) return \"rate-limited\";\n if (status === 401 || status === 403) return \"auth-failed\";\n if (status === 404) return \"not-found\";\n if (status === 413) return \"size-exceeded\";\n return \"provider-error\";\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fileway/azure",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "keywords": [
6
6
  "fileway",
@@ -32,10 +32,11 @@
32
32
  "dist"
33
33
  ],
34
34
  "publishConfig": {
35
- "access": "public"
35
+ "access": "public",
36
+ "provenance": true
36
37
  },
37
38
  "dependencies": {
38
- "@fileway/core": "0.5.0"
39
+ "@fileway/core": "0.6.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "tsup": "^8.0.0",