azure-mock 2.34.2 → 2.36.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![NPM downloads][badge-npm-downloads]][url-npm]
6
6
  [![NPM Unpacked Size (with version)][badge-npm-unpacked-size]][url-npm]
7
7
 
8
- Mock Azure service classes for local development and testing. Provides in-memory implementations of Azure Table Storage, Blob Storage, Queue Storage, and EventGrid clients that mirror the real Azure SDK interfaces.
8
+ Mock Azure service classes for local development and testing. Provides in-memory implementations of Azure Table Storage, Blob Storage, Service Bus, and EventGrid clients that mirror the real Azure SDK interfaces.
9
9
 
10
10
  ## Table of Contents
11
11
 
@@ -18,7 +18,7 @@ Mock Azure service classes for local development and testing. Provides in-memory
18
18
  ## <a name="getting-started">🚀 Getting Started</a>
19
19
 
20
20
  ```bash
21
- pnpm i -D azure-mock @azure/core-http-compat @azure/core-rest-pipeline @azure/data-tables @azure/eventgrid @azure/storage-blob @azure/storage-queue
21
+ pnpm i -D azure-mock @azure/core-http-compat @azure/core-rest-pipeline @azure/data-tables @azure/eventgrid @azure/service-bus @azure/storage-blob
22
22
  ```
23
23
 
24
24
  ## <a name="documentation">📖 Documentation</a>
@@ -31,7 +31,7 @@ We highly recommend you take a look at the [documentation](https://esposter.com/
31
31
  | ------------------------------ | -------------------------- | ----------------------------- |
32
32
  | `MockTableClient` | `TableClient` | In-memory Azure Table Storage |
33
33
  | `MockBlobServiceClient` | `BlobServiceClient` | In-memory Azure Blob Storage |
34
- | `MockQueueClient` | `QueueClient` | In-memory Azure Queue Storage |
34
+ | `MockServiceBusSender` | `ServiceBusSender` | In-memory Azure Service Bus |
35
35
  | `MockEventGridPublisherClient` | `EventGridPublisherClient` | No-op EventGrid publisher |
36
36
 
37
37
  ### Usage
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
+ import { PipelineRequest, RestError } from "@azure/core-rest-pipeline";
1
2
  import { HttpHeadersLike, WebResourceLike } from "@azure/core-http-compat";
2
3
  import { MapValue } from "@esposter/shared";
3
- import { PipelineRequest } from "@azure/core-rest-pipeline";
4
- import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobItem, BlobLeaseClient, BlobPrefix, BlobSetHTTPHeadersResponse, BlobSetImmutabilityPolicyResponse, BlobSetLegalHoldResponse, BlobSetMetadataResponse, BlobSetTagsResponse, BlobSetTierResponse, BlobUndeleteResponse, BlobUploadCommonResponse, BlockBlobClient, BlockBlobCommitBlockListResponse, BlockBlobGetBlockListResponse, BlockBlobPutBlobFromUrlResponse, BlockBlobStageBlockFromURLResponse, BlockBlobStageBlockResponse, BlockBlobUploadResponse, ContainerClient, ContainerCreateIfNotExistsResponse, ContainerCreateResponse, ContainerDeleteIfExistsResponse, ContainerDeleteResponse, ContainerFindBlobsByTagsSegmentResponse, ContainerGenerateSasUrlOptions, ContainerGetAccessPolicyResponse, ContainerGetAccountInfoResponse, ContainerGetPropertiesResponse, ContainerListBlobFlatSegmentResponse, ContainerListBlobHierarchySegmentResponse, ContainerListBlobsOptions, ContainerSetAccessPolicyResponse, ContainerSetMetadataResponse, FilterBlobItem, HttpRequestBody, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
4
+ import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobItem, BlobLeaseClient, BlobPrefix, BlobSetHTTPHeadersResponse, BlobSetImmutabilityPolicyResponse, BlobSetLegalHoldResponse, BlobSetMetadataResponse, BlobSetTagsResponse, BlobSetTierResponse, BlobUndeleteResponse, BlobUploadCommonResponse, BlockBlobClient, BlockBlobCommitBlockListResponse, BlockBlobGetBlockListResponse, BlockBlobPutBlobFromUrlResponse, BlockBlobStageBlockFromURLResponse, BlockBlobStageBlockResponse, BlockBlobUploadOptions, BlockBlobUploadResponse, ContainerClient, ContainerCreateIfNotExistsResponse, ContainerCreateResponse, ContainerDeleteIfExistsResponse, ContainerDeleteResponse, ContainerFindBlobsByTagsSegmentResponse, ContainerGenerateSasUrlOptions, ContainerGetAccessPolicyResponse, ContainerGetAccountInfoResponse, ContainerGetPropertiesResponse, ContainerListBlobFlatSegmentResponse, ContainerListBlobHierarchySegmentResponse, ContainerListBlobsOptions, ContainerSetAccessPolicyResponse, ContainerSetMetadataResponse, FilterBlobItem, HttpRequestBody, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
5
5
  import { BinaryOperator, Clause } from "@esposter/db-schema";
6
6
  import { EventGridEvent, EventGridPublisherClient } from "@azure/eventgrid";
7
+ import { ServiceBusMessage, ServiceBusMessageBatch, ServiceBusSender } from "@azure/service-bus";
7
8
  import { CreateTableEntityResponse, GetAccessPolicyResponse, GetTableEntityResponse, ListTableEntitiesOptions, TableClient, TableDeleteEntityHeaders, TableEntity, TableEntityResult, TableEntityResultPage, TableMergeEntityHeaders, TableSetAccessPolicyHeaders, TableTransactionResponse, UpdateMode } from "@azure/data-tables";
8
9
  import { QueueClearMessagesResponse, QueueClient, QueueCreateIfNotExistsResponse, QueueCreateOptions, QueueCreateResponse, QueueDeleteIfExistsResponse, QueueDeleteMessageResponse, QueueDeleteResponse, QueueGenerateSasUrlOptions, QueueGetAccessPolicyResponse, QueueGetPropertiesResponse, QueueItem, QueuePeekMessagesOptions, QueuePeekMessagesResponse, QueueReceiveMessageOptions, QueueReceiveMessageResponse, QueueSendMessageOptions, QueueSendMessageResponse, QueueServiceProperties, QueueSetAccessPolicyResponse, QueueSetMetadataResponse, QueueUpdateMessageResponse, SignedIdentifier } from "@azure/storage-queue";
9
10
  import { AutocompleteResult, CountDocumentsOptions, DeleteDocumentsOptions, GetDocumentOptions, IndexDocumentsBatch, IndexDocumentsOptions, IndexDocumentsResult, MergeDocumentsOptions, MergeOrUploadDocumentsOptions, NarrowedModel, SearchClient, SearchDocumentsResult, SearchOptions, SelectFields, SuggestDocumentsResult, SuggestOptions, UploadDocumentsOptions } from "@azure/search-documents";
@@ -17,8 +18,7 @@ interface MockResponse {
17
18
  request: WebResourceLike;
18
19
  status: number;
19
20
  }
20
- declare class MockRestError extends Error {
21
- statusCode: number;
21
+ declare class MockRestError extends RestError {
22
22
  constructor(message: string, statusCode: number);
23
23
  }
24
24
  /**
@@ -55,10 +55,14 @@ declare const createMockResponse: (status: number, url?: string) => MockResponse
55
55
  declare const getMockSasUrl: (url: string, permissions: undefined | {
56
56
  toString: () => string;
57
57
  }, resourceType?: string) => string;
58
+ declare const MockContainerCreatedOnDatabase: Map<string, Date>;
59
+ declare const MOCK_BLOB_SEEDED_CREATED_ON: Date;
60
+ declare const getMockContainerCreatedOnKey: (containerName: string, blobName: string) => string;
58
61
  declare const MockContainerDatabase: Map<string, Map<string, Buffer>>;
59
62
  declare const MockEventGridDatabase: Map<string, EventGridEvent<unknown>[]>;
60
63
  declare const MockQueueDatabase: Map<string, string[]>;
61
64
  declare const MockSearchDatabase: Map<string, object[]>;
65
+ declare const MockServiceBusDatabase: Map<string, ServiceBusMessage[]>;
62
66
  declare const MockTableDatabase: Map<string, Map<string, TableEntity>>;
63
67
  type BlobHierarchyItem = (BlobItem & {
64
68
  kind: "blob";
@@ -181,8 +185,8 @@ const update2: UpdateOperation<User> = {
181
185
  @category Utilities
182
186
  */
183
187
  type OptionalKeysOf<Type extends object> = Type extends unknown // For distributing `Type`
184
- ? (keyof { [Key in keyof Type as IsOptionalKeyOf<Type, Key> extends false ? never : Key]: never }) & keyof Type // Intersect with `keyof Type` to ensure result of `OptionalKeysOf<Type>` is always assignable to `keyof Type`
185
- : never;
188
+ ? (keyof { [Key in keyof Type as IsOptionalKeyOf<Type, Key> extends false ? never : Key]: never; }) & keyof Type // Intersect with `keyof Type` to ensure result of `OptionalKeysOf<Type>` is always assignable to `keyof Type`
189
+ : never; // Should never happen
186
190
  /**
187
191
  Extract all required keys from the given type.
188
192
 
@@ -214,7 +218,7 @@ const validator3 = createValidation<User>('luckyNumber', value => value > 0);
214
218
  @category Utilities
215
219
  */
216
220
  type RequiredKeysOf<Type extends object> = Type extends unknown // For distributing `Type`
217
- ? Exclude<keyof Type, OptionalKeysOf<Type>> : never;
221
+ ? Exclude<keyof Type, OptionalKeysOf<Type>> : never; // Should never happen
218
222
  /**
219
223
  Returns a boolean for whether the given type is `never`.
220
224
 
@@ -420,7 +424,7 @@ fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface`
420
424
  @see {@link SimplifyDeep}
421
425
  @category Object
422
426
  */
423
- type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
427
+ type Simplify<T> = { [KeyType in keyof T]: T[KeyType]; } & {};
424
428
  /**
425
429
  Returns a boolean for whether the two given types are equal.
426
430
 
@@ -541,7 +545,7 @@ type ExampleWithoutIndexSignatures = OmitIndexSignature<Example>;
541
545
  @see {@link PickIndexSignature}
542
546
  @category Object
543
547
  */
544
- type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType] };
548
+ type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType]; };
545
549
  /**
546
550
  Pick only index signatures from the given object type, leaving out all explicitly defined properties.
547
551
 
@@ -587,9 +591,9 @@ type ExampleIndexSignature = PickIndexSignature<Example>;
587
591
  @see {@link OmitIndexSignature}
588
592
  @category Object
589
593
  */
590
- type PickIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? KeyType : never]: ObjectType[KeyType] };
594
+ type PickIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? KeyType : never]: ObjectType[KeyType]; };
591
595
  // Merges two objects without worrying about index signatures.
592
- type SimpleMerge<Destination, Source> = Simplify<{ [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key] } & Source>;
596
+ type SimpleMerge<Destination, Source> = Simplify<{ [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key]; } & Source>;
593
597
  /**
594
598
  Merge two types into a new type. Keys of the second type overrides keys of the first type.
595
599
 
@@ -653,10 +657,9 @@ Note: If you want a merge type that more accurately reflects the runtime behavio
653
657
  @category Object
654
658
  */
655
659
  type Merge<Destination, Source> = Destination extends unknown // For distributing `Destination`
656
- ? Source extends unknown // For distributing `Source`
657
- ? If<IsEqual<Destination, Source>, Destination, _Merge<Destination, Source>> : never // Should never happen
658
- : never;
659
- // Should never happen
660
+ ? Source extends unknown // For distributing `Source`
661
+ ? If<IsEqual<Destination, Source>, Destination, _Merge<Destination, Source>> : never // Should never happen
662
+ : never; // Should never happen
660
663
  type _Merge<Destination, Source> = Simplify<SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
661
664
  /**
662
665
  Merges user specified options with default options.
@@ -710,36 +713,9 @@ type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOp
710
713
  // Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'.
711
714
  ```
712
715
  */
713
- type ApplyDefaultOptions<Options extends object, Defaults extends Simplify<Omit<Required<Options>, RequiredKeysOf<Options>> & Partial<Record<RequiredKeysOf<Options>, never>>>, SpecifiedOptions extends Options> = _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> extends infer Result extends Required<Options> // `extends Required<Options>` ensures that `ApplyDefaultOptions<SomeOption, ...>` is always assignable to `Required<SomeOption>`
714
- ? Result : never;
715
- type _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> = If<IsAny<SpecifiedOptions>, Defaults, If<IsNever<SpecifiedOptions>, Defaults, Merge<Defaults, { [Key in keyof SpecifiedOptions as undefined extends Required<Options>[Key & keyof Options] ? Key : undefined extends SpecifiedOptions[Key] ? never : Key]: SpecifiedOptions[Key] }>>>;
716
- /**
717
- Collapses literal types in a union into their corresponding primitive types, when possible. For example, `CollapseLiterals<'foo' | 'bar' | (string & {})>` returns `string`.
718
-
719
- Note: This doesn't collapse literals within tagged types. For example, `CollapseLiterals<Tagged<'foo' | (string & {}), 'Tag'>>` returns `("foo" & Tag<"Tag", never>) | (string & Tag<"Tag", never>)` and not `string & Tag<"Tag", never>`.
720
-
721
- Use-case: For collapsing unions created using {@link LiteralUnion}.
722
-
723
- @example
724
- ```
725
- import type {LiteralUnion} from 'type-fest';
726
-
727
- type A = CollapseLiterals<'foo' | 'bar' | (string & {})>;
728
- //=> string
729
-
730
- type B = CollapseLiterals<LiteralUnion<1 | 2 | 3, number>>;
731
- //=> number
732
-
733
- type C = CollapseLiterals<LiteralUnion<'onClick' | 'onChange', `on${string}`>>;
734
- //=> `on${string}`
735
-
736
- type D = CollapseLiterals<'click' | 'change' | (`on${string}` & {})>;
737
- //=> 'click' | 'change' | `on${string}`
738
-
739
- type E = CollapseLiterals<LiteralUnion<'foo' | 'bar', string> | null | undefined>;
740
- //=> string | null | undefined
741
- ```
742
- */
716
+ type ApplyDefaultOptions<Options extends object, Defaults extends Simplify<Omit<Required<Options>, RequiredKeysOf<Options>> & Partial<Record<RequiredKeysOf<Options>, never>>>, SpecifiedOptions extends Options> = _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> extends (infer Result extends Required<Options> // `extends Required<Options>` ensures that `ApplyDefaultOptions<SomeOption, ...>` is always assignable to `Required<SomeOption>`
717
+ ) ? Result : never;
718
+ type _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> = If<IsAny<SpecifiedOptions>, Defaults, If<IsNever<SpecifiedOptions>, Defaults, Merge<Defaults, { [Key in keyof SpecifiedOptions as undefined extends Required<Options>[Key & keyof Options] ? Key : undefined extends SpecifiedOptions[Key] ? never : Key]: SpecifiedOptions[Key]; }>>>;
743
719
  /**
744
720
  Filter out keys from an object.
745
721
 
@@ -770,10 +746,12 @@ type Filtered = Filter<'bar', 'foo'>;
770
746
  type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);
771
747
  type ExceptOptions = {
772
748
  /**
773
- Disallow assigning non-specified properties.
774
- Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`.
775
- @default false
776
- */
749
+ Disallow assigning non-specified properties.
750
+
751
+ Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`.
752
+
753
+ @default false
754
+ */
777
755
  requireExactProps?: boolean;
778
756
  };
779
757
  type DefaultExceptOptions = {
@@ -835,7 +813,7 @@ type PostPayloadFixed = Except<UserData, 'email'>;
835
813
  @category Object
836
814
  */
837
815
  type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {}> = _Except<ObjectType, KeysType, ApplyDefaultOptions<ExceptOptions, DefaultExceptOptions, Options>>;
838
- type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType] } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
816
+ type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType]; } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
839
817
  declare class MockBlobClient implements Except<BlobClient, "accountName"> {
840
818
  connectionString: string;
841
819
  containerName: string;
@@ -883,7 +861,7 @@ declare class MockBlockBlobClient extends MockBlobClient implements Except<Block
883
861
  stageBlock(): Promise<BlockBlobStageBlockResponse>;
884
862
  stageBlockFromURL(): Promise<BlockBlobStageBlockFromURLResponse>;
885
863
  syncUploadFromURL(): Promise<BlockBlobPutBlobFromUrlResponse>;
886
- upload(body: HttpRequestBody, _contentLength: number): Promise<BlockBlobUploadResponse>;
864
+ upload(body: HttpRequestBody, _contentLength: number, options?: BlockBlobUploadOptions): Promise<BlockBlobUploadResponse>;
887
865
  uploadBrowserData(): Promise<BlobUploadCommonResponse>;
888
866
  uploadData(): Promise<BlobUploadCommonResponse>;
889
867
  uploadFile(): Promise<BlobUploadCommonResponse>;
@@ -928,7 +906,7 @@ declare class MockContainerClient implements Except<ContainerClient, "accountNam
928
906
  getPageBlobClient(): PageBlobClient;
929
907
  getProperties(): Promise<ContainerGetPropertiesResponse>;
930
908
  listBlobsByHierarchy(delimiter: string, options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobHierarchyItem, ContainerListBlobHierarchySegmentResponse>;
931
- listBlobsFlat(): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
909
+ listBlobsFlat(options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
932
910
  setAccessPolicy(): Promise<ContainerSetAccessPolicyResponse>;
933
911
  setMetadata(): Promise<ContainerSetMetadataResponse>;
934
912
  uploadBlockBlob(blobName: string, body: HttpRequestBody, contentLength: number): Promise<{
@@ -1014,6 +992,26 @@ declare class MockSearchClient<TModel extends object = Record<string, unknown>>
1014
992
  suggest<TFields extends SelectFields<TModel> = never>(_searchText: string, _suggesterName: string, _options?: SuggestOptions<TModel, TFields>): Promise<SuggestDocumentsResult<TModel, TFields>>;
1015
993
  uploadDocuments(documents: TModel[], _options?: UploadDocumentsOptions): Promise<IndexDocumentsResult>;
1016
994
  }
995
+ /**
996
+ * An in-memory mock of the Azure ServiceBusSender.
997
+ * It uses a Map to simulate a Service Bus queue and correctly implements the ServiceBusSender interface.
998
+ *
999
+ * @example
1000
+ * const mockServiceBusSender = new MockServiceBusSender("hello world");
1001
+ * await mockServiceBusSender.scheduleMessages({ body: "hello world" }, new Date());
1002
+ */
1003
+ declare class MockServiceBusSender implements ServiceBusSender {
1004
+ entityPath: string;
1005
+ identifier: string;
1006
+ isClosed: boolean;
1007
+ get queue(): MapValue<typeof MockServiceBusDatabase>;
1008
+ constructor(queueName: string);
1009
+ cancelScheduledMessages(): Promise<void>;
1010
+ close(): Promise<void>;
1011
+ createMessageBatch(): Promise<ServiceBusMessageBatch>;
1012
+ scheduleMessages(messages: ServiceBusMessage | ServiceBusMessage[], scheduledEnqueueTimeUtc: Date): Promise<never[]>;
1013
+ sendMessages(messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch): Promise<void>;
1014
+ }
1017
1015
  /**
1018
1016
  * An in-memory mock of the Azure TableClient.
1019
1017
  * It uses a Map to simulate table storage and correctly implements the TableClient interface.
@@ -1039,7 +1037,9 @@ declare class MockTableClient<TEntity extends TableEntity = TableEntity> impleme
1039
1037
  listEntities<T extends object>(options?: ListTableEntitiesOptions): PagedAsyncIterableIterator<TableEntityResult<T>, TableEntityResultPage<T>>;
1040
1038
  setAccessPolicy(): Promise<TableSetAccessPolicyHeaders>;
1041
1039
  submitTransaction(actions: Parameters<TableClient["submitTransaction"]>[0]): Promise<TableTransactionResponse>;
1042
- updateEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
1040
+ updateEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode, options?: {
1041
+ etag?: string;
1042
+ }): Promise<TableMergeEntityHeaders>;
1043
1043
  upsertEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
1044
1044
  }
1045
1045
  /**
@@ -1059,11 +1059,13 @@ declare const bodyToBuffer: (body: HttpRequestBody) => Promise<Buffer>;
1059
1059
  * @returns A formatted XML string.
1060
1060
  */
1061
1061
  declare const getAzureErrorXml: (errorCode: string, errorMessage: string) => string;
1062
- declare const getBlobItemXml: ({
1063
- name,
1064
- properties
1065
- }: BlobItem) => string;
1062
+ declare const getBlobItemXml: ({ name, properties }: BlobItem) => string;
1066
1063
  declare const getBlobPrefixXml: (name: string) => string;
1064
+ declare const getBlobUrl: (containerName: string, blobName: string) => string;
1065
+ declare const getBlobUrlParts: (url: string) => undefined | {
1066
+ blobName: string;
1067
+ containerName: string;
1068
+ };
1067
1069
  /**
1068
1070
  * Generates a standard Azure Storage list blobs XML response body.
1069
1071
  * @param containerName The container name.
@@ -1077,4 +1079,5 @@ declare const applyFilter: <T extends Record<string, unknown>>(documents: T[], c
1077
1079
  declare const compare: <T>(operator: BinaryOperator, leftHandSide: T, rightHandSide: null | T) => boolean;
1078
1080
  declare const createFilterPredicate: (filter: string) => ((document: Record<string, unknown>) => boolean);
1079
1081
  declare const isNullClause: (clause: Clause<Record<string, unknown>>) => boolean;
1080
- export { BlobHierarchyItem, MOCK_BLOB_BASE_URL, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockResponse, MockRestError, MockSearchClient, MockSearchDatabase, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, PageSettings, PagedAsyncIterableIterator, applyFilter, bodyToBuffer, compare, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getListBlobsXml, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
1082
+ declare const compareByCompositeKey: (a: TableEntity, b: TableEntity) => number;
1083
+ export { BlobHierarchyItem, MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_CREATED_ON, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerCreatedOnDatabase, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockResponse, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, PageSettings, PagedAsyncIterableIterator, applyFilter, bodyToBuffer, compare, compareByCompositeKey, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsXml, getMockContainerCreatedOnKey, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
+ import { RestError, createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
1
2
  import { toHttpHeadersLike } from "@azure/core-http-compat";
2
- import { ID_SEPARATOR, InvalidOperationError, Operation, exhaustiveGuard, getOrCreate, getResultAsync, html, noop, streamToText, takeOne } from "@esposter/shared";
3
- import { createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
3
+ import { ID_SEPARATOR, InvalidOperationError, Operation, exhaustiveGuard, getOrCreate, getResult, html, noop, streamToText, takeOne } from "@esposter/shared";
4
4
  import { AnonymousCredential } from "@azure/storage-blob";
5
5
  import { Readable } from "node:stream";
6
6
  import { MAX_QUEUE_VISIBILITY_TIMEOUT_MS, deserializeClause, deserializeKey, getTableNullClause, serializeClauses } from "@esposter/db";
7
- import { BinaryOperator } from "@esposter/db-schema";
7
+ import { AZURE_MAX_PAGE_SIZE, BinaryOperator } from "@esposter/db-schema";
8
8
  var __create = Object.create;
9
9
  var __defProp = Object.defineProperty;
10
10
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -22,7 +22,7 @@ var __copyProps = (to, from, except, desc) => {
22
22
  }
23
23
  return to;
24
24
  };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
26
26
  value: mod,
27
27
  enumerable: true
28
28
  }) : target, mod));
@@ -30,12 +30,10 @@ const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
30
30
  const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
31
31
  const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
32
32
  const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
33
- var MockRestError = class extends Error {
34
- statusCode;
33
+ var MockRestError = class extends RestError {
35
34
  constructor(message, statusCode) {
36
- super(message);
35
+ super(message, { statusCode });
37
36
  this.name = "MockRestError";
38
- this.statusCode = statusCode;
39
37
  }
40
38
  };
41
39
  const toWebResourceLike = (request) => ({
@@ -70,10 +68,20 @@ const createMockResponse = (status, url = "") => ({
70
68
  status
71
69
  });
72
70
  const getMockSasUrl = (url, permissions, resourceType) => `${url}?sv=2025-11-05&${resourceType ? `sr=${resourceType}&` : ""}sig=mock-signature&st=1970-01-01T00:00:00Z&se=2099-12-31T23:59:59Z&sp=${permissions?.toString() ?? "r"}`;
73
- const MockContainerDatabase = /* @__PURE__ */ new Map();
71
+ const MockContainerCreatedOnDatabase = /* @__PURE__ */ new Map();
72
+ const MOCK_BLOB_SEEDED_CREATED_ON = /* @__PURE__ */ new Date(-864e13);
73
+ const getMockContainerCreatedOnKey = (containerName, blobName) => `${containerName}/${blobName}`;
74
+ var MockContainerDatabaseMap = class extends Map {
75
+ clear() {
76
+ super.clear();
77
+ MockContainerCreatedOnDatabase.clear();
78
+ }
79
+ };
80
+ const MockContainerDatabase = new MockContainerDatabaseMap();
74
81
  const MockEventGridDatabase = /* @__PURE__ */ new Map();
75
82
  const MockQueueDatabase = /* @__PURE__ */ new Map();
76
83
  const MockSearchDatabase = /* @__PURE__ */ new Map();
84
+ const MockServiceBusDatabase = /* @__PURE__ */ new Map();
77
85
  const MockTableDatabase = /* @__PURE__ */ new Map();
78
86
  /**
79
87
  * Generates a standard Azure Storage error XML response body.
@@ -86,6 +94,17 @@ const getAzureErrorXml = (errorCode, errorMessage) => html`<?xml version="1.0" e
86
94
  <code>${errorCode}</code>
87
95
  <Message>${errorMessage}</Message>
88
96
  </Error>`;
97
+ const decodeSegment = (segment) => getResult(() => decodeURIComponent(segment)).unwrapOr(segment);
98
+ const getBlobUrlParts = (url) => {
99
+ const parsedUrl = getResult(() => new URL(url)).unwrapOr(void 0);
100
+ if (!parsedUrl) return void 0;
101
+ const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
102
+ if (pathSegments.length < 2) return void 0;
103
+ return {
104
+ blobName: decodeSegment(pathSegments.slice(1).join("/")),
105
+ containerName: decodeSegment(takeOne(pathSegments))
106
+ };
107
+ };
89
108
  var MockBlobBatchClient = class {
90
109
  url;
91
110
  constructor(url) {
@@ -102,8 +121,8 @@ var MockBlobBatchClient = class {
102
121
  let subResponsesSucceededCount = 0;
103
122
  let subResponsesFailedCount = 0;
104
123
  for (const url of urls) {
105
- const pathSegments = new URL(url).pathname.split("/").filter(Boolean);
106
- if (pathSegments.length < 2) {
124
+ const urlParts = getBlobUrlParts(url);
125
+ if (!urlParts) {
107
126
  const errorCode = "InvalidUri";
108
127
  const statusMessage = "Invalid blob URL format.";
109
128
  subResponses.push({
@@ -120,11 +139,11 @@ var MockBlobBatchClient = class {
120
139
  subResponsesFailedCount++;
121
140
  continue;
122
141
  }
123
- const containerName = takeOne(pathSegments);
124
- const blobName = pathSegments.slice(1).join("/");
142
+ const { blobName, containerName } = urlParts;
125
143
  const container = this.getContainer(containerName);
126
144
  if (container.has(blobName)) {
127
145
  container.delete(blobName);
146
+ MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(containerName, blobName));
128
147
  subResponses.push({
129
148
  _request: {
130
149
  credential,
@@ -169,6 +188,7 @@ var MockBlobBatchClient = class {
169
188
  return getOrCreate(MockContainerDatabase, containerName, () => /* @__PURE__ */ new Map());
170
189
  }
171
190
  };
191
+ const getBlobUrl = (containerName, blobName) => `${MOCK_BLOB_BASE_URL}/${containerName}/${blobName.split("/").map((segment) => encodeURIComponent(segment)).join("/")}`;
172
192
  var MockBlobClient = class {
173
193
  connectionString;
174
194
  containerName;
@@ -182,21 +202,21 @@ var MockBlobClient = class {
182
202
  this.connectionString = connectionString;
183
203
  this.containerName = containerName;
184
204
  this.name = blobName;
185
- this.url = `${MOCK_BLOB_BASE_URL}/${this.containerName}/${this.name}`;
205
+ this.url = getBlobUrl(this.containerName, this.name);
186
206
  }
187
207
  abortCopyFromURL() {
188
208
  throw new Error("Method not implemented.");
189
209
  }
190
210
  beginCopyFromURL(copySource) {
191
- const pathSegments = new URL(copySource).pathname.split("/").filter(Boolean);
192
- if (pathSegments.length < 2) throw new MockRestError("Invalid copy source URL format", 400);
193
- const sourceContainerName = takeOne(pathSegments);
194
- const sourceBlobName = pathSegments.slice(1).join("/");
211
+ const sourceParts = getBlobUrlParts(copySource);
212
+ if (!sourceParts) throw new MockRestError("Invalid copy source URL format", 400);
213
+ const { blobName: sourceBlobName, containerName: sourceContainerName } = sourceParts;
195
214
  const sourceContainer = MockContainerDatabase.get(sourceContainerName);
196
215
  if (!sourceContainer) throw new MockRestError("Source container not found", 404);
197
216
  const sourceData = sourceContainer.get(sourceBlobName);
198
217
  if (!sourceData) throw new MockRestError("Source blob not found", 404);
199
218
  this.container.set(this.name, Buffer.from(sourceData));
219
+ MockContainerCreatedOnDatabase.set(getMockContainerCreatedOnKey(this.containerName, this.name), /* @__PURE__ */ new Date());
200
220
  const response = { _response: createMockResponse(202, `${this.url}?comp=copy`) };
201
221
  return Promise.resolve({
202
222
  cancelOperation: () => Promise.resolve(),
@@ -219,11 +239,15 @@ var MockBlobClient = class {
219
239
  delete() {
220
240
  if (!this.container.has(this.name)) throw new MockRestError("The specified blob does not exist.", 404);
221
241
  this.container.delete(this.name);
242
+ MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, this.name));
222
243
  return Promise.resolve({ _response: createMockResponse(200) });
223
244
  }
224
245
  deleteIfExists() {
225
246
  const succeeded = this.container.has(this.name);
226
- if (succeeded) this.container.delete(this.name);
247
+ if (succeeded) {
248
+ this.container.delete(this.name);
249
+ MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, this.name));
250
+ }
227
251
  return Promise.resolve({
228
252
  _response: createMockResponse(succeeded ? 200 : 404),
229
253
  succeeded
@@ -248,7 +272,7 @@ var MockBlobClient = class {
248
272
  throw new Error("Method not implemented.");
249
273
  }
250
274
  exists() {
251
- throw new Error("Method not implemented.");
275
+ return Promise.resolve(this.container.has(this.name));
252
276
  }
253
277
  generateSasStringToSign() {
254
278
  throw new Error("Method not implemented.");
@@ -361,8 +385,11 @@ var MockBlockBlobClient = class extends MockBlobClient {
361
385
  syncUploadFromURL() {
362
386
  throw new Error("Method not implemented.");
363
387
  }
364
- async upload(body, _contentLength) {
365
- this.container.set(this.name, await bodyToBuffer(body));
388
+ async upload(body, _contentLength, options) {
389
+ const buffer = await bodyToBuffer(body);
390
+ if (options?.conditions?.ifNoneMatch === "*" && this.container.has(this.name)) throw new MockRestError("The specified blob already exists.", 409);
391
+ this.container.set(this.name, buffer);
392
+ MockContainerCreatedOnDatabase.set(getMockContainerCreatedOnKey(this.containerName, this.name), /* @__PURE__ */ new Date());
366
393
  return { _response: createMockResponse(201) };
367
394
  }
368
395
  uploadBrowserData() {
@@ -437,7 +464,8 @@ var MockContainerClient = class {
437
464
  deleteBlob(blobName) {
438
465
  if (!this.container.has(blobName)) throw new MockRestError("The specified blob does not exist.", 404);
439
466
  this.container.delete(blobName);
440
- return Promise.resolve({ _response: createMockResponse(200, `${this.url}/${blobName}`) });
467
+ MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, blobName));
468
+ return Promise.resolve({ _response: createMockResponse(200, getBlobUrl(this.containerName, blobName)) });
441
469
  }
442
470
  deleteIfExists() {
443
471
  throw new Error("Method not implemented.");
@@ -527,8 +555,8 @@ var MockContainerClient = class {
527
555
  }
528
556
  };
529
557
  }
530
- listBlobsFlat() {
531
- const blobItemIterator = this.#getBlobItemIterator();
558
+ listBlobsFlat(options) {
559
+ const blobItemIterator = this.#getBlobItemIterator(options);
532
560
  return {
533
561
  byPage: () => async function* () {
534
562
  const allBlobItems = [];
@@ -544,14 +572,14 @@ var MockContainerClient = class {
544
572
  parsedBody: {
545
573
  containerName: this.containerName,
546
574
  marker: "",
547
- prefix: "",
575
+ prefix: options?.prefix ?? "",
548
576
  segment: { blobItems: allBlobItems },
549
577
  serviceEndpoint: ""
550
578
  }
551
579
  },
552
580
  containerName: this.containerName,
553
581
  marker: "",
554
- prefix: "",
582
+ prefix: options?.prefix ?? "",
555
583
  segment: { blobItems: allBlobItems },
556
584
  serviceEndpoint: ""
557
585
  });
@@ -590,6 +618,7 @@ var MockContainerClient = class {
590
618
  blobType: "BlockBlob",
591
619
  contentLength: buffer.length,
592
620
  contentType: "application/octet-stream",
621
+ createdOn: MockContainerCreatedOnDatabase.get(getMockContainerCreatedOnKey(this.containerName, name)) ?? MOCK_BLOB_SEEDED_CREATED_ON,
593
622
  etag: `"${crypto.randomUUID()}"`,
594
623
  lastModified: /* @__PURE__ */ new Date(),
595
624
  leaseState: "available",
@@ -611,21 +640,26 @@ var MockContainerClient = class {
611
640
  ...blobItem
612
641
  });
613
642
  }
614
- async *#getBlobItemIterator() {
615
- for (const [name, buffer] of this.container.entries()) yield await Promise.resolve({
616
- deleted: false,
617
- name,
618
- properties: {
619
- blobType: "BlockBlob",
620
- contentLength: buffer.length,
621
- contentType: "application/octet-stream",
622
- etag: `"${crypto.randomUUID()}"`,
623
- lastModified: /* @__PURE__ */ new Date(),
624
- leaseState: "available",
625
- leaseStatus: "unlocked"
626
- },
627
- snapshot: ""
628
- });
643
+ async *#getBlobItemIterator(options) {
644
+ const prefix = options?.prefix ?? "";
645
+ for (const [name, buffer] of this.container.entries()) {
646
+ if (!name.startsWith(prefix)) continue;
647
+ yield await Promise.resolve({
648
+ deleted: false,
649
+ name,
650
+ properties: {
651
+ blobType: "BlockBlob",
652
+ contentLength: buffer.length,
653
+ contentType: "application/octet-stream",
654
+ createdOn: MockContainerCreatedOnDatabase.get(getMockContainerCreatedOnKey(this.containerName, name)) ?? MOCK_BLOB_SEEDED_CREATED_ON,
655
+ etag: `"${crypto.randomUUID()}"`,
656
+ lastModified: /* @__PURE__ */ new Date(),
657
+ leaseState: "available",
658
+ leaseStatus: "unlocked"
659
+ },
660
+ snapshot: ""
661
+ });
662
+ }
629
663
  }
630
664
  };
631
665
  /**
@@ -836,7 +870,8 @@ var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJS
836
870
  };
837
871
  })))(), 1);
838
872
  const isNullClause = (clause) => {
839
- return (0, import_fast_deep_equal.default)(clause, getTableNullClause(clause.key));
873
+ const tableNullClause = getTableNullClause(clause.key);
874
+ return (0, import_fast_deep_equal.default)(clause, tableNullClause);
840
875
  };
841
876
  const createFilterPredicate = (filter) => {
842
877
  const orGroups = filter.replaceAll(String.raw`(`, " ").replaceAll(String.raw`)`, "").split(/\s+and\s+/iu).filter(Boolean).map((group) => group.split(/\s+or\s+/iu).filter(Boolean));
@@ -962,6 +997,53 @@ var MockSearchClient = class {
962
997
  }
963
998
  };
964
999
  /**
1000
+ * An in-memory mock of the Azure ServiceBusSender.
1001
+ * It uses a Map to simulate a Service Bus queue and correctly implements the ServiceBusSender interface.
1002
+ *
1003
+ * @example
1004
+ * const mockServiceBusSender = new MockServiceBusSender("hello world");
1005
+ * await mockServiceBusSender.scheduleMessages({ body: "hello world" }, new Date());
1006
+ */
1007
+ var MockServiceBusSender = class {
1008
+ entityPath;
1009
+ identifier;
1010
+ isClosed = false;
1011
+ get queue() {
1012
+ return getOrCreate(MockServiceBusDatabase, this.entityPath, () => []);
1013
+ }
1014
+ constructor(queueName) {
1015
+ this.entityPath = queueName;
1016
+ this.identifier = queueName;
1017
+ }
1018
+ cancelScheduledMessages() {
1019
+ throw new Error("Method not implemented.");
1020
+ }
1021
+ close() {
1022
+ this.isClosed = true;
1023
+ return Promise.resolve();
1024
+ }
1025
+ createMessageBatch() {
1026
+ throw new Error("Method not implemented.");
1027
+ }
1028
+ scheduleMessages(messages, scheduledEnqueueTimeUtc) {
1029
+ for (const message of Array.isArray(messages) ? messages : [messages]) this.queue.push({
1030
+ ...message,
1031
+ scheduledEnqueueTimeUtc
1032
+ });
1033
+ return Promise.resolve([]);
1034
+ }
1035
+ sendMessages(messages) {
1036
+ if (!Array.isArray(messages) && "tryAddMessage" in messages) throw new Error("Method not implemented.");
1037
+ this.queue.push(...Array.isArray(messages) ? messages : [messages]);
1038
+ return Promise.resolve();
1039
+ }
1040
+ };
1041
+ const compareByCompositeKey = (a, b) => {
1042
+ if (a.partitionKey !== b.partitionKey) return a.partitionKey < b.partitionKey ? -1 : 1;
1043
+ else if (a.rowKey === b.rowKey) return 0;
1044
+ else return a.rowKey < b.rowKey ? -1 : 1;
1045
+ };
1046
+ /**
965
1047
  * An in-memory mock of the Azure TableClient.
966
1048
  * It uses a Map to simulate table storage and correctly implements the TableClient interface.
967
1049
  *
@@ -981,9 +1063,7 @@ var MockTableClient = class {
981
1063
  this.url = `${MOCK_TABLE_BASE_URL}/${this.tableName}`;
982
1064
  }
983
1065
  createEntity(entity) {
984
- const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
985
- if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
986
- this.table.set(key, entity);
1066
+ this.#applyCreate(entity);
987
1067
  return Promise.resolve({
988
1068
  date: /* @__PURE__ */ new Date(),
989
1069
  etag: this.#getEtag()
@@ -993,9 +1073,7 @@ var MockTableClient = class {
993
1073
  throw new Error("Method not implemented.");
994
1074
  }
995
1075
  deleteEntity(partitionKey, rowKey) {
996
- const key = this.#getCompositeKey(partitionKey, rowKey);
997
- if (!this.table.has(key)) throw new MockRestError("The specified resource does not exist.", 404);
998
- this.table.delete(key);
1076
+ this.#applyDelete(partitionKey, rowKey);
999
1077
  return Promise.resolve({});
1000
1078
  }
1001
1079
  deleteTable() {
@@ -1010,18 +1088,22 @@ var MockTableClient = class {
1010
1088
  if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
1011
1089
  return Promise.resolve({
1012
1090
  ...entity,
1013
- etag: this.#getEtag()
1091
+ etag: entity.etag ?? this.#getEtag()
1014
1092
  });
1015
1093
  }
1016
1094
  listEntities(options) {
1017
1095
  const withMetadata = this.#withMetadata.bind(this);
1018
1096
  const filter = options?.queryOptions?.filter;
1019
- const tableEntities = [...this.table.values()];
1097
+ const tableEntities = [...this.table.values()].toSorted(compareByCompositeKey);
1020
1098
  const predicate = filter ? createFilterPredicate(filter) : void 0;
1021
1099
  const resultTableEntities = predicate ? tableEntities.filter((e) => predicate(e)) : tableEntities;
1100
+ const entityIterator = (async function* (entities) {
1101
+ for (const entity of entities) yield await Promise.resolve(withMetadata(entity));
1102
+ })(resultTableEntities);
1022
1103
  return {
1023
1104
  byPage: ({ maxPageSize } = {}) => (async function* (entities) {
1024
1105
  if (maxPageSize !== void 0 && maxPageSize <= 0) throw new RangeError("maxPageSize must be greater than 0.");
1106
+ else if (maxPageSize !== void 0 && maxPageSize > AZURE_MAX_PAGE_SIZE) throw new MockRestError("One of the request inputs is not valid.", 400);
1025
1107
  const allEntitiesWithMetadata = entities.map((e) => withMetadata(e));
1026
1108
  if (allEntitiesWithMetadata.length === 0) return;
1027
1109
  else if (!maxPageSize) {
@@ -1030,9 +1112,7 @@ var MockTableClient = class {
1030
1112
  }
1031
1113
  for (let i = 0; i < allEntitiesWithMetadata.length; i += maxPageSize) yield await Promise.resolve(allEntitiesWithMetadata.slice(i, i + maxPageSize));
1032
1114
  })(resultTableEntities),
1033
- next: () => (async function* (entities) {
1034
- for (const entity of entities) yield await Promise.resolve(withMetadata(entity));
1035
- })(resultTableEntities).next(),
1115
+ next: () => entityIterator.next(),
1036
1116
  [Symbol.asyncIterator]() {
1037
1117
  return this;
1038
1118
  }
@@ -1041,24 +1121,24 @@ var MockTableClient = class {
1041
1121
  setAccessPolicy() {
1042
1122
  throw new Error("Method not implemented.");
1043
1123
  }
1044
- async submitTransaction(actions) {
1124
+ submitTransaction(actions) {
1045
1125
  let partitionKey;
1046
1126
  for (const [, entity] of actions) if (partitionKey === void 0) partitionKey = entity.partitionKey;
1047
1127
  else if (entity.partitionKey !== partitionKey) throw new MockRestError("All transaction actions must target the same partitionKey.", 400);
1048
1128
  const snapshot = new Map(this.table);
1049
- await getResultAsync(async () => {
1129
+ getResult(() => {
1050
1130
  for (const [type, entity, updateMode] of actions) switch (type) {
1051
1131
  case "create":
1052
- await this.createEntity(entity);
1132
+ this.#applyCreate(entity);
1053
1133
  break;
1054
1134
  case "delete":
1055
- await this.deleteEntity(entity.partitionKey, entity.rowKey);
1135
+ this.#applyDelete(entity.partitionKey, entity.rowKey);
1056
1136
  break;
1057
1137
  case "update":
1058
- await this.updateEntity(entity, updateMode);
1138
+ this.#applyUpdate(entity, updateMode);
1059
1139
  break;
1060
1140
  case "upsert":
1061
- await this.upsertEntity(entity, updateMode);
1141
+ this.#applyUpsert(entity, updateMode);
1062
1142
  break;
1063
1143
  default: exhaustiveGuard(type);
1064
1144
  }
@@ -1068,57 +1148,77 @@ var MockTableClient = class {
1068
1148
  }).match(noop, (error) => {
1069
1149
  throw error;
1070
1150
  });
1071
- return {
1072
- getResponseForEntity: noop,
1151
+ return Promise.resolve({
1152
+ getResponseForEntity: () => void 0,
1073
1153
  status: 202,
1074
1154
  subResponses: []
1075
- };
1155
+ });
1156
+ }
1157
+ updateEntity(entity, mode = "Merge", options) {
1158
+ this.#applyUpdate(entity, mode, options?.etag);
1159
+ return Promise.resolve({
1160
+ date: /* @__PURE__ */ new Date(),
1161
+ etag: this.#getEtag()
1162
+ });
1163
+ }
1164
+ upsertEntity(entity, mode = "Merge") {
1165
+ this.#applyUpsert(entity, mode);
1166
+ return Promise.resolve({
1167
+ date: /* @__PURE__ */ new Date(),
1168
+ etag: this.#getEtag()
1169
+ });
1170
+ }
1171
+ #applyCreate(entity) {
1172
+ const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1173
+ if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
1174
+ this.table.set(key, {
1175
+ ...entity,
1176
+ etag: this.#getEtag()
1177
+ });
1178
+ }
1179
+ #applyDelete(partitionKey, rowKey) {
1180
+ const key = this.#getCompositeKey(partitionKey, rowKey);
1181
+ if (!this.table.has(key)) throw new MockRestError("The specified resource does not exist.", 404);
1182
+ this.table.delete(key);
1076
1183
  }
1077
- updateEntity(entity, mode = "Merge") {
1184
+ #applyUpdate(entity, mode = "Merge", etag) {
1078
1185
  const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1079
1186
  const existingEntity = this.table.get(key);
1080
1187
  if (!existingEntity) throw new MockRestError("The specified resource does not exist.", 404);
1081
- else if (mode === "Merge") return this.#mergeEntity(key, existingEntity, entity);
1082
- else {
1083
- this.table.set(key, entity);
1084
- return Promise.resolve({
1085
- date: /* @__PURE__ */ new Date(),
1086
- etag: this.#getEtag()
1087
- });
1088
- }
1188
+ else if (etag !== void 0 && etag !== "*" && etag !== existingEntity.etag) throw new MockRestError("The update condition specified in the request was not satisfied.", 412);
1189
+ else if (mode === "Merge") this.table.set(key, {
1190
+ ...existingEntity,
1191
+ ...entity,
1192
+ etag: this.#getEtag()
1193
+ });
1194
+ else this.table.set(key, {
1195
+ ...entity,
1196
+ etag: this.#getEtag()
1197
+ });
1089
1198
  }
1090
- upsertEntity(entity, mode = "Merge") {
1199
+ #applyUpsert(entity, mode = "Merge") {
1091
1200
  const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
1092
1201
  const existingEntity = this.table.get(key);
1093
- if (existingEntity && mode === "Merge") return this.#mergeEntity(key, existingEntity, entity);
1094
- else {
1095
- this.table.set(key, entity);
1096
- return Promise.resolve({
1097
- date: /* @__PURE__ */ new Date(),
1098
- etag: this.#getEtag()
1099
- });
1100
- }
1202
+ if (existingEntity && mode === "Merge") this.table.set(key, {
1203
+ ...existingEntity,
1204
+ ...entity,
1205
+ etag: this.#getEtag()
1206
+ });
1207
+ else this.table.set(key, {
1208
+ ...entity,
1209
+ etag: this.#getEtag()
1210
+ });
1101
1211
  }
1102
1212
  #getCompositeKey(partitionKey, rowKey) {
1103
1213
  return `${partitionKey}${ID_SEPARATOR}${rowKey}`;
1104
1214
  }
1105
1215
  #getEtag() {
1106
- return `W/"datetime'${(/* @__PURE__ */ new Date()).toISOString()}'"`;
1107
- }
1108
- #mergeEntity(key, entity, entityToMerge) {
1109
- this.table.set(key, {
1110
- ...entity,
1111
- ...entityToMerge
1112
- });
1113
- return Promise.resolve({
1114
- date: /* @__PURE__ */ new Date(),
1115
- etag: this.#getEtag()
1116
- });
1216
+ return `W/"${crypto.randomUUID()}"`;
1117
1217
  }
1118
1218
  #withMetadata(entity) {
1119
1219
  return {
1120
1220
  ...entity,
1121
- etag: this.#getEtag()
1221
+ etag: entity.etag ?? this.#getEtag()
1122
1222
  };
1123
1223
  }
1124
1224
  };
@@ -1135,4 +1235,4 @@ const applyFilter = (documents, clauses) => {
1135
1235
  const predicate = createFilterPredicate(serializeClauses(clauses));
1136
1236
  return documents.filter((document) => predicate(document));
1137
1237
  };
1138
- export { MOCK_BLOB_BASE_URL, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, applyFilter, bodyToBuffer, compare, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getListBlobsXml, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
1238
+ export { MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_CREATED_ON, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerCreatedOnDatabase, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, applyFilter, bodyToBuffer, compare, compareByCompositeKey, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsXml, getMockContainerCreatedOnKey, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azure-mock",
3
- "version": "2.34.2",
3
+ "version": "2.36.0",
4
4
  "description": "A library that contains azure mock classes.",
5
5
  "keywords": [
6
6
  "azure",
@@ -10,6 +10,7 @@
10
10
  "eventgrid",
11
11
  "mock",
12
12
  "search",
13
+ "service-bus",
13
14
  "storage-queue",
14
15
  "table-storage",
15
16
  "testing",
@@ -46,27 +47,28 @@
46
47
  "typecheck": "tsgo"
47
48
  },
48
49
  "dependencies": {
49
- "@esposter/db": "2.34.2",
50
- "@esposter/db-schema": "2.34.2",
51
- "@esposter/shared": "2.34.2",
50
+ "@esposter/db": "2.36.0",
51
+ "@esposter/db-schema": "2.36.0",
52
+ "@esposter/shared": "2.36.0",
52
53
  "fast-deep-equal": "^3.1.3"
53
54
  },
54
55
  "devDependencies": {
55
- "@esposter/configuration": "2.34.2",
56
- "ctix": "^2.8.1",
57
- "rolldown": "^1.1.4",
56
+ "@esposter/configuration": "2.36.0",
57
+ "ctix": "^2.8.2",
58
+ "rolldown": "^1.2.1",
58
59
  "type-fest": "^5.8.0",
59
- "typedoc": "^0.28.19",
60
- "vitest": "^4.1.9"
60
+ "typedoc": "^0.28.20",
61
+ "vitest": "^4.1.10"
61
62
  },
62
63
  "peerDependencies": {
63
- "@azure/core-http-compat": "^2.4.0",
64
- "@azure/core-rest-pipeline": "^1.24.0",
64
+ "@azure/core-http-compat": "^2.5.0",
65
+ "@azure/core-rest-pipeline": "^1.25.0",
65
66
  "@azure/data-tables": "^13.3.2",
66
67
  "@azure/eventgrid": "^5.12.0",
67
68
  "@azure/search-documents": "^13.0.0",
69
+ "@azure/service-bus": "^7.9.5",
68
70
  "@azure/storage-blob": "^12.33.0",
69
71
  "@azure/storage-queue": "^12.31.0"
70
72
  },
71
- "gitHead": "70032635c47bdf1e55414c7d84c955350f991055"
73
+ "gitHead": "786902aca40292b2883de2ad1eb876ca32f6ac75"
72
74
  }