@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.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";
787
+ }
788
+ /** Numeric effective level for a service. */
789
+ numericLevel(svc) {
790
+ return LEVEL[this.get(svc)];
844
791
  }
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();
792
+ /** True if `svc`'s effective level allows emitting at `at`. */
793
+ isAtLeast(svc, at) {
794
+ return this.numericLevel(svc) <= LEVEL[at];
854
795
  }
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;
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.9.4";
1390
+ var version = "4.11.0";
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
  }
@@ -8523,13 +8678,18 @@ var SearchService = class {
8523
8678
  return `/search/${this.ctx.tenant}`;
8524
8679
  }
8525
8680
  /**
8526
- * Search the documents of one custom schema type. Paging, `sort` and
8527
- * `fields` travel as query parameters, not in the body.
8681
+ * Search the documents of one type — a custom schema type, `customer` or
8682
+ * `legal-entity`. Paging, `sort` and `fields` travel as query parameters, not
8683
+ * in the body.
8528
8684
  *
8529
8685
  * Filter and sort fields must be indexed on the index the search uses —
8530
- * `exact`, or `text: true` for a `CONTAINS` filter — or the call answers `400`. A saved-search call (`{ searchQueryId, query }`) answers
8531
- * `404` when no saved search has that id for `type`, and `502` means the
8532
- * Schema Service could not confirm that `type` is a custom entity.
8686
+ * `exact`, or `text: true` for a `CONTAINS` filter — or the call answers
8687
+ * `400`. A `TEXT` or `PHRASE` query may pick the field's `analyzer` or one of
8688
+ * its `additionalAnalyzers`. Conditions joined by `and` on fields of one
8689
+ * `itemFields` list must match the same list item. A saved-search call
8690
+ * (`{ searchQueryId, query }`) answers `404` when no saved search has that id
8691
+ * for `type`, and `502` means the Schema Service could not confirm that a
8692
+ * custom `type` is a custom entity.
8533
8693
  *
8534
8694
  * @example
8535
8695
  * const hits = await client.search.search("VEHICLE", {
@@ -8554,7 +8714,7 @@ var SearchService = class {
8554
8714
  page
8555
8715
  );
8556
8716
  }
8557
- /** List saved searches of one custom schema type (`opts.type`), or of every type. */
8717
+ /** List saved searches of one type (`opts.type`), or of every type. */
8558
8718
  async listSavedSearches(opts = {}, auth2 = SERVICE29) {
8559
8719
  const { query, page } = pageQuery(opts);
8560
8720
  const t = opts.type;
@@ -8602,7 +8762,7 @@ var SearchService = class {
8602
8762
  auth: auth2
8603
8763
  });
8604
8764
  }
8605
- /** List search indexes of one custom schema type (`opts.type`), or of every type. */
8765
+ /** List search indexes of one type (`opts.type`), or of every type. */
8606
8766
  async listIndexes(opts = {}, auth2 = SERVICE29) {
8607
8767
  const { query, page } = pageQuery(opts);
8608
8768
  const t = opts.type;
@@ -8896,6 +9056,11 @@ var ImportService = class {
8896
9056
  * streams. Read it back with {@link getRun}, or follow it with
8897
9057
  * {@link streamRun}, before treating a resolved call as a started import.
8898
9058
  *
9059
+ * A configuration that does not exist answers `404` (since 2026-11-06; it used
9060
+ * to be a `500`). A real run with nothing to import answers `400`: every stream
9061
+ * it starts from reads uploaded files, and none has ever had one. Upload a file
9062
+ * with {@link uploadStreamFile} first. A dry run is not refused for this.
9063
+ *
8899
9064
  * Not retried on a 5xx: a POST that timed out may already have queued a run.
8900
9065
  *
8901
9066
  * @example
@@ -9146,7 +9311,7 @@ var ImportService = class {
9146
9311
  * {@link streamRun} exactly as you would a triggered run. Like one, it comes
9147
9312
  * back `QUEUED`, answers `409` while the configuration has a run `QUEUED`,
9148
9313
  * `RUNNING` or `RESUMABLE`, runs the published mappings and can finish
9149
- * `ABORTED`.
9314
+ * `ABORTED`. A run that does not exist answers `404`.
9150
9315
  *
9151
9316
  * Not retried on a 5xx, for the same reason {@link triggerRun} is not: a POST
9152
9317
  * that timed out may already have queued the retry.
@@ -9257,6 +9422,29 @@ var ImportService = class {
9257
9422
  auth: auth2
9258
9423
  });
9259
9424
  }
9425
+ /**
9426
+ * Put a `FAILED` file back at the end of the stream's queue, so the next run
9427
+ * reads the content the service already holds — no new upload. Use it after
9428
+ * fixing the cause of the failure, a mapping say. Resolves to the file,
9429
+ * `PENDING` again.
9430
+ *
9431
+ * Only a failed file can be queued again; `409` otherwise. A file imported with
9432
+ * some records failing is `PROCESSED`: retry those records with
9433
+ * {@link retryRun} instead. On a `SNAPSHOT` stream a failed file cannot be
9434
+ * queued again once a newer file arrived, since the older snapshot would be
9435
+ * imported over newer data — upload a corrected current file instead. `409`
9436
+ * also when the stored content is gone, and while the job has a run in flight.
9437
+ *
9438
+ * Not retried on a 5xx: if the first attempt went through, the file is no
9439
+ * longer `FAILED` and a repeat answers `409`.
9440
+ */
9441
+ async requeueStreamFile(streamId, fileId, auth2 = SERVICE30) {
9442
+ return this.ctx.http.request({
9443
+ method: "POST",
9444
+ path: `${this.base()}/streams/${encodeURIComponent(streamId)}/files/${encodeURIComponent(fileId)}/requeue`,
9445
+ auth: auth2
9446
+ });
9447
+ }
9260
9448
  };
9261
9449
 
9262
9450
  // src/services/audit-log.ts
@@ -9989,6 +10177,15 @@ var EmporixClient = class {
9989
10177
  setCustomerTokenRefresher(refresher) {
9990
10178
  this.core.setCustomerTokenRefresher(refresher);
9991
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
+ }
9992
10189
  };
9993
10190
 
9994
10191
  // src/create-emporix-client.ts
@@ -10070,6 +10267,7 @@ exports.LevelResolver = LevelResolver;
10070
10267
  exports.LocationsService = LocationsService;
10071
10268
  exports.MediaService = MediaService;
10072
10269
  exports.OrdersService = OrdersService;
10270
+ exports.PROXY_CUSTOMER_TOKEN = PROXY_CUSTOMER_TOKEN;
10073
10271
  exports.PaymentGatewayService = PaymentGatewayService;
10074
10272
  exports.PriceService = PriceService;
10075
10273
  exports.ProductService = ProductService;
@@ -10104,6 +10302,8 @@ exports.createLocalStorage = createLocalStorage;
10104
10302
  exports.createLocalStorageStorage = createLocalStorageStorage;
10105
10303
  exports.createMemoryStorage = createMemoryStorage;
10106
10304
  exports.createNoopLogger = createNoopLogger;
10305
+ exports.createProxyModeTokenProvider = createProxyModeTokenProvider;
10306
+ exports.createProxySessionStorage = createProxySessionStorage;
10107
10307
  exports.createServerStorage = createServerStorage;
10108
10308
  exports.createSessionStorage = createSessionStorage;
10109
10309
  exports.emporixKey = emporixKey;