@innvoid/getmarket-sdk 0.2.2 → 0.2.3

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 CHANGED
@@ -37,6 +37,7 @@ __export(src_exports, {
37
37
  HEADER_INTERNAL_API_KEY: () => HEADER_INTERNAL_API_KEY,
38
38
  HEADER_REQUEST_ID: () => HEADER_REQUEST_ID,
39
39
  InternalHttp: () => InternalHttp,
40
+ InternalHttpError: () => InternalHttpError,
40
41
  TwoLevelCache: () => TwoLevelCache,
41
42
  UpstreamError: () => UpstreamError,
42
43
  allowAuthAdminOrPerm: () => allowAuthAdminOrPerm,
@@ -48,10 +49,18 @@ __export(src_exports, {
48
49
  authCustomerRequired: () => authCustomerRequired,
49
50
  authEmployeeAllowFirebase: () => authEmployeeAllowFirebase,
50
51
  authEmployeeRequired: () => authEmployeeRequired,
52
+ buildInternalHeaders: () => buildInternalHeaders,
51
53
  closeCache: () => closeCache,
52
54
  createAuthMiddleware: () => createAuthMiddleware,
53
55
  createAuthMiddlewareLegacySimple: () => createAuthMiddleware2,
56
+ createBulkRefsClient: () => createBulkRefsClient,
57
+ createFisClient: () => createFisClient,
54
58
  createHttpClient: () => createHttpClient,
59
+ createInternalHttpClient: () => createInternalHttpClient,
60
+ createMdClient: () => createMdClient,
61
+ createMediaClient: () => createMediaClient,
62
+ createPlatformClient: () => createPlatformClient,
63
+ createResClient: () => createResClient,
55
64
  getOrSet: () => getOrSet,
56
65
  getRequestContextFromHeaders: () => getRequestContextFromHeaders,
57
66
  getTwoLevelCache: () => getTwoLevelCache,
@@ -59,6 +68,7 @@ __export(src_exports, {
59
68
  mapAxiosToUpstreamError: () => mapAxiosToUpstreamError,
60
69
  parseHeaders: () => parseHeaders,
61
70
  readRs256PublicKey: () => readRs256PublicKey,
71
+ readServiceEnv: () => readServiceEnv,
62
72
  requestId: () => requestId,
63
73
  requireAnyPermission: () => requireAnyPermission,
64
74
  requireAuthContext: () => requireAuthContext,
@@ -1183,6 +1193,365 @@ function allowSysAdminOrRolesOrAnyPermission(roles, permissions) {
1183
1193
  function allowAuthAdminOrPerm(permission) {
1184
1194
  return allowSysAdminOrRolesOrAnyPermission(["AUTH_ADMIN"], [permission]);
1185
1195
  }
1196
+
1197
+ // src/internalHttpClient.ts
1198
+ var import_fs4 = __toESM(require("fs"), 1);
1199
+ var InternalHttpError = class extends Error {
1200
+ status;
1201
+ code;
1202
+ details;
1203
+ constructor(status, code, message, details) {
1204
+ super(message);
1205
+ this.status = status;
1206
+ this.code = code;
1207
+ this.details = details ?? null;
1208
+ }
1209
+ };
1210
+ function readSecretFile2(path) {
1211
+ if (!path) return null;
1212
+ try {
1213
+ const v = import_fs4.default.readFileSync(path, "utf8").trim();
1214
+ return v.length ? v : null;
1215
+ } catch {
1216
+ return null;
1217
+ }
1218
+ }
1219
+ function splitKeys2(v) {
1220
+ if (!v) return [];
1221
+ return v.split(",").map((s) => s.trim()).filter(Boolean);
1222
+ }
1223
+ function getInternalApiKey(override) {
1224
+ if (override && override.trim()) return override.trim();
1225
+ const fileKey = readSecretFile2(process.env.INTERNAL_API_KEY_FILE);
1226
+ const envKey = (process.env.INTERNAL_API_KEY || "").trim();
1227
+ const raw = fileKey || envKey;
1228
+ const keys = splitKeys2(raw);
1229
+ return keys[0] || null;
1230
+ }
1231
+ function normalizeBaseURL(baseURL) {
1232
+ const b = (baseURL || "").trim();
1233
+ if (!b) throw new Error("InternalHttpClient: baseURL is required");
1234
+ return b.replace(/\/+$/g, "");
1235
+ }
1236
+ function normalizePrefix(prefix) {
1237
+ const p = (prefix || "").trim();
1238
+ if (!p) return "";
1239
+ const withSlash = p.startsWith("/") ? p : `/${p}`;
1240
+ return withSlash.replace(/\/+$/g, "");
1241
+ }
1242
+ function buildQuery(query) {
1243
+ if (!query) return "";
1244
+ const params = new URLSearchParams();
1245
+ for (const [k, v] of Object.entries(query)) {
1246
+ if (v === null || v === void 0) continue;
1247
+ params.set(k, String(v));
1248
+ }
1249
+ const s = params.toString();
1250
+ return s ? `?${s}` : "";
1251
+ }
1252
+ function buildInternalHeaders(ctx, extra, idempotencyKey) {
1253
+ const h2 = {
1254
+ "Content-Type": "application/json"
1255
+ };
1256
+ if (ctx?.requestId) h2[HEADER_REQUEST_ID] = String(ctx.requestId);
1257
+ if (ctx?.company_uid) h2[HEADER_COMPANY_UID] = String(ctx.company_uid);
1258
+ if (ctx?.branch_uid) h2[HEADER_BRANCH_UID] = String(ctx.branch_uid);
1259
+ if (ctx?.employee_uid) h2[HEADER_EMPLOYEE_UID] = String(ctx.employee_uid);
1260
+ if (idempotencyKey && idempotencyKey.trim()) {
1261
+ h2["Idempotency-Key"] = idempotencyKey.trim();
1262
+ }
1263
+ for (const [k, v] of Object.entries(extra || {})) {
1264
+ if (v === void 0) continue;
1265
+ const vv = String(v).trim();
1266
+ if (!vv) continue;
1267
+ h2[k] = vv;
1268
+ }
1269
+ return h2;
1270
+ }
1271
+ async function sleep2(ms) {
1272
+ await new Promise((r) => setTimeout(r, ms));
1273
+ }
1274
+ function shouldRetry(status, err) {
1275
+ if (err?.name === "AbortError") return true;
1276
+ if (!status) return true;
1277
+ if (status >= 500) return true;
1278
+ if (status === 429) return true;
1279
+ return false;
1280
+ }
1281
+ async function safeReadJson(res) {
1282
+ const text = await res.text();
1283
+ if (!text) return null;
1284
+ try {
1285
+ return JSON.parse(text);
1286
+ } catch {
1287
+ return { raw: text };
1288
+ }
1289
+ }
1290
+ function createInternalHttpClient(opts) {
1291
+ const baseURL = normalizeBaseURL(opts.baseURL);
1292
+ const apiPrefix = normalizePrefix(opts.apiPrefix);
1293
+ const timeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : 15e3;
1294
+ const retries = typeof opts.retries === "number" ? opts.retries : 2;
1295
+ const retryDelayMs = typeof opts.retryDelayMs === "number" ? opts.retryDelayMs : 250;
1296
+ const apiKey = getInternalApiKey(opts.apiKey ?? null);
1297
+ if (!apiKey) {
1298
+ throw new Error(
1299
+ "InternalHttpClient: INTERNAL_API_KEY or INTERNAL_API_KEY_FILE is required to call internal endpoints."
1300
+ );
1301
+ }
1302
+ async function request(r) {
1303
+ const path = (r.path || "").trim();
1304
+ if (!path.startsWith("/")) {
1305
+ throw new Error(`InternalHttpClient: path must start with '/': ${path}`);
1306
+ }
1307
+ const url = `${baseURL}${apiPrefix}${path}${buildQuery(r.query)}`;
1308
+ const headers = buildInternalHeaders(r.ctx ?? null, r.headers, r.idempotencyKey ?? null);
1309
+ if (apiKey != null) {
1310
+ headers[HEADER_INTERNAL_API_KEY] = apiKey;
1311
+ }
1312
+ const method = r.method;
1313
+ const expectJson = r.expectJson !== false;
1314
+ const body = method === "GET" || method === "DELETE" ? void 0 : r.body !== void 0 ? JSON.stringify(r.body) : void 0;
1315
+ let attempt = 0;
1316
+ let lastErr = null;
1317
+ while (attempt <= retries) {
1318
+ attempt++;
1319
+ const ac = new AbortController();
1320
+ const to = setTimeout(() => ac.abort(), timeoutMs);
1321
+ try {
1322
+ const res = await fetch(url, {
1323
+ method,
1324
+ headers,
1325
+ body,
1326
+ signal: ac.signal
1327
+ });
1328
+ clearTimeout(to);
1329
+ if (res.ok) {
1330
+ if (!expectJson) return void 0;
1331
+ const data = await safeReadJson(res);
1332
+ return data;
1333
+ }
1334
+ const errBody = await safeReadJson(res);
1335
+ const status = res.status;
1336
+ if (attempt <= retries && shouldRetry(status, null)) {
1337
+ lastErr = new InternalHttpError(
1338
+ status,
1339
+ "INTERNAL_HTTP_RETRY",
1340
+ `Retryable internal HTTP error (${status})`,
1341
+ { url, status, response: errBody, attempt }
1342
+ );
1343
+ await sleep2(retryDelayMs * attempt);
1344
+ continue;
1345
+ }
1346
+ throw new InternalHttpError(
1347
+ status,
1348
+ "INTERNAL_HTTP_ERROR",
1349
+ `Internal HTTP error (${status})`,
1350
+ { url, status, response: errBody }
1351
+ );
1352
+ } catch (e) {
1353
+ clearTimeout(to);
1354
+ if (attempt <= retries && shouldRetry(null, e)) {
1355
+ lastErr = e;
1356
+ await sleep2(retryDelayMs * attempt);
1357
+ continue;
1358
+ }
1359
+ if (e instanceof InternalHttpError) throw e;
1360
+ throw new InternalHttpError(
1361
+ 0,
1362
+ "INTERNAL_HTTP_NETWORK_ERROR",
1363
+ e?.message || "Internal HTTP network error",
1364
+ { url, attempt, error: String(e) }
1365
+ );
1366
+ }
1367
+ }
1368
+ throw lastErr || new InternalHttpError(0, "INTERNAL_HTTP_FAILED", "Internal request failed");
1369
+ }
1370
+ return {
1371
+ request,
1372
+ get: (path, o) => request({ ...o || {}, method: "GET", path }),
1373
+ post: (path, body, o) => request({ ...o || {}, method: "POST", path, body }),
1374
+ put: (path, body, o) => request({ ...o || {}, method: "PUT", path, body }),
1375
+ patch: (path, body, o) => request({ ...o || {}, method: "PATCH", path, body }),
1376
+ del: (path, o) => request({ ...o || {}, method: "DELETE", path })
1377
+ };
1378
+ }
1379
+
1380
+ // src/clients/env.ts
1381
+ function must(v, name) {
1382
+ const s = (v || "").trim();
1383
+ if (!s) throw new Error(`Missing env var: ${name}`);
1384
+ return s;
1385
+ }
1386
+ function readServiceEnv(prefix, defaults) {
1387
+ const baseURL = (process.env[`${prefix}_BASE_URL`] || defaults?.baseURL || "").trim();
1388
+ const apiPrefix = (process.env[`${prefix}_API_PREFIX`] || defaults?.apiPrefix || "/internal/v1").trim();
1389
+ return {
1390
+ baseURL: must(baseURL, `${prefix}_BASE_URL`),
1391
+ apiPrefix
1392
+ };
1393
+ }
1394
+
1395
+ // src/clients/cacheFactory.ts
1396
+ function makeRefsCache(namespace) {
1397
+ const l2 = createCacheProvider();
1398
+ return new TwoLevelCache(l2, {
1399
+ namespace,
1400
+ ttlMsL1: 3e4,
1401
+ ttlMsL2: 3e5,
1402
+ negativeTtlMsL1: 3e4,
1403
+ negativeTtlMsL2: 3e4
1404
+ });
1405
+ }
1406
+
1407
+ // src/clients/bulkRefsClient.ts
1408
+ function uniq(arr) {
1409
+ return Array.from(new Set(arr.filter(Boolean)));
1410
+ }
1411
+ function createBulkRefsClient(args) {
1412
+ const http = createInternalHttpClient({
1413
+ baseURL: args.baseURL,
1414
+ apiPrefix: args.apiPrefix
1415
+ });
1416
+ const cache = makeRefsCache(args.namespace);
1417
+ async function bulkRefs(uids, opts) {
1418
+ const list = uniq(uids);
1419
+ if (list.length === 0) {
1420
+ return { ok: true, refs: [], meta: { requested: 0, returned: 0, missing: [] } };
1421
+ }
1422
+ const hits = [];
1423
+ const missing = [];
1424
+ for (const uid of list) {
1425
+ const v = await cache.get(uid);
1426
+ if (v) hits.push(v);
1427
+ else missing.push(uid);
1428
+ }
1429
+ let fetched = [];
1430
+ if (missing.length) {
1431
+ const resp = await http.post(
1432
+ args.path,
1433
+ { uids: missing },
1434
+ {
1435
+ ctx: opts?.ctx ?? null
1436
+ }
1437
+ );
1438
+ const refs = resp.refs ?? resp.items ?? [];
1439
+ fetched = refs;
1440
+ const ttlMsL1 = opts?.ttlMsL1 ?? args.defaultTtlMsL1;
1441
+ const ttlMsL2 = opts?.ttlMsL2 ?? args.defaultTtlMsL2;
1442
+ for (const ref of refs) {
1443
+ const uid = String(ref?.uid || "").trim();
1444
+ if (!uid) continue;
1445
+ await cache.set(uid, ref, {
1446
+ ttlMsL1,
1447
+ ttlMsL2
1448
+ });
1449
+ }
1450
+ }
1451
+ const all = [...hits, ...fetched];
1452
+ const returnedSet = new Set(all.map((r) => String(r?.uid || "")));
1453
+ const missingFinal = list.filter((u) => !returnedSet.has(u));
1454
+ return {
1455
+ ok: true,
1456
+ refs: all,
1457
+ meta: {
1458
+ requested: list.length,
1459
+ returned: all.length,
1460
+ missing: missingFinal
1461
+ }
1462
+ };
1463
+ }
1464
+ return { bulkRefs };
1465
+ }
1466
+
1467
+ // src/clients/platformClient.ts
1468
+ function createPlatformClient(env) {
1469
+ const http = createInternalHttpClient(env);
1470
+ const cache = makeRefsCache("platform:tenant");
1471
+ async function resolveTenant(input, ctx) {
1472
+ const key = `${input.company_uid}:${input.service}`;
1473
+ const cached = await cache.get(key);
1474
+ if (cached) return cached;
1475
+ const resp = await http.post(
1476
+ "/tenants/resolve",
1477
+ input,
1478
+ { ctx: ctx ?? null }
1479
+ );
1480
+ await cache.set(key, resp);
1481
+ return resp;
1482
+ }
1483
+ return { resolveTenant };
1484
+ }
1485
+
1486
+ // src/clients/resClient.ts
1487
+ function createResClient() {
1488
+ const env = readServiceEnv("RES", { apiPrefix: "/internal/v1" });
1489
+ const varieties = createBulkRefsClient({
1490
+ namespace: "res:variety",
1491
+ baseURL: env.baseURL,
1492
+ apiPrefix: env.apiPrefix,
1493
+ path: "/refs/varieties"
1494
+ });
1495
+ return {
1496
+ varietiesRefs: (uids, opts) => varieties.bulkRefs(uids, opts)
1497
+ };
1498
+ }
1499
+
1500
+ // src/clients/mdClient.ts
1501
+ function createMdClient() {
1502
+ const env = readServiceEnv("MD", { apiPrefix: "/internal/v1" });
1503
+ const measures = createBulkRefsClient({
1504
+ namespace: "md:measure",
1505
+ baseURL: env.baseURL,
1506
+ apiPrefix: env.apiPrefix,
1507
+ path: "/refs/measures"
1508
+ });
1509
+ const measureTypes = createBulkRefsClient({
1510
+ namespace: "md:measure_type",
1511
+ baseURL: env.baseURL,
1512
+ apiPrefix: env.apiPrefix,
1513
+ path: "/refs/measure-types"
1514
+ });
1515
+ const countries = createBulkRefsClient({
1516
+ namespace: "md:country",
1517
+ baseURL: env.baseURL,
1518
+ apiPrefix: env.apiPrefix,
1519
+ path: "/refs/countries"
1520
+ });
1521
+ return {
1522
+ measuresRefs: (uids, opts) => measures.bulkRefs(uids, opts),
1523
+ measureTypesRefs: (uids, opts) => measureTypes.bulkRefs(uids, opts),
1524
+ countriesRefs: (uids, opts) => countries.bulkRefs(uids, opts)
1525
+ };
1526
+ }
1527
+
1528
+ // src/clients/fisClient.ts
1529
+ function createFisClient() {
1530
+ const env = readServiceEnv("FIS", { apiPrefix: "/internal/v1" });
1531
+ const taxes = createBulkRefsClient({
1532
+ namespace: "fis:tax",
1533
+ baseURL: env.baseURL,
1534
+ apiPrefix: env.apiPrefix,
1535
+ path: "/refs/taxes"
1536
+ });
1537
+ return {
1538
+ taxesRefs: (uids, opts) => taxes.bulkRefs(uids, opts)
1539
+ };
1540
+ }
1541
+
1542
+ // src/clients/mediaClient.ts
1543
+ function createMediaClient() {
1544
+ const env = readServiceEnv("MEDIA", { apiPrefix: "/internal/v1" });
1545
+ const files = createBulkRefsClient({
1546
+ namespace: "media:file",
1547
+ baseURL: env.baseURL,
1548
+ apiPrefix: env.apiPrefix,
1549
+ path: "/refs/files"
1550
+ });
1551
+ return {
1552
+ filesRefs: (uids, opts) => files.bulkRefs(uids, opts)
1553
+ };
1554
+ }
1186
1555
  // Annotate the CommonJS export names for ESM import in node:
1187
1556
  0 && (module.exports = {
1188
1557
  HEADER_AUTHORIZATION,
@@ -1192,6 +1561,7 @@ function allowAuthAdminOrPerm(permission) {
1192
1561
  HEADER_INTERNAL_API_KEY,
1193
1562
  HEADER_REQUEST_ID,
1194
1563
  InternalHttp,
1564
+ InternalHttpError,
1195
1565
  TwoLevelCache,
1196
1566
  UpstreamError,
1197
1567
  allowAuthAdminOrPerm,
@@ -1203,10 +1573,18 @@ function allowAuthAdminOrPerm(permission) {
1203
1573
  authCustomerRequired,
1204
1574
  authEmployeeAllowFirebase,
1205
1575
  authEmployeeRequired,
1576
+ buildInternalHeaders,
1206
1577
  closeCache,
1207
1578
  createAuthMiddleware,
1208
1579
  createAuthMiddlewareLegacySimple,
1580
+ createBulkRefsClient,
1581
+ createFisClient,
1209
1582
  createHttpClient,
1583
+ createInternalHttpClient,
1584
+ createMdClient,
1585
+ createMediaClient,
1586
+ createPlatformClient,
1587
+ createResClient,
1210
1588
  getOrSet,
1211
1589
  getRequestContextFromHeaders,
1212
1590
  getTwoLevelCache,
@@ -1214,6 +1592,7 @@ function allowAuthAdminOrPerm(permission) {
1214
1592
  mapAxiosToUpstreamError,
1215
1593
  parseHeaders,
1216
1594
  readRs256PublicKey,
1595
+ readServiceEnv,
1217
1596
  requestId,
1218
1597
  requireAnyPermission,
1219
1598
  requireAuthContext,