@viu/emporix-sdk 4.10.0 → 4.11.1

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 +27 -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 +458 -291
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +208 -33
  26. package/dist/index.d.ts +208 -33
  27. package/dist/index.js +457 -293
  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.cjs CHANGED
@@ -114,6 +114,17 @@ function validateConfig(input) {
114
114
  throw new Error("credentials is required (provide at least one of backend/storefront/custom)");
115
115
  }
116
116
  warnOnMisplacedContext(input);
117
+ const proxy = input.proxy;
118
+ if (proxy) {
119
+ if (input.tokenProvider) {
120
+ throw new Error("proxy and tokenProvider are mutually exclusive: in proxy mode the SDK requests no tokens");
121
+ }
122
+ if (input.credentials.backend || input.credentials.custom) {
123
+ throw new Error(
124
+ "credentials.backend and credentials.custom cannot be used in proxy mode: the proxy attaches the token, and service credentials never belong in a browser"
125
+ );
126
+ }
127
+ }
117
128
  return {
118
129
  tenant: input.tenant,
119
130
  host: input.host ?? DEFAULT_HOST,
@@ -130,241 +141,15 @@ function validateConfig(input) {
130
141
  },
131
142
  logger: input.logger,
132
143
  fetch: input.fetch,
133
- contentLanguage: input.contentLanguage
144
+ contentLanguage: input.contentLanguage,
145
+ proxy: proxy ? {
146
+ headers: proxy.headers ?? {},
147
+ credentials: proxy.credentials ?? "same-origin",
148
+ onUnauthorized: proxy.onUnauthorized
149
+ } : void 0
134
150
  };
135
151
  }
136
152
 
137
- // src/core/logger.ts
138
- var LEVEL = {
139
- trace: 10,
140
- debug: 20,
141
- info: 30,
142
- warn: 40,
143
- error: 50,
144
- silent: 60
145
- };
146
- function isValidLevel(v) {
147
- return v !== void 0 && v in LEVEL;
148
- }
149
- function readEnv(name) {
150
- try {
151
- return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
152
- } catch {
153
- return void 0;
154
- }
155
- }
156
- var LevelResolver = class {
157
- constructor(cfg, warn = () => {
158
- }) {
159
- this.warn = warn;
160
- this.cfgLevel = cfg.level ?? "warn";
161
- this.cfgServices = { ...cfg.services };
162
- }
163
- warn;
164
- cfgLevel;
165
- cfgServices;
166
- forcedServices = {};
167
- forcedGlobal;
168
- warned = false;
169
- envFor(svc) {
170
- const raw = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`);
171
- if (raw === void 0) return void 0;
172
- if (isValidLevel(raw)) return raw;
173
- if (!this.warned) {
174
- this.warned = true;
175
- this.warn(`Invalid EMPORIX_LOG_LEVEL_${svc.toUpperCase()}="${raw}" ignored`);
176
- }
177
- return void 0;
178
- }
179
- envGlobal() {
180
- const raw = readEnv("EMPORIX_LOG_LEVEL");
181
- if (raw === void 0) return void 0;
182
- if (isValidLevel(raw)) return raw;
183
- if (!this.warned) {
184
- this.warned = true;
185
- this.warn(`Invalid EMPORIX_LOG_LEVEL="${raw}" ignored`);
186
- }
187
- return void 0;
188
- }
189
- /**
190
- * Effective level for a service. Forced overrides (from `set(..., force)`)
191
- * take precedence over env vars; otherwise env > programmatic config.
192
- */
193
- get(svc) {
194
- return this.forcedServices[svc] ?? this.envFor(svc) ?? this.forcedGlobal ?? this.envGlobal() ?? this.cfgServices[svc] ?? this.cfgLevel ?? "warn";
195
- }
196
- /** Numeric effective level for a service. */
197
- numericLevel(svc) {
198
- return LEVEL[this.get(svc)];
199
- }
200
- /** True if `svc`'s effective level allows emitting at `at`. */
201
- isAtLeast(svc, at) {
202
- return this.numericLevel(svc) <= LEVEL[at];
203
- }
204
- /** Mutates programmatic level (global or one service). Env-set levels are sticky unless `force`. */
205
- set(level, svc, force = false) {
206
- if (svc) {
207
- const envBound = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`) !== void 0;
208
- if (envBound && !force) {
209
- this.warn(`Level for "${svc}" is env-controlled; pass force to override`);
210
- return;
211
- }
212
- if (force) this.forcedServices[svc] = level;
213
- else this.cfgServices[svc] = level;
214
- } else {
215
- const envBound = readEnv("EMPORIX_LOG_LEVEL") !== void 0;
216
- if (envBound && !force) {
217
- this.warn("Global level is env-controlled; pass force to override");
218
- return;
219
- }
220
- if (force) this.forcedGlobal = level;
221
- else this.cfgLevel = level;
222
- }
223
- }
224
- };
225
- var DEFAULT_REDACT = /* @__PURE__ */ new Set([
226
- "authorization",
227
- "password",
228
- "oldpassword",
229
- "newpassword",
230
- "clientsecret",
231
- "secret",
232
- "access_token",
233
- "refresh_token",
234
- "customertoken",
235
- "saastoken",
236
- "saas-token",
237
- "bearertoken",
238
- "apikey",
239
- "token"
240
- ]);
241
- function redact(value, extra = []) {
242
- const keys = new Set(DEFAULT_REDACT);
243
- for (const k of extra) keys.add(k.toLowerCase());
244
- const walk = (v) => {
245
- if (Array.isArray(v)) return v.map(walk);
246
- if (v && typeof v === "object") {
247
- const src = v;
248
- if (typeof src.kind === "string" && "token" in src) return { kind: src.kind };
249
- const out = {};
250
- for (const [k, val] of Object.entries(src)) {
251
- if (keys.has(k.toLowerCase())) {
252
- out[k] = k.toLowerCase() === "authorization" && typeof val === "string" ? "Bearer ***redacted***" : "***redacted***";
253
- } else {
254
- out[k] = walk(val);
255
- }
256
- }
257
- return out;
258
- }
259
- return v;
260
- };
261
- return walk(value);
262
- }
263
- var EMIT = {
264
- trace: LEVEL.trace,
265
- debug: LEVEL.debug,
266
- info: LEVEL.info,
267
- warn: LEVEL.warn,
268
- error: LEVEL.error
269
- };
270
- var BaseLogger = class {
271
- constructor(resolver, bindings, extraRedact) {
272
- this.resolver = resolver;
273
- this.bindings = bindings;
274
- this.extraRedact = extraRedact;
275
- }
276
- resolver;
277
- bindings;
278
- extraRedact;
279
- svc() {
280
- return this.bindings.service ?? "http";
281
- }
282
- get level() {
283
- return this.resolver.get(this.svc());
284
- }
285
- isLevelEnabled(level) {
286
- return this.resolver.isAtLeast(this.svc(), level);
287
- }
288
- at(level, msg, fields) {
289
- if (this.resolver.numericLevel(this.svc()) > EMIT[level]) return;
290
- const merged = { ...this.bindings, ...fields ?? {} };
291
- this.emit(level, msg, redact(merged, this.extraRedact));
292
- }
293
- trace(m, f) {
294
- this.at("trace", m, f);
295
- }
296
- debug(m, f) {
297
- this.at("debug", m, f);
298
- }
299
- info(m, f) {
300
- this.at("info", m, f);
301
- }
302
- warn(m, f) {
303
- this.at("warn", m, f);
304
- }
305
- error(m, f) {
306
- this.at("error", m, f);
307
- }
308
- };
309
- var ConsoleLogger = class _ConsoleLogger extends BaseLogger {
310
- constructor(resolver, bindings, extra, sink, pretty) {
311
- super(resolver, bindings, extra);
312
- this.sink = sink;
313
- this.pretty = pretty;
314
- }
315
- sink;
316
- pretty;
317
- emit(level, msg, fields) {
318
- if (this.pretty) {
319
- const fn = level === "error" ? this.sink.error : level === "warn" ? this.sink.warn : level === "info" ? this.sink.info : this.sink.log;
320
- fn(`[${level}] ${msg}`, fields);
321
- } else {
322
- this.sink.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...fields }));
323
- }
324
- }
325
- child(bindings) {
326
- return new _ConsoleLogger(
327
- this.resolver,
328
- { ...this.bindings, ...bindings },
329
- this.extraRedact,
330
- this.sink,
331
- this.pretty
332
- );
333
- }
334
- };
335
- var NoopLogger = class {
336
- level = "silent";
337
- isLevelEnabled() {
338
- return false;
339
- }
340
- trace() {
341
- }
342
- debug() {
343
- }
344
- info() {
345
- }
346
- warn() {
347
- }
348
- error() {
349
- }
350
- child() {
351
- return this;
352
- }
353
- };
354
- function createConsoleLogger(resolver, bindings = {}, opts = {}) {
355
- const pretty = opts.pretty ?? readEnv("NODE_ENV") !== "production";
356
- const sink = opts.sink ?? {
357
- log: console.log,
358
- info: console.info,
359
- warn: console.warn,
360
- error: console.error
361
- };
362
- return new ConsoleLogger(resolver, bindings, opts.redact ?? [], sink, pretty);
363
- }
364
- function createNoopLogger() {
365
- return new NoopLogger();
366
- }
367
-
368
153
  // src/core/auth.ts
369
154
  var auth = {
370
155
  /** Service/custom credential set (default `backend`). */
@@ -833,46 +618,343 @@ function createLocalStorage(opts = {}) {
833
618
  console.warn("[emporix] localStorage unavailable; falling back to in-memory storage");
834
619
  return createMemoryStorage();
835
620
  }
836
- return fromWebStorage(store, opts);
837
- }
838
- var createLocalStorageStorage = createLocalStorage;
839
- function createSessionStorage(opts = {}) {
840
- const store = browser.sessionStorage;
841
- if (store === void 0) {
842
- console.warn("[emporix] sessionStorage unavailable; falling back to in-memory storage");
843
- return createMemoryStorage();
621
+ return fromWebStorage(store, opts);
622
+ }
623
+ var createLocalStorageStorage = createLocalStorage;
624
+ function createSessionStorage(opts = {}) {
625
+ const store = browser.sessionStorage;
626
+ if (store === void 0) {
627
+ console.warn("[emporix] sessionStorage unavailable; falling back to in-memory storage");
628
+ return createMemoryStorage();
629
+ }
630
+ return fromWebStorage(store, opts);
631
+ }
632
+ function createCookieStorage(opts = {}) {
633
+ const sameSite = opts.sameSite ?? "lax";
634
+ const secure = opts.secure ?? browser.location?.protocol === "https:";
635
+ const doc = browser.document;
636
+ if (doc === void 0) {
637
+ console.warn("[emporix] document unavailable; cookie storage falling back to in-memory");
638
+ return createMemoryStorage();
639
+ }
640
+ const attrs = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
641
+ const all = createListenerSet();
642
+ const storage = createCookieBackedStorage(
643
+ {
644
+ get: (name) => {
645
+ for (const part of doc.cookie.split("; ")) {
646
+ const [k, ...v] = part.split("=");
647
+ if (k === name) return decodeURIComponent(v.join("=")) || null;
648
+ }
649
+ return null;
650
+ },
651
+ set: (name, value) => {
652
+ doc.cookie = value === null ? `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${attrs}` : `${name}=${encodeURIComponent(value)}; ${attrs}`;
653
+ }
654
+ },
655
+ {
656
+ ...opts.name !== void 0 ? { tokenName: opts.name } : {},
657
+ notify: (key) => all.notify(key)
658
+ }
659
+ );
660
+ return { ...storage, subscribeAll: (l) => all.add(l) };
661
+ }
662
+
663
+ // src/core/proxy.ts
664
+ var PROXY_CUSTOMER_TOKEN = "emporix-proxy-session";
665
+ function createProxyModeTokenProvider() {
666
+ const refuse = () => {
667
+ throw new EmporixAuthError(
668
+ "The client runs in proxy mode: the proxy attaches the token, so the SDK requests none"
669
+ );
670
+ };
671
+ return {
672
+ getToken: async () => refuse(),
673
+ getAnonymousToken: async () => refuse(),
674
+ refreshAnonymous: async () => refuse(),
675
+ setAnonymousContext: () => {
676
+ }
677
+ };
678
+ }
679
+ var TOKEN_KEYS = /* @__PURE__ */ new Set([
680
+ "customerToken",
681
+ "anonymousSession",
682
+ "refreshToken",
683
+ "saasToken"
684
+ ]);
685
+ function createProxySessionStorage(base, authenticated) {
686
+ let current = authenticated;
687
+ const tokenListeners = createListenerSet();
688
+ const keyListeners = createListenerSet();
689
+ const token = () => current ? PROXY_CUSTOMER_TOKEN : null;
690
+ const drop = () => {
691
+ };
692
+ return {
693
+ getCustomerToken: token,
694
+ setCustomerToken: drop,
695
+ subscribe: (listener) => tokenListeners.add(listener),
696
+ getCartId: () => base.getCartId(),
697
+ setCartId: (id) => base.setCartId(id),
698
+ getAnonymousSession: () => null,
699
+ setAnonymousSession: drop,
700
+ getSiteCode: () => base.getSiteCode(),
701
+ setSiteCode: (code) => base.setSiteCode(code),
702
+ getLanguage: () => base.getLanguage(),
703
+ setLanguage: (language) => base.setLanguage(language),
704
+ getActiveLegalEntityId: () => base.getActiveLegalEntityId(),
705
+ setActiveLegalEntityId: (id) => base.setActiveLegalEntityId(id),
706
+ getRefreshToken: () => null,
707
+ setRefreshToken: drop,
708
+ getSaasToken: () => null,
709
+ setSaasToken: drop,
710
+ subscribeAll(listener) {
711
+ const own = keyListeners.add(listener);
712
+ const passed = base.subscribeAll?.((key) => {
713
+ if (!TOKEN_KEYS.has(key)) listener(key);
714
+ });
715
+ return () => {
716
+ own();
717
+ passed?.();
718
+ };
719
+ },
720
+ setProxyAuthenticated(next) {
721
+ if (next === current) return;
722
+ current = next;
723
+ tokenListeners.notify(token());
724
+ keyListeners.notify("customerToken");
725
+ }
726
+ };
727
+ }
728
+
729
+ // src/core/logger.ts
730
+ var LEVEL = {
731
+ trace: 10,
732
+ debug: 20,
733
+ info: 30,
734
+ warn: 40,
735
+ error: 50,
736
+ silent: 60
737
+ };
738
+ function isValidLevel(v) {
739
+ return v !== void 0 && v in LEVEL;
740
+ }
741
+ function readEnv(name) {
742
+ try {
743
+ return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
744
+ } catch {
745
+ return void 0;
746
+ }
747
+ }
748
+ var LevelResolver = class {
749
+ constructor(cfg, warn = () => {
750
+ }) {
751
+ this.warn = warn;
752
+ this.cfgLevel = cfg.level ?? "warn";
753
+ this.cfgServices = { ...cfg.services };
754
+ }
755
+ warn;
756
+ cfgLevel;
757
+ cfgServices;
758
+ forcedServices = {};
759
+ forcedGlobal;
760
+ warned = false;
761
+ envFor(svc) {
762
+ const raw = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`);
763
+ if (raw === void 0) return void 0;
764
+ if (isValidLevel(raw)) return raw;
765
+ if (!this.warned) {
766
+ this.warned = true;
767
+ this.warn(`Invalid EMPORIX_LOG_LEVEL_${svc.toUpperCase()}="${raw}" ignored`);
768
+ }
769
+ return void 0;
770
+ }
771
+ envGlobal() {
772
+ const raw = readEnv("EMPORIX_LOG_LEVEL");
773
+ if (raw === void 0) return void 0;
774
+ if (isValidLevel(raw)) return raw;
775
+ if (!this.warned) {
776
+ this.warned = true;
777
+ this.warn(`Invalid EMPORIX_LOG_LEVEL="${raw}" ignored`);
778
+ }
779
+ return void 0;
780
+ }
781
+ /**
782
+ * Effective level for a service. Forced overrides (from `set(..., force)`)
783
+ * take precedence over env vars; otherwise env > programmatic config.
784
+ */
785
+ get(svc) {
786
+ return this.forcedServices[svc] ?? this.envFor(svc) ?? this.forcedGlobal ?? this.envGlobal() ?? this.cfgServices[svc] ?? this.cfgLevel ?? "warn";
844
787
  }
845
- return fromWebStorage(store, opts);
846
- }
847
- function createCookieStorage(opts = {}) {
848
- const sameSite = opts.sameSite ?? "lax";
849
- const secure = opts.secure ?? browser.location?.protocol === "https:";
850
- const doc = browser.document;
851
- if (doc === void 0) {
852
- console.warn("[emporix] document unavailable; cookie storage falling back to in-memory");
853
- return createMemoryStorage();
788
+ /** Numeric effective level for a service. */
789
+ numericLevel(svc) {
790
+ return LEVEL[this.get(svc)];
854
791
  }
855
- const attrs = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
856
- const all = createListenerSet();
857
- const storage = createCookieBackedStorage(
858
- {
859
- get: (name) => {
860
- for (const part of doc.cookie.split("; ")) {
861
- const [k, ...v] = part.split("=");
862
- if (k === name) return decodeURIComponent(v.join("=")) || null;
792
+ /** True if `svc`'s effective level allows emitting at `at`. */
793
+ isAtLeast(svc, at) {
794
+ return this.numericLevel(svc) <= LEVEL[at];
795
+ }
796
+ /** Mutates programmatic level (global or one service). Env-set levels are sticky unless `force`. */
797
+ set(level, svc, force = false) {
798
+ if (svc) {
799
+ const envBound = readEnv(`EMPORIX_LOG_LEVEL_${svc.toUpperCase()}`) !== void 0;
800
+ if (envBound && !force) {
801
+ this.warn(`Level for "${svc}" is env-controlled; pass force to override`);
802
+ return;
803
+ }
804
+ if (force) this.forcedServices[svc] = level;
805
+ else this.cfgServices[svc] = level;
806
+ } else {
807
+ const envBound = readEnv("EMPORIX_LOG_LEVEL") !== void 0;
808
+ if (envBound && !force) {
809
+ this.warn("Global level is env-controlled; pass force to override");
810
+ return;
811
+ }
812
+ if (force) this.forcedGlobal = level;
813
+ else this.cfgLevel = level;
814
+ }
815
+ }
816
+ };
817
+ var DEFAULT_REDACT = /* @__PURE__ */ new Set([
818
+ "authorization",
819
+ "password",
820
+ "oldpassword",
821
+ "newpassword",
822
+ "clientsecret",
823
+ "secret",
824
+ "access_token",
825
+ "refresh_token",
826
+ "customertoken",
827
+ "saastoken",
828
+ "saas-token",
829
+ "bearertoken",
830
+ "apikey",
831
+ "token"
832
+ ]);
833
+ function redact(value, extra = []) {
834
+ const keys = new Set(DEFAULT_REDACT);
835
+ for (const k of extra) keys.add(k.toLowerCase());
836
+ const walk = (v) => {
837
+ if (Array.isArray(v)) return v.map(walk);
838
+ if (v && typeof v === "object") {
839
+ const src = v;
840
+ if (typeof src.kind === "string" && "token" in src) return { kind: src.kind };
841
+ const out = {};
842
+ for (const [k, val] of Object.entries(src)) {
843
+ if (keys.has(k.toLowerCase())) {
844
+ out[k] = k.toLowerCase() === "authorization" && typeof val === "string" ? "Bearer ***redacted***" : "***redacted***";
845
+ } else {
846
+ out[k] = walk(val);
863
847
  }
864
- return null;
865
- },
866
- set: (name, value) => {
867
- doc.cookie = value === null ? `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${attrs}` : `${name}=${encodeURIComponent(value)}; ${attrs}`;
868
848
  }
869
- },
870
- {
871
- ...opts.name !== void 0 ? { tokenName: opts.name } : {},
872
- notify: (key) => all.notify(key)
849
+ return out;
873
850
  }
874
- );
875
- return { ...storage, subscribeAll: (l) => all.add(l) };
851
+ return v;
852
+ };
853
+ return walk(value);
854
+ }
855
+ var EMIT = {
856
+ trace: LEVEL.trace,
857
+ debug: LEVEL.debug,
858
+ info: LEVEL.info,
859
+ warn: LEVEL.warn,
860
+ error: LEVEL.error
861
+ };
862
+ var BaseLogger = class {
863
+ constructor(resolver, bindings, extraRedact) {
864
+ this.resolver = resolver;
865
+ this.bindings = bindings;
866
+ this.extraRedact = extraRedact;
867
+ }
868
+ resolver;
869
+ bindings;
870
+ extraRedact;
871
+ svc() {
872
+ return this.bindings.service ?? "http";
873
+ }
874
+ get level() {
875
+ return this.resolver.get(this.svc());
876
+ }
877
+ isLevelEnabled(level) {
878
+ return this.resolver.isAtLeast(this.svc(), level);
879
+ }
880
+ at(level, msg, fields) {
881
+ if (this.resolver.numericLevel(this.svc()) > EMIT[level]) return;
882
+ const merged = { ...this.bindings, ...fields ?? {} };
883
+ this.emit(level, msg, redact(merged, this.extraRedact));
884
+ }
885
+ trace(m, f) {
886
+ this.at("trace", m, f);
887
+ }
888
+ debug(m, f) {
889
+ this.at("debug", m, f);
890
+ }
891
+ info(m, f) {
892
+ this.at("info", m, f);
893
+ }
894
+ warn(m, f) {
895
+ this.at("warn", m, f);
896
+ }
897
+ error(m, f) {
898
+ this.at("error", m, f);
899
+ }
900
+ };
901
+ var ConsoleLogger = class _ConsoleLogger extends BaseLogger {
902
+ constructor(resolver, bindings, extra, sink, pretty) {
903
+ super(resolver, bindings, extra);
904
+ this.sink = sink;
905
+ this.pretty = pretty;
906
+ }
907
+ sink;
908
+ pretty;
909
+ emit(level, msg, fields) {
910
+ if (this.pretty) {
911
+ const fn = level === "error" ? this.sink.error : level === "warn" ? this.sink.warn : level === "info" ? this.sink.info : this.sink.log;
912
+ fn(`[${level}] ${msg}`, fields);
913
+ } else {
914
+ this.sink.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), level, msg, ...fields }));
915
+ }
916
+ }
917
+ child(bindings) {
918
+ return new _ConsoleLogger(
919
+ this.resolver,
920
+ { ...this.bindings, ...bindings },
921
+ this.extraRedact,
922
+ this.sink,
923
+ this.pretty
924
+ );
925
+ }
926
+ };
927
+ var NoopLogger = class {
928
+ level = "silent";
929
+ isLevelEnabled() {
930
+ return false;
931
+ }
932
+ trace() {
933
+ }
934
+ debug() {
935
+ }
936
+ info() {
937
+ }
938
+ warn() {
939
+ }
940
+ error() {
941
+ }
942
+ child() {
943
+ return this;
944
+ }
945
+ };
946
+ function createConsoleLogger(resolver, bindings = {}, opts = {}) {
947
+ const pretty = opts.pretty ?? readEnv("NODE_ENV") !== "production";
948
+ const sink = opts.sink ?? {
949
+ log: console.log,
950
+ info: console.info,
951
+ warn: console.warn,
952
+ error: console.error
953
+ };
954
+ return new ConsoleLogger(resolver, bindings, opts.redact ?? [], sink, pretty);
955
+ }
956
+ function createNoopLogger() {
957
+ return new NoopLogger();
876
958
  }
877
959
 
878
960
  // src/core/query-keys.ts
@@ -986,20 +1068,72 @@ var HttpClient = class {
986
1068
  sleep;
987
1069
  /** Resolved request fetch. The arrow keeps `globalThis.fetch` correctly bound. */
988
1070
  doFetch;
1071
+ /**
1072
+ * `null` in proxy mode, where no token is resolved at all — the provider is
1073
+ * never asked, so no token request can happen. A service context is refused
1074
+ * there rather than sent anonymously through the proxy.
1075
+ */
1076
+ async tokenFor(o) {
1077
+ if (!this.opts.proxy) return resolveToken(o.auth, this.opts.provider);
1078
+ if (o.auth.kind === "service") {
1079
+ throw new EmporixAuthError(
1080
+ `${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.`
1081
+ );
1082
+ }
1083
+ return null;
1084
+ }
1085
+ /** `host + path` plus query. A relative host (`/emporix`) resolves against the page origin. */
1086
+ urlFor(o) {
1087
+ const host = this.opts.host;
1088
+ let url;
1089
+ if (host.startsWith("/")) {
1090
+ const origin = globalThis.location?.origin;
1091
+ if (!origin) {
1092
+ throw new EmporixError(
1093
+ `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.`
1094
+ );
1095
+ }
1096
+ url = new URL(host + o.path, origin);
1097
+ } else {
1098
+ url = new URL(host + o.path);
1099
+ }
1100
+ for (const [k, v] of Object.entries(o.query ?? {})) {
1101
+ if (v !== void 0) url.searchParams.set(k, String(v));
1102
+ }
1103
+ return url;
1104
+ }
1105
+ /** Proxy mode: report the 401 before it is thrown. A throwing callback must not mask it. */
1106
+ reportUnauthorized(o) {
1107
+ try {
1108
+ this.opts.proxy?.onUnauthorized?.({ method: o.method, path: o.path });
1109
+ } catch {
1110
+ }
1111
+ }
989
1112
  buildHeaders(o, token, isFormData) {
990
- return {
1113
+ const headers = {
991
1114
  // Before `o.headers` on purpose: a caller may override the tenant for a
992
1115
  // single request, the way `Accept-Language` and `Content-Language` can be
993
1116
  // overridden. Only `Authorization` is non-negotiable.
994
1117
  ...this.opts.tenant !== void 0 ? { "Emporix-Tenant": this.opts.tenant } : {},
995
1118
  ...this.opts.requestContext?.language ? { "Accept-Language": this.opts.requestContext.language } : {},
996
1119
  ...this.opts.contentLanguage && o.body !== void 0 ? { "Content-Language": this.opts.contentLanguage } : {},
1120
+ ...this.opts.proxy?.headers ?? {},
997
1121
  ...o.headers ?? {},
998
- Authorization: `Bearer ${token}`,
1122
+ ...token !== null ? { Authorization: `Bearer ${token}` } : {},
999
1123
  // JSON bodies: set Content-Type. FormData bodies: let `fetch`
1000
1124
  // emit `multipart/form-data; boundary=...` itself.
1001
1125
  ...o.body !== void 0 && !isFormData ? { "Content-Type": "application/json" } : {}
1002
1126
  };
1127
+ if (token === null) {
1128
+ for (const k of Object.keys(headers)) {
1129
+ if (k.toLowerCase() === "authorization") delete headers[k];
1130
+ }
1131
+ }
1132
+ return headers;
1133
+ }
1134
+ /** `fetch`'s `credentials` — set only in proxy mode, where the session cookie must go along. */
1135
+ credentialsInit() {
1136
+ return this.opts.proxy ? { credentials: this.opts.proxy.credentials } : {};
1003
1137
  }
1004
1138
  /**
1005
1139
  * Issues a request and returns the parsed body. Retries 5xx/429 on idempotent
@@ -1022,17 +1156,14 @@ var HttpClient = class {
1022
1156
  async send(o) {
1023
1157
  const requestId = `req-${++requestSeq}`;
1024
1158
  const log = this.opts.logger.child({ requestId });
1025
- const url = new URL(this.opts.host + o.path);
1026
- for (const [k, v] of Object.entries(o.query ?? {})) {
1027
- if (v !== void 0) url.searchParams.set(k, String(v));
1028
- }
1159
+ const url = this.urlFor(o);
1029
1160
  const sdkManaged = o.auth.kind === "service" || o.auth.kind === "anonymous";
1030
1161
  const maxAttempts = this.opts.retry.maxAttempts;
1031
1162
  let reauthed = false;
1032
1163
  let customerToken = o.auth.kind === "customer" ? o.auth.token : void 0;
1033
1164
  let customerReauthed = false;
1034
1165
  for (let attempt = 1; ; attempt++) {
1035
- const token = customerToken ?? await resolveToken(o.auth, this.opts.provider);
1166
+ const token = !this.opts.proxy && customerToken !== void 0 ? customerToken : await this.tokenFor(o);
1036
1167
  log.debug("http request", {
1037
1168
  authKind: o.auth.kind,
1038
1169
  method: o.method,
@@ -1056,7 +1187,8 @@ var HttpClient = class {
1056
1187
  const init = {
1057
1188
  method: o.method,
1058
1189
  headers: this.buildHeaders(o, token, isFormData),
1059
- signal: controller.signal
1190
+ signal: controller.signal,
1191
+ ...this.credentialsInit()
1060
1192
  };
1061
1193
  if (o.body !== void 0) {
1062
1194
  init.body = isFormData ? o.body : JSON.stringify(o.body);
@@ -1089,6 +1221,10 @@ var HttpClient = class {
1089
1221
  return { data: parsed, headers: res.headers };
1090
1222
  }
1091
1223
  if (res.status === 401) {
1224
+ if (this.opts.proxy) {
1225
+ this.reportUnauthorized(o);
1226
+ throw errorFromResponse(res.status, `${o.method} ${o.path} \u2192 401`, parsed);
1227
+ }
1092
1228
  if (sdkManaged && !reauthed) {
1093
1229
  reauthed = true;
1094
1230
  if (o.auth.kind === "service") {
@@ -1141,11 +1277,8 @@ var HttpClient = class {
1141
1277
  async requestRaw(o, extra) {
1142
1278
  const requestId = `req-${++requestSeq}`;
1143
1279
  const log = this.opts.logger.child({ requestId });
1144
- const url = new URL(this.opts.host + o.path);
1145
- for (const [k, v] of Object.entries(o.query ?? {})) {
1146
- if (v !== void 0) url.searchParams.set(k, String(v));
1147
- }
1148
- const token = await resolveToken(o.auth, this.opts.provider);
1280
+ const url = this.urlFor(o);
1281
+ const token = await this.tokenFor(o);
1149
1282
  log.debug("http requestRaw", {
1150
1283
  authKind: o.auth.kind,
1151
1284
  method: o.method,
@@ -1162,13 +1295,15 @@ var HttpClient = class {
1162
1295
  method: o.method,
1163
1296
  headers: this.buildHeaders(o, token, isFormData),
1164
1297
  signal: controller.signal,
1165
- ...extra?.redirect ? { redirect: extra.redirect } : {}
1298
+ ...extra?.redirect ? { redirect: extra.redirect } : {},
1299
+ ...this.credentialsInit()
1166
1300
  };
1167
1301
  if (o.body !== void 0) {
1168
1302
  init.body = isFormData ? o.body : JSON.stringify(o.body);
1169
1303
  }
1304
+ let res;
1170
1305
  try {
1171
- return await this.doFetch(url, init);
1306
+ res = await this.doFetch(url, init);
1172
1307
  } catch (err) {
1173
1308
  if (err.name === "AbortError") {
1174
1309
  throw new EmporixTimeoutError(
@@ -1181,6 +1316,12 @@ var HttpClient = class {
1181
1316
  } finally {
1182
1317
  clearTimeout(timer);
1183
1318
  }
1319
+ if (this.opts.proxy && res.status === 401) {
1320
+ this.reportUnauthorized(o);
1321
+ const text = await res.text().catch(() => "");
1322
+ throw errorFromResponse(401, `${o.method} ${o.path} \u2192 401`, text ? safeJson(text) : void 0);
1323
+ }
1324
+ return res;
1184
1325
  }
1185
1326
  /**
1186
1327
  * Open a Server-Sent Events stream (`text/event-stream`) and yield parsed
@@ -1192,11 +1333,8 @@ var HttpClient = class {
1192
1333
  */
1193
1334
  async *requestStream(o) {
1194
1335
  const log = this.opts.logger.child({ requestId: `req-${++requestSeq}` });
1195
- const url = new URL(this.opts.host + o.path);
1196
- for (const [k, v] of Object.entries(o.query ?? {})) {
1197
- if (v !== void 0) url.searchParams.set(k, String(v));
1198
- }
1199
- const token = await resolveToken(o.auth, this.opts.provider);
1336
+ const url = this.urlFor(o);
1337
+ const token = await this.tokenFor(o);
1200
1338
  const controller = new AbortController();
1201
1339
  const connectTimer = setTimeout(() => controller.abort(), this.opts.timeouts.connectMs);
1202
1340
  const headers = this.buildHeaders(o, token, false);
@@ -1207,7 +1345,8 @@ var HttpClient = class {
1207
1345
  method: o.method,
1208
1346
  headers,
1209
1347
  signal: controller.signal,
1210
- ...o.body !== void 0 ? { body: JSON.stringify(o.body) } : {}
1348
+ ...o.body !== void 0 ? { body: JSON.stringify(o.body) } : {},
1349
+ ...this.credentialsInit()
1211
1350
  });
1212
1351
  } catch (err) {
1213
1352
  clearTimeout(connectTimer);
@@ -1220,6 +1359,7 @@ var HttpClient = class {
1220
1359
  }
1221
1360
  clearTimeout(connectTimer);
1222
1361
  if (!res.ok) {
1362
+ if (this.opts.proxy && res.status === 401) this.reportUnauthorized(o);
1223
1363
  const text = await res.text().catch(() => "");
1224
1364
  throw errorFromResponse(
1225
1365
  res.status,
@@ -1247,7 +1387,7 @@ var HttpClient = class {
1247
1387
  };
1248
1388
 
1249
1389
  // package.json
1250
- var version = "4.10.0";
1390
+ var version = "4.11.1";
1251
1391
 
1252
1392
  // src/version.ts
1253
1393
  var SDK_VERSION = version;
@@ -1270,8 +1410,19 @@ function createCore(config) {
1270
1410
  sdkVersion: SDK_VERSION,
1271
1411
  tenant: cfg.tenant
1272
1412
  });
1273
- const tokenProvider = cfg.tokenProvider ?? new DefaultTokenProvider(cfg);
1413
+ const tokenProvider = cfg.proxy ? createProxyModeTokenProvider() : cfg.tokenProvider ?? new DefaultTokenProvider(cfg);
1274
1414
  const customerRefresh = new CustomerRefreshRegistry();
1415
+ const unauthorized = createListenerSet();
1416
+ const proxy = cfg.proxy && {
1417
+ ...cfg.proxy,
1418
+ onUnauthorized: (info) => {
1419
+ try {
1420
+ cfg.proxy?.onUnauthorized?.(info);
1421
+ } finally {
1422
+ unauthorized.notify(info);
1423
+ }
1424
+ }
1425
+ };
1275
1426
  const requestContext = {
1276
1427
  language: cfg.credentials.storefront?.context?.language
1277
1428
  };
@@ -1291,7 +1442,8 @@ function createCore(config) {
1291
1442
  // exactOptionalPropertyTypes: `{ fetch: undefined }` is a type error
1292
1443
  // against `fetch?: typeof globalThis.fetch`, so spread conditionally.
1293
1444
  ...cfg.fetch !== void 0 ? { fetch: cfg.fetch } : {},
1294
- ...cfg.contentLanguage !== void 0 ? { contentLanguage: cfg.contentLanguage } : {}
1445
+ ...cfg.contentLanguage !== void 0 ? { contentLanguage: cfg.contentLanguage } : {},
1446
+ ...proxy ? { proxy } : {}
1295
1447
  })
1296
1448
  });
1297
1449
  return {
@@ -1316,6 +1468,9 @@ function createCore(config) {
1316
1468
  },
1317
1469
  setCustomerTokenRefresher(refresher) {
1318
1470
  customerRefresh.set(refresher);
1471
+ },
1472
+ onUnauthorized(listener) {
1473
+ return unauthorized.add(listener);
1319
1474
  }
1320
1475
  };
1321
1476
  }
@@ -10022,6 +10177,15 @@ var EmporixClient = class {
10022
10177
  setCustomerTokenRefresher(refresher) {
10023
10178
  this.core.setCustomerTokenRefresher(refresher);
10024
10179
  }
10180
+ /**
10181
+ * Proxy mode (`config.proxy`): subscribes to the 401s the proxy answers, next
10182
+ * to `proxy.onUnauthorized`. Returns the unsubscribe function. The React
10183
+ * `EmporixProvider` uses it for `onCustomerSessionExpired`. Never fires outside
10184
+ * proxy mode — there a 401 is answered by re-authenticating.
10185
+ */
10186
+ onUnauthorized(listener) {
10187
+ return this.core.onUnauthorized(listener);
10188
+ }
10025
10189
  };
10026
10190
 
10027
10191
  // src/create-emporix-client.ts
@@ -10103,6 +10267,7 @@ exports.LevelResolver = LevelResolver;
10103
10267
  exports.LocationsService = LocationsService;
10104
10268
  exports.MediaService = MediaService;
10105
10269
  exports.OrdersService = OrdersService;
10270
+ exports.PROXY_CUSTOMER_TOKEN = PROXY_CUSTOMER_TOKEN;
10106
10271
  exports.PaymentGatewayService = PaymentGatewayService;
10107
10272
  exports.PriceService = PriceService;
10108
10273
  exports.ProductService = ProductService;
@@ -10137,6 +10302,8 @@ exports.createLocalStorage = createLocalStorage;
10137
10302
  exports.createLocalStorageStorage = createLocalStorageStorage;
10138
10303
  exports.createMemoryStorage = createMemoryStorage;
10139
10304
  exports.createNoopLogger = createNoopLogger;
10305
+ exports.createProxyModeTokenProvider = createProxyModeTokenProvider;
10306
+ exports.createProxySessionStorage = createProxySessionStorage;
10140
10307
  exports.createServerStorage = createServerStorage;
10141
10308
  exports.createSessionStorage = createSessionStorage;
10142
10309
  exports.emporixKey = emporixKey;