altcha 3.0.0-beta.2 → 3.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,13 +119,13 @@ ALTCHA is optimized for performance:
119
119
 
120
120
  | Distribution | Size (GZIPped) |
121
121
  | ---------------------------- | -------------- |
122
- | ALTCHA | 31 kB |
122
+ | ALTCHA | 34 kB |
123
123
  | ALTCHA with all translations | 49 kB |
124
124
  | Cloudflare Turnstile | 85+ kB |
125
125
  | hCaptcha | 250+ kB |
126
126
  | reCAPTCHA | 300+ kB |
127
127
 
128
- When GZIPped, it totals about 31 kB, making ALTCHA’s widget about 90% smaller than reCAPTCHA.
128
+ When GZIPped, it totals about 34 kB, making ALTCHA’s widget about ~90% smaller than reCAPTCHA.
129
129
 
130
130
  ## Content Security Policy (CSP)
131
131
 
@@ -205,6 +205,7 @@ For simple implementations, the widget supports a subset of configuration option
205
205
  - **`floatingPlacement`**: Preferred position relative to the anchor (`'auto'`, `'bottom'`, or `'top'`).
206
206
  - **`floatingOffset`**: Vertical offset in pixels between the UI and its anchor. (Default: `12`)
207
207
  - **`floatingPersist`**: Whether the floating widget remains visible after successful verification.
208
+ - **`popoverPlacement`**: Preferred position of popovers relative to the widget (`'auto'`, `'bottom'`, or `'top'`).
208
209
 
209
210
  ### Modal & Challenge Settings
210
211
 
@@ -219,7 +220,9 @@ For simple implementations, the widget supports a subset of configuration option
219
220
  - **`test`**: Mocks a successful verification for testing environments.
220
221
  - **`mockError`**: Forces the widget into a failed state for UI testing.
221
222
  - **`fetch`**: A custom `fetch` implementation for network requests.
223
+ - **`humanInteractionSignature`**: Whether the collector for HIS is enabled (Default: `true`).
222
224
  - **`setCookie`**: When configured, sends the payload as a cookie.
225
+ - **`timeout`**: Verification timeout in milliseconds (Default: `90_000`).
223
226
  - **`verifyFunction`**: A custom verification handler that overrides default network verification.
224
227
 
225
228
  ## Cookies
@@ -5431,7 +5431,7 @@ var root_3$1 = /* @__PURE__ */ from_html(`<div role="button" class="altcha-popov
5431
5431
  var root$1 = /* @__PURE__ */ from_html(`<!> <div><!> <!> <div class="altcha-popover-content"><!></div></div>`, 1);
5432
5432
  function Popover($$anchor, $$props) {
5433
5433
  push($$props, true);
5434
- let anchor = prop($$props, "anchor"), children = prop($$props, "children"), display = prop($$props, "display", 7, "standard"), backdrop = prop($$props, "backdrop", 7, false), onClickOutside = prop($$props, "onClickOutside"), onClickOutsideDelay = prop($$props, "onClickOutsideDelay", 7, 600), onClose = prop($$props, "onClose"), variant = prop($$props, "variant", 7, "neutral"), rest = /* @__PURE__ */ rest_props($$props, [
5434
+ let anchor = prop($$props, "anchor"), children = prop($$props, "children"), display = prop($$props, "display", 7, "standard"), backdrop = prop($$props, "backdrop", 7, false), onClickOutside = prop($$props, "onClickOutside"), onClickOutsideDelay = prop($$props, "onClickOutsideDelay", 7, 600), onClose = prop($$props, "onClose"), placement = prop($$props, "placement", 7, "auto"), variant = prop($$props, "variant", 7, "neutral"), rest = /* @__PURE__ */ rest_props($$props, [
5435
5435
  "$$slots",
5436
5436
  "$$events",
5437
5437
  "$$legacy",
@@ -5443,12 +5443,18 @@ function Popover($$anchor, $$props) {
5443
5443
  "onClickOutside",
5444
5444
  "onClickOutsideDelay",
5445
5445
  "onClose",
5446
+ "placement",
5446
5447
  "variant"
5447
5448
  ]);
5448
5449
  let el = /* @__PURE__ */ state(void 0);
5449
5450
  let elBackdrop = /* @__PURE__ */ state(void 0);
5450
5451
  let top = /* @__PURE__ */ state(false);
5451
5452
  let mountedAt = /* @__PURE__ */ state(0);
5453
+ user_effect(() => {
5454
+ if (placement() !== "auto") {
5455
+ set(top, placement() === "top");
5456
+ }
5457
+ });
5452
5458
  onMount(() => {
5453
5459
  const moveToBody = display() === "bottomsheet" || display() === "overlay";
5454
5460
  if (moveToBody) {
@@ -5482,7 +5488,7 @@ function Popover($$anchor, $$props) {
5482
5488
  reposition();
5483
5489
  }
5484
5490
  function reposition() {
5485
- if (anchor() && get(el)) {
5491
+ if (anchor() && placement() === "auto" && get(el)) {
5486
5492
  const boundary2 = anchor().getBoundingClientRect();
5487
5493
  const bottomGap = document.documentElement.clientHeight - (boundary2.top + boundary2.height);
5488
5494
  const newTop = bottomGap < get(el).clientHeight;
@@ -5541,6 +5547,13 @@ function Popover($$anchor, $$props) {
5541
5547
  onClose($$value);
5542
5548
  flushSync();
5543
5549
  },
5550
+ get placement() {
5551
+ return placement();
5552
+ },
5553
+ set placement($$value = "auto") {
5554
+ placement($$value);
5555
+ flushSync();
5556
+ },
5544
5557
  get variant() {
5545
5558
  return variant();
5546
5559
  },
@@ -5614,6 +5627,7 @@ create_custom_element(
5614
5627
  onClickOutside: {},
5615
5628
  onClickOutsideDelay: {},
5616
5629
  onClose: {},
5630
+ placement: {},
5617
5631
  variant: {}
5618
5632
  },
5619
5633
  [],
@@ -5630,7 +5644,8 @@ async function solveChallengeWorkers(options) {
5630
5644
  controller = new AbortController(),
5631
5645
  createWorker,
5632
5646
  onOutOfMemory = (c) => c > 1 ? Math.floor(c / 2) : 0,
5633
- counterMode
5647
+ counterMode,
5648
+ timeout = 9e4
5634
5649
  } = options;
5635
5650
  const workersConcurrency = Math.min(16, Math.max(1, concurrency));
5636
5651
  const workersInstances = [];
@@ -5671,6 +5686,7 @@ async function solveChallengeWorkers(options) {
5671
5686
  counterMode,
5672
5687
  counterStart: i,
5673
5688
  counterStep: workersConcurrency,
5689
+ timeout,
5674
5690
  type: "work"
5675
5691
  });
5676
5692
  });
@@ -5702,6 +5718,147 @@ async function solveChallengeWorkers(options) {
5702
5718
  }
5703
5719
  return solution || null;
5704
5720
  }
5721
+ class Collector {
5722
+ TAG_CODES = {
5723
+ INPUT: 1,
5724
+ TEXTAREA: 2,
5725
+ SELECT: 3,
5726
+ BUTTON: 4,
5727
+ A: 5,
5728
+ DETAILS: 6,
5729
+ SUMMARY: 7,
5730
+ IFRAME: 8,
5731
+ VIDEO: 9,
5732
+ AUDIO: 10
5733
+ };
5734
+ maxSamples;
5735
+ sampleInterval;
5736
+ target;
5737
+ focusStartTime = 0;
5738
+ focusInteraction = 0;
5739
+ focusInteractionTimer = null;
5740
+ lastPointerSample = 0;
5741
+ lastTouchSample = 0;
5742
+ lastScrollSample = 0;
5743
+ pendingPointer = null;
5744
+ pendingTouch = null;
5745
+ focus = [];
5746
+ pointer = [];
5747
+ scroll = [];
5748
+ touch = [];
5749
+ constructor(options = {}) {
5750
+ const { maxSamples = 60, sampleInterval = 50, target = window } = options;
5751
+ this.maxSamples = maxSamples;
5752
+ this.sampleInterval = sampleInterval;
5753
+ this.target = target;
5754
+ this.attach();
5755
+ }
5756
+ destroy() {
5757
+ const o = { capture: true };
5758
+ this.target.removeEventListener("focusin", this.onFocus, o);
5759
+ this.target.removeEventListener("keydown", this.onInteraction, o);
5760
+ this.target.removeEventListener("pointerdown", this.onInteraction, o);
5761
+ this.target.removeEventListener("pointermove", this.onPointer, o);
5762
+ this.target.removeEventListener("scroll", this.onScroll, o);
5763
+ this.target.removeEventListener("touchmove", this.onTouchMove, o);
5764
+ }
5765
+ export() {
5766
+ return {
5767
+ focus: this.focus,
5768
+ maxTouchPoints: navigator.maxTouchPoints || 0,
5769
+ pointer: this.pointer,
5770
+ scroll: this.scroll,
5771
+ time: Date.now(),
5772
+ touch: this.touch
5773
+ };
5774
+ }
5775
+ attach() {
5776
+ const o = { passive: true, capture: true };
5777
+ this.target.addEventListener("focusin", this.onFocus, o);
5778
+ this.target.addEventListener("keydown", this.onInteraction, o);
5779
+ this.target.addEventListener("pointerdown", this.onInteraction, o);
5780
+ this.target.addEventListener("pointermove", this.onPointer, o);
5781
+ this.target.addEventListener("scroll", this.onScroll, o);
5782
+ this.target.addEventListener("touchmove", this.onTouchMove, o);
5783
+ }
5784
+ evict(buffer) {
5785
+ if (buffer.length > this.maxSamples) {
5786
+ buffer.splice(0, buffer.length - this.maxSamples);
5787
+ }
5788
+ }
5789
+ onFocus = (e) => {
5790
+ if (this.focusInteraction === 2) {
5791
+ return;
5792
+ }
5793
+ const el = e.target;
5794
+ if (!(el instanceof Element)) {
5795
+ return;
5796
+ }
5797
+ const now = performance.now();
5798
+ if (this.focusStartTime === 0) {
5799
+ this.focusStartTime = now;
5800
+ }
5801
+ this.focus.push([
5802
+ Math.round(now - this.focusStartTime),
5803
+ el.tabIndex,
5804
+ this.TAG_CODES[el.tagName] ?? 0,
5805
+ this.focusInteraction ? 1 : 0
5806
+ ]);
5807
+ this.evict(this.focus);
5808
+ };
5809
+ onInteraction = (e) => {
5810
+ this.focusInteraction = "keyCode" in e ? 1 : 2;
5811
+ if (this.focusInteractionTimer) {
5812
+ clearTimeout(this.focusInteractionTimer);
5813
+ }
5814
+ this.focusInteractionTimer = setTimeout(() => {
5815
+ this.focusInteraction = 0;
5816
+ }, 100);
5817
+ };
5818
+ onPointer = (e) => {
5819
+ if (e.pointerType === "touch") {
5820
+ return;
5821
+ }
5822
+ const now = e.timeStamp || performance.now();
5823
+ this.pendingPointer = [Math.round(e.clientX), Math.round(e.clientY), Math.round(now)];
5824
+ if (now - this.lastPointerSample >= this.sampleInterval) {
5825
+ this.pointer.push(this.pendingPointer);
5826
+ this.lastPointerSample = now;
5827
+ this.pendingPointer = null;
5828
+ this.evict(this.pointer);
5829
+ }
5830
+ };
5831
+ onScroll = () => {
5832
+ const now = performance.now();
5833
+ if (now - this.lastScrollSample < this.sampleInterval) {
5834
+ return;
5835
+ }
5836
+ this.scroll.push([Math.round(window.scrollY), Math.round(now)]);
5837
+ this.lastScrollSample = now;
5838
+ this.evict(this.scroll);
5839
+ };
5840
+ onTouchMove = (e) => {
5841
+ const now = e.timeStamp || performance.now();
5842
+ const t = e.touches[0];
5843
+ if (!t) {
5844
+ return;
5845
+ }
5846
+ this.pendingTouch = [
5847
+ Math.round(t.clientX),
5848
+ Math.round(t.clientY),
5849
+ Math.round(now),
5850
+ Math.round(t.force * 1e3) / 1e3,
5851
+ Math.round(t.radiusX || 0),
5852
+ Math.round(t.radiusY || 0)
5853
+ ];
5854
+ if (now - this.lastTouchSample >= this.sampleInterval) {
5855
+ this.touch.push(this.pendingTouch);
5856
+ this.lastTouchSample = now;
5857
+ this.pendingTouch = null;
5858
+ this.evict(this.touch);
5859
+ }
5860
+ };
5861
+ }
5705
5862
  var root_1 = /* @__PURE__ */ from_html(`<div class="altcha-overlay-backdrop" data-backdrop=""></div>`);
5706
5863
  var root_3 = /* @__PURE__ */ from_html(`<div class="altcha-overlay-content"></div>`);
5707
5864
  var root_2 = /* @__PURE__ */ from_html(`<div role="button" class="altcha-overlay-close">&times;</div> <!>`, 1);
@@ -5732,6 +5889,7 @@ function Widget($$anchor, $$props) {
5732
5889
  instance?.dispatchEvent(new CustomEvent(event2, { detail }));
5733
5890
  });
5734
5891
  };
5892
+ let hisCollector = null;
5735
5893
  let baseUrl = /* @__PURE__ */ state(proxy(new URL(location.origin)));
5736
5894
  let checked = /* @__PURE__ */ state(false);
5737
5895
  let codeChallenge = /* @__PURE__ */ state(null);
@@ -5766,16 +5924,19 @@ function Widget($$anchor, $$props) {
5766
5924
  floatingPlacement: "auto",
5767
5925
  hideFooter: false,
5768
5926
  hideLogo: false,
5927
+ humanInteractionSignature: true,
5769
5928
  language: "",
5770
5929
  mockError: false,
5771
5930
  minDuration: 500,
5772
5931
  overlayContent: "",
5773
5932
  name: "altcha",
5933
+ popoverPlacement: "auto",
5774
5934
  retryOnOutOfMemoryError: true,
5775
5935
  setCookie: null,
5776
5936
  serverVerificationFields: false,
5777
5937
  serverVerificationTimeZone: false,
5778
5938
  test: false,
5939
+ timeout: 9e4,
5779
5940
  type: "checkbox",
5780
5941
  validationMessage: "",
5781
5942
  verifyFunction: null,
@@ -5786,6 +5947,7 @@ function Widget($$anchor, $$props) {
5786
5947
  }));
5787
5948
  const checkboxId = /* @__PURE__ */ user_derived(() => `altcha-checkbox-${$$props.id || Math.floor(Math.random() * 1e12).toString(16)}`);
5788
5949
  const CheckboxComponent = /* @__PURE__ */ user_derived(() => getCheckboxComponent(get(config).type));
5950
+ const auto = /* @__PURE__ */ user_derived(() => get(config).auto);
5789
5951
  const loading = /* @__PURE__ */ user_derived(() => get(currentState) === State.VERIFYING);
5790
5952
  const showFooter = /* @__PURE__ */ user_derived(() => !get(config).hideFooter);
5791
5953
  const showLogo = /* @__PURE__ */ user_derived(() => !get(config).hideLogo && get(config).display !== "bar");
@@ -5847,7 +6009,7 @@ function Widget($$anchor, $$props) {
5847
6009
  }
5848
6010
  });
5849
6011
  user_effect(() => {
5850
- if (get(config).auto === "onload") {
6012
+ if (get(auto) === "onload") {
5851
6013
  const tm = setTimeout(
5852
6014
  () => {
5853
6015
  verify();
@@ -5872,15 +6034,19 @@ function Widget($$anchor, $$props) {
5872
6034
  }
5873
6035
  });
5874
6036
  onMount(() => {
5875
- log("mounted", "3.0.0-beta.2");
6037
+ log("mounted", "3.0.0-beta.4");
5876
6038
  if (instance) {
5877
6039
  globalThis.$altcha.instances.add(instance);
5878
6040
  }
5879
6041
  set(elForm, get(elRoot)?.closest("form"), true);
5880
6042
  get(elForm)?.addEventListener("reset", onFormReset);
5881
- get(elForm)?.addEventListener("submit", onFormSubmit);
6043
+ get(elForm)?.addEventListener("submit", onFormSubmit, { capture: true });
5882
6044
  get(elForm)?.addEventListener("focusin", onFormFocusIn);
5883
6045
  activatePlugins();
6046
+ if (get(config).humanInteractionSignature) {
6047
+ log("human interaction signature enabled");
6048
+ hisCollector = new Collector();
6049
+ }
5884
6050
  dispatch("load");
5885
6051
  if (!isSecureContext) {
5886
6052
  log("secure context (HTTPS) required");
@@ -5894,8 +6060,9 @@ function Widget($$anchor, $$props) {
5894
6060
  clearTimeout(get(expirationTimeout));
5895
6061
  }
5896
6062
  get(elForm)?.removeEventListener("reset", onFormReset);
5897
- get(elForm)?.removeEventListener("submit", onFormSubmit);
6063
+ get(elForm)?.removeEventListener("submit", onFormSubmit, { capture: true });
5898
6064
  get(elForm)?.removeEventListener("focusin", onFormFocusIn);
6065
+ hisCollector?.destroy();
5899
6066
  };
5900
6067
  });
5901
6068
  function activatePlugins() {
@@ -5959,23 +6126,41 @@ function Widget($$anchor, $$props) {
5959
6126
  async function delay(ms) {
5960
6127
  await new Promise((resolve) => setTimeout(resolve, ms));
5961
6128
  }
5962
- async function fetchChallenge(source2 = get(config).challenge) {
6129
+ async function fetchChallenge(source2 = get(config).challenge, requestOptions) {
5963
6130
  const hook = await callHook("onFetchChallenge", source2);
6131
+ let challenge = null;
5964
6132
  if (hook !== void 0) {
5965
6133
  return hook;
5966
6134
  }
5967
6135
  if (typeof source2 === "string") {
5968
- let challenge = null;
5969
6136
  if (source2.match(/^(https?:)?\//)) {
5970
- log("fetching challenge from", source2);
6137
+ log("fetching challenge from", requestOptions?.method || "GET", source2);
5971
6138
  set(baseUrl, new URL(source2, location.origin), true);
5972
- const resp = await get(config).fetch(source2, { credentials: get(config).credentials || void 0 });
5973
- validateResponse(resp);
6139
+ const resp = await get(config).fetch(source2, {
6140
+ credentials: get(config).credentials || void 0,
6141
+ ...requestOptions
6142
+ });
6143
+ await validateResponse(resp);
5974
6144
  const configHeader = resp.headers.get("x-altcha-config");
5975
6145
  if (configHeader) {
5976
6146
  processConfigHeader(configHeader);
5977
6147
  }
5978
- challenge = await resp.json();
6148
+ const json = await resp.json();
6149
+ if (json && "his" in json && json.his) {
6150
+ log("requested HIS");
6151
+ if (!hisCollector) {
6152
+ throw new Error("Server requested HIS data but collector is disabled.");
6153
+ }
6154
+ return fetchChallenge(getUrl(json.his.url, get(baseUrl)), {
6155
+ body: JSON.stringify({ his: hisCollector.export() }),
6156
+ headers: { "content-type": "application/json" },
6157
+ method: "POST"
6158
+ });
6159
+ }
6160
+ if (json && "hisResult" in json && json.hisResult) {
6161
+ log("HIS result", json.hisResult);
6162
+ }
6163
+ challenge = json;
5979
6164
  } else {
5980
6165
  log("parsing JSON challenge");
5981
6166
  try {
@@ -5984,20 +6169,26 @@ function Widget($$anchor, $$props) {
5984
6169
  throw new Error(`Unable to parse JSON challenge.`);
5985
6170
  }
5986
6171
  }
5987
- if (typeof challenge === "object" && "challenge" in challenge) {
5988
- challenge = createChallengeFromV1(challenge);
5989
- }
5990
- if (!isChallengeValid(challenge)) {
5991
- throw new Error(`Challenge validation failed.`);
5992
- }
5993
- return challenge;
5994
6172
  } else if (source2 && typeof source2 === "object") {
5995
- return JSON.parse(JSON.stringify(source2));
6173
+ try {
6174
+ challenge = JSON.parse(JSON.stringify(source2));
6175
+ } catch {
6176
+ throw new Error(`Unable to parse JSON challenge.`);
6177
+ }
5996
6178
  }
5997
- return null;
6179
+ if (isChallengeV1(challenge)) {
6180
+ challenge = createChallengeFromV1(challenge);
6181
+ }
6182
+ if (!isChallengeValid(challenge)) {
6183
+ throw new Error(`Challenge validation failed.`);
6184
+ }
6185
+ return challenge;
6186
+ }
6187
+ function isChallengeV1(challenge) {
6188
+ return typeof challenge === "object" && "challenge" in challenge;
5998
6189
  }
5999
6190
  function isChallengeValid(challenge) {
6000
- return !!challenge && typeof challenge === "object" && "parameters" in challenge && "signature" in challenge && !!challenge.parameters && typeof challenge.parameters === "object" && "algorithm" in challenge.parameters && "nonce" in challenge.parameters && "salt" in challenge.parameters && "keyPrefix" in challenge.parameters;
6191
+ return !!challenge && typeof challenge === "object" && "parameters" in challenge && !!challenge.parameters && typeof challenge.parameters === "object" && "algorithm" in challenge.parameters && "nonce" in challenge.parameters && "salt" in challenge.parameters && "keyPrefix" in challenge.parameters;
6001
6192
  }
6002
6193
  function getCheckboxElement() {
6003
6194
  return document.getElementById(get(checkboxId));
@@ -6110,7 +6301,7 @@ function Widget($$anchor, $$props) {
6110
6301
  }
6111
6302
  }
6112
6303
  function onFormFocusIn(ev) {
6113
- if (get(config).auto === "onfocus" && get(currentState) === State.UNVERIFIED) {
6304
+ if (get(auto) === "onfocus" && get(currentState) === State.UNVERIFIED) {
6114
6305
  verify();
6115
6306
  }
6116
6307
  }
@@ -6123,7 +6314,7 @@ function Widget($$anchor, $$props) {
6123
6314
  }
6124
6315
  function onFormSubmit(ev) {
6125
6316
  set(elSubmitter, ev.submitter, true);
6126
- if (get(config).auto === "onsubmit" && get(currentState) === State.UNVERIFIED) {
6317
+ if (get(auto) === "onsubmit" && get(currentState) === State.UNVERIFIED) {
6127
6318
  ev.preventDefault();
6128
6319
  ev.stopPropagation();
6129
6320
  show();
@@ -6209,7 +6400,7 @@ function Widget($$anchor, $$props) {
6209
6400
  headers: { "Content-Type": "application/json" },
6210
6401
  method: "POST"
6211
6402
  });
6212
- validateResponse(resp);
6403
+ await validateResponse(resp);
6213
6404
  const json = await resp.json();
6214
6405
  if (json && typeof json === "object" && "payload" in json && !!json.payload) {
6215
6406
  dispatch("serververification", json);
@@ -6250,7 +6441,7 @@ function Widget($$anchor, $$props) {
6250
6441
  case "floating":
6251
6442
  case "overlay":
6252
6443
  hide();
6253
- if (!get(config).auto || get(config).auto === "off") {
6444
+ if (!get(auto) || get(auto) === "off") {
6254
6445
  get(userConfig).auto = "onsubmit";
6255
6446
  }
6256
6447
  break;
@@ -6278,8 +6469,18 @@ function Widget($$anchor, $$props) {
6278
6469
  onExpired();
6279
6470
  }
6280
6471
  }
6281
- function validateResponse(resp) {
6472
+ async function validateResponse(resp) {
6282
6473
  if (resp.status >= 400) {
6474
+ if (resp.headers.get("content-type")?.includes("/json")) {
6475
+ let json;
6476
+ try {
6477
+ json = await resp.json();
6478
+ } catch {
6479
+ }
6480
+ if (json && "error" in json) {
6481
+ throw new Error(`Server responded with ${resp.status} - ${json.error}`);
6482
+ }
6483
+ }
6283
6484
  throw new Error(`Server responded with ${resp.status}.`);
6284
6485
  }
6285
6486
  const contentType = resp.headers.get("content-type");
@@ -6310,7 +6511,7 @@ function Widget($$anchor, $$props) {
6310
6511
  log("verified");
6311
6512
  setState(State.VERIFIED);
6312
6513
  dispatch("verified", { payload: get(payload) });
6313
- if (get(config).auto === "onsubmit") {
6514
+ if (get(auto) === "onsubmit") {
6314
6515
  tick().then(() => {
6315
6516
  requestSubmit(get(elSubmitter));
6316
6517
  });
@@ -6377,7 +6578,7 @@ function Widget($$anchor, $$props) {
6377
6578
  const start = performance.now();
6378
6579
  let challenge = null;
6379
6580
  let solution = null;
6380
- let isChallengeV1 = false;
6581
+ let isChallengeV12 = false;
6381
6582
  const hook = await callHook("onVerify", options);
6382
6583
  if (hook !== void 0) {
6383
6584
  return hook;
@@ -6412,7 +6613,7 @@ function Widget($$anchor, $$props) {
6412
6613
  if (challenge.parameters.expiresAt) {
6413
6614
  setChallengeExpiration(challenge.parameters.expiresAt);
6414
6615
  }
6415
- isChallengeV1 = "_version" in challenge && challenge._version === 1;
6616
+ isChallengeV12 = "_version" in challenge && challenge._version === 1;
6416
6617
  const createWorker = globalThis.$altcha.algorithms.get(challenge.parameters.algorithm);
6417
6618
  if (!createWorker) {
6418
6619
  throw new Error(`Unsupported algorithm ${challenge.parameters.algorithm}.`);
@@ -6422,7 +6623,7 @@ function Widget($$anchor, $$props) {
6422
6623
  concurrency,
6423
6624
  controller,
6424
6625
  createWorker,
6425
- counterMode: isChallengeV1 ? "string" : "uint32",
6626
+ counterMode: isChallengeV12 ? "string" : "uint32",
6426
6627
  onOutOfMemory: (c) => {
6427
6628
  log("out of memory error received");
6428
6629
  dispatch("outofmemory");
@@ -6431,7 +6632,8 @@ function Widget($$anchor, $$props) {
6431
6632
  log(`retrying with ${retryConcurrency} workers...`);
6432
6633
  return retryConcurrency;
6433
6634
  }
6434
- }
6635
+ },
6636
+ timeout: get(config).timeout
6435
6637
  });
6436
6638
  if (get(currentController)?.signal.aborted) {
6437
6639
  reset$1();
@@ -6443,13 +6645,16 @@ function Widget($$anchor, $$props) {
6443
6645
  log("solution", solution);
6444
6646
  await delay(Math.max(0, minDuration - (performance.now() - start)));
6445
6647
  set(codeChallenge, challenge.codeChallenge || get(config).codeChallenge || null, true);
6446
- if (isChallengeV1) {
6648
+ if (isChallengeV12) {
6447
6649
  set(payload, btoa(JSON.stringify(createPayloadV1(challenge, solution))), true);
6448
6650
  } else {
6449
6651
  set(
6450
6652
  payload,
6451
6653
  btoa(JSON.stringify({
6452
- challenge: { ...challenge, codeChallenge: void 0 },
6654
+ challenge: {
6655
+ parameters: challenge.parameters,
6656
+ signature: challenge.signature
6657
+ },
6453
6658
  solution
6454
6659
  })),
6455
6660
  true
@@ -6532,7 +6737,7 @@ function Widget($$anchor, $$props) {
6532
6737
  var div_6 = child(div_5);
6533
6738
  var node_3 = child(div_6);
6534
6739
  {
6535
- let $0 = /* @__PURE__ */ user_derived(() => get(config).display === "standard" && get(config).auto !== "onsubmit" || get(currentState) === State.VERIFYING);
6740
+ let $0 = /* @__PURE__ */ user_derived(() => get(config).display === "standard" && get(auto) !== "onsubmit" || get(currentState) === State.VERIFYING);
6536
6741
  component(node_3, () => get(CheckboxComponent), ($$anchor2, CheckboxComponent_1) => {
6537
6742
  CheckboxComponent_1($$anchor2, {
6538
6743
  get id() {
@@ -6657,6 +6862,9 @@ function Widget($$anchor, $$props) {
6657
6862
  reset$1();
6658
6863
  }
6659
6864
  },
6865
+ get placement() {
6866
+ return get(config).popoverPlacement;
6867
+ },
6660
6868
  role: "alert",
6661
6869
  variant: "error",
6662
6870
  get dir() {
@@ -6717,6 +6925,9 @@ function Widget($$anchor, $$props) {
6717
6925
  onClose: () => {
6718
6926
  reset$1();
6719
6927
  },
6928
+ get placement() {
6929
+ return get(config).popoverPlacement;
6930
+ },
6720
6931
  role: "dialog",
6721
6932
  get "aria-label"() {
6722
6933
  return get(strings).verificationRequired;