{"id":46666241,"url":"https://github.com/akadenia/akadeniaazurestorage","last_synced_at":"2026-06-27T02:00:39.039Z","repository":{"id":222192513,"uuid":"718365262","full_name":"akadenia/AkadeniaAzureStorage","owner":"akadenia","description":"A wrapper around Azure Storage SDK to make it easier to use ","archived":false,"fork":false,"pushed_at":"2026-06-24T20:56:17.000Z","size":1509,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-06-24T22:14:59.787Z","etag":null,"topics":["akadenia","azure","azure-storage","blob-storage","managed-identity","nodejs","npm-package","queue-storage","sas","table-storage","typescript"],"latest_commit_sha":null,"homepage":"https://akadenia.com/packages/akadenia-azure","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/akadenia.png","metadata":{"files":{"readme":"README.MD","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2023-11-13T23:39:32.000Z","updated_at":"2026-06-24T20:56:21.000Z","dependencies_parsed_at":"2024-08-27T17:45:49.551Z","dependency_job_id":"f407b5a6-f046-4c3a-949b-29d4b587428a","html_url":"https://github.com/akadenia/AkadeniaAzureStorage","commit_stats":null,"previous_names":["akadenia/akadeniaazure"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/akadenia/AkadeniaAzureStorage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akadenia%2FAkadeniaAzureStorage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akadenia%2FAkadeniaAzureStorage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akadenia%2FAkadeniaAzureStorage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akadenia%2FAkadeniaAzureStorage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akadenia","download_url":"https://codeload.github.com/akadenia/AkadeniaAzureStorage/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akadenia%2FAkadeniaAzureStorage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34839005,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["akadenia","azure","azure-storage","blob-storage","managed-identity","nodejs","npm-package","queue-storage","sas","table-storage","typescript"],"created_at":"2026-03-08T18:37:33.930Z","updated_at":"2026-06-27T02:00:39.031Z","avatar_url":"https://github.com/akadenia.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://cdn.akadenia.com/images/akadenia-webp/logo/horizontal-logo.svg\" alt=\"Akadenia\" width=\"200\" /\u003e\n  \n  \u003ch2\u003e\u003ccode\u003e@akadenia/azure-storage\u003c/code\u003e\u003c/h2\u003e\n  \n  \u003cimg src=\"https://cdn.akadenia.com/images/badges/npm-version-azure-storage.svg\" alt=\"npm version\" /\u003e\n  \u003cimg src=\"https://cdn.akadenia.com/images/badges/license-mit.svg\" alt=\"License: MIT\" /\u003e\n  \u003cimg src=\"https://cdn.akadenia.com/images/badges/typescript.svg\" alt=\"TypeScript\" /\u003e\n  \n  A TypeScript library that wraps Azure Blob, Table, and Queue Storage with a clean, consistent API. Supports both connection strings and Managed Identity — so it works the same in local dev and production Azure environments.\n\n  [Documentation](https://akadenia.com/packages/akadenia-azure-storage) · [GitHub](https://github.com/akadenia/AkadeniaAzureStorage) · [Issues](https://github.com/akadenia/AkadeniaAzureStorage/issues)\n\u003c/div\u003e\n\n## Features\n\n- **Blob Storage**: Upload, download, list, and manage blobs with SAS URL support\n- **Table Storage**: Complete CRUD operations for Azure Table Storage\n- **Queue Storage**: Send, receive, and manage queue messages\n- **Managed Identity Support**: Passwordless authentication using Azure Managed Identity (system-assigned and user-assigned)\n- **SAS URL Support**: Generate Shared Access Signature URLs — User Delegation SAS with Managed Identity, Service SAS with connection strings\n- **TypeScript Support**: Full type definitions included\n- **Error Handling**: Built-in error handling and validation\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Blob Storage](#blob-storage)\n- [Table Storage](#table-storage)\n- [Queue Storage](#queue-storage)\n- [Configuration](#configuration)\n- [Error Handling](#error-handling)\n- [Best Practices](#best-practices)\n- [API Reference](#api-reference)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Installation\n\n```bash\nnpm install @akadenia/azure-storage --save\n```\n\n## Quick Start\n\n### Prerequisites\n\nYou can authenticate using either:\n- **Connection String**: Get this from the Azure Portal under your storage account's \"Access keys\" section\n- **Managed Identity**: For applications running in Azure (recommended for production)\n\n**Option 1: Connection string**\n\n```typescript\nimport { BlobStorage, TableStorage, QueueStorage } from '@akadenia/azure-storage';\n\nconst connectionString = \"DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=yourkey;EndpointSuffix=core.windows.net\";\n\nconst blobStorage  = new BlobStorage(connectionString);\nconst tableStorage = new TableStorage(connectionString, 'tableName');\nconst queueStorage = new QueueStorage(connectionString);\n```\n\n**Option 2: Managed Identity (recommended for production)**\n\n```typescript\nimport { BlobStorage, TableStorage, QueueStorage } from '@akadenia/azure-storage';\n\nconst blobStorage  = new BlobStorage({ accountName: 'yourstorageaccount' });\nconst tableStorage = new TableStorage({ accountName: 'yourstorageaccount', tableName: 'tableName' });\nconst queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });\n```\n\n---\n\n## Blob Storage\n\nThe `BlobStorage` class provides methods to interact with Azure Blob Storage.\n\n### Basic Setup\n\n```typescript\nimport { BlobStorage } from '@akadenia/azure-storage';\n\nconst blobStorage = new BlobStorage(connectionString);\n```\n\n### Container Operations\n\n```typescript\n// Create a container\nconst containerCreated = await blobStorage.createContainer('my-container');\nconsole.log('Container created:', containerCreated);\n\n// Delete a container\nconst containerDeleted = await blobStorage.deleteContainer('my-container');\nconsole.log('Container deleted:', containerDeleted);\n```\n\n### Upload Operations\n\n```typescript\n// Upload data from Buffer\nconst data = Buffer.from('Hello, Azure Blob Storage!');\nconst uploaded = await blobStorage.uploadData('my-container', 'my-blob.txt', data, {\n  blobContentType: 'text/plain'\n});\n\n// Upload from stream\nimport { Readable } from 'stream';\nconst stream = Readable.from(['Stream data content']);\nawait blobStorage.uploadStream('my-container', 'stream-blob.txt', stream, {\n  blobContentType: 'text/plain'\n});\n\n// Upload with additional headers\nawait blobStorage.uploadData('my-container', 'document.pdf', pdfBuffer, {\n  blobContentType: 'application/pdf',\n  blobCacheControl: 'max-age=3600',\n  blobContentEncoding: 'gzip'\n});\n\n// Legacy upload method\nawait blobStorage.upload('my-container', 'file.pdf', buffer, buffer.length, 'application/pdf');\n```\n\n### Download Operations\n\n```typescript\n// Download blob as Buffer\nconst blobData = await blobStorage.downloadBlob('my-container', 'my-blob.txt');\nconsole.log('Downloaded content:', blobData.toString());\n\n// Check if blob exists before downloading\nconst exists = await blobStorage.blobExists('my-container', 'my-blob.txt');\nif (exists) {\n  const data = await blobStorage.downloadBlob('my-container', 'my-blob.txt');\n}\n```\n\n### List Operations\n\n```typescript\n// List all blobs with a prefix\nconst blobs = await blobStorage.listBlobs('my-container', 'documents/');\nblobs.forEach(blob =\u003e {\n  console.log(`Blob: ${blob.name}, Size: ${blob.properties.contentLength}`);\n});\n```\n\n### Delete Operations\n\n```typescript\nconst deleted = await blobStorage.deleteBlob('my-container', 'my-blob.txt');\nconsole.log('Blob deleted:', deleted);\n```\n\n### SAS URL Generation\n\n`generateSASUrl` supports two SAS token types automatically:\n- **User Delegation SAS** — used with Managed Identity (more secure, no account keys)\n- **Service SAS** — used with connection string authentication\n\n```typescript\nimport { BlobPermissions } from '@akadenia/azure-storage';\n\n// Read-only SAS, 1 hour expiry\nconst sasUrl = await blobStorage.generateSASUrl('my-container', 'my-blob.txt', {\n  startsOn: new Date(),\n  expiresOn: new Date(Date.now() + 3600 * 1000),\n  permissions: [BlobPermissions.READ]\n});\nconsole.log('SAS URL:', sasUrl.fullUrlWithSAS);\n\n// Write-enabled container-level SAS\nconst containerSas = await blobStorage.generateSASUrl('my-container', undefined, {\n  permissions: [BlobPermissions.ADD, BlobPermissions.WRITE],\n  expiresOn: new Date(Date.now() + 24 * 3600 * 1000)\n});\n```\n\n**SAS Permissions**\n\n```typescript\nimport { BlobPermissions } from '@akadenia/azure-storage';\n\nBlobPermissions.READ    // \"r\"\nBlobPermissions.WRITE   // \"w\"\nBlobPermissions.CREATE  // \"c\"\nBlobPermissions.DELETE  // \"d\"\nBlobPermissions.ADD     // \"a\"\n```\n\n---\n\n## Table Storage\n\nThe `TableStorage` class provides methods to interact with Azure Table Storage.\n\n### Basic Setup\n\n```typescript\nimport { TableStorage, ITableEntity } from '@akadenia/azure-storage';\n\nconst tableStorage = new TableStorage(connectionString, 'MyTable');\n```\n\n### Table Management\n\n```typescript\nconst tableCreated = await tableStorage.createTable();\nconst tableDeleted = await tableStorage.deleteTable();\n```\n\n### Entity Operations\n\n```typescript\ninterface UserEntity extends ITableEntity {\n  partitionKey: string;\n  rowKey: string;\n  name: string;\n  email: string;\n  age: number;\n  isActive: boolean;\n}\n\n// Insert\nconst user: UserEntity = {\n  partitionKey: 'users',\n  rowKey: 'user-123',\n  name: 'John Doe',\n  email: 'john@example.com',\n  age: 30,\n  isActive: true\n};\nawait tableStorage.insert(user);\n\n// Get\nconst retrievedUser = await tableStorage.get('users', 'user-123');\n\n// Update\nuser.age = 31;\nawait tableStorage.update(user);\n\n// Upsert (insert or update)\nawait tableStorage.upsert(newUser);\n\n// Delete\nawait tableStorage.delete('users', 'user-123');\n```\n\n### List Operations\n\n```typescript\n// List all entities\nconst allUsers = await tableStorage.list\u003cUserEntity\u003e();\n\n// List with filter\nconst activeUsers = await tableStorage.list\u003cUserEntity\u003e({\n  queryOptions: { filter: \"isActive eq true and age gt 25\" }\n});\n\n// Access underlying TableClient for advanced operations\nconst tableClient = tableStorage.getTableClient();\n```\n\n---\n\n## Queue Storage\n\nThe `QueueStorage` class provides methods to interact with Azure Queue Storage.\n\n### Basic Setup\n\n```typescript\nimport { QueueStorage } from '@akadenia/azure-storage';\n\nconst queueStorage = new QueueStorage(connectionString);\n// or with Managed Identity:\nconst queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });\n```\n\n### Message Operations\n\n```typescript\n// Send a string message (base64 encoded by default)\nawait queueStorage.sendMessage('my-queue', 'Hello, Queue!');\n\n// Send an object (auto JSON stringified + base64 encoded)\nawait queueStorage.sendMessage('my-queue', { orderNumber: '12345', action: 'process' });\n\n// Send without base64 encoding\nawait queueStorage.sendMessage('my-queue', 'plain text', false);\n\n// Receive messages\nconst { receivedMessageItems } = await queueStorage.receiveMessages('my-queue', 5, 30);\n\n// Process and delete\nfor (const message of receivedMessageItems) {\n  const decoded = Buffer.from(message.messageText, 'base64').toString('utf-8');\n  const data = JSON.parse(decoded);\n  // ... process data ...\n  await queueStorage.deleteMessage('my-queue', message.messageId, message.popReceipt);\n}\n\n// Peek without consuming\nconst { peekedMessageItems } = await queueStorage.peekMessages('my-queue', 5);\n```\n\n### Queue Management\n\n```typescript\nawait queueStorage.createQueue('my-new-queue');\n\nconst exists = await queueStorage.queueExists('my-queue');\nconst count  = await queueStorage.getMessageCount('my-queue');\n\nawait queueStorage.clearMessages('my-queue');\nawait queueStorage.deleteQueue('old-queue');\n\n// Access underlying QueueClient for advanced options\nconst queueClient = queueStorage.getQueueClient('my-queue');\nawait queueClient.sendMessage('Custom', {\n  visibilityTimeoutInSeconds: 30,\n  timeToLiveInSeconds: 3600\n});\n```\n\n### Integration Example: Order Processing Pipeline\n\n```typescript\nclass OrderProcessor {\n  private queueStorage: QueueStorage;\n\n  constructor(connectionString: string) {\n    this.queueStorage = new QueueStorage(connectionString);\n  }\n\n  async queueOrder(orderNumber: string): Promise\u003cvoid\u003e {\n    await this.queueStorage.sendMessage('orders-to-process', { orderNumber, timestamp: Date.now() });\n  }\n\n  async processOrders(): Promise\u003cvoid\u003e {\n    const { receivedMessageItems } = await this.queueStorage.receiveMessages('orders-to-process', 10);\n\n    for (const message of receivedMessageItems) {\n      try {\n        const decoded = Buffer.from(message.messageText, 'base64').toString('utf-8');\n        const order = JSON.parse(decoded);\n        console.log(`Processing order ${order.orderNumber}`);\n        await this.queueStorage.deleteMessage('orders-to-process', message.messageId, message.popReceipt);\n      } catch (error) {\n        console.error('Failed to process message:', error);\n        // Message will become visible again after visibility timeout\n      }\n    }\n  }\n}\n```\n\n---\n\n## Configuration\n\n### Environment Variables\n\n```typescript\nconst connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;\nif (!connectionString) throw new Error('AZURE_STORAGE_CONNECTION_STRING is required');\n```\n\n### Local Development with Azurite\n\n```typescript\nconst blobStorage  = new BlobStorage(\"UseDevelopmentStorage=true\");\nconst tableStorage = new TableStorage(\"UseDevelopmentStorage=true\", 'MyTable');\nconst queueStorage = new QueueStorage(\"UseDevelopmentStorage=true\");\n```\n\n### Managed Identity Authentication\n\n**System-Assigned (Most Common)**\n\nAutomatically created when you enable managed identity on your Azure resource — no client ID needed.\n\n```typescript\nconst blobStorage  = new BlobStorage({ accountName: 'yourstorageaccount' });\nconst tableStorage = new TableStorage({ accountName: 'yourstorageaccount', tableName: 'MyTable' });\nconst queueStorage = new QueueStorage({ accountName: 'yourstorageaccount' });\n```\n\n**User-Assigned**\n\nA standalone identity shared across multiple Azure resources. Requires the client ID.\n\n```typescript\nconst blobStorage = new BlobStorage({\n  accountName: 'yourstorageaccount',\n  managedIdentityClientId: 'your-client-id'\n});\n```\n\n**Environment-Based Configuration**\n\n```typescript\nconst blobStorage = process.env.NODE_ENV === 'production'\n  ? new BlobStorage({ accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME! })\n  : new BlobStorage(process.env.AZURE_STORAGE_CONNECTION_STRING!);\n```\n\n\u003e **Note:** When using Managed Identity, ensure your Azure resource has the appropriate role assignments:\n\u003e - **Storage Blob Data Contributor** for Blob Storage\n\u003e - **Storage Table Data Contributor** for Table Storage\n\u003e - **Storage Queue Data Contributor** for Queue Storage\n\n---\n\n## Error Handling\n\n```typescript\ntry {\n  const blobData = await blobStorage.downloadBlob('container', 'non-existent.txt');\n} catch (error) {\n  if (error.statusCode === 404) {\n    console.log('Blob not found');\n  } else {\n    console.error('Error downloading blob:', error);\n  }\n}\n```\n\n---\n\n## Best Practices\n\n### 1. Use Managed Identity in Production\n\n```typescript\nconst blobStorage = process.env.NODE_ENV === 'production'\n  ? new BlobStorage({ accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME! })\n  : new BlobStorage(process.env.AZURE_STORAGE_CONNECTION_STRING!);\n```\n\n### 2. Short-Lived SAS URLs\n\n```typescript\nconst sasUrl = await blobStorage.generateSASUrl('container', 'blob', {\n  permissions: [BlobPermissions.READ],\n  expiresOn: new Date(Date.now() + 3600 * 1000) // 1 hour max\n});\n```\n\n### 3. Always Handle Errors\n\n```typescript\nasync function safeUpload(container: string, name: string, data: Buffer) {\n  try {\n    return await blobStorage.uploadData(container, name, data);\n  } catch (error) {\n    console.error('Upload failed:', error);\n    throw error;\n  }\n}\n```\n\n### 4. Clean Up Temporary Resources\n\n```typescript\nasync function cleanup() {\n  try {\n    await blobStorage.deleteContainer('temp-container');\n    await tableStorage.deleteTable();\n  } catch (error) {\n    console.error('Cleanup failed:', error);\n  }\n}\n```\n\n---\n\n## API Reference\n\n### BlobStorage\n\n| Method | Description | Returns |\n|---|---|---|\n| `createContainer(containerName)` | Creates a container if it doesn't exist | `Promise\u003cboolean\u003e` |\n| `deleteContainer(containerName)` | Deletes a container | `Promise\u003cboolean\u003e` |\n| `upload(container, blob, buffer, length, contentType)` | Uploads a buffer (legacy) | `Promise\u003cboolean\u003e` |\n| `uploadData(container, blob, data, headers?)` | Uploads a Buffer with optional headers | `Promise\u003cboolean\u003e` |\n| `uploadStream(container, blob, stream, headers?)` | Uploads a readable stream | `Promise\u003cboolean\u003e` |\n| `downloadBlob(container, blob)` | Downloads a blob as Buffer | `Promise\u003cBuffer\u003e` |\n| `blobExists(container, blob)` | Checks if a blob exists | `Promise\u003cboolean\u003e` |\n| `listBlobs(container, prefix)` | Lists blobs with a prefix | `Promise\u003cBlobItem[]\u003e` |\n| `deleteBlob(container, blob)` | Deletes a blob | `Promise\u003cboolean\u003e` |\n| `generateSASUrl(container, blob?, options?)` | Generates a SAS URL | `Promise\u003cSASUrlComponents\u003e` |\n\n### TableStorage\n\n| Method | Description | Returns |\n|---|---|---|\n| `createTable()` | Creates the table | `Promise\u003cboolean\u003e` |\n| `deleteTable()` | Deletes the table | `Promise\u003cboolean\u003e` |\n| `insert(entity)` | Inserts an entity | `Promise\u003cboolean\u003e` |\n| `update(entity)` | Updates an entity | `Promise\u003cboolean\u003e` |\n| `upsert(entity)` | Inserts or updates an entity | `Promise\u003cboolean\u003e` |\n| `get(partitionKey, rowKey)` | Gets an entity | `Promise\u003cGetTableEntityResponse\u003e` |\n| `delete(partitionKey, rowKey)` | Deletes an entity | `Promise\u003cboolean\u003e` |\n| `list(options?)` | Lists entities with optional filter | `Promise\u003cT[]\u003e` |\n| `getTableClient()` | Returns the underlying TableClient | `TableClient` |\n\n### QueueStorage\n\n| Method | Description | Returns |\n|---|---|---|\n| `getQueueClient(queueName)` | Gets a QueueClient | `QueueClient` |\n| `sendMessage(queue, message, base64Encode?)` | Sends a message | `Promise\u003cQueueSendMessageResponse\u003e` |\n| `receiveMessages(queue, maxMessages?, visibilityTimeout?)` | Receives messages | `Promise\u003cany\u003e` |\n| `deleteMessage(queue, messageId, popReceipt)` | Deletes a message | `Promise\u003cvoid\u003e` |\n| `peekMessages(queue, maxMessages?)` | Peeks messages without consuming | `Promise\u003cany\u003e` |\n| `clearMessages(queue)` | Clears all messages | `Promise\u003cvoid\u003e` |\n| `createQueue(queue)` | Creates a queue | `Promise\u003cvoid\u003e` |\n| `deleteQueue(queue)` | Deletes a queue | `Promise\u003cvoid\u003e` |\n| `queueExists(queue)` | Checks if a queue exists | `Promise\u003cboolean\u003e` |\n| `getMessageCount(queue)` | Gets approximate message count | `Promise\u003cnumber\u003e` |\n\n---\n\n## Contributing\n\nWe welcome contributions! Please feel free to submit a Pull Request.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/akadenia/AkadeniaAzureStorage.git\ncd AkadeniaAzureStorage\npnpm install\npnpm run build\npnpm test:with-azurite  # requires Azurite running locally\n```\n\n### Commit Message Guidelines\n\nWe follow [Conventional Commits](https://www.conventionalcommits.org/). Scope is required.\n\n```text\ntype(scope): description\n```\n\n**Common scopes:** `blob` · `table` · `queue` · `docs` · `deps` · `test` · `build` · `ci`\n\n**Types:** `feat` · `fix` · `docs` · `style` · `refactor` · `test` · `chore`\n\n## Requirements\n\n- Node.js \u003e= 20\n- Azure Storage account (or Azurite for local development)\n\n## License\n\n[MIT](https://github.com/akadenia/AkadeniaAzureStorage/blob/main/LICENSE)\n\n## Support\n\nFor support, please open an issue on [GitHub](https://github.com/akadenia/AkadeniaAzureStorage/issues).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakadenia%2Fakadeniaazurestorage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakadenia%2Fakadeniaazurestorage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakadenia%2Fakadeniaazurestorage/lists"}