azure-mock 4.4.0 → 4.5.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.js ADDED
@@ -0,0 +1,1310 @@
1
+ import { ID_SEPARATOR, InvalidOperationError, Operation, checkIsDeepEqual, chunk, exhaustiveGuard, getDecodedUriComponent, getOrCreate, getResult, html, noop, streamToText, takeOne } from "@esposter/shared";
2
+ import { toHttpHeadersLike } from "@azure/core-http-compat";
3
+ import { RestError, createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
4
+ import { AZURE_MAX_BATCH_SIZE, AZURE_MAX_PAGE_SIZE, AZURE_MAX_QUEUE_VISIBILITY_TIMEOUT_MS, BinaryOperator, MAX_BLOB_BATCH_DELETIONS, MAX_EVENT_GRID_PUBLISH_EVENT_COUNT, MAX_EVENT_GRID_PUBLISH_REQUEST_BYTES, deserializeClause, deserializeKey, getTableNullClause, serializeClauses } from "@esposter/azure";
5
+ import { AnonymousCredential } from "@azure/storage-blob";
6
+ import { Readable } from "node:stream";
7
+ // src/models/shared/NotImplementedError.ts
8
+ var NotImplementedError = class extends Error {
9
+ constructor(name) {
10
+ super(`${name} is not implemented in the mock`);
11
+ this.name = "NotImplementedError";
12
+ }
13
+ };
14
+ // src/services/constants.ts
15
+ const BLOB_NOT_FOUND_ERROR_CODE = "BlobNotFound";
16
+ const BLOB_NOT_FOUND_MESSAGE = "The specified blob does not exist.";
17
+ const CONDITION_NOT_MET_ERROR_CODE = "ConditionNotMet";
18
+ const CONDITION_NOT_MET_MESSAGE = "The condition specified using HTTP conditional header(s) is not met.";
19
+ const INVALID_INPUT_MESSAGE = "One of the request inputs is not valid.";
20
+ const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
21
+ const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
22
+ const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
23
+ const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
24
+ // src/services/container/getMockBlobKey.ts
25
+ const getMockBlobKey = (containerName, blobName) => `${encodeURIComponent(containerName)}${ID_SEPARATOR}${encodeURIComponent(blobName)}`;
26
+ // src/store/MockContainerBlobDatesDatabase.ts
27
+ const MockContainerBlobDatesDatabase = /* @__PURE__ */ new Map();
28
+ // src/store/MockContainerBlobMetadataDatabase.ts
29
+ const MockContainerBlobMetadataDatabase = /* @__PURE__ */ new Map();
30
+ // src/store/MockContainerDatabase.ts
31
+ var MockContainerDatabaseMap = class extends Map {
32
+ clear() {
33
+ super.clear();
34
+ MockContainerBlobDatesDatabase.clear();
35
+ MockContainerBlobMetadataDatabase.clear();
36
+ }
37
+ };
38
+ const MockContainerDatabase = new MockContainerDatabaseMap();
39
+ // src/services/container/getMockContainer.ts
40
+ const getMockContainer = (containerName) => getOrCreate(MockContainerDatabase, containerName, () => /* @__PURE__ */ new Map());
41
+ // src/services/container/storeMockBlobMetadata.ts
42
+ const storeMockBlobMetadata = (containerName, blobName, metadata) => {
43
+ const key = getMockBlobKey(containerName, blobName);
44
+ if (metadata) MockContainerBlobMetadataDatabase.set(key, metadata);
45
+ else MockContainerBlobMetadataDatabase.delete(key);
46
+ };
47
+ // src/services/container/deleteMockBlob.ts
48
+ const deleteMockBlob = (containerName, blobName) => {
49
+ if (!getMockContainer(containerName).delete(blobName)) return false;
50
+ MockContainerBlobDatesDatabase.delete(getMockBlobKey(containerName, blobName));
51
+ storeMockBlobMetadata(containerName, blobName, void 0);
52
+ return true;
53
+ };
54
+ // src/services/container/getAzureErrorXml.ts
55
+ /**
56
+ * Generates a standard Azure Storage error XML response body.
57
+ * @param errorCode The official Azure error code (e.g., "BlobNotFound").
58
+ * @param errorMessage The user-friendly error message.
59
+ * @returns A formatted XML string.
60
+ */
61
+ const getAzureErrorXml = (errorCode, errorMessage) => html`<?xml version="1.0" encoding="utf-8"?>
62
+ <Error>
63
+ <code>${errorCode}</code>
64
+ <Message>${errorMessage}</Message>
65
+ </Error>`;
66
+ // src/services/container/decodeSegment.ts
67
+ const decodeSegment = (segment) => getDecodedUriComponent(segment, segment);
68
+ // src/services/container/getBlobUrlParts.ts
69
+ const getBlobUrlParts = (url) => {
70
+ const parsedUrl = getResult(() => new URL(url)).unwrapOr(void 0);
71
+ if (!parsedUrl) return void 0;
72
+ const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
73
+ if (pathSegments.length < 2) return void 0;
74
+ return {
75
+ blobName: pathSegments.slice(1).map((segment) => decodeSegment(segment)).join("/"),
76
+ containerName: decodeSegment(takeOne(pathSegments))
77
+ };
78
+ };
79
+ // src/services/container/constants.ts
80
+ const MOCK_BLOB_SEEDED_DATE = /* @__PURE__ */ new Date(-864e13);
81
+ const MOCK_BLOB_SEEDED_PROPERTIES = {
82
+ createdOn: MOCK_BLOB_SEEDED_DATE,
83
+ etag: "\"seeded\"",
84
+ lastModified: MOCK_BLOB_SEEDED_DATE
85
+ };
86
+ // src/services/container/readMockBlobDates.ts
87
+ const readMockBlobDates = (containerName, blobName) => MockContainerBlobDatesDatabase.get(getMockBlobKey(containerName, blobName)) ?? MOCK_BLOB_SEEDED_PROPERTIES;
88
+ // src/services/container/toWebResourceLike.ts
89
+ const toWebResourceLike = (request) => ({
90
+ abortSignal: request.abortSignal,
91
+ agent: request.agent,
92
+ body: request.body,
93
+ clone: () => {
94
+ throw new NotImplementedError(`${toWebResourceLike.name}(…).clone`);
95
+ },
96
+ formData: request.formData,
97
+ headers: toHttpHeadersLike(request.headers),
98
+ keepAlive: request.disableKeepAlive,
99
+ method: request.method,
100
+ onDownloadProgress: request.onDownloadProgress,
101
+ onUploadProgress: request.onUploadProgress,
102
+ prepare: () => {
103
+ throw new NotImplementedError(`${toWebResourceLike.name}(…).prepare`);
104
+ },
105
+ proxySettings: request.proxySettings,
106
+ requestId: request.headers.get("x-ms-client-request-id") ?? request.requestId,
107
+ requestOverrides: request.requestOverrides,
108
+ streamResponseStatusCodes: request.streamResponseStatusCodes,
109
+ timeout: request.timeout,
110
+ url: request.url,
111
+ validateRequestProperties: noop,
112
+ withCredentials: request.withCredentials
113
+ });
114
+ // src/services/shared/createMockResponse.ts
115
+ const createMockResponse = (status, url = "") => ({
116
+ headers: toHttpHeadersLike(createHttpHeaders()),
117
+ parsedHeaders: {},
118
+ request: toWebResourceLike(createPipelineRequest({ url })),
119
+ status
120
+ });
121
+ // src/models/container/MockBlobBatchClient.ts
122
+ var MockBlobBatchClient = class {
123
+ url;
124
+ constructor(url) {
125
+ this.url = url;
126
+ }
127
+ /**
128
+ * Simulates the deletion of multiple blobs in a single batch request.
129
+ * It iterates through the requested deletions, removes existing blobs from the
130
+ * underlying MockContainerDatabase, and builds a response object that reports
131
+ * which deletions succeeded and which failed (e.g. for blobs that didn't exist).
132
+ */
133
+ deleteBlobs(urls, credential, options) {
134
+ const { ifUnmodifiedSince, ...unsupportedConditions } = options?.conditions ?? {};
135
+ const unsupportedConditionName = Object.entries(unsupportedConditions).find(([, value]) => value !== void 0)?.[0];
136
+ if (unsupportedConditionName !== void 0) return Promise.reject(new NotImplementedError(`deleteBlobs with ${unsupportedConditionName}`));
137
+ if (urls.length > MAX_BLOB_BATCH_DELETIONS) return Promise.reject(/* @__PURE__ */ new RangeError(`Cannot exceed ${MAX_BLOB_BATCH_DELETIONS} sub requests in a single batch`));
138
+ const subResponses = [];
139
+ let subResponsesSucceededCount = 0;
140
+ let subResponsesFailedCount = 0;
141
+ for (const url of urls) {
142
+ const urlParts = getBlobUrlParts(url);
143
+ if (!urlParts) {
144
+ subResponses.push(this.#createFailedSubResponse(credential, 400, "InvalidUri", "Invalid blob URL format."));
145
+ subResponsesFailedCount++;
146
+ continue;
147
+ }
148
+ const { blobName, containerName } = urlParts;
149
+ if (this.#checkIsModifiedSince(containerName, blobName, ifUnmodifiedSince)) {
150
+ subResponses.push(this.#createFailedSubResponse(credential, 412, CONDITION_NOT_MET_ERROR_CODE, CONDITION_NOT_MET_MESSAGE));
151
+ subResponsesFailedCount++;
152
+ continue;
153
+ }
154
+ if (deleteMockBlob(containerName, blobName)) {
155
+ subResponses.push({
156
+ _request: {
157
+ credential,
158
+ url: this.url
159
+ },
160
+ headers: toHttpHeadersLike(createHttpHeaders()),
161
+ status: 202,
162
+ statusMessage: "Accepted"
163
+ });
164
+ subResponsesSucceededCount++;
165
+ } else {
166
+ subResponses.push(this.#createFailedSubResponse(credential, 404, BLOB_NOT_FOUND_ERROR_CODE, BLOB_NOT_FOUND_MESSAGE));
167
+ subResponsesFailedCount++;
168
+ }
169
+ }
170
+ return Promise.resolve({
171
+ _response: {
172
+ ...createMockResponse(202, this.url),
173
+ headers: toHttpHeadersLike(createHttpHeaders({
174
+ "content-type": "multipart/mixed",
175
+ "x-ms-request-id": crypto.randomUUID()
176
+ }))
177
+ },
178
+ subResponses,
179
+ subResponsesFailedCount,
180
+ subResponsesSucceededCount
181
+ });
182
+ }
183
+ getContainer(containerName) {
184
+ return getMockContainer(containerName);
185
+ }
186
+ #checkIsModifiedSince(containerName, blobName, ifUnmodifiedSince) {
187
+ return ifUnmodifiedSince !== void 0 && getMockContainer(containerName).has(blobName) && readMockBlobDates(containerName, blobName).lastModified > ifUnmodifiedSince;
188
+ }
189
+ #createFailedSubResponse(credential, status, errorCode, statusMessage) {
190
+ return {
191
+ _request: {
192
+ credential,
193
+ url: this.url
194
+ },
195
+ bodyAsText: getAzureErrorXml(errorCode, statusMessage),
196
+ errorCode,
197
+ headers: toHttpHeadersLike(createHttpHeaders()),
198
+ status,
199
+ statusMessage
200
+ };
201
+ }
202
+ };
203
+ // src/models/shared/MockRestError.ts
204
+ var MockRestError = class extends RestError {
205
+ constructor(message, statusCode) {
206
+ super(message, { statusCode });
207
+ this.name = "MockRestError";
208
+ }
209
+ };
210
+ // src/services/container/assertMockBlobIfMatch.ts
211
+ const assertMockBlobIfMatch = (containerName, blobName, ifMatch) => {
212
+ if (ifMatch === void 0) return;
213
+ if (ifMatch !== (getMockContainer(containerName).has(blobName) ? readMockBlobDates(containerName, blobName).etag : void 0)) throw new MockRestError(CONDITION_NOT_MET_MESSAGE, 412);
214
+ };
215
+ // src/services/container/getBlobUrl.ts
216
+ const getBlobUrl = (containerName, blobName) => `${MOCK_BLOB_BASE_URL}/${containerName}/${blobName.split("/").map((segment) => encodeURIComponent(segment)).join("/")}`;
217
+ // src/services/container/storeMockBlobWrite.ts
218
+ const storeMockBlobWrite = (containerName, blobName, isExisting) => {
219
+ const key = getMockBlobKey(containerName, blobName);
220
+ const lastModified = /* @__PURE__ */ new Date();
221
+ MockContainerBlobDatesDatabase.set(key, {
222
+ createdOn: MockContainerBlobDatesDatabase.get(key)?.createdOn ?? (isExisting ? MOCK_BLOB_SEEDED_DATE : lastModified),
223
+ etag: `"${crypto.randomUUID()}"`,
224
+ lastModified
225
+ });
226
+ };
227
+ // src/services/shared/getMockSasUrl.ts
228
+ const MOCK_SAS_FAR_FUTURE_EXPIRY = /* @__PURE__ */ new Date("2099-12-31T23:59:59Z");
229
+ const getMockSasUrl = (url, permissions, resourceType, expiresOn = MOCK_SAS_FAR_FUTURE_EXPIRY) => `${url}?sv=2025-11-05&${resourceType ? `sr=${resourceType}&` : ""}sig=mock-signature&st=1970-01-01T00:00:00Z&se=${expiresOn.toISOString().replace(/\.\d{3}Z$/u, "Z")}&sp=${permissions?.toString() ?? "r"}`;
230
+ // src/models/container/MockBlobClient.ts
231
+ var MockBlobClient = class {
232
+ connectionString;
233
+ containerName;
234
+ credential = new AnonymousCredential();
235
+ name;
236
+ url;
237
+ get container() {
238
+ return getMockContainer(this.containerName);
239
+ }
240
+ constructor(connectionString, containerName, blobName) {
241
+ this.connectionString = connectionString;
242
+ this.containerName = containerName;
243
+ this.name = blobName;
244
+ this.url = getBlobUrl(this.containerName, this.name);
245
+ }
246
+ abortCopyFromURL() {
247
+ throw new NotImplementedError(this.abortCopyFromURL.name);
248
+ }
249
+ beginCopyFromURL(copySource) {
250
+ const sourceParts = getBlobUrlParts(copySource);
251
+ if (!sourceParts) throw new MockRestError("Invalid copy source URL format", 400);
252
+ const { blobName: sourceBlobName, containerName: sourceContainerName } = sourceParts;
253
+ const sourceContainer = MockContainerDatabase.get(sourceContainerName);
254
+ if (!sourceContainer) throw new MockRestError("Source container not found", 404);
255
+ const sourceData = sourceContainer.get(sourceBlobName);
256
+ if (!sourceData) throw new MockRestError("Source blob not found", 404);
257
+ storeMockBlobWrite(this.containerName, this.name, this.container.has(this.name));
258
+ this.container.set(this.name, Buffer.from(sourceData));
259
+ const response = { _response: createMockResponse(202, `${this.url}?comp=copy`) };
260
+ return Promise.resolve({
261
+ cancelOperation: () => Promise.resolve(),
262
+ getOperationState: () => ({
263
+ isCompleted: true,
264
+ result: response
265
+ }),
266
+ getResult: () => response,
267
+ isDone: () => true,
268
+ isStopped: () => false,
269
+ onProgress: () => noop,
270
+ poll: () => Promise.resolve(),
271
+ pollUntilDone: () => Promise.resolve(response),
272
+ stopPolling: noop
273
+ });
274
+ }
275
+ createSnapshot() {
276
+ throw new NotImplementedError(this.createSnapshot.name);
277
+ }
278
+ delete(options) {
279
+ if (!this.#deleteIfPresent(options?.conditions?.ifMatch)) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
280
+ return Promise.resolve({ _response: createMockResponse(200) });
281
+ }
282
+ deleteIfExists(options) {
283
+ const succeeded = this.#deleteIfPresent(options?.conditions?.ifMatch);
284
+ return Promise.resolve({
285
+ _response: createMockResponse(succeeded ? 200 : 404),
286
+ succeeded
287
+ });
288
+ }
289
+ deleteImmutabilityPolicy() {
290
+ throw new NotImplementedError(this.deleteImmutabilityPolicy.name);
291
+ }
292
+ download() {
293
+ const buffer = this.container.get(this.name);
294
+ if (!buffer) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
295
+ return Promise.resolve({
296
+ _response: createMockResponse(200),
297
+ etag: readMockBlobDates(this.containerName, this.name).etag,
298
+ readableStreamBody: Readable.from(buffer)
299
+ });
300
+ }
301
+ downloadToBuffer() {
302
+ const data = this.container.get(this.name);
303
+ if (!data) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
304
+ return Promise.resolve(Buffer.from(data));
305
+ }
306
+ downloadToFile() {
307
+ throw new NotImplementedError(this.downloadToFile.name);
308
+ }
309
+ exists() {
310
+ return Promise.resolve(this.container.has(this.name));
311
+ }
312
+ generateSasStringToSign() {
313
+ throw new NotImplementedError(this.generateSasStringToSign.name);
314
+ }
315
+ generateSasUrl(options) {
316
+ return Promise.resolve(getMockSasUrl(this.url, options.permissions, "b", options.expiresOn));
317
+ }
318
+ generateUserDelegationSasStringToSign() {
319
+ throw new NotImplementedError(this.generateUserDelegationSasStringToSign.name);
320
+ }
321
+ generateUserDelegationSasUrl() {
322
+ throw new NotImplementedError(this.generateUserDelegationSasUrl.name);
323
+ }
324
+ getAccountInfo() {
325
+ throw new NotImplementedError(this.getAccountInfo.name);
326
+ }
327
+ getAppendBlobClient() {
328
+ throw new NotImplementedError(this.getAppendBlobClient.name);
329
+ }
330
+ getBlobLeaseClient() {
331
+ throw new NotImplementedError(this.getBlobLeaseClient.name);
332
+ }
333
+ getBlockBlobClient() {
334
+ throw new NotImplementedError(this.getBlockBlobClient.name);
335
+ }
336
+ getPageBlobClient() {
337
+ throw new NotImplementedError(this.getPageBlobClient.name);
338
+ }
339
+ getProperties() {
340
+ const blob = this.container.get(this.name);
341
+ if (!blob) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
342
+ return Promise.resolve({
343
+ _response: createMockResponse(200),
344
+ contentLength: blob.byteLength,
345
+ ...readMockBlobDates(this.containerName, this.name)
346
+ });
347
+ }
348
+ getTags() {
349
+ throw new NotImplementedError(this.getTags.name);
350
+ }
351
+ setAccessTier() {
352
+ throw new NotImplementedError(this.setAccessTier.name);
353
+ }
354
+ setHTTPHeaders() {
355
+ throw new NotImplementedError(this.setHTTPHeaders.name);
356
+ }
357
+ setImmutabilityPolicy() {
358
+ throw new NotImplementedError(this.setImmutabilityPolicy.name);
359
+ }
360
+ setLegalHold() {
361
+ throw new NotImplementedError(this.setLegalHold.name);
362
+ }
363
+ setMetadata(metadata) {
364
+ if (!this.container.has(this.name)) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
365
+ MockContainerBlobMetadataDatabase.set(getMockBlobKey(this.containerName, this.name), metadata ?? {});
366
+ storeMockBlobWrite(this.containerName, this.name, true);
367
+ return Promise.resolve({
368
+ _response: createMockResponse(200),
369
+ ...readMockBlobDates(this.containerName, this.name)
370
+ });
371
+ }
372
+ setTags() {
373
+ throw new NotImplementedError(this.setTags.name);
374
+ }
375
+ syncCopyFromURL() {
376
+ throw new NotImplementedError(this.syncCopyFromURL.name);
377
+ }
378
+ undelete() {
379
+ throw new NotImplementedError(this.undelete.name);
380
+ }
381
+ withSnapshot() {
382
+ throw new NotImplementedError(this.withSnapshot.name);
383
+ }
384
+ withVersion() {
385
+ throw new NotImplementedError(this.withVersion.name);
386
+ }
387
+ #deleteIfPresent(ifMatch) {
388
+ if (!this.container.has(this.name)) return false;
389
+ assertMockBlobIfMatch(this.containerName, this.name, ifMatch);
390
+ return deleteMockBlob(this.containerName, this.name);
391
+ }
392
+ };
393
+ // src/services/container/checkIsReadableStream.ts
394
+ const checkIsReadableStream = (value) => value instanceof Readable;
395
+ // src/services/container/bodyToBuffer.ts
396
+ const bodyToBuffer = async (body) => {
397
+ if (body === null) return Buffer.alloc(0);
398
+ else if (typeof body === "string") return Buffer.from(body);
399
+ else if (body instanceof ArrayBuffer) return Buffer.from(body);
400
+ else if (ArrayBuffer.isView(body)) return Buffer.from(body.buffer, body.byteOffset, body.byteLength);
401
+ else if (typeof body === "function") {
402
+ const streamOrBlob = body();
403
+ return bodyToBuffer(streamOrBlob);
404
+ } else if (body instanceof Blob) {
405
+ const arrayBuffer = await body.arrayBuffer();
406
+ return Buffer.from(arrayBuffer);
407
+ } else if (checkIsReadableStream(body)) return Buffer.from(await streamToText(body));
408
+ else if (body instanceof ReadableStream) {
409
+ const reader = body.getReader();
410
+ const chunks = [];
411
+ while (true) {
412
+ const { done, value } = await reader.read();
413
+ if (done) break;
414
+ else chunks.push(value);
415
+ }
416
+ return Buffer.concat(chunks);
417
+ } else if (body instanceof FormData) throw new NotImplementedError(`${bodyToBuffer.name}(FormData)`);
418
+ else {
419
+ exhaustiveGuard(body);
420
+ return Buffer.alloc(0);
421
+ }
422
+ };
423
+ // src/models/container/MockBlockBlobClient.ts
424
+ var MockBlockBlobClient = class extends MockBlobClient {
425
+ commitBlockList() {
426
+ throw new NotImplementedError(this.commitBlockList.name);
427
+ }
428
+ getBlockList() {
429
+ throw new NotImplementedError(this.getBlockList.name);
430
+ }
431
+ query() {
432
+ throw new NotImplementedError(this.query.name);
433
+ }
434
+ stageBlock() {
435
+ throw new NotImplementedError(this.stageBlock.name);
436
+ }
437
+ stageBlockFromURL() {
438
+ throw new NotImplementedError(this.stageBlockFromURL.name);
439
+ }
440
+ syncUploadFromURL() {
441
+ throw new NotImplementedError(this.syncUploadFromURL.name);
442
+ }
443
+ async upload(body, _contentLength, options) {
444
+ const buffer = await bodyToBuffer(body);
445
+ if (options?.conditions?.ifNoneMatch === "*" && this.container.has(this.name)) throw new MockRestError("The specified blob already exists.", 409);
446
+ assertMockBlobIfMatch(this.containerName, this.name, options?.conditions?.ifMatch);
447
+ storeMockBlobWrite(this.containerName, this.name, this.container.has(this.name));
448
+ storeMockBlobMetadata(this.containerName, this.name, options?.metadata);
449
+ this.container.set(this.name, buffer);
450
+ const { etag } = readMockBlobDates(this.containerName, this.name);
451
+ return {
452
+ _response: createMockResponse(201),
453
+ etag
454
+ };
455
+ }
456
+ uploadBrowserData() {
457
+ throw new NotImplementedError(this.uploadBrowserData.name);
458
+ }
459
+ uploadData() {
460
+ throw new NotImplementedError(this.uploadData.name);
461
+ }
462
+ uploadFile() {
463
+ throw new NotImplementedError(this.uploadFile.name);
464
+ }
465
+ async uploadStream(stream, _bufferSize, _maxConcurrency, options) {
466
+ const chunks = [];
467
+ for await (const chunk of stream) chunks.push(chunk);
468
+ return this.upload(Buffer.concat(chunks), 0, options);
469
+ }
470
+ };
471
+ // src/services/container/getBlobItemXml.ts
472
+ const getBlobItemXml = ({ name, properties }) => html`<Blob>
473
+ <Name>${name}</Name>
474
+ <Properties>
475
+ <Last-Modified>${properties.lastModified.toUTCString()}</Last-Modified>
476
+ <Etag>${properties.etag}</Etag>
477
+ <Content-Length>${properties.contentLength}</Content-Length>
478
+ <Content-Type>${properties.contentType}</Content-Type>
479
+ <BlobType>BlockBlob</BlobType>
480
+ <LeaseStatus>unlocked</LeaseStatus>
481
+ <LeaseState>available</LeaseState>
482
+ </Properties>
483
+ </Blob>`;
484
+ // src/services/container/getBlobPrefixXml.ts
485
+ const getBlobPrefixXml = (name) => html`<BlobPrefix><Name>${name}</Name></BlobPrefix>`;
486
+ // src/services/container/getListBlobsXml.ts
487
+ /**
488
+ * Generates a standard Azure Storage list blobs XML response body.
489
+ * @param containerName The container name.
490
+ * @param blobsXml The blobs xml data.
491
+ * @returns A formatted XML string.
492
+ */
493
+ const getListBlobsXml = (containerName, blobsXml) => html`<?xml version="1.0" encoding="utf8"?>
494
+ <EnumerationResults ServiceEndpoint="" ContainerName="${containerName}">
495
+ <Blobs>${blobsXml}</Blobs>
496
+ <NextMarker />
497
+ </EnumerationResults>`;
498
+ // src/services/container/getListBlobsSegmentResponse.ts
499
+ const getListBlobsSegmentResponse = (containerName, prefix, segment, blobsXml) => {
500
+ const segmentResponse = {
501
+ containerName,
502
+ marker: "",
503
+ prefix,
504
+ segment,
505
+ serviceEndpoint: ""
506
+ };
507
+ return {
508
+ _response: {
509
+ ...createMockResponse(200),
510
+ bodyAsText: getListBlobsXml(containerName, blobsXml),
511
+ parsedBody: segmentResponse
512
+ },
513
+ ...segmentResponse
514
+ };
515
+ };
516
+ // src/services/container/readMockBlobMetadata.ts
517
+ const readMockBlobMetadata = (containerName, blobName) => MockContainerBlobMetadataDatabase.get(getMockBlobKey(containerName, blobName));
518
+ // src/models/container/MockContainerClient.ts
519
+ /**
520
+ * An in-memory mock of the Azure ContainerClient — no emulator and no network.
521
+ *
522
+ * @example
523
+ * const mockContainerClient = new MockContainerClient("", "hello world");
524
+ * const blockBlobClient = mockContainerClient.getBlockBlobClient("hello world.txt");
525
+ * await blockBlobClient.upload("hello world", 11);
526
+ * const content = await blockBlobClient.downloadToBuffer();
527
+ */
528
+ var MockContainerClient = class {
529
+ connectionString;
530
+ containerName;
531
+ credential = new AnonymousCredential();
532
+ url;
533
+ get container() {
534
+ return getMockContainer(this.containerName);
535
+ }
536
+ constructor(connectionString, containerName) {
537
+ this.connectionString = connectionString;
538
+ this.containerName = containerName;
539
+ this.url = `${MOCK_BLOB_BASE_URL}/${this.containerName}`;
540
+ }
541
+ create() {
542
+ throw new NotImplementedError(this.create.name);
543
+ }
544
+ createIfNotExists() {
545
+ throw new NotImplementedError(this.createIfNotExists.name);
546
+ }
547
+ delete() {
548
+ throw new NotImplementedError(this.delete.name);
549
+ }
550
+ deleteBlob(blobName) {
551
+ if (!deleteMockBlob(this.containerName, blobName)) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
552
+ return Promise.resolve({ _response: createMockResponse(200, getBlobUrl(this.containerName, blobName)) });
553
+ }
554
+ deleteIfExists() {
555
+ throw new NotImplementedError(this.deleteIfExists.name);
556
+ }
557
+ exists() {
558
+ throw new NotImplementedError(this.exists.name);
559
+ }
560
+ findBlobsByTags() {
561
+ throw new NotImplementedError(this.findBlobsByTags.name);
562
+ }
563
+ generateSasStringToSign() {
564
+ throw new NotImplementedError(this.generateSasStringToSign.name);
565
+ }
566
+ generateSasUrl(options) {
567
+ return Promise.resolve(getMockSasUrl(this.url, options.permissions, "c", options.expiresOn));
568
+ }
569
+ generateUserDelegationSasStringToSign() {
570
+ throw new NotImplementedError(this.generateUserDelegationSasStringToSign.name);
571
+ }
572
+ generateUserDelegationSasUrl() {
573
+ throw new NotImplementedError(this.generateUserDelegationSasUrl.name);
574
+ }
575
+ getAccessPolicy() {
576
+ throw new NotImplementedError(this.getAccessPolicy.name);
577
+ }
578
+ getAccountInfo() {
579
+ throw new NotImplementedError(this.getAccountInfo.name);
580
+ }
581
+ getAppendBlobClient() {
582
+ throw new NotImplementedError(this.getAppendBlobClient.name);
583
+ }
584
+ getBlobBatchClient() {
585
+ return new MockBlobBatchClient(this.url);
586
+ }
587
+ getBlobClient(blobName) {
588
+ return new MockBlockBlobClient(this.connectionString, this.containerName, blobName);
589
+ }
590
+ getBlobLeaseClient() {
591
+ throw new NotImplementedError(this.getBlobLeaseClient.name);
592
+ }
593
+ getBlockBlobClient(blobName) {
594
+ return new MockBlockBlobClient(this.connectionString, this.containerName, blobName);
595
+ }
596
+ getPageBlobClient() {
597
+ throw new NotImplementedError(this.getPageBlobClient.name);
598
+ }
599
+ getProperties() {
600
+ throw new NotImplementedError(this.getProperties.name);
601
+ }
602
+ listBlobsByHierarchy(delimiter, options) {
603
+ const blobHierarchyItemIterator = this.#getBlobHierarchyItemIterator(delimiter, options);
604
+ return {
605
+ byPage: () => async function* () {
606
+ const allBlobItems = [];
607
+ const allBlobItemXml = [];
608
+ const allBlobPrefixes = [];
609
+ const allBlobPrefixXml = [];
610
+ for await (const blobHierarchyItem of blobHierarchyItemIterator) if (blobHierarchyItem.kind === "blob") {
611
+ allBlobItems.push(blobHierarchyItem);
612
+ allBlobItemXml.push(getBlobItemXml(blobHierarchyItem));
613
+ } else {
614
+ allBlobPrefixes.push({ name: blobHierarchyItem.name });
615
+ allBlobPrefixXml.push(getBlobPrefixXml(blobHierarchyItem.name));
616
+ }
617
+ if (allBlobItems.length > 0 || allBlobPrefixes.length > 0) yield await Promise.resolve(getListBlobsSegmentResponse(this.containerName, options?.prefix ?? "", {
618
+ blobItems: allBlobItems,
619
+ blobPrefixes: allBlobPrefixes
620
+ }, `${allBlobItemXml.join("")}${allBlobPrefixXml.join("")}`));
621
+ }.bind(this)(),
622
+ next: blobHierarchyItemIterator.next.bind(blobHierarchyItemIterator),
623
+ [Symbol.asyncIterator]() {
624
+ return this;
625
+ }
626
+ };
627
+ }
628
+ listBlobsFlat(options) {
629
+ const blobItemIterator = this.#getBlobItemIterator(options);
630
+ return {
631
+ byPage: () => async function* () {
632
+ const allBlobItems = [];
633
+ const allBlobItemXml = [];
634
+ for await (const blobItem of blobItemIterator) {
635
+ allBlobItems.push(blobItem);
636
+ allBlobItemXml.push(getBlobItemXml(blobItem));
637
+ }
638
+ if (allBlobItems.length > 0) yield await Promise.resolve(getListBlobsSegmentResponse(this.containerName, options?.prefix ?? "", { blobItems: allBlobItems }, allBlobItemXml.join("")));
639
+ }.bind(this)(),
640
+ next: blobItemIterator.next.bind(blobItemIterator),
641
+ [Symbol.asyncIterator]() {
642
+ return this;
643
+ }
644
+ };
645
+ }
646
+ setAccessPolicy() {
647
+ throw new NotImplementedError(this.setAccessPolicy.name);
648
+ }
649
+ setMetadata() {
650
+ throw new NotImplementedError(this.setMetadata.name);
651
+ }
652
+ async uploadBlockBlob(blobName, body, contentLength) {
653
+ const blockBlobClient = this.getBlockBlobClient(blobName);
654
+ return {
655
+ blockBlobClient,
656
+ response: await blockBlobClient.upload(body, contentLength)
657
+ };
658
+ }
659
+ async *#getBlobHierarchyItemIterator(delimiter, options) {
660
+ const prefix = options?.prefix ?? "";
661
+ const uniqueSubprefixes = /* @__PURE__ */ new Set();
662
+ const blobsInCurrentLevel = [];
663
+ for (const [name, buffer] of this.container.entries()) {
664
+ if (!name.startsWith(prefix)) continue;
665
+ const nameAfterPrefix = name.slice(prefix.length);
666
+ const delimiterIndex = nameAfterPrefix.indexOf(delimiter);
667
+ if (delimiterIndex === -1) blobsInCurrentLevel.push(this.#getBlobItem(name, buffer, options?.includeMetadata));
668
+ else {
669
+ const subprefix = `${prefix}${nameAfterPrefix.slice(0, delimiterIndex + delimiter.length)}`;
670
+ uniqueSubprefixes.add(subprefix);
671
+ }
672
+ }
673
+ for (const prefixName of [...uniqueSubprefixes].toSorted()) yield await Promise.resolve({
674
+ kind: "prefix",
675
+ name: prefixName
676
+ });
677
+ for (const blobItem of blobsInCurrentLevel) yield await Promise.resolve({
678
+ kind: "blob",
679
+ ...blobItem
680
+ });
681
+ }
682
+ #getBlobItem(name, buffer, isMetadataIncluded) {
683
+ const { createdOn, etag, lastModified } = readMockBlobDates(this.containerName, name);
684
+ return {
685
+ deleted: false,
686
+ ...isMetadataIncluded && { metadata: readMockBlobMetadata(this.containerName, name) },
687
+ name,
688
+ properties: {
689
+ blobType: "BlockBlob",
690
+ contentLength: buffer.length,
691
+ contentType: "application/octet-stream",
692
+ createdOn,
693
+ etag,
694
+ lastModified,
695
+ leaseState: "available",
696
+ leaseStatus: "unlocked"
697
+ },
698
+ snapshot: ""
699
+ };
700
+ }
701
+ async *#getBlobItemIterator(options) {
702
+ const prefix = options?.prefix ?? "";
703
+ for (const [name, buffer] of this.container.entries()) {
704
+ if (!name.startsWith(prefix)) continue;
705
+ yield await Promise.resolve(this.#getBlobItem(name, buffer, options?.includeMetadata));
706
+ }
707
+ }
708
+ };
709
+ // src/store/MockEventGridDatabase.ts
710
+ const MockEventGridDatabase = /* @__PURE__ */ new Map();
711
+ // src/models/eventGrid/MockEventGridPublisherClient.ts
712
+ /**
713
+ * An in-memory mock of the Azure EventGridPublisherClient — no emulator and no network.
714
+ * `client` is excluded because it is a private member of the real class and cannot be satisfied structurally.
715
+ */
716
+ var MockEventGridPublisherClient = class {
717
+ endpointUrl;
718
+ inputSchema;
719
+ topicType;
720
+ constructor(endpoint, topicType) {
721
+ this.endpointUrl = endpoint;
722
+ this.topicType = topicType;
723
+ this.inputSchema = topicType;
724
+ }
725
+ send(newEvents) {
726
+ if (newEvents.length > MAX_EVENT_GRID_PUBLISH_EVENT_COUNT || Buffer.byteLength(JSON.stringify(newEvents)) > MAX_EVENT_GRID_PUBLISH_REQUEST_BYTES) throw new MockRestError("The publish request exceeds the Event Grid publish limits.", 413);
727
+ getOrCreate(MockEventGridDatabase, this.endpointUrl, () => []).push(...newEvents);
728
+ return Promise.resolve();
729
+ }
730
+ };
731
+ // src/services/queue/getMockQueueMessageItem.ts
732
+ const getMockQueueMessageItem = (messageText) => {
733
+ const insertedOn = /* @__PURE__ */ new Date();
734
+ return {
735
+ expiresOn: new Date(insertedOn.getTime() + AZURE_MAX_QUEUE_VISIBILITY_TIMEOUT_MS),
736
+ insertedOn,
737
+ messageId: crypto.randomUUID(),
738
+ messageText
739
+ };
740
+ };
741
+ // src/store/MockQueueDatabase.ts
742
+ const MockQueueDatabase = /* @__PURE__ */ new Map();
743
+ // src/models/queue/MockQueueClient.ts
744
+ /**
745
+ * An in-memory mock of the Azure QueueClient — no emulator and no network.
746
+ *
747
+ * @example
748
+ * const mockQueueClient = new MockQueueClient("", "hello world");
749
+ * await mockQueueClient.createIfNotExists();
750
+ * await mockQueueClient.sendMessage("hello world");
751
+ * const messages = await mockQueueClient.peekMessages();
752
+ */
753
+ var MockQueueClient = class {
754
+ connectionString;
755
+ name;
756
+ url;
757
+ get queue() {
758
+ return getOrCreate(MockQueueDatabase, this.name, () => []);
759
+ }
760
+ constructor(connectionString, queueName) {
761
+ this.connectionString = connectionString;
762
+ this.name = queueName;
763
+ this.url = `${MOCK_QUEUE_BASE_URL}/${this.name}`;
764
+ }
765
+ clearMessages() {
766
+ throw new NotImplementedError(this.clearMessages.name);
767
+ }
768
+ create(_options) {
769
+ throw new NotImplementedError(this.create.name);
770
+ }
771
+ createIfNotExists() {
772
+ throw new NotImplementedError(this.createIfNotExists.name);
773
+ }
774
+ delete() {
775
+ throw new NotImplementedError(this.delete.name);
776
+ }
777
+ deleteIfExists() {
778
+ throw new NotImplementedError(this.deleteIfExists.name);
779
+ }
780
+ deleteMessage() {
781
+ throw new NotImplementedError(this.deleteMessage.name);
782
+ }
783
+ exists() {
784
+ throw new NotImplementedError(this.exists.name);
785
+ }
786
+ generateSasStringToSign() {
787
+ throw new NotImplementedError(this.generateSasStringToSign.name);
788
+ }
789
+ generateSasUrl(options) {
790
+ return getMockSasUrl(this.url, options.permissions, void 0, options.expiresOn);
791
+ }
792
+ generateUserDelegationSasUrl() {
793
+ throw new NotImplementedError(this.generateUserDelegationSasUrl.name);
794
+ }
795
+ generateUserDelegationStringToSign() {
796
+ throw new NotImplementedError(this.generateUserDelegationStringToSign.name);
797
+ }
798
+ getAccessPolicy() {
799
+ throw new NotImplementedError(this.getAccessPolicy.name);
800
+ }
801
+ getProperties() {
802
+ throw new NotImplementedError(this.getProperties.name);
803
+ }
804
+ getServiceProperties() {
805
+ throw new NotImplementedError(this.getServiceProperties.name);
806
+ }
807
+ listQueues() {
808
+ throw new NotImplementedError(this.listQueues.name);
809
+ }
810
+ peekMessages(_options) {
811
+ const peekedMessageItems = this.queue.map((text) => ({
812
+ dequeueCount: 0,
813
+ ...getMockQueueMessageItem(text)
814
+ }));
815
+ return Promise.resolve({
816
+ _response: this.#createResponse(peekedMessageItems),
817
+ peekedMessageItems
818
+ });
819
+ }
820
+ receiveMessages(_options) {
821
+ const queuedTexts = this.queue;
822
+ MockQueueDatabase.set(this.name, []);
823
+ const receivedMessageItems = queuedTexts.map((text) => Object.assign(getMockQueueMessageItem(text), {
824
+ dequeueCount: 1,
825
+ nextVisibleOn: /* @__PURE__ */ new Date(),
826
+ popReceipt: crypto.randomUUID()
827
+ }));
828
+ return Promise.resolve({
829
+ _response: this.#createResponse(receivedMessageItems),
830
+ receivedMessageItems
831
+ });
832
+ }
833
+ sendMessage(messageText, _options) {
834
+ this.queue.push(messageText);
835
+ const { expiresOn, insertedOn, messageId } = getMockQueueMessageItem(messageText);
836
+ const enqueuedMessage = {
837
+ expiresOn,
838
+ insertedOn,
839
+ messageId,
840
+ nextVisibleOn: insertedOn,
841
+ popReceipt: crypto.randomUUID()
842
+ };
843
+ const enqueuedMessages = [enqueuedMessage];
844
+ return Promise.resolve({
845
+ _response: this.#createResponse(enqueuedMessages),
846
+ ...enqueuedMessage
847
+ });
848
+ }
849
+ setAccessPolicy(_identifiers) {
850
+ throw new NotImplementedError(this.setAccessPolicy.name);
851
+ }
852
+ setMetadata() {
853
+ throw new NotImplementedError(this.setMetadata.name);
854
+ }
855
+ updateMessage() {
856
+ throw new NotImplementedError(this.updateMessage.name);
857
+ }
858
+ #createResponse(parsedBody) {
859
+ return {
860
+ ...createMockResponse(200, this.url),
861
+ bodyAsText: "",
862
+ parsedBody
863
+ };
864
+ }
865
+ };
866
+ // src/services/filter/checkIsNullClause.ts
867
+ const checkIsNullClause = (clause) => {
868
+ const tableNullClause = getTableNullClause(clause.key);
869
+ return checkIsDeepEqual(clause, tableNullClause);
870
+ };
871
+ // src/services/filter/compare.ts
872
+ const compare = (operator, leftHandSide, rightHandSide) => {
873
+ if (rightHandSide === null || rightHandSide === void 0) {
874
+ if (operator !== BinaryOperator.Eq) throw new InvalidOperationError(Operation.Read, compare.name, JSON.stringify({
875
+ leftHandSide,
876
+ operator,
877
+ rightHandSide
878
+ }));
879
+ return leftHandSide === null || leftHandSide === void 0;
880
+ }
881
+ switch (operator) {
882
+ case BinaryOperator.Eq: return leftHandSide === rightHandSide;
883
+ case BinaryOperator.Ge: return leftHandSide >= rightHandSide;
884
+ case BinaryOperator.Gt: return leftHandSide > rightHandSide;
885
+ case BinaryOperator.Le: return leftHandSide <= rightHandSide;
886
+ case BinaryOperator.Lt: return leftHandSide < rightHandSide;
887
+ case BinaryOperator.Ne: return leftHandSide !== rightHandSide;
888
+ default: return exhaustiveGuard(operator);
889
+ }
890
+ };
891
+ // src/services/filter/createFilterPredicate.ts
892
+ const LITERAL_REGEX = /'(?:[^']|'')*'/gu;
893
+ const LITERAL_PLACEHOLDER = "";
894
+ const LITERAL_PLACEHOLDER_REGEX = new RegExp(`${LITERAL_PLACEHOLDER}(\\d+)${LITERAL_PLACEHOLDER}`, "gu");
895
+ const createFilterPredicate = (filter) => {
896
+ const literals = [];
897
+ const orGroups = filter.replaceAll(LITERAL_REGEX, (literal) => {
898
+ literals.push(literal);
899
+ return `${LITERAL_PLACEHOLDER}${literals.length - 1}${LITERAL_PLACEHOLDER}`;
900
+ }).replaceAll(String.raw`(`, " ").replaceAll(String.raw`)`, "").split(/\s+and\s+/iu).filter(Boolean).map((group) => group.split(/\s+or\s+/iu).filter(Boolean).map((clauseString) => {
901
+ const clause = deserializeClause(clauseString.replaceAll(LITERAL_PLACEHOLDER_REGEX, (_placeholder, index) => takeOne(literals, Number(index))));
902
+ return {
903
+ clause,
904
+ isNull: checkIsNullClause(clause)
905
+ };
906
+ }));
907
+ return (document) => {
908
+ for (const orGroup of orGroups) {
909
+ let isGroupMatched = false;
910
+ for (const { clause, isNull } of orGroup) {
911
+ const value = takeOne(document, clause.key);
912
+ let isMatched;
913
+ if (isNull) isMatched = compare(BinaryOperator.Eq, value, null);
914
+ else {
915
+ const comparisonResult = compare(clause.operator, value, clause.value);
916
+ isMatched = clause.not ? !comparisonResult : comparisonResult;
917
+ }
918
+ if (isMatched) {
919
+ isGroupMatched = true;
920
+ break;
921
+ }
922
+ }
923
+ if (!isGroupMatched) return false;
924
+ }
925
+ return true;
926
+ };
927
+ };
928
+ // src/services/search/getSearchFieldValues.ts
929
+ const getSearchFieldValues = (value, pathSegments) => {
930
+ if (pathSegments.length === 0) return [value];
931
+ else if (Array.isArray(value)) return value.flatMap((item) => getSearchFieldValues(item, pathSegments));
932
+ else if (typeof value === "object" && value !== null) {
933
+ const [field = "", ...remainingPathSegments] = pathSegments;
934
+ return getSearchFieldValues(value[deserializeKey(field)], remainingPathSegments);
935
+ } else return [];
936
+ };
937
+ // src/services/search/searchDocuments.ts
938
+ const searchDocuments = (documents, searchText, searchFields) => {
939
+ if (!searchText || searchText === "*") return documents;
940
+ const normalizedSearchText = searchText.toLocaleLowerCase();
941
+ const searchFieldPaths = searchFields?.map((searchField) => searchField.split("/"));
942
+ return documents.filter((document) => (searchFieldPaths ?? Object.keys(document).map((field) => field.split("/"))).some((pathSegments) => getSearchFieldValues(document, pathSegments).some((value) => typeof value === "string" && value.toLocaleLowerCase().includes(normalizedSearchText))));
943
+ };
944
+ // src/services/search/toComparable.ts
945
+ const toComparable = (value) => {
946
+ if (value instanceof Date) return value.getTime();
947
+ else if (typeof value === "number" || typeof value === "string") return value;
948
+ else return String(value);
949
+ };
950
+ // src/services/search/compareValues.ts
951
+ const compareValues = (firstValue, secondValue) => {
952
+ const firstComparable = toComparable(firstValue);
953
+ const secondComparable = toComparable(secondValue);
954
+ if (typeof firstComparable === "number" && typeof secondComparable === "number") return firstComparable - secondComparable;
955
+ else return String(firstComparable).localeCompare(String(secondComparable));
956
+ };
957
+ // src/services/search/sortDocuments.ts
958
+ const sortDocuments = (documents, orderBy) => {
959
+ const parsedOrderBy = orderBy.map((clause) => {
960
+ const [field = "", direction = "asc"] = clause.split(/\s+/u);
961
+ return {
962
+ direction,
963
+ key: deserializeKey(field)
964
+ };
965
+ });
966
+ return documents.toSorted((firstDocument, secondDocument) => {
967
+ for (const { direction, key } of parsedOrderBy) {
968
+ const comparison = compareValues(firstDocument[key], secondDocument[key]);
969
+ if (comparison !== 0) return direction === "desc" ? -comparison : comparison;
970
+ }
971
+ return 0;
972
+ });
973
+ };
974
+ // src/store/MockSearchDatabase.ts
975
+ const MockSearchDatabase = /* @__PURE__ */ new Map();
976
+ // src/models/search/MockSearchClient.ts
977
+ /**
978
+ * An in-memory mock of the Azure SearchClient — no emulator and no network. It applies the same OData filtering
979
+ * as the other mock clients, so a query behaves the same here as it does against the service.
980
+ *
981
+ * @example
982
+ * const mockSearchClient = new MockSearchClient(SearchIndex.Messages);
983
+ * await mockSearchClient.uploadDocuments([{ ... }]);
984
+ * const { count, results } = await mockSearchClient.search("*", { filter, includeTotalCount: true });
985
+ */
986
+ var MockSearchClient = class {
987
+ apiVersion = "";
988
+ endpoint = MOCK_SEARCH_BASE_URL;
989
+ indexName;
990
+ serviceVersion = "";
991
+ get documents() {
992
+ return getOrCreate(MockSearchDatabase, this.indexName, () => []);
993
+ }
994
+ constructor(indexName) {
995
+ this.indexName = indexName;
996
+ }
997
+ autocomplete() {
998
+ throw new NotImplementedError(this.autocomplete.name);
999
+ }
1000
+ deleteDocuments() {
1001
+ throw new NotImplementedError(this.deleteDocuments.name);
1002
+ }
1003
+ getDocument(_key, _options) {
1004
+ throw new NotImplementedError(this.getDocument.name);
1005
+ }
1006
+ getDocumentsCount(_options) {
1007
+ return Promise.resolve(this.documents.length);
1008
+ }
1009
+ indexDocuments(_batch, _options) {
1010
+ throw new NotImplementedError(this.indexDocuments.name);
1011
+ }
1012
+ mergeDocuments(_documents, _options) {
1013
+ throw new NotImplementedError(this.mergeDocuments.name);
1014
+ }
1015
+ mergeOrUploadDocuments(_documents, _options) {
1016
+ throw new NotImplementedError(this.mergeOrUploadDocuments.name);
1017
+ }
1018
+ search(searchText, options) {
1019
+ const { filter, includeTotalCount, orderBy, searchFields, skip = 0, top } = options ?? {};
1020
+ let documents = searchDocuments(this.documents, searchText, searchFields);
1021
+ if (filter) {
1022
+ const predicate = createFilterPredicate(filter);
1023
+ documents = documents.filter((document) => predicate(document));
1024
+ }
1025
+ if (orderBy) documents = sortDocuments(documents, orderBy);
1026
+ const count = documents.length;
1027
+ const paginatedDocuments = documents.slice(skip, top === void 0 ? void 0 : skip + top);
1028
+ const results = { async *[Symbol.asyncIterator]() {
1029
+ for (const document of paginatedDocuments) yield { document };
1030
+ } };
1031
+ return Promise.resolve({
1032
+ ...includeTotalCount ? { count } : {},
1033
+ results
1034
+ });
1035
+ }
1036
+ suggest(_searchText, _suggesterName, _options) {
1037
+ throw new NotImplementedError(this.suggest.name);
1038
+ }
1039
+ uploadDocuments(documents, _options) {
1040
+ for (const document of documents) {
1041
+ const key = document.rowKey;
1042
+ const index = this.documents.findIndex((existingDocument) => existingDocument.rowKey === key);
1043
+ if (index === -1) this.documents.push(document);
1044
+ else this.documents[index] = document;
1045
+ }
1046
+ return Promise.resolve({ results: [] });
1047
+ }
1048
+ };
1049
+ // src/store/MockServiceBusDatabase.ts
1050
+ const MockServiceBusDatabase = /* @__PURE__ */ new Map();
1051
+ // src/models/serviceBus/MockServiceBusSender.ts
1052
+ /**
1053
+ * An in-memory mock of the Azure ServiceBusSender — no emulator and no network.
1054
+ *
1055
+ * @example
1056
+ * const mockServiceBusSender = new MockServiceBusSender("hello world");
1057
+ * await mockServiceBusSender.scheduleMessages({ body: "hello world" }, new Date());
1058
+ */
1059
+ var MockServiceBusSender = class {
1060
+ entityPath;
1061
+ identifier;
1062
+ isClosed = false;
1063
+ get queue() {
1064
+ return getOrCreate(MockServiceBusDatabase, this.entityPath, () => []);
1065
+ }
1066
+ constructor(queueName) {
1067
+ this.entityPath = queueName;
1068
+ this.identifier = queueName;
1069
+ }
1070
+ cancelScheduledMessages() {
1071
+ throw new NotImplementedError(this.cancelScheduledMessages.name);
1072
+ }
1073
+ close() {
1074
+ this.isClosed = true;
1075
+ return Promise.resolve();
1076
+ }
1077
+ createMessageBatch() {
1078
+ throw new NotImplementedError(this.createMessageBatch.name);
1079
+ }
1080
+ scheduleMessages(messages, scheduledEnqueueTimeUtc) {
1081
+ for (const message of Array.isArray(messages) ? messages : [messages]) this.queue.push({
1082
+ ...message,
1083
+ scheduledEnqueueTimeUtc
1084
+ });
1085
+ return Promise.resolve([]);
1086
+ }
1087
+ sendMessages(messages) {
1088
+ if (!Array.isArray(messages) && "tryAddMessage" in messages) throw new NotImplementedError(`${this.sendMessages.name}(ServiceBusMessageBatch)`);
1089
+ this.queue.push(...Array.isArray(messages) ? messages : [messages]);
1090
+ return Promise.resolve();
1091
+ }
1092
+ };
1093
+ // src/services/table/compareByCompositeKey.ts
1094
+ const compareByCompositeKey = (firstEntity, secondEntity) => {
1095
+ if (firstEntity.partitionKey !== secondEntity.partitionKey) return firstEntity.partitionKey < secondEntity.partitionKey ? -1 : 1;
1096
+ else if (firstEntity.rowKey === secondEntity.rowKey) return 0;
1097
+ else return firstEntity.rowKey < secondEntity.rowKey ? -1 : 1;
1098
+ };
1099
+ // src/store/MockTableDatabase.ts
1100
+ const MockTableDatabase = /* @__PURE__ */ new Map();
1101
+ // src/models/table/MockTableClient.ts
1102
+ /**
1103
+ * An in-memory mock of the Azure TableClient — no emulator and no network.
1104
+ *
1105
+ * @example
1106
+ * const mockTableClient = new MockTableClient("", "hello world");
1107
+ * await mockTableClient.createEntity({ partitionKey: "partitionKey", rowKey: "rowKey" });
1108
+ * const entity = await mockTableClient.getEntity("partitionKey", "rowKey");
1109
+ */
1110
+ var MockTableClient = class {
1111
+ tableName;
1112
+ url;
1113
+ get table() {
1114
+ return getOrCreate(MockTableDatabase, this.tableName, () => /* @__PURE__ */ new Map());
1115
+ }
1116
+ constructor(_url, tableName) {
1117
+ this.tableName = tableName;
1118
+ this.url = `${MOCK_TABLE_BASE_URL}/${this.tableName}`;
1119
+ }
1120
+ createEntity(entity) {
1121
+ this.#applyCreate(entity);
1122
+ return Promise.resolve({
1123
+ date: /* @__PURE__ */ new Date(),
1124
+ etag: this.#getEtag()
1125
+ });
1126
+ }
1127
+ createTable() {
1128
+ throw new NotImplementedError(this.createTable.name);
1129
+ }
1130
+ deleteEntity(partitionKey, rowKey, options) {
1131
+ this.#applyDelete(partitionKey, rowKey, options?.etag);
1132
+ return Promise.resolve({});
1133
+ }
1134
+ deleteTable() {
1135
+ throw new NotImplementedError(this.deleteTable.name);
1136
+ }
1137
+ getAccessPolicy() {
1138
+ throw new NotImplementedError(this.getAccessPolicy.name);
1139
+ }
1140
+ getEntity(partitionKey, rowKey) {
1141
+ const key = this.#getCompositeKey(partitionKey, rowKey);
1142
+ const entity = this.#getExistingEntity(key);
1143
+ return Promise.resolve({
1144
+ ...entity,
1145
+ etag: entity.etag ?? this.#getEtag()
1146
+ });
1147
+ }
1148
+ listEntities(options) {
1149
+ const withMetadata = this.#withMetadata.bind(this);
1150
+ const filter = options?.queryOptions?.filter;
1151
+ const tableEntities = [...this.table.values()];
1152
+ const predicate = filter ? createFilterPredicate(filter) : void 0;
1153
+ const resultTableEntities = (predicate ? tableEntities.filter((entity) => predicate(entity)) : tableEntities).toSorted(compareByCompositeKey);
1154
+ const entityIterator = (async function* (entities) {
1155
+ for (const entity of entities) yield await Promise.resolve(withMetadata(entity));
1156
+ })(resultTableEntities);
1157
+ return {
1158
+ byPage: ({ maxPageSize } = {}) => (async function* (entities) {
1159
+ if (maxPageSize !== void 0 && maxPageSize <= 0) throw new RangeError("maxPageSize must be greater than 0.");
1160
+ else if (maxPageSize !== void 0 && maxPageSize > AZURE_MAX_PAGE_SIZE) throw new MockRestError(INVALID_INPUT_MESSAGE, 400);
1161
+ if (entities.length === 0) return;
1162
+ else if (!maxPageSize) {
1163
+ yield await Promise.resolve(entities.map((entity) => withMetadata(entity)));
1164
+ return;
1165
+ }
1166
+ for (const page of chunk(entities, maxPageSize)) yield await Promise.resolve(page.map((entity) => withMetadata(entity)));
1167
+ })(resultTableEntities),
1168
+ next: () => entityIterator.next(),
1169
+ [Symbol.asyncIterator]() {
1170
+ return this;
1171
+ }
1172
+ };
1173
+ }
1174
+ setAccessPolicy() {
1175
+ throw new NotImplementedError(this.setAccessPolicy.name);
1176
+ }
1177
+ submitTransaction(actions) {
1178
+ if (actions.length > AZURE_MAX_BATCH_SIZE) throw new MockRestError(INVALID_INPUT_MESSAGE, 400);
1179
+ let partitionKey;
1180
+ for (const [, entity] of actions) if (partitionKey === void 0) partitionKey = entity.partitionKey;
1181
+ else if (entity.partitionKey !== partitionKey) throw new MockRestError("All transaction actions must target the same partitionKey.", 400);
1182
+ const snapshot = new Map(this.table);
1183
+ getResult(() => {
1184
+ for (const [type, entity, updateMode] of actions) switch (type) {
1185
+ case "create":
1186
+ this.#applyCreate(entity);
1187
+ break;
1188
+ case "delete":
1189
+ this.#applyDelete(entity.partitionKey, entity.rowKey);
1190
+ break;
1191
+ case "update":
1192
+ this.#applyUpdate(entity, updateMode);
1193
+ break;
1194
+ case "upsert":
1195
+ this.#applyUpsert(entity, updateMode);
1196
+ break;
1197
+ default: exhaustiveGuard(type);
1198
+ }
1199
+ }).orTee(() => {
1200
+ this.table.clear();
1201
+ for (const [key, value] of snapshot) this.table.set(key, value);
1202
+ }).match(noop, (error) => {
1203
+ throw error;
1204
+ });
1205
+ return Promise.resolve({
1206
+ getResponseForEntity: () => void 0,
1207
+ status: 202,
1208
+ subResponses: []
1209
+ });
1210
+ }
1211
+ updateEntity(entity, mode = "Merge", options) {
1212
+ this.#applyUpdate(entity, mode, options?.etag);
1213
+ return Promise.resolve({
1214
+ date: /* @__PURE__ */ new Date(),
1215
+ etag: this.#getEtag()
1216
+ });
1217
+ }
1218
+ upsertEntity(entity, mode = "Merge") {
1219
+ this.#applyUpsert(entity, mode);
1220
+ return Promise.resolve({
1221
+ date: /* @__PURE__ */ new Date(),
1222
+ etag: this.#getEtag()
1223
+ });
1224
+ }
1225
+ #applyCreate(entity) {
1226
+ const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1227
+ if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
1228
+ this.table.set(key, {
1229
+ ...entity,
1230
+ etag: this.#getEtag()
1231
+ });
1232
+ }
1233
+ #applyDelete(partitionKey, rowKey, etag) {
1234
+ const key = this.#getCompositeKey(partitionKey, rowKey);
1235
+ const existingEntity = this.#getExistingEntity(key);
1236
+ this.#assertEtag(existingEntity.etag, etag);
1237
+ this.table.delete(key);
1238
+ }
1239
+ #applyUpdate(entity, mode = "Merge", etag) {
1240
+ const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1241
+ const existingEntity = this.#getExistingEntity(key);
1242
+ this.#assertEtag(existingEntity.etag, etag);
1243
+ this.#writeEntity(key, entity, mode, existingEntity);
1244
+ }
1245
+ #applyUpsert(entity, mode = "Merge") {
1246
+ const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1247
+ const existingEntity = this.table.get(key);
1248
+ this.#writeEntity(key, entity, mode, existingEntity);
1249
+ }
1250
+ #assertEtag(existingEtag, etag) {
1251
+ if (etag !== void 0 && etag !== "*" && etag !== existingEtag) throw new MockRestError("The update condition specified in the request was not satisfied.", 412);
1252
+ }
1253
+ #getCompositeKey(partitionKey, rowKey) {
1254
+ return `${encodeURIComponent(partitionKey)}${ID_SEPARATOR}${encodeURIComponent(rowKey)}`;
1255
+ }
1256
+ #getEtag() {
1257
+ return `W/"${crypto.randomUUID()}"`;
1258
+ }
1259
+ #getExistingEntity(key) {
1260
+ const entity = this.table.get(key);
1261
+ if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
1262
+ return entity;
1263
+ }
1264
+ #withMetadata(entity) {
1265
+ return {
1266
+ ...entity,
1267
+ etag: entity.etag ?? this.#getEtag()
1268
+ };
1269
+ }
1270
+ #writeEntity(key, entity, mode, existingEntity) {
1271
+ if (existingEntity && mode === "Merge") this.table.set(key, {
1272
+ ...existingEntity,
1273
+ ...entity,
1274
+ etag: this.#getEtag()
1275
+ });
1276
+ else this.table.set(key, {
1277
+ ...entity,
1278
+ etag: this.#getEtag()
1279
+ });
1280
+ }
1281
+ };
1282
+ // src/store/MockWebPubSubDatabase.ts
1283
+ const MockWebPubSubDatabase = /* @__PURE__ */ new Map();
1284
+ // src/models/webPubSub/MockWebPubSubServiceClient.ts
1285
+ /**
1286
+ * A minimal in-memory mock of the Azure WebPubSubServiceClient — no service and no network.
1287
+ * A group's members are the user ids added to it, and `sendToAll` reaches nobody, since no connection is held.
1288
+ */
1289
+ var MockWebPubSubServiceClient = class {
1290
+ group(groupName) {
1291
+ const getMembers = () => getOrCreate(MockWebPubSubDatabase, groupName, () => /* @__PURE__ */ new Set());
1292
+ return {
1293
+ addUser: (userId) => {
1294
+ getMembers().add(userId);
1295
+ return Promise.resolve();
1296
+ },
1297
+ removeUser: (userId) => {
1298
+ getMembers().delete(userId);
1299
+ return Promise.resolve();
1300
+ },
1301
+ sendToAll: () => Promise.resolve()
1302
+ };
1303
+ }
1304
+ };
1305
+ // src/services/filter/applyFilter.ts
1306
+ const applyFilter = (documents, clauses) => {
1307
+ const predicate = createFilterPredicate(serializeClauses(clauses));
1308
+ return documents.filter((document) => predicate(document));
1309
+ };
1310
+ export { BLOB_NOT_FOUND_ERROR_CODE, BLOB_NOT_FOUND_MESSAGE, CONDITION_NOT_MET_ERROR_CODE, CONDITION_NOT_MET_MESSAGE, INVALID_INPUT_MESSAGE, MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_DATE, MOCK_BLOB_SEEDED_PROPERTIES, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerBlobDatesDatabase, MockContainerBlobMetadataDatabase, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubDatabase, MockWebPubSubServiceClient, NotImplementedError, applyFilter, assertMockBlobIfMatch, bodyToBuffer, checkIsNullClause, checkIsReadableStream, compare, compareByCompositeKey, compareValues, createFilterPredicate, createMockResponse, decodeSegment, deleteMockBlob, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsSegmentResponse, getListBlobsXml, getMockBlobKey, getMockContainer, getMockQueueMessageItem, getMockSasUrl, getSearchFieldValues, readMockBlobDates, readMockBlobMetadata, searchDocuments, sortDocuments, storeMockBlobMetadata, storeMockBlobWrite, toComparable, toWebResourceLike };