@oxyhq/core 12.0.0 → 12.1.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/i18n/locales/ar-SA.json +26 -2
- package/dist/cjs/i18n/locales/ca-ES.json +26 -2
- package/dist/cjs/i18n/locales/de-DE.json +26 -2
- package/dist/cjs/i18n/locales/en-US.json +25 -3
- package/dist/cjs/i18n/locales/es-ES.json +25 -3
- package/dist/cjs/i18n/locales/fr-FR.json +26 -2
- package/dist/cjs/i18n/locales/it-IT.json +26 -2
- package/dist/cjs/i18n/locales/ja-JP.json +26 -2
- package/dist/cjs/i18n/locales/ko-KR.json +26 -2
- package/dist/cjs/i18n/locales/locales/ar-SA.json +26 -2
- package/dist/cjs/i18n/locales/locales/ca-ES.json +26 -2
- package/dist/cjs/i18n/locales/locales/de-DE.json +26 -2
- package/dist/cjs/i18n/locales/locales/en-US.json +25 -3
- package/dist/cjs/i18n/locales/locales/es-ES.json +25 -3
- package/dist/cjs/i18n/locales/locales/fr-FR.json +26 -2
- package/dist/cjs/i18n/locales/locales/it-IT.json +26 -2
- package/dist/cjs/i18n/locales/locales/ja-JP.json +26 -2
- package/dist/cjs/i18n/locales/locales/ko-KR.json +26 -2
- package/dist/cjs/i18n/locales/locales/pt-PT.json +26 -2
- package/dist/cjs/i18n/locales/locales/zh-CN.json +26 -2
- package/dist/cjs/i18n/locales/pt-PT.json +26 -2
- package/dist/cjs/i18n/locales/zh-CN.json +26 -2
- package/dist/cjs/session/accountDialogController.js +83 -21
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/i18n/locales/ar-SA.json +26 -2
- package/dist/esm/i18n/locales/ca-ES.json +26 -2
- package/dist/esm/i18n/locales/de-DE.json +26 -2
- package/dist/esm/i18n/locales/en-US.json +25 -3
- package/dist/esm/i18n/locales/es-ES.json +25 -3
- package/dist/esm/i18n/locales/fr-FR.json +26 -2
- package/dist/esm/i18n/locales/it-IT.json +26 -2
- package/dist/esm/i18n/locales/ja-JP.json +26 -2
- package/dist/esm/i18n/locales/ko-KR.json +26 -2
- package/dist/esm/i18n/locales/locales/ar-SA.json +26 -2
- package/dist/esm/i18n/locales/locales/ca-ES.json +26 -2
- package/dist/esm/i18n/locales/locales/de-DE.json +26 -2
- package/dist/esm/i18n/locales/locales/en-US.json +25 -3
- package/dist/esm/i18n/locales/locales/es-ES.json +25 -3
- package/dist/esm/i18n/locales/locales/fr-FR.json +26 -2
- package/dist/esm/i18n/locales/locales/it-IT.json +26 -2
- package/dist/esm/i18n/locales/locales/ja-JP.json +26 -2
- package/dist/esm/i18n/locales/locales/ko-KR.json +26 -2
- package/dist/esm/i18n/locales/locales/pt-PT.json +26 -2
- package/dist/esm/i18n/locales/locales/zh-CN.json +26 -2
- package/dist/esm/i18n/locales/pt-PT.json +26 -2
- package/dist/esm/i18n/locales/zh-CN.json +26 -2
- package/dist/esm/session/accountDialogController.js +83 -21
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/session/accountDialogController.d.ts +81 -16
- package/package.json +1 -1
- package/src/i18n/locales/ar-SA.json +26 -2
- package/src/i18n/locales/ca-ES.json +26 -2
- package/src/i18n/locales/de-DE.json +26 -2
- package/src/i18n/locales/en-US.json +25 -3
- package/src/i18n/locales/es-ES.json +25 -3
- package/src/i18n/locales/fr-FR.json +26 -2
- package/src/i18n/locales/it-IT.json +26 -2
- package/src/i18n/locales/ja-JP.json +26 -2
- package/src/i18n/locales/ko-KR.json +26 -2
- package/src/i18n/locales/pt-PT.json +26 -2
- package/src/i18n/locales/zh-CN.json +26 -2
- package/src/index.ts +1 -0
- package/src/session/__tests__/accountDialogController.test.ts +79 -2
- package/src/session/__tests__/accountProjection.test.ts +30 -0
- package/src/session/accountDialogController.ts +128 -27
|
@@ -13,17 +13,23 @@
|
|
|
13
13
|
* - the unified account list (via {@link projectSwitchableAccounts}), fetched
|
|
14
14
|
* from `SessionClient` state ∪ `oxyServices.listAccounts()` and hydrated
|
|
15
15
|
* with `oxyServices.getUsersByIds()`;
|
|
16
|
-
* - the dialog `view` state machine (`accounts` | `signin` | `qr` | `add`
|
|
16
|
+
* - the dialog `view` state machine (`accounts` | `signin` | `qr` | `add` |
|
|
17
|
+
* `signup`);
|
|
17
18
|
* - `switchTo` (the uniform switch: `SessionClient.switchAccount` for an
|
|
18
19
|
* account already on the device, `oxyServices.switchToAccount` to mint on
|
|
19
20
|
* first entry into a graph account — reusing the existing SDK primitives, no
|
|
20
21
|
* new switch path);
|
|
21
22
|
* - the "Sign in with Oxy" device flow (same-device shared-keychain via
|
|
22
23
|
* `oxyServices.signInWithSharedIdentity`, else the cross-device QR handoff
|
|
23
|
-
* via `startCommonsSignIn` → poll → `claimSessionByToken`)
|
|
24
|
+
* via `startCommonsSignIn` → poll → `claimSessionByToken`);
|
|
25
|
+
* - `commonsAvailability` — whether Commons is installed on this device
|
|
26
|
+
* (native only, via the injected `canOpenApp` probe), so the QR view can
|
|
27
|
+
* offer a "Get Commons" fallback instead of a same-device dead end.
|
|
24
28
|
*
|
|
25
29
|
* Sign-in is passkey (WebAuthn) or the Commons QR / shared-keychain handoff —
|
|
26
|
-
* password, social login, and 2FA were removed ecosystem-wide.
|
|
30
|
+
* password, social login, and 2FA were removed ecosystem-wide. Account
|
|
31
|
+
* creation (`signup` view) is the same two identity backends: a passkey
|
|
32
|
+
* ceremony on web, or a Commons-created identity.
|
|
27
33
|
*/
|
|
28
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
35
|
exports.AccountDialogController = void 0;
|
|
@@ -67,6 +73,7 @@ class AccountDialogController {
|
|
|
67
73
|
this.error = null;
|
|
68
74
|
this.switchingAccountId = null;
|
|
69
75
|
this.signIn = IDLE_SIGN_IN;
|
|
76
|
+
this.commonsAvailability = 'unknown';
|
|
70
77
|
// --- Sign-in device-flow bookkeeping ---
|
|
71
78
|
/** The secret device-flow token of the active QR flow (never surfaced). */
|
|
72
79
|
this.signInToken = null;
|
|
@@ -95,6 +102,7 @@ class AccountDialogController {
|
|
|
95
102
|
this.clientId = options.clientId ?? null;
|
|
96
103
|
this.locale = options.locale;
|
|
97
104
|
this.commitSession = options.commitSession;
|
|
105
|
+
this.commitSwitchedSession = options.commitSwitchedSession;
|
|
98
106
|
this.onSignedIn = options.onSignedIn;
|
|
99
107
|
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
100
108
|
this.openUrl = options.openUrl;
|
|
@@ -151,6 +159,11 @@ class AccountDialogController {
|
|
|
151
159
|
// bearer is already planted (warm start); when signed out (cold boot before
|
|
152
160
|
// restore) it re-projects from device state and makes NO private call.
|
|
153
161
|
void this.refresh();
|
|
162
|
+
// Eager, cached Commons-availability probe (native only — a no-op when no
|
|
163
|
+
// `canOpenApp` was injected). It's a cheap local OS check, so by the time a
|
|
164
|
+
// user actually opens the sign-in entry it has almost always resolved —
|
|
165
|
+
// `showQr`'s own lazy probe below is only the safety net for the rare race.
|
|
166
|
+
void this.resolveCommonsAvailability();
|
|
154
167
|
}
|
|
155
168
|
/**
|
|
156
169
|
* Stop driving the dialog: unsubscribe from `SessionClient` and tear down the
|
|
@@ -234,6 +247,10 @@ class AccountDialogController {
|
|
|
234
247
|
add() {
|
|
235
248
|
this.setView('add');
|
|
236
249
|
}
|
|
250
|
+
/** Switch to the "create account" view (passkey / Commons signup entry). */
|
|
251
|
+
startSignup() {
|
|
252
|
+
this.setView('signup');
|
|
253
|
+
}
|
|
237
254
|
// =========================================================================
|
|
238
255
|
// Account list
|
|
239
256
|
// =========================================================================
|
|
@@ -379,7 +396,11 @@ class AccountDialogController {
|
|
|
379
396
|
expiresAt: result.expiresAt,
|
|
380
397
|
user: result.user,
|
|
381
398
|
accessToken: result.accessToken,
|
|
382
|
-
}, result.user
|
|
399
|
+
}, result.user,
|
|
400
|
+
// A switch is IN-PLACE: commit without the hub-sync redirect (the
|
|
401
|
+
// device is already known/synced). Cross-tab/app propagation rides the
|
|
402
|
+
// server's `session_state` socket broadcast, not a navigation.
|
|
403
|
+
{ fromSwitch: true });
|
|
383
404
|
}
|
|
384
405
|
// Re-project + refetch immediately; the subscription also fires.
|
|
385
406
|
await this.refresh();
|
|
@@ -447,32 +468,63 @@ class AccountDialogController {
|
|
|
447
468
|
// connect, so it now runs at the slow fallback cadence.
|
|
448
469
|
this.openAuthSessionSocket(handle.sessionToken);
|
|
449
470
|
this.scheduleNextPoll(handle.sessionToken);
|
|
450
|
-
// Same-device convenience: if Commons is installed (native
|
|
451
|
-
//
|
|
452
|
-
//
|
|
471
|
+
// Same-device convenience: if Commons is confirmed installed (native
|
|
472
|
+
// only — stays `'unknown'` on web, where this never opens anything),
|
|
473
|
+
// deep-link straight into its approve screen with the same
|
|
474
|
+
// `oxycommons://approve?...` payload the QR encodes. The QR + polling
|
|
453
475
|
// stay live as the fallback, so a user who dismisses the app-open still
|
|
454
476
|
// completes the sign-in by scanning.
|
|
455
|
-
void this.
|
|
477
|
+
void this.deepLinkIntoCommonsIfAvailable(handle.qrPayload);
|
|
456
478
|
}
|
|
457
479
|
catch (error) {
|
|
458
480
|
this.setSignIn({ ...IDLE_SIGN_IN, phase: 'error', error: errorMessage(error) });
|
|
459
481
|
}
|
|
460
482
|
}
|
|
461
483
|
/**
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
484
|
+
* Resolve whether Commons is installed on this device via the injected
|
|
485
|
+
* `canOpenApp` probe, updating {@link commonsAvailability} as durable,
|
|
486
|
+
* observable snapshot state. Native only — a no-op when `canOpenApp` was
|
|
487
|
+
* not injected (web), where `commonsAvailability` stays `'unknown'` forever
|
|
488
|
+
* and the QR view renders unconditionally (no gating).
|
|
489
|
+
*
|
|
490
|
+
* Replaces the old `maybeOpenCommons` fire-and-forget probe, whose outcome
|
|
491
|
+
* was only ever reflected by whether Commons silently opened — a probe
|
|
492
|
+
* failure or "not installed" answer was swallowed into a debug log with no
|
|
493
|
+
* way for the UI to react. `commonsAvailability` fixes that.
|
|
465
494
|
*/
|
|
466
|
-
async
|
|
467
|
-
if (!this.canOpenApp
|
|
495
|
+
async resolveCommonsAvailability() {
|
|
496
|
+
if (!this.canOpenApp)
|
|
468
497
|
return;
|
|
498
|
+
this.commonsAvailability = 'checking';
|
|
499
|
+
this.emit();
|
|
500
|
+
let available = false;
|
|
469
501
|
try {
|
|
470
|
-
|
|
471
|
-
this.openUrl(qrPayload);
|
|
472
|
-
}
|
|
502
|
+
available = await this.canOpenApp(COMMONS_APP_SCHEME);
|
|
473
503
|
}
|
|
474
504
|
catch (error) {
|
|
475
|
-
logger_1.logger.debug('[AccountDialogController] Commons
|
|
505
|
+
logger_1.logger.debug('[AccountDialogController] Commons availability probe failed', { component: 'AccountDialogController' }, error);
|
|
506
|
+
available = false; // fail-closed — treat a probe error as "not installed"
|
|
507
|
+
}
|
|
508
|
+
this.commonsAvailability = available ? 'available' : 'unavailable';
|
|
509
|
+
this.emit();
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* When Commons is confirmed installed, deep-link straight into its approve
|
|
513
|
+
* screen via the injected `openUrl` with the same `oxycommons://approve?...`
|
|
514
|
+
* payload the QR encodes. Best-effort and non-blocking — the QR/polling
|
|
515
|
+
* fallback stays live regardless of the outcome here.
|
|
516
|
+
*/
|
|
517
|
+
async deepLinkIntoCommonsIfAvailable(qrPayload) {
|
|
518
|
+
if (!this.openUrl)
|
|
519
|
+
return;
|
|
520
|
+
if (this.commonsAvailability === 'unknown' || this.commonsAvailability === 'checking') {
|
|
521
|
+
// The eager `start()` probe hasn't resolved yet (or was never run, e.g.
|
|
522
|
+
// `showQr` called without a prior `start()`) — resolve it now rather
|
|
523
|
+
// than skipping the deep link.
|
|
524
|
+
await this.resolveCommonsAvailability();
|
|
525
|
+
}
|
|
526
|
+
if (this.commonsAvailability === 'available') {
|
|
527
|
+
this.openUrl(qrPayload);
|
|
476
528
|
}
|
|
477
529
|
}
|
|
478
530
|
/** Tear down the active sign-in device flow (timers + socket + token) and reset to idle. */
|
|
@@ -595,12 +647,21 @@ class AccountDialogController {
|
|
|
595
647
|
}
|
|
596
648
|
/**
|
|
597
649
|
* Register a token-planted session into the device set. Prefers the
|
|
598
|
-
* consumer's
|
|
650
|
+
* consumer's commit funnel (durable persist + hydration); falls back to
|
|
599
651
|
* `SessionClient.registerAndActivate` (registration + activation only).
|
|
652
|
+
*
|
|
653
|
+
* A SWITCH (`opts.fromSwitch`) uses the IN-PLACE `commitSwitchedSession` funnel
|
|
654
|
+
* so it never runs the cross-origin hub-sync redirect; a SIGN-IN uses
|
|
655
|
+
* `commitSession` (which may hub-sync on an official web origin). When the
|
|
656
|
+
* switch funnel is not wired it falls back to the sign-in funnel, then to
|
|
657
|
+
* `registerAndActivate`.
|
|
600
658
|
*/
|
|
601
|
-
async commitAuthorizedSession(session, user) {
|
|
602
|
-
|
|
603
|
-
|
|
659
|
+
async commitAuthorizedSession(session, user, opts) {
|
|
660
|
+
const commit = opts?.fromSwitch
|
|
661
|
+
? this.commitSwitchedSession ?? this.commitSession
|
|
662
|
+
: this.commitSession;
|
|
663
|
+
if (commit) {
|
|
664
|
+
await commit(session);
|
|
604
665
|
}
|
|
605
666
|
else {
|
|
606
667
|
await this.sessionClient.registerAndActivate(user.id);
|
|
@@ -712,6 +773,7 @@ class AccountDialogController {
|
|
|
712
773
|
error: this.error,
|
|
713
774
|
switchingAccountId: this.switchingAccountId,
|
|
714
775
|
signIn: this.signIn,
|
|
776
|
+
commonsAvailability: this.commonsAvailability,
|
|
715
777
|
};
|
|
716
778
|
}
|
|
717
779
|
/** Recompute the snapshot and notify subscribers. */
|