@viu/emporix-sdk 4.9.4 → 4.11.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +2 -1
  3. package/dist/availability.d.cts +1 -1
  4. package/dist/availability.d.ts +1 -1
  5. package/dist/cart.d.cts +1 -1
  6. package/dist/cart.d.ts +1 -1
  7. package/dist/category.d.cts +1 -1
  8. package/dist/category.d.ts +1 -1
  9. package/dist/checkout.d.cts +1 -1
  10. package/dist/checkout.d.ts +1 -1
  11. package/dist/companies.d.cts +1 -1
  12. package/dist/companies.d.ts +1 -1
  13. package/dist/contacts.d.cts +1 -1
  14. package/dist/contacts.d.ts +1 -1
  15. package/dist/{context-BJeomwxV.d.cts → context-BqSLYN2q.d.cts} +68 -1
  16. package/dist/{context-BJeomwxV.d.ts → context-BqSLYN2q.d.ts} +68 -1
  17. package/dist/{customer-groups-ZlnnZrX_.d.cts → customer-groups-B3jlWR4O.d.cts} +1 -1
  18. package/dist/{customer-groups-ByyBHCBH.d.ts → customer-groups-oJ9YcjDe.d.ts} +1 -1
  19. package/dist/customer-groups.d.cts +2 -2
  20. package/dist/customer-groups.d.ts +2 -2
  21. package/dist/customer.d.cts +1 -1
  22. package/dist/customer.d.ts +1 -1
  23. package/dist/index.cjs +499 -299
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +174 -28
  26. package/dist/index.d.ts +174 -28
  27. package/dist/index.js +498 -301
  28. package/dist/index.js.map +1 -1
  29. package/dist/locations.d.cts +1 -1
  30. package/dist/locations.d.ts +1 -1
  31. package/dist/media.d.cts +1 -1
  32. package/dist/media.d.ts +1 -1
  33. package/dist/orders.d.cts +1 -1
  34. package/dist/orders.d.ts +1 -1
  35. package/dist/payment.d.cts +1 -1
  36. package/dist/payment.d.ts +1 -1
  37. package/dist/price.d.cts +1 -1
  38. package/dist/price.d.ts +1 -1
  39. package/dist/product.d.cts +1 -1
  40. package/dist/product.d.ts +1 -1
  41. package/dist/segment.d.cts +1 -1
  42. package/dist/segment.d.ts +1 -1
  43. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ export { CustomerGroupsService, IamService } from './chunk-VXORPTMP.js';
34
34
  import { CustomerService } from './chunk-EQE5WGME.js';
35
35
  export { CustomerService } from './chunk-EQE5WGME.js';
36
36
  import './chunk-ZVIEFW3X.js';
37
- import { EmporixTimeoutError, EmporixNetworkError, errorFromResponse, EmporixNotFoundError, EmporixError } from './chunk-4Q76UV4J.js';
37
+ import { EmporixAuthError, EmporixError, EmporixTimeoutError, EmporixNetworkError, errorFromResponse, EmporixNotFoundError } from './chunk-4Q76UV4J.js';
38
38
  export { EmporixAuthError, EmporixConflictError, EmporixError, EmporixForbiddenError, EmporixInsufficientScopeError, EmporixNetworkError, EmporixNotFoundError, EmporixServerError, EmporixTimeoutError, EmporixValidationError, errorFromResponse } from './chunk-4Q76UV4J.js';
39
39
 
40
40
  // src/core/config.ts
@@ -61,6 +61,17 @@ function validateConfig(input) {
61
61
  throw new Error("credentials is required (provide at least one of backend/storefront/custom)");
62
62
  }
63
63
  warnOnMisplacedContext(input);
64
+ const proxy = input.proxy;
65
+ if (proxy) {
66
+ if (input.tokenProvider) {
67
+ throw new Error("proxy and tokenProvider are mutually exclusive: in proxy mode the SDK requests no tokens");
68
+ }
69
+ if (input.credentials.backend || input.credentials.custom) {
70
+ throw new Error(
71
+ "credentials.backend and credentials.custom cannot be used in proxy mode: the proxy attaches the token, and service credentials never belong in a browser"
72
+ );
73
+ }
74
+ }
64
75
  return {
65
76
  tenant: input.tenant,
66
77
  host: input.host ?? DEFAULT_HOST,
@@ -77,239 +88,13 @@ function validateConfig(input) {
77
88
  },
78
89
  logger: input.logger,
79
90
  fetch: input.fetch,
80
- contentLanguage: input.contentLanguage
81
- };
82
- }
83
-
84
- // src/core/logger.ts
85
- var LEVEL = {
86
- trace: 10,
87
- debug: 20,
88
- info: 30,
89
- warn: 40,
90
- error: 50,
91
- silent: 60
92
- };
93
- function isValidLevel(v) {
94
- return v !== void 0 && v in LEVEL;
95
- }
96
- function readEnv(name) {
97
- try {
98
- return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
99
- } catch {
100
- return void 0;
101
- }
102
- }
103
- var LevelResolver = class {
104
- constructor(cfg, warn = () => {
105
- }) {
106
- this.warn = warn;
107
- this.cfgLevel = cfg.level ?? "warn";
108
- this.cfgServices = { ...cfg.services };
109
- }
110
- warn;
111
- cfgLevel;
112
- cfgServices;
113
- forcedServices = {};
114
- forcedGlobal;
115
- warned = false;
116
- envFor(svc) {
117
- const raw = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`);
118
- if (raw === void 0) return void 0;
119
- if (isValidLevel(raw)) return raw;
120
- if (!this.warned) {
121
- this.warned = true;
122
- this.warn(`Invalid EMPORIX_LOG_LEVEL_${svc.toUpperCase()}="${raw}" ignored`);
123
- }
124
- return void 0;
125
- }
126
- envGlobal() {
127
- const raw = readEnv("EMPORIX_LOG_LEVEL");
128
- if (raw === void 0) return void 0;
129
- if (isValidLevel(raw)) return raw;
130
- if (!this.warned) {
131
- this.warned = true;
132
- this.warn(`Invalid EMPORIX_LOG_LEVEL="${raw}" ignored`);
133
- }
134
- return void 0;
135
- }
136
- /**
137
- * Effective level for a service. Forced overrides (from `set(..., force)`)
138
- * take precedence over env vars; otherwise env > programmatic config.
139
- */
140
- get(svc) {
141
- return this.forcedServices[svc] ?? this.envFor(svc) ?? this.forcedGlobal ?? this.envGlobal() ?? this.cfgServices[svc] ?? this.cfgLevel ?? "warn";
142
- }
143
- /** Numeric effective level for a service. */
144
- numericLevel(svc) {
145
- return LEVEL[this.get(svc)];
146
- }
147
- /** True if `svc`'s effective level allows emitting at `at`. */
148
- isAtLeast(svc, at) {
149
- return this.numericLevel(svc) <= LEVEL[at];
150
- }
151
- /** Mutates programmatic level (global or one service). Env-set levels are sticky unless `force`. */
152
- set(level, svc, force = false) {
153
- if (svc) {
154
- const envBound = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`) !== void 0;
155
- if (envBound && !force) {
156
- this.warn(`Level for "${svc}" is env-controlled; pass force to override`);
157
- return;
158
- }
159
- if (force) this.forcedServices[svc] = level;
160
- else this.cfgServices[svc] = level;
161
- } else {
162
- const envBound = readEnv("EMPORIX_LOG_LEVEL") !== void 0;
163
- if (envBound && !force) {
164
- this.warn("Global level is env-controlled; pass force to override");
165
- return;
166
- }
167
- if (force) this.forcedGlobal = level;
168
- else this.cfgLevel = level;
169
- }
170
- }
171
- };
172
- var DEFAULT_REDACT = /* @__PURE__ */ new Set([
173
- "authorization",
174
- "password",
175
- "oldpassword",
176
- "newpassword",
177
- "clientsecret",
178
- "secret",
179
- "access_token",
180
- "refresh_token",
181
- "customertoken",
182
- "saastoken",
183
- "saas-token",
184
- "bearertoken",
185
- "apikey",
186
- "token"
187
- ]);
188
- function redact(value, extra = []) {
189
- const keys = new Set(DEFAULT_REDACT);
190
- for (const k of extra) keys.add(k.toLowerCase());
191
- const walk = (v) => {
192
- if (Array.isArray(v)) return v.map(walk);
193
- if (v && typeof v === "object") {
194
- const src = v;
195
- if (typeof src.kind === "string" && "token" in src) return { kind: src.kind };
196
- const out = {};
197
- for (const [k, val] of Object.entries(src)) {
198
- if (keys.has(k.toLowerCase())) {
199
- out[k] = k.toLowerCase() === "authorization" && typeof val === "string" ? "Bearer ***redacted***" : "***redacted***";
200
- } else {
201
- out[k] = walk(val);
202
- }
203
- }
204
- return out;
205
- }
206
- return v;
207
- };
208
- return walk(value);
209
- }
210
- var EMIT = {
211
- trace: LEVEL.trace,
212
- debug: LEVEL.debug,
213
- info: LEVEL.info,
214
- warn: LEVEL.warn,
215
- error: LEVEL.error
216
- };
217
- var BaseLogger = class {
218
- constructor(resolver, bindings, extraRedact) {
219
- this.resolver = resolver;
220
- this.bindings = bindings;
221
- this.extraRedact = extraRedact;
222
- }
223
- resolver;
224
- bindings;
225
- extraRedact;
226
- svc() {
227
- return this.bindings.service ?? "http";
228
- }
229
- get level() {
230
- return this.resolver.get(this.svc());
231
- }
232
- isLevelEnabled(level) {
233
- return this.resolver.isAtLeast(this.svc(), level);
234
- }
235
- at(level, msg, fields) {
236
- if (this.resolver.numericLevel(this.svc()) > EMIT[level]) return;
237
- const merged = { ...this.bindings, ...fields ?? {} };
238
- this.emit(level, msg, redact(merged, this.extraRedact));
239
- }
240
- trace(m, f) {
241
- this.at("trace", m, f);
242
- }
243
- debug(m, f) {
244
- this.at("debug", m, f);
245
- }
246
- info(m, f) {
247
- this.at("info", m, f);
248
- }
249
- warn(m, f) {
250
- this.at("warn", m, f);
251
- }
252
- error(m, f) {
253
- this.at("error", m, f);
254
- }
255
- };
256
- var ConsoleLogger = class _ConsoleLogger extends BaseLogger {
257
- constructor(resolver, bindings, extra, sink, pretty) {
258
- super(resolver, bindings, extra);
259
- this.sink = sink;
260
- this.pretty = pretty;
261
- }
262
- sink;
263
- pretty;
264
- emit(level, msg, fields) {
265
- if (this.pretty) {
266
- const fn = level === "error" ? this.sink.error : level === "warn" ? this.sink.warn : level === "info" ? this.sink.info : this.sink.log;
267
- fn(`[${level}] ${msg}`, fields);
268
- } else {
269
- this.sink.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...fields }));
270
- }
271
- }
272
- child(bindings) {
273
- return new _ConsoleLogger(
274
- this.resolver,
275
- { ...this.bindings, ...bindings },
276
- this.extraRedact,
277
- this.sink,
278
- this.pretty
279
- );
280
- }
281
- };
282
- var NoopLogger = class {
283
- level = "silent";
284
- isLevelEnabled() {
285
- return false;
286
- }
287
- trace() {
288
- }
289
- debug() {
290
- }
291
- info() {
292
- }
293
- warn() {
294
- }
295
- error() {
296
- }
297
- child() {
298
- return this;
299
- }
300
- };
301
- function createConsoleLogger(resolver, bindings = {}, opts = {}) {
302
- const pretty = opts.pretty ?? readEnv("NODE_ENV") !== "production";
303
- const sink = opts.sink ?? {
304
- log: console.log,
305
- info: console.info,
306
- warn: console.warn,
307
- error: console.error
91
+ contentLanguage: input.contentLanguage,
92
+ proxy: proxy ? {
93
+ headers: proxy.headers ?? {},
94
+ credentials: proxy.credentials ?? "same-origin",
95
+ onUnauthorized: proxy.onUnauthorized
96
+ } : void 0
308
97
  };
309
- return new ConsoleLogger(resolver, bindings, opts.redact ?? [], sink, pretty);
310
- }
311
- function createNoopLogger() {
312
- return new NoopLogger();
313
98
  }
314
99
 
315
100
  // src/core/session-keys.ts
@@ -534,46 +319,343 @@ function createLocalStorage(opts = {}) {
534
319
  console.warn("[emporix] localStorage unavailable; falling back to in-memory storage");
535
320
  return createMemoryStorage();
536
321
  }
537
- return fromWebStorage(store, opts);
538
- }
539
- var createLocalStorageStorage = createLocalStorage;
540
- function createSessionStorage(opts = {}) {
541
- const store = browser.sessionStorage;
542
- if (store === void 0) {
543
- console.warn("[emporix] sessionStorage unavailable; falling back to in-memory storage");
544
- return createMemoryStorage();
322
+ return fromWebStorage(store, opts);
323
+ }
324
+ var createLocalStorageStorage = createLocalStorage;
325
+ function createSessionStorage(opts = {}) {
326
+ const store = browser.sessionStorage;
327
+ if (store === void 0) {
328
+ console.warn("[emporix] sessionStorage unavailable; falling back to in-memory storage");
329
+ return createMemoryStorage();
330
+ }
331
+ return fromWebStorage(store, opts);
332
+ }
333
+ function createCookieStorage(opts = {}) {
334
+ const sameSite = opts.sameSite ?? "lax";
335
+ const secure = opts.secure ?? browser.location?.protocol === "https:";
336
+ const doc = browser.document;
337
+ if (doc === void 0) {
338
+ console.warn("[emporix] document unavailable; cookie storage falling back to in-memory");
339
+ return createMemoryStorage();
340
+ }
341
+ const attrs = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
342
+ const all = createListenerSet();
343
+ const storage = createCookieBackedStorage(
344
+ {
345
+ get: (name) => {
346
+ for (const part of doc.cookie.split("; ")) {
347
+ const [k, ...v] = part.split("=");
348
+ if (k === name) return decodeURIComponent(v.join("=")) || null;
349
+ }
350
+ return null;
351
+ },
352
+ set: (name, value) => {
353
+ doc.cookie = value === null ? `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${attrs}` : `${name}=${encodeURIComponent(value)}; ${attrs}`;
354
+ }
355
+ },
356
+ {
357
+ ...opts.name !== void 0 ? { tokenName: opts.name } : {},
358
+ notify: (key) => all.notify(key)
359
+ }
360
+ );
361
+ return { ...storage, subscribeAll: (l) => all.add(l) };
362
+ }
363
+
364
+ // src/core/proxy.ts
365
+ var PROXY_CUSTOMER_TOKEN = "emporix-proxy-session";
366
+ function createProxyModeTokenProvider() {
367
+ const refuse = () => {
368
+ throw new EmporixAuthError(
369
+ "The client runs in proxy mode: the proxy attaches the token, so the SDK requests none"
370
+ );
371
+ };
372
+ return {
373
+ getToken: async () => refuse(),
374
+ getAnonymousToken: async () => refuse(),
375
+ refreshAnonymous: async () => refuse(),
376
+ setAnonymousContext: () => {
377
+ }
378
+ };
379
+ }
380
+ var TOKEN_KEYS = /* @__PURE__ */ new Set([
381
+ "customerToken",
382
+ "anonymousSession",
383
+ "refreshToken",
384
+ "saasToken"
385
+ ]);
386
+ function createProxySessionStorage(base, authenticated) {
387
+ let current = authenticated;
388
+ const tokenListeners = createListenerSet();
389
+ const keyListeners = createListenerSet();
390
+ const token = () => current ? PROXY_CUSTOMER_TOKEN : null;
391
+ const drop = () => {
392
+ };
393
+ return {
394
+ getCustomerToken: token,
395
+ setCustomerToken: drop,
396
+ subscribe: (listener) => tokenListeners.add(listener),
397
+ getCartId: () => base.getCartId(),
398
+ setCartId: (id) => base.setCartId(id),
399
+ getAnonymousSession: () => null,
400
+ setAnonymousSession: drop,
401
+ getSiteCode: () => base.getSiteCode(),
402
+ setSiteCode: (code) => base.setSiteCode(code),
403
+ getLanguage: () => base.getLanguage(),
404
+ setLanguage: (language) => base.setLanguage(language),
405
+ getActiveLegalEntityId: () => base.getActiveLegalEntityId(),
406
+ setActiveLegalEntityId: (id) => base.setActiveLegalEntityId(id),
407
+ getRefreshToken: () => null,
408
+ setRefreshToken: drop,
409
+ getSaasToken: () => null,
410
+ setSaasToken: drop,
411
+ subscribeAll(listener) {
412
+ const own = keyListeners.add(listener);
413
+ const passed = base.subscribeAll?.((key) => {
414
+ if (!TOKEN_KEYS.has(key)) listener(key);
415
+ });
416
+ return () => {
417
+ own();
418
+ passed?.();
419
+ };
420
+ },
421
+ setProxyAuthenticated(next) {
422
+ if (next === current) return;
423
+ current = next;
424
+ tokenListeners.notify(token());
425
+ keyListeners.notify("customerToken");
426
+ }
427
+ };
428
+ }
429
+
430
+ // src/core/logger.ts
431
+ var LEVEL = {
432
+ trace: 10,
433
+ debug: 20,
434
+ info: 30,
435
+ warn: 40,
436
+ error: 50,
437
+ silent: 60
438
+ };
439
+ function isValidLevel(v) {
440
+ return v !== void 0 && v in LEVEL;
441
+ }
442
+ function readEnv(name) {
443
+ try {
444
+ return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
445
+ } catch {
446
+ return void 0;
447
+ }
448
+ }
449
+ var LevelResolver = class {
450
+ constructor(cfg, warn = () => {
451
+ }) {
452
+ this.warn = warn;
453
+ this.cfgLevel = cfg.level ?? "warn";
454
+ this.cfgServices = { ...cfg.services };
455
+ }
456
+ warn;
457
+ cfgLevel;
458
+ cfgServices;
459
+ forcedServices = {};
460
+ forcedGlobal;
461
+ warned = false;
462
+ envFor(svc) {
463
+ const raw = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`);
464
+ if (raw === void 0) return void 0;
465
+ if (isValidLevel(raw)) return raw;
466
+ if (!this.warned) {
467
+ this.warned = true;
468
+ this.warn(`Invalid EMPORIX_LOG_LEVEL_${svc.toUpperCase()}="${raw}" ignored`);
469
+ }
470
+ return void 0;
471
+ }
472
+ envGlobal() {
473
+ const raw = readEnv("EMPORIX_LOG_LEVEL");
474
+ if (raw === void 0) return void 0;
475
+ if (isValidLevel(raw)) return raw;
476
+ if (!this.warned) {
477
+ this.warned = true;
478
+ this.warn(`Invalid EMPORIX_LOG_LEVEL="${raw}" ignored`);
479
+ }
480
+ return void 0;
481
+ }
482
+ /**
483
+ * Effective level for a service. Forced overrides (from `set(..., force)`)
484
+ * take precedence over env vars; otherwise env > programmatic config.
485
+ */
486
+ get(svc) {
487
+ return this.forcedServices[svc] ?? this.envFor(svc) ?? this.forcedGlobal ?? this.envGlobal() ?? this.cfgServices[svc] ?? this.cfgLevel ?? "warn";
488
+ }
489
+ /** Numeric effective level for a service. */
490
+ numericLevel(svc) {
491
+ return LEVEL[this.get(svc)];
545
492
  }
546
- return fromWebStorage(store, opts);
547
- }
548
- function createCookieStorage(opts = {}) {
549
- const sameSite = opts.sameSite ?? "lax";
550
- const secure = opts.secure ?? browser.location?.protocol === "https:";
551
- const doc = browser.document;
552
- if (doc === void 0) {
553
- console.warn("[emporix] document unavailable; cookie storage falling back to in-memory");
554
- return createMemoryStorage();
493
+ /** True if `svc`'s effective level allows emitting at `at`. */
494
+ isAtLeast(svc, at) {
495
+ return this.numericLevel(svc) <= LEVEL[at];
555
496
  }
556
- const attrs = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
557
- const all = createListenerSet();
558
- const storage = createCookieBackedStorage(
559
- {
560
- get: (name) => {
561
- for (const part of doc.cookie.split("; ")) {
562
- const [k, ...v] = part.split("=");
563
- if (k === name) return decodeURIComponent(v.join("=")) || null;
497
+ /** Mutates programmatic level (global or one service). Env-set levels are sticky unless `force`. */
498
+ set(level, svc, force = false) {
499
+ if (svc) {
500
+ const envBound = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`) !== void 0;
501
+ if (envBound && !force) {
502
+ this.warn(`Level for "${svc}" is env-controlled; pass force to override`);
503
+ return;
504
+ }
505
+ if (force) this.forcedServices[svc] = level;
506
+ else this.cfgServices[svc] = level;
507
+ } else {
508
+ const envBound = readEnv("EMPORIX_LOG_LEVEL") !== void 0;
509
+ if (envBound && !force) {
510
+ this.warn("Global level is env-controlled; pass force to override");
511
+ return;
512
+ }
513
+ if (force) this.forcedGlobal = level;
514
+ else this.cfgLevel = level;
515
+ }
516
+ }
517
+ };
518
+ var DEFAULT_REDACT = /* @__PURE__ */ new Set([
519
+ "authorization",
520
+ "password",
521
+ "oldpassword",
522
+ "newpassword",
523
+ "clientsecret",
524
+ "secret",
525
+ "access_token",
526
+ "refresh_token",
527
+ "customertoken",
528
+ "saastoken",
529
+ "saas-token",
530
+ "bearertoken",
531
+ "apikey",
532
+ "token"
533
+ ]);
534
+ function redact(value, extra = []) {
535
+ const keys = new Set(DEFAULT_REDACT);
536
+ for (const k of extra) keys.add(k.toLowerCase());
537
+ const walk = (v) => {
538
+ if (Array.isArray(v)) return v.map(walk);
539
+ if (v && typeof v === "object") {
540
+ const src = v;
541
+ if (typeof src.kind === "string" && "token" in src) return { kind: src.kind };
542
+ const out = {};
543
+ for (const [k, val] of Object.entries(src)) {
544
+ if (keys.has(k.toLowerCase())) {
545
+ out[k] = k.toLowerCase() === "authorization" && typeof val === "string" ? "Bearer ***redacted***" : "***redacted***";
546
+ } else {
547
+ out[k] = walk(val);
564
548
  }
565
- return null;
566
- },
567
- set: (name, value) => {
568
- doc.cookie = value === null ? `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${attrs}` : `${name}=${encodeURIComponent(value)}; ${attrs}`;
569
549
  }
570
- },
571
- {
572
- ...opts.name !== void 0 ? { tokenName: opts.name } : {},
573
- notify: (key) => all.notify(key)
550
+ return out;
574
551
  }
575
- );
576
- return { ...storage, subscribeAll: (l) => all.add(l) };
552
+ return v;
553
+ };
554
+ return walk(value);
555
+ }
556
+ var EMIT = {
557
+ trace: LEVEL.trace,
558
+ debug: LEVEL.debug,
559
+ info: LEVEL.info,
560
+ warn: LEVEL.warn,
561
+ error: LEVEL.error
562
+ };
563
+ var BaseLogger = class {
564
+ constructor(resolver, bindings, extraRedact) {
565
+ this.resolver = resolver;
566
+ this.bindings = bindings;
567
+ this.extraRedact = extraRedact;
568
+ }
569
+ resolver;
570
+ bindings;
571
+ extraRedact;
572
+ svc() {
573
+ return this.bindings.service ?? "http";
574
+ }
575
+ get level() {
576
+ return this.resolver.get(this.svc());
577
+ }
578
+ isLevelEnabled(level) {
579
+ return this.resolver.isAtLeast(this.svc(), level);
580
+ }
581
+ at(level, msg, fields) {
582
+ if (this.resolver.numericLevel(this.svc()) > EMIT[level]) return;
583
+ const merged = { ...this.bindings, ...fields ?? {} };
584
+ this.emit(level, msg, redact(merged, this.extraRedact));
585
+ }
586
+ trace(m, f) {
587
+ this.at("trace", m, f);
588
+ }
589
+ debug(m, f) {
590
+ this.at("debug", m, f);
591
+ }
592
+ info(m, f) {
593
+ this.at("info", m, f);
594
+ }
595
+ warn(m, f) {
596
+ this.at("warn", m, f);
597
+ }
598
+ error(m, f) {
599
+ this.at("error", m, f);
600
+ }
601
+ };
602
+ var ConsoleLogger = class _ConsoleLogger extends BaseLogger {
603
+ constructor(resolver, bindings, extra, sink, pretty) {
604
+ super(resolver, bindings, extra);
605
+ this.sink = sink;
606
+ this.pretty = pretty;
607
+ }
608
+ sink;
609
+ pretty;
610
+ emit(level, msg, fields) {
611
+ if (this.pretty) {
612
+ const fn = level === "error" ? this.sink.error : level === "warn" ? this.sink.warn : level === "info" ? this.sink.info : this.sink.log;
613
+ fn(`[${level}] ${msg}`, fields);
614
+ } else {
615
+ this.sink.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...fields }));
616
+ }
617
+ }
618
+ child(bindings) {
619
+ return new _ConsoleLogger(
620
+ this.resolver,
621
+ { ...this.bindings, ...bindings },
622
+ this.extraRedact,
623
+ this.sink,
624
+ this.pretty
625
+ );
626
+ }
627
+ };
628
+ var NoopLogger = class {
629
+ level = "silent";
630
+ isLevelEnabled() {
631
+ return false;
632
+ }
633
+ trace() {
634
+ }
635
+ debug() {
636
+ }
637
+ info() {
638
+ }
639
+ warn() {
640
+ }
641
+ error() {
642
+ }
643
+ child() {
644
+ return this;
645
+ }
646
+ };
647
+ function createConsoleLogger(resolver, bindings = {}, opts = {}) {
648
+ const pretty = opts.pretty ?? readEnv("NODE_ENV") !== "production";
649
+ const sink = opts.sink ?? {
650
+ log: console.log,
651
+ info: console.info,
652
+ warn: console.warn,
653
+ error: console.error
654
+ };
655
+ return new ConsoleLogger(resolver, bindings, opts.redact ?? [], sink, pretty);
656
+ }
657
+ function createNoopLogger() {
658
+ return new NoopLogger();
577
659
  }
578
660
 
579
661
  // src/core/query-keys.ts
@@ -687,20 +769,72 @@ var HttpClient = class {
687
769
  sleep;
688
770
  /** Resolved request fetch. The arrow keeps `globalThis.fetch` correctly bound. */
689
771
  doFetch;
772
+ /**
773
+ * `null` in proxy mode, where no token is resolved at all — the provider is
774
+ * never asked, so no token request can happen. A service context is refused
775
+ * there rather than sent anonymously through the proxy.
776
+ */
777
+ async tokenFor(o) {
778
+ if (!this.opts.proxy) return resolveToken(o.auth, this.opts.provider);
779
+ if (o.auth.kind === "service") {
780
+ throw new EmporixAuthError(
781
+ `${o.method} ${o.path}: auth.service() is not available in proxy mode \u2014 service tokens never belong in a browser. Make this call from your server.`
782
+ );
783
+ }
784
+ return null;
785
+ }
786
+ /** `host + path` plus query. A relative host (`/emporix`) resolves against the page origin. */
787
+ urlFor(o) {
788
+ const host = this.opts.host;
789
+ let url;
790
+ if (host.startsWith("/")) {
791
+ const origin = globalThis.location?.origin;
792
+ if (!origin) {
793
+ throw new EmporixError(
794
+ `host "${host}" is a path, which needs a page origin to resolve against, and globalThis.location is not available here. Use an absolute host on a server.`
795
+ );
796
+ }
797
+ url = new URL(host + o.path, origin);
798
+ } else {
799
+ url = new URL(host + o.path);
800
+ }
801
+ for (const [k, v] of Object.entries(o.query ?? {})) {
802
+ if (v !== void 0) url.searchParams.set(k, String(v));
803
+ }
804
+ return url;
805
+ }
806
+ /** Proxy mode: report the 401 before it is thrown. A throwing callback must not mask it. */
807
+ reportUnauthorized(o) {
808
+ try {
809
+ this.opts.proxy?.onUnauthorized?.({ method: o.method, path: o.path });
810
+ } catch {
811
+ }
812
+ }
690
813
  buildHeaders(o, token, isFormData) {
691
- return {
814
+ const headers = {
692
815
  // Before `o.headers` on purpose: a caller may override the tenant for a
693
816
  // single request, the way `Accept-Language` and `Content-Language` can be
694
817
  // overridden. Only `Authorization` is non-negotiable.
695
818
  ...this.opts.tenant !== void 0 ? { "Emporix-Tenant": this.opts.tenant } : {},
696
819
  ...this.opts.requestContext?.language ? { "Accept-Language": this.opts.requestContext.language } : {},
697
820
  ...this.opts.contentLanguage && o.body !== void 0 ? { "Content-Language": this.opts.contentLanguage } : {},
821
+ ...this.opts.proxy?.headers ?? {},
698
822
  ...o.headers ?? {},
699
- Authorization: `Bearer ${token}`,
823
+ ...token !== null ? { Authorization: `Bearer ${token}` } : {},
700
824
  // JSON bodies: set Content-Type. FormData bodies: let `fetch`
701
825
  // emit `multipart/form-data; boundary=...` itself.
702
826
  ...o.body !== void 0 && !isFormData ? { "Content-Type": "application/json" } : {}
703
827
  };
828
+ if (token === null) {
829
+ for (const k of Object.keys(headers)) {
830
+ if (k.toLowerCase() === "authorization") delete headers[k];
831
+ }
832
+ }
833
+ return headers;
834
+ }
835
+ /** `fetch`'s `credentials` — set only in proxy mode, where the session cookie must go along. */
836
+ credentialsInit() {
837
+ return this.opts.proxy ? { credentials: this.opts.proxy.credentials } : {};
704
838
  }
705
839
  /**
706
840
  * Issues a request and returns the parsed body. Retries 5xx/429 on idempotent
@@ -723,17 +857,14 @@ var HttpClient = class {
723
857
  async send(o) {
724
858
  const requestId = `req-${++requestSeq}`;
725
859
  const log = this.opts.logger.child({ requestId });
726
- const url = new URL(this.opts.host + o.path);
727
- for (const [k, v] of Object.entries(o.query ?? {})) {
728
- if (v !== void 0) url.searchParams.set(k, String(v));
729
- }
860
+ const url = this.urlFor(o);
730
861
  const sdkManaged = o.auth.kind === "service" || o.auth.kind === "anonymous";
731
862
  const maxAttempts = this.opts.retry.maxAttempts;
732
863
  let reauthed = false;
733
864
  let customerToken = o.auth.kind === "customer" ? o.auth.token : void 0;
734
865
  let customerReauthed = false;
735
866
  for (let attempt = 1; ; attempt++) {
736
- const token = customerToken ?? await resolveToken(o.auth, this.opts.provider);
867
+ const token = !this.opts.proxy && customerToken !== void 0 ? customerToken : await this.tokenFor(o);
737
868
  log.debug("http request", {
738
869
  authKind: o.auth.kind,
739
870
  method: o.method,
@@ -757,7 +888,8 @@ var HttpClient = class {
757
888
  const init = {
758
889
  method: o.method,
759
890
  headers: this.buildHeaders(o, token, isFormData),
760
- signal: controller.signal
891
+ signal: controller.signal,
892
+ ...this.credentialsInit()
761
893
  };
762
894
  if (o.body !== void 0) {
763
895
  init.body = isFormData ? o.body : JSON.stringify(o.body);
@@ -790,6 +922,10 @@ var HttpClient = class {
790
922
  return { data: parsed, headers: res.headers };
791
923
  }
792
924
  if (res.status === 401) {
925
+ if (this.opts.proxy) {
926
+ this.reportUnauthorized(o);
927
+ throw errorFromResponse(res.status, `${o.method} ${o.path} \u2192 401`, parsed);
928
+ }
793
929
  if (sdkManaged && !reauthed) {
794
930
  reauthed = true;
795
931
  if (o.auth.kind === "service") {
@@ -842,11 +978,8 @@ var HttpClient = class {
842
978
  async requestRaw(o, extra) {
843
979
  const requestId = `req-${++requestSeq}`;
844
980
  const log = this.opts.logger.child({ requestId });
845
- const url = new URL(this.opts.host + o.path);
846
- for (const [k, v] of Object.entries(o.query ?? {})) {
847
- if (v !== void 0) url.searchParams.set(k, String(v));
848
- }
849
- const token = await resolveToken(o.auth, this.opts.provider);
981
+ const url = this.urlFor(o);
982
+ const token = await this.tokenFor(o);
850
983
  log.debug("http requestRaw", {
851
984
  authKind: o.auth.kind,
852
985
  method: o.method,
@@ -863,13 +996,15 @@ var HttpClient = class {
863
996
  method: o.method,
864
997
  headers: this.buildHeaders(o, token, isFormData),
865
998
  signal: controller.signal,
866
- ...extra?.redirect ? { redirect: extra.redirect } : {}
999
+ ...extra?.redirect ? { redirect: extra.redirect } : {},
1000
+ ...this.credentialsInit()
867
1001
  };
868
1002
  if (o.body !== void 0) {
869
1003
  init.body = isFormData ? o.body : JSON.stringify(o.body);
870
1004
  }
1005
+ let res;
871
1006
  try {
872
- return await this.doFetch(url, init);
1007
+ res = await this.doFetch(url, init);
873
1008
  } catch (err) {
874
1009
  if (err.name === "AbortError") {
875
1010
  throw new EmporixTimeoutError(
@@ -882,6 +1017,12 @@ var HttpClient = class {
882
1017
  } finally {
883
1018
  clearTimeout(timer);
884
1019
  }
1020
+ if (this.opts.proxy && res.status === 401) {
1021
+ this.reportUnauthorized(o);
1022
+ const text = await res.text().catch(() => "");
1023
+ throw errorFromResponse(401, `${o.method} ${o.path} \u2192 401`, text ? safeJson(text) : void 0);
1024
+ }
1025
+ return res;
885
1026
  }
886
1027
  /**
887
1028
  * Open a Server-Sent Events stream (`text/event-stream`) and yield parsed
@@ -893,11 +1034,8 @@ var HttpClient = class {
893
1034
  */
894
1035
  async *requestStream(o) {
895
1036
  const log = this.opts.logger.child({ requestId: `req-${++requestSeq}` });
896
- const url = new URL(this.opts.host + o.path);
897
- for (const [k, v] of Object.entries(o.query ?? {})) {
898
- if (v !== void 0) url.searchParams.set(k, String(v));
899
- }
900
- const token = await resolveToken(o.auth, this.opts.provider);
1037
+ const url = this.urlFor(o);
1038
+ const token = await this.tokenFor(o);
901
1039
  const controller = new AbortController();
902
1040
  const connectTimer = setTimeout(() => controller.abort(), this.opts.timeouts.connectMs);
903
1041
  const headers = this.buildHeaders(o, token, false);
@@ -908,7 +1046,8 @@ var HttpClient = class {
908
1046
  method: o.method,
909
1047
  headers,
910
1048
  signal: controller.signal,
911
- ...o.body !== void 0 ? { body: JSON.stringify(o.body) } : {}
1049
+ ...o.body !== void 0 ? { body: JSON.stringify(o.body) } : {},
1050
+ ...this.credentialsInit()
912
1051
  });
913
1052
  } catch (err) {
914
1053
  clearTimeout(connectTimer);
@@ -921,6 +1060,7 @@ var HttpClient = class {
921
1060
  }
922
1061
  clearTimeout(connectTimer);
923
1062
  if (!res.ok) {
1063
+ if (this.opts.proxy && res.status === 401) this.reportUnauthorized(o);
924
1064
  const text = await res.text().catch(() => "");
925
1065
  throw errorFromResponse(
926
1066
  res.status,
@@ -948,7 +1088,7 @@ var HttpClient = class {
948
1088
  };
949
1089
 
950
1090
  // package.json
951
- var version = "4.9.4";
1091
+ var version = "4.11.0";
952
1092
 
953
1093
  // src/version.ts
954
1094
  var SDK_VERSION = version;
@@ -971,8 +1111,19 @@ function createCore(config) {
971
1111
  sdkVersion: SDK_VERSION,
972
1112
  tenant: cfg.tenant
973
1113
  });
974
- const tokenProvider = cfg.tokenProvider ?? new DefaultTokenProvider(cfg);
1114
+ const tokenProvider = cfg.proxy ? createProxyModeTokenProvider() : cfg.tokenProvider ?? new DefaultTokenProvider(cfg);
975
1115
  const customerRefresh = new CustomerRefreshRegistry();
1116
+ const unauthorized = createListenerSet();
1117
+ const proxy = cfg.proxy && {
1118
+ ...cfg.proxy,
1119
+ onUnauthorized: (info) => {
1120
+ try {
1121
+ cfg.proxy?.onUnauthorized?.(info);
1122
+ } finally {
1123
+ unauthorized.notify(info);
1124
+ }
1125
+ }
1126
+ };
976
1127
  const requestContext = {
977
1128
  language: cfg.credentials.storefront?.context?.language
978
1129
  };
@@ -992,7 +1143,8 @@ function createCore(config) {
992
1143
  // exactOptionalPropertyTypes: `{ fetch: undefined }` is a type error
993
1144
  // against `fetch?: typeof globalThis.fetch`, so spread conditionally.
994
1145
  ...cfg.fetch !== void 0 ? { fetch: cfg.fetch } : {},
995
- ...cfg.contentLanguage !== void 0 ? { contentLanguage: cfg.contentLanguage } : {}
1146
+ ...cfg.contentLanguage !== void 0 ? { contentLanguage: cfg.contentLanguage } : {},
1147
+ ...proxy ? { proxy } : {}
996
1148
  })
997
1149
  });
998
1150
  return {
@@ -1017,6 +1169,9 @@ function createCore(config) {
1017
1169
  },
1018
1170
  setCustomerTokenRefresher(refresher) {
1019
1171
  customerRefresh.set(refresher);
1172
+ },
1173
+ onUnauthorized(listener) {
1174
+ return unauthorized.add(listener);
1020
1175
  }
1021
1176
  };
1022
1177
  }
@@ -4364,13 +4519,18 @@ var SearchService = class {
4364
4519
  return `/search/${this.ctx.tenant}`;
4365
4520
  }
4366
4521
  /**
4367
- * Search the documents of one custom schema type. Paging, `sort` and
4368
- * `fields` travel as query parameters, not in the body.
4522
+ * Search the documents of one type — a custom schema type, `customer` or
4523
+ * `legal-entity`. Paging, `sort` and `fields` travel as query parameters, not
4524
+ * in the body.
4369
4525
  *
4370
4526
  * Filter and sort fields must be indexed on the index the search uses —
4371
- * `exact`, or `text: true` for a `CONTAINS` filter — or the call answers `400`. A saved-search call (`{ searchQueryId, query }`) answers
4372
- * `404` when no saved search has that id for `type`, and `502` means the
4373
- * Schema Service could not confirm that `type` is a custom entity.
4527
+ * `exact`, or `text: true` for a `CONTAINS` filter — or the call answers
4528
+ * `400`. A `TEXT` or `PHRASE` query may pick the field's `analyzer` or one of
4529
+ * its `additionalAnalyzers`. Conditions joined by `and` on fields of one
4530
+ * `itemFields` list must match the same list item. A saved-search call
4531
+ * (`{ searchQueryId, query }`) answers `404` when no saved search has that id
4532
+ * for `type`, and `502` means the Schema Service could not confirm that a
4533
+ * custom `type` is a custom entity.
4374
4534
  *
4375
4535
  * @example
4376
4536
  * const hits = await client.search.search("VEHICLE", {
@@ -4395,7 +4555,7 @@ var SearchService = class {
4395
4555
  page
4396
4556
  );
4397
4557
  }
4398
- /** List saved searches of one custom schema type (`opts.type`), or of every type. */
4558
+ /** List saved searches of one type (`opts.type`), or of every type. */
4399
4559
  async listSavedSearches(opts = {}, auth2 = SERVICE22) {
4400
4560
  const { query, page } = pageQuery(opts);
4401
4561
  const t = opts.type;
@@ -4443,7 +4603,7 @@ var SearchService = class {
4443
4603
  auth: auth2
4444
4604
  });
4445
4605
  }
4446
- /** List search indexes of one custom schema type (`opts.type`), or of every type. */
4606
+ /** List search indexes of one type (`opts.type`), or of every type. */
4447
4607
  async listIndexes(opts = {}, auth2 = SERVICE22) {
4448
4608
  const { query, page } = pageQuery(opts);
4449
4609
  const t = opts.type;
@@ -4737,6 +4897,11 @@ var ImportService = class {
4737
4897
  * streams. Read it back with {@link getRun}, or follow it with
4738
4898
  * {@link streamRun}, before treating a resolved call as a started import.
4739
4899
  *
4900
+ * A configuration that does not exist answers `404` (since 2026-11-06; it used
4901
+ * to be a `500`). A real run with nothing to import answers `400`: every stream
4902
+ * it starts from reads uploaded files, and none has ever had one. Upload a file
4903
+ * with {@link uploadStreamFile} first. A dry run is not refused for this.
4904
+ *
4740
4905
  * Not retried on a 5xx: a POST that timed out may already have queued a run.
4741
4906
  *
4742
4907
  * @example
@@ -4987,7 +5152,7 @@ var ImportService = class {
4987
5152
  * {@link streamRun} exactly as you would a triggered run. Like one, it comes
4988
5153
  * back `QUEUED`, answers `409` while the configuration has a run `QUEUED`,
4989
5154
  * `RUNNING` or `RESUMABLE`, runs the published mappings and can finish
4990
- * `ABORTED`.
5155
+ * `ABORTED`. A run that does not exist answers `404`.
4991
5156
  *
4992
5157
  * Not retried on a 5xx, for the same reason {@link triggerRun} is not: a POST
4993
5158
  * that timed out may already have queued the retry.
@@ -5098,6 +5263,29 @@ var ImportService = class {
5098
5263
  auth: auth2
5099
5264
  });
5100
5265
  }
5266
+ /**
5267
+ * Put a `FAILED` file back at the end of the stream's queue, so the next run
5268
+ * reads the content the service already holds — no new upload. Use it after
5269
+ * fixing the cause of the failure, a mapping say. Resolves to the file,
5270
+ * `PENDING` again.
5271
+ *
5272
+ * Only a failed file can be queued again; `409` otherwise. A file imported with
5273
+ * some records failing is `PROCESSED`: retry those records with
5274
+ * {@link retryRun} instead. On a `SNAPSHOT` stream a failed file cannot be
5275
+ * queued again once a newer file arrived, since the older snapshot would be
5276
+ * imported over newer data — upload a corrected current file instead. `409`
5277
+ * also when the stored content is gone, and while the job has a run in flight.
5278
+ *
5279
+ * Not retried on a 5xx: if the first attempt went through, the file is no
5280
+ * longer `FAILED` and a repeat answers `409`.
5281
+ */
5282
+ async requeueStreamFile(streamId, fileId, auth2 = SERVICE23) {
5283
+ return this.ctx.http.request({
5284
+ method: "POST",
5285
+ path: `${this.base()}/streams/${encodeURIComponent(streamId)}/files/${encodeURIComponent(fileId)}/requeue`,
5286
+ auth: auth2
5287
+ });
5288
+ }
5101
5289
  };
5102
5290
 
5103
5291
  // src/services/audit-log.ts
@@ -5830,6 +6018,15 @@ var EmporixClient = class {
5830
6018
  setCustomerTokenRefresher(refresher) {
5831
6019
  this.core.setCustomerTokenRefresher(refresher);
5832
6020
  }
6021
+ /**
6022
+ * Proxy mode (`config.proxy`): subscribes to the 401s the proxy answers, next
6023
+ * to `proxy.onUnauthorized`. Returns the unsubscribe function. The React
6024
+ * `EmporixProvider` uses it for `onCustomerSessionExpired`. Never fires outside
6025
+ * proxy mode — there a 401 is answered by re-authenticating.
6026
+ */
6027
+ onUnauthorized(listener) {
6028
+ return this.core.onUnauthorized(listener);
6029
+ }
5833
6030
  };
5834
6031
 
5835
6032
  // src/create-emporix-client.ts
@@ -5866,6 +6063,6 @@ function productYrn(tenant, productId) {
5866
6063
  return `urn:yaas:hybris:product:product:${tenant};${productId}`;
5867
6064
  }
5868
6065
 
5869
- export { AiService, ApprovalService, AuditLogService, BrandService, CatalogService, ClientConfigService, CloudFunctionsService, CountryService, CouponService, CurrencyService, CustomerAdminService, DEFAULT_HOST, EmporixClient, FeeService, HttpClient, ImportService, IndexingService, InvoiceService, LEVEL, LabelService, LevelResolver, QuoteService, RagIndexerService, ReturnsService, RewardPointsService, STORAGE_KEYS, SchemaService, SearchService, SequentialIdService, SessionContextService, ShippingService, ShoppingListService, SiteService, TaxService, TenantConfigService, UnitHandlingService, VendorService, WebhookService, approvalStatusPatch, createConsoleLogger, createCookieBackedStorage, createCookieStorage, createCore, createEmporixClient, createListenerSet, createLocalStorage, createLocalStorageStorage, createMemoryStorage, createNoopLogger, createServerStorage, createSessionStorage, emporixKey, fromWebStorage, getCustomerSessionStore, parseAnonymousSession, pickFee, productIdFromYrn, productYrn, redact, resolveZone, serverAuth, siteMeta, validateConfig };
6066
+ export { AiService, ApprovalService, AuditLogService, BrandService, CatalogService, ClientConfigService, CloudFunctionsService, CountryService, CouponService, CurrencyService, CustomerAdminService, DEFAULT_HOST, EmporixClient, FeeService, HttpClient, ImportService, IndexingService, InvoiceService, LEVEL, LabelService, LevelResolver, PROXY_CUSTOMER_TOKEN, QuoteService, RagIndexerService, ReturnsService, RewardPointsService, STORAGE_KEYS, SchemaService, SearchService, SequentialIdService, SessionContextService, ShippingService, ShoppingListService, SiteService, TaxService, TenantConfigService, UnitHandlingService, VendorService, WebhookService, approvalStatusPatch, createConsoleLogger, createCookieBackedStorage, createCookieStorage, createCore, createEmporixClient, createListenerSet, createLocalStorage, createLocalStorageStorage, createMemoryStorage, createNoopLogger, createProxyModeTokenProvider, createProxySessionStorage, createServerStorage, createSessionStorage, emporixKey, fromWebStorage, getCustomerSessionStore, parseAnonymousSession, pickFee, productIdFromYrn, productYrn, redact, resolveZone, serverAuth, siteMeta, validateConfig };
5870
6067
  //# sourceMappingURL=index.js.map
5871
6068
  //# sourceMappingURL=index.js.map