aws-sdk-vitest-mock 0.5.0 → 0.7.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
@@ -12,10 +12,12 @@
12
12
  <a href="https://www.npmjs.com/package/aws-sdk-vitest-mock">
13
13
  <img src="https://img.shields.io/npm/v/aws-sdk-vitest-mock?color=cb3837&logo=npm" alt="npm version" />
14
14
  </a>
15
+ <img alt="NPM Downloads" src="https://img.shields.io/npm/dm/aws-sdk-vitest-mock">
16
+ <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sudokar/aws-sdk-vitest-mock">
15
17
  <a href="https://github.com/sudokar/aws-sdk-vitest-mock/actions">
16
18
  <img src="https://github.com/sudokar/aws-sdk-vitest-mock/actions/workflows/ci.yml/badge.svg" alt="CI Status" />
17
19
  </a>
18
- <img src="https://img.shields.io/badge/ESM%20Support-yes-4B32C3?logo=javascript" alt="ESM Support" />
20
+ <img src="https://img.shields.io/badge/ESM%20Support-yes-4B32C3?logo=typescript" alt="ESM Support" />
19
21
  <img src="https://img.shields.io/badge/Zero%20Dependencies-yes-brightgreen" alt="Zero Dependencies" />
20
22
  <a href="https://eslint.org/">
21
23
  <img src="https://img.shields.io/badge/code%20style-eslint-4B32C3?logo=eslint" alt="ESLint" />
@@ -564,6 +566,46 @@ s3Mock.restore();
564
566
  s3Mock.disableDebug();
565
567
  ```
566
568
 
569
+ #### Global Debug Configuration
570
+
571
+ Enable debug logging for all mocks globally, with the ability to override at the individual mock level:
572
+
573
+ ```typescript
574
+ import { setGlobalDebug, mockClient } from "aws-sdk-vitest-mock";
575
+ import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
576
+ import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
577
+
578
+ // Enable debug for all mocks
579
+ setGlobalDebug(true);
580
+
581
+ // All mocks will inherit the global debug setting
582
+ const s3Mock = mockClient(S3Client);
583
+ const dynamoMock = mockClient(DynamoDBClient);
584
+
585
+ // Both mocks will log debug information
586
+ s3Mock.on(GetObjectCommand).resolves({ Body: "data" });
587
+ dynamoMock.on(GetItemCommand).resolves({ Item: { id: { S: "1" } } });
588
+
589
+ // Override global setting for a specific mock
590
+ s3Mock.disableDebug(); // This mock won't log, but dynamoMock still will
591
+
592
+ // Disable global debug
593
+ setGlobalDebug(false);
594
+ ```
595
+
596
+ **Debug Priority (highest to lowest):**
597
+
598
+ 1. Individual mock's `enableDebug()` or `disableDebug()` call (explicit override)
599
+ 2. Global debug setting via `setGlobalDebug()`
600
+ 3. Default: disabled
601
+
602
+ **Key behaviors:**
603
+
604
+ - When global debug is enabled, all new and existing mocks will log unless explicitly disabled
605
+ - Individual mock settings always take priority over global settings
606
+ - `reset()` preserves individual debug settings
607
+ - Global debug can be changed at any time and affects all mocks without explicit settings
608
+
567
609
  Debug mode provides comprehensive logging for:
568
610
 
569
611
  **Mock Configuration:**
@@ -650,6 +692,8 @@ test("should call DynamoDB", async () => {
650
692
 
651
693
  ## 📚 API Reference
652
694
 
695
+ > TypeScript documentation for this library can be found at [here](https://sudokar.github.io/aws-sdk-vitest-mock/)
696
+
653
697
  ### `mockClient<TClient>(ClientConstructor)`
654
698
 
655
699
  Creates a mock for an AWS SDK client constructor.
@@ -662,14 +706,18 @@ Mocks an existing AWS SDK client instance.
662
706
 
663
707
  **Returns:** `AwsClientStub<TClient>`
664
708
 
709
+ ### Global Debug Functions
710
+
711
+ - `setGlobalDebug(enabled: boolean)` - Enable or disable debug logging globally for all mocks
712
+
665
713
  ### `AwsClientStub` Methods
666
714
 
667
715
  - `on(Command, matcher?, options?)` - Configure mock for a command
668
716
  - `reset()` - Clear call history while preserving mock configurations
669
717
  - `restore()` - Restore original client behavior
670
718
  - `calls()` - Get call history
671
- - `enableDebug()` - Enable debug logging for troubleshooting
672
- - `disableDebug()` - Disable debug logging
719
+ - `enableDebug()` - Enable debug logging for troubleshooting (overrides global setting)
720
+ - `disableDebug()` - Disable debug logging (overrides global setting)
673
721
 
674
722
  ### `AwsCommandStub` Methods (Chainable)
675
723
 
package/index.cjs CHANGED
@@ -1,18 +1,18 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("vitest"),m=require("./matchers-Dkkl4vtx.cjs"),x=require("node:fs"),O=require("node:path"),F=require("node:stream");class f extends Error{constructor(t,o,i,n){super(t),this.name="AwsError",this.code=o,this.statusCode=i,this.retryable=n}}const L=e=>{const t=e?`The specified key does not exist. Key: ${e}`:"The specified key does not exist.";return new f(t,"NoSuchKey",404,!1)},D=e=>{const t=e?`The specified bucket does not exist. Bucket: ${e}`:"The specified bucket does not exist.";return new f(t,"NoSuchBucket",404,!1)},I=e=>{const t=e?`Access Denied for resource: ${e}`:"Access Denied";return new f(t,"AccessDenied",403,!1)},T=e=>{const t=e?`Requested resource not found: ${e}`:"Requested resource not found";return new f(t,"ResourceNotFoundException",400,!1)},K=()=>new f("The conditional request failed","ConditionalCheckFailedException",400,!1),M=()=>new f("Rate exceeded","Throttling",400,!0),A=()=>new f("We encountered an internal error. Please try again.","InternalServerError",500,!0);function B(e){try{return JSON.stringify(e,void 0,2)}catch{return typeof e=="object"&&e!==null?"[Complex Object]":typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?String(e):"[Non-serializable value]"}}function j(){return{enabled:!1,log(e,t){if(this.enabled)if(t===void 0)console.log(`${m.colors.magenta("aws-sdk-vitest-mock(debug):")} ${e}`);else{const o=B(t);console.log(`${m.colors.magenta("aws-sdk-vitest-mock(debug):")} ${e}
2
- ${o}`)}}}}function S(e){e.enabled=!0}function w(e){e.enabled=!1}function q(e){const t=O.resolve(e),o=x.readFileSync(t,"utf8");return e.endsWith(".json")?JSON.parse(o):o}function J(e,t={}){const{pageSize:o=10,tokenKey:i="NextToken",itemsKey:n="Items"}=t;if(e.length===0)return[{[n]:[]}];const s=[];for(let r=0;r<e.length;r+=o){const c=e.slice(r,r+o),u=r+o<e.length,l={[n]:c};if(u){const g=l,a=c[c.length-1];g[i]=a}s.push(l)}return s}function _(){return typeof process<"u"&&process.versions?.node?"node":typeof process<"u"&&process.versions?.bun?"bun":"browser"}function z(e){const t=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e);let o=!1;return new F.Readable({read(){o||(this.push(t),this.push(null),o=!0)}})}function V(e){let t;return typeof e=="string"?t=new TextEncoder().encode(e):e instanceof Buffer?t=new Uint8Array(e):t=e,new ReadableStream({start(o){o.enqueue(t),o.close()}})}function k(e){const t=_();return t==="node"||t==="bun"?z(e):V(e)}function $(e,t){return Object.keys(t).every(o=>{const i=t[o],n=e[o];return i&&typeof i=="object"&&!Array.isArray(i)?typeof n!="object"||n===null?!1:$(n,i):n===i})}function C(e,t){if(e===t)return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return e===t;const o=Object.keys(e),i=Object.keys(t);return o.length!==i.length?!1:i.every(n=>{if(!Object.prototype.hasOwnProperty.call(e,n))return!1;const s=e,r=t,c=s[n],u=r[n];return typeof c=="object"&&c!==null&&typeof u=="object"&&u!==null?C(c,u):c===u})}function N(e){return async function(t){const o=()=>this;e.debugLogger.log(`Received command: ${t.constructor.name}`,t.input);const i=e.map.get(t.constructor);if(i){e.debugLogger.log(`Found ${i.length} mock(s) for ${t.constructor.name}`);const s=i.findIndex(r=>r.strict?r.matcher&&C(t.input,r.matcher):!r.matcher||$(t.input,r.matcher));if(s===-1){e.debugLogger.log(`No matching mock found for ${t.constructor.name}`,t.input);const r=i.map((u,l)=>{const g=u.matcher?JSON.stringify(u.matcher,void 0,2):"any input",a=u.strict?" (strict mode)":"";return` Mock #${l+1}: ${g}${a}`}).join(`
3
- `),c=JSON.stringify(t.input,void 0,2);throw new Error(`No matching mock found for ${t.constructor.name}.
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("vitest"),$=require("./matchers-CNhdB_9q.cjs"),W=require("node:fs"),C=require("node:path"),I=require("node:stream");class d extends Error{constructor(t,o,s,n){super(t),this.name=o,this.code=o,this.statusCode=s,this.retryable=n}}const T=e=>new d(e?`The specified key does not exist. Key: ${e}`:"The specified key does not exist.","NoSuchKey",404,!1),M=e=>new d(e?`The specified bucket does not exist. Bucket: ${e}`:"The specified bucket does not exist.","NoSuchBucket",404,!1),K=e=>new d(e?`Access Denied for resource: ${e}`:"Access Denied","AccessDenied",403,!1),A=e=>new d(e?`Requested resource not found: ${e}`:"Requested resource not found","ResourceNotFoundException",400,!1),B=()=>new d("The conditional request failed","ConditionalCheckFailedException",400,!1),J=()=>new d("Rate exceeded","Throttling",400,!0),q=()=>new d("We encountered an internal error. Please try again.","InternalServerError",500,!0),_=e=>{try{return JSON.stringify(e,void 0,2)}catch{return typeof e=="object"&&e!==null?"[Complex Object]":typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?String(e):"[Non-serializable value]"}},k=(e,t)=>{const o=$.colors.magenta("aws-sdk-vitest-mock(debug):");if(t===void 0)console.log(`${o} ${e}`);else{const s=_(t);console.log(`${o} ${e}
2
+ ${s}`)}},w=(e=!1)=>({enabled:e,explicitlySet:!1,log(t,o){this.enabled&&k(t,o)},logDirect(t,o){k(t,o)}}),z=e=>{e.enabled=!0,e.explicitlySet=!0},V=e=>{e.enabled=!1,e.explicitlySet=!0},G=e=>{const t=C.resolve(e);try{const o=W.readFileSync(t,"utf8");if(e.endsWith(".json"))try{return JSON.parse(o)}catch(s){const n=s instanceof Error?s.message:String(s);throw new Error(`Failed to parse JSON fixture at ${t}: ${n}`)}return o}catch(o){const s=o instanceof Error?o.message:String(o);throw new Error(`Failed to load fixture at ${t}: ${s}`)}},U=(e,t={})=>{const{pageSize:o=10,tokenKey:s="NextToken",itemsKey:n="Items"}=t;if(e.length===0)return[{[n]:[]}];const c=[];for(let r=0;r<e.length;r+=o){const i=e.slice(r,r+o),l=r+o<e.length,u={[n]:i};if(l){const a=u,f=i[i.length-1];a[s]=f}c.push(u)}return c},H=()=>typeof process<"u"&&process.versions?.node?"node":typeof process<"u"&&process.versions?.bun?"bun":"browser",Q=e=>{const t=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e);let o=!1;return new I.Readable({read(){o||(this.push(t),this.push(null),o=!0)}})},X=e=>{let t;return typeof e=="string"?t=new TextEncoder().encode(e):e instanceof Buffer?t=new Uint8Array(e):t=e,new ReadableStream({start(o){o.enqueue(t),o.close()}})},S=e=>{const t=H();return t==="node"||t==="bun"?Q(e):X(e)};let D=!1;function Y(e){D=e}function b(e){return e.explicitlySet?e.enabled:D}function E(e,t){return Object.keys(t).every(o=>{const s=t[o],n=e[o];return s&&typeof s=="object"&&!Array.isArray(s)?typeof n!="object"||n===null?!1:E(n,s):n===s})}function N(e,t){if(e===t)return!0;if(typeof e!="object"||e===null||typeof t!="object"||t===null)return e===t;const o=Object.keys(e),s=Object.keys(t);return o.length!==s.length?!1:s.every(n=>{if(!Object.prototype.hasOwnProperty.call(e,n))return!1;const c=e,r=t,i=c[n],l=r[n];return typeof i=="object"&&i!==null&&typeof l=="object"&&l!==null?N(i,l):i===l})}function Z(e,t,o){const s=t.map((c,r)=>{const i=c.matcher?JSON.stringify(c.matcher,void 0,2):"any input",l=c.strict?" (strict mode)":"";return` Mock #${r+1}: ${i}${l}`}).join(`
3
+ `),n=JSON.stringify(o,void 0,2);return new Error(`No matching mock found for ${e}.
4
4
 
5
- Found ${i.length} mock(s) but none matched the input.
5
+ Found ${t.length} mock(s) but none matched the input.
6
6
 
7
7
  Configured mocks:
8
- ${r}
8
+ ${s}
9
9
 
10
10
  Received input:
11
- ${c}
11
+ ${n}
12
12
 
13
- Tip: Enable debug mode with enableDebug() for detailed matching information.`)}else{const r=i[s];if(!r)throw new Error(`Mock at index ${s} not found`);return e.debugLogger.log(`Using mock at index ${s} for ${t.constructor.name}`),r.once&&(i.splice(s,1),e.debugLogger.log(`Removed one-time mock for ${t.constructor.name}`)),r.handler(t.input,o())}}else e.debugLogger.log(`No mocks configured for ${t.constructor.name}`);const n=JSON.stringify(t.input,void 0,2);throw new Error(`No mock configured for command: ${t.constructor.name}.
13
+ Tip: Enable debug mode with enableDebug() for detailed matching information.`)}function ee(e,t){const o=JSON.stringify(t,void 0,2);return new Error(`No mock configured for command: ${e}.
14
14
 
15
15
  Received input:
16
- ${n}
16
+ ${o}
17
17
 
18
- Did you forget to call mockClient.on(${t.constructor.name})?`)}}function R(e,t,o,i={}){const n=(r,c,u)=>{const l={matcher:o,handler:r,once:c,strict:!!i.strict},g=e.map.get(t)??[];if(c){const a=g.findIndex(d=>!d.once);a===-1?g.push(l):g.splice(a,0,l),e.map.set(t,g),e.debugLogger.log(`Configured ${u}Once for ${t.name}`,o?{matcher:o,strict:!!i.strict}:void 0)}else{const a=g.filter(d=>d.once||JSON.stringify(d.matcher)!==JSON.stringify(o));a.push(l),e.map.set(t,a),e.debugLogger.log(`Configured ${u} for ${t.name}`,o?{matcher:o,strict:!!i.strict}:void 0)}},s={resolves(r){return n(()=>Promise.resolve(r),!1,"resolves"),s},rejects(r){return n(()=>{const c=typeof r=="string"?new Error(r):r;return Promise.reject(c)},!1,"rejects"),s},callsFake(r){return n(r,!1,"callsFake"),s},resolvesOnce(r){return n(()=>Promise.resolve(r),!0,"resolves"),s},rejectsOnce(r){return n(()=>{const c=typeof r=="string"?new Error(r):r;return Promise.reject(c)},!0,"rejects"),s},callsFakeOnce(r){return n(r,!0,"callsFake"),s},resolvesStream(r){return n(()=>Promise.resolve({Body:k(r)}),!1,"resolvesStream"),s},resolvesStreamOnce(r){return n(()=>Promise.resolve({Body:k(r)}),!0,"resolvesStream"),s},resolvesWithDelay(r,c){const u=l=>{setTimeout(()=>l(r),c)};return n(()=>new Promise(u),!1,"resolvesWithDelay"),s},rejectsWithDelay(r,c){const u=typeof r=="string"?new Error(r):r,l=(g,a)=>{setTimeout(()=>a(u),c)};return n(()=>new Promise(l),!1,"rejectsWithDelay"),s},rejectsWithNoSuchKey(r){return n(()=>Promise.reject(L(r)),!1,"rejectsWithNoSuchKey"),s},rejectsWithNoSuchBucket(r){return n(()=>Promise.reject(D(r)),!1,"rejectsWithNoSuchBucket"),s},rejectsWithAccessDenied(r){return n(()=>Promise.reject(I(r)),!1,"rejectsWithAccessDenied"),s},rejectsWithResourceNotFound(r){return n(()=>Promise.reject(T(r)),!1,"rejectsWithResourceNotFound"),s},rejectsWithConditionalCheckFailed(){return n(()=>Promise.reject(K()),!1,"rejectsWithConditionalCheckFailed"),s},rejectsWithThrottling(){return n(()=>Promise.reject(M()),!1,"rejectsWithThrottling"),s},rejectsWithInternalServerError(){return n(()=>Promise.reject(A()),!1,"rejectsWithInternalServerError"),s},resolvesPaginated(r,c={}){const u=J(r,c);let l=0;return e.debugLogger.log(`Configured resolvesPaginated for ${t.name}`,{pageSize:c.pageSize,itemsCount:r.length}),n(g=>{const a=c.tokenKey||"NextToken",d=c.inputTokenKey||a,h=g[d];if(h!=null){const E=c.itemsKey||"Items";let y=0;for(const W of u){const p=W[E];if(p&&p.length>0){const P=p[p.length-1];if(JSON.stringify(P)===JSON.stringify(h)){l=y+1;break}}y++}}else l=0;const b=u[l]||u[u.length-1]||u[0];if(!b)throw new Error("No paginated responses available");return l=Math.min(l+1,u.length-1),Promise.resolve(b)},!1,"resolvesPaginated"),s},resolvesFromFile(r){return e.debugLogger.log(`Configured resolvesFromFile for ${t.name}`,{filePath:r}),n(()=>{const c=q(r);return Promise.resolve(c)},!1,"resolvesFromFile"),s}};return s}const U=e=>{const t={map:new WeakMap,debugLogger:j()},o=e.prototype,i=v.vi.spyOn(o,"send").mockImplementation(N(t));return{client:void 0,on:(s,r,c)=>R(t,s,r,c),reset:()=>{t.debugLogger.log("Clearing call history (mocks preserved)"),i.mockClear()},restore:()=>{t.debugLogger.log("Restoring original client behavior and clearing all mocks"),i.mockRestore(),t.map=new WeakMap},calls:()=>i.mock.calls.map(s=>s[0]),__rawCalls:()=>i.mock.calls,enableDebug:()=>{S(t.debugLogger)},disableDebug:()=>{w(t.debugLogger)}}},G=e=>{const t={map:new WeakMap,debugLogger:j()},o=v.vi.spyOn(e,"send").mockImplementation(N(t));return{client:e,on:(n,s,r)=>R(t,n,s,r),reset:()=>{t.debugLogger.log("Clearing call history (mocks preserved) for client instance"),o.mockClear()},restore:()=>{t.debugLogger.log("Restoring original client behavior and clearing all mocks for client instance"),o.mockRestore(),t.map=new WeakMap},calls:()=>o.mock.calls.map(n=>n[0]),__rawCalls:()=>o.mock.calls,enableDebug:()=>{S(t.debugLogger)},disableDebug:()=>{w(t.debugLogger)}}};exports.matchers=m.matchers;exports.mockClient=U;exports.mockClientInstance=G;
18
+ Did you forget to call mockClient.on(${e})?`)}function te(e,t){return e.findIndex(o=>o.strict?o.matcher&&N(t,o.matcher):!o.matcher||E(t,o.matcher))}function x(e){return async function(t){const o=()=>this,s=b(e.debugLogger),n=t.constructor.name;s&&e.debugLogger.logDirect(`Received command: ${n}`,t.input);const c=e.map.get(t.constructor);if(!c)throw s&&e.debugLogger.logDirect(`No mocks configured for ${n}`),ee(n,t.input);s&&e.debugLogger.logDirect(`Found ${c.length} mock(s) for ${n}`);const r=te(c,t.input);if(r===-1)throw s&&e.debugLogger.logDirect(`No matching mock found for ${n}`,t.input),Z(n,c,t.input);const i=c[r];if(!i)throw new Error(`Mock at index ${r} not found`);return s&&e.debugLogger.logDirect(`Using mock at index ${r} for ${n}`),i.once&&(c.splice(r,1),s&&e.debugLogger.logDirect(`Removed one-time mock for ${n}`)),i.handler(t.input,o())}}const L=(e,t,o)=>({reset(){b(e.debugLogger)&&e.debugLogger.logDirect(o.reset),t.mockClear()},restore(){b(e.debugLogger)&&e.debugLogger.logDirect(o.restore),t.mockRestore(),e.map=new WeakMap},calls(){return t.mock.calls.map(s=>s[0])},__rawCalls(){return t.mock.calls},enableDebug(){z(e.debugLogger)},disableDebug(){V(e.debugLogger)}});function R(e,t,o,s={}){const n=(r,i,l)=>{const u={matcher:o,handler:r,once:i,strict:!!s.strict},a=e.map.get(t)??[],f=b(e.debugLogger);if(i){const g=a.findIndex(h=>!h.once);g===-1?a.push(u):a.splice(g,0,u),e.map.set(t,a),f&&e.debugLogger.logDirect(`Configured ${l}Once for ${t.name}`,o?{matcher:o,strict:!!s.strict}:void 0)}else{const g=a.filter(h=>h.once||JSON.stringify(h.matcher)!==JSON.stringify(o));g.push(u),e.map.set(t,g),f&&e.debugLogger.logDirect(`Configured ${l} for ${t.name}`,o?{matcher:o,strict:!!s.strict}:void 0)}},c={resolves(r){return n(()=>Promise.resolve(r),!1,"resolves"),c},rejects(r){return n(()=>{const i=typeof r=="string"?new Error(r):r;return Promise.reject(i)},!1,"rejects"),c},callsFake(r){return n(r,!1,"callsFake"),c},resolvesOnce(r){return n(()=>Promise.resolve(r),!0,"resolves"),c},rejectsOnce(r){return n(()=>{const i=typeof r=="string"?new Error(r):r;return Promise.reject(i)},!0,"rejects"),c},callsFakeOnce(r){return n(r,!0,"callsFake"),c},resolvesStream(r){return n(()=>Promise.resolve({Body:S(r)}),!1,"resolvesStream"),c},resolvesStreamOnce(r){return n(()=>Promise.resolve({Body:S(r)}),!0,"resolvesStream"),c},resolvesWithDelay(r,i){const l=u=>{setTimeout(()=>u(r),i)};return n(()=>new Promise(l),!1,"resolvesWithDelay"),c},rejectsWithDelay(r,i){const l=typeof r=="string"?new Error(r):r,u=(a,f)=>{setTimeout(()=>f(l),i)};return n(()=>new Promise(u),!1,"rejectsWithDelay"),c},rejectsWithNoSuchKey(r){return n(()=>Promise.reject(T(r)),!1,"rejectsWithNoSuchKey"),c},rejectsWithNoSuchBucket(r){return n(()=>Promise.reject(M(r)),!1,"rejectsWithNoSuchBucket"),c},rejectsWithAccessDenied(r){return n(()=>Promise.reject(K(r)),!1,"rejectsWithAccessDenied"),c},rejectsWithResourceNotFound(r){return n(()=>Promise.reject(A(r)),!1,"rejectsWithResourceNotFound"),c},rejectsWithConditionalCheckFailed(){return n(()=>Promise.reject(B()),!1,"rejectsWithConditionalCheckFailed"),c},rejectsWithThrottling(){return n(()=>Promise.reject(J()),!1,"rejectsWithThrottling"),c},rejectsWithInternalServerError(){return n(()=>Promise.reject(q()),!1,"rejectsWithInternalServerError"),c},resolvesPaginated(r,i={}){const l=U(r,i);let u=0;return e.debugLogger.log(`Configured resolvesPaginated for ${t.name}`,{pageSize:i.pageSize,itemsCount:r.length}),n(a=>{const f=i.tokenKey||"NextToken",g=i.inputTokenKey||f,m=a[g];if(m!=null){const F=i.itemsKey||"Items";let v=0;for(const O of l){const p=O[F];if(p&&p.length>0){const P=p[p.length-1];if(JSON.stringify(P)===JSON.stringify(m)){u=v+1;break}}v++}}else u=0;const y=l[u]||l[l.length-1]||l[0];if(!y)throw new Error("No paginated responses available");return u=Math.min(u+1,l.length-1),Promise.resolve(y)},!1,"resolvesPaginated"),c},resolvesFromFile(r){return e.debugLogger.log(`Configured resolvesFromFile for ${t.name}`,{filePath:r}),n(()=>{const i=G(r);return Promise.resolve(i)},!1,"resolvesFromFile"),c}};return c}const re=e=>{const t={map:new WeakMap,debugLogger:w()},o=e.prototype,s=j.vi.spyOn(o,"send").mockImplementation(x(t)),n=L(t,s,{reset:"Clearing call history (mocks preserved)",restore:"Restoring original client behavior and clearing all mocks"});return{client:void 0,on:(r,i,l)=>R(t,r,i,l),...n}},oe=e=>{const t={map:new WeakMap,debugLogger:w()},o=j.vi.spyOn(e,"send").mockImplementation(x(t)),s=L(t,o,{reset:"Clearing call history (mocks preserved) for client instance",restore:"Restoring original client behavior and clearing all mocks for client instance"});return{client:e,on:(c,r,i)=>R(t,c,r,i),...s}};exports.matchers=$.matchers;exports.mockClient=re;exports.mockClientInstance=oe;exports.setGlobalDebug=Y;
package/index.d.ts CHANGED
@@ -29,7 +29,7 @@
29
29
  * Core Functions for mocking AWS SDK clients
30
30
  * @category Core Functions
31
31
  */
32
- export { mockClient, mockClientInstance } from './lib/mock-client.js';
32
+ export { mockClient, mockClientInstance, setGlobalDebug, } from './lib/mock-client.js';
33
33
  /**
34
34
  * Command stub interface for configuring mock behaviors
35
35
  * @category Command Stub
package/index.js CHANGED
@@ -1,454 +1,476 @@
1
- import { vi as v } from "vitest";
2
- import { c as y } from "./matchers-ClGOsQx8.js";
3
- import { m as ne } from "./matchers-ClGOsQx8.js";
1
+ import { vi as j } from "vitest";
2
+ import { c as O } from "./matchers-DmTtFk31.js";
3
+ import { m as ge } from "./matchers-DmTtFk31.js";
4
4
  import { readFileSync as P } from "node:fs";
5
- import F from "node:path";
6
- import { Readable as O } from "node:stream";
7
- class f extends Error {
8
- constructor(t, n, i, o) {
9
- super(t), this.name = "AwsError", this.code = n, this.statusCode = i, this.retryable = o;
5
+ import C from "node:path";
6
+ import { Readable as I } from "node:stream";
7
+ class d extends Error {
8
+ constructor(t, o, s, n) {
9
+ super(t), this.name = o, this.code = o, this.statusCode = s, this.retryable = n;
10
10
  }
11
11
  }
12
- const L = (e) => {
13
- const t = e ? `The specified key does not exist. Key: ${e}` : "The specified key does not exist.";
14
- return new f(t, "NoSuchKey", 404, !1);
15
- }, D = (e) => {
16
- const t = e ? `The specified bucket does not exist. Bucket: ${e}` : "The specified bucket does not exist.";
17
- return new f(t, "NoSuchBucket", 404, !1);
18
- }, I = (e) => {
19
- const t = e ? `Access Denied for resource: ${e}` : "Access Denied";
20
- return new f(t, "AccessDenied", 403, !1);
21
- }, T = (e) => {
22
- const t = e ? `Requested resource not found: ${e}` : "Requested resource not found";
23
- return new f(t, "ResourceNotFoundException", 400, !1);
24
- }, K = () => new f(
12
+ const T = (e) => new d(
13
+ e ? `The specified key does not exist. Key: ${e}` : "The specified key does not exist.",
14
+ "NoSuchKey",
15
+ 404,
16
+ !1
17
+ ), M = (e) => new d(
18
+ e ? `The specified bucket does not exist. Bucket: ${e}` : "The specified bucket does not exist.",
19
+ "NoSuchBucket",
20
+ 404,
21
+ !1
22
+ ), K = (e) => new d(
23
+ e ? `Access Denied for resource: ${e}` : "Access Denied",
24
+ "AccessDenied",
25
+ 403,
26
+ !1
27
+ ), A = (e) => new d(
28
+ e ? `Requested resource not found: ${e}` : "Requested resource not found",
29
+ "ResourceNotFoundException",
30
+ 400,
31
+ !1
32
+ ), B = () => new d(
25
33
  "The conditional request failed",
26
34
  "ConditionalCheckFailedException",
27
35
  400,
28
36
  !1
29
- ), M = () => new f("Rate exceeded", "Throttling", 400, !0), A = () => new f(
37
+ ), J = () => new d("Rate exceeded", "Throttling", 400, !0), q = () => new d(
30
38
  "We encountered an internal error. Please try again.",
31
39
  "InternalServerError",
32
40
  500,
33
41
  !0
34
- );
35
- function B(e) {
42
+ ), z = (e) => {
36
43
  try {
37
44
  return JSON.stringify(e, void 0, 2);
38
45
  } catch {
39
46
  return typeof e == "object" && e !== null ? "[Complex Object]" : typeof e == "string" ? e : typeof e == "number" || typeof e == "boolean" ? String(e) : "[Non-serializable value]";
40
47
  }
41
- }
42
- function j() {
43
- return {
44
- enabled: !1,
45
- log(e, t) {
46
- if (this.enabled)
47
- if (t === void 0)
48
- console.log(
49
- `${y.magenta("aws-sdk-vitest-mock(debug):")} ${e}`
50
- );
51
- else {
52
- const n = B(t);
53
- console.log(
54
- `${y.magenta("aws-sdk-vitest-mock(debug):")} ${e}
55
- ${n}`
56
- );
57
- }
58
- }
59
- };
60
- }
61
- function S(e) {
62
- e.enabled = !0;
63
- }
64
- function w(e) {
65
- e.enabled = !1;
66
- }
67
- function J(e) {
68
- const t = F.resolve(e), n = P(t, "utf8");
69
- return e.endsWith(".json") ? JSON.parse(n) : n;
70
- }
71
- function _(e, t = {}) {
72
- const { pageSize: n = 10, tokenKey: i = "NextToken", itemsKey: o = "Items" } = t;
48
+ }, k = (e, t) => {
49
+ const o = O.magenta("aws-sdk-vitest-mock(debug):");
50
+ if (t === void 0)
51
+ console.log(`${o} ${e}`);
52
+ else {
53
+ const s = z(t);
54
+ console.log(`${o} ${e}
55
+ ${s}`);
56
+ }
57
+ }, $ = (e = !1) => ({
58
+ enabled: e,
59
+ explicitlySet: !1,
60
+ log(t, o) {
61
+ this.enabled && k(t, o);
62
+ },
63
+ logDirect(t, o) {
64
+ k(t, o);
65
+ }
66
+ }), V = (e) => {
67
+ e.enabled = !0, e.explicitlySet = !0;
68
+ }, _ = (e) => {
69
+ e.enabled = !1, e.explicitlySet = !0;
70
+ }, U = (e) => {
71
+ const t = C.resolve(e);
72
+ try {
73
+ const o = P(t, "utf8");
74
+ if (e.endsWith(".json"))
75
+ try {
76
+ return JSON.parse(o);
77
+ } catch (s) {
78
+ const n = s instanceof Error ? s.message : String(s);
79
+ throw new Error(
80
+ `Failed to parse JSON fixture at ${t}: ${n}`
81
+ );
82
+ }
83
+ return o;
84
+ } catch (o) {
85
+ const s = o instanceof Error ? o.message : String(o);
86
+ throw new Error(`Failed to load fixture at ${t}: ${s}`);
87
+ }
88
+ }, G = (e, t = {}) => {
89
+ const { pageSize: o = 10, tokenKey: s = "NextToken", itemsKey: n = "Items" } = t;
73
90
  if (e.length === 0)
74
- return [{ [o]: [] }];
75
- const s = [];
76
- for (let r = 0; r < e.length; r += n) {
77
- const c = e.slice(r, r + n), l = r + n < e.length, u = { [o]: c };
91
+ return [{ [n]: [] }];
92
+ const c = [];
93
+ for (let r = 0; r < e.length; r += o) {
94
+ const i = e.slice(r, r + o), l = r + o < e.length, u = { [n]: i };
78
95
  if (l) {
79
- const g = u, a = c[c.length - 1];
80
- g[i] = a;
96
+ const a = u, f = i[i.length - 1];
97
+ a[s] = f;
81
98
  }
82
- s.push(u);
99
+ c.push(u);
83
100
  }
84
- return s;
85
- }
86
- function q() {
87
- return typeof process < "u" && process.versions?.node ? "node" : typeof process < "u" && process.versions?.bun ? "bun" : "browser";
88
- }
89
- function z(e) {
101
+ return c;
102
+ }, H = () => typeof process < "u" && process.versions?.node ? "node" : typeof process < "u" && process.versions?.bun ? "bun" : "browser", Q = (e) => {
90
103
  const t = typeof e == "string" ? Buffer.from(e, "utf8") : Buffer.from(e);
91
- let n = !1;
92
- return new O({
104
+ let o = !1;
105
+ return new I({
93
106
  read() {
94
- n || (this.push(t), this.push(null), n = !0);
107
+ o || (this.push(t), this.push(null), o = !0);
95
108
  }
96
109
  });
97
- }
98
- function V(e) {
110
+ }, X = (e) => {
99
111
  let t;
100
112
  return typeof e == "string" ? t = new TextEncoder().encode(e) : e instanceof Buffer ? t = new Uint8Array(e) : t = e, new ReadableStream({
101
- start(n) {
102
- n.enqueue(t), n.close();
113
+ start(o) {
114
+ o.enqueue(t), o.close();
103
115
  }
104
116
  });
117
+ }, S = (e) => {
118
+ const t = H();
119
+ return t === "node" || t === "bun" ? Q(e) : X(e);
120
+ };
121
+ let w = !1;
122
+ function ie(e) {
123
+ w = e;
105
124
  }
106
- function k(e) {
107
- const t = q();
108
- return t === "node" || t === "bun" ? z(e) : V(e);
125
+ function m(e) {
126
+ return e.explicitlySet ? e.enabled : w;
109
127
  }
110
- function $(e, t) {
111
- return Object.keys(t).every((n) => {
112
- const i = t[n], o = e[n];
113
- return i && typeof i == "object" && !Array.isArray(i) ? typeof o != "object" || o === null ? !1 : $(
114
- o,
115
- i
116
- ) : o === i;
128
+ function E(e, t) {
129
+ return Object.keys(t).every((o) => {
130
+ const s = t[o], n = e[o];
131
+ return s && typeof s == "object" && !Array.isArray(s) ? typeof n != "object" || n === null ? !1 : E(
132
+ n,
133
+ s
134
+ ) : n === s;
117
135
  });
118
136
  }
119
137
  function N(e, t) {
120
138
  if (e === t) return !0;
121
139
  if (typeof e != "object" || e === null || typeof t != "object" || t === null)
122
140
  return e === t;
123
- const n = Object.keys(e), i = Object.keys(t);
124
- return n.length !== i.length ? !1 : i.every((o) => {
125
- if (!Object.prototype.hasOwnProperty.call(e, o)) return !1;
126
- const s = e, r = t, c = s[o], l = r[o];
127
- return typeof c == "object" && c !== null && typeof l == "object" && l !== null ? N(c, l) : c === l;
141
+ const o = Object.keys(e), s = Object.keys(t);
142
+ return o.length !== s.length ? !1 : s.every((n) => {
143
+ if (!Object.prototype.hasOwnProperty.call(e, n)) return !1;
144
+ const c = e, r = t, i = c[n], l = r[n];
145
+ return typeof i == "object" && i !== null && typeof l == "object" && l !== null ? N(i, l) : i === l;
128
146
  });
129
147
  }
130
- function C(e) {
131
- return async function(t) {
132
- const n = () => this;
133
- e.debugLogger.log(
134
- `Received command: ${t.constructor.name}`,
135
- t.input
136
- );
137
- const i = e.map.get(
138
- t.constructor
139
- );
140
- if (i) {
141
- e.debugLogger.log(
142
- `Found ${i.length} mock(s) for ${t.constructor.name}`
143
- );
144
- const s = i.findIndex((r) => r.strict ? r.matcher && N(t.input, r.matcher) : !r.matcher || $(t.input, r.matcher));
145
- if (s === -1) {
146
- e.debugLogger.log(
147
- `No matching mock found for ${t.constructor.name}`,
148
- t.input
149
- );
150
- const r = i.map((l, u) => {
151
- const g = l.matcher ? JSON.stringify(l.matcher, void 0, 2) : "any input", a = l.strict ? " (strict mode)" : "";
152
- return ` Mock #${u + 1}: ${g}${a}`;
153
- }).join(`
154
- `), c = JSON.stringify(t.input, void 0, 2);
155
- throw new Error(
156
- `No matching mock found for ${t.constructor.name}.
148
+ function Y(e, t, o) {
149
+ const s = t.map((c, r) => {
150
+ const i = c.matcher ? JSON.stringify(c.matcher, void 0, 2) : "any input", l = c.strict ? " (strict mode)" : "";
151
+ return ` Mock #${r + 1}: ${i}${l}`;
152
+ }).join(`
153
+ `), n = JSON.stringify(o, void 0, 2);
154
+ return new Error(
155
+ `No matching mock found for ${e}.
157
156
 
158
- Found ${i.length} mock(s) but none matched the input.
157
+ Found ${t.length} mock(s) but none matched the input.
159
158
 
160
159
  Configured mocks:
161
- ${r}
160
+ ${s}
162
161
 
163
162
  Received input:
164
- ${c}
163
+ ${n}
165
164
 
166
165
  Tip: Enable debug mode with enableDebug() for detailed matching information.`
167
- );
168
- } else {
169
- const r = i[s];
170
- if (!r)
171
- throw new Error(`Mock at index ${s} not found`);
172
- return e.debugLogger.log(
173
- `Using mock at index ${s} for ${t.constructor.name}`
174
- ), r.once && (i.splice(s, 1), e.debugLogger.log(
175
- `Removed one-time mock for ${t.constructor.name}`
176
- )), r.handler(
177
- t.input,
178
- n()
179
- );
180
- }
181
- } else
182
- e.debugLogger.log(
183
- `No mocks configured for ${t.constructor.name}`
184
- );
185
- const o = JSON.stringify(t.input, void 0, 2);
186
- throw new Error(
187
- `No mock configured for command: ${t.constructor.name}.
166
+ );
167
+ }
168
+ function Z(e, t) {
169
+ const o = JSON.stringify(t, void 0, 2);
170
+ return new Error(
171
+ `No mock configured for command: ${e}.
188
172
 
189
173
  Received input:
190
174
  ${o}
191
175
 
192
- Did you forget to call mockClient.on(${t.constructor.name})?`
176
+ Did you forget to call mockClient.on(${e})?`
177
+ );
178
+ }
179
+ function ee(e, t) {
180
+ return e.findIndex((o) => o.strict ? o.matcher && N(t, o.matcher) : !o.matcher || E(t, o.matcher));
181
+ }
182
+ function x(e) {
183
+ return async function(t) {
184
+ const o = () => this, s = m(e.debugLogger), n = t.constructor.name;
185
+ s && e.debugLogger.logDirect(
186
+ `Received command: ${n}`,
187
+ t.input
188
+ );
189
+ const c = e.map.get(
190
+ t.constructor
191
+ );
192
+ if (!c)
193
+ throw s && e.debugLogger.logDirect(
194
+ `No mocks configured for ${n}`
195
+ ), Z(n, t.input);
196
+ s && e.debugLogger.logDirect(
197
+ `Found ${c.length} mock(s) for ${n}`
193
198
  );
199
+ const r = ee(c, t.input);
200
+ if (r === -1)
201
+ throw s && e.debugLogger.logDirect(
202
+ `No matching mock found for ${n}`,
203
+ t.input
204
+ ), Y(n, c, t.input);
205
+ const i = c[r];
206
+ if (!i)
207
+ throw new Error(`Mock at index ${r} not found`);
208
+ return s && e.debugLogger.logDirect(
209
+ `Using mock at index ${r} for ${n}`
210
+ ), i.once && (c.splice(r, 1), s && e.debugLogger.logDirect(
211
+ `Removed one-time mock for ${n}`
212
+ )), i.handler(t.input, o());
194
213
  };
195
214
  }
196
- function R(e, t, n, i = {}) {
197
- const o = (r, c, l) => {
215
+ const D = (e, t, o) => ({
216
+ reset() {
217
+ m(e.debugLogger) && e.debugLogger.logDirect(o.reset), t.mockClear();
218
+ },
219
+ restore() {
220
+ m(e.debugLogger) && e.debugLogger.logDirect(o.restore), t.mockRestore(), e.map = /* @__PURE__ */ new WeakMap();
221
+ },
222
+ calls() {
223
+ return t.mock.calls.map((s) => s[0]);
224
+ },
225
+ __rawCalls() {
226
+ return t.mock.calls;
227
+ },
228
+ enableDebug() {
229
+ V(e.debugLogger);
230
+ },
231
+ disableDebug() {
232
+ _(e.debugLogger);
233
+ }
234
+ });
235
+ function L(e, t, o, s = {}) {
236
+ const n = (r, i, l) => {
198
237
  const u = {
199
- matcher: n,
238
+ matcher: o,
200
239
  handler: r,
201
- once: c,
202
- strict: !!i.strict
203
- }, g = e.map.get(t) ?? [];
204
- if (c) {
205
- const a = g.findIndex((d) => !d.once);
206
- a === -1 ? g.push(u) : g.splice(a, 0, u), e.map.set(
240
+ once: i,
241
+ strict: !!s.strict
242
+ }, a = e.map.get(t) ?? [], f = m(e.debugLogger);
243
+ if (i) {
244
+ const g = a.findIndex((h) => !h.once);
245
+ g === -1 ? a.push(u) : a.splice(g, 0, u), e.map.set(
207
246
  t,
208
- g
209
- ), e.debugLogger.log(
247
+ a
248
+ ), f && e.debugLogger.logDirect(
210
249
  `Configured ${l}Once for ${t.name}`,
211
- n ? { matcher: n, strict: !!i.strict } : void 0
250
+ o ? { matcher: o, strict: !!s.strict } : void 0
212
251
  );
213
252
  } else {
214
- const a = g.filter(
215
- (d) => d.once || JSON.stringify(d.matcher) !== JSON.stringify(n)
253
+ const g = a.filter(
254
+ (h) => h.once || JSON.stringify(h.matcher) !== JSON.stringify(o)
216
255
  );
217
- a.push(u), e.map.set(
256
+ g.push(u), e.map.set(
218
257
  t,
219
- a
220
- ), e.debugLogger.log(
258
+ g
259
+ ), f && e.debugLogger.logDirect(
221
260
  `Configured ${l} for ${t.name}`,
222
- n ? { matcher: n, strict: !!i.strict } : void 0
261
+ o ? { matcher: o, strict: !!s.strict } : void 0
223
262
  );
224
263
  }
225
- }, s = {
264
+ }, c = {
226
265
  resolves(r) {
227
- return o(() => Promise.resolve(r), !1, "resolves"), s;
266
+ return n(() => Promise.resolve(r), !1, "resolves"), c;
228
267
  },
229
268
  rejects(r) {
230
- return o(
269
+ return n(
231
270
  () => {
232
- const c = typeof r == "string" ? new Error(r) : r;
233
- return Promise.reject(c);
271
+ const i = typeof r == "string" ? new Error(r) : r;
272
+ return Promise.reject(i);
234
273
  },
235
274
  !1,
236
275
  "rejects"
237
- ), s;
276
+ ), c;
238
277
  },
239
278
  callsFake(r) {
240
- return o(r, !1, "callsFake"), s;
279
+ return n(r, !1, "callsFake"), c;
241
280
  },
242
281
  resolvesOnce(r) {
243
- return o(() => Promise.resolve(r), !0, "resolves"), s;
282
+ return n(() => Promise.resolve(r), !0, "resolves"), c;
244
283
  },
245
284
  rejectsOnce(r) {
246
- return o(
285
+ return n(
247
286
  () => {
248
- const c = typeof r == "string" ? new Error(r) : r;
249
- return Promise.reject(c);
287
+ const i = typeof r == "string" ? new Error(r) : r;
288
+ return Promise.reject(i);
250
289
  },
251
290
  !0,
252
291
  "rejects"
253
- ), s;
292
+ ), c;
254
293
  },
255
294
  callsFakeOnce(r) {
256
- return o(r, !0, "callsFake"), s;
295
+ return n(r, !0, "callsFake"), c;
257
296
  },
258
297
  resolvesStream(r) {
259
- return o(
260
- () => Promise.resolve({ Body: k(r) }),
298
+ return n(
299
+ () => Promise.resolve({
300
+ Body: S(r)
301
+ }),
261
302
  !1,
262
303
  "resolvesStream"
263
- ), s;
304
+ ), c;
264
305
  },
265
306
  resolvesStreamOnce(r) {
266
- return o(
267
- () => Promise.resolve({ Body: k(r) }),
307
+ return n(
308
+ () => Promise.resolve({
309
+ Body: S(r)
310
+ }),
268
311
  !0,
269
312
  "resolvesStream"
270
- ), s;
313
+ ), c;
271
314
  },
272
- resolvesWithDelay(r, c) {
315
+ resolvesWithDelay(r, i) {
273
316
  const l = (u) => {
274
- setTimeout(() => u(r), c);
317
+ setTimeout(() => u(r), i);
275
318
  };
276
- return o(() => new Promise(l), !1, "resolvesWithDelay"), s;
319
+ return n(
320
+ () => new Promise(l),
321
+ !1,
322
+ "resolvesWithDelay"
323
+ ), c;
277
324
  },
278
- rejectsWithDelay(r, c) {
279
- const l = typeof r == "string" ? new Error(r) : r, u = (g, a) => {
280
- setTimeout(() => a(l), c);
325
+ rejectsWithDelay(r, i) {
326
+ const l = typeof r == "string" ? new Error(r) : r, u = (a, f) => {
327
+ setTimeout(() => f(l), i);
281
328
  };
282
- return o(() => new Promise(u), !1, "rejectsWithDelay"), s;
329
+ return n(() => new Promise(u), !1, "rejectsWithDelay"), c;
283
330
  },
284
331
  rejectsWithNoSuchKey(r) {
285
- return o(
286
- () => Promise.reject(L(r)),
332
+ return n(
333
+ () => Promise.reject(T(r)),
287
334
  !1,
288
335
  "rejectsWithNoSuchKey"
289
- ), s;
336
+ ), c;
290
337
  },
291
338
  rejectsWithNoSuchBucket(r) {
292
- return o(
293
- () => Promise.reject(D(r)),
339
+ return n(
340
+ () => Promise.reject(M(r)),
294
341
  !1,
295
342
  "rejectsWithNoSuchBucket"
296
- ), s;
343
+ ), c;
297
344
  },
298
345
  rejectsWithAccessDenied(r) {
299
- return o(
300
- () => Promise.reject(I(r)),
346
+ return n(
347
+ () => Promise.reject(K(r)),
301
348
  !1,
302
349
  "rejectsWithAccessDenied"
303
- ), s;
350
+ ), c;
304
351
  },
305
352
  rejectsWithResourceNotFound(r) {
306
- return o(
307
- () => Promise.reject(T(r)),
353
+ return n(
354
+ () => Promise.reject(A(r)),
308
355
  !1,
309
356
  "rejectsWithResourceNotFound"
310
- ), s;
357
+ ), c;
311
358
  },
312
359
  rejectsWithConditionalCheckFailed() {
313
- return o(
314
- () => Promise.reject(K()),
360
+ return n(
361
+ () => Promise.reject(B()),
315
362
  !1,
316
363
  "rejectsWithConditionalCheckFailed"
317
- ), s;
364
+ ), c;
318
365
  },
319
366
  rejectsWithThrottling() {
320
- return o(
321
- () => Promise.reject(M()),
367
+ return n(
368
+ () => Promise.reject(J()),
322
369
  !1,
323
370
  "rejectsWithThrottling"
324
- ), s;
371
+ ), c;
325
372
  },
326
373
  rejectsWithInternalServerError() {
327
- return o(
328
- () => Promise.reject(A()),
374
+ return n(
375
+ () => Promise.reject(q()),
329
376
  !1,
330
377
  "rejectsWithInternalServerError"
331
- ), s;
378
+ ), c;
332
379
  },
333
- resolvesPaginated(r, c = {}) {
334
- const l = _(r, c);
380
+ resolvesPaginated(r, i = {}) {
381
+ const l = G(r, i);
335
382
  let u = 0;
336
383
  return e.debugLogger.log(
337
384
  `Configured resolvesPaginated for ${t.name}`,
338
- { pageSize: c.pageSize, itemsCount: r.length }
339
- ), o(
340
- (g) => {
341
- const a = c.tokenKey || "NextToken", d = c.inputTokenKey || a, m = g[d];
342
- if (m != null) {
343
- const E = c.itemsKey || "Items";
344
- let b = 0;
345
- for (const x of l) {
346
- const p = x[E];
385
+ { pageSize: i.pageSize, itemsCount: r.length }
386
+ ), n(
387
+ (a) => {
388
+ const f = i.tokenKey || "NextToken", g = i.inputTokenKey || f, b = a[g];
389
+ if (b != null) {
390
+ const R = i.itemsKey || "Items";
391
+ let v = 0;
392
+ for (const F of l) {
393
+ const p = F[R];
347
394
  if (p && p.length > 0) {
348
395
  const W = p[p.length - 1];
349
- if (JSON.stringify(W) === JSON.stringify(m)) {
350
- u = b + 1;
396
+ if (JSON.stringify(W) === JSON.stringify(b)) {
397
+ u = v + 1;
351
398
  break;
352
399
  }
353
400
  }
354
- b++;
401
+ v++;
355
402
  }
356
403
  } else
357
404
  u = 0;
358
- const h = (
405
+ const y = (
359
406
  // eslint-disable-next-line security/detect-object-injection
360
407
  l[u] || // eslint-disable-next-line unicorn/prefer-at -- TypeScript target doesn't support Array.at() method
361
408
  l[l.length - 1] || l[0]
362
409
  );
363
- if (!h)
410
+ if (!y)
364
411
  throw new Error("No paginated responses available");
365
- return u = Math.min(u + 1, l.length - 1), Promise.resolve(h);
412
+ return u = Math.min(u + 1, l.length - 1), Promise.resolve(y);
366
413
  },
367
414
  !1,
368
415
  "resolvesPaginated"
369
- ), s;
416
+ ), c;
370
417
  },
371
418
  resolvesFromFile(r) {
372
419
  return e.debugLogger.log(
373
420
  `Configured resolvesFromFile for ${t.name}`,
374
421
  { filePath: r }
375
- ), o(
422
+ ), n(
376
423
  () => {
377
- const c = J(r);
378
- return Promise.resolve(c);
424
+ const i = U(r);
425
+ return Promise.resolve(i);
379
426
  },
380
427
  !1,
381
428
  "resolvesFromFile"
382
- ), s;
429
+ ), c;
383
430
  }
384
431
  };
385
- return s;
432
+ return c;
386
433
  }
387
- const ee = (e) => {
434
+ const le = (e) => {
388
435
  const t = {
389
436
  map: /* @__PURE__ */ new WeakMap(),
390
- debugLogger: j()
391
- }, n = e.prototype, i = v.spyOn(n, "send").mockImplementation(C(t));
437
+ debugLogger: $()
438
+ }, o = e.prototype, s = j.spyOn(o, "send").mockImplementation(x(t)), n = D(t, s, {
439
+ reset: "Clearing call history (mocks preserved)",
440
+ restore: "Restoring original client behavior and clearing all mocks"
441
+ });
392
442
  return {
393
443
  client: void 0,
394
- on: (s, r, c) => R(
444
+ on: (r, i, l) => L(
395
445
  t,
396
- s,
397
446
  r,
398
- c
447
+ i,
448
+ l
399
449
  ),
400
- reset: () => {
401
- t.debugLogger.log("Clearing call history (mocks preserved)"), i.mockClear();
402
- },
403
- restore: () => {
404
- t.debugLogger.log(
405
- "Restoring original client behavior and clearing all mocks"
406
- ), i.mockRestore(), t.map = /* @__PURE__ */ new WeakMap();
407
- },
408
- calls: () => i.mock.calls.map((s) => s[0]),
409
- __rawCalls: () => i.mock.calls,
410
- enableDebug: () => {
411
- S(t.debugLogger);
412
- },
413
- disableDebug: () => {
414
- w(t.debugLogger);
415
- }
450
+ ...n
416
451
  };
417
- }, te = (e) => {
452
+ }, ue = (e) => {
418
453
  const t = {
419
454
  map: /* @__PURE__ */ new WeakMap(),
420
- debugLogger: j()
421
- }, n = v.spyOn(e, "send").mockImplementation(C(t));
455
+ debugLogger: $()
456
+ }, o = j.spyOn(e, "send").mockImplementation(x(t)), s = D(t, o, {
457
+ reset: "Clearing call history (mocks preserved) for client instance",
458
+ restore: "Restoring original client behavior and clearing all mocks for client instance"
459
+ });
422
460
  return {
423
461
  client: e,
424
- on: (o, s, r) => R(
462
+ on: (c, r, i) => L(
425
463
  t,
426
- o,
427
- s,
428
- r
464
+ c,
465
+ r,
466
+ i
429
467
  ),
430
- reset: () => {
431
- t.debugLogger.log(
432
- "Clearing call history (mocks preserved) for client instance"
433
- ), n.mockClear();
434
- },
435
- restore: () => {
436
- t.debugLogger.log(
437
- "Restoring original client behavior and clearing all mocks for client instance"
438
- ), n.mockRestore(), t.map = /* @__PURE__ */ new WeakMap();
439
- },
440
- calls: () => n.mock.calls.map((o) => o[0]),
441
- __rawCalls: () => n.mock.calls,
442
- enableDebug: () => {
443
- S(t.debugLogger);
444
- },
445
- disableDebug: () => {
446
- w(t.debugLogger);
447
- }
468
+ ...s
448
469
  };
449
470
  };
450
471
  export {
451
- ne as matchers,
452
- ee as mockClient,
453
- te as mockClientInstance
472
+ ge as matchers,
473
+ le as mockClient,
474
+ ue as mockClientInstance,
475
+ ie as setGlobalDebug
454
476
  };
@@ -3,6 +3,30 @@ import { HttpHandlerOptions, MetadataBearer } from '@smithy/types';
3
3
  import { Mock } from 'vitest';
4
4
  import { PaginatorOptions } from './utils/paginator-helpers.js';
5
5
  import { StreamInput } from './utils/stream-helpers.js';
6
+ type DeepPartial<T> = T extends (...args: unknown[]) => unknown ? T : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends object ? {
7
+ [K in keyof T]?: DeepPartial<T[K]>;
8
+ } : T;
9
+ /**
10
+ * Set global debug mode for all mocks.
11
+ * When enabled, all mocks will log debug information unless explicitly disabled at the mock level.
12
+ *
13
+ * @param enabled - Whether to enable debug logging globally
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { setGlobalDebug, mockClient } from 'aws-sdk-vitest-mock';
18
+ * import { S3Client } from '@aws-sdk/client-s3';
19
+ *
20
+ * // Enable debug for all mocks
21
+ * setGlobalDebug(true);
22
+ *
23
+ * const s3Mock = mockClient(S3Client); // Automatically has debug enabled
24
+ *
25
+ * // Disable debug for a specific mock
26
+ * s3Mock.disableDebug(); // This mock won't log, but others will
27
+ * ```
28
+ */
29
+ export declare function setGlobalDebug(enabled: boolean): void;
6
30
  export type StructuralCommand<TInput extends object, TOutput extends MetadataBearer> = SmithyCommand<TInput, TOutput, any, any, any> | {
7
31
  readonly input: TInput;
8
32
  readonly __awsSdkVitestMockOutput?: TOutput;
@@ -19,7 +43,7 @@ export type AwsSdkCommand = StructuralCommand<object, MetadataBearer>;
19
43
  export type ClientConstructor<TClient extends AnyClient> = (abstract new (...args: unknown[]) => TClient) | {
20
44
  prototype: TClient;
21
45
  };
22
- type CommandHandler<TInput extends object = object, TOutput extends MetadataBearer = MetadataBearer, TClient extends AnyClient = AnyClient> = (input: TInput, clientInstance: TClient | undefined) => Promise<Partial<TOutput>>;
46
+ type CommandHandler<TInput extends object = object, TOutput extends MetadataBearer = MetadataBearer, TClient extends AnyClient = AnyClient> = (input: TInput, clientInstance: TClient | undefined) => Promise<DeepPartial<TOutput>>;
23
47
  interface MockOptions {
24
48
  strict?: boolean;
25
49
  }
@@ -177,7 +201,7 @@ export interface AwsCommandStub<TInput extends object, TOutput extends MetadataB
177
201
  * s3Mock.on(GetObjectCommand).resolves({ Body: 'file contents' });
178
202
  * ```
179
203
  */
180
- resolves: (output: Partial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
204
+ resolves: (output: DeepPartial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
181
205
  /**
182
206
  * Set a permanent mock rejection that will be used after all one-time handlers are consumed.
183
207
  *
@@ -217,7 +241,7 @@ export interface AwsCommandStub<TInput extends object, TOutput extends MetadataB
217
241
  * .resolvesOnce({ Body: 'second call' });
218
242
  * ```
219
243
  */
220
- resolvesOnce: (output: Partial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
244
+ resolvesOnce: (output: DeepPartial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
221
245
  /**
222
246
  * Add a one-time mock rejection that will be consumed in order.
223
247
  *
@@ -284,7 +308,7 @@ export interface AwsCommandStub<TInput extends object, TOutput extends MetadataB
284
308
  * s3Mock.on(GetObjectCommand).resolvesWithDelay({ Body: 'data' }, 1000);
285
309
  * ```
286
310
  */
287
- resolvesWithDelay: (output: Partial<TOutput>, delayMs: number) => AwsCommandStub<TInput, TOutput, TClient>;
311
+ resolvesWithDelay: (output: DeepPartial<TOutput>, delayMs: number) => AwsCommandStub<TInput, TOutput, TClient>;
288
312
  /**
289
313
  * Set a permanent mock rejection with a delay in milliseconds.
290
314
  *
@@ -1,7 +1,9 @@
1
1
  export interface DebugLogger {
2
2
  enabled: boolean;
3
+ explicitlySet: boolean;
3
4
  log: (message: string, data?: unknown) => void;
5
+ logDirect: (message: string, data?: unknown) => void;
4
6
  }
5
- export declare function createDebugLogger(): DebugLogger;
6
- export declare function enableDebug(logger: DebugLogger): void;
7
- export declare function disableDebug(logger: DebugLogger): void;
7
+ export declare const createDebugLogger: (initialEnabled?: boolean) => DebugLogger;
8
+ export declare const enableDebug: (logger: DebugLogger) => void;
9
+ export declare const disableDebug: (logger: DebugLogger) => void;
@@ -1 +1 @@
1
- export declare function loadFixture(filePath: string): unknown;
1
+ export declare const loadFixture: (filePath: string) => unknown;
@@ -95,4 +95,4 @@ export interface PaginatedResponse<T = unknown> {
95
95
  * // ]
96
96
  * ```
97
97
  */
98
- export declare function createPaginatedResponses<T>(items: T[], options?: PaginatorOptions): PaginatedResponse<T>[];
98
+ export declare const createPaginatedResponses: <T>(items: T[], options?: PaginatorOptions) => PaginatedResponse<T>[];
@@ -3,4 +3,4 @@ export type StreamInput = string | Buffer | Uint8Array;
3
3
  /**
4
4
  * Creates an appropriate stream for the current environment
5
5
  */
6
- export declare function createStream(data: StreamInput): Readable | ReadableStream<Uint8Array>;
6
+ export declare const createStream: (data: StreamInput) => Readable | ReadableStream<Uint8Array>;
@@ -1,4 +1,4 @@
1
- "use strict";const e={red:t=>`\x1B[31m${t}\x1B[39m`,green:t=>`\x1B[32m${t}\x1B[39m`,yellow:t=>`\x1B[33m${t}\x1B[39m`,blue:t=>`\x1B[34m${t}\x1B[39m`,magenta:t=>`\x1B[35m${t}\x1B[39m`,cyan:t=>`\x1B[36m${t}\x1B[39m`,gray:t=>`\x1B[90m${t}\x1B[39m`,bold:t=>`\x1B[1m${t}\x1B[22m`},$=t=>{const c=t.__rawCalls();return Array.isArray(c)?c.filter(a=>Array.isArray(a)&&a.length>0):[]},f={toHaveReceivedCommand(t,c){const a=$(t),r=a.some(o=>o[0]instanceof c),m=c.name;return{pass:r,message:()=>{if(r)return`Expected AWS SDK mock not to have received command ${e.red(m)}`;const o=a.map(n=>n[0].constructor?.name??"Unknown");return o.length===0?`Expected AWS SDK mock to have received command ${e.red(m)}, but ${e.gray("no commands were received")}`:`Expected AWS SDK mock to have received command ${e.red(m)}, but received: ${e.yellow(o.join(", "))}`}}},toHaveReceivedCommandTimes(t,c,a){const r=$(t).filter(n=>n[0]instanceof c),m=r.length===a,o=c.name;return{pass:m,message:()=>m?`Expected AWS SDK mock not to have received command ${o} ${a} times`:`Expected AWS SDK mock to have received command ${o} ${a} times, but received ${r.length} times`}},toHaveReceivedCommandWith(t,c,a){const r=$(t).filter(n=>n[0]instanceof c),m=r.some(n=>this.equals(n[0].input,a)),o=c.name;return{pass:m,message:()=>{if(m){const s=e.red(o),u=e.cyan(JSON.stringify(a,void 0,2));return`Expected AWS SDK mock not to have received command ${s} with input:
1
+ "use strict";const e={red:t=>`\x1B[31m${t}\x1B[39m`,green:t=>`\x1B[32m${t}\x1B[39m`,yellow:t=>`\x1B[33m${t}\x1B[39m`,blue:t=>`\x1B[34m${t}\x1B[39m`,magenta:t=>`\x1B[35m${t}\x1B[39m`,cyan:t=>`\x1B[36m${t}\x1B[39m`,gray:t=>`\x1B[90m${t}\x1B[39m`,bold:t=>`\x1B[1m${t}\x1B[22m`},$=t=>{const c=t.__rawCalls();return Array.isArray(c)?c.filter(a=>Array.isArray(a)&&a.length>0):[]},f={toHaveReceivedCommand(t,c){const a=$(t),r=a.some(([o])=>o instanceof c),m=c.name;return{pass:r,message:()=>{if(r)return`Expected AWS SDK mock not to have received command ${e.red(m)}`;const o=a.map(([n])=>n.constructor?.name??"Unknown");return o.length===0?`Expected AWS SDK mock to have received command ${e.red(m)}, but ${e.gray("no commands were received")}`:`Expected AWS SDK mock to have received command ${e.red(m)}, but received: ${e.yellow(o.join(", "))}`}}},toHaveReceivedCommandTimes(t,c,a){const r=$(t).filter(n=>n[0]instanceof c),m=r.length===a,o=c.name;return{pass:m,message:()=>m?`Expected AWS SDK mock not to have received command ${o} ${a} times`:`Expected AWS SDK mock to have received command ${o} ${a} times, but received ${r.length} times`}},toHaveReceivedCommandWith(t,c,a){const r=$(t).filter(n=>n[0]instanceof c),m=r.some(n=>this.equals(n[0].input,a)),o=c.name;return{pass:m,message:()=>{if(m){const s=e.red(o),u=e.cyan(JSON.stringify(a,void 0,2));return`Expected AWS SDK mock not to have received command ${s} with input:
2
2
  ${u}`}if(r.length===0){const s=e.red(o),u=e.cyan(JSON.stringify(a,void 0,2)),i=e.gray(`${o} was never called`);return`Expected AWS SDK mock to have received command ${s} with input:
3
3
  ${u}
4
4
 
@@ -31,13 +31,13 @@ const e = {
31
31
  * ```
32
32
  */
33
33
  toHaveReceivedCommand(t, c) {
34
- const a = $(t), r = a.some((o) => o[0] instanceof c), m = c.name;
34
+ const a = $(t), r = a.some(([o]) => o instanceof c), m = c.name;
35
35
  return {
36
36
  pass: r,
37
37
  message: () => {
38
38
  if (r)
39
39
  return `Expected AWS SDK mock not to have received command ${e.red(m)}`;
40
- const o = a.map((n) => n[0].constructor?.name ?? "Unknown");
40
+ const o = a.map(([n]) => n.constructor?.name ?? "Unknown");
41
41
  return o.length === 0 ? `Expected AWS SDK mock to have received command ${e.red(m)}, but ${e.gray("no commands were received")}` : `Expected AWS SDK mock to have received command ${e.red(m)}, but received: ${e.yellow(o.join(", "))}`;
42
42
  }
43
43
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-sdk-vitest-mock",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -55,6 +55,7 @@
55
55
  "@swc-node/register": "1.11.1",
56
56
  "@swc/core": "1.15.3",
57
57
  "@swc/helpers": "0.5.17",
58
+ "@types/eslint-plugin-security": "3.0.0",
58
59
  "@types/node": "24.10.3",
59
60
  "@typescript-eslint/parser": "8.49.0",
60
61
  "@vitest/coverage-v8": "4.0.15",
@@ -66,6 +67,7 @@
66
67
  "eslint-plugin-sonarjs": "3.0.5",
67
68
  "eslint-plugin-unicorn": "62.0.0",
68
69
  "husky": "9.1.7",
70
+ "jiti": "2.6.1",
69
71
  "lint-staged": "16.2.7",
70
72
  "nx": "22.2.2",
71
73
  "prettier": "3.7.4",
package/vitest-setup.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";const e=require("vitest"),t=require("./matchers-Dkkl4vtx.cjs");e.expect.extend(t.matchers);
1
+ "use strict";const e=require("vitest"),t=require("./matchers-CNhdB_9q.cjs");e.expect.extend(t.matchers);
package/vitest-setup.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { expect as m } from "vitest";
2
- import { m as e } from "./matchers-ClGOsQx8.js";
2
+ import { m as e } from "./matchers-DmTtFk31.js";
3
3
  m.extend(e);