@vitrina/api 11.47.1 → 11.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -10
- package/dist/index.d.ts +62 -10
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":["createHmac","timingSafeEqual","createOpenApiClient","randomUUID"],"mappings":";;;;;;;;;;;;AA2GO,IAAM,WAAA,GAAoC;AAAA,EAC/C,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,iBAAA;AAAA,EACA,0BAAA;AAAA,EACA,sBAAA;AAAA,EACA,wBAAA;AAAA,EACA,0BAAA;AAAA,EACA,qBAAA;AAAA,EACA,mBAAA;AAAA,EACA,wBAAA;AAAA,EACA,gBAAA;AAAA,EACA,cAAA;AAAA,EACA,MAAA;AAAA,EACA,gBAAA;AAAA,EACA,kCAAA;AAAA,EACA,+BAAA;AAAA,EACA,iCAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,6BAAA;AAAA,EACA,iCAAA;AAAA,EACA,wBAAA;AAAA,EACA,wBAAA;AAAA,EACA,yBAAA;AAAA,EACA,iCAAA;AAAA,EACA,2BAAA;AAAA,EACA,8BAAA;AAAA,EACA,sBAAA;AAAA,EACA,6BAAA;AAAA,EACA,2BAAA;AAAA,EACA,0BAAA;AAAA,EACA,2BAAA;AAAA,EACA,6BAAA;AAAA,EACA,oBAAA;AAAA,EACA,mBAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA,wBAAA;AAAA,EACA,qBAAA;AAAA,EACA,wBAAA;AAAA,EACA,qBAAA;AAAA,EACA,mBAAA;AAAA,EACA,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA,mBAAA;AAAA,EACA,iBAAA;AAAA,EACA,uBAAA;AAAA,EACA,yBAAA;AAAA,EACA,0BAAA;AAAA,EACA,0BAAA;AAAA,EACA,sBAAA;AAAA,EACA,uBAAA;AAAA,EACA,oCAAA;AAAA,EACA,2BAAA;AAAA,EACA,6BAAA;AAAA,EACA,iCAAA;AAAA,EACA,+BAAA;AAAA,EACA,gCAAA;AAAA,EACA,qCAAA;AAAA,EACA,6CAAA;AAAA,EACA,6CAAA;AAAA,EACA,qBAAA;AAAA,EACA,2BAAA;AAAA,EACA,2BAAA;AAAA,EACA,yBAAA;AAAA,EACA,2BAAA;AAAA,EACA,8BAAA;AAAA,EACA,kBAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA,aAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA;AAAA,EACA,oBAAA;AAAA,EACA,6BAAA;AAAA,EACA,uBAAA;AAAA,EACA,qBAAA;AAAA,EACA,iBAAA;AAAA,EACA,oBAAA;AAAA,EACA,sBAAA;AAAA,EACA,iBAAA;AAAA,EACA,eAAA;AAAA,EACA,uBAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,yBAAA;AAAA,EACA,yBAAA;AAAA,EACA,iCAAA;AAAA,EACA,0BAAA;AAAA,EACA,kBAAA;AAAA,EACA,uBAAA;AAAA,EACA;AACF;;;AC5JO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAqBzC,WAAA,CACE,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EAC5B;AACA,IAAA,KAAA,CAAM,KAAK,OAAA,IAAW,CAAA,kBAAA,EAAqB,KAAK,IAAI,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,CAAG,CAAA;AACvE,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpB,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,YAAA;AACxB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,gBAAgB,OAAA,CAAQ,aAAA;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,aAAA,GAAyB;AAC3B,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,cAAA,GAA0B;AAC5B,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,UAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,qBAAA,GAAiC;AACnC,IAAA,OAAO,KAAK,IAAA,KAAS,0BAAA;AAAA,EACvB;AACF;AAQO,IAAM,oBAAA,GAAN,cAAmC,eAAA,CAAgB;AAAA,EAOxD,WAAA,CACE,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EAC5B;AACA,IAAA,KAAA,CAAM,MAAA,EAAQ,MAAM,OAAO,CAAA;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AACZ,IAAA,MAAM,cAAA,GACJ,KAAK,OAAA,EACJ,OAAA;AACH,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,cAAA,IAAkB,EAAC;AAClD,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA,IAAQ,KAAK,OAAA,CAAQ,CAAC,GAAG,IAAA,IAAQ,EAAA;AAAA,EACpD;AAAA;AAAA,EAGA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,IAAA,KAAS,kBAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,SAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,IAAA,KAAS,kBAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,gBAAA,GAA6B;AAC/B,IAAA,OAAO,KAAK,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AAAA,EACvC;AACF;AAEA,SAAS,WAAA,CAAY,SAAkB,MAAA,EAA8B;AACnE,EAAA,IAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC1C,IAAA,MAAM,aAAA,GAAgB,OAAA;AACtB,IAAA,MAAM,SAAA,GACJ,cAAc,KAAA,IAAS,OAAO,cAAc,KAAA,KAAU,QAAA,GACjD,cAAc,KAAA,GACd,OAAA;AACP,IAAA,IACE,OAAO,SAAA,CAAU,IAAA,KAAS,YAC1B,OAAO,SAAA,CAAU,YAAY,QAAA,EAC7B;AACA,MAAA,OAAO,SAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,QAAQ,MAAM,CAAA,mCAAA;AAAA,GACzB;AACF;AAGO,SAAS,UAAA,CACd,MAAA,EACA,OAAA,EACA,OAAA,GAA2B,EAAC,EACX;AACjB,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,OAAA,EAAS,MAAM,CAAA;AACxC,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,kBAAA,IAAsB,IAAA,CAAK,SAAS,kBAAA,EAAoB;AACxE,IAAA,OAAO,IAAI,oBAAA,CAAqB,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AAAA,EACvD;AACA,EAAA,OAAO,IAAI,eAAA,CAAgB,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AAClD;AAgBO,SAAS,MAAA,CACd,MAAA,EACA,OAAA,GAA2B,EAAC,EACzB;AACH,EAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,IAAA,MAAM,WAAW,MAAA,CAAO,QAAA,CAAS,MAAA,EAAQ,MAAA,CAAO,OAAO,OAAO,CAAA;AAAA,EAChE;AACA,EAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAW;AAC7B,IAAA,MAAM,IAAI,eAAA;AAAA,MACR,OAAO,QAAA,CAAS,MAAA;AAAA,MAChB;AAAA,QACE,IAAA,EAAM,gBAAA;AAAA,QACN,OAAA,EAAS;AAAA,OACX;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;ACnJA,SAAS,qBACP,MAAA,EAC0C;AAC1C,EAAA,MAAM,QAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA,EAAG;AACpC,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC3B,IAAA,IAAI,OAAO,EAAA,EAAI;AACf,IAAA,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,IAAA,EAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,CAAC,EAAE,IAAA,EAAK;AAAA,EAC5D;AACA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA;AAChC,EAAA,IAAI,CAAC,OAAO,QAAA,CAAS,SAAS,KAAK,CAAC,KAAA,CAAM,IAAI,OAAO,IAAA;AACrD,EAAA,OAAO,EAAE,SAAA,EAAW,EAAA,EAAI,KAAA,CAAM,EAAA,EAAG;AACnC;AASO,SAAS,uBACd,MAAA,EAKc;AACd,EAAA,MAAM,MAAA,GAAS,oBAAA,CAAqB,MAAA,CAAO,eAAe,CAAA;AAC1D,EAAA,IAAI,CAAC,MAAA,EAAQ,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,kBAAA,EAAmB;AAE5D,EAAA,MAAM,IAAA,GACJ,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GACtB,OAAO,OAAA,GACP,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA;AACpC,EAAA,MAAM,QAAA,GAAWA,iBAAA,CAAW,QAAA,EAAU,MAAA,CAAO,MAAM,CAAA,CAChD,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,SAAS,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE,CAAA,CACpC,OAAO,KAAK,CAAA;AAEf,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,IAAI,MAAM,CAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,MAAM,CAAA;AACzC,EAAA,MAAM,mBACJ,KAAA,CAAM,MAAA,KAAW,KAAK,MAAA,IAAUC,sBAAA,CAAgB,OAAO,IAAI,CAAA;AAC7D,EAAA,IAAI,CAAC,gBAAA,EAAkB,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,QAAQ,eAAA,EAAgB;AAEnE,EAAA,MAAM,SAAA,GAAY,OAAO,gBAAA,IAAoB,GAAA;AAC7C,EAAA,MAAM,GAAA,GAAM,OAAO,OAAA,IAAW,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,KAAQ,GAAI,CAAA;AAC1D,EAAA,IAAI,KAAK,GAAA,CAAI,GAAA,GAAM,MAAA,CAAO,SAAS,IAAI,SAAA,EAAW;AAChD,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,OAAA,EAAQ;AAAA,EACtC;AACA,EAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AACpB;AAEO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA,EAG/C,YAAY,MAAA,EAAuD;AACjE,IAAA,KAAA,CAAM,CAAA,uCAAA,EAA0C,MAAM,CAAA,CAAE,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AACF;AAQO,SAAS,uBACd,MAAA,EAK6B;AAC7B,EAAA,MAAM,MAAA,GAAS,uBAAuB,MAAM,CAAA;AAC5C,EAAA,IAAI,CAAC,MAAA,CAAO,EAAA,QAAU,IAAI,qBAAA,CAAsB,OAAO,MAAM,CAAA;AAC7D,EAAA,MAAM,IAAA,GACJ,OAAO,MAAA,CAAO,OAAA,KAAY,QAAA,GACtB,OAAO,OAAA,GACP,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA;AACpC,EAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AACxB;;;AC7IO,IAAM,6BAAA,GAAN,cAA4C,KAAA,CAAM;AAAA,EAGvD,YAAY,KAAA,EAA6B;AACvC,IAAA,KAAA;AAAA,MACE,MAAM,YAAA,GACF,CAAA,MAAA,EAAS,KAAA,CAAM,EAAE,KAAK,KAAA,CAAM,IAAI,CAAA,+CAAA,EAA6C,KAAA,CAAM,YAAY,CAAA,CAAA,GAC/F,CAAA,MAAA,EAAS,MAAM,EAAE,CAAA,EAAA,EAAK,MAAM,IAAI,CAAA,yBAAA;AAAA,KACtC;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,+BAAA;AACZ,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AAAA,EACf;AACF;AAwBO,SAAS,sBAAsB,IAAA,EAAwB;AAK5D,EAAA,eAAe,cACb,KAAA,EACoB;AACpB,IAAA,IAAI,CAAC,KAAA,CAAM,QAAA,CAAS,KAAK,MAAM,IAAI,8BAA8B,KAAK,CAAA;AACtE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,QAAA,CAAS,GAAA,EAAK,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,CAAA;AAC1E,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAM,IAAI,IAAA,EAAK;AAAA,IACxB,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,GAAO,MAAA;AAAA,IACT;AACA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,UAAA,CAAW,GAAA,CAAI,MAAA,EAAQ,IAAA,EAAM;AAAA,QACjC,UAAA,EAAY,MAAM,QAAA,CAAS,GAAA;AAAA,QAC3B,aAAA,EAAe;AAAA,OAChB,CAAA;AAAA,IACH;AACA,IAAA,OAAQ,IAAA,EAA8B,IAAA;AAAA,EACxC;AAEA,EAAA,OAAO;AAAA;AAAA,IAEL,eAAA,EAAiB,CACf,MAAA,KAKG,sBAAA,CAAuB,MAAM,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAelC,eACE,MAAA,EAKqB;AACrB,MAAA,MAAM,QAAA,GAAW,uBAA8B,MAAM,CAAA;AACrD,MAAA,OAAO,MAAA,CAAO,OAAO,QAAA,EAAU;AAAA,QAC7B,KAAA,EAAO,MAAyB,aAAA,CAAyB,QAAQ;AAAA,OAClE,CAAA;AAAA,IACH;AAAA,GACF;AACF;;;AC5DO,SAAS,aAAa,IAAA,EAA0C;AACrE,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA,EAAQ;AAC9B,IAAA,MAAM,IAAI,UAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA;AACvC,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,GAAI,aAAa,EAAE,aAAA,EAAe,UAAU,UAAU,CAAA,CAAA,KAAO,EAAC;AAAA,IAC9D,GAAG,IAAA,CAAK;AAAA,GACV;AACA,EAAA,MAAM,SAAA,GAAY,KAAK,KAAA,IAAS,KAAA;AAEhC,EAAA,MAAM,OAAOC,oCAAA,CAA2B;AAAA,IACtC,SAAS,IAAA,CAAK,OAAA;AAAA,IACd,OAAA;AAAA,IACA,KAAA,EAAO;AAAA,GACR,CAAA;AAED,EAAA,MAAM,SAAS,qBAAA,CAAsB,EAAE,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAElE,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,EAAE,QAAQ,CAAA;AACvC;;;ACnCA,SAAS,aAAa,IAAA,EAAsD;AAC1E,EAAA,MAAM,MAAA,GAAS,IAAA,EAAM,UAAA,EAAY,UAAA,IAAc,MAAM,UAAA,IAAc,IAAA;AACnE,EAAA,OAAO,MAAA,IAAU,MAAA;AACnB;AAyBA,gBAAuB,SACrB,SAAA,EACkC;AAClC,EAAA,IAAI,MAAA;AACJ,EAAA,WAAS;AAEP,IAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,MAAM,CAAA;AACrC,IAAA,IAAI,MAAA,CAAO,UAAU,MAAA,EAAW;AAC9B,MAAA,MAAM,UAAA,CAAW,MAAA,CAAO,QAAA,CAAS,MAAA,EAAQ,OAAO,KAAK,CAAA;AAAA,IACvD;AACA,IAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,EAAG;AACxC,IAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,IAAA,EAAM,MAAM,IAAA;AACpC,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAA,GAAS,IAAA;AAAA,EACX;AACF;AAKA,eAAsB,YACpB,SAAA,EACc;AACd,EAAA,MAAM,QAAa,EAAC;AAEpB,EAAA,WAAA,MAAiB,QAAQ,QAAA,CAAS,SAAS,CAAA,EAAG,KAAA,CAAM,KAAK,IAAI,CAAA;AAC7D,EAAA,OAAO,KAAA;AACT;AC9EA,IAAM,WAAA,GAAc,wBAAA;AAab,SAAS,eAAe,GAAA,EAAmC;AAChE,EAAA,IAAI,CAAC,WAAA,CAAY,IAAA,CAAK,GAAG,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,gEAAA,EAAmE,IAAA,CAAK,SAAA,CAAU,GAAG,CAAC,CAAA;AAAA,KACxF;AAAA,EACF;AACA,EAAA,OAAO,EAAE,mBAAmB,GAAA,EAAI;AAClC;AASO,SAAS,uBAAuB,MAAA,EAAyB;AAC9D,EAAA,MAAM,EAAA,GAAKC,iBAAA,EAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA;AACxC,EAAA,OAAO,MAAA,GAAS,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,EAAA;AACtC","file":"index.cjs","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE'\n | 'WORKSPACE_MEMBER_INACTIVE'\n | 'DEVICE_AGENT_ALREADY_PAIRED'\n | 'DEVICE_AGENT_SELF_REQUIRES_USER'\n | 'DEVICE_AGENT_KIND_NOT_ALLOWED'\n | 'DEVICE_TASK_STATUS_NOT_ALLOWED'\n | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT'\n | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE'\n | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED'\n | 'REPLY_PACING_LOCKED'\n | 'SANDBOX_CHANNEL_FORBIDDEN'\n | 'SANDBOX_BILLING_FORBIDDEN'\n | 'SANDBOX_NOT_PROVISIONED'\n | 'SANDBOX_RESET_UNAVAILABLE'\n | 'CONNECTED_APP_SENSITIVE_DATA'\n | 'handoff_mismatch'\n | 'sync_in_flight'\n | 'disconnect_refused'\n | 'start_again'\n | 'reconnect'\n | 'not_selecting'\n | 'selection_expired'\n | 'selection_conflict'\n | 'account_connected_elsewhere'\n | 'candidate_unavailable'\n | 'integration_managed'\n | 'stage_not_found'\n | 'stage_not_writable'\n | 'stages_write_blocked'\n | 'invalid_outcome'\n | 'not_connected'\n | 'partner_token_invalid'\n | 'OUTBOUND_BLOCKED'\n | 'OUTBOUND_WARNING'\n | 'dpa_acceptance_required'\n | 'EMAIL_SENDER_UNRESOLVED'\n | 'CAMPAIGN_FROM_CONNECTED_MAILBOX'\n | 'BANK_FEED_NOT_CONFIGURED'\n | 'ADDON_NOT_PRICED'\n | 'MIRROR_LINES_UNMAPPED'\n | 'MONTH_OWNED_BY_VENDOR';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE',\n 'WORKSPACE_MEMBER_INACTIVE',\n 'DEVICE_AGENT_ALREADY_PAIRED',\n 'DEVICE_AGENT_SELF_REQUIRES_USER',\n 'DEVICE_AGENT_KIND_NOT_ALLOWED',\n 'DEVICE_TASK_STATUS_NOT_ALLOWED',\n 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT',\n 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE',\n 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED',\n 'REPLY_PACING_LOCKED',\n 'SANDBOX_CHANNEL_FORBIDDEN',\n 'SANDBOX_BILLING_FORBIDDEN',\n 'SANDBOX_NOT_PROVISIONED',\n 'SANDBOX_RESET_UNAVAILABLE',\n 'CONNECTED_APP_SENSITIVE_DATA',\n 'handoff_mismatch',\n 'sync_in_flight',\n 'disconnect_refused',\n 'start_again',\n 'reconnect',\n 'not_selecting',\n 'selection_expired',\n 'selection_conflict',\n 'account_connected_elsewhere',\n 'candidate_unavailable',\n 'integration_managed',\n 'stage_not_found',\n 'stage_not_writable',\n 'stages_write_blocked',\n 'invalid_outcome',\n 'not_connected',\n 'partner_token_invalid',\n 'OUTBOUND_BLOCKED',\n 'OUTBOUND_WARNING',\n 'dpa_acceptance_required',\n 'EMAIL_SENDER_UNRESOLVED',\n 'CAMPAIGN_FROM_CONNECTED_MAILBOX',\n 'BANK_FEED_NOT_CONFIGURED',\n 'ADDON_NOT_PRICED',\n 'MIRROR_LINES_UNMAPPED',\n 'MONTH_OWNED_BY_VENDOR',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":["createHmac","timingSafeEqual","createOpenApiClient","randomUUID"],"mappings":"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","file":"index.cjs","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE'\n | 'WORKSPACE_MEMBER_INACTIVE'\n | 'DEVICE_AGENT_ALREADY_PAIRED'\n | 'DEVICE_AGENT_SELF_REQUIRES_USER'\n | 'DEVICE_AGENT_KIND_NOT_ALLOWED'\n | 'DEVICE_TASK_STATUS_NOT_ALLOWED'\n | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT'\n | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE'\n | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED'\n | 'REPLY_PACING_LOCKED'\n | 'SANDBOX_CHANNEL_FORBIDDEN'\n | 'SANDBOX_BILLING_FORBIDDEN'\n | 'SANDBOX_NOT_PROVISIONED'\n | 'SANDBOX_RESET_UNAVAILABLE'\n | 'CONNECTED_APP_SENSITIVE_DATA'\n | 'handoff_mismatch'\n | 'sync_in_flight'\n | 'disconnect_refused'\n | 'start_again'\n | 'reconnect'\n | 'not_selecting'\n | 'selection_expired'\n | 'selection_conflict'\n | 'account_connected_elsewhere'\n | 'candidate_unavailable'\n | 'integration_managed'\n | 'stage_not_found'\n | 'stage_not_writable'\n | 'stages_write_blocked'\n | 'invalid_outcome'\n | 'not_connected'\n | 'partner_token_invalid'\n | 'OUTBOUND_BLOCKED'\n | 'OUTBOUND_WARNING'\n | 'dpa_acceptance_required'\n | 'EMAIL_SENDER_UNRESOLVED'\n | 'CAMPAIGN_FROM_CONNECTED_MAILBOX'\n | 'BANK_FEED_NOT_CONFIGURED'\n | 'ADDON_NOT_PRICED'\n | 'ADDON_NOT_OFFERED'\n | 'MIRROR_LINES_UNMAPPED'\n | 'MONTH_OWNED_BY_VENDOR';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE',\n 'WORKSPACE_MEMBER_INACTIVE',\n 'DEVICE_AGENT_ALREADY_PAIRED',\n 'DEVICE_AGENT_SELF_REQUIRES_USER',\n 'DEVICE_AGENT_KIND_NOT_ALLOWED',\n 'DEVICE_TASK_STATUS_NOT_ALLOWED',\n 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT',\n 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE',\n 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED',\n 'REPLY_PACING_LOCKED',\n 'SANDBOX_CHANNEL_FORBIDDEN',\n 'SANDBOX_BILLING_FORBIDDEN',\n 'SANDBOX_NOT_PROVISIONED',\n 'SANDBOX_RESET_UNAVAILABLE',\n 'CONNECTED_APP_SENSITIVE_DATA',\n 'handoff_mismatch',\n 'sync_in_flight',\n 'disconnect_refused',\n 'start_again',\n 'reconnect',\n 'not_selecting',\n 'selection_expired',\n 'selection_conflict',\n 'account_connected_elsewhere',\n 'candidate_unavailable',\n 'integration_managed',\n 'stage_not_found',\n 'stage_not_writable',\n 'stages_write_blocked',\n 'invalid_outcome',\n 'not_connected',\n 'partner_token_invalid',\n 'OUTBOUND_BLOCKED',\n 'OUTBOUND_WARNING',\n 'dpa_acceptance_required',\n 'EMAIL_SENDER_UNRESOLVED',\n 'CAMPAIGN_FROM_CONNECTED_MAILBOX',\n 'BANK_FEED_NOT_CONFIGURED',\n 'ADDON_NOT_PRICED',\n 'ADDON_NOT_OFFERED',\n 'MIRROR_LINES_UNMAPPED',\n 'MONTH_OWNED_BY_VENDOR',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -13919,7 +13919,7 @@ interface paths {
|
|
|
13919
13919
|
};
|
|
13920
13920
|
/**
|
|
13921
13921
|
* List the add-on catalog, this workspace's entitlements, and live usage
|
|
13922
|
-
* @description The Complementos hub read. `catalog` is the typed feature catalog (prices are PR-reviewed code, never a runtime SKU table); `entitlements` is one row per add-on this workspace has ever held; `usage` carries the live meter for each metered add-on over the current **Santiago** month — `usage.tasador` is `{ used, free_allowance, period }`, where `used` is aggregated on read from `usage_event` (never a counter) and `free_allowance` reflects any per-workspace override, so it is the same pair of numbers the access gate decides on. Each entitlement carries `billed_by`: `vitrina` when Vitrina invoices the add-on, any other value when another party covers it (Vitrina charges nothing, so a client shows it as included rather than at the catalog price). A row may also carry a time-boxed waiver: `waiver_until` is the last free calendar day (America/Santiago) and `waiver_active` says whether it covers the add-on right now; once it lapses the add-on bills normally with no further action.
|
|
13922
|
+
* @description The Complementos hub read. `catalog` is the typed feature catalog (prices are PR-reviewed code, never a runtime SKU table); `entitlements` is one row per add-on this workspace has ever held; `usage` carries the live meter for each metered add-on over the current **Santiago** month — `usage.tasador` is `{ used, free_allowance, period }`, where `used` is aggregated on read from `usage_event` (never a counter) and `free_allowance` reflects any per-workspace override, so it is the same pair of numbers the access gate decides on. Each entitlement carries `billed_by`: `vitrina` when Vitrina invoices the add-on, any other value when another party covers it (Vitrina charges nothing, so a client shows it as included rather than at the catalog price). A row may also carry a time-boxed waiver: `waiver_until` is the last free calendar day (America/Santiago) and `waiver_active` says whether it covers the add-on right now; once it lapses the add-on bills normally with no further action. `offers` says, per add-on, whether THIS workspace can buy it (`available`) and if not why (`sandbox`: a demonstration workspace; `vertical`: not sold to this type of business) — the same rule the activate route enforces with a 409 `ADDON_NOT_OFFERED`. `activators` names the active owners and admins a member without `tenant:write` can ask to activate one.
|
|
13923
13923
|
*/
|
|
13924
13924
|
get: {
|
|
13925
13925
|
parameters: {
|
|
@@ -13976,7 +13976,23 @@ interface paths {
|
|
|
13976
13976
|
* "free_allowance": 10,
|
|
13977
13977
|
* "period": "2026-09"
|
|
13978
13978
|
* }
|
|
13979
|
-
* }
|
|
13979
|
+
* },
|
|
13980
|
+
* "offers": {
|
|
13981
|
+
* "vitrina_ads": {
|
|
13982
|
+
* "available": true,
|
|
13983
|
+
* "reason": null
|
|
13984
|
+
* },
|
|
13985
|
+
* "tasador": {
|
|
13986
|
+
* "available": true,
|
|
13987
|
+
* "reason": null
|
|
13988
|
+
* }
|
|
13989
|
+
* },
|
|
13990
|
+
* "activators": [
|
|
13991
|
+
* {
|
|
13992
|
+
* "user_id": "22222222-2222-4222-8222-222222222222",
|
|
13993
|
+
* "name": "Ana Rojas"
|
|
13994
|
+
* }
|
|
13995
|
+
* ]
|
|
13980
13996
|
* }
|
|
13981
13997
|
* }
|
|
13982
13998
|
*/
|
|
@@ -13993,6 +14009,8 @@ interface paths {
|
|
|
13993
14009
|
meter?: string;
|
|
13994
14010
|
perUnitClpMonth?: number | null;
|
|
13995
14011
|
billedUnit?: string;
|
|
14012
|
+
/** @description The business types (`tenant.vertical`) this add-on is sold to. Omitted = every vertical. Read `offers` for the per-workspace answer. */
|
|
14013
|
+
verticals?: string[];
|
|
13996
14014
|
}[];
|
|
13997
14015
|
entitlements: {
|
|
13998
14016
|
tenant_id: string;
|
|
@@ -14023,6 +14041,24 @@ interface paths {
|
|
|
14023
14041
|
period: string;
|
|
14024
14042
|
};
|
|
14025
14043
|
};
|
|
14044
|
+
/** @description The offer for every add-on in `catalog`, keyed by its `key`. */
|
|
14045
|
+
offers: {
|
|
14046
|
+
[key: string]: {
|
|
14047
|
+
/** @description True when `POST /entitlements/{feature}/activate` would accept this add-on for this workspace (a payment method may still be needed). */
|
|
14048
|
+
available: boolean;
|
|
14049
|
+
/**
|
|
14050
|
+
* @description `null` when available. `sandbox`: a demonstration workspace never buys add-ons. `vertical`: the add-on is not sold to this type of business (see the catalog entry `verticals`).
|
|
14051
|
+
* @enum {string|null}
|
|
14052
|
+
*/
|
|
14053
|
+
reason: "sandbox" | "vertical" | null;
|
|
14054
|
+
};
|
|
14055
|
+
};
|
|
14056
|
+
/** @description Active owners and admins with a display name: who a member without `tenant:write` should ask to activate an add-on. Can be empty. */
|
|
14057
|
+
activators: {
|
|
14058
|
+
user_id: string;
|
|
14059
|
+
/** @description The member's display name. */
|
|
14060
|
+
name: string;
|
|
14061
|
+
}[];
|
|
14026
14062
|
};
|
|
14027
14063
|
};
|
|
14028
14064
|
};
|
|
@@ -14102,7 +14138,7 @@ interface paths {
|
|
|
14102
14138
|
put?: never;
|
|
14103
14139
|
/**
|
|
14104
14140
|
* Self-serve activate an add-on (returns needs_enrollment without a payment mandate)
|
|
14105
|
-
* @description Grants `feature` to the workspace once an active payment mandate exists (`{ status: "active", entitlement }`); with none, answers `{ status: "needs_enrollment" }` and grants nothing — the caller routes the dealer through the Fintoc/Mercado Pago enrollment step and retries. No payment method is needed when the add-on costs the workspace nothing: its row is covered by another biller (`billed_by` other than `vitrina`) or sits inside an active `waiver_until` waiver; such an activation answers `active` and accrues no charge for the covered days. Idempotent on an already-active feature (re-answers the same `active` row rather than erroring).
|
|
14141
|
+
* @description Grants `feature` to the workspace once an active payment mandate exists (`{ status: "active", entitlement }`); with none, answers `{ status: "needs_enrollment" }` and grants nothing — the caller routes the dealer through the Fintoc/Mercado Pago enrollment step and retries. No payment method is needed when the add-on costs the workspace nothing: its row is covered by another biller (`billed_by` other than `vitrina`) or sits inside an active `waiver_until` waiver; such an activation answers `active` and accrues no charge for the covered days. An add-on this workspace cannot buy answers 409 `ADDON_NOT_OFFERED` (`details.reason`: `sandbox` or `vertical`, the same answer `GET /entitlements` publishes in `offers`); reactivating an add-on the workspace still holds is never refused for that. Idempotent on an already-active feature (re-answers the same `active` row rather than erroring).
|
|
14106
14142
|
*/
|
|
14107
14143
|
post: {
|
|
14108
14144
|
parameters: {
|
|
@@ -14860,6 +14896,8 @@ interface paths {
|
|
|
14860
14896
|
needs_review?: boolean | string;
|
|
14861
14897
|
sin_estacionamiento?: boolean | string;
|
|
14862
14898
|
sin_sucursal?: boolean | string;
|
|
14899
|
+
papers_expired?: boolean;
|
|
14900
|
+
papers_expiring?: boolean;
|
|
14863
14901
|
tenencia?: "propio" | "consignacion";
|
|
14864
14902
|
consignacion_modalidad?: "en_local" | "virtual" | "sin_contrato";
|
|
14865
14903
|
tenencia_source?: "document" | "declared" | "default" | "decided";
|
|
@@ -15367,6 +15405,8 @@ interface paths {
|
|
|
15367
15405
|
needs_review?: boolean | null;
|
|
15368
15406
|
sin_estacionamiento?: boolean | null;
|
|
15369
15407
|
sin_sucursal?: boolean | null;
|
|
15408
|
+
papers_expired?: boolean | string;
|
|
15409
|
+
papers_expiring?: boolean | string;
|
|
15370
15410
|
tenencia?: "propio" | "consignacion";
|
|
15371
15411
|
consignacion_modalidad?: "en_local" | "virtual" | "sin_contrato";
|
|
15372
15412
|
tenencia_source?: "document" | "declared" | "default" | "decided";
|
|
@@ -39387,13 +39427,16 @@ interface paths {
|
|
|
39387
39427
|
};
|
|
39388
39428
|
/**
|
|
39389
39429
|
* List the car's papers
|
|
39390
|
-
* @description `vehicle_id` is required; there is no tenant-wide list. Newest first. Each paper carries its `kind`, its `label` (an `otro` paper) and its `expires_on` («Vence»), when recorded.
|
|
39430
|
+
* @description `vehicle_id` is required; there is no tenant-wide list of documents. Newest first. Each paper carries its `kind`, its `label` (an `otro` paper) and its `expires_on` («Vence»), when recorded.
|
|
39431
|
+
*
|
|
39432
|
+
* `expiring_within_days` (ADR 0114 §7) narrows to the papers «Vencido» or due within that many days, soonest first, each with `expiry_state`; without `vehicle_id` it covers every car in stock and never returns a cédula or a paper naming a person. The same papers raise the `vehicle_papers_expiring` dealer alert and fill the Stock counts `papers_expired` / `papers_expiring` on `GET /vehicles/stats`.
|
|
39391
39433
|
*/
|
|
39392
39434
|
get: {
|
|
39393
39435
|
parameters: {
|
|
39394
|
-
query
|
|
39395
|
-
vehicle_id
|
|
39436
|
+
query?: {
|
|
39437
|
+
vehicle_id?: string;
|
|
39396
39438
|
kind?: "padron" | "cedula" | "contrato" | "certificado_anotaciones" | "factura" | "permiso_circulacion" | "revision_tecnica" | "soap" | "otro";
|
|
39439
|
+
expiring_within_days?: number;
|
|
39397
39440
|
};
|
|
39398
39441
|
header?: never;
|
|
39399
39442
|
path?: never;
|
|
@@ -115719,6 +115762,8 @@ interface paths {
|
|
|
115719
115762
|
* - `sin_sucursal` — «Sin sucursal»: no sucursal assigned. Opens with `GET /vehicles?active=false&sin_sucursal=true`. Always 0 for a member who only sees their own sucursales.
|
|
115720
115763
|
* - `sin_estacionamiento` — «Sin estacionamiento»: no estacionamiento in the patio. Opens with `GET /vehicles?active=false&sin_estacionamiento=true`.
|
|
115721
115764
|
* - `source_deactivated` — «Retirados del portal»: the portal withdrew the car, it is deactivated and still `disponible`, so nobody has said whether it was sold. Opens with `GET /vehicles?view=source_deactivated`.
|
|
115765
|
+
* - `papers_expired` — «Papeles vencidos»: cars in stock (active, not vendido) holding a paper (SOAP, permiso de circulación, revisión técnica or a named paper) whose «Vence» day has passed and that no later paper of the same kind replaces. Opens with `GET /vehicles?papers_expired=true`.
|
|
115766
|
+
* - `papers_expiring` — «Papeles por vencer»: cars in stock holding a paper whose «Vence» day is today or within the next 30 days. Opens with `GET /vehicles?papers_expiring=true`. A car can be in both.
|
|
115722
115767
|
*
|
|
115723
115768
|
* Every count respects the caller’s sucursal scope (ADR 0102), the same ceiling the list applies. Automotive-vertical only: a workspace on another vertical gets 403 here however its scopes are set.
|
|
115724
115769
|
*/
|
|
@@ -115756,6 +115801,8 @@ interface paths {
|
|
|
115756
115801
|
* "sin_sucursal": 2,
|
|
115757
115802
|
* "sin_estacionamiento": 4,
|
|
115758
115803
|
* "source_deactivated": 0,
|
|
115804
|
+
* "papers_expired": 1,
|
|
115805
|
+
* "papers_expiring": 2,
|
|
115759
115806
|
* "by_location": {
|
|
115760
115807
|
* "b1b1b1b1-0000-4000-8000-000000000001": 3,
|
|
115761
115808
|
* "b1b1b1b1-0000-4000-8000-000000000002": 1,
|
|
@@ -130851,6 +130898,10 @@ interface components {
|
|
|
130851
130898
|
clipped: boolean;
|
|
130852
130899
|
/** @description `false` when the comparison period was not measured from its first day: show «—» for every comparison, never a delta against it. */
|
|
130853
130900
|
previous_measured: boolean;
|
|
130901
|
+
/** @description The earliest ad touch Vitrina holds (an imported history, a conversation an ad opened). Bounds the people views («antes de medir»), never a ratio. */
|
|
130902
|
+
touches_from?: string | null;
|
|
130903
|
+
/** @description `true` when the measurement start could not be read: every ratio (return, primer pago, MER, accepted plans) is unknown — show «—», never a return over days nobody measured. */
|
|
130904
|
+
start_unknown?: boolean;
|
|
130854
130905
|
};
|
|
130855
130906
|
AdsReportRow: {
|
|
130856
130907
|
spend: number | null;
|
|
@@ -131219,7 +131270,8 @@ interface components {
|
|
|
131219
131270
|
/** @description Conversions the attribution engine credits to this campaign over the period, under `model`. Today the engine counts CASH conversions only (payments; `revenue_type = cash`) — a lead, a cita or an accepted plan without a payment is not in it, so a workspace with no payments recorded reads 0 on every row. Not the population of `/ads/overview` `attributed_outcomes` (every kind) nor of `closed_outcome_count`. */
|
|
131220
131271
|
outcome_count: number;
|
|
131221
131272
|
outcome_value: number;
|
|
131222
|
-
|
|
131273
|
+
/** @description Attributed value ÷ spend. `null` when the measurement start could not be read (`coverage.start_unknown`): unknown, never 0. */
|
|
131274
|
+
roas: number | null;
|
|
131223
131275
|
direct_cash_value: number | null;
|
|
131224
131276
|
inherited_cash_value: number | null;
|
|
131225
131277
|
/** @description Closes over the WHOLE period credited to this campaign: the outcomes Vitrina recorded at the closed stage (`closed_won`) with `occurred_at` in the period (America/Santiago days) that the attribution engine matched to an ad, each counted once on its largest-credit campaign — the same rows the feed shows as `stage: closed_won, matched: true` with this `ad.campaign`, so it does not move while paging the feed. `null` = unknown (never 0): the campaign credit is not available yet (it warms in the background; retry shortly), the campaign is outside the credited set read (the top 25 by outcomes), or the period starts more than 92 days ago. */
|
|
@@ -131333,10 +131385,10 @@ interface components {
|
|
|
131333
131385
|
/** @description All seven keys, always in this order; each person in ONE. */
|
|
131334
131386
|
buckets: {
|
|
131335
131387
|
/**
|
|
131336
|
-
* @description `ads` an ad
|
|
131388
|
+
* @description `ads` an outcome the engine credited to an ad in the window, or an ad touch (click-to-WhatsApp, an Instagram / Messenger DM ad, an imported ad touch) within 90 days before the person’s first outcome; `ads_stale` an ad touch, but only more than 90 days before it (known, not credited, never a measurement problem); `*_organic` the first conversation on that channel, no ad; `in_person` an appointment with no conversation before it (walk-in / phone); `before_measuring` first contact before the measurement start; `unknown` nothing.
|
|
131337
131389
|
* @enum {string}
|
|
131338
131390
|
*/
|
|
131339
|
-
key: "ads" | "whatsapp_organic" | "instagram_organic" | "messenger_organic" | "in_person" | "before_measuring" | "unknown";
|
|
131391
|
+
key: "ads" | "ads_stale" | "whatsapp_organic" | "instagram_organic" | "messenger_organic" | "in_person" | "before_measuring" | "unknown";
|
|
131340
131392
|
people: number;
|
|
131341
131393
|
}[];
|
|
131342
131394
|
} | null;
|
|
@@ -132232,7 +132284,7 @@ declare function createClient(opts: CreateClientOptions): VitrinaClient;
|
|
|
132232
132284
|
* is regenerated — narrow on the ones you handle, never assume this
|
|
132233
132285
|
* list is exhaustive at runtime.
|
|
132234
132286
|
*/
|
|
132235
|
-
type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE' | 'WORKSPACE_MEMBER_INACTIVE' | 'DEVICE_AGENT_ALREADY_PAIRED' | 'DEVICE_AGENT_SELF_REQUIRES_USER' | 'DEVICE_AGENT_KIND_NOT_ALLOWED' | 'DEVICE_TASK_STATUS_NOT_ALLOWED' | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT' | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE' | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED' | 'REPLY_PACING_LOCKED' | 'SANDBOX_CHANNEL_FORBIDDEN' | 'SANDBOX_BILLING_FORBIDDEN' | 'SANDBOX_NOT_PROVISIONED' | 'SANDBOX_RESET_UNAVAILABLE' | 'CONNECTED_APP_SENSITIVE_DATA' | 'handoff_mismatch' | 'sync_in_flight' | 'disconnect_refused' | 'start_again' | 'reconnect' | 'not_selecting' | 'selection_expired' | 'selection_conflict' | 'account_connected_elsewhere' | 'candidate_unavailable' | 'integration_managed' | 'stage_not_found' | 'stage_not_writable' | 'stages_write_blocked' | 'invalid_outcome' | 'not_connected' | 'partner_token_invalid' | 'OUTBOUND_BLOCKED' | 'OUTBOUND_WARNING' | 'dpa_acceptance_required' | 'EMAIL_SENDER_UNRESOLVED' | 'CAMPAIGN_FROM_CONNECTED_MAILBOX' | 'BANK_FEED_NOT_CONFIGURED' | 'ADDON_NOT_PRICED' | 'MIRROR_LINES_UNMAPPED' | 'MONTH_OWNED_BY_VENDOR';
|
|
132287
|
+
type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE' | 'WORKSPACE_MEMBER_INACTIVE' | 'DEVICE_AGENT_ALREADY_PAIRED' | 'DEVICE_AGENT_SELF_REQUIRES_USER' | 'DEVICE_AGENT_KIND_NOT_ALLOWED' | 'DEVICE_TASK_STATUS_NOT_ALLOWED' | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT' | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE' | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED' | 'REPLY_PACING_LOCKED' | 'SANDBOX_CHANNEL_FORBIDDEN' | 'SANDBOX_BILLING_FORBIDDEN' | 'SANDBOX_NOT_PROVISIONED' | 'SANDBOX_RESET_UNAVAILABLE' | 'CONNECTED_APP_SENSITIVE_DATA' | 'handoff_mismatch' | 'sync_in_flight' | 'disconnect_refused' | 'start_again' | 'reconnect' | 'not_selecting' | 'selection_expired' | 'selection_conflict' | 'account_connected_elsewhere' | 'candidate_unavailable' | 'integration_managed' | 'stage_not_found' | 'stage_not_writable' | 'stages_write_blocked' | 'invalid_outcome' | 'not_connected' | 'partner_token_invalid' | 'OUTBOUND_BLOCKED' | 'OUTBOUND_WARNING' | 'dpa_acceptance_required' | 'EMAIL_SENDER_UNRESOLVED' | 'CAMPAIGN_FROM_CONNECTED_MAILBOX' | 'BANK_FEED_NOT_CONFIGURED' | 'ADDON_NOT_PRICED' | 'ADDON_NOT_OFFERED' | 'MIRROR_LINES_UNMAPPED' | 'MONTH_OWNED_BY_VENDOR';
|
|
132236
132288
|
declare const ERROR_CODES: readonly ErrorCode[];
|
|
132237
132289
|
|
|
132238
132290
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -13919,7 +13919,7 @@ interface paths {
|
|
|
13919
13919
|
};
|
|
13920
13920
|
/**
|
|
13921
13921
|
* List the add-on catalog, this workspace's entitlements, and live usage
|
|
13922
|
-
* @description The Complementos hub read. `catalog` is the typed feature catalog (prices are PR-reviewed code, never a runtime SKU table); `entitlements` is one row per add-on this workspace has ever held; `usage` carries the live meter for each metered add-on over the current **Santiago** month — `usage.tasador` is `{ used, free_allowance, period }`, where `used` is aggregated on read from `usage_event` (never a counter) and `free_allowance` reflects any per-workspace override, so it is the same pair of numbers the access gate decides on. Each entitlement carries `billed_by`: `vitrina` when Vitrina invoices the add-on, any other value when another party covers it (Vitrina charges nothing, so a client shows it as included rather than at the catalog price). A row may also carry a time-boxed waiver: `waiver_until` is the last free calendar day (America/Santiago) and `waiver_active` says whether it covers the add-on right now; once it lapses the add-on bills normally with no further action.
|
|
13922
|
+
* @description The Complementos hub read. `catalog` is the typed feature catalog (prices are PR-reviewed code, never a runtime SKU table); `entitlements` is one row per add-on this workspace has ever held; `usage` carries the live meter for each metered add-on over the current **Santiago** month — `usage.tasador` is `{ used, free_allowance, period }`, where `used` is aggregated on read from `usage_event` (never a counter) and `free_allowance` reflects any per-workspace override, so it is the same pair of numbers the access gate decides on. Each entitlement carries `billed_by`: `vitrina` when Vitrina invoices the add-on, any other value when another party covers it (Vitrina charges nothing, so a client shows it as included rather than at the catalog price). A row may also carry a time-boxed waiver: `waiver_until` is the last free calendar day (America/Santiago) and `waiver_active` says whether it covers the add-on right now; once it lapses the add-on bills normally with no further action. `offers` says, per add-on, whether THIS workspace can buy it (`available`) and if not why (`sandbox`: a demonstration workspace; `vertical`: not sold to this type of business) — the same rule the activate route enforces with a 409 `ADDON_NOT_OFFERED`. `activators` names the active owners and admins a member without `tenant:write` can ask to activate one.
|
|
13923
13923
|
*/
|
|
13924
13924
|
get: {
|
|
13925
13925
|
parameters: {
|
|
@@ -13976,7 +13976,23 @@ interface paths {
|
|
|
13976
13976
|
* "free_allowance": 10,
|
|
13977
13977
|
* "period": "2026-09"
|
|
13978
13978
|
* }
|
|
13979
|
-
* }
|
|
13979
|
+
* },
|
|
13980
|
+
* "offers": {
|
|
13981
|
+
* "vitrina_ads": {
|
|
13982
|
+
* "available": true,
|
|
13983
|
+
* "reason": null
|
|
13984
|
+
* },
|
|
13985
|
+
* "tasador": {
|
|
13986
|
+
* "available": true,
|
|
13987
|
+
* "reason": null
|
|
13988
|
+
* }
|
|
13989
|
+
* },
|
|
13990
|
+
* "activators": [
|
|
13991
|
+
* {
|
|
13992
|
+
* "user_id": "22222222-2222-4222-8222-222222222222",
|
|
13993
|
+
* "name": "Ana Rojas"
|
|
13994
|
+
* }
|
|
13995
|
+
* ]
|
|
13980
13996
|
* }
|
|
13981
13997
|
* }
|
|
13982
13998
|
*/
|
|
@@ -13993,6 +14009,8 @@ interface paths {
|
|
|
13993
14009
|
meter?: string;
|
|
13994
14010
|
perUnitClpMonth?: number | null;
|
|
13995
14011
|
billedUnit?: string;
|
|
14012
|
+
/** @description The business types (`tenant.vertical`) this add-on is sold to. Omitted = every vertical. Read `offers` for the per-workspace answer. */
|
|
14013
|
+
verticals?: string[];
|
|
13996
14014
|
}[];
|
|
13997
14015
|
entitlements: {
|
|
13998
14016
|
tenant_id: string;
|
|
@@ -14023,6 +14041,24 @@ interface paths {
|
|
|
14023
14041
|
period: string;
|
|
14024
14042
|
};
|
|
14025
14043
|
};
|
|
14044
|
+
/** @description The offer for every add-on in `catalog`, keyed by its `key`. */
|
|
14045
|
+
offers: {
|
|
14046
|
+
[key: string]: {
|
|
14047
|
+
/** @description True when `POST /entitlements/{feature}/activate` would accept this add-on for this workspace (a payment method may still be needed). */
|
|
14048
|
+
available: boolean;
|
|
14049
|
+
/**
|
|
14050
|
+
* @description `null` when available. `sandbox`: a demonstration workspace never buys add-ons. `vertical`: the add-on is not sold to this type of business (see the catalog entry `verticals`).
|
|
14051
|
+
* @enum {string|null}
|
|
14052
|
+
*/
|
|
14053
|
+
reason: "sandbox" | "vertical" | null;
|
|
14054
|
+
};
|
|
14055
|
+
};
|
|
14056
|
+
/** @description Active owners and admins with a display name: who a member without `tenant:write` should ask to activate an add-on. Can be empty. */
|
|
14057
|
+
activators: {
|
|
14058
|
+
user_id: string;
|
|
14059
|
+
/** @description The member's display name. */
|
|
14060
|
+
name: string;
|
|
14061
|
+
}[];
|
|
14026
14062
|
};
|
|
14027
14063
|
};
|
|
14028
14064
|
};
|
|
@@ -14102,7 +14138,7 @@ interface paths {
|
|
|
14102
14138
|
put?: never;
|
|
14103
14139
|
/**
|
|
14104
14140
|
* Self-serve activate an add-on (returns needs_enrollment without a payment mandate)
|
|
14105
|
-
* @description Grants `feature` to the workspace once an active payment mandate exists (`{ status: "active", entitlement }`); with none, answers `{ status: "needs_enrollment" }` and grants nothing — the caller routes the dealer through the Fintoc/Mercado Pago enrollment step and retries. No payment method is needed when the add-on costs the workspace nothing: its row is covered by another biller (`billed_by` other than `vitrina`) or sits inside an active `waiver_until` waiver; such an activation answers `active` and accrues no charge for the covered days. Idempotent on an already-active feature (re-answers the same `active` row rather than erroring).
|
|
14141
|
+
* @description Grants `feature` to the workspace once an active payment mandate exists (`{ status: "active", entitlement }`); with none, answers `{ status: "needs_enrollment" }` and grants nothing — the caller routes the dealer through the Fintoc/Mercado Pago enrollment step and retries. No payment method is needed when the add-on costs the workspace nothing: its row is covered by another biller (`billed_by` other than `vitrina`) or sits inside an active `waiver_until` waiver; such an activation answers `active` and accrues no charge for the covered days. An add-on this workspace cannot buy answers 409 `ADDON_NOT_OFFERED` (`details.reason`: `sandbox` or `vertical`, the same answer `GET /entitlements` publishes in `offers`); reactivating an add-on the workspace still holds is never refused for that. Idempotent on an already-active feature (re-answers the same `active` row rather than erroring).
|
|
14106
14142
|
*/
|
|
14107
14143
|
post: {
|
|
14108
14144
|
parameters: {
|
|
@@ -14860,6 +14896,8 @@ interface paths {
|
|
|
14860
14896
|
needs_review?: boolean | string;
|
|
14861
14897
|
sin_estacionamiento?: boolean | string;
|
|
14862
14898
|
sin_sucursal?: boolean | string;
|
|
14899
|
+
papers_expired?: boolean;
|
|
14900
|
+
papers_expiring?: boolean;
|
|
14863
14901
|
tenencia?: "propio" | "consignacion";
|
|
14864
14902
|
consignacion_modalidad?: "en_local" | "virtual" | "sin_contrato";
|
|
14865
14903
|
tenencia_source?: "document" | "declared" | "default" | "decided";
|
|
@@ -15367,6 +15405,8 @@ interface paths {
|
|
|
15367
15405
|
needs_review?: boolean | null;
|
|
15368
15406
|
sin_estacionamiento?: boolean | null;
|
|
15369
15407
|
sin_sucursal?: boolean | null;
|
|
15408
|
+
papers_expired?: boolean | string;
|
|
15409
|
+
papers_expiring?: boolean | string;
|
|
15370
15410
|
tenencia?: "propio" | "consignacion";
|
|
15371
15411
|
consignacion_modalidad?: "en_local" | "virtual" | "sin_contrato";
|
|
15372
15412
|
tenencia_source?: "document" | "declared" | "default" | "decided";
|
|
@@ -39387,13 +39427,16 @@ interface paths {
|
|
|
39387
39427
|
};
|
|
39388
39428
|
/**
|
|
39389
39429
|
* List the car's papers
|
|
39390
|
-
* @description `vehicle_id` is required; there is no tenant-wide list. Newest first. Each paper carries its `kind`, its `label` (an `otro` paper) and its `expires_on` («Vence»), when recorded.
|
|
39430
|
+
* @description `vehicle_id` is required; there is no tenant-wide list of documents. Newest first. Each paper carries its `kind`, its `label` (an `otro` paper) and its `expires_on` («Vence»), when recorded.
|
|
39431
|
+
*
|
|
39432
|
+
* `expiring_within_days` (ADR 0114 §7) narrows to the papers «Vencido» or due within that many days, soonest first, each with `expiry_state`; without `vehicle_id` it covers every car in stock and never returns a cédula or a paper naming a person. The same papers raise the `vehicle_papers_expiring` dealer alert and fill the Stock counts `papers_expired` / `papers_expiring` on `GET /vehicles/stats`.
|
|
39391
39433
|
*/
|
|
39392
39434
|
get: {
|
|
39393
39435
|
parameters: {
|
|
39394
|
-
query
|
|
39395
|
-
vehicle_id
|
|
39436
|
+
query?: {
|
|
39437
|
+
vehicle_id?: string;
|
|
39396
39438
|
kind?: "padron" | "cedula" | "contrato" | "certificado_anotaciones" | "factura" | "permiso_circulacion" | "revision_tecnica" | "soap" | "otro";
|
|
39439
|
+
expiring_within_days?: number;
|
|
39397
39440
|
};
|
|
39398
39441
|
header?: never;
|
|
39399
39442
|
path?: never;
|
|
@@ -115719,6 +115762,8 @@ interface paths {
|
|
|
115719
115762
|
* - `sin_sucursal` — «Sin sucursal»: no sucursal assigned. Opens with `GET /vehicles?active=false&sin_sucursal=true`. Always 0 for a member who only sees their own sucursales.
|
|
115720
115763
|
* - `sin_estacionamiento` — «Sin estacionamiento»: no estacionamiento in the patio. Opens with `GET /vehicles?active=false&sin_estacionamiento=true`.
|
|
115721
115764
|
* - `source_deactivated` — «Retirados del portal»: the portal withdrew the car, it is deactivated and still `disponible`, so nobody has said whether it was sold. Opens with `GET /vehicles?view=source_deactivated`.
|
|
115765
|
+
* - `papers_expired` — «Papeles vencidos»: cars in stock (active, not vendido) holding a paper (SOAP, permiso de circulación, revisión técnica or a named paper) whose «Vence» day has passed and that no later paper of the same kind replaces. Opens with `GET /vehicles?papers_expired=true`.
|
|
115766
|
+
* - `papers_expiring` — «Papeles por vencer»: cars in stock holding a paper whose «Vence» day is today or within the next 30 days. Opens with `GET /vehicles?papers_expiring=true`. A car can be in both.
|
|
115722
115767
|
*
|
|
115723
115768
|
* Every count respects the caller’s sucursal scope (ADR 0102), the same ceiling the list applies. Automotive-vertical only: a workspace on another vertical gets 403 here however its scopes are set.
|
|
115724
115769
|
*/
|
|
@@ -115756,6 +115801,8 @@ interface paths {
|
|
|
115756
115801
|
* "sin_sucursal": 2,
|
|
115757
115802
|
* "sin_estacionamiento": 4,
|
|
115758
115803
|
* "source_deactivated": 0,
|
|
115804
|
+
* "papers_expired": 1,
|
|
115805
|
+
* "papers_expiring": 2,
|
|
115759
115806
|
* "by_location": {
|
|
115760
115807
|
* "b1b1b1b1-0000-4000-8000-000000000001": 3,
|
|
115761
115808
|
* "b1b1b1b1-0000-4000-8000-000000000002": 1,
|
|
@@ -130851,6 +130898,10 @@ interface components {
|
|
|
130851
130898
|
clipped: boolean;
|
|
130852
130899
|
/** @description `false` when the comparison period was not measured from its first day: show «—» for every comparison, never a delta against it. */
|
|
130853
130900
|
previous_measured: boolean;
|
|
130901
|
+
/** @description The earliest ad touch Vitrina holds (an imported history, a conversation an ad opened). Bounds the people views («antes de medir»), never a ratio. */
|
|
130902
|
+
touches_from?: string | null;
|
|
130903
|
+
/** @description `true` when the measurement start could not be read: every ratio (return, primer pago, MER, accepted plans) is unknown — show «—», never a return over days nobody measured. */
|
|
130904
|
+
start_unknown?: boolean;
|
|
130854
130905
|
};
|
|
130855
130906
|
AdsReportRow: {
|
|
130856
130907
|
spend: number | null;
|
|
@@ -131219,7 +131270,8 @@ interface components {
|
|
|
131219
131270
|
/** @description Conversions the attribution engine credits to this campaign over the period, under `model`. Today the engine counts CASH conversions only (payments; `revenue_type = cash`) — a lead, a cita or an accepted plan without a payment is not in it, so a workspace with no payments recorded reads 0 on every row. Not the population of `/ads/overview` `attributed_outcomes` (every kind) nor of `closed_outcome_count`. */
|
|
131220
131271
|
outcome_count: number;
|
|
131221
131272
|
outcome_value: number;
|
|
131222
|
-
|
|
131273
|
+
/** @description Attributed value ÷ spend. `null` when the measurement start could not be read (`coverage.start_unknown`): unknown, never 0. */
|
|
131274
|
+
roas: number | null;
|
|
131223
131275
|
direct_cash_value: number | null;
|
|
131224
131276
|
inherited_cash_value: number | null;
|
|
131225
131277
|
/** @description Closes over the WHOLE period credited to this campaign: the outcomes Vitrina recorded at the closed stage (`closed_won`) with `occurred_at` in the period (America/Santiago days) that the attribution engine matched to an ad, each counted once on its largest-credit campaign — the same rows the feed shows as `stage: closed_won, matched: true` with this `ad.campaign`, so it does not move while paging the feed. `null` = unknown (never 0): the campaign credit is not available yet (it warms in the background; retry shortly), the campaign is outside the credited set read (the top 25 by outcomes), or the period starts more than 92 days ago. */
|
|
@@ -131333,10 +131385,10 @@ interface components {
|
|
|
131333
131385
|
/** @description All seven keys, always in this order; each person in ONE. */
|
|
131334
131386
|
buckets: {
|
|
131335
131387
|
/**
|
|
131336
|
-
* @description `ads` an ad
|
|
131388
|
+
* @description `ads` an outcome the engine credited to an ad in the window, or an ad touch (click-to-WhatsApp, an Instagram / Messenger DM ad, an imported ad touch) within 90 days before the person’s first outcome; `ads_stale` an ad touch, but only more than 90 days before it (known, not credited, never a measurement problem); `*_organic` the first conversation on that channel, no ad; `in_person` an appointment with no conversation before it (walk-in / phone); `before_measuring` first contact before the measurement start; `unknown` nothing.
|
|
131337
131389
|
* @enum {string}
|
|
131338
131390
|
*/
|
|
131339
|
-
key: "ads" | "whatsapp_organic" | "instagram_organic" | "messenger_organic" | "in_person" | "before_measuring" | "unknown";
|
|
131391
|
+
key: "ads" | "ads_stale" | "whatsapp_organic" | "instagram_organic" | "messenger_organic" | "in_person" | "before_measuring" | "unknown";
|
|
131340
131392
|
people: number;
|
|
131341
131393
|
}[];
|
|
131342
131394
|
} | null;
|
|
@@ -132232,7 +132284,7 @@ declare function createClient(opts: CreateClientOptions): VitrinaClient;
|
|
|
132232
132284
|
* is regenerated — narrow on the ones you handle, never assume this
|
|
132233
132285
|
* list is exhaustive at runtime.
|
|
132234
132286
|
*/
|
|
132235
|
-
type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE' | 'WORKSPACE_MEMBER_INACTIVE' | 'DEVICE_AGENT_ALREADY_PAIRED' | 'DEVICE_AGENT_SELF_REQUIRES_USER' | 'DEVICE_AGENT_KIND_NOT_ALLOWED' | 'DEVICE_TASK_STATUS_NOT_ALLOWED' | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT' | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE' | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED' | 'REPLY_PACING_LOCKED' | 'SANDBOX_CHANNEL_FORBIDDEN' | 'SANDBOX_BILLING_FORBIDDEN' | 'SANDBOX_NOT_PROVISIONED' | 'SANDBOX_RESET_UNAVAILABLE' | 'CONNECTED_APP_SENSITIVE_DATA' | 'handoff_mismatch' | 'sync_in_flight' | 'disconnect_refused' | 'start_again' | 'reconnect' | 'not_selecting' | 'selection_expired' | 'selection_conflict' | 'account_connected_elsewhere' | 'candidate_unavailable' | 'integration_managed' | 'stage_not_found' | 'stage_not_writable' | 'stages_write_blocked' | 'invalid_outcome' | 'not_connected' | 'partner_token_invalid' | 'OUTBOUND_BLOCKED' | 'OUTBOUND_WARNING' | 'dpa_acceptance_required' | 'EMAIL_SENDER_UNRESOLVED' | 'CAMPAIGN_FROM_CONNECTED_MAILBOX' | 'BANK_FEED_NOT_CONFIGURED' | 'ADDON_NOT_PRICED' | 'MIRROR_LINES_UNMAPPED' | 'MONTH_OWNED_BY_VENDOR';
|
|
132287
|
+
type ErrorCode = 'VALIDATION_ERROR' | 'NOT_FOUND' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'CONFLICT' | 'UNIQUE_CONFLICT' | 'IDEMPOTENCY_KEY_CONFLICT' | 'VERTICAL_NOT_ENABLED' | 'ENTITLEMENT_NOT_ACTIVE' | 'UNSUPPORTED_VEHICLE_TYPE' | 'NO_OFFERING_ADAPTER' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPSTREAM_ERROR' | 'RATE_LIMITED' | 'GONE' | 'INTERNAL_ERROR' | 'MESSAGING_ACCOUNT_TENANT_MISSING' | 'COPILOT_APPROVALS_UNAVAILABLE' | 'COPILOT_APPROVAL_REPLAY_BLOCKED' | 'COPILOT_DISABLED' | 'COPILOT_BUDGET_EXCEEDED' | 'DEALER_SITES_NOT_CONFIGURED' | 'STOREFRONT_ZONES_NOT_CONFIGURED' | 'RECONTACTO_NOT_ENABLED' | 'SYNC_QUEUE_UNAVAILABLE' | 'SHARE_IMAGE_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_UNAVAILABLE' | 'DOCUMENT_LOGO_UNAVAILABLE' | 'ATTRIBUTION_PROFILE_MISMATCH' | 'ADS_KEY_NEEDS_REMINT' | 'ADS_ACTION_READ_UNAVAILABLE' | 'ADS_ACTION_REPLAY_BLOCKED' | 'ADS_ACTION_NOT_AVAILABLE' | 'ADS_ACTION_NOT_REVERSIBLE' | 'ADS_ACTION_CONSENT_REQUIRED' | 'GTM_NOT_CONFIGURED' | 'GTM_NOT_CONNECTED' | 'GTM_CONSENT_REQUIRED' | 'GTM_PERMISSION_DENIED' | 'GTM_CONTAINER_REQUIRED' | 'GTM_PENDING_CHANGES' | 'GTM_WORKSPACE_CONFLICT' | 'GTM_WORKSPACE_LIMIT' | 'MISSING_SIGNATURE' | 'MALFORMED_SIGNATURE' | 'STALE_SIGNATURE' | 'INVALID_SIGNATURE' | 'REPLAY_DETECTED' | 'TURNSTILE_UNAVAILABLE' | 'TASADOR_QUOTA_EXHAUSTED' | 'TASADOR_ANON_DAILY_LIMIT' | 'TASADOR_CAPACITY_REACHED' | 'CLINIC_PLAN_REQUIRED' | 'TENANT_BILLING_PAUSED' | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE' | 'WORKSPACE_MEMBER_INACTIVE' | 'DEVICE_AGENT_ALREADY_PAIRED' | 'DEVICE_AGENT_SELF_REQUIRES_USER' | 'DEVICE_AGENT_KIND_NOT_ALLOWED' | 'DEVICE_TASK_STATUS_NOT_ALLOWED' | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT' | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE' | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED' | 'REPLY_PACING_LOCKED' | 'SANDBOX_CHANNEL_FORBIDDEN' | 'SANDBOX_BILLING_FORBIDDEN' | 'SANDBOX_NOT_PROVISIONED' | 'SANDBOX_RESET_UNAVAILABLE' | 'CONNECTED_APP_SENSITIVE_DATA' | 'handoff_mismatch' | 'sync_in_flight' | 'disconnect_refused' | 'start_again' | 'reconnect' | 'not_selecting' | 'selection_expired' | 'selection_conflict' | 'account_connected_elsewhere' | 'candidate_unavailable' | 'integration_managed' | 'stage_not_found' | 'stage_not_writable' | 'stages_write_blocked' | 'invalid_outcome' | 'not_connected' | 'partner_token_invalid' | 'OUTBOUND_BLOCKED' | 'OUTBOUND_WARNING' | 'dpa_acceptance_required' | 'EMAIL_SENDER_UNRESOLVED' | 'CAMPAIGN_FROM_CONNECTED_MAILBOX' | 'BANK_FEED_NOT_CONFIGURED' | 'ADDON_NOT_PRICED' | 'ADDON_NOT_OFFERED' | 'MIRROR_LINES_UNMAPPED' | 'MONTH_OWNED_BY_VENDOR';
|
|
132236
132288
|
declare const ERROR_CODES: readonly ErrorCode[];
|
|
132237
132289
|
|
|
132238
132290
|
/**
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE'\n | 'WORKSPACE_MEMBER_INACTIVE'\n | 'DEVICE_AGENT_ALREADY_PAIRED'\n | 'DEVICE_AGENT_SELF_REQUIRES_USER'\n | 'DEVICE_AGENT_KIND_NOT_ALLOWED'\n | 'DEVICE_TASK_STATUS_NOT_ALLOWED'\n | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT'\n | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE'\n | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED'\n | 'REPLY_PACING_LOCKED'\n | 'SANDBOX_CHANNEL_FORBIDDEN'\n | 'SANDBOX_BILLING_FORBIDDEN'\n | 'SANDBOX_NOT_PROVISIONED'\n | 'SANDBOX_RESET_UNAVAILABLE'\n | 'CONNECTED_APP_SENSITIVE_DATA'\n | 'handoff_mismatch'\n | 'sync_in_flight'\n | 'disconnect_refused'\n | 'start_again'\n | 'reconnect'\n | 'not_selecting'\n | 'selection_expired'\n | 'selection_conflict'\n | 'account_connected_elsewhere'\n | 'candidate_unavailable'\n | 'integration_managed'\n | 'stage_not_found'\n | 'stage_not_writable'\n | 'stages_write_blocked'\n | 'invalid_outcome'\n | 'not_connected'\n | 'partner_token_invalid'\n | 'OUTBOUND_BLOCKED'\n | 'OUTBOUND_WARNING'\n | 'dpa_acceptance_required'\n | 'EMAIL_SENDER_UNRESOLVED'\n | 'CAMPAIGN_FROM_CONNECTED_MAILBOX'\n | 'BANK_FEED_NOT_CONFIGURED'\n | 'ADDON_NOT_PRICED'\n | 'MIRROR_LINES_UNMAPPED'\n | 'MONTH_OWNED_BY_VENDOR';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE',\n 'WORKSPACE_MEMBER_INACTIVE',\n 'DEVICE_AGENT_ALREADY_PAIRED',\n 'DEVICE_AGENT_SELF_REQUIRES_USER',\n 'DEVICE_AGENT_KIND_NOT_ALLOWED',\n 'DEVICE_TASK_STATUS_NOT_ALLOWED',\n 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT',\n 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE',\n 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED',\n 'REPLY_PACING_LOCKED',\n 'SANDBOX_CHANNEL_FORBIDDEN',\n 'SANDBOX_BILLING_FORBIDDEN',\n 'SANDBOX_NOT_PROVISIONED',\n 'SANDBOX_RESET_UNAVAILABLE',\n 'CONNECTED_APP_SENSITIVE_DATA',\n 'handoff_mismatch',\n 'sync_in_flight',\n 'disconnect_refused',\n 'start_again',\n 'reconnect',\n 'not_selecting',\n 'selection_expired',\n 'selection_conflict',\n 'account_connected_elsewhere',\n 'candidate_unavailable',\n 'integration_managed',\n 'stage_not_found',\n 'stage_not_writable',\n 'stages_write_blocked',\n 'invalid_outcome',\n 'not_connected',\n 'partner_token_invalid',\n 'OUTBOUND_BLOCKED',\n 'OUTBOUND_WARNING',\n 'dpa_acceptance_required',\n 'EMAIL_SENDER_UNRESOLVED',\n 'CAMPAIGN_FROM_CONNECTED_MAILBOX',\n 'BANK_FEED_NOT_CONFIGURED',\n 'ADDON_NOT_PRICED',\n 'MIRROR_LINES_UNMAPPED',\n 'MONTH_OWNED_BY_VENDOR',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/generated/error-codes.ts","../src/errors.ts","../src/webhooks.ts","../src/events.ts","../src/client.ts","../src/pagination.ts","../src/idempotency.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// AUTO-GENERATED by scripts/generate-error-codes.ts from the repo-root src/errors/index.ts's `ErrorCode` union — do not edit by hand.\n\n/**\n * Every code the API error envelope can carry as `error.code`, at the\n * commit this SDK was generated from. Kept OPEN (see `OpenErrorCode` in\n * `../errors.ts`) because a server can ship a new code before this SDK\n * is regenerated — narrow on the ones you handle, never assume this\n * list is exhaustive at runtime.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR'\n | 'NOT_FOUND'\n | 'UNAUTHORIZED'\n | 'FORBIDDEN'\n | 'CONFLICT'\n | 'UNIQUE_CONFLICT'\n | 'IDEMPOTENCY_KEY_CONFLICT'\n | 'VERTICAL_NOT_ENABLED'\n | 'ENTITLEMENT_NOT_ACTIVE'\n | 'UNSUPPORTED_VEHICLE_TYPE'\n | 'NO_OFFERING_ADAPTER'\n | 'PAYLOAD_TOO_LARGE'\n | 'UNSUPPORTED_MEDIA_TYPE'\n | 'UPSTREAM_ERROR'\n | 'RATE_LIMITED'\n | 'GONE'\n | 'INTERNAL_ERROR'\n | 'MESSAGING_ACCOUNT_TENANT_MISSING'\n | 'COPILOT_APPROVALS_UNAVAILABLE'\n | 'COPILOT_APPROVAL_REPLAY_BLOCKED'\n | 'COPILOT_DISABLED'\n | 'COPILOT_BUDGET_EXCEEDED'\n | 'DEALER_SITES_NOT_CONFIGURED'\n | 'STOREFRONT_ZONES_NOT_CONFIGURED'\n | 'RECONTACTO_NOT_ENABLED'\n | 'SYNC_QUEUE_UNAVAILABLE'\n | 'SHARE_IMAGE_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_UNAVAILABLE'\n | 'DOCUMENT_LOGO_UNAVAILABLE'\n | 'ATTRIBUTION_PROFILE_MISMATCH'\n | 'ADS_KEY_NEEDS_REMINT'\n | 'ADS_ACTION_READ_UNAVAILABLE'\n | 'ADS_ACTION_REPLAY_BLOCKED'\n | 'ADS_ACTION_NOT_AVAILABLE'\n | 'ADS_ACTION_NOT_REVERSIBLE'\n | 'ADS_ACTION_CONSENT_REQUIRED'\n | 'GTM_NOT_CONFIGURED'\n | 'GTM_NOT_CONNECTED'\n | 'GTM_CONSENT_REQUIRED'\n | 'GTM_PERMISSION_DENIED'\n | 'GTM_CONTAINER_REQUIRED'\n | 'GTM_PENDING_CHANGES'\n | 'GTM_WORKSPACE_CONFLICT'\n | 'GTM_WORKSPACE_LIMIT'\n | 'MISSING_SIGNATURE'\n | 'MALFORMED_SIGNATURE'\n | 'STALE_SIGNATURE'\n | 'INVALID_SIGNATURE'\n | 'REPLAY_DETECTED'\n | 'TURNSTILE_UNAVAILABLE'\n | 'TASADOR_QUOTA_EXHAUSTED'\n | 'TASADOR_ANON_DAILY_LIMIT'\n | 'TASADOR_CAPACITY_REACHED'\n | 'CLINIC_PLAN_REQUIRED'\n | 'TENANT_BILLING_PAUSED'\n | 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE'\n | 'WORKSPACE_MEMBER_INACTIVE'\n | 'DEVICE_AGENT_ALREADY_PAIRED'\n | 'DEVICE_AGENT_SELF_REQUIRES_USER'\n | 'DEVICE_AGENT_KIND_NOT_ALLOWED'\n | 'DEVICE_TASK_STATUS_NOT_ALLOWED'\n | 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT'\n | 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE'\n | 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED'\n | 'REPLY_PACING_LOCKED'\n | 'SANDBOX_CHANNEL_FORBIDDEN'\n | 'SANDBOX_BILLING_FORBIDDEN'\n | 'SANDBOX_NOT_PROVISIONED'\n | 'SANDBOX_RESET_UNAVAILABLE'\n | 'CONNECTED_APP_SENSITIVE_DATA'\n | 'handoff_mismatch'\n | 'sync_in_flight'\n | 'disconnect_refused'\n | 'start_again'\n | 'reconnect'\n | 'not_selecting'\n | 'selection_expired'\n | 'selection_conflict'\n | 'account_connected_elsewhere'\n | 'candidate_unavailable'\n | 'integration_managed'\n | 'stage_not_found'\n | 'stage_not_writable'\n | 'stages_write_blocked'\n | 'invalid_outcome'\n | 'not_connected'\n | 'partner_token_invalid'\n | 'OUTBOUND_BLOCKED'\n | 'OUTBOUND_WARNING'\n | 'dpa_acceptance_required'\n | 'EMAIL_SENDER_UNRESOLVED'\n | 'CAMPAIGN_FROM_CONNECTED_MAILBOX'\n | 'BANK_FEED_NOT_CONFIGURED'\n | 'ADDON_NOT_PRICED'\n | 'ADDON_NOT_OFFERED'\n | 'MIRROR_LINES_UNMAPPED'\n | 'MONTH_OWNED_BY_VENDOR';\n\nexport const ERROR_CODES: readonly ErrorCode[] = [\n 'VALIDATION_ERROR',\n 'NOT_FOUND',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'CONFLICT',\n 'UNIQUE_CONFLICT',\n 'IDEMPOTENCY_KEY_CONFLICT',\n 'VERTICAL_NOT_ENABLED',\n 'ENTITLEMENT_NOT_ACTIVE',\n 'UNSUPPORTED_VEHICLE_TYPE',\n 'NO_OFFERING_ADAPTER',\n 'PAYLOAD_TOO_LARGE',\n 'UNSUPPORTED_MEDIA_TYPE',\n 'UPSTREAM_ERROR',\n 'RATE_LIMITED',\n 'GONE',\n 'INTERNAL_ERROR',\n 'MESSAGING_ACCOUNT_TENANT_MISSING',\n 'COPILOT_APPROVALS_UNAVAILABLE',\n 'COPILOT_APPROVAL_REPLAY_BLOCKED',\n 'COPILOT_DISABLED',\n 'COPILOT_BUDGET_EXCEEDED',\n 'DEALER_SITES_NOT_CONFIGURED',\n 'STOREFRONT_ZONES_NOT_CONFIGURED',\n 'RECONTACTO_NOT_ENABLED',\n 'SYNC_QUEUE_UNAVAILABLE',\n 'SHARE_IMAGE_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_UNAVAILABLE',\n 'DOCUMENT_LOGO_UNAVAILABLE',\n 'ATTRIBUTION_PROFILE_MISMATCH',\n 'ADS_KEY_NEEDS_REMINT',\n 'ADS_ACTION_READ_UNAVAILABLE',\n 'ADS_ACTION_REPLAY_BLOCKED',\n 'ADS_ACTION_NOT_AVAILABLE',\n 'ADS_ACTION_NOT_REVERSIBLE',\n 'ADS_ACTION_CONSENT_REQUIRED',\n 'GTM_NOT_CONFIGURED',\n 'GTM_NOT_CONNECTED',\n 'GTM_CONSENT_REQUIRED',\n 'GTM_PERMISSION_DENIED',\n 'GTM_CONTAINER_REQUIRED',\n 'GTM_PENDING_CHANGES',\n 'GTM_WORKSPACE_CONFLICT',\n 'GTM_WORKSPACE_LIMIT',\n 'MISSING_SIGNATURE',\n 'MALFORMED_SIGNATURE',\n 'STALE_SIGNATURE',\n 'INVALID_SIGNATURE',\n 'REPLAY_DETECTED',\n 'TURNSTILE_UNAVAILABLE',\n 'TASADOR_QUOTA_EXHAUSTED',\n 'TASADOR_ANON_DAILY_LIMIT',\n 'TASADOR_CAPACITY_REACHED',\n 'CLINIC_PLAN_REQUIRED',\n 'TENANT_BILLING_PAUSED',\n 'FACEBOOK_MARKETPLACE_NOT_AVAILABLE',\n 'WORKSPACE_MEMBER_INACTIVE',\n 'DEVICE_AGENT_ALREADY_PAIRED',\n 'DEVICE_AGENT_SELF_REQUIRES_USER',\n 'DEVICE_AGENT_KIND_NOT_ALLOWED',\n 'DEVICE_TASK_STATUS_NOT_ALLOWED',\n 'FACEBOOK_MARKETPLACE_NO_PHONE_AGENT',\n 'FACEBOOK_MARKETPLACE_THREAD_NOT_ADDRESSABLE',\n 'FACEBOOK_MARKETPLACE_ATTACHMENT_UNSUPPORTED',\n 'REPLY_PACING_LOCKED',\n 'SANDBOX_CHANNEL_FORBIDDEN',\n 'SANDBOX_BILLING_FORBIDDEN',\n 'SANDBOX_NOT_PROVISIONED',\n 'SANDBOX_RESET_UNAVAILABLE',\n 'CONNECTED_APP_SENSITIVE_DATA',\n 'handoff_mismatch',\n 'sync_in_flight',\n 'disconnect_refused',\n 'start_again',\n 'reconnect',\n 'not_selecting',\n 'selection_expired',\n 'selection_conflict',\n 'account_connected_elsewhere',\n 'candidate_unavailable',\n 'integration_managed',\n 'stage_not_found',\n 'stage_not_writable',\n 'stages_write_blocked',\n 'invalid_outcome',\n 'not_connected',\n 'partner_token_invalid',\n 'OUTBOUND_BLOCKED',\n 'OUTBOUND_WARNING',\n 'dpa_acceptance_required',\n 'EMAIL_SENDER_UNRESOLVED',\n 'CAMPAIGN_FROM_CONNECTED_MAILBOX',\n 'BANK_FEED_NOT_CONFIGURED',\n 'ADDON_NOT_PRICED',\n 'ADDON_NOT_OFFERED',\n 'MIRROR_LINES_UNMAPPED',\n 'MONTH_OWNED_BY_VENDOR',\n] as const;\n","/**\n * Typed errors over the API's one error envelope: `{ error: { code, message,\n * details?, field_errors?, requestId?, ...extensions } }` (`src/errors/\n * index.ts` + `src/middlewares/error-handler.ts` in vitrina-app). `code` is\n * generated from the committed error catalogue (see `generated/error-codes.ts`\n * and `../scripts/generate-error-codes.ts`) but kept OPEN at the type level —\n * `ErrorCode | (string & Record<string, never>)` — because a server release\n * can ship a new code before this SDK is regenerated; never treat the union\n * as exhaustive at runtime (no `switch` without a `default`).\n */\nimport { ERROR_CODES, type ErrorCode } from './generated/error-codes';\n\nexport { ERROR_CODES };\nexport type { ErrorCode };\n\n/** `error.code`, widened so an unrecognised future code still type-checks.\n * The `Record<string, never>` intersection (not a bare `{}`) is what keeps\n * known `ErrorCode` literals showing up in autocomplete while still\n * accepting any string. */\nexport type OpenErrorCode = ErrorCode | (string & Record<string, never>);\n\n/** One entry of `error.details.reasons` on an outbound-policy verdict (C4). */\nexport interface OutboundVerdictReason {\n code: string;\n kind: 'bloqueo' | 'advertencia';\n hint: string;\n detail?: string;\n}\n\n/** The wire shape of `error` inside the envelope. */\nexport interface ApiErrorBody {\n code: OpenErrorCode;\n message: string;\n details?: unknown;\n field_errors?: Record<string, string>;\n requestId?: string;\n /** Send-verdict envelopes (C4) put `reasons`/`hint` at this level too. */\n reasons?: OutboundVerdictReason[];\n hint?: string;\n [key: string]: unknown;\n}\n\nexport interface ApiErrorOptions {\n requestUrl?: string;\n requestMethod?: string;\n}\n\n/** Base class for every error this SDK throws for a non-2xx API response. */\nexport class VitrinaApiError extends Error {\n /** The catalogue code — branch on this, never on `message`. */\n readonly code: OpenErrorCode;\n\n /** HTTP status of the response that produced this error. */\n readonly status: number;\n\n readonly details?: unknown;\n\n readonly fieldErrors?: Record<string, string>;\n\n /** Correlates this failure with the server's own logs. */\n readonly requestId?: string;\n\n /** The full parsed `error` object, for anything not surfaced above. */\n readonly body: ApiErrorBody;\n\n readonly requestUrl?: string;\n\n readonly requestMethod?: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(body.message || `Vitrina API error ${body.code} (HTTP ${status})`);\n this.name = 'VitrinaApiError';\n this.status = status;\n this.code = body.code;\n this.details = body.details;\n this.fieldErrors = body.field_errors;\n this.requestId = body.requestId;\n this.body = body;\n this.requestUrl = options.requestUrl;\n this.requestMethod = options.requestMethod;\n }\n\n /** `429 RATE_LIMITED`. */\n get isRateLimited(): boolean {\n return this.status === 429;\n }\n\n /** `401` — no credential, or one the server does not recognise. */\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n /** `403` — a real credential, missing the scope this call needs. */\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n /** `404 NOT_FOUND`. */\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n /** `409 IDEMPOTENCY_KEY_CONFLICT` — same `Idempotency-Key`, different body. */\n get isIdempotencyConflict(): boolean {\n return this.code === 'IDEMPOTENCY_KEY_CONFLICT';\n }\n}\n\n/**\n * `422 OUTBOUND_BLOCKED` (a Bloqueo, never overridable) or `409\n * OUTBOUND_WARNING` (an Advertencia — resend the same call with\n * `acknowledge: reasons.map(r => r.code)` in the body to proceed). Cross-\n * ticket contract C4 — `src/services/api-send-verdicts.ts` in vitrina-app.\n */\nexport class OutboundVerdictError extends VitrinaApiError {\n /** Every reason the send was stopped for, with its kind and hint. */\n readonly reasons: OutboundVerdictReason[];\n\n /** The first reason's hint, surfaced at the top level like the wire body. */\n readonly hint: string;\n\n constructor(\n status: number,\n body: ApiErrorBody,\n options: ApiErrorOptions = {},\n ) {\n super(status, body, options);\n this.name = 'OutboundVerdictError';\n const detailsReasons = (\n body.details as { reasons?: OutboundVerdictReason[] } | undefined\n )?.reasons;\n this.reasons = body.reasons ?? detailsReasons ?? [];\n this.hint = body.hint ?? this.reasons[0]?.hint ?? '';\n }\n\n /** A Bloqueo — the law, the customer or the provider already said no. */\n get isBlocked(): boolean {\n return this.code === 'OUTBOUND_BLOCKED';\n }\n\n /** An Advertencia — resend once with `acknowledge` to proceed. */\n get isWarning(): boolean {\n return this.code === 'OUTBOUND_WARNING';\n }\n\n /** `reasons` codes, ready to pass back as `{ acknowledge: [...] }`. */\n get acknowledgeCodes(): string[] {\n return this.reasons.map((r) => r.code);\n }\n}\n\nfunction asErrorBody(payload: unknown, status: number): ApiErrorBody {\n if (payload && typeof payload === 'object') {\n const maybeEnvelope = payload as { error?: unknown };\n const candidate =\n maybeEnvelope.error && typeof maybeEnvelope.error === 'object'\n ? (maybeEnvelope.error as Record<string, unknown>)\n : (payload as Record<string, unknown>);\n if (\n typeof candidate.code === 'string' &&\n typeof candidate.message === 'string'\n ) {\n return candidate as unknown as ApiErrorBody;\n }\n }\n return {\n code: 'INTERNAL_ERROR',\n message: `HTTP ${status} with an unrecognised response body`,\n };\n}\n\n/** Turns a non-2xx response's status + parsed JSON body into a typed error. */\nexport function toApiError(\n status: number,\n payload: unknown,\n options: ApiErrorOptions = {},\n): VitrinaApiError {\n const body = asErrorBody(payload, status);\n if (body.code === 'OUTBOUND_BLOCKED' || body.code === 'OUTBOUND_WARNING') {\n return new OutboundVerdictError(status, body, options);\n }\n return new VitrinaApiError(status, body, options);\n}\n\n/** The subset of an `openapi-fetch` call's resolved value `unwrap` needs. */\nexport interface UnwrappableResult<T> {\n data?: T;\n error?: unknown;\n response: Response;\n}\n\n/**\n * `const location = unwrap(await api.GET('/locations/{id}', ...))` — throws\n * a typed `VitrinaApiError` (or `OutboundVerdictError`) instead of making\n * every call site check `error` by hand. `openapi-fetch` itself never\n * throws; reach for the raw `{ data, error }` result directly when you want\n * that instead.\n */\nexport function unwrap<T>(\n result: UnwrappableResult<T>,\n options: ApiErrorOptions = {},\n): T {\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error, options);\n }\n if (result.data === undefined) {\n throw new VitrinaApiError(\n result.response.status,\n {\n code: 'INTERNAL_ERROR',\n message: 'Response had neither data nor error',\n },\n options,\n );\n }\n return result.data;\n}\n","/**\n * Webhook Evento envelope + Stripe-style signature verification, matching\n * the dispatch service's own `signPayload`/`buildHeaders`\n * (`src/services/webhook-dispatch.service.ts` in vitrina-app; cross-ticket\n * contract C3). The signature is an HMAC-SHA256 over `${timestamp}.${body}`\n * with the subscription's `whsec_...` secret, carried as\n * `X-Webhook-Signature: t=<unix_seconds>,v1=<hex>`.\n *\n * `tests/api-sdk-webhook-signature.test.ts` (vitrina-app repo root) verifies\n * this against the REAL `signPayload` the dispatch service calls in\n * production — not a hand-written fixture.\n */\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** `resource` on the envelope — what changed, and where to read it. */\nexport interface WebhookEventResource {\n type: string;\n id: string | null;\n /** Absolute `GET` URL for this resource, or `null` when none is available\n * (e.g. the event has no single owning record). Feeds `event.fetch()`. */\n url: string | null;\n}\n\n/** Who did it — see `Author` in vitrina-app's `src/types/author.ts` (C3). */\nexport interface WebhookEventAuthor {\n kind: 'member' | 'api_key' | 'ai_agent' | 'system' | 'contact';\n id: string | null;\n name: string | null;\n via?: { kind: 'connected_app' | 'personal_token'; name: string };\n}\n\n/** Why `data` is absent — every reason a subscription's owner can be denied\n * the resource body. `missing_scope:<scope>` is a family, not one literal. */\nexport type DataOmittedReason =\n | 'not_requested'\n | 'sensitive'\n | 'owner_unavailable'\n | 'restricted_visibility'\n | `missing_scope:${string}`;\n\n/** One Evento, exactly as it arrives on the wire. Exactly one of `data` /\n * `data_omitted` is ever present. */\nexport interface WebhookEventEnvelope<TData = Record<string, unknown>> {\n id: string;\n type: string;\n version: number;\n /** `false` for an event from a sandbox (test-mode) workspace, `true`\n * otherwise; mirrored on the delivery's `Vitrina-Livemode` header.\n * Optional here only so an envelope captured before vitrina-app#2693\n * still satisfies this type — every envelope on the wire carries it. */\n livemode?: boolean;\n created_at: string;\n tenant_id: string;\n resource: WebhookEventResource;\n changes?: Record<string, { from: unknown; to: unknown }>;\n author: WebhookEventAuthor;\n data?: TData;\n data_omitted?: DataOmittedReason;\n}\n\nexport interface VerifySignatureOptions {\n /** How far `X-Webhook-Timestamp` may drift from now before a signature\n * that otherwise matches is rejected as stale (replay protection).\n * Default 300 (5 minutes), matching the docs' own verifier. */\n toleranceSeconds?: number;\n /** Override \"now\", in Unix seconds — for tests. */\n nowUnix?: number;\n}\n\nexport type VerifyResult =\n | { ok: true }\n | { ok: false; reason: 'malformed_header' | 'bad_signature' | 'stale' };\n\nfunction parseSignatureHeader(\n header: string,\n): { timestamp: number; v1: string } | null {\n const parts: Record<string, string> = {};\n for (const pair of header.split(',')) {\n const at = pair.indexOf('=');\n if (at === -1) continue;\n parts[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();\n }\n const timestamp = Number(parts.t);\n if (!Number.isFinite(timestamp) || !parts.v1) return null;\n return { timestamp, v1: parts.v1 };\n}\n\n/**\n * Verifies an `X-Webhook-Signature` header against the raw delivery body.\n * Never throws — returns `{ ok: false, reason }` so a caller can log why a\n * delivery was rejected. `rawBody` must be the exact bytes the delivery\n * sent, before any JSON re-serialization (re-stringifying a parsed body\n * produces different bytes and the signature will never match).\n */\nexport function verifyWebhookSignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): VerifyResult {\n const parsed = parseSignatureHeader(params.signatureHeader);\n if (!parsed) return { ok: false, reason: 'malformed_header' };\n\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n const expected = createHmac('sha256', params.secret)\n .update(`${parsed.timestamp}.${body}`)\n .digest('hex');\n\n const given = Buffer.from(parsed.v1, 'utf8');\n const mine = Buffer.from(expected, 'utf8');\n const signatureMatches =\n given.length === mine.length && timingSafeEqual(given, mine);\n if (!signatureMatches) return { ok: false, reason: 'bad_signature' };\n\n const tolerance = params.toleranceSeconds ?? 300;\n const now = params.nowUnix ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - parsed.timestamp) > tolerance) {\n return { ok: false, reason: 'stale' };\n }\n return { ok: true };\n}\n\nexport class WebhookSignatureError extends Error {\n readonly reason: Exclude<VerifyResult, { ok: true }>['reason'];\n\n constructor(reason: Exclude<VerifyResult, { ok: true }>['reason']) {\n super(`Webhook signature verification failed: ${reason}`);\n this.name = 'WebhookSignatureError';\n this.reason = reason;\n }\n}\n\n/**\n * Verifies + parses an Evento delivery in one call (Stripe's\n * `constructEvent` naming, same idea): throws `WebhookSignatureError`\n * instead of returning a result a caller might forget to check before\n * trusting `rawBody`.\n */\nexport function constructEventEnvelope<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n): WebhookEventEnvelope<TData> {\n const result = verifyWebhookSignature(params);\n if (!result.ok) throw new WebhookSignatureError(result.reason);\n const body =\n typeof params.rawBody === 'string'\n ? params.rawBody\n : params.rawBody.toString('utf8');\n return JSON.parse(body) as WebhookEventEnvelope<TData>;\n}\n","/**\n * `event.fetch()` — read an Evento's resource in one call, using the same\n * credentials the client was created with. `resource.url` is the absolute\n * `GET` URL the server already computed (`apiPublicUrl()` in\n * `webhook-dispatch.service.ts`), so this is a plain authenticated fetch of\n * that URL — no per-resource-type routing table to keep in sync.\n */\nimport { toApiError } from './errors';\nimport {\n constructEventEnvelope,\n verifyWebhookSignature,\n type VerifySignatureOptions,\n type WebhookEventEnvelope,\n} from './webhooks';\n\nexport class EventResourceUnavailableError extends Error {\n readonly event: WebhookEventEnvelope;\n\n constructor(event: WebhookEventEnvelope) {\n super(\n event.data_omitted\n ? `Event ${event.id} (${event.type}) carries no resource URL — data_omitted: ${event.data_omitted}`\n : `Event ${event.id} (${event.type}) carries no resource URL`,\n );\n this.name = 'EventResourceUnavailableError';\n this.event = event;\n }\n}\n\n/** A parsed Evento, plus a `fetch()` bound to the client it came from. */\nexport type VitrinaEvent<TData = Record<string, unknown>> =\n WebhookEventEnvelope<TData> & {\n /**\n * `GET`s `resource.url` with this client's credentials and returns its\n * `data`. Throws `EventResourceUnavailableError` when the envelope has no\n * URL (an event with no single owning resource, or one whose `data` you\n * already have inline). Pass a type argument to get the resource typed:\n * `await event.fetch<Contact>()`.\n */\n fetch<TResource = TData>(): Promise<TResource>;\n };\n\nexport interface EventFetcherDeps {\n headers: HeadersInit;\n fetch: typeof fetch;\n}\n\n/** Builds the `events` namespace `createClient()` attaches to every client:\n * signature verification bound to nothing (pure functions), and\n * `constructEvent` bound to this client's own auth so the returned event's\n * `.fetch()` needs no arguments. */\nexport function createEventsNamespace(deps: EventFetcherDeps) {\n // `WebhookEventEnvelope<any>`: this helper only ever reads `resource` /\n // `data_omitted` / `id` / `type`, never `.data`, so it is deliberately\n // NOT generic over TData — a `WebhookEventEnvelope<TData>` for any TData\n // must be assignable here.\n async function fetchResource<TResource>(\n event: WebhookEventEnvelope<any>,\n ): Promise<TResource> {\n if (!event.resource.url) throw new EventResourceUnavailableError(event);\n const res = await deps.fetch(event.resource.url, { headers: deps.headers });\n let json: unknown;\n try {\n json = await res.json();\n } catch {\n json = undefined;\n }\n if (!res.ok) {\n throw toApiError(res.status, json, {\n requestUrl: event.resource.url,\n requestMethod: 'GET',\n });\n }\n return (json as { data: TResource })?.data;\n }\n\n return {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature: (\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ) => verifyWebhookSignature(params),\n\n /**\n * Verifies + parses a webhook delivery and returns it with a bound\n * `.fetch()`. Throws `WebhookSignatureError` on a bad/stale signature.\n *\n * const event = client.events.constructEvent({\n * secret: subscriptionSecret,\n * signatureHeader: req.headers['x-webhook-signature'],\n * rawBody: req.rawBody,\n * });\n * if (event.type === 'contact.created') {\n * const contact = await event.fetch<Contact>();\n * }\n */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData> {\n const envelope = constructEventEnvelope<TData>(params);\n return Object.assign(envelope, {\n fetch: <TResource = TData>() => fetchResource<TResource>(envelope),\n });\n },\n };\n}\n","/**\n * `createClient()` — a typed `openapi-fetch` client over the API pública\n * (`paths` generated from the committed `openapi.public.json`), plus the\n * `events` namespace (webhook signature verification + `event.fetch()`).\n *\n * Auth: pass an `sk_*` API key (workspace credential or personal token —\n * both are Bearer) or a raw bearer token you manage yourself. Exactly one of\n * `apiKey` / `bearer`.\n */\nimport createOpenApiClient, { type Client } from 'openapi-fetch';\n\nimport { createEventsNamespace, type VitrinaEvent } from './events';\nimport type { paths } from './generated/api-types';\nimport type { VerifySignatureOptions, VerifyResult } from './webhooks';\n\nexport interface CreateClientOptions {\n /** Absolute base URL, e.g. `https://api.vitrinadev.com/api/v1`. No trailing slash. */\n baseUrl: string;\n /** An `sk_*` API key (workspace key or personal token). Mutually exclusive with `bearer`. */\n apiKey?: string;\n /** A raw bearer token, for a caller that manages tokens itself (e.g. a Supabase session JWT). */\n bearer?: string;\n /** Extra headers merged onto every request (e.g. a custom `User-Agent`). */\n headers?: Record<string, string>;\n /** Override `fetch` — Node <18, undici, msw, a test double. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n}\n\nexport interface VitrinaEventsNamespace {\n /** Verifies an `X-Webhook-Signature` header. Never throws. */\n verifySignature(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VerifyResult;\n /** Verifies + parses a webhook delivery; the returned event carries a\n * `.fetch()` bound to this client. Throws `WebhookSignatureError`. */\n constructEvent<TData = Record<string, unknown>>(\n params: {\n secret: string;\n signatureHeader: string;\n rawBody: string | Buffer;\n } & VerifySignatureOptions,\n ): VitrinaEvent<TData>;\n}\n\n/** The typed REST client (`GET`/`POST`/.../`use`/`eject`, from `openapi-fetch`)\n * plus the `events` namespace. */\nexport type VitrinaClient = Client<paths> & { events: VitrinaEventsNamespace };\n\nexport function createClient(opts: CreateClientOptions): VitrinaClient {\n if (opts.apiKey && opts.bearer) {\n throw new RangeError(\n 'createClient(): pass at most one of `apiKey` / `bearer`, not both',\n );\n }\n const credential = opts.apiKey ?? opts.bearer;\n const headers: Record<string, string> = {\n ...(credential ? { Authorization: `Bearer ${credential}` } : {}),\n ...opts.headers,\n };\n const fetchImpl = opts.fetch ?? fetch;\n\n const base = createOpenApiClient<paths>({\n baseUrl: opts.baseUrl,\n headers,\n fetch: fetchImpl,\n });\n\n const events = createEventsNamespace({ headers, fetch: fetchImpl });\n\n return Object.assign(base, { events });\n}\n","/**\n * Cursor pagination — walks a list endpoint page by page, yielding one item\n * at a time (`for await`). Vitrina's list envelopes carry the cursor as\n * `meta.pagination.nextCursor` (the shape every v1 list route in production\n * answers with — `ok(res, rows, { pagination: { nextCursor, limit } })`,\n * `src/routes/api/v1/*.ts`) with a `meta.nextCursor` fallback for the flat\n * `PaginationMeta` OpenAPI shape (`src/api/openapi-responses.ts`) some\n * generated docs describe; this helper reads either. The opaque cursor\n * itself is a base64url-encoded `{ ts, id }` pair (`src/api/pagination.ts`)\n * — never decode it, just pass it back as the next page's `cursor` query\n * param.\n */\nimport { toApiError } from './errors';\n\nexport interface PaginationMeta {\n pagination?: {\n nextCursor?: string | null;\n prevCursor?: string | null;\n limit?: number;\n };\n nextCursor?: string | null;\n prevCursor?: string | null;\n total?: number;\n limit?: number;\n [key: string]: unknown;\n}\n\nexport interface ListEnvelope<T> {\n data?: T[];\n meta?: PaginationMeta;\n}\n\n/** The subset of an `openapi-fetch` GET call's resolved value `paginate` needs. */\nexport interface FetchPageResult<T> {\n data?: ListEnvelope<T>;\n error?: unknown;\n response: Response;\n}\n\nfunction nextCursorOf(meta: PaginationMeta | undefined): string | undefined {\n const cursor = meta?.pagination?.nextCursor ?? meta?.nextCursor ?? null;\n return cursor ?? undefined;\n}\n\n/**\n * Walks every page of a cursor-paginated list, yielding one item at a time.\n *\n * for await (const appointment of paginate((cursor) =>\n * api.GET('/appointments', { params: { query: { cursor, limit: 100 } } }),\n * )) {\n * console.log(appointment.id);\n * }\n *\n * CURSOR IS THE MINORITY SHAPE, so check the route before reaching for this.\n * Measured against the published contract: exactly two operations take a\n * `cursor` — `GET /appointments` and `GET /conversations/{id}/messages`.\n * Fifteen take `offset`/`limit` (`/stock`, `/vehicles`, `/contacts/search`,\n * `/webhooks/{id}/deliveries` and the rest), and others take `page`/`page_size`\n * (`/leads` among them). Passing `cursor` to one of those is a type error, not\n * a silent first page — which is the point of generating this package from the\n * contract.\n *\n * `fetchPage` is any typed `api.GET(...)` call, called once per page with\n * the previous page's cursor (`undefined` for the first page). Throws a\n * typed `VitrinaApiError` if a page comes back as an error, same as\n * `unwrap()`.\n */\nexport async function* paginate<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): AsyncGenerator<T, void, unknown> {\n let cursor: string | undefined;\n for (;;) {\n // eslint-disable-next-line no-await-in-loop -- pages are inherently sequential (each cursor depends on the last)\n const result = await fetchPage(cursor);\n if (result.error !== undefined) {\n throw toApiError(result.response.status, result.error);\n }\n const page = result.data;\n if (!page || !Array.isArray(page.data)) return;\n for (const item of page.data) yield item;\n const next = nextCursorOf(page.meta);\n if (!next) return;\n cursor = next;\n }\n}\n\n/** Same as `paginate`, but collects every page into one array. Only reach\n * for this on a list you know is bounded — an unbounded one belongs on the\n * `for await` form above. */\nexport async function paginateAll<T>(\n fetchPage: (cursor: string | undefined) => Promise<FetchPageResult<T>>,\n): Promise<T[]> {\n const items: T[] = [];\n // eslint-disable-next-line no-restricted-syntax -- collecting an async generator IS the sequential-await case for-await exists for\n for await (const item of paginate(fetchPage)) items.push(item);\n return items;\n}\n","/**\n * `Idempotency-Key` — replay-safe retries on the 39 published write\n * operations that accept it (ADR 0106 §2.3/#2400). Resending the SAME key\n * with the SAME body returns the original response (`X-Idempotent-Replay:\n * 1`) instead of creating a second copy; the same key with a DIFFERENT body\n * answers `409 IDEMPOTENCY_KEY_CONFLICT` (`VitrinaApiError#isIdempotencyConflict`).\n *\n * The generated types already carry `Idempotency-Key` as a typed header\n * parameter per operation — `idempotencyKey()` just builds a well-formed\n * value to put there:\n *\n * await api.POST('/pipelines', {\n * params: { header: idempotencyKey(`provision-${userId}-2026-04-24`) },\n * body: { ... },\n * });\n */\nimport { randomUUID } from 'node:crypto';\n\nconst KEY_PATTERN = /^[A-Za-z0-9_-]{8,200}$/;\n\nexport interface IdempotencyKeyHeader {\n 'Idempotency-Key': string;\n}\n\n/**\n * Validates `key` against the server's own constraint (`minLength: 8,\n * maxLength: 200, pattern: ^[A-Za-z0-9_-]+$` on every operation that accepts\n * it) and returns it as a header object to spread into `params.header`.\n * Throws `RangeError` on a malformed key — better to fail before the\n * request leaves than to get back a generic `VALIDATION_ERROR`.\n */\nexport function idempotencyKey(key: string): IdempotencyKeyHeader {\n if (!KEY_PATTERN.test(key)) {\n throw new RangeError(\n `Idempotency-Key must be 8-200 characters of [A-Za-z0-9_-], got: ${JSON.stringify(key)}`,\n );\n }\n return { 'Idempotency-Key': key };\n}\n\n/**\n * A fresh, valid `Idempotency-Key`. Mint ONE per logical operation and reuse\n * it across THAT operation's retries — generating a new key per attempt\n * defeats the point (each attempt would look like a different write). `\n * prefix` is optional and purely for your own log-grepping; it plays no\n * role on the server.\n */\nexport function generateIdempotencyKey(prefix?: string): string {\n const id = randomUUID().replace(/-/g, '');\n return prefix ? `${prefix}-${id}` : id;\n}\n"]}
|
package/package.json
CHANGED