@lonca/core 0.1.0 → 0.2.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 +17 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -54,6 +54,22 @@ async function* paginate(fetchPage, params = {}) {
|
|
|
54
54
|
cursor = page.nextCursor;
|
|
55
55
|
} while (cursor);
|
|
56
56
|
}
|
|
57
|
+
async function* paginateOffset(fetchPage, params = {}) {
|
|
58
|
+
const limit = params.limit;
|
|
59
|
+
const maxPages = params.maxPages ?? Infinity;
|
|
60
|
+
let offset = 0;
|
|
61
|
+
let pages = 0;
|
|
62
|
+
while (pages < maxPages) {
|
|
63
|
+
const page = await fetchPage({ offset, limit });
|
|
64
|
+
for (const item of page.items) {
|
|
65
|
+
yield item;
|
|
66
|
+
}
|
|
67
|
+
pages += 1;
|
|
68
|
+
if (page.items.length === 0) return;
|
|
69
|
+
if (page.pageCount > 0 && pages >= page.pageCount) return;
|
|
70
|
+
offset += page.limit || limit || page.items.length;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
57
73
|
|
|
58
74
|
// src/errors.ts
|
|
59
75
|
var LoncaError = class extends Error {
|
|
@@ -292,6 +308,7 @@ exports.moneyFromMajor = moneyFromMajor;
|
|
|
292
308
|
exports.moneyToMajor = moneyToMajor;
|
|
293
309
|
exports.noopLogger = noopLogger;
|
|
294
310
|
exports.paginate = paginate;
|
|
311
|
+
exports.paginateOffset = paginateOffset;
|
|
295
312
|
exports.retry = retry;
|
|
296
313
|
exports.subMoney = subMoney;
|
|
297
314
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/currency.ts","../src/money.ts","../src/pagination.ts","../src/errors.ts","../src/retry.ts","../src/logger.ts","../src/rate-limiter.ts"],"names":[],"mappings":";;;AASO,IAAM,GAAA,GAAM;AACZ,IAAM,GAAA,GAAM;AACZ,IAAM,GAAA,GAAM;AACZ,IAAM,GAAA,GAAM;AAEnB,IAAM,WAAA,GAAc,YAAA;AAEb,SAAS,oBAAoB,IAAA,EAAuB;AACzD,EAAA,OAAO,WAAA,CAAY,KAAK,IAAI,CAAA;AAC9B;;;ACLO,SAAS,KAAA,CAAM,QAAgB,QAAA,EAA2B;AAC/D,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,gDAAA,EAAmD,MAAM,CAAA,CAAE,CAAA;AAAA,EACjF;AACA,EAAA,OAAO,EAAE,QAAQ,QAAA,EAAS;AAC5B;AAEO,SAAS,cAAA,CAAe,KAAA,EAAe,QAAA,EAAoB,UAAA,GAAa,CAAA,EAAU;AACvF,EAAA,MAAM,SAAS,EAAA,IAAM,UAAA;AACrB,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,MAAM,CAAA;AACxC,EAAA,OAAO,KAAA,CAAM,QAAQ,QAAQ,CAAA;AAC/B;AAEO,SAAS,YAAA,CAAa,KAAA,EAAc,UAAA,GAAa,CAAA,EAAW;AACjE,EAAA,OAAO,KAAA,CAAM,SAAS,EAAA,IAAM,UAAA;AAC9B;AAEO,SAAS,cAAA,CAAe,GAAU,CAAA,EAAmB;AAC1D,EAAA,OAAO,CAAA,CAAE,aAAa,CAAA,CAAE,QAAA;AAC1B;AAEO,SAAS,QAAA,CAAS,GAAU,CAAA,EAAiB;AAClD,EAAA,IAAI,CAAC,cAAA,CAAe,CAAA,EAAG,CAAC,CAAA,EAAG;AACzB,IAAA,MAAM,IAAI,UAAU,CAAA,0CAAA,EAA6C,CAAA,CAAE,QAAQ,CAAA,IAAA,EAAO,CAAA,CAAE,QAAQ,CAAA,CAAE,CAAA;AAAA,EAChG;AACA,EAAA,OAAO,MAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAA,EAAQ,EAAE,QAAQ,CAAA;AAC9C;AAEO,SAAS,QAAA,CAAS,GAAU,CAAA,EAAiB;AAClD,EAAA,IAAI,CAAC,cAAA,CAAe,CAAA,EAAG,CAAC,CAAA,EAAG;AACzB,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,CAAA,+CAAA,EAAkD,CAAA,CAAE,QAAQ,CAAA,IAAA,EAAO,EAAE,QAAQ,CAAA;AAAA,KAC/E;AAAA,EACF;AACA,EAAA,OAAO,MAAM,CAAA,CAAE,MAAA,GAAS,CAAA,CAAE,MAAA,EAAQ,EAAE,QAAQ,CAAA;AAC9C;;;AC3BA,gBAAuB,QAAA,CACrB,SAAA,EACA,MAAA,GAAiD,EAAC,EACd;AACpC,EAAA,IAAI,MAAA;AACJ,EAAA,GAAG;AACD,IAAA,MAAM,OAAO,MAAM,SAAA,CAAU,EAAE,GAAG,MAAA,EAAQ,QAAQ,CAAA;AAClD,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,KAAA,EAAO;AAC7B,MAAA,MAAM,IAAA;AAAA,IACR;AACA,IAAA,MAAA,GAAS,IAAA,CAAK,UAAA;AAAA,EAChB,CAAA,QAAS,MAAA;AACX;;;ACPO,IAAM,UAAA,GAAN,cAAyB,KAAA,CAAM;AAAA,EAC3B,IAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,IAAA;AAAA,EAET,YAAY,IAAA,EAAyB;AACnC,IAAA,KAAA,CAAM,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAM,GAAI,MAAS,CAAA;AAChF,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,SAAA,GAAY,KAAK,SAAA,IAAa,KAAA;AACnC,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,eAAe,IAAA,CAAK,YAAA;AACzB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AAAA,EACnB;AACF;AAIO,IAAM,SAAA,GAAN,cAAwB,UAAA,CAAW;AAAA,EACxC,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,aAAA,EAAe,SAAA,EAAW,OAAO,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,WAAA;AAAA,EACd;AACF;AAEO,IAAM,cAAA,GAAN,cAA6B,UAAA,CAAW;AAAA,EAC7C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,cAAA,EAAgB,SAAA,EAAW,MAAM,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EACd;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,UAAA,CAAW;AAAA,EAC9C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,mBAAA,EAAqB,SAAA,EAAW,OAAO,CAAA;AAC9D,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;AAEO,IAAM,aAAA,GAAN,cAA4B,UAAA,CAAW;AAAA,EAC5C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,WAAA,EAAa,SAAA,EAAW,OAAO,CAAA;AACtD,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AAAA,EACd;AACF;AAEO,IAAM,WAAA,GAAN,cAA0B,UAAA,CAAW;AAAA,EAC1C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,cAAA,EAAgB,SAAA,EAAW,MAAM,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF;AAEO,IAAM,YAAA,GAAN,cAA2B,UAAA,CAAW;AAAA,EAC3C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,eAAA,EAAiB,SAAA,EAAW,MAAM,CAAA;AACzD,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAAA,EACd;AACF;AAEO,IAAM,YAAA,GAAN,cAA2B,UAAA,CAAW;AAAA,EAC3C,YAAY,IAAA,EAAuB;AACjC,IAAA,KAAA,CAAM,EAAE,GAAG,IAAA,EAAM,MAAM,SAAA,EAAW,SAAA,EAAW,MAAM,CAAA;AACnD,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAAA,EACd;AACF;AAEO,SAAS,aAAa,KAAA,EAAqC;AAChE,EAAA,OAAO,KAAA,YAAiB,UAAA;AAC1B;AAEO,SAAS,iBAAiB,KAAA,EAAyB;AACxD,EAAA,OAAO,YAAA,CAAa,KAAK,CAAA,IAAK,KAAA,CAAM,SAAA;AACtC;;;AClFA,IAAM,QAAA,GAAW;AAAA,EACf,WAAA,EAAa,CAAA;AAAA,EACb,WAAA,EAAa,GAAA;AAAA,EACb,UAAA,EAAY,GAAA;AAAA,EACZ,MAAA,EAAQ;AACV,CAAA;AAEA,SAAS,KAAA,CAAM,IAAY,MAAA,EAAqC;AAC9D,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAA,CAAO,OAAO,MAAM,CAAA;AACpB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,EAAS,EAAE,CAAA;AACpC,IAAA,MAAA,EAAQ,gBAAA;AAAA,MACN,OAAA;AAAA,MACA,MAAM;AACJ,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,MAAA,CAAO,OAAO,MAAM,CAAA;AAAA,MACtB,CAAA;AAAA,MACA,EAAE,MAAM,IAAA;AAAK,KACf;AAAA,EACF,CAAC,CAAA;AACH;AAEA,eAAsB,KAAA,CACpB,EAAA,EACA,OAAA,GAAwB,EAAC,EACb;AACZ,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,QAAA,CAAS,WAAA;AACpD,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,QAAA,CAAS,WAAA;AACpD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,UAAA,IAAc,QAAA,CAAS,UAAA;AAClD,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,QAAA,CAAS,MAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,QAAQ,WAAA,IAAe,gBAAA;AAE3C,EAAA,IAAI,OAAA;AACJ,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,WAAA,EAAa,OAAA,EAAA,EAAW;AACvD,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,GAAG,OAAO,CAAA;AAAA,IACzB,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,GAAU,GAAA;AACV,MAAA,IAAI,OAAA,KAAY,WAAA,IAAe,CAAC,WAAA,CAAY,GAAG,CAAA,EAAG;AAChD,QAAA,MAAM,GAAA;AAAA,MACR;AAEA,MAAA,MAAM,YAAA,GAAe,YAAA,CAAa,GAAG,CAAA,GAAI,IAAI,YAAA,GAAe,MAAA;AAC5D,MAAA,MAAM,cAAc,IAAA,CAAK,GAAA,CAAI,YAAY,WAAA,GAAc,CAAA,KAAM,UAAU,CAAA,CAAE,CAAA;AACzE,MAAA,MAAM,OAAO,YAAA,IAAgB,WAAA;AAC7B,MAAA,MAAM,WAAW,MAAA,GAAS,IAAA,CAAK,MAAA,EAAO,GAAI,OAAO,GAAA,GAAM,CAAA;AACvD,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,OAAO,QAAQ,CAAA;AAElD,MAAA,OAAA,CAAQ,OAAA,GAAU,GAAA,EAAK,OAAA,EAAS,KAAK,CAAA;AACrC,MAAA,MAAM,KAAA,CAAM,KAAA,EAAO,OAAA,CAAQ,MAAM,CAAA;AAAA,IACnC;AAAA,EACF;AACA,EAAA,MAAM,OAAA;AACR;;;AC9DO,IAAM,UAAA,GAAqB;AAAA,EAChC,OAAO,MAAM;AAAA,EAAC,CAAA;AAAA,EACd,MAAM,MAAM;AAAA,EAAC,CAAA;AAAA,EACb,MAAM,MAAM;AAAA,EAAC,CAAA;AAAA,EACb,OAAO,MAAM;AAAA,EAAC,CAAA;AAAA,EACd,OAAO,MAAM;AACf;AAOO,SAAS,aAAA,CAAc,QAAA,GAAuB,EAAC,EAAW;AAC/D,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,EAAiB,OAAA,EAAiB,OAAA,KAA+B;AAC7E,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,KAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MAClC,GAAG,QAAA;AAAA,MACH,GAAG;AAAA,KACL;AACA,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AACjC,IAAA,IAAI,KAAA,KAAU,OAAA,IAAW,KAAA,KAAU,MAAA,EAAQ;AACzC,MAAA,OAAA,CAAQ,MAAM,IAAI,CAAA;AAAA,IACpB,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAAA,IAClB;AAAA,EACF,CAAA;AACA,EAAA,OAAO;AAAA,IACL,OAAO,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,OAAA,EAAS,GAAG,CAAC,CAAA;AAAA,IACnC,MAAM,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,MAAA,EAAQ,GAAG,CAAC,CAAA;AAAA,IACjC,MAAM,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,MAAA,EAAQ,GAAG,CAAC,CAAA;AAAA,IACjC,OAAO,CAAC,CAAA,EAAG,MAAM,IAAA,CAAK,OAAA,EAAS,GAAG,CAAC,CAAA;AAAA,IACnC,KAAA,EAAO,CAAC,CAAA,KAAM,aAAA,CAAc,EAAE,GAAG,QAAA,EAAU,GAAG,CAAA,EAAG;AAAA,GACnD;AACF;;;AC/BO,IAAM,yBAAN,MAA6B;AAAA,EAC1B,MAAA;AAAA,EACA,YAAA;AAAA,EACS,QAAA;AAAA,EACA,UAAA;AAAA,EAEjB,YAAY,OAAA,EAA6B;AACvC,IAAA,IAAI,OAAA,CAAQ,QAAA,IAAY,CAAA,IAAK,OAAA,CAAQ,cAAc,CAAA,EAAG;AACpD,MAAA,MAAM,IAAI,WAAW,qCAAqC,CAAA;AAAA,IAC5D;AACA,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AACxB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,QAAA;AACtB,IAAA,IAAA,CAAK,YAAA,GAAe,KAAK,GAAA,EAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAA,GAA6B;AACnC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,YAAA;AAC3B,IAAA,IAAI,UAAU,CAAA,EAAG;AACf,MAAA,MAAM,MAAA,GAAU,OAAA,GAAU,IAAA,CAAK,UAAA,GAAc,IAAA,CAAK,QAAA;AAClD,MAAA,IAAA,CAAK,SAAS,IAAA,CAAK,GAAA,CAAI,KAAK,QAAA,EAAU,IAAA,CAAK,SAAS,MAAM,CAAA;AAC1D,MAAA,IAAA,CAAK,YAAA,GAAe,GAAA;AAAA,IACtB;AACA,IAAA,IAAI,IAAA,CAAK,MAAA,IAAU,CAAA,EAAG,OAAO,CAAA;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,MAAA;AACxB,IAAA,OAAO,KAAK,IAAA,CAAM,MAAA,GAAS,IAAA,CAAK,QAAA,GAAY,KAAK,UAAU,CAAA;AAAA,EAC7D;AAAA,EAEA,MAAM,QAAQ,MAAA,EAAqC;AACjD,IAAA,WAAS;AACP,MAAA,IAAI,MAAA,EAAQ,OAAA,EAAS,MAAM,MAAA,CAAO,MAAA;AAClC,MAAA,MAAM,IAAA,GAAO,KAAK,kBAAA,EAAmB;AACrC,MAAA,IAAI,SAAS,CAAA,EAAG;AACd,QAAA,IAAA,CAAK,MAAA,IAAU,CAAA;AACf,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,QAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,EAAS,IAAI,CAAA;AACtC,QAAA,MAAA,EAAQ,gBAAA;AAAA,UACN,OAAA;AAAA,UACA,MAAM;AACJ,YAAA,YAAA,CAAa,KAAK,CAAA;AAClB,YAAA,MAAA,CAAO,OAAO,MAAM,CAAA;AAAA,UACtB,CAAA;AAAA,UACA,EAAE,MAAM,IAAA;AAAK,SACf;AAAA,MACF,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAAA;AAAA,EAGA,SAAA,GAAoB;AAClB,IAAA,IAAA,CAAK,kBAAA,EAAmB;AACxB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF","file":"index.cjs","sourcesContent":["/**\n * ISO 4217 three-letter currency code.\n *\n * Typed as a string for flexibility — Trendyol and similar marketplaces may\n * expose currencies beyond the common set, so we don't lock down to a union.\n * Use `isValidCurrencyCode` for runtime validation.\n */\nexport type Currency = string;\n\nexport const TRY = 'TRY';\nexport const USD = 'USD';\nexport const EUR = 'EUR';\nexport const GBP = 'GBP';\n\nconst ISO_4217_RE = /^[A-Z]{3}$/;\n\nexport function isValidCurrencyCode(code: string): boolean {\n return ISO_4217_RE.test(code);\n}\n","import type { Currency } from './currency.js';\n\n/**\n * A monetary amount in integer minor units (e.g., kuruş for TRY, cents for USD).\n *\n * Using integers avoids floating-point surprises. Convert to/from major units\n * with `moneyFromMajor` and `moneyToMajor`.\n */\nexport interface Money {\n amount: number;\n currency: Currency;\n}\n\nexport function money(amount: number, currency: Currency): Money {\n if (!Number.isInteger(amount)) {\n throw new TypeError(`Money amount must be an integer minor unit, got ${amount}`);\n }\n return { amount, currency };\n}\n\nexport function moneyFromMajor(major: number, currency: Currency, minorScale = 2): Money {\n const factor = 10 ** minorScale;\n const amount = Math.round(major * factor);\n return money(amount, currency);\n}\n\nexport function moneyToMajor(value: Money, minorScale = 2): number {\n return value.amount / 10 ** minorScale;\n}\n\nexport function isSameCurrency(a: Money, b: Money): boolean {\n return a.currency === b.currency;\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(`Cannot add Money of different currencies: ${a.currency} vs ${b.currency}`);\n }\n return money(a.amount + b.amount, a.currency);\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(\n `Cannot subtract Money of different currencies: ${a.currency} vs ${b.currency}`,\n );\n }\n return money(a.amount - b.amount, a.currency);\n}\n","export interface CursorPaginationParams {\n /** Opaque cursor from previous response's `nextCursor`. Omit for the first page. */\n cursor?: string;\n /** Maximum items in the page. Marketplace SDKs may clamp this to their own bounds. */\n limit?: number;\n}\n\nexport interface CursorPage<T> {\n items: T[];\n /** Opaque cursor for the next page. Absent when there are no more pages. */\n nextCursor?: string;\n}\n\n/**\n * Drive a cursor-paginated endpoint as an async iterator.\n *\n * @example\n * for await (const order of paginate((p) => client.orders.list(p))) {\n * console.log(order.id);\n * }\n */\nexport async function* paginate<T>(\n fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>,\n params: Omit<CursorPaginationParams, 'cursor'> = {},\n): AsyncGenerator<T, void, undefined> {\n let cursor: string | undefined;\n do {\n const page = await fetchPage({ ...params, cursor });\n for (const item of page.items) {\n yield item;\n }\n cursor = page.nextCursor;\n } while (cursor);\n}\n","export type LoncaErrorCode =\n | 'AUTH_FAILED'\n | 'RATE_LIMITED'\n | 'VALIDATION_FAILED'\n | 'NOT_FOUND'\n | 'SERVER_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'UNKNOWN';\n\nexport interface LoncaErrorOptions {\n code: LoncaErrorCode;\n message: string;\n cause?: unknown;\n retryable?: boolean;\n status?: number;\n retryAfterMs?: number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Root of the Lonca error hierarchy.\n *\n * Carries a tagged `code` and a `retryable` boolean so retry helpers\n * can decide what to do without sniffing message strings.\n */\nexport class LoncaError extends Error {\n readonly code: LoncaErrorCode;\n readonly retryable: boolean;\n readonly status?: number;\n readonly retryAfterMs?: number;\n readonly data?: Record<string, unknown>;\n\n constructor(opts: LoncaErrorOptions) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = 'LoncaError';\n this.code = opts.code;\n this.retryable = opts.retryable ?? false;\n this.status = opts.status;\n this.retryAfterMs = opts.retryAfterMs;\n this.data = opts.data;\n }\n}\n\ntype SubclassOptions = Omit<LoncaErrorOptions, 'code' | 'retryable'>;\n\nexport class AuthError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'AUTH_FAILED', retryable: false });\n this.name = 'AuthError';\n }\n}\n\nexport class RateLimitError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'RATE_LIMITED', retryable: true });\n this.name = 'RateLimitError';\n }\n}\n\nexport class ValidationError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'VALIDATION_FAILED', retryable: false });\n this.name = 'ValidationError';\n }\n}\n\nexport class NotFoundError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NOT_FOUND', retryable: false });\n this.name = 'NotFoundError';\n }\n}\n\nexport class ServerError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'SERVER_ERROR', retryable: true });\n this.name = 'ServerError';\n }\n}\n\nexport class NetworkError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NETWORK_ERROR', retryable: true });\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'TIMEOUT', retryable: true });\n this.name = 'TimeoutError';\n }\n}\n\nexport function isLoncaError(value: unknown): value is LoncaError {\n return value instanceof LoncaError;\n}\n\nexport function isRetryableError(value: unknown): boolean {\n return isLoncaError(value) && value.retryable;\n}\n","import { isLoncaError, isRetryableError } from './errors.js';\n\nexport interface RetryOptions {\n /** Total number of attempts including the initial call. Defaults to 3. */\n maxAttempts?: number;\n /** Initial delay before the second attempt, in ms. Defaults to 200. */\n baseDelayMs?: number;\n /** Upper bound on delay between any two attempts, in ms. Defaults to 10_000. */\n maxDelayMs?: number;\n /** Add up to 50% random jitter on each delay. Defaults to true. */\n jitter?: boolean;\n /** Override the default predicate. Default treats `retryable` LoncaErrors as retryable. */\n isRetryable?: (err: unknown) => boolean;\n /** Called before each retry sleep. Useful for structured logging. */\n onRetry?: (err: unknown, attempt: number, nextDelayMs: number) => void;\n /** Abort all pending sleeps and bubble the signal's reason. */\n signal?: AbortSignal;\n}\n\nconst DEFAULTS = {\n maxAttempts: 3,\n baseDelayMs: 200,\n maxDelayMs: 10_000,\n jitter: true,\n} as const;\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n}\n\nexport async function retry<T>(\n fn: (attempt: number) => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULTS.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULTS.maxDelayMs;\n const jitter = options.jitter ?? DEFAULTS.jitter;\n const isRetryable = options.isRetryable ?? isRetryableError;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === maxAttempts || !isRetryable(err)) {\n throw err;\n }\n\n const retryAfterMs = isLoncaError(err) ? err.retryAfterMs : undefined;\n const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));\n const base = retryAfterMs ?? exponential;\n const jitterMs = jitter ? Math.random() * base * 0.5 : 0;\n const delay = Math.min(maxDelayMs, base + jitterMs);\n\n options.onRetry?.(err, attempt, delay);\n await sleep(delay, options.signal);\n }\n }\n throw lastErr;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type LogContext = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, context?: LogContext): void;\n info(message: string, context?: LogContext): void;\n warn(message: string, context?: LogContext): void;\n error(message: string, context?: LogContext): void;\n /** Create a child logger that merges `bindings` into every log entry. */\n child(bindings: LogContext): Logger;\n}\n\nexport const noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n child: () => noopLogger,\n};\n\n/**\n * Minimal JSON-line logger to stdout/stderr. Suitable for development and\n * containerized environments. For production prefer wiring a real logger\n * (pino, winston) by implementing the `Logger` interface.\n */\nexport function consoleLogger(bindings: LogContext = {}): Logger {\n const emit = (level: LogLevel, message: string, context?: LogContext): void => {\n const entry = {\n level,\n message,\n timestamp: new Date().toISOString(),\n ...bindings,\n ...context,\n };\n const line = JSON.stringify(entry);\n if (level === 'error' || level === 'warn') {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => emit('debug', m, c),\n info: (m, c) => emit('info', m, c),\n warn: (m, c) => emit('warn', m, c),\n error: (m, c) => emit('error', m, c),\n child: (b) => consoleLogger({ ...bindings, ...b }),\n };\n}\n","export interface RateLimiterOptions {\n /** Maximum number of tokens the bucket holds. Each `acquire()` consumes 1. */\n capacity: number;\n /** Time window in milliseconds over which `capacity` tokens are refilled. */\n intervalMs: number;\n}\n\n/**\n * Token-bucket rate limiter. `acquire()` returns immediately when a token\n * is available, otherwise it sleeps until enough tokens have been refilled.\n *\n * @example\n * const limiter = new TokenBucketRateLimiter({ capacity: 50, intervalMs: 60_000 });\n * for (const item of items) {\n * await limiter.acquire();\n * await fetchSomething(item);\n * }\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private lastRefillAt: number;\n private readonly capacity: number;\n private readonly intervalMs: number;\n\n constructor(options: RateLimiterOptions) {\n if (options.capacity <= 0 || options.intervalMs <= 0) {\n throw new RangeError('capacity and intervalMs must be > 0');\n }\n this.capacity = options.capacity;\n this.intervalMs = options.intervalMs;\n this.tokens = options.capacity;\n this.lastRefillAt = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time, then return ms to wait until the next\n * token is available (0 if one is available right now).\n */\n private timeUntilNextToken(): number {\n const now = Date.now();\n const elapsed = now - this.lastRefillAt;\n if (elapsed > 0) {\n const refill = (elapsed / this.intervalMs) * this.capacity;\n this.tokens = Math.min(this.capacity, this.tokens + refill);\n this.lastRefillAt = now;\n }\n if (this.tokens >= 1) return 0;\n const needed = 1 - this.tokens;\n return Math.ceil((needed / this.capacity) * this.intervalMs);\n }\n\n async acquire(signal?: AbortSignal): Promise<void> {\n for (;;) {\n if (signal?.aborted) throw signal.reason as Error;\n const wait = this.timeUntilNextToken();\n if (wait === 0) {\n this.tokens -= 1;\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, wait);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n }\n }\n\n /** Snapshot of available tokens (refills the bucket as a side effect). */\n available(): number {\n this.timeUntilNextToken();\n return this.tokens;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/currency.ts","../src/money.ts","../src/pagination.ts","../src/errors.ts","../src/retry.ts","../src/logger.ts","../src/rate-limiter.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ISO 4217 three-letter currency code.\n *\n * Typed as a string for flexibility — Trendyol and similar marketplaces may\n * expose currencies beyond the common set, so we don't lock down to a union.\n * Use `isValidCurrencyCode` for runtime validation.\n */\nexport type Currency = string;\n\nexport const TRY = 'TRY';\nexport const USD = 'USD';\nexport const EUR = 'EUR';\nexport const GBP = 'GBP';\n\nconst ISO_4217_RE = /^[A-Z]{3}$/;\n\nexport function isValidCurrencyCode(code: string): boolean {\n return ISO_4217_RE.test(code);\n}\n","import type { Currency } from './currency.js';\n\n/**\n * A monetary amount in integer minor units (e.g., kuruş for TRY, cents for USD).\n *\n * Using integers avoids floating-point surprises. Convert to/from major units\n * with `moneyFromMajor` and `moneyToMajor`.\n */\nexport interface Money {\n amount: number;\n currency: Currency;\n}\n\nexport function money(amount: number, currency: Currency): Money {\n if (!Number.isInteger(amount)) {\n throw new TypeError(`Money amount must be an integer minor unit, got ${amount}`);\n }\n return { amount, currency };\n}\n\nexport function moneyFromMajor(major: number, currency: Currency, minorScale = 2): Money {\n const factor = 10 ** minorScale;\n const amount = Math.round(major * factor);\n return money(amount, currency);\n}\n\nexport function moneyToMajor(value: Money, minorScale = 2): number {\n return value.amount / 10 ** minorScale;\n}\n\nexport function isSameCurrency(a: Money, b: Money): boolean {\n return a.currency === b.currency;\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(`Cannot add Money of different currencies: ${a.currency} vs ${b.currency}`);\n }\n return money(a.amount + b.amount, a.currency);\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(\n `Cannot subtract Money of different currencies: ${a.currency} vs ${b.currency}`,\n );\n }\n return money(a.amount - b.amount, a.currency);\n}\n","export interface CursorPaginationParams {\n /** Opaque cursor from previous response's `nextCursor`. Omit for the first page. */\n cursor?: string;\n /** Maximum items in the page. Marketplace SDKs may clamp this to their own bounds. */\n limit?: number;\n}\n\nexport interface CursorPage<T> {\n items: T[];\n /** Opaque cursor for the next page. Absent when there are no more pages. */\n nextCursor?: string;\n}\n\n/**\n * Drive a cursor-paginated endpoint as an async iterator.\n *\n * @example\n * for await (const order of paginate((p) => client.orders.list(p))) {\n * console.log(order.id);\n * }\n */\nexport async function* paginate<T>(\n fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>,\n params: Omit<CursorPaginationParams, 'cursor'> = {},\n): AsyncGenerator<T, void, undefined> {\n let cursor: string | undefined;\n do {\n const page = await fetchPage({ ...params, cursor });\n for (const item of page.items) {\n yield item;\n }\n cursor = page.nextCursor;\n } while (cursor);\n}\n\n// ─── Offset pagination ─────────────────────────────────────────────────────\n\nexport interface OffsetPaginationParams {\n /** Zero-based offset. Default depends on the SDK; usually `0`. */\n offset?: number;\n /** Maximum items in the page. */\n limit?: number;\n}\n\n/**\n * Offset-based pagination envelope returned by marketplaces that expose\n * `{ totalCount, limit, offset, pageCount, items[] }` instead of opaque\n * cursors. Hepsiburada's OMS uses this shape; some legacy Trendyol\n * endpoints do too. Distinct from `CursorPage<T>` so callers can tell at\n * the type level which pagination model an endpoint uses.\n */\nexport interface OffsetPage<T> {\n /** Total number of items matching the query across all pages. */\n totalCount: number;\n /** Echo of the request `limit`. */\n limit: number;\n /** Echo of the request `offset`. */\n offset: number;\n /** Total number of pages at the requested `limit`. */\n pageCount: number;\n /** Page rows. */\n items: T[];\n}\n\n/**\n * Drive an offset-paginated endpoint as an async iterator. Stops when the\n * server's `pageCount` is exhausted (or after the configured `maxPages`\n * to bound runaway iteration). Default page size is whatever the\n * underlying endpoint clamps to — pass `limit` explicitly for control.\n *\n * @example\n * for await (const order of paginateOffset((p) =>\n * client.orders.list(p),\n * )) {\n * console.log(order.orderNumber);\n * }\n */\nexport async function* paginateOffset<T>(\n fetchPage: (params: OffsetPaginationParams) => Promise<OffsetPage<T>>,\n params: { limit?: number; maxPages?: number } = {},\n): AsyncGenerator<T, void, undefined> {\n const limit = params.limit;\n const maxPages = params.maxPages ?? Infinity;\n let offset = 0;\n let pages = 0;\n while (pages < maxPages) {\n const page = await fetchPage({ offset, limit });\n for (const item of page.items) {\n yield item;\n }\n pages += 1;\n if (page.items.length === 0) return;\n if (page.pageCount > 0 && pages >= page.pageCount) return;\n // Step by the server-echoed limit (when present) so requests stay aligned with how\n // the server batches; fall back to the caller's `limit` or the observed batch size.\n offset += page.limit || limit || page.items.length;\n }\n}\n","export type LoncaErrorCode =\n | 'AUTH_FAILED'\n | 'RATE_LIMITED'\n | 'VALIDATION_FAILED'\n | 'NOT_FOUND'\n | 'SERVER_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'UNKNOWN';\n\nexport interface LoncaErrorOptions {\n code: LoncaErrorCode;\n message: string;\n cause?: unknown;\n retryable?: boolean;\n status?: number;\n retryAfterMs?: number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Root of the Lonca error hierarchy.\n *\n * Carries a tagged `code` and a `retryable` boolean so retry helpers\n * can decide what to do without sniffing message strings.\n */\nexport class LoncaError extends Error {\n readonly code: LoncaErrorCode;\n readonly retryable: boolean;\n readonly status?: number;\n readonly retryAfterMs?: number;\n readonly data?: Record<string, unknown>;\n\n constructor(opts: LoncaErrorOptions) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = 'LoncaError';\n this.code = opts.code;\n this.retryable = opts.retryable ?? false;\n this.status = opts.status;\n this.retryAfterMs = opts.retryAfterMs;\n this.data = opts.data;\n }\n}\n\ntype SubclassOptions = Omit<LoncaErrorOptions, 'code' | 'retryable'>;\n\nexport class AuthError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'AUTH_FAILED', retryable: false });\n this.name = 'AuthError';\n }\n}\n\nexport class RateLimitError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'RATE_LIMITED', retryable: true });\n this.name = 'RateLimitError';\n }\n}\n\nexport class ValidationError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'VALIDATION_FAILED', retryable: false });\n this.name = 'ValidationError';\n }\n}\n\nexport class NotFoundError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NOT_FOUND', retryable: false });\n this.name = 'NotFoundError';\n }\n}\n\nexport class ServerError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'SERVER_ERROR', retryable: true });\n this.name = 'ServerError';\n }\n}\n\nexport class NetworkError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NETWORK_ERROR', retryable: true });\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'TIMEOUT', retryable: true });\n this.name = 'TimeoutError';\n }\n}\n\nexport function isLoncaError(value: unknown): value is LoncaError {\n return value instanceof LoncaError;\n}\n\nexport function isRetryableError(value: unknown): boolean {\n return isLoncaError(value) && value.retryable;\n}\n","import { isLoncaError, isRetryableError } from './errors.js';\n\nexport interface RetryOptions {\n /** Total number of attempts including the initial call. Defaults to 3. */\n maxAttempts?: number;\n /** Initial delay before the second attempt, in ms. Defaults to 200. */\n baseDelayMs?: number;\n /** Upper bound on delay between any two attempts, in ms. Defaults to 10_000. */\n maxDelayMs?: number;\n /** Add up to 50% random jitter on each delay. Defaults to true. */\n jitter?: boolean;\n /** Override the default predicate. Default treats `retryable` LoncaErrors as retryable. */\n isRetryable?: (err: unknown) => boolean;\n /** Called before each retry sleep. Useful for structured logging. */\n onRetry?: (err: unknown, attempt: number, nextDelayMs: number) => void;\n /** Abort all pending sleeps and bubble the signal's reason. */\n signal?: AbortSignal;\n}\n\nconst DEFAULTS = {\n maxAttempts: 3,\n baseDelayMs: 200,\n maxDelayMs: 10_000,\n jitter: true,\n} as const;\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n}\n\nexport async function retry<T>(\n fn: (attempt: number) => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULTS.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULTS.maxDelayMs;\n const jitter = options.jitter ?? DEFAULTS.jitter;\n const isRetryable = options.isRetryable ?? isRetryableError;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === maxAttempts || !isRetryable(err)) {\n throw err;\n }\n\n const retryAfterMs = isLoncaError(err) ? err.retryAfterMs : undefined;\n const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));\n const base = retryAfterMs ?? exponential;\n const jitterMs = jitter ? Math.random() * base * 0.5 : 0;\n const delay = Math.min(maxDelayMs, base + jitterMs);\n\n options.onRetry?.(err, attempt, delay);\n await sleep(delay, options.signal);\n }\n }\n throw lastErr;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type LogContext = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, context?: LogContext): void;\n info(message: string, context?: LogContext): void;\n warn(message: string, context?: LogContext): void;\n error(message: string, context?: LogContext): void;\n /** Create a child logger that merges `bindings` into every log entry. */\n child(bindings: LogContext): Logger;\n}\n\nexport const noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n child: () => noopLogger,\n};\n\n/**\n * Minimal JSON-line logger to stdout/stderr. Suitable for development and\n * containerized environments. For production prefer wiring a real logger\n * (pino, winston) by implementing the `Logger` interface.\n */\nexport function consoleLogger(bindings: LogContext = {}): Logger {\n const emit = (level: LogLevel, message: string, context?: LogContext): void => {\n const entry = {\n level,\n message,\n timestamp: new Date().toISOString(),\n ...bindings,\n ...context,\n };\n const line = JSON.stringify(entry);\n if (level === 'error' || level === 'warn') {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => emit('debug', m, c),\n info: (m, c) => emit('info', m, c),\n warn: (m, c) => emit('warn', m, c),\n error: (m, c) => emit('error', m, c),\n child: (b) => consoleLogger({ ...bindings, ...b }),\n };\n}\n","export interface RateLimiterOptions {\n /** Maximum number of tokens the bucket holds. Each `acquire()` consumes 1. */\n capacity: number;\n /** Time window in milliseconds over which `capacity` tokens are refilled. */\n intervalMs: number;\n}\n\n/**\n * Token-bucket rate limiter. `acquire()` returns immediately when a token\n * is available, otherwise it sleeps until enough tokens have been refilled.\n *\n * @example\n * const limiter = new TokenBucketRateLimiter({ capacity: 50, intervalMs: 60_000 });\n * for (const item of items) {\n * await limiter.acquire();\n * await fetchSomething(item);\n * }\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private lastRefillAt: number;\n private readonly capacity: number;\n private readonly intervalMs: number;\n\n constructor(options: RateLimiterOptions) {\n if (options.capacity <= 0 || options.intervalMs <= 0) {\n throw new RangeError('capacity and intervalMs must be > 0');\n }\n this.capacity = options.capacity;\n this.intervalMs = options.intervalMs;\n this.tokens = options.capacity;\n this.lastRefillAt = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time, then return ms to wait until the next\n * token is available (0 if one is available right now).\n */\n private timeUntilNextToken(): number {\n const now = Date.now();\n const elapsed = now - this.lastRefillAt;\n if (elapsed > 0) {\n const refill = (elapsed / this.intervalMs) * this.capacity;\n this.tokens = Math.min(this.capacity, this.tokens + refill);\n this.lastRefillAt = now;\n }\n if (this.tokens >= 1) return 0;\n const needed = 1 - this.tokens;\n return Math.ceil((needed / this.capacity) * this.intervalMs);\n }\n\n async acquire(signal?: AbortSignal): Promise<void> {\n for (;;) {\n if (signal?.aborted) throw signal.reason as Error;\n const wait = this.timeUntilNextToken();\n if (wait === 0) {\n this.tokens -= 1;\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, wait);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n }\n }\n\n /** Snapshot of available tokens (refills the bucket as a side effect). */\n available(): number {\n this.timeUntilNextToken();\n return this.tokens;\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -49,6 +49,48 @@ interface CursorPage<T> {
|
|
|
49
49
|
* }
|
|
50
50
|
*/
|
|
51
51
|
declare function paginate<T>(fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>, params?: Omit<CursorPaginationParams, 'cursor'>): AsyncGenerator<T, void, undefined>;
|
|
52
|
+
interface OffsetPaginationParams {
|
|
53
|
+
/** Zero-based offset. Default depends on the SDK; usually `0`. */
|
|
54
|
+
offset?: number;
|
|
55
|
+
/** Maximum items in the page. */
|
|
56
|
+
limit?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Offset-based pagination envelope returned by marketplaces that expose
|
|
60
|
+
* `{ totalCount, limit, offset, pageCount, items[] }` instead of opaque
|
|
61
|
+
* cursors. Hepsiburada's OMS uses this shape; some legacy Trendyol
|
|
62
|
+
* endpoints do too. Distinct from `CursorPage<T>` so callers can tell at
|
|
63
|
+
* the type level which pagination model an endpoint uses.
|
|
64
|
+
*/
|
|
65
|
+
interface OffsetPage<T> {
|
|
66
|
+
/** Total number of items matching the query across all pages. */
|
|
67
|
+
totalCount: number;
|
|
68
|
+
/** Echo of the request `limit`. */
|
|
69
|
+
limit: number;
|
|
70
|
+
/** Echo of the request `offset`. */
|
|
71
|
+
offset: number;
|
|
72
|
+
/** Total number of pages at the requested `limit`. */
|
|
73
|
+
pageCount: number;
|
|
74
|
+
/** Page rows. */
|
|
75
|
+
items: T[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Drive an offset-paginated endpoint as an async iterator. Stops when the
|
|
79
|
+
* server's `pageCount` is exhausted (or after the configured `maxPages`
|
|
80
|
+
* to bound runaway iteration). Default page size is whatever the
|
|
81
|
+
* underlying endpoint clamps to — pass `limit` explicitly for control.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* for await (const order of paginateOffset((p) =>
|
|
85
|
+
* client.orders.list(p),
|
|
86
|
+
* )) {
|
|
87
|
+
* console.log(order.orderNumber);
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
declare function paginateOffset<T>(fetchPage: (params: OffsetPaginationParams) => Promise<OffsetPage<T>>, params?: {
|
|
91
|
+
limit?: number;
|
|
92
|
+
maxPages?: number;
|
|
93
|
+
}): AsyncGenerator<T, void, undefined>;
|
|
52
94
|
|
|
53
95
|
type LoncaErrorCode = 'AUTH_FAILED' | 'RATE_LIMITED' | 'VALIDATION_FAILED' | 'NOT_FOUND' | 'SERVER_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
54
96
|
interface LoncaErrorOptions {
|
|
@@ -168,4 +210,4 @@ declare class TokenBucketRateLimiter {
|
|
|
168
210
|
available(): number;
|
|
169
211
|
}
|
|
170
212
|
|
|
171
|
-
export { AuthError, type Currency, type CursorPage, type CursorPaginationParams, EUR, GBP, type LogContext, type LogLevel, type Logger, LoncaError, type LoncaErrorCode, type LoncaErrorOptions, type Money, NetworkError, NotFoundError, RateLimitError, type RateLimiterOptions, type RetryOptions, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, retry, subMoney };
|
|
213
|
+
export { AuthError, type Currency, type CursorPage, type CursorPaginationParams, EUR, GBP, type LogContext, type LogLevel, type Logger, LoncaError, type LoncaErrorCode, type LoncaErrorOptions, type Money, NetworkError, NotFoundError, type OffsetPage, type OffsetPaginationParams, RateLimitError, type RateLimiterOptions, type RetryOptions, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, paginateOffset, retry, subMoney };
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,48 @@ interface CursorPage<T> {
|
|
|
49
49
|
* }
|
|
50
50
|
*/
|
|
51
51
|
declare function paginate<T>(fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>, params?: Omit<CursorPaginationParams, 'cursor'>): AsyncGenerator<T, void, undefined>;
|
|
52
|
+
interface OffsetPaginationParams {
|
|
53
|
+
/** Zero-based offset. Default depends on the SDK; usually `0`. */
|
|
54
|
+
offset?: number;
|
|
55
|
+
/** Maximum items in the page. */
|
|
56
|
+
limit?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Offset-based pagination envelope returned by marketplaces that expose
|
|
60
|
+
* `{ totalCount, limit, offset, pageCount, items[] }` instead of opaque
|
|
61
|
+
* cursors. Hepsiburada's OMS uses this shape; some legacy Trendyol
|
|
62
|
+
* endpoints do too. Distinct from `CursorPage<T>` so callers can tell at
|
|
63
|
+
* the type level which pagination model an endpoint uses.
|
|
64
|
+
*/
|
|
65
|
+
interface OffsetPage<T> {
|
|
66
|
+
/** Total number of items matching the query across all pages. */
|
|
67
|
+
totalCount: number;
|
|
68
|
+
/** Echo of the request `limit`. */
|
|
69
|
+
limit: number;
|
|
70
|
+
/** Echo of the request `offset`. */
|
|
71
|
+
offset: number;
|
|
72
|
+
/** Total number of pages at the requested `limit`. */
|
|
73
|
+
pageCount: number;
|
|
74
|
+
/** Page rows. */
|
|
75
|
+
items: T[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Drive an offset-paginated endpoint as an async iterator. Stops when the
|
|
79
|
+
* server's `pageCount` is exhausted (or after the configured `maxPages`
|
|
80
|
+
* to bound runaway iteration). Default page size is whatever the
|
|
81
|
+
* underlying endpoint clamps to — pass `limit` explicitly for control.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* for await (const order of paginateOffset((p) =>
|
|
85
|
+
* client.orders.list(p),
|
|
86
|
+
* )) {
|
|
87
|
+
* console.log(order.orderNumber);
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
declare function paginateOffset<T>(fetchPage: (params: OffsetPaginationParams) => Promise<OffsetPage<T>>, params?: {
|
|
91
|
+
limit?: number;
|
|
92
|
+
maxPages?: number;
|
|
93
|
+
}): AsyncGenerator<T, void, undefined>;
|
|
52
94
|
|
|
53
95
|
type LoncaErrorCode = 'AUTH_FAILED' | 'RATE_LIMITED' | 'VALIDATION_FAILED' | 'NOT_FOUND' | 'SERVER_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT' | 'UNKNOWN';
|
|
54
96
|
interface LoncaErrorOptions {
|
|
@@ -168,4 +210,4 @@ declare class TokenBucketRateLimiter {
|
|
|
168
210
|
available(): number;
|
|
169
211
|
}
|
|
170
212
|
|
|
171
|
-
export { AuthError, type Currency, type CursorPage, type CursorPaginationParams, EUR, GBP, type LogContext, type LogLevel, type Logger, LoncaError, type LoncaErrorCode, type LoncaErrorOptions, type Money, NetworkError, NotFoundError, RateLimitError, type RateLimiterOptions, type RetryOptions, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, retry, subMoney };
|
|
213
|
+
export { AuthError, type Currency, type CursorPage, type CursorPaginationParams, EUR, GBP, type LogContext, type LogLevel, type Logger, LoncaError, type LoncaErrorCode, type LoncaErrorOptions, type Money, NetworkError, NotFoundError, type OffsetPage, type OffsetPaginationParams, RateLimitError, type RateLimiterOptions, type RetryOptions, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, paginateOffset, retry, subMoney };
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,22 @@ async function* paginate(fetchPage, params = {}) {
|
|
|
52
52
|
cursor = page.nextCursor;
|
|
53
53
|
} while (cursor);
|
|
54
54
|
}
|
|
55
|
+
async function* paginateOffset(fetchPage, params = {}) {
|
|
56
|
+
const limit = params.limit;
|
|
57
|
+
const maxPages = params.maxPages ?? Infinity;
|
|
58
|
+
let offset = 0;
|
|
59
|
+
let pages = 0;
|
|
60
|
+
while (pages < maxPages) {
|
|
61
|
+
const page = await fetchPage({ offset, limit });
|
|
62
|
+
for (const item of page.items) {
|
|
63
|
+
yield item;
|
|
64
|
+
}
|
|
65
|
+
pages += 1;
|
|
66
|
+
if (page.items.length === 0) return;
|
|
67
|
+
if (page.pageCount > 0 && pages >= page.pageCount) return;
|
|
68
|
+
offset += page.limit || limit || page.items.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
55
71
|
|
|
56
72
|
// src/errors.ts
|
|
57
73
|
var LoncaError = class extends Error {
|
|
@@ -266,6 +282,6 @@ var TokenBucketRateLimiter = class {
|
|
|
266
282
|
}
|
|
267
283
|
};
|
|
268
284
|
|
|
269
|
-
export { AuthError, EUR, GBP, LoncaError, NetworkError, NotFoundError, RateLimitError, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, retry, subMoney };
|
|
285
|
+
export { AuthError, EUR, GBP, LoncaError, NetworkError, NotFoundError, RateLimitError, ServerError, TRY, TimeoutError, TokenBucketRateLimiter, USD, ValidationError, addMoney, consoleLogger, isLoncaError, isRetryableError, isSameCurrency, isValidCurrencyCode, money, moneyFromMajor, moneyToMajor, noopLogger, paginate, paginateOffset, retry, subMoney };
|
|
270
286
|
//# sourceMappingURL=index.js.map
|
|
271
287
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/currency.ts","../src/money.ts","../src/pagination.ts","../src/errors.ts","../src/retry.ts","../src/logger.ts","../src/rate-limiter.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ISO 4217 three-letter currency code.\n *\n * Typed as a string for flexibility — Trendyol and similar marketplaces may\n * expose currencies beyond the common set, so we don't lock down to a union.\n * Use `isValidCurrencyCode` for runtime validation.\n */\nexport type Currency = string;\n\nexport const TRY = 'TRY';\nexport const USD = 'USD';\nexport const EUR = 'EUR';\nexport const GBP = 'GBP';\n\nconst ISO_4217_RE = /^[A-Z]{3}$/;\n\nexport function isValidCurrencyCode(code: string): boolean {\n return ISO_4217_RE.test(code);\n}\n","import type { Currency } from './currency.js';\n\n/**\n * A monetary amount in integer minor units (e.g., kuruş for TRY, cents for USD).\n *\n * Using integers avoids floating-point surprises. Convert to/from major units\n * with `moneyFromMajor` and `moneyToMajor`.\n */\nexport interface Money {\n amount: number;\n currency: Currency;\n}\n\nexport function money(amount: number, currency: Currency): Money {\n if (!Number.isInteger(amount)) {\n throw new TypeError(`Money amount must be an integer minor unit, got ${amount}`);\n }\n return { amount, currency };\n}\n\nexport function moneyFromMajor(major: number, currency: Currency, minorScale = 2): Money {\n const factor = 10 ** minorScale;\n const amount = Math.round(major * factor);\n return money(amount, currency);\n}\n\nexport function moneyToMajor(value: Money, minorScale = 2): number {\n return value.amount / 10 ** minorScale;\n}\n\nexport function isSameCurrency(a: Money, b: Money): boolean {\n return a.currency === b.currency;\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(`Cannot add Money of different currencies: ${a.currency} vs ${b.currency}`);\n }\n return money(a.amount + b.amount, a.currency);\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(\n `Cannot subtract Money of different currencies: ${a.currency} vs ${b.currency}`,\n );\n }\n return money(a.amount - b.amount, a.currency);\n}\n","export interface CursorPaginationParams {\n /** Opaque cursor from previous response's `nextCursor`. Omit for the first page. */\n cursor?: string;\n /** Maximum items in the page. Marketplace SDKs may clamp this to their own bounds. */\n limit?: number;\n}\n\nexport interface CursorPage<T> {\n items: T[];\n /** Opaque cursor for the next page. Absent when there are no more pages. */\n nextCursor?: string;\n}\n\n/**\n * Drive a cursor-paginated endpoint as an async iterator.\n *\n * @example\n * for await (const order of paginate((p) => client.orders.list(p))) {\n * console.log(order.id);\n * }\n */\nexport async function* paginate<T>(\n fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>,\n params: Omit<CursorPaginationParams, 'cursor'> = {},\n): AsyncGenerator<T, void, undefined> {\n let cursor: string | undefined;\n do {\n const page = await fetchPage({ ...params, cursor });\n for (const item of page.items) {\n yield item;\n }\n cursor = page.nextCursor;\n } while (cursor);\n}\n","export type LoncaErrorCode =\n | 'AUTH_FAILED'\n | 'RATE_LIMITED'\n | 'VALIDATION_FAILED'\n | 'NOT_FOUND'\n | 'SERVER_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'UNKNOWN';\n\nexport interface LoncaErrorOptions {\n code: LoncaErrorCode;\n message: string;\n cause?: unknown;\n retryable?: boolean;\n status?: number;\n retryAfterMs?: number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Root of the Lonca error hierarchy.\n *\n * Carries a tagged `code` and a `retryable` boolean so retry helpers\n * can decide what to do without sniffing message strings.\n */\nexport class LoncaError extends Error {\n readonly code: LoncaErrorCode;\n readonly retryable: boolean;\n readonly status?: number;\n readonly retryAfterMs?: number;\n readonly data?: Record<string, unknown>;\n\n constructor(opts: LoncaErrorOptions) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = 'LoncaError';\n this.code = opts.code;\n this.retryable = opts.retryable ?? false;\n this.status = opts.status;\n this.retryAfterMs = opts.retryAfterMs;\n this.data = opts.data;\n }\n}\n\ntype SubclassOptions = Omit<LoncaErrorOptions, 'code' | 'retryable'>;\n\nexport class AuthError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'AUTH_FAILED', retryable: false });\n this.name = 'AuthError';\n }\n}\n\nexport class RateLimitError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'RATE_LIMITED', retryable: true });\n this.name = 'RateLimitError';\n }\n}\n\nexport class ValidationError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'VALIDATION_FAILED', retryable: false });\n this.name = 'ValidationError';\n }\n}\n\nexport class NotFoundError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NOT_FOUND', retryable: false });\n this.name = 'NotFoundError';\n }\n}\n\nexport class ServerError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'SERVER_ERROR', retryable: true });\n this.name = 'ServerError';\n }\n}\n\nexport class NetworkError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NETWORK_ERROR', retryable: true });\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'TIMEOUT', retryable: true });\n this.name = 'TimeoutError';\n }\n}\n\nexport function isLoncaError(value: unknown): value is LoncaError {\n return value instanceof LoncaError;\n}\n\nexport function isRetryableError(value: unknown): boolean {\n return isLoncaError(value) && value.retryable;\n}\n","import { isLoncaError, isRetryableError } from './errors.js';\n\nexport interface RetryOptions {\n /** Total number of attempts including the initial call. Defaults to 3. */\n maxAttempts?: number;\n /** Initial delay before the second attempt, in ms. Defaults to 200. */\n baseDelayMs?: number;\n /** Upper bound on delay between any two attempts, in ms. Defaults to 10_000. */\n maxDelayMs?: number;\n /** Add up to 50% random jitter on each delay. Defaults to true. */\n jitter?: boolean;\n /** Override the default predicate. Default treats `retryable` LoncaErrors as retryable. */\n isRetryable?: (err: unknown) => boolean;\n /** Called before each retry sleep. Useful for structured logging. */\n onRetry?: (err: unknown, attempt: number, nextDelayMs: number) => void;\n /** Abort all pending sleeps and bubble the signal's reason. */\n signal?: AbortSignal;\n}\n\nconst DEFAULTS = {\n maxAttempts: 3,\n baseDelayMs: 200,\n maxDelayMs: 10_000,\n jitter: true,\n} as const;\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n}\n\nexport async function retry<T>(\n fn: (attempt: number) => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULTS.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULTS.maxDelayMs;\n const jitter = options.jitter ?? DEFAULTS.jitter;\n const isRetryable = options.isRetryable ?? isRetryableError;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === maxAttempts || !isRetryable(err)) {\n throw err;\n }\n\n const retryAfterMs = isLoncaError(err) ? err.retryAfterMs : undefined;\n const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));\n const base = retryAfterMs ?? exponential;\n const jitterMs = jitter ? Math.random() * base * 0.5 : 0;\n const delay = Math.min(maxDelayMs, base + jitterMs);\n\n options.onRetry?.(err, attempt, delay);\n await sleep(delay, options.signal);\n }\n }\n throw lastErr;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type LogContext = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, context?: LogContext): void;\n info(message: string, context?: LogContext): void;\n warn(message: string, context?: LogContext): void;\n error(message: string, context?: LogContext): void;\n /** Create a child logger that merges `bindings` into every log entry. */\n child(bindings: LogContext): Logger;\n}\n\nexport const noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n child: () => noopLogger,\n};\n\n/**\n * Minimal JSON-line logger to stdout/stderr. Suitable for development and\n * containerized environments. For production prefer wiring a real logger\n * (pino, winston) by implementing the `Logger` interface.\n */\nexport function consoleLogger(bindings: LogContext = {}): Logger {\n const emit = (level: LogLevel, message: string, context?: LogContext): void => {\n const entry = {\n level,\n message,\n timestamp: new Date().toISOString(),\n ...bindings,\n ...context,\n };\n const line = JSON.stringify(entry);\n if (level === 'error' || level === 'warn') {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => emit('debug', m, c),\n info: (m, c) => emit('info', m, c),\n warn: (m, c) => emit('warn', m, c),\n error: (m, c) => emit('error', m, c),\n child: (b) => consoleLogger({ ...bindings, ...b }),\n };\n}\n","export interface RateLimiterOptions {\n /** Maximum number of tokens the bucket holds. Each `acquire()` consumes 1. */\n capacity: number;\n /** Time window in milliseconds over which `capacity` tokens are refilled. */\n intervalMs: number;\n}\n\n/**\n * Token-bucket rate limiter. `acquire()` returns immediately when a token\n * is available, otherwise it sleeps until enough tokens have been refilled.\n *\n * @example\n * const limiter = new TokenBucketRateLimiter({ capacity: 50, intervalMs: 60_000 });\n * for (const item of items) {\n * await limiter.acquire();\n * await fetchSomething(item);\n * }\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private lastRefillAt: number;\n private readonly capacity: number;\n private readonly intervalMs: number;\n\n constructor(options: RateLimiterOptions) {\n if (options.capacity <= 0 || options.intervalMs <= 0) {\n throw new RangeError('capacity and intervalMs must be > 0');\n }\n this.capacity = options.capacity;\n this.intervalMs = options.intervalMs;\n this.tokens = options.capacity;\n this.lastRefillAt = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time, then return ms to wait until the next\n * token is available (0 if one is available right now).\n */\n private timeUntilNextToken(): number {\n const now = Date.now();\n const elapsed = now - this.lastRefillAt;\n if (elapsed > 0) {\n const refill = (elapsed / this.intervalMs) * this.capacity;\n this.tokens = Math.min(this.capacity, this.tokens + refill);\n this.lastRefillAt = now;\n }\n if (this.tokens >= 1) return 0;\n const needed = 1 - this.tokens;\n return Math.ceil((needed / this.capacity) * this.intervalMs);\n }\n\n async acquire(signal?: AbortSignal): Promise<void> {\n for (;;) {\n if (signal?.aborted) throw signal.reason as Error;\n const wait = this.timeUntilNextToken();\n if (wait === 0) {\n this.tokens -= 1;\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, wait);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n }\n }\n\n /** Snapshot of available tokens (refills the bucket as a side effect). */\n available(): number {\n this.timeUntilNextToken();\n return this.tokens;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/currency.ts","../src/money.ts","../src/pagination.ts","../src/errors.ts","../src/retry.ts","../src/logger.ts","../src/rate-limiter.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ISO 4217 three-letter currency code.\n *\n * Typed as a string for flexibility — Trendyol and similar marketplaces may\n * expose currencies beyond the common set, so we don't lock down to a union.\n * Use `isValidCurrencyCode` for runtime validation.\n */\nexport type Currency = string;\n\nexport const TRY = 'TRY';\nexport const USD = 'USD';\nexport const EUR = 'EUR';\nexport const GBP = 'GBP';\n\nconst ISO_4217_RE = /^[A-Z]{3}$/;\n\nexport function isValidCurrencyCode(code: string): boolean {\n return ISO_4217_RE.test(code);\n}\n","import type { Currency } from './currency.js';\n\n/**\n * A monetary amount in integer minor units (e.g., kuruş for TRY, cents for USD).\n *\n * Using integers avoids floating-point surprises. Convert to/from major units\n * with `moneyFromMajor` and `moneyToMajor`.\n */\nexport interface Money {\n amount: number;\n currency: Currency;\n}\n\nexport function money(amount: number, currency: Currency): Money {\n if (!Number.isInteger(amount)) {\n throw new TypeError(`Money amount must be an integer minor unit, got ${amount}`);\n }\n return { amount, currency };\n}\n\nexport function moneyFromMajor(major: number, currency: Currency, minorScale = 2): Money {\n const factor = 10 ** minorScale;\n const amount = Math.round(major * factor);\n return money(amount, currency);\n}\n\nexport function moneyToMajor(value: Money, minorScale = 2): number {\n return value.amount / 10 ** minorScale;\n}\n\nexport function isSameCurrency(a: Money, b: Money): boolean {\n return a.currency === b.currency;\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(`Cannot add Money of different currencies: ${a.currency} vs ${b.currency}`);\n }\n return money(a.amount + b.amount, a.currency);\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n if (!isSameCurrency(a, b)) {\n throw new TypeError(\n `Cannot subtract Money of different currencies: ${a.currency} vs ${b.currency}`,\n );\n }\n return money(a.amount - b.amount, a.currency);\n}\n","export interface CursorPaginationParams {\n /** Opaque cursor from previous response's `nextCursor`. Omit for the first page. */\n cursor?: string;\n /** Maximum items in the page. Marketplace SDKs may clamp this to their own bounds. */\n limit?: number;\n}\n\nexport interface CursorPage<T> {\n items: T[];\n /** Opaque cursor for the next page. Absent when there are no more pages. */\n nextCursor?: string;\n}\n\n/**\n * Drive a cursor-paginated endpoint as an async iterator.\n *\n * @example\n * for await (const order of paginate((p) => client.orders.list(p))) {\n * console.log(order.id);\n * }\n */\nexport async function* paginate<T>(\n fetchPage: (params: CursorPaginationParams) => Promise<CursorPage<T>>,\n params: Omit<CursorPaginationParams, 'cursor'> = {},\n): AsyncGenerator<T, void, undefined> {\n let cursor: string | undefined;\n do {\n const page = await fetchPage({ ...params, cursor });\n for (const item of page.items) {\n yield item;\n }\n cursor = page.nextCursor;\n } while (cursor);\n}\n\n// ─── Offset pagination ─────────────────────────────────────────────────────\n\nexport interface OffsetPaginationParams {\n /** Zero-based offset. Default depends on the SDK; usually `0`. */\n offset?: number;\n /** Maximum items in the page. */\n limit?: number;\n}\n\n/**\n * Offset-based pagination envelope returned by marketplaces that expose\n * `{ totalCount, limit, offset, pageCount, items[] }` instead of opaque\n * cursors. Hepsiburada's OMS uses this shape; some legacy Trendyol\n * endpoints do too. Distinct from `CursorPage<T>` so callers can tell at\n * the type level which pagination model an endpoint uses.\n */\nexport interface OffsetPage<T> {\n /** Total number of items matching the query across all pages. */\n totalCount: number;\n /** Echo of the request `limit`. */\n limit: number;\n /** Echo of the request `offset`. */\n offset: number;\n /** Total number of pages at the requested `limit`. */\n pageCount: number;\n /** Page rows. */\n items: T[];\n}\n\n/**\n * Drive an offset-paginated endpoint as an async iterator. Stops when the\n * server's `pageCount` is exhausted (or after the configured `maxPages`\n * to bound runaway iteration). Default page size is whatever the\n * underlying endpoint clamps to — pass `limit` explicitly for control.\n *\n * @example\n * for await (const order of paginateOffset((p) =>\n * client.orders.list(p),\n * )) {\n * console.log(order.orderNumber);\n * }\n */\nexport async function* paginateOffset<T>(\n fetchPage: (params: OffsetPaginationParams) => Promise<OffsetPage<T>>,\n params: { limit?: number; maxPages?: number } = {},\n): AsyncGenerator<T, void, undefined> {\n const limit = params.limit;\n const maxPages = params.maxPages ?? Infinity;\n let offset = 0;\n let pages = 0;\n while (pages < maxPages) {\n const page = await fetchPage({ offset, limit });\n for (const item of page.items) {\n yield item;\n }\n pages += 1;\n if (page.items.length === 0) return;\n if (page.pageCount > 0 && pages >= page.pageCount) return;\n // Step by the server-echoed limit (when present) so requests stay aligned with how\n // the server batches; fall back to the caller's `limit` or the observed batch size.\n offset += page.limit || limit || page.items.length;\n }\n}\n","export type LoncaErrorCode =\n | 'AUTH_FAILED'\n | 'RATE_LIMITED'\n | 'VALIDATION_FAILED'\n | 'NOT_FOUND'\n | 'SERVER_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'UNKNOWN';\n\nexport interface LoncaErrorOptions {\n code: LoncaErrorCode;\n message: string;\n cause?: unknown;\n retryable?: boolean;\n status?: number;\n retryAfterMs?: number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Root of the Lonca error hierarchy.\n *\n * Carries a tagged `code` and a `retryable` boolean so retry helpers\n * can decide what to do without sniffing message strings.\n */\nexport class LoncaError extends Error {\n readonly code: LoncaErrorCode;\n readonly retryable: boolean;\n readonly status?: number;\n readonly retryAfterMs?: number;\n readonly data?: Record<string, unknown>;\n\n constructor(opts: LoncaErrorOptions) {\n super(opts.message, opts.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = 'LoncaError';\n this.code = opts.code;\n this.retryable = opts.retryable ?? false;\n this.status = opts.status;\n this.retryAfterMs = opts.retryAfterMs;\n this.data = opts.data;\n }\n}\n\ntype SubclassOptions = Omit<LoncaErrorOptions, 'code' | 'retryable'>;\n\nexport class AuthError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'AUTH_FAILED', retryable: false });\n this.name = 'AuthError';\n }\n}\n\nexport class RateLimitError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'RATE_LIMITED', retryable: true });\n this.name = 'RateLimitError';\n }\n}\n\nexport class ValidationError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'VALIDATION_FAILED', retryable: false });\n this.name = 'ValidationError';\n }\n}\n\nexport class NotFoundError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NOT_FOUND', retryable: false });\n this.name = 'NotFoundError';\n }\n}\n\nexport class ServerError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'SERVER_ERROR', retryable: true });\n this.name = 'ServerError';\n }\n}\n\nexport class NetworkError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'NETWORK_ERROR', retryable: true });\n this.name = 'NetworkError';\n }\n}\n\nexport class TimeoutError extends LoncaError {\n constructor(opts: SubclassOptions) {\n super({ ...opts, code: 'TIMEOUT', retryable: true });\n this.name = 'TimeoutError';\n }\n}\n\nexport function isLoncaError(value: unknown): value is LoncaError {\n return value instanceof LoncaError;\n}\n\nexport function isRetryableError(value: unknown): boolean {\n return isLoncaError(value) && value.retryable;\n}\n","import { isLoncaError, isRetryableError } from './errors.js';\n\nexport interface RetryOptions {\n /** Total number of attempts including the initial call. Defaults to 3. */\n maxAttempts?: number;\n /** Initial delay before the second attempt, in ms. Defaults to 200. */\n baseDelayMs?: number;\n /** Upper bound on delay between any two attempts, in ms. Defaults to 10_000. */\n maxDelayMs?: number;\n /** Add up to 50% random jitter on each delay. Defaults to true. */\n jitter?: boolean;\n /** Override the default predicate. Default treats `retryable` LoncaErrors as retryable. */\n isRetryable?: (err: unknown) => boolean;\n /** Called before each retry sleep. Useful for structured logging. */\n onRetry?: (err: unknown, attempt: number, nextDelayMs: number) => void;\n /** Abort all pending sleeps and bubble the signal's reason. */\n signal?: AbortSignal;\n}\n\nconst DEFAULTS = {\n maxAttempts: 3,\n baseDelayMs: 200,\n maxDelayMs: 10_000,\n jitter: true,\n} as const;\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n}\n\nexport async function retry<T>(\n fn: (attempt: number) => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;\n const baseDelayMs = options.baseDelayMs ?? DEFAULTS.baseDelayMs;\n const maxDelayMs = options.maxDelayMs ?? DEFAULTS.maxDelayMs;\n const jitter = options.jitter ?? DEFAULTS.jitter;\n const isRetryable = options.isRetryable ?? isRetryableError;\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn(attempt);\n } catch (err) {\n lastErr = err;\n if (attempt === maxAttempts || !isRetryable(err)) {\n throw err;\n }\n\n const retryAfterMs = isLoncaError(err) ? err.retryAfterMs : undefined;\n const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));\n const base = retryAfterMs ?? exponential;\n const jitterMs = jitter ? Math.random() * base * 0.5 : 0;\n const delay = Math.min(maxDelayMs, base + jitterMs);\n\n options.onRetry?.(err, attempt, delay);\n await sleep(delay, options.signal);\n }\n }\n throw lastErr;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport type LogContext = Record<string, unknown>;\n\nexport interface Logger {\n debug(message: string, context?: LogContext): void;\n info(message: string, context?: LogContext): void;\n warn(message: string, context?: LogContext): void;\n error(message: string, context?: LogContext): void;\n /** Create a child logger that merges `bindings` into every log entry. */\n child(bindings: LogContext): Logger;\n}\n\nexport const noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n child: () => noopLogger,\n};\n\n/**\n * Minimal JSON-line logger to stdout/stderr. Suitable for development and\n * containerized environments. For production prefer wiring a real logger\n * (pino, winston) by implementing the `Logger` interface.\n */\nexport function consoleLogger(bindings: LogContext = {}): Logger {\n const emit = (level: LogLevel, message: string, context?: LogContext): void => {\n const entry = {\n level,\n message,\n timestamp: new Date().toISOString(),\n ...bindings,\n ...context,\n };\n const line = JSON.stringify(entry);\n if (level === 'error' || level === 'warn') {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n return {\n debug: (m, c) => emit('debug', m, c),\n info: (m, c) => emit('info', m, c),\n warn: (m, c) => emit('warn', m, c),\n error: (m, c) => emit('error', m, c),\n child: (b) => consoleLogger({ ...bindings, ...b }),\n };\n}\n","export interface RateLimiterOptions {\n /** Maximum number of tokens the bucket holds. Each `acquire()` consumes 1. */\n capacity: number;\n /** Time window in milliseconds over which `capacity` tokens are refilled. */\n intervalMs: number;\n}\n\n/**\n * Token-bucket rate limiter. `acquire()` returns immediately when a token\n * is available, otherwise it sleeps until enough tokens have been refilled.\n *\n * @example\n * const limiter = new TokenBucketRateLimiter({ capacity: 50, intervalMs: 60_000 });\n * for (const item of items) {\n * await limiter.acquire();\n * await fetchSomething(item);\n * }\n */\nexport class TokenBucketRateLimiter {\n private tokens: number;\n private lastRefillAt: number;\n private readonly capacity: number;\n private readonly intervalMs: number;\n\n constructor(options: RateLimiterOptions) {\n if (options.capacity <= 0 || options.intervalMs <= 0) {\n throw new RangeError('capacity and intervalMs must be > 0');\n }\n this.capacity = options.capacity;\n this.intervalMs = options.intervalMs;\n this.tokens = options.capacity;\n this.lastRefillAt = Date.now();\n }\n\n /**\n * Refill tokens based on elapsed time, then return ms to wait until the next\n * token is available (0 if one is available right now).\n */\n private timeUntilNextToken(): number {\n const now = Date.now();\n const elapsed = now - this.lastRefillAt;\n if (elapsed > 0) {\n const refill = (elapsed / this.intervalMs) * this.capacity;\n this.tokens = Math.min(this.capacity, this.tokens + refill);\n this.lastRefillAt = now;\n }\n if (this.tokens >= 1) return 0;\n const needed = 1 - this.tokens;\n return Math.ceil((needed / this.capacity) * this.intervalMs);\n }\n\n async acquire(signal?: AbortSignal): Promise<void> {\n for (;;) {\n if (signal?.aborted) throw signal.reason as Error;\n const wait = this.timeUntilNextToken();\n if (wait === 0) {\n this.tokens -= 1;\n return;\n }\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, wait);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason);\n },\n { once: true },\n );\n });\n }\n }\n\n /** Snapshot of available tokens (refills the bucket as a side effect). */\n available(): number {\n this.timeUntilNextToken();\n return this.tokens;\n }\n}\n"]}
|