@unisim/sdk 0.125.0 → 0.126.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.
- package/README.md +13 -0
- package/android/src/main/AndroidManifest.xml +12 -1
- package/dist/OAuthButtons.d.ts +21 -0
- package/dist/OAuthButtons.d.ts.map +1 -0
- package/dist/OAuthButtons.js +79 -0
- package/dist/OAuthButtons.js.map +1 -0
- package/dist/SignInDialog.d.ts.map +1 -1
- package/dist/SignInDialog.js +86 -3
- package/dist/SignInDialog.js.map +1 -1
- package/dist/SuiteSwitcher.d.ts.map +1 -1
- package/dist/SuiteSwitcher.js +46 -1
- package/dist/SuiteSwitcher.js.map +1 -1
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +56 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mockAuth.d.ts.map +1 -1
- package/dist/mockAuth.js +115 -5
- package/dist/mockAuth.js.map +1 -1
- package/dist/oauth.d.ts +99 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +132 -0
- package/dist/oauth.js.map +1 -0
- package/dist/security.d.ts +56 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +129 -0
- package/dist/security.js.map +1 -0
- package/dist/suiteApps.d.ts +29 -6
- package/dist/suiteApps.d.ts.map +1 -1
- package/dist/suiteApps.js +25 -6
- package/dist/suiteApps.js.map +1 -1
- package/package.json +3 -1
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
export const OAUTH_PROVIDERS = ['google', 'azure'];
|
|
3
|
+
/** Human label for a provider, for buttons and error text. */
|
|
4
|
+
export function oauthProviderLabel(provider) {
|
|
5
|
+
return provider === 'azure' ? 'Microsoft' : 'Google';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Begin a Microsoft / Google sign-in, upgrading an anonymous trial session in
|
|
9
|
+
* place where there is one. Resolves only if the redirect could not be
|
|
10
|
+
* started — on success the browser is already navigating away.
|
|
11
|
+
*/
|
|
12
|
+
export async function startOAuthSignIn(supabase, provider, options = {}) {
|
|
13
|
+
const redirectTo = options.redirectTo ?? defaultRedirectTo();
|
|
14
|
+
let anonymous = false;
|
|
15
|
+
if (!options.forceNewSession) {
|
|
16
|
+
const { data } = await supabase.auth.getSession();
|
|
17
|
+
anonymous = data.session?.user?.is_anonymous === true;
|
|
18
|
+
}
|
|
19
|
+
if (anonymous) {
|
|
20
|
+
const { error } = await supabase.auth.linkIdentity({
|
|
21
|
+
provider,
|
|
22
|
+
options: { redirectTo },
|
|
23
|
+
});
|
|
24
|
+
return { mode: 'link', error: error ? normaliseError(error) : null };
|
|
25
|
+
}
|
|
26
|
+
const { error } = await supabase.auth.signInWithOAuth({
|
|
27
|
+
provider,
|
|
28
|
+
options: { redirectTo },
|
|
29
|
+
});
|
|
30
|
+
return { mode: 'signin', error: error ? normaliseError(error) : null };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sign out of the anonymous session and start a fresh provider sign-in.
|
|
34
|
+
*
|
|
35
|
+
* The escape hatch for `identity_already_exists`: the visitor's chosen account
|
|
36
|
+
* already belongs to a different Universal ID, so their anonymous work cannot
|
|
37
|
+
* be carried over by any means. Call this only after they have confirmed they
|
|
38
|
+
* are willing to leave it behind.
|
|
39
|
+
*/
|
|
40
|
+
export async function abandonTrialAndSignIn(supabase, provider, options = {}) {
|
|
41
|
+
// `scope: 'local'` — the anonymous session is being discarded on this device,
|
|
42
|
+
// and a global sign-out would be a pointless network round trip against a
|
|
43
|
+
// user we are about to stop being.
|
|
44
|
+
await supabase.auth.signOut({ scope: 'local' });
|
|
45
|
+
return startOAuthSignIn(supabase, provider, { ...options, forceNewSession: true });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read an OAuth failure off the URL we were just redirected back to.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ **Both the query string and the hash have to be checked.** Supabase's
|
|
51
|
+
* PKCE flow reports failures as query params, the implicit flow puts them in
|
|
52
|
+
* the fragment, and which one you get depends on project configuration rather
|
|
53
|
+
* than on anything in this repo. Checking one of the two produces a page that
|
|
54
|
+
* looks like a completely successful sign-in that simply did not sign anybody
|
|
55
|
+
* in — the single worst outcome for this feature.
|
|
56
|
+
*/
|
|
57
|
+
export function readOAuthError(href) {
|
|
58
|
+
if (!href && typeof window === 'undefined')
|
|
59
|
+
return null;
|
|
60
|
+
const url = new URL(href ?? window.location.href);
|
|
61
|
+
for (const params of [url.searchParams, new URLSearchParams(url.hash.replace(/^#/, ''))]) {
|
|
62
|
+
const bucket = params.get('error');
|
|
63
|
+
const code = params.get('error_code');
|
|
64
|
+
const description = params.get('error_description');
|
|
65
|
+
if (bucket || code || description) {
|
|
66
|
+
return {
|
|
67
|
+
code: code ?? bucket ?? null,
|
|
68
|
+
// URLSearchParams already decodes, but providers double-encode the
|
|
69
|
+
// '+' that separates words often enough to be worth normalising.
|
|
70
|
+
message: (description ?? bucket ?? 'Sign-in failed').replace(/\+/g, ' '),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Strip the OAuth error params from the address bar without adding a history
|
|
78
|
+
* entry, so a reload (or a second attempt) doesn't resurrect a dead error.
|
|
79
|
+
*/
|
|
80
|
+
export function clearOAuthError() {
|
|
81
|
+
if (typeof window === 'undefined')
|
|
82
|
+
return;
|
|
83
|
+
const url = new URL(window.location.href);
|
|
84
|
+
let touched = false;
|
|
85
|
+
for (const key of ['error', 'error_code', 'error_description']) {
|
|
86
|
+
if (url.searchParams.has(key)) {
|
|
87
|
+
url.searchParams.delete(key);
|
|
88
|
+
touched = true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (/(^|[#&])error(_code|_description)?=/.test(url.hash)) {
|
|
92
|
+
url.hash = '';
|
|
93
|
+
touched = true;
|
|
94
|
+
}
|
|
95
|
+
if (touched)
|
|
96
|
+
window.history.replaceState(null, '', url.toString());
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Whether a failure means "that account already belongs to somebody else", the
|
|
100
|
+
* one case where the anonymous work genuinely cannot be carried across and the
|
|
101
|
+
* user has a decision to make.
|
|
102
|
+
*/
|
|
103
|
+
export function isIdentityCollision(error) {
|
|
104
|
+
if (!error)
|
|
105
|
+
return false;
|
|
106
|
+
if (error.code === 'identity_already_exists')
|
|
107
|
+
return true;
|
|
108
|
+
return /already (been )?(linked|registered|exists)/i.test(error.message);
|
|
109
|
+
}
|
|
110
|
+
/** Whether the project is missing the "Allow manual linking" setting. */
|
|
111
|
+
export function isManualLinkingDisabled(error) {
|
|
112
|
+
if (!error)
|
|
113
|
+
return false;
|
|
114
|
+
if (error.code === 'manual_linking_disabled')
|
|
115
|
+
return true;
|
|
116
|
+
return /manual linking is disabled/i.test(error.message);
|
|
117
|
+
}
|
|
118
|
+
// ── Internals ─────────────────────────────────────────────────────────────────
|
|
119
|
+
function defaultRedirectTo() {
|
|
120
|
+
if (typeof window === 'undefined')
|
|
121
|
+
return undefined;
|
|
122
|
+
const url = new URL(window.location.href);
|
|
123
|
+
for (const key of ['error', 'error_code', 'error_description'])
|
|
124
|
+
url.searchParams.delete(key);
|
|
125
|
+
url.hash = '';
|
|
126
|
+
return url.toString();
|
|
127
|
+
}
|
|
128
|
+
function normaliseError(error) {
|
|
129
|
+
const e = error;
|
|
130
|
+
return { message: e?.message ?? 'Sign-in failed', code: e?.code ?? null };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useUniversal } from './provider.js';
|
|
2
|
+
export interface OrgSecuritySettings {
|
|
3
|
+
requireMfa: boolean;
|
|
4
|
+
requireSso: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface UseOrgSecurityResult {
|
|
7
|
+
settings: OrgSecuritySettings;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Non-null when the query FAILED. Callers must not read this as "nothing is
|
|
11
|
+
* required" — a policy that disappears whenever the network hiccups is not a
|
|
12
|
+
* policy. Gates should hold, or retry, rather than fail open.
|
|
13
|
+
*/
|
|
14
|
+
error: string | null;
|
|
15
|
+
refresh: () => void;
|
|
16
|
+
/** Admin write. Rejected by RLS for anyone who isn't owner/admin of the org. */
|
|
17
|
+
save: (next: Partial<OrgSecuritySettings>) => Promise<{
|
|
18
|
+
ok: true;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
error: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare function useOrgSecurity(orgId?: string | null): UseOrgSecurityResult;
|
|
25
|
+
export interface MfaFactor {
|
|
26
|
+
id: string;
|
|
27
|
+
friendlyName: string | null;
|
|
28
|
+
/** Supabase marks a factor 'unverified' until the first correct code. */
|
|
29
|
+
verified: boolean;
|
|
30
|
+
createdAt: string | null;
|
|
31
|
+
}
|
|
32
|
+
export interface UseMfaFactorsResult {
|
|
33
|
+
factors: MfaFactor[];
|
|
34
|
+
/** True once at least one factor is VERIFIED — an unverified one protects nothing. */
|
|
35
|
+
enrolled: boolean;
|
|
36
|
+
loading: boolean;
|
|
37
|
+
error: string | null;
|
|
38
|
+
refresh: () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function useMfaFactors(): UseMfaFactorsResult;
|
|
41
|
+
/**
|
|
42
|
+
* Whether this session has actually passed a second factor, as opposed to
|
|
43
|
+
* merely having one enrolled.
|
|
44
|
+
*
|
|
45
|
+
* `nextLevel === 'aal2'` means the user HAS a verified factor;
|
|
46
|
+
* `currentLevel === 'aal2'` means they have used it in this session. The gap
|
|
47
|
+
* between the two is the challenge step — read them separately, because
|
|
48
|
+
* conflating them either nags a user who has just passed the challenge or
|
|
49
|
+
* waves through one who never started it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function readAssuranceLevel(supabase: ReturnType<typeof useUniversal>['supabase']): Promise<{
|
|
52
|
+
current: string | null;
|
|
53
|
+
next: string | null;
|
|
54
|
+
challengeRequired: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
//# sourceMappingURL=security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAgB5C,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,OAAO,CAAA;CACpB;AAID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,gFAAgF;IAChF,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,mBAAmB,CAAC,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnG;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,oBAAoB,CAgE1E;AAID,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,sFAAsF;IACtF,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,wBAAgB,aAAa,IAAI,mBAAmB,CAsDnD;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,UAAU,CAAC,GACpD,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAE,CAAC,CAQtF"}
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { useUniversal } from './provider.js';
|
|
4
|
+
const NOTHING_REQUIRED = { requireMfa: false, requireSso: false };
|
|
5
|
+
export function useOrgSecurity(orgId) {
|
|
6
|
+
const { supabase, activeOrgId, session } = useUniversal();
|
|
7
|
+
const targetOrg = orgId ?? activeOrgId ?? null;
|
|
8
|
+
const [settings, setSettings] = useState(NOTHING_REQUIRED);
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const [nonce, setNonce] = useState(0);
|
|
12
|
+
const refresh = useCallback(() => setNonce((n) => n + 1), []);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!session || !targetOrg) {
|
|
15
|
+
setSettings(NOTHING_REQUIRED);
|
|
16
|
+
setError(null);
|
|
17
|
+
setLoading(false);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let cancelled = false;
|
|
21
|
+
setLoading(true);
|
|
22
|
+
supabase
|
|
23
|
+
.from('org_security_settings')
|
|
24
|
+
.select('require_mfa, require_sso')
|
|
25
|
+
.eq('org_id', targetOrg)
|
|
26
|
+
.maybeSingle()
|
|
27
|
+
.then(({ data, error: queryError }) => {
|
|
28
|
+
if (cancelled)
|
|
29
|
+
return;
|
|
30
|
+
if (queryError) {
|
|
31
|
+
setError(queryError.message);
|
|
32
|
+
setSettings(NOTHING_REQUIRED);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
setError(null);
|
|
36
|
+
setSettings({
|
|
37
|
+
requireMfa: data?.require_mfa === true,
|
|
38
|
+
requireSso: data?.require_sso === true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
setLoading(false);
|
|
42
|
+
});
|
|
43
|
+
return () => { cancelled = true; };
|
|
44
|
+
}, [supabase, session, targetOrg, nonce]);
|
|
45
|
+
const save = useCallback(async (next) => {
|
|
46
|
+
if (!targetOrg)
|
|
47
|
+
return { ok: false, error: 'No active organisation' };
|
|
48
|
+
const merged = { ...settings, ...next };
|
|
49
|
+
// Upsert, because the common case is that no row exists yet. updated_at
|
|
50
|
+
// and updated_by are written by the 0138 trigger — never send them.
|
|
51
|
+
const { error: writeError } = await supabase
|
|
52
|
+
.from('org_security_settings')
|
|
53
|
+
.upsert({ org_id: targetOrg, require_mfa: merged.requireMfa, require_sso: merged.requireSso }, { onConflict: 'org_id' });
|
|
54
|
+
if (writeError)
|
|
55
|
+
return { ok: false, error: writeError.message };
|
|
56
|
+
setSettings(merged);
|
|
57
|
+
return { ok: true };
|
|
58
|
+
}, [supabase, targetOrg, settings]);
|
|
59
|
+
return { settings, loading, error, refresh, save };
|
|
60
|
+
}
|
|
61
|
+
export function useMfaFactors() {
|
|
62
|
+
const { supabase, session } = useUniversal();
|
|
63
|
+
const [factors, setFactors] = useState([]);
|
|
64
|
+
const [loading, setLoading] = useState(true);
|
|
65
|
+
const [error, setError] = useState(null);
|
|
66
|
+
const [nonce, setNonce] = useState(0);
|
|
67
|
+
const refresh = useCallback(() => setNonce((n) => n + 1), []);
|
|
68
|
+
const userId = session?.user?.id ?? null;
|
|
69
|
+
const isAnonymous = session?.user?.is_anonymous === true;
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
// An anonymous trial user has no factors and cannot enrol one — asking
|
|
72
|
+
// costs a round trip per page and answers nothing.
|
|
73
|
+
if (!userId || isAnonymous) {
|
|
74
|
+
setFactors([]);
|
|
75
|
+
setError(null);
|
|
76
|
+
setLoading(false);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let cancelled = false;
|
|
80
|
+
setLoading(true);
|
|
81
|
+
supabase.auth.mfa.listFactors().then(({ data, error: listError }) => {
|
|
82
|
+
if (cancelled)
|
|
83
|
+
return;
|
|
84
|
+
if (listError) {
|
|
85
|
+
setError(listError.message);
|
|
86
|
+
setFactors([]);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
setError(null);
|
|
90
|
+
setFactors((data?.all ?? []).map((f) => ({
|
|
91
|
+
id: f.id,
|
|
92
|
+
friendlyName: f.friendly_name ?? null,
|
|
93
|
+
verified: f.status === 'verified',
|
|
94
|
+
createdAt: f.created_at ?? null,
|
|
95
|
+
})));
|
|
96
|
+
}
|
|
97
|
+
setLoading(false);
|
|
98
|
+
});
|
|
99
|
+
return () => { cancelled = true; };
|
|
100
|
+
}, [supabase, userId, isAnonymous, nonce]);
|
|
101
|
+
return {
|
|
102
|
+
factors,
|
|
103
|
+
enrolled: factors.some((f) => f.verified),
|
|
104
|
+
loading,
|
|
105
|
+
error,
|
|
106
|
+
refresh,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether this session has actually passed a second factor, as opposed to
|
|
111
|
+
* merely having one enrolled.
|
|
112
|
+
*
|
|
113
|
+
* `nextLevel === 'aal2'` means the user HAS a verified factor;
|
|
114
|
+
* `currentLevel === 'aal2'` means they have used it in this session. The gap
|
|
115
|
+
* between the two is the challenge step — read them separately, because
|
|
116
|
+
* conflating them either nags a user who has just passed the challenge or
|
|
117
|
+
* waves through one who never started it.
|
|
118
|
+
*/
|
|
119
|
+
export async function readAssuranceLevel(supabase) {
|
|
120
|
+
const { data, error } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel();
|
|
121
|
+
if (error || !data)
|
|
122
|
+
return { current: null, next: null, challengeRequired: false };
|
|
123
|
+
return {
|
|
124
|
+
current: data.currentLevel,
|
|
125
|
+
next: data.nextLevel,
|
|
126
|
+
challengeRequired: data.nextLevel === 'aal2' && data.currentLevel !== 'aal2',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
|
package/dist/suiteApps.d.ts
CHANGED
|
@@ -8,7 +8,12 @@ export interface SuiteNativeApp {
|
|
|
8
8
|
* not legal in a URL scheme and hyphens are.
|
|
9
9
|
*/
|
|
10
10
|
scheme: string;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* iOS bundle identifier — the app's `appId` in its capacitor.config, or for a
|
|
13
|
+
* natively-built app (Ergo Assess) its `PRODUCT_BUNDLE_IDENTIFIER`. Used to
|
|
14
|
+
* work out which app a plist belongs to, never to open one: iOS opens on the
|
|
15
|
+
* scheme.
|
|
16
|
+
*/
|
|
12
17
|
iosBundleId: string;
|
|
13
18
|
/** Android application id. Usually the same string; Universal AI's is not. */
|
|
14
19
|
androidPackage: string;
|
|
@@ -16,15 +21,33 @@ export interface SuiteNativeApp {
|
|
|
16
21
|
/**
|
|
17
22
|
* Every suite product that has a native build, and only those.
|
|
18
23
|
*
|
|
19
|
-
* ⚠️ **Being in the switcher is not enough to be in here.**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* ⚠️ **Being in the switcher is not enough to be in here.** Exports, Webinar and
|
|
25
|
+
* the rest are web-only; listing them would put a scheme nobody answers into
|
|
26
|
+
* fourteen Info.plists and mark rows "Installed" that can never be. A product
|
|
27
|
+
* missing from this list simply keeps today's behaviour — the https link —
|
|
28
|
+
* which is the correct answer for a product with no app.
|
|
24
29
|
*
|
|
25
30
|
* Universal Family has a native build and is deliberately absent for the same
|
|
26
31
|
* reason: it has no row in the switcher to open.
|
|
27
32
|
*
|
|
33
|
+
* ⚠️ **Ergo Assess used to be in that web-only sentence and is the reason this
|
|
34
|
+
* paragraph is worth re-reading rather than trusting.** It shipped a native app
|
|
35
|
+
* on both platforms while this list still called it web-only, so its switcher
|
|
36
|
+
* row went on falling back to the https link — the website, inside the webview
|
|
37
|
+
* the user was already in — while every other Assess app handed off. Nothing
|
|
38
|
+
* reported it: a missing registry entry is indistinguishable from "not
|
|
39
|
+
* installed", which is the same silence the three copies of this list have
|
|
40
|
+
* always had. **An app gaining a native build is a change to THIS file.**
|
|
41
|
+
*
|
|
42
|
+
* Ergo is also the first entry that is not a Capacitor app: it is a SwiftUI /
|
|
43
|
+
* Compose pair, so it has no `ios/App/App/` and its bundle id keeps the
|
|
44
|
+
* `com.universalsimulation.` prefix its Xcode project was generated under
|
|
45
|
+
* rather than the `uk.co.unisim.` the Capacitor apps share. Neither matters to
|
|
46
|
+
* the handoff — iOS matches on the scheme and Android on the package, and both
|
|
47
|
+
* are declared below — but it does mean `add-suite-app-links.mjs` has to find
|
|
48
|
+
* its plist by a different path, and that its own switcher rows cannot hand off
|
|
49
|
+
* yet, because its native nav bar has no switcher to tap.
|
|
50
|
+
*
|
|
28
51
|
* Universal AI is listed, and is the one entry where the two platform ids
|
|
29
52
|
* differ: its Capacitor `appId` `com.universal-ai.app` is legal on iOS and
|
|
30
53
|
* illegal on Android (a hyphen cannot appear in a Java package), so its Android
|
package/dist/suiteApps.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suiteApps.d.ts","sourceRoot":"","sources":["../src/suiteApps.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,EAAE,EAAE,cAAc,CAAA;IAClB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd
|
|
1
|
+
{"version":3,"file":"suiteApps.d.ts","sourceRoot":"","sources":["../src/suiteApps.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,EAAE,EAAE,cAAc,CAAA;IAClB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAetD,CAAA;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS,CAE7E;AA2BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAOD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAkBjE;AAUD,iDAAiD;AACjD,wBAAgB,uBAAuB,SAEtC;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAWvE"}
|
package/dist/suiteApps.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// • **iOS** matches on SCHEME, and `canOpenURL` answers `false` — with only a
|
|
20
20
|
// console line to say why — for any scheme the CALLING app has not listed
|
|
21
21
|
// in its own `LSApplicationQueriesSchemes`. So every app needs every other
|
|
22
|
-
// app's scheme in its Info.plist. `scripts/
|
|
22
|
+
// app's scheme in its Info.plist. `scripts/add-suite-app-links.mjs`
|
|
23
23
|
// writes that from this file; nobody should be hand-editing the lists.
|
|
24
24
|
//
|
|
25
25
|
// • **Android** matches on PACKAGE, and needs no per-app change at all: the
|
|
@@ -36,15 +36,33 @@ import { isNativeShell, capacitorPlugin } from './sessionStorage.js';
|
|
|
36
36
|
/**
|
|
37
37
|
* Every suite product that has a native build, and only those.
|
|
38
38
|
*
|
|
39
|
-
* ⚠️ **Being in the switcher is not enough to be in here.**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* ⚠️ **Being in the switcher is not enough to be in here.** Exports, Webinar and
|
|
40
|
+
* the rest are web-only; listing them would put a scheme nobody answers into
|
|
41
|
+
* fourteen Info.plists and mark rows "Installed" that can never be. A product
|
|
42
|
+
* missing from this list simply keeps today's behaviour — the https link —
|
|
43
|
+
* which is the correct answer for a product with no app.
|
|
44
44
|
*
|
|
45
45
|
* Universal Family has a native build and is deliberately absent for the same
|
|
46
46
|
* reason: it has no row in the switcher to open.
|
|
47
47
|
*
|
|
48
|
+
* ⚠️ **Ergo Assess used to be in that web-only sentence and is the reason this
|
|
49
|
+
* paragraph is worth re-reading rather than trusting.** It shipped a native app
|
|
50
|
+
* on both platforms while this list still called it web-only, so its switcher
|
|
51
|
+
* row went on falling back to the https link — the website, inside the webview
|
|
52
|
+
* the user was already in — while every other Assess app handed off. Nothing
|
|
53
|
+
* reported it: a missing registry entry is indistinguishable from "not
|
|
54
|
+
* installed", which is the same silence the three copies of this list have
|
|
55
|
+
* always had. **An app gaining a native build is a change to THIS file.**
|
|
56
|
+
*
|
|
57
|
+
* Ergo is also the first entry that is not a Capacitor app: it is a SwiftUI /
|
|
58
|
+
* Compose pair, so it has no `ios/App/App/` and its bundle id keeps the
|
|
59
|
+
* `com.universalsimulation.` prefix its Xcode project was generated under
|
|
60
|
+
* rather than the `uk.co.unisim.` the Capacitor apps share. Neither matters to
|
|
61
|
+
* the handoff — iOS matches on the scheme and Android on the package, and both
|
|
62
|
+
* are declared below — but it does mean `add-suite-app-links.mjs` has to find
|
|
63
|
+
* its plist by a different path, and that its own switcher rows cannot hand off
|
|
64
|
+
* yet, because its native nav bar has no switcher to tap.
|
|
65
|
+
*
|
|
48
66
|
* Universal AI is listed, and is the one entry where the two platform ids
|
|
49
67
|
* differ: its Capacitor `appId` `com.universal-ai.app` is legal on iOS and
|
|
50
68
|
* illegal on Android (a hyphen cannot appear in a Java package), so its Android
|
|
@@ -53,6 +71,7 @@ import { isNativeShell, capacitorPlugin } from './sessionStorage.js';
|
|
|
53
71
|
* is here so that stops being true the moment the badge comes off.
|
|
54
72
|
*/
|
|
55
73
|
export const SUITE_NATIVE_APPS = [
|
|
74
|
+
{ id: 'ergo_assess', scheme: 'unisim-ergo-assess', iosBundleId: 'com.universalsimulation.ergoassesscapture', androidPackage: 'com.universalsimulation.ergoassesscapture' },
|
|
56
75
|
{ id: 'cyber_assess', scheme: 'unisim-cyber-assess', iosBundleId: 'uk.co.unisim.cyberassess', androidPackage: 'uk.co.unisim.cyberassess' },
|
|
57
76
|
{ id: 'workplace_assess', scheme: 'unisim-workplace-assess', iosBundleId: 'uk.co.unisim.wfhassess', androidPackage: 'uk.co.unisim.wfhassess' },
|
|
58
77
|
{ id: 'fire_assess', scheme: 'unisim-fire-assess', iosBundleId: 'uk.co.unisim.fireassess', androidPackage: 'uk.co.unisim.fireassess' },
|
package/dist/suiteApps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suiteApps.js","sourceRoot":"","sources":["../src/suiteApps.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sEAAsE;AACtE,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,0DAA0D;AAC1D,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,gFAAgF;AAChF,yEAAyE;AACzE,0EAA0E;AAC1E,+CAA+C;AAC/C,EAAE;AACF,gEAAgE;AAChE,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,
|
|
1
|
+
{"version":3,"file":"suiteApps.js","sourceRoot":"","sources":["../src/suiteApps.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sEAAsE;AACtE,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,0DAA0D;AAC1D,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,gFAAgF;AAChF,yEAAyE;AACzE,0EAA0E;AAC1E,+CAA+C;AAC/C,EAAE;AACF,gEAAgE;AAChE,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,wEAAwE;AACxE,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,6EAA6E;AAC7E,iDAAiD;AACjD,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,cAAc;AACd,iFAAiF;AAEjF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAuBpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA8B;IAC1D,EAAE,EAAE,EAAE,aAAa,EAAO,MAAM,EAAE,oBAAoB,EAAO,WAAW,EAAE,2CAA2C,EAAE,cAAc,EAAE,2CAA2C,EAAE;IACpL,EAAE,EAAE,EAAE,cAAc,EAAM,MAAM,EAAE,qBAAqB,EAAM,WAAW,EAAE,0BAA0B,EAAE,cAAc,EAAE,0BAA0B,EAAE;IAClJ,EAAE,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,yBAAyB,EAAE,WAAW,EAAE,wBAAwB,EAAI,cAAc,EAAE,wBAAwB,EAAE;IAChJ,EAAE,EAAE,EAAE,aAAa,EAAO,MAAM,EAAE,oBAAoB,EAAO,WAAW,EAAE,yBAAyB,EAAG,cAAc,EAAE,yBAAyB,EAAE;IACjJ,EAAE,EAAE,EAAE,cAAc,EAAM,MAAM,EAAE,qBAAqB,EAAM,WAAW,EAAE,0BAA0B,EAAE,cAAc,EAAE,0BAA0B,EAAE;IAClJ,EAAE,EAAE,EAAE,aAAa,EAAO,MAAM,EAAE,oBAAoB,EAAO,WAAW,EAAE,yBAAyB,EAAG,cAAc,EAAE,yBAAyB,EAAE;IACjJ,EAAE,EAAE,EAAE,KAAK,EAAe,MAAM,EAAE,YAAY,EAAe,WAAW,EAAE,kBAAkB,EAAU,cAAc,EAAE,kBAAkB,EAAE;IAC1I,EAAE,EAAE,EAAE,QAAQ,EAAY,MAAM,EAAE,eAAe,EAAY,WAAW,EAAE,qBAAqB,EAAO,cAAc,EAAE,qBAAqB,EAAE;IAC7I,EAAE,EAAE,EAAE,IAAI,EAAgB,MAAM,EAAE,WAAW,EAAgB,WAAW,EAAE,iBAAiB,EAAW,cAAc,EAAE,iBAAiB,EAAE;IACzI,EAAE,EAAE,EAAE,UAAU,EAAU,MAAM,EAAE,iBAAiB,EAAU,WAAW,EAAE,uBAAuB,EAAK,cAAc,EAAE,uBAAuB,EAAE;IAC/I,EAAE,EAAE,EAAE,WAAW,EAAS,MAAM,EAAE,kBAAkB,EAAS,WAAW,EAAE,wBAAwB,EAAI,cAAc,EAAE,wBAAwB,EAAE;IAChJ,EAAE,EAAE,EAAE,SAAS,EAAW,MAAM,EAAE,gBAAgB,EAAW,WAAW,EAAE,sBAAsB,EAAM,cAAc,EAAE,sBAAsB,EAAE;IAC9I,EAAE,EAAE,EAAE,WAAW,EAAS,MAAM,EAAE,kBAAkB,EAAS,WAAW,EAAE,wBAAwB,EAAI,cAAc,EAAE,wBAAwB,EAAE;IAChJ,EAAE,EAAE,EAAE,IAAI,EAAgB,MAAM,EAAE,WAAW,EAAgB,WAAW,EAAE,sBAAsB,EAAM,cAAc,EAAE,iBAAiB,EAAE;CAC1I,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,EAAkB;IAC/C,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AACnD,CAAC;AASD;;;;;;;;;;;GAWG;AACH,SAAS,eAAe;IACtB,MAAM,MAAM,GAAG,eAAe,CAA2B,iBAAiB,CAAC,CAAA;IAC3E,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,aAAa,KAAK,UAAU;QAAE,OAAO,IAAI,CAAA;IACtE,OAAO,MAAyB,CAAA;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,aAAa,EAAE,IAAI,eAAe,EAAE,KAAK,IAAI,CAAA;AACtD,CAAC;AAED,MAAM,IAAI,GAAwB,IAAI,GAAG,EAAE,CAAA;AAE3C,IAAI,KAAK,GAAwC,IAAI,CAAA;AACrD,IAAI,YAAY,GAAG,KAAK,CAAA;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAA;IACvB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAChC,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAE7D,UAAU,EAAE,CAAA;IACZ,KAAK,GAAG,MAAM;SACX,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,iBAAiB,CAAC,EAAE,CAAC;SAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,SAAS,IAAI,EAAE,CAAwB,CAAC;SAC/D,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,yEAAyE;QACzE,yEAAyE;QACzE,gCAAgC;QAChC,OAAO,CAAC,IAAI,CAAC,6DAA6D,EAAE,GAAG,CAAC,CAAA;QAChF,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACJ,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,YAAY,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAM;IAC3D,YAAY,GAAG,IAAI,CAAA;IACnB,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS;YAAE,KAAK,GAAG,IAAI,CAAA;IAC1D,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,uBAAuB;IACrC,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAkB;IACnD,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,CAAC,CAAA;IAC9B,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;IAChC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE;QAAE,OAAO,KAAK,CAAA;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5C,OAAO,MAAM,KAAK,IAAI,CAAA;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,+BAA+B,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;QAC1D,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unisim/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.126.0",
|
|
4
4
|
"description": "Shared React SDK for the Universal Suite — auth, entitlements, usage telemetry, changelog, org admin.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Universal Simulation Ltd",
|
|
@@ -61,6 +61,8 @@
|
|
|
61
61
|
"test:session-storage": "npm run build && node tests/session-storage.browser.mjs",
|
|
62
62
|
"test:shared-signout": "node tests/shared-signout.mjs",
|
|
63
63
|
"test:suite-apps": "npm run build && node tests/suite-apps.mjs",
|
|
64
|
+
"test:suite-switcher": "npm run build && node tests/suite-switcher-close.browser.mjs",
|
|
65
|
+
"test:multi-org": "npm run build && node tests/multi-org.mjs",
|
|
64
66
|
"prepublishOnly": "npm run typecheck && npm run build"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|