@poly-x/react 0.1.0-alpha.16 → 0.1.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +47 -22
- package/dist/index.d.cts +21 -10
- package/dist/index.d.mts +21 -10
- package/dist/index.mjs +47 -22
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -515,43 +515,68 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
515
515
|
}
|
|
516
516
|
//#endregion
|
|
517
517
|
//#region src/geolocation.ts
|
|
518
|
-
/**
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
async function capturePreciseLocation(consented, opts = {}) {
|
|
523
|
-
if (!consented) return { consent: "denied" };
|
|
524
|
-
const geo = opts.geolocation ?? (typeof navigator !== "undefined" ? navigator.geolocation : void 0);
|
|
525
|
-
if (!geo) return { consent: "denied" };
|
|
526
|
-
const timeoutMs = opts.timeoutMs ?? 8e3;
|
|
518
|
+
/** W3C Geolocation `PositionError.PERMISSION_DENIED`. The only code that means a real "no". */
|
|
519
|
+
const PERMISSION_DENIED = 1;
|
|
520
|
+
/** One capture attempt at a given accuracy. Never rejects. */
|
|
521
|
+
function attemptCapture(geo, highAccuracy, timeout) {
|
|
527
522
|
return new Promise((resolve) => {
|
|
528
523
|
let settled = false;
|
|
529
|
-
const done = (
|
|
524
|
+
const done = (outcome) => {
|
|
530
525
|
if (settled) return;
|
|
531
526
|
settled = true;
|
|
532
|
-
resolve(
|
|
527
|
+
resolve(outcome);
|
|
533
528
|
};
|
|
534
|
-
const timer = setTimeout(() => done({
|
|
535
|
-
|
|
529
|
+
const timer = setTimeout(() => done({
|
|
530
|
+
result: { consent: "granted" },
|
|
531
|
+
retriable: true
|
|
532
|
+
}), timeout + 1500);
|
|
533
|
+
const finish = (outcome) => {
|
|
536
534
|
clearTimeout(timer);
|
|
537
|
-
done(
|
|
535
|
+
done(outcome);
|
|
538
536
|
};
|
|
539
537
|
try {
|
|
540
538
|
geo.getCurrentPosition((position) => finish({
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
539
|
+
result: {
|
|
540
|
+
latitude: String(position.coords.latitude),
|
|
541
|
+
longitude: String(position.coords.longitude),
|
|
542
|
+
accuracyMeters: typeof position.coords.accuracy === "number" ? position.coords.accuracy : void 0,
|
|
543
|
+
consent: "granted"
|
|
544
|
+
},
|
|
545
|
+
retriable: false
|
|
546
|
+
}), (err) => {
|
|
547
|
+
const code = err?.code;
|
|
548
|
+
const denied = typeof code === "number" && code === PERMISSION_DENIED;
|
|
549
|
+
finish({
|
|
550
|
+
result: { consent: denied ? "denied" : "granted" },
|
|
551
|
+
retriable: !denied
|
|
552
|
+
});
|
|
553
|
+
}, {
|
|
554
|
+
enableHighAccuracy: highAccuracy,
|
|
555
|
+
timeout,
|
|
548
556
|
maximumAge: 0
|
|
549
557
|
});
|
|
550
558
|
} catch {
|
|
551
|
-
finish({
|
|
559
|
+
finish({
|
|
560
|
+
result: { consent: "granted" },
|
|
561
|
+
retriable: true
|
|
562
|
+
});
|
|
552
563
|
}
|
|
553
564
|
});
|
|
554
565
|
}
|
|
566
|
+
/**
|
|
567
|
+
* Capture the browser's precise location. Resolves to a consent-labelled result and never
|
|
568
|
+
* throws/rejects. High-accuracy first; on a no-fix (not a denial) retries once at low accuracy.
|
|
569
|
+
*/
|
|
570
|
+
async function capturePreciseLocation(consented, opts = {}) {
|
|
571
|
+
if (!consented) return { consent: "denied" };
|
|
572
|
+
const geo = opts.geolocation ?? (typeof navigator !== "undefined" ? navigator.geolocation : void 0);
|
|
573
|
+
if (!geo) return { consent: "denied" };
|
|
574
|
+
const highAccuracyMs = opts.timeoutMs ?? 8e3;
|
|
575
|
+
const lowAccuracyMs = opts.fallbackTimeoutMs ?? 12e3;
|
|
576
|
+
const first = await attemptCapture(geo, true, highAccuracyMs);
|
|
577
|
+
if (first.result.latitude !== void 0 || !first.retriable) return first.result;
|
|
578
|
+
return (await attemptCapture(geo, false, lowAccuracyMs)).result;
|
|
579
|
+
}
|
|
555
580
|
//#endregion
|
|
556
581
|
//#region src/components/sign-in.tsx
|
|
557
582
|
const DEFAULT_LABELS$3 = {
|
package/dist/index.d.cts
CHANGED
|
@@ -410,11 +410,17 @@ declare function AuthCallback({
|
|
|
410
410
|
/**
|
|
411
411
|
* @poly-x/react/geolocation — browser precise-location capture for sign-in (v6, FR-CNST/FR-LOC).
|
|
412
412
|
*
|
|
413
|
-
* The consent-gated bridge to `navigator.geolocation`. It NEVER rejects: a
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
413
|
+
* The consent-gated bridge to `navigator.geolocation`. It NEVER rejects: a missing API resolves
|
|
414
|
+
* to `{ consent: "denied" }` with no coordinates so the caller can always proceed — sign-in must
|
|
415
|
+
* never block on location (D52).
|
|
416
|
+
*
|
|
417
|
+
* Consent vs. fix are kept SEPARATE. `consent` reflects the browser PERMISSION decision only:
|
|
418
|
+
* `denied` means the user actually said no (`PERMISSION_DENIED`). A position that is merely
|
|
419
|
+
* unavailable or times out (common on desktops with no GPS) is NOT a denial — the user consented,
|
|
420
|
+
* we just couldn't get coordinates, so consent stays `granted` with no coords and the platform
|
|
421
|
+
* uses the coarse (IP) fallback. Conflating the two is why an allowed login could be stored as
|
|
422
|
+
* `denied` + coarse. On a no-fix (not a denial) we retry once at low accuracy (network-based),
|
|
423
|
+
* which is far more reliable than high-accuracy GPS on desktop browsers.
|
|
418
424
|
*/
|
|
419
425
|
/** Minimal shape of the browser Geolocation API we depend on (injectable for testing). */
|
|
420
426
|
interface GeolocationLike {
|
|
@@ -424,28 +430,33 @@ interface GeolocationLike {
|
|
|
424
430
|
longitude: number;
|
|
425
431
|
accuracy?: number;
|
|
426
432
|
};
|
|
427
|
-
}) => void, error?: (err:
|
|
433
|
+
}) => void, error?: (err: {
|
|
434
|
+
code?: number;
|
|
435
|
+
} | unknown) => void, options?: {
|
|
428
436
|
enableHighAccuracy?: boolean;
|
|
429
437
|
timeout?: number;
|
|
430
438
|
maximumAge?: number;
|
|
431
439
|
}): void;
|
|
432
440
|
}
|
|
433
|
-
/** The consent-labelled result of a capture attempt. Coordinates present only
|
|
441
|
+
/** The consent-labelled result of a capture attempt. Coordinates present only on a successful fix. */
|
|
434
442
|
interface CapturedGeo {
|
|
435
443
|
latitude?: string;
|
|
436
444
|
longitude?: string;
|
|
437
445
|
accuracyMeters?: number;
|
|
446
|
+
/** The browser PERMISSION decision only — never downgraded by a failed/timed-out fix. */
|
|
438
447
|
consent: "granted" | "denied";
|
|
439
448
|
}
|
|
440
449
|
interface CaptureOptions {
|
|
441
|
-
/**
|
|
450
|
+
/** High-accuracy (GPS) attempt budget before the network-based retry. Default 8000ms. */
|
|
442
451
|
timeoutMs?: number;
|
|
452
|
+
/** Low-accuracy (network) retry budget when the first attempt gets no fix. Default 12000ms. */
|
|
453
|
+
fallbackTimeoutMs?: number;
|
|
443
454
|
/** Override the geolocation source (tests). Defaults to `navigator.geolocation`. */
|
|
444
455
|
geolocation?: GeolocationLike;
|
|
445
456
|
}
|
|
446
457
|
/**
|
|
447
|
-
* Capture the browser's precise location
|
|
448
|
-
*
|
|
458
|
+
* Capture the browser's precise location. Resolves to a consent-labelled result and never
|
|
459
|
+
* throws/rejects. High-accuracy first; on a no-fix (not a denial) retries once at low accuracy.
|
|
449
460
|
*/
|
|
450
461
|
declare function capturePreciseLocation(consented: boolean, opts?: CaptureOptions): Promise<CapturedGeo>;
|
|
451
462
|
//#endregion
|
package/dist/index.d.mts
CHANGED
|
@@ -410,11 +410,17 @@ declare function AuthCallback({
|
|
|
410
410
|
/**
|
|
411
411
|
* @poly-x/react/geolocation — browser precise-location capture for sign-in (v6, FR-CNST/FR-LOC).
|
|
412
412
|
*
|
|
413
|
-
* The consent-gated bridge to `navigator.geolocation`. It NEVER rejects: a
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
413
|
+
* The consent-gated bridge to `navigator.geolocation`. It NEVER rejects: a missing API resolves
|
|
414
|
+
* to `{ consent: "denied" }` with no coordinates so the caller can always proceed — sign-in must
|
|
415
|
+
* never block on location (D52).
|
|
416
|
+
*
|
|
417
|
+
* Consent vs. fix are kept SEPARATE. `consent` reflects the browser PERMISSION decision only:
|
|
418
|
+
* `denied` means the user actually said no (`PERMISSION_DENIED`). A position that is merely
|
|
419
|
+
* unavailable or times out (common on desktops with no GPS) is NOT a denial — the user consented,
|
|
420
|
+
* we just couldn't get coordinates, so consent stays `granted` with no coords and the platform
|
|
421
|
+
* uses the coarse (IP) fallback. Conflating the two is why an allowed login could be stored as
|
|
422
|
+
* `denied` + coarse. On a no-fix (not a denial) we retry once at low accuracy (network-based),
|
|
423
|
+
* which is far more reliable than high-accuracy GPS on desktop browsers.
|
|
418
424
|
*/
|
|
419
425
|
/** Minimal shape of the browser Geolocation API we depend on (injectable for testing). */
|
|
420
426
|
interface GeolocationLike {
|
|
@@ -424,28 +430,33 @@ interface GeolocationLike {
|
|
|
424
430
|
longitude: number;
|
|
425
431
|
accuracy?: number;
|
|
426
432
|
};
|
|
427
|
-
}) => void, error?: (err:
|
|
433
|
+
}) => void, error?: (err: {
|
|
434
|
+
code?: number;
|
|
435
|
+
} | unknown) => void, options?: {
|
|
428
436
|
enableHighAccuracy?: boolean;
|
|
429
437
|
timeout?: number;
|
|
430
438
|
maximumAge?: number;
|
|
431
439
|
}): void;
|
|
432
440
|
}
|
|
433
|
-
/** The consent-labelled result of a capture attempt. Coordinates present only
|
|
441
|
+
/** The consent-labelled result of a capture attempt. Coordinates present only on a successful fix. */
|
|
434
442
|
interface CapturedGeo {
|
|
435
443
|
latitude?: string;
|
|
436
444
|
longitude?: string;
|
|
437
445
|
accuracyMeters?: number;
|
|
446
|
+
/** The browser PERMISSION decision only — never downgraded by a failed/timed-out fix. */
|
|
438
447
|
consent: "granted" | "denied";
|
|
439
448
|
}
|
|
440
449
|
interface CaptureOptions {
|
|
441
|
-
/**
|
|
450
|
+
/** High-accuracy (GPS) attempt budget before the network-based retry. Default 8000ms. */
|
|
442
451
|
timeoutMs?: number;
|
|
452
|
+
/** Low-accuracy (network) retry budget when the first attempt gets no fix. Default 12000ms. */
|
|
453
|
+
fallbackTimeoutMs?: number;
|
|
443
454
|
/** Override the geolocation source (tests). Defaults to `navigator.geolocation`. */
|
|
444
455
|
geolocation?: GeolocationLike;
|
|
445
456
|
}
|
|
446
457
|
/**
|
|
447
|
-
* Capture the browser's precise location
|
|
448
|
-
*
|
|
458
|
+
* Capture the browser's precise location. Resolves to a consent-labelled result and never
|
|
459
|
+
* throws/rejects. High-accuracy first; on a no-fix (not a denial) retries once at low accuracy.
|
|
449
460
|
*/
|
|
450
461
|
declare function capturePreciseLocation(consented: boolean, opts?: CaptureOptions): Promise<CapturedGeo>;
|
|
451
462
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -514,43 +514,68 @@ function Protected({ children, fallback = null, loading = null }) {
|
|
|
514
514
|
}
|
|
515
515
|
//#endregion
|
|
516
516
|
//#region src/geolocation.ts
|
|
517
|
-
/**
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
async function capturePreciseLocation(consented, opts = {}) {
|
|
522
|
-
if (!consented) return { consent: "denied" };
|
|
523
|
-
const geo = opts.geolocation ?? (typeof navigator !== "undefined" ? navigator.geolocation : void 0);
|
|
524
|
-
if (!geo) return { consent: "denied" };
|
|
525
|
-
const timeoutMs = opts.timeoutMs ?? 8e3;
|
|
517
|
+
/** W3C Geolocation `PositionError.PERMISSION_DENIED`. The only code that means a real "no". */
|
|
518
|
+
const PERMISSION_DENIED = 1;
|
|
519
|
+
/** One capture attempt at a given accuracy. Never rejects. */
|
|
520
|
+
function attemptCapture(geo, highAccuracy, timeout) {
|
|
526
521
|
return new Promise((resolve) => {
|
|
527
522
|
let settled = false;
|
|
528
|
-
const done = (
|
|
523
|
+
const done = (outcome) => {
|
|
529
524
|
if (settled) return;
|
|
530
525
|
settled = true;
|
|
531
|
-
resolve(
|
|
526
|
+
resolve(outcome);
|
|
532
527
|
};
|
|
533
|
-
const timer = setTimeout(() => done({
|
|
534
|
-
|
|
528
|
+
const timer = setTimeout(() => done({
|
|
529
|
+
result: { consent: "granted" },
|
|
530
|
+
retriable: true
|
|
531
|
+
}), timeout + 1500);
|
|
532
|
+
const finish = (outcome) => {
|
|
535
533
|
clearTimeout(timer);
|
|
536
|
-
done(
|
|
534
|
+
done(outcome);
|
|
537
535
|
};
|
|
538
536
|
try {
|
|
539
537
|
geo.getCurrentPosition((position) => finish({
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
538
|
+
result: {
|
|
539
|
+
latitude: String(position.coords.latitude),
|
|
540
|
+
longitude: String(position.coords.longitude),
|
|
541
|
+
accuracyMeters: typeof position.coords.accuracy === "number" ? position.coords.accuracy : void 0,
|
|
542
|
+
consent: "granted"
|
|
543
|
+
},
|
|
544
|
+
retriable: false
|
|
545
|
+
}), (err) => {
|
|
546
|
+
const code = err?.code;
|
|
547
|
+
const denied = typeof code === "number" && code === PERMISSION_DENIED;
|
|
548
|
+
finish({
|
|
549
|
+
result: { consent: denied ? "denied" : "granted" },
|
|
550
|
+
retriable: !denied
|
|
551
|
+
});
|
|
552
|
+
}, {
|
|
553
|
+
enableHighAccuracy: highAccuracy,
|
|
554
|
+
timeout,
|
|
547
555
|
maximumAge: 0
|
|
548
556
|
});
|
|
549
557
|
} catch {
|
|
550
|
-
finish({
|
|
558
|
+
finish({
|
|
559
|
+
result: { consent: "granted" },
|
|
560
|
+
retriable: true
|
|
561
|
+
});
|
|
551
562
|
}
|
|
552
563
|
});
|
|
553
564
|
}
|
|
565
|
+
/**
|
|
566
|
+
* Capture the browser's precise location. Resolves to a consent-labelled result and never
|
|
567
|
+
* throws/rejects. High-accuracy first; on a no-fix (not a denial) retries once at low accuracy.
|
|
568
|
+
*/
|
|
569
|
+
async function capturePreciseLocation(consented, opts = {}) {
|
|
570
|
+
if (!consented) return { consent: "denied" };
|
|
571
|
+
const geo = opts.geolocation ?? (typeof navigator !== "undefined" ? navigator.geolocation : void 0);
|
|
572
|
+
if (!geo) return { consent: "denied" };
|
|
573
|
+
const highAccuracyMs = opts.timeoutMs ?? 8e3;
|
|
574
|
+
const lowAccuracyMs = opts.fallbackTimeoutMs ?? 12e3;
|
|
575
|
+
const first = await attemptCapture(geo, true, highAccuracyMs);
|
|
576
|
+
if (first.result.latitude !== void 0 || !first.retriable) return first.result;
|
|
577
|
+
return (await attemptCapture(geo, false, lowAccuracyMs)).result;
|
|
578
|
+
}
|
|
554
579
|
//#endregion
|
|
555
580
|
//#region src/components/sign-in.tsx
|
|
556
581
|
const DEFAULT_LABELS$3 = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poly-x/react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.17",
|
|
4
4
|
"description": "PolyX SDK for React SPAs - provider, hooks, drop-in auth UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"module": "./dist/index.mjs",
|
|
28
28
|
"types": "./dist/index.d.cts",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@poly-x/core": "0.1.0-alpha.
|
|
30
|
+
"@poly-x/core": "0.1.0-alpha.17"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=18",
|