[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XEX0C1H7VGFHQYNFPK0S7P":3,"skill-same:01M6XEX0C1H7VGFHQYNFPK0S7P":63},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":14,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":48,"install":49,"resources":62},"01M6XEX0C1H7VGFHQYNFPK0S7P","azure-storage-queue-ts",null,"Azure Queue Storage JavaScript\u002FTypeScript SDK (@azure\u002Fstorage-queue) for message queue operations. Use for sending, receiving, peeking, and deleting messages in queues.","sapsapshen\u002Fai-memory-router","sapsapshen","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F20347959?v=4","01M6XDQEF1DP50DKF37H81J3J5","shenmian","01M6XDQEJPE41Y3DT2763X8ZJN",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fazure-storage-queue-ts\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36],"storage","azure storage","azure","azure storage queue ts","queue","use","2026","queues","unknown","for","azure queue storage","message","community","typescript","operations","messages","javascript","sdk","2026-11-10 10:12:30",false,"---\nname: azure-storage-queue-ts\ndescription: Azure Queue Storage JavaScript\u002FTypeScript SDK (@azure\u002Fstorage-queue) for message queue operations. Use for sending, receiving, peeking, and deleting messages in queues.\nrisk: unknown\nsource: community\ndate_added: '2026-02-27'\n---\n\n# @azure\u002Fstorage-queue (TypeScript\u002FJavaScript)\n\nSDK for Azure Queue Storage operations — send, receive, peek, and manage messages in queues.\n\n## Installation\n\n```bash\nnpm install @azure\u002Fstorage-queue @azure\u002Fidentity\n```\n\n**Current Version**: 12.x  \n**Node.js**: >= 18.0.0\n\n## Environment Variables\n\n```bash\nAZURE_STORAGE_ACCOUNT_NAME=\u003Caccount-name>\nAZURE_STORAGE_ACCOUNT_KEY=\u003Caccount-key>\n# OR connection string\nAZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...\n```\n\n## Authentication\n\n### DefaultAzureCredential (Recommended)\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\nimport { DefaultAzureCredential } from \"@azure\u002Fidentity\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net`,\n  new DefaultAzureCredential()\n);\n```\n\n### Connection String\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\n\nconst client = QueueServiceClient.fromConnectionString(\n  process.env.AZURE_STORAGE_CONNECTION_STRING!\n);\n```\n\n### StorageSharedKeyCredential (Node.js only)\n\n```typescript\nimport { QueueServiceClient, StorageSharedKeyCredential } from \"@azure\u002Fstorage-queue\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;\n\nconst sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net`,\n  sharedKeyCredential\n);\n```\n\n### SAS Token\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!;\n\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net${sasToken}`\n);\n```\n\n## Client Hierarchy\n\n```\nQueueServiceClient (account level)\n└── QueueClient (queue level)\n    └── Messages (send, receive, peek, delete)\n```\n\n## Queue Operations\n\n### Create Queue\n\n```typescript\nconst queueClient = client.getQueueClient(\"my-queue\");\nawait queueClient.create();\n\n\u002F\u002F Or create if not exists\nawait queueClient.createIfNotExists();\n```\n\n### List Queues\n\n```typescript\nfor await (const queue of client.listQueues()) {\n  console.log(queue.name);\n}\n\n\u002F\u002F With prefix filter\nfor await (const queue of client.listQueues({ prefix: \"task-\" })) {\n  console.log(queue.name);\n}\n```\n\n### Delete Queue\n\n```typescript\nawait queueClient.delete();\n\n\u002F\u002F Or delete if exists\nawait queueClient.deleteIfExists();\n```\n\n### Get Queue Properties\n\n```typescript\nconst properties = await queueClient.getProperties();\nconsole.log(\"Approximate message count:\", properties.approximateMessagesCount);\nconsole.log(\"Metadata:\", properties.metadata);\n```\n\n### Set Queue Metadata\n\n```typescript\nawait queueClient.setMetadata({\n  department: \"engineering\",\n  priority: \"high\",\n});\n```\n\n## Message Operations\n\n### Send Message\n\n```typescript\nconst queueClient = client.getQueueClient(\"my-queue\");\n\n\u002F\u002F Simple message\nawait queueClient.sendMessage(\"Hello, World!\");\n\n\u002F\u002F With options\nawait queueClient.sendMessage(\"Delayed message\", {\n  visibilityTimeout: 60, \u002F\u002F Hidden for 60 seconds\n  messageTimeToLive: 3600, \u002F\u002F Expires in 1 hour\n});\n\n\u002F\u002F JSON message (must be string)\nconst task = { type: \"process\", data: { id: 123 } };\nawait queueClient.sendMessage(JSON.stringify(task));\n```\n\n### Receive Messages\n\n```typescript\n\u002F\u002F Receive up to 32 messages (default: 1)\nconst response = await queueClient.receiveMessages({\n  numberOfMessages: 10,\n  visibilityTimeout: 30, \u002F\u002F 30 seconds to process\n});\n\nfor (const message of response.receivedMessageItems) {\n  console.log(\"Message ID:\", message.messageId);\n  console.log(\"Content:\", message.messageText);\n  console.log(\"Dequeue Count:\", message.dequeueCount);\n  console.log(\"Pop Receipt:\", message.popReceipt);\n  \n  \u002F\u002F Process the message...\n  \n  \u002F\u002F Delete after processing\n  await queueClient.deleteMessage(message.messageId, message.popReceipt);\n}\n```\n\n### Peek Messages\n\nPeek without removing from queue (no visibility timeout).\n\n```typescript\nconst response = await queueClient.peekMessages({\n  numberOfMessages: 5,\n});\n\nfor (const message of response.peekedMessageItems) {\n  console.log(\"Message ID:\", message.messageId);\n  console.log(\"Content:\", message.messageText);\n  \u002F\u002F Note: No popReceipt - cannot delete peeked messages\n}\n```\n\n### Update Message\n\nExtend visibility timeout or update content.\n\n```typescript\n\u002F\u002F Receive a message\nconst response = await queueClient.receiveMessages();\nconst message = response.receivedMessageItems[0];\n\nif (message) {\n  \u002F\u002F Update content and extend visibility\n  const updateResponse = await queueClient.updateMessage(\n    message.messageId,\n    message.popReceipt,\n    \"Updated content\",\n    60 \u002F\u002F New visibility timeout in seconds\n  );\n  \n  \u002F\u002F Use new popReceipt for subsequent operations\n  console.log(\"New pop receipt:\", updateResponse.popReceipt);\n}\n```\n\n### Delete Message\n\n```typescript\n\u002F\u002F After receiving\nconst response = await queueClient.receiveMessages();\nconst message = response.receivedMessageItems[0];\n\nif (message) {\n  await queueClient.deleteMessage(message.messageId, message.popReceipt);\n}\n```\n\n### Clear All Messages\n\n```typescript\nawait queueClient.clearMessages();\n```\n\n## Message Processing Patterns\n\n### Basic Worker Pattern\n\n```typescript\nasync function processQueue(queueClient: QueueClient): Promise\u003Cvoid> {\n  while (true) {\n    const response = await queueClient.receiveMessages({\n      numberOfMessages: 10,\n      visibilityTimeout: 30,\n    });\n\n    if (response.receivedMessageItems.length === 0) {\n      \u002F\u002F No messages, wait before polling again\n      await sleep(5000);\n      continue;\n    }\n\n    for (const message of response.receivedMessageItems) {\n      try {\n        await processMessage(message.messageText);\n        await queueClient.deleteMessage(message.messageId, message.popReceipt);\n      } catch (error) {\n        console.error(`Failed to process message ${message.messageId}:`, error);\n        \u002F\u002F Message will become visible again after timeout\n      }\n    }\n  }\n}\n\nasync function processMessage(content: string): Promise\u003Cvoid> {\n  const task = JSON.parse(content);\n  \u002F\u002F Process task...\n}\n\nfunction sleep(ms: number): Promise\u003Cvoid> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n```\n\n### Poison Message Handling\n\n```typescript\nconst MAX_DEQUEUE_COUNT = 5;\n\nasync function processWithPoisonHandling(\n  queueClient: QueueClient,\n  poisonQueueClient: QueueClient\n): Promise\u003Cvoid> {\n  const response = await queueClient.receiveMessages({\n    numberOfMessages: 10,\n    visibilityTimeout: 30,\n  });\n\n  for (const message of response.receivedMessageItems) {\n    if (message.dequeueCount > MAX_DEQUEUE_COUNT) {\n      \u002F\u002F Move to poison queue\n      await poisonQueueClient.sendMessage(message.messageText);\n      await queueClient.deleteMessage(message.messageId, message.popReceipt);\n      console.log(`Moved message ${message.messageId} to poison queue`);\n      continue;\n    }\n\n    try {\n      await processMessage(message.messageText);\n      await queueClient.deleteMessage(message.messageId, message.popReceipt);\n    } catch (error) {\n      console.error(`Processing failed (attempt ${message.dequeueCount}):`, error);\n    }\n  }\n}\n```\n\n### Batch Processing with Visibility Extension\n\n```typescript\nasync function processBatchWithExtension(queueClient: QueueClient): Promise\u003Cvoid> {\n  const response = await queueClient.receiveMessages({\n    numberOfMessages: 1,\n    visibilityTimeout: 60,\n  });\n\n  const message = response.receivedMessageItems[0];\n  if (!message) return;\n\n  let popReceipt = message.popReceipt;\n\n  \u002F\u002F Start visibility extension timer\n  const extensionInterval = setInterval(async () => {\n    try {\n      const updateResponse = await queueClient.updateMessage(\n        message.messageId,\n        popReceipt,\n        message.messageText,\n        60 \u002F\u002F Extend by another 60 seconds\n      );\n      popReceipt = updateResponse.popReceipt;\n    } catch (error) {\n      console.error(\"Failed to extend visibility:\", error);\n    }\n  }, 45000); \u002F\u002F Extend every 45 seconds\n\n  try {\n    await longRunningProcess(message.messageText);\n    await queueClient.deleteMessage(message.messageId, popReceipt);\n  } finally {\n    clearInterval(extensionInterval);\n  }\n}\n```\n\n## Message Encoding\n\nBy default, messages are Base64 encoded. You can customize this:\n\n```typescript\nimport { QueueClient } from \"@azure\u002Fstorage-queue\";\n\n\u002F\u002F Custom encoder\u002Fdecoder for plain text\nconst queueClient = new QueueClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue`,\n  credential,\n  {\n    messageEncoding: \"text\", \u002F\u002F \"base64\" (default) or \"text\"\n  }\n);\n\n\u002F\u002F Or with custom encoder\nconst customQueueClient = new QueueClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue`,\n  credential,\n  {\n    messageEncoding: {\n      encode: (message: string) => Buffer.from(message).toString(\"base64\"),\n      decode: (message: string) => Buffer.from(message, \"base64\").toString(),\n    },\n  }\n);\n```\n\n## SAS Token Generation (Node.js only)\n\n### Generate Queue SAS\n\n```typescript\nimport {\n  QueueSASPermissions,\n  generateQueueSASQueryParameters,\n  StorageSharedKeyCredential,\n} from \"@azure\u002Fstorage-queue\";\n\nconst sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);\n\nconst sasToken = generateQueueSASQueryParameters(\n  {\n    queueName: \"my-queue\",\n    permissions: QueueSASPermissions.parse(\"raup\"), \u002F\u002F read, add, update, process\n    startsOn: new Date(),\n    expiresOn: new Date(Date.now() + 3600 * 1000), \u002F\u002F 1 hour\n  },\n  sharedKeyCredential\n).toString();\n\nconst sasUrl = `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue?${sasToken}`;\n```\n\n### Generate Account SAS\n\n```typescript\nimport {\n  AccountSASPermissions,\n  AccountSASResourceTypes,\n  AccountSASServices,\n  generateAccountSASQueryParameters,\n} from \"@azure\u002Fstorage-queue\";\n\nconst sasToken = generateAccountSASQueryParameters(\n  {\n    services: AccountSASServices.parse(\"q\").toString(), \u002F\u002F queue\n    resourceTypes: AccountSASResourceTypes.parse(\"sco\").toString(),\n    permissions: AccountSASPermissions.parse(\"rwdlacupi\"),\n    expiresOn: new Date(Date.now() + 24 * 3600 * 1000),\n  },\n  sharedKeyCredential\n).toString();\n```\n\n## Error Handling\n\n```typescript\nimport { RestError } from \"@azure\u002Fstorage-queue\";\n\ntry {\n  await queueClient.sendMessage(\"test\");\n} catch (error) {\n  if (error instanceof RestError) {\n    switch (error.statusCode) {\n      case 404:\n        console.log(\"Queue not found\");\n        break;\n      case 400:\n        console.log(\"Bad request - message too large or invalid\");\n        break;\n      case 403:\n        console.log(\"Access denied\");\n        break;\n      case 409:\n        console.log(\"Queue already exists or being deleted\");\n        break;\n      default:\n        console.error(`Storage error ${error.statusCode}: ${error.message}`);\n    }\n  }\n  throw error;\n}\n```\n\n## TypeScript Types Reference\n\n```typescript\nimport {\n  \u002F\u002F Clients\n  QueueServiceClient,\n  QueueClient,\n\n  \u002F\u002F Authentication\n  StorageSharedKeyCredential,\n  AnonymousCredential,\n\n  \u002F\u002F SAS\n  QueueSASPermissions,\n  AccountSASPermissions,\n  AccountSASServices,\n  AccountSASResourceTypes,\n  generateQueueSASQueryParameters,\n  generateAccountSASQueryParameters,\n\n  \u002F\u002F Messages\n  DequeuedMessageItem,\n  PeekedMessageItem,\n  QueueSendMessageResponse,\n  QueueReceiveMessageResponse,\n  QueueUpdateMessageResponse,\n\n  \u002F\u002F Queue\n  QueueItem,\n  QueueGetPropertiesResponse,\n\n  \u002F\u002F Errors\n  RestError,\n} from \"@azure\u002Fstorage-queue\";\n```\n\n## Message Limits\n\n| Limit | Value |\n|-------|-------|\n| Max message size | 64 KB |\n| Max visibility timeout | 7 days |\n| Max time-to-live | 7 days (or -1 for infinite) |\n| Max messages per receive | 32 |\n| Default visibility timeout | 30 seconds |\n\n## Best Practices\n\n1. **Use DefaultAzureCredential** — Prefer AAD over connection strings\u002Fkeys\n2. **Always delete after processing** — Prevent duplicate processing\n3. **Handle poison messages** — Move failed messages to a dead-letter queue\n4. **Use appropriate visibility timeout** — Set based on expected processing time\n5. **Extend visibility for long tasks** — Update message to prevent timeout\n6. **Use JSON for structured data** — Serialize objects to JSON strings\n7. **Check dequeueCount** — Detect repeatedly failing messages\n8. **Use batch receive** — Receive multiple messages for efficiency\n\n## Platform Differences\n\n| Feature | Node.js | Browser |\n|---------|---------|---------|\n| `StorageSharedKeyCredential` | ✅ | ❌ |\n| SAS generation | ✅ | ❌ |\n| DefaultAzureCredential | ✅ | ❌ |\n| Anonymous\u002FSAS access | ✅ | ✅ |\n| All message operations | ✅ | ✅ |\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n","# @azure\u002Fstorage-queue (TypeScript\u002FJavaScript)\n\nSDK for Azure Queue Storage operations — send, receive, peek, and manage messages in queues.\n\n## Installation\n\n```bash\nnpm install @azure\u002Fstorage-queue @azure\u002Fidentity\n```\n\n**Current Version**: 12.x  \n**Node.js**: >= 18.0.0\n\n## Environment Variables\n\n```bash\nAZURE_STORAGE_ACCOUNT_NAME=\u003Caccount-name>\nAZURE_STORAGE_ACCOUNT_KEY=\u003Caccount-key>\n# OR connection string\nAZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...\n```\n\n## Authentication\n\n### DefaultAzureCredential (Recommended)\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\nimport { DefaultAzureCredential } from \"@azure\u002Fidentity\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net`,\n  new DefaultAzureCredential()\n);\n```\n\n### Connection String\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\n\nconst client = QueueServiceClient.fromConnectionString(\n  process.env.AZURE_STORAGE_CONNECTION_STRING!\n);\n```\n\n### StorageSharedKeyCredential (Node.js only)\n\n```typescript\nimport { QueueServiceClient, StorageSharedKeyCredential } from \"@azure\u002Fstorage-queue\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;\n\nconst sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net`,\n  sharedKeyCredential\n);\n```\n\n### SAS Token\n\n```typescript\nimport { QueueServiceClient } from \"@azure\u002Fstorage-queue\";\n\nconst accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;\nconst sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!;\n\nconst client = new QueueServiceClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net${sasToken}`\n);\n```\n\n## Client Hierarchy\n\n```\nQueueServiceClient (account level)\n└── QueueClient (queue level)\n    └── Messages (send, receive, peek, delete)\n```\n\n## Queue Operations\n\n### Create Queue\n\n```typescript\nconst queueClient = client.getQueueClient(\"my-queue\");\nawait queueClient.create();\n\n\u002F\u002F Or create if not exists\nawait queueClient.createIfNotExists();\n```\n\n### List Queues\n\n```typescript\nfor await (const queue of client.listQueues()) {\n  console.log(queue.name);\n}\n\n\u002F\u002F With prefix filter\nfor await (const queue of client.listQueues({ prefix: \"task-\" })) {\n  console.log(queue.name);\n}\n```\n\n### Delete Queue\n\n```typescript\nawait queueClient.delete();\n\n\u002F\u002F Or delete if exists\nawait queueClient.deleteIfExists();\n```\n\n### Get Queue Properties\n\n```typescript\nconst properties = await queueClient.getProperties();\nconsole.log(\"Approximate message count:\", properties.approximateMessagesCount);\nconsole.log(\"Metadata:\", properties.metadata);\n```\n\n### Set Queue Metadata\n\n```typescript\nawait queueClient.setMetadata({\n  department: \"engineering\",\n  priority: \"high\",\n});\n```\n\n## Message Operations\n\n### Send Message\n\n```typescript\nconst queueClient = client.getQueueClient(\"my-queue\");\n\n\u002F\u002F Simple message\nawait queueClient.sendMessage(\"Hello, World!\");\n\n\u002F\u002F With options\nawait queueClient.sendMessage(\"Delayed message\", {\n  visibilityTimeout: 60, \u002F\u002F Hidden for 60 seconds\n  messageTimeToLive: 3600, \u002F\u002F Expires in 1 hour\n});\n\n\u002F\u002F JSON message (must be string)\nconst task = { type: \"process\", data: { id: 123 } };\nawait queueClient.sendMessage(JSON.stringify(task));\n```\n\n### Receive Messages\n\n```typescript\n\u002F\u002F Receive up to 32 messages (default: 1)\nconst response = await queueClient.receiveMessages({\n  numberOfMessages: 10,\n  visibilityTimeout: 30, \u002F\u002F 30 seconds to process\n});\n\nfor (const message of response.receivedMessageItems) {\n  console.log(\"Message ID:\", message.messageId);\n  console.log(\"Content:\", message.messageText);\n  console.log(\"Dequeue Count:\", message.dequeueCount);\n  console.log(\"Pop Receipt:\", message.popReceipt);\n  \n  \u002F\u002F Process the message...\n  \n  \u002F\u002F Delete after processing\n  await queueClient.deleteMessage(message.messageId, message.popReceipt);\n}\n```\n\n### Peek Messages\n\nPeek without removing from queue (no visibility timeout).\n\n```typescript\nconst response = await queueClient.peekMessages({\n  numberOfMessages: 5,\n});\n\nfor (const message of response.peekedMessageItems) {\n  console.log(\"Message ID:\", message.messageId);\n  console.log(\"Content:\", message.messageText);\n  \u002F\u002F Note: No popReceipt - cannot delete peeked messages\n}\n```\n\n### Update Message\n\nExtend visibility timeout or update content.\n\n```typescript\n\u002F\u002F Receive a message\nconst response = await queueClient.receiveMessages();\nconst message = response.receivedMessageItems[0];\n\nif (message) {\n  \u002F\u002F Update content and extend visibility\n  const updateResponse = await queueClient.updateMessage(\n    message.messageId,\n    message.popReceipt,\n    \"Updated content\",\n    60 \u002F\u002F New visibility timeout in seconds\n  );\n  \n  \u002F\u002F Use new popReceipt for subsequent operations\n  console.log(\"New pop receipt:\", updateResponse.popReceipt);\n}\n```\n\n### Delete Message\n\n```typescript\n\u002F\u002F After receiving\nconst response = await queueClient.receiveMessages();\nconst message = response.receivedMessageItems[0];\n\nif (message) {\n  await queueClient.deleteMessage(message.messageId, message.popReceipt);\n}\n```\n\n### Clear All Messages\n\n```typescript\nawait queueClient.clearMessages();\n```\n\n## Message Processing Patterns\n\n### Basic Worker Pattern\n\n```typescript\nasync function processQueue(queueClient: QueueClient): Promise\u003Cvoid> {\n  while (true) {\n    const response = await queueClient.receiveMessages({\n      numberOfMessages: 10,\n      visibilityTimeout: 30,\n    });\n\n    if (response.receivedMessageItems.length === 0) {\n      \u002F\u002F No messages, wait before polling again\n      await sleep(5000);\n      continue;\n    }\n\n    for (const message of response.receivedMessageItems) {\n      try {\n        await processMessage(message.messageText);\n        await queueClient.deleteMessage(message.messageId, message.popReceipt);\n      } catch (error) {\n        console.error(`Failed to process message ${message.messageId}:`, error);\n        \u002F\u002F Message will become visible again after timeout\n      }\n    }\n  }\n}\n\nasync function processMessage(content: string): Promise\u003Cvoid> {\n  const task = JSON.parse(content);\n  \u002F\u002F Process task...\n}\n\nfunction sleep(ms: number): Promise\u003Cvoid> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n```\n\n### Poison Message Handling\n\n```typescript\nconst MAX_DEQUEUE_COUNT = 5;\n\nasync function processWithPoisonHandling(\n  queueClient: QueueClient,\n  poisonQueueClient: QueueClient\n): Promise\u003Cvoid> {\n  const response = await queueClient.receiveMessages({\n    numberOfMessages: 10,\n    visibilityTimeout: 30,\n  });\n\n  for (const message of response.receivedMessageItems) {\n    if (message.dequeueCount > MAX_DEQUEUE_COUNT) {\n      \u002F\u002F Move to poison queue\n      await poisonQueueClient.sendMessage(message.messageText);\n      await queueClient.deleteMessage(message.messageId, message.popReceipt);\n      console.log(`Moved message ${message.messageId} to poison queue`);\n      continue;\n    }\n\n    try {\n      await processMessage(message.messageText);\n      await queueClient.deleteMessage(message.messageId, message.popReceipt);\n    } catch (error) {\n      console.error(`Processing failed (attempt ${message.dequeueCount}):`, error);\n    }\n  }\n}\n```\n\n### Batch Processing with Visibility Extension\n\n```typescript\nasync function processBatchWithExtension(queueClient: QueueClient): Promise\u003Cvoid> {\n  const response = await queueClient.receiveMessages({\n    numberOfMessages: 1,\n    visibilityTimeout: 60,\n  });\n\n  const message = response.receivedMessageItems[0];\n  if (!message) return;\n\n  let popReceipt = message.popReceipt;\n\n  \u002F\u002F Start visibility extension timer\n  const extensionInterval = setInterval(async () => {\n    try {\n      const updateResponse = await queueClient.updateMessage(\n        message.messageId,\n        popReceipt,\n        message.messageText,\n        60 \u002F\u002F Extend by another 60 seconds\n      );\n      popReceipt = updateResponse.popReceipt;\n    } catch (error) {\n      console.error(\"Failed to extend visibility:\", error);\n    }\n  }, 45000); \u002F\u002F Extend every 45 seconds\n\n  try {\n    await longRunningProcess(message.messageText);\n    await queueClient.deleteMessage(message.messageId, popReceipt);\n  } finally {\n    clearInterval(extensionInterval);\n  }\n}\n```\n\n## Message Encoding\n\nBy default, messages are Base64 encoded. You can customize this:\n\n```typescript\nimport { QueueClient } from \"@azure\u002Fstorage-queue\";\n\n\u002F\u002F Custom encoder\u002Fdecoder for plain text\nconst queueClient = new QueueClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue`,\n  credential,\n  {\n    messageEncoding: \"text\", \u002F\u002F \"base64\" (default) or \"text\"\n  }\n);\n\n\u002F\u002F Or with custom encoder\nconst customQueueClient = new QueueClient(\n  `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue`,\n  credential,\n  {\n    messageEncoding: {\n      encode: (message: string) => Buffer.from(message).toString(\"base64\"),\n      decode: (message: string) => Buffer.from(message, \"base64\").toString(),\n    },\n  }\n);\n```\n\n## SAS Token Generation (Node.js only)\n\n### Generate Queue SAS\n\n```typescript\nimport {\n  QueueSASPermissions,\n  generateQueueSASQueryParameters,\n  StorageSharedKeyCredential,\n} from \"@azure\u002Fstorage-queue\";\n\nconst sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);\n\nconst sasToken = generateQueueSASQueryParameters(\n  {\n    queueName: \"my-queue\",\n    permissions: QueueSASPermissions.parse(\"raup\"), \u002F\u002F read, add, update, process\n    startsOn: new Date(),\n    expiresOn: new Date(Date.now() + 3600 * 1000), \u002F\u002F 1 hour\n  },\n  sharedKeyCredential\n).toString();\n\nconst sasUrl = `https:\u002F\u002F${accountName}.queue.core.windows.net\u002Fmy-queue?${sasToken}`;\n```\n\n### Generate Account SAS\n\n```typescript\nimport {\n  AccountSASPermissions,\n  AccountSASResourceTypes,\n  AccountSASServices,\n  generateAccountSASQueryParameters,\n} from \"@azure\u002Fstorage-queue\";\n\nconst sasToken = generateAccountSASQueryParameters(\n  {\n    services: AccountSASServices.parse(\"q\").toString(), \u002F\u002F queue\n    resourceTypes: AccountSASResourceTypes.parse(\"sco\").toString(),\n    permissions: AccountSASPermissions.parse(\"rwdlacupi\"),\n    expiresOn: new Date(Date.now() + 24 * 3600 * 1000),\n  },\n  sharedKeyCredential\n).toString();\n```\n\n## Error Handling\n\n```typescript\nimport { RestError } from \"@azure\u002Fstorage-queue\";\n\ntry {\n  await queueClient.sendMessage(\"test\");\n} catch (error) {\n  if (error instanceof RestError) {\n    switch (error.statusCode) {\n      case 404:\n        console.log(\"Queue not found\");\n        break;\n      case 400:\n        console.log(\"Bad request - message too large or invalid\");\n        break;\n      case 403:\n        console.log(\"Access denied\");\n        break;\n      case 409:\n        console.log(\"Queue already exists or being deleted\");\n        break;\n      default:\n        console.error(`Storage error ${error.statusCode}: ${error.message}`);\n    }\n  }\n  throw error;\n}\n```\n\n## TypeScript Types Reference\n\n```typescript\nimport {\n  \u002F\u002F Clients\n  QueueServiceClient,\n  QueueClient,\n\n  \u002F\u002F Authentication\n  StorageSharedKeyCredential,\n  AnonymousCredential,\n\n  \u002F\u002F SAS\n  QueueSASPermissions,\n  AccountSASPermissions,\n  AccountSASServices,\n  AccountSASResourceTypes,\n  generateQueueSASQueryParameters,\n  generateAccountSASQueryParameters,\n\n  \u002F\u002F Messages\n  DequeuedMessageItem,\n  PeekedMessageItem,\n  QueueSendMessageResponse,\n  QueueReceiveMessageResponse,\n  QueueUpdateMessageResponse,\n\n  \u002F\u002F Queue\n  QueueItem,\n  QueueGetPropertiesResponse,\n\n  \u002F\u002F Errors\n  RestError,\n} from \"@azure\u002Fstorage-queue\";\n```\n\n## Message Limits\n\n| Limit | Value |\n|-------|-------|\n| Max message size | 64 KB |\n| Max visibility timeout | 7 days |\n| Max time-to-live | 7 days (or -1 for infinite) |\n| Max messages per receive | 32 |\n| Default visibility timeout | 30 seconds |\n\n## Best Practices\n\n1. **Use DefaultAzureCredential** — Prefer AAD over connection strings\u002Fkeys\n2. **Always delete after processing** — Prevent duplicate processing\n3. **Handle poison messages** — Move failed messages to a dead-letter queue\n4. **Use appropriate visibility timeout** — Set based on expected processing time\n5. **Extend visibility for long tasks** — Update message to prevent timeout\n6. **Use JSON for structured data** — Serialize objects to JSON strings\n7. **Check dequeueCount** — Detect repeatedly failing messages\n8. **Use batch receive** — Receive multiple messages for efficiency\n\n## Platform Differences\n\n| Feature | Node.js | Browser |\n|---------|---------|---------|\n| `StorageSharedKeyCredential` | ✅ | ❌ |\n| SAS generation | ✅ | ❌ |\n| DefaultAzureCredential | ✅ | ❌ |\n| Anonymous\u002FSAS access | ✅ | ✅ |\n| All message operations | ✅ | ✅ |\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n",{"name":5,"risk":27,"source":31,"date_added":42,"description":7},"2026-02-27","main","7a38d8e7073e61c16eda85681cb32fb68a8657a5","skills-store\u002Fcopilot\u002Fazure-storage-queue-ts","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router","https:\u002F\u002Fgithub.com\u002Fsapsapshen","2026-04-14 21:12:12",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add sapsapshen\u002Fai-memory-router --skill azure-storage-queue-ts",{"tool":55,"command":56},"bunx","bunx skills add sapsapshen\u002Fai-memory-router --skill azure-storage-queue-ts",{"tool":58,"command":59},"pnpm","pnpm dlx skills add sapsapshen\u002Fai-memory-router --skill azure-storage-queue-ts","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fcopilot\u002Fazure-storage-queue-ts\u002FSKILL.md",true,[],[64,88,114,124,137,156],{"skillNo":65,"name":66,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":75,"publishedAt":37},"01M71ATQRKG48SH06FQ697T5G9","wechat-search","Search WeChat Official Account articles using OpenClaw's web search and fetch capabilities with compliance-focused design.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fwechat-search-release\u002FSKILL.md",[70],{"categoryNo":71,"categoryName":72,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000037","律师与法务","PROFESSION","适用职业",[76,77,78,79,80,81,82,83,84,85,86,87],"wechat search","wechat","search","capabilities","openclaw","design","web search","compliance","web","official","fetch","with",{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":94,"publishedAt":37},"01M71ATNAVGZB35PFYM2GC5N00","trend-tap","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend tap', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-tap\u002FSKILL.md",[],[95,96,97,98,99,100,78,101,102,103,104,105,106,107,108,109,110,111,112,113],"trend","trend tap","tap","api keys","Bash","time","trending","hacker","aggregator","do","when","brew","live","data","google","install","read","across","api",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":37},"01M71ATJ9AM5KM15AA8PS302EB","trend-radar","Real-time trending topics aggregator across 7 platforms (X\u002FTwitter, Reddit, Google Trends, Hacker News, Zhihu, Bilibili, Weibo). Trigger: when user says 'trend', 'trends', 'trend pulse', '热点', '热搜', or asks what is trending\u002Fhot. Do NOT use web_search — this skill already fetches live data from all platforms with zero API keys.","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftrend-radar\u002FSKILL.md",[],[121,122,95,106,107,108,109,99,78,111,123,112,113,105,103,102,101,110,100,98],"trend radar","radar","pulse",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":130,"publishedAt":37},"01M71ATGF095N3EJM9G8SD0BB5","tech-news-daily","每日科技新闻汇——从全球多个 RSS 源抓取最新科技新闻，翻译（英→中）、去重、排版成每日简报。当用户询问科技新闻、科技新鲜事、每日科技、技术动态、Daily Tech News 或类似表达时触发此技能。","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Ftech-news-daily\u002FSKILL.md",[],[131,132,133,134,135,136],"news","tech","tech news daily","daily","rss","翻译",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":148,"publishedAt":37},"01M71ATFAEAMQ17M70NQNY7SR8","summarize","Summarize URLs or files with the summarize CLI (web, PDFs, images, audio, YouTube).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsummarize\u002FSKILL.md",[143],{"categoryNo":144,"categoryName":145,"taxonomyType":146,"taxonomyTypeDesc":147},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[139,84,149,150,151,87,106,152,153,154,97,155,110],"Audio","cli","youtube","images","https","files","pdfs",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":37},"01M71ATDNS3R0M4H936PACQAWZ","sonoscli","Control Sonos speakers (discover\u002Fstatus\u002Fplay\u002Fvolume\u002Fgroup).","https:\u002F\u002Fgithub.com\u002Fsapsapshen\u002Fai-memory-router\u002Fblob\u002Fmain\u002Fskills-store\u002Fopenclaw\u002Fsonoscli\u002FSKILL.md",[],[158,163,164,165,110,166,153,167,168,169,170,171],"status","play","go","latest","github","discover","control","cmd","volume"]